diff --git a/.changelog-pending/2026-06-17T20-56-16-0e1c039566c6e6c4061925ea050b1bf288163094.md b/.changelog-pending/2026-06-17T20-56-16-0e1c039566c6e6c4061925ea050b1bf288163094.md new file mode 100644 index 00000000..36e7de75 --- /dev/null +++ b/.changelog-pending/2026-06-17T20-56-16-0e1c039566c6e6c4061925ea050b1bf288163094.md @@ -0,0 +1,55 @@ +* [#401](https://github.com/workos/workos-php/pull/401) feat(generated)!: regenerate from spec (11 changes) + + **⚠️ Breaking** + * **[organization_membership](https://workos.com/docs/reference/authkit/organization-membership)**: + * Changed response for `UserManagementOrganizationMembership.list` + * **[pipes](https://workos.com/docs/reference/pipes)**: + * SDK surface change: Symbol "Pipes.createDataIntegrationToken" was removed + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Changed response for `UserManagementInvitations.list` + + **Features** + * **[authorization](https://workos.com/docs/reference/fga)**: + * Added model `ReplaceGroupRoleAssignmentEntry` + * Added model `ReplaceGroupRoleAssignments` + * Added model `DeleteGroupRoleAssignmentsByCriteria` + * Added endpoint `POST /authorization/groups/{group_id}/role_assignments` + * Added endpoint `PUT /authorization/groups/{group_id}/role_assignments` + * Added endpoint `DELETE /authorization/groups/{group_id}/role_assignments` + * Added endpoint `GET /authorization/groups/{group_id}/role_assignments/{role_assignment_id}` + * Added endpoint `DELETE /authorization/groups/{group_id}/role_assignments/{role_assignment_id}` + * **[client](https://workos.com/docs/reference)**: + * Added model `ClientApiToken` + * Added model `ClientApiTokenResponse` + * Added service `Client` + * **[connect](https://workos.com/docs/reference/workos-connect/standalone)**: + * Added `auth_method` to `ConnectedAccount` + * Added `api_key_last_4` to `ConnectedAccount` + * Added enum `ConnectedAccountAuthMethod` + * **[groups](https://workos.com/docs/reference/groups)**: + * Added model `CreateGroupRoleAssignment` + * Added model `GroupRoleAssignment` + * Added model `GroupRoleAssignmentList` + * Added model `GroupRoleAssignmentResource` + * **[organization_membership](https://workos.com/docs/reference/authkit/organization-membership)**: + * Added model `UserOrganizationMembershipList` + * Added model `UserOrganizationMembershipListListMetadata` + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Added model `DataIntegrationCredentials` + * Added model `DataIntegrationConfigurationResponse` + * Added model `DataIntegrationConfigurationListResponse` + * Added model `ConfigureDataIntegrationBody` + * Added `auth_methods` to `DataIntegrationsListResponseData` + * Added `auth_method` to `DataIntegrationsListResponseDataConnectedAccount` + * Added `api_key_last_4` to `DataIntegrationsListResponseDataConnectedAccount` + * Added enum `DataIntegrationCredentialsCredentialsType` + * Added enum `DataIntegrationsListResponseDataAuthMethods` + * Added enum `DataIntegrationsListResponseDataConnectedAccountAuthMethod` + * Added service `PipesProvider` + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Added model `UserInviteList` + * Added model `UserInviteListListMetadata` + * Made `AuthorizationCodeSessionAuthenticateRequest.client_secret` optional + * Made `RefreshTokenSessionAuthenticateRequest.client_secret` optional + * **[widgets](https://workos.com/docs/reference/widgets)**: + * Added `widgets:pipes:manage` to `WidgetSessionTokenScopes` diff --git a/.last-synced-sha b/.last-synced-sha index 99cf20f6..409f7391 100644 --- a/.last-synced-sha +++ b/.last-synced-sha @@ -1 +1 @@ -d8c5a7de598792b1cee18d4a9842825110e5c74a +b6a68da8bd60c1478e0a86ca97c75448677e8871 diff --git a/.oagen-manifest.json b/.oagen-manifest.json index abc7b738..c446677a 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -1,7 +1,7 @@ { "version": 2, "language": "php", - "generatedAt": "2026-06-03T19:19:56.689Z", + "generatedAt": "2026-06-17T20:55:54.166Z", "files": [ "lib/Resource/ActionAuthenticationDenied.php", "lib/Resource/ActionAuthenticationDeniedData.php", @@ -118,6 +118,9 @@ "lib/Resource/CORSOriginResponse.php", "lib/Resource/ChallengeAuthenticationFactor.php", "lib/Resource/CheckAuthorization.php", + "lib/Resource/ClientApiToken.php", + "lib/Resource/ClientApiTokenResponse.php", + "lib/Resource/ConfigureDataIntegrationBody.php", "lib/Resource/ConfirmEmailChange.php", "lib/Resource/ConnectApplication.php", "lib/Resource/ConnectApplicationM2M.php", @@ -125,6 +128,7 @@ "lib/Resource/ConnectApplicationOAuthRedirectUris.php", "lib/Resource/ConnectApplicationRedirectUri.php", "lib/Resource/ConnectedAccount.php", + "lib/Resource/ConnectedAccountAuthMethod.php", "lib/Resource/ConnectedAccountState.php", "lib/Resource/Connection.php", "lib/Resource/ConnectionActivated.php", @@ -160,6 +164,7 @@ "lib/Resource/CreateDataKeyResponse.php", "lib/Resource/CreateGroup.php", "lib/Resource/CreateGroupMembership.php", + "lib/Resource/CreateGroupRoleAssignment.php", "lib/Resource/CreateM2MApplication.php", "lib/Resource/CreateMagicCodeAndReturn.php", "lib/Resource/CreateOAuthApplication.php", @@ -183,6 +188,10 @@ "lib/Resource/DataIntegrationAccessTokenResponseAccessToken.php", "lib/Resource/DataIntegrationAccessTokenResponseError.php", "lib/Resource/DataIntegrationAuthorizeUrlResponse.php", + "lib/Resource/DataIntegrationConfigurationListResponse.php", + "lib/Resource/DataIntegrationConfigurationResponse.php", + "lib/Resource/DataIntegrationCredentials.php", + "lib/Resource/DataIntegrationCredentialsCredentialsType.php", "lib/Resource/DataIntegrationsGetDataIntegrationAuthorizeUrlRequest.php", "lib/Resource/DataIntegrationsGetUserTokenRequest.php", "lib/Resource/DataIntegrationsListResponse.php", @@ -191,6 +200,7 @@ "lib/Resource/DataIntegrationsListResponseDataOwnership.php", "lib/Resource/DecryptRequest.php", "lib/Resource/DecryptResponse.php", + "lib/Resource/DeleteGroupRoleAssignmentsByCriteria.php", "lib/Resource/DeleteObjectResponse.php", "lib/Resource/DeviceAuthorizationResponse.php", "lib/Resource/DeviceCodeSessionAuthenticateRequest.php", @@ -292,6 +302,9 @@ "lib/Resource/GroupMemberAddedData.php", "lib/Resource/GroupMemberRemoved.php", "lib/Resource/GroupMemberRemovedData.php", + "lib/Resource/GroupRoleAssignment.php", + "lib/Resource/GroupRoleAssignmentList.php", + "lib/Resource/GroupRoleAssignmentResource.php", "lib/Resource/GroupUpdated.php", "lib/Resource/IntentOptions.php", "lib/Resource/Invitation.php", @@ -404,6 +417,8 @@ "lib/Resource/RefreshTokenSessionAuthenticateRequest.php", "lib/Resource/RekeyRequest.php", "lib/Resource/RemoveRole.php", + "lib/Resource/ReplaceGroupRoleAssignmentEntry.php", + "lib/Resource/ReplaceGroupRoleAssignments.php", "lib/Resource/ResendUserInviteOptions.php", "lib/Resource/ResetPasswordResponse.php", "lib/Resource/RevokeSession.php", @@ -521,6 +536,7 @@ "lib/Service/ApiKeys.php", "lib/Service/AuditLogs.php", "lib/Service/Authorization.php", + "lib/Service/ClientApi.php", "lib/Service/Connect.php", "lib/Service/DirectorySync.php", "lib/Service/Events.php", @@ -537,6 +553,7 @@ "lib/Service/PasswordHashed.php", "lib/Service/PasswordPlaintext.php", "lib/Service/Pipes.php", + "lib/Service/PipesProvider.php", "lib/Service/Radar.php", "lib/Service/ResourceTargetByExternalId.php", "lib/Service/ResourceTargetById.php", @@ -654,6 +671,9 @@ "tests/Fixtures/authorized_connect_application_list_data.json", "tests/Fixtures/challenge_authentication_factor.json", "tests/Fixtures/check_authorization.json", + "tests/Fixtures/client_api_token.json", + "tests/Fixtures/client_api_token_response.json", + "tests/Fixtures/configure_data_integration_body.json", "tests/Fixtures/confirm_email_change.json", "tests/Fixtures/connect_application.json", "tests/Fixtures/connect_application_m2m.json", @@ -688,6 +708,7 @@ "tests/Fixtures/create_data_key_response.json", "tests/Fixtures/create_group.json", "tests/Fixtures/create_group_membership.json", + "tests/Fixtures/create_group_role_assignment.json", "tests/Fixtures/create_m2m_application.json", "tests/Fixtures/create_magic_code_and_return.json", "tests/Fixtures/create_oauth_application.json", @@ -707,6 +728,9 @@ "tests/Fixtures/data_integration_access_token_response.json", "tests/Fixtures/data_integration_access_token_response_access_token.json", "tests/Fixtures/data_integration_authorize_url_response.json", + "tests/Fixtures/data_integration_configuration_list_response.json", + "tests/Fixtures/data_integration_configuration_response.json", + "tests/Fixtures/data_integration_credentials.json", "tests/Fixtures/data_integrations_get_data_integration_authorize_url_request.json", "tests/Fixtures/data_integrations_get_user_token_request.json", "tests/Fixtures/data_integrations_list_response.json", @@ -714,6 +738,7 @@ "tests/Fixtures/data_integrations_list_response_data_connected_account.json", "tests/Fixtures/decrypt_request.json", "tests/Fixtures/decrypt_response.json", + "tests/Fixtures/delete_group_role_assignments_by_criteria.json", "tests/Fixtures/delete_object_response.json", "tests/Fixtures/device_authorization_response.json", "tests/Fixtures/device_code_session_authenticate_request.json", @@ -807,6 +832,8 @@ "tests/Fixtures/group_member_added_data.json", "tests/Fixtures/group_member_removed.json", "tests/Fixtures/group_member_removed_data.json", + "tests/Fixtures/group_role_assignment.json", + "tests/Fixtures/group_role_assignment_resource.json", "tests/Fixtures/group_updated.json", "tests/Fixtures/intent_options.json", "tests/Fixtures/invitation.json", @@ -835,6 +862,7 @@ "tests/Fixtures/list_event_schema.json", "tests/Fixtures/list_flag.json", "tests/Fixtures/list_group.json", + "tests/Fixtures/list_group_role_assignment.json", "tests/Fixtures/list_metadata.json", "tests/Fixtures/list_object_summary.json", "tests/Fixtures/list_organization.json", @@ -930,6 +958,8 @@ "tests/Fixtures/refresh_token_session_authenticate_request.json", "tests/Fixtures/rekey_request.json", "tests/Fixtures/remove_role.json", + "tests/Fixtures/replace_group_role_assignment_entry.json", + "tests/Fixtures/replace_group_role_assignments.json", "tests/Fixtures/resend_user_invite_options.json", "tests/Fixtures/reset_password_response.json", "tests/Fixtures/revoke_session.json", @@ -1033,6 +1063,7 @@ "tests/Service/ApiKeysTest.php", "tests/Service/AuditLogsTest.php", "tests/Service/AuthorizationTest.php", + "tests/Service/ClientApiTest.php", "tests/Service/ConnectTest.php", "tests/Service/DirectorySyncTest.php", "tests/Service/EventsTest.php", @@ -1042,6 +1073,7 @@ "tests/Service/OrganizationDomainsTest.php", "tests/Service/OrganizationMembershipTest.php", "tests/Service/OrganizationsTest.php", + "tests/Service/PipesProviderTest.php", "tests/Service/PipesTest.php", "tests/Service/RadarTest.php", "tests/Service/SSOTest.php", @@ -1075,6 +1107,30 @@ "sdkMethod": "completeOAuth2", "service": "connect" }, + "GET /authorization/groups/{group_id}/role_assignments": { + "sdkMethod": "listGroupRoleAssignments", + "service": "authorization" + }, + "POST /authorization/groups/{group_id}/role_assignments": { + "sdkMethod": "createGroupRoleAssignment", + "service": "authorization" + }, + "PUT /authorization/groups/{group_id}/role_assignments": { + "sdkMethod": "updateGroupRoleAssignments", + "service": "authorization" + }, + "DELETE /authorization/groups/{group_id}/role_assignments": { + "sdkMethod": "deleteGroupRoleAssignments", + "service": "authorization" + }, + "GET /authorization/groups/{group_id}/role_assignments/{role_assignment_id}": { + "sdkMethod": "getGroupRoleAssignment", + "service": "authorization" + }, + "DELETE /authorization/groups/{group_id}/role_assignments/{role_assignment_id}": { + "sdkMethod": "deleteGroupRoleAssignment", + "service": "authorization" + }, "POST /authorization/organization_memberships/{organization_membership_id}/check": { "sdkMethod": "check", "service": "authorization" @@ -1231,6 +1287,10 @@ "sdkMethod": "deletePermission", "service": "authorization" }, + "POST /client/token": { + "sdkMethod": "createToken", + "service": "clientApi" + }, "GET /connect/applications": { "sdkMethod": "listApplications", "service": "connect" @@ -1279,8 +1339,8 @@ "sdkMethod": "authorizeDataIntegration", "service": "pipes" }, - "POST /data-integrations/{slug}/token": { - "sdkMethod": "createDataIntegrationToken", + "POST /data-integrations/{provider}/token": { + "sdkMethod": "getAccessToken", "service": "pipes" }, "GET /directories": { @@ -1399,6 +1459,14 @@ "sdkMethod": "createOrganizationApiKey", "service": "apiKeys" }, + "GET /organizations/{organizationId}/data_integration_configurations": { + "sdkMethod": "listOrganizationDataIntegrationConfigurations", + "service": "pipesProvider" + }, + "PUT /organizations/{organizationId}/data_integration_configurations/{slug}": { + "sdkMethod": "updateOrganizationDataIntegrationConfiguration", + "service": "pipesProvider" + }, "GET /organizations/{organizationId}/feature-flags": { "sdkMethod": "listOrganizationFeatureFlags", "service": "featureFlags" diff --git a/lib/Resource/AuthorizationCodeSessionAuthenticateRequest.php b/lib/Resource/AuthorizationCodeSessionAuthenticateRequest.php index 5b39819e..b91b29be 100644 --- a/lib/Resource/AuthorizationCodeSessionAuthenticateRequest.php +++ b/lib/Resource/AuthorizationCodeSessionAuthenticateRequest.php @@ -13,11 +13,11 @@ public function __construct( /** The client ID of the application. */ public string $clientId, - /** The client secret of the application. */ - public string $clientSecret, public string $grantType, /** The authorization code received from the redirect. */ public string $code, + /** The client secret of the application. May be omitted by public clients that authenticate through other means, such as a PKCE `code_verifier`. */ + public ?string $clientSecret = null, /** The PKCE code verifier used to derive the code challenge passed to the authorization URL. */ public ?string $codeVerifier = null, /** An invitation token to accept during authentication. */ @@ -35,9 +35,9 @@ public static function fromArray(array $data): self { return new self( clientId: $data['client_id'], - clientSecret: $data['client_secret'], grantType: $data['grant_type'] ?? 'authorization_code', code: $data['code'], + clientSecret: $data['client_secret'] ?? null, codeVerifier: $data['code_verifier'] ?? null, invitationToken: $data['invitation_token'] ?? null, ipAddress: $data['ip_address'] ?? null, @@ -50,9 +50,9 @@ public function toArray(): array { return [ 'client_id' => $this->clientId, - 'client_secret' => $this->clientSecret, 'grant_type' => $this->grantType, 'code' => $this->code, + 'client_secret' => $this->clientSecret, 'code_verifier' => $this->codeVerifier, 'invitation_token' => $this->invitationToken, 'ip_address' => $this->ipAddress, diff --git a/lib/Resource/ClientApiToken.php b/lib/Resource/ClientApiToken.php new file mode 100644 index 00000000..0dcba142 --- /dev/null +++ b/lib/Resource/ClientApiToken.php @@ -0,0 +1,36 @@ + $this->organizationId, + 'user_id' => $this->userId, + ]; + } +} diff --git a/lib/Resource/ClientApiTokenResponse.php b/lib/Resource/ClientApiTokenResponse.php new file mode 100644 index 00000000..4b424096 --- /dev/null +++ b/lib/Resource/ClientApiTokenResponse.php @@ -0,0 +1,32 @@ + $this->token, + ]; + } +} diff --git a/lib/Resource/ConfigureDataIntegrationBody.php b/lib/Resource/ConfigureDataIntegrationBody.php new file mode 100644 index 00000000..cfd84733 --- /dev/null +++ b/lib/Resource/ConfigureDataIntegrationBody.php @@ -0,0 +1,47 @@ +|null + */ + public ?array $scopes = null, + /** The OAuth client ID of the organization's own application. Must be provided together with `client_secret`, and only for providers whose credentials are supplied by the organization. */ + public ?string $clientId = null, + /** The OAuth client secret of the organization's own application. Must be provided together with `client_id`. */ + public ?string $clientSecret = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + enabled: $data['enabled'] ?? null, + scopes: $data['scopes'] ?? null, + clientId: $data['client_id'] ?? null, + clientSecret: $data['client_secret'] ?? null, + ); + } + + public function toArray(): array + { + return [ + 'enabled' => $this->enabled, + 'scopes' => $this->scopes, + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + ]; + } +} diff --git a/lib/Resource/ConnectedAccount.php b/lib/Resource/ConnectedAccount.php index 8e806ea5..34e18426 100644 --- a/lib/Resource/ConnectedAccount.php +++ b/lib/Resource/ConnectedAccount.php @@ -35,6 +35,10 @@ public function __construct( public string $createdAt, /** The timestamp when the connection was last updated. */ public string $updatedAt, + /** The authentication method used for this connection (`oauth` or `api_key`). Defaults to `oauth` if absent. */ + public ?ConnectedAccountAuthMethod $authMethod = null, + /** The last four characters of the API key, or `null` for OAuth connections. */ + public ?string $apiKeyLast4 = null, ) { } @@ -49,6 +53,8 @@ public static function fromArray(array $data): self state: ConnectedAccountState::from($data['state']), createdAt: $data['created_at'], updatedAt: $data['updated_at'], + authMethod: isset($data['auth_method']) ? ConnectedAccountAuthMethod::from($data['auth_method']) : null, + apiKeyLast4: $data['api_key_last_4'] ?? null, ); } @@ -63,6 +69,8 @@ public function toArray(): array 'state' => $this->state->value, 'created_at' => $this->createdAt, 'updated_at' => $this->updatedAt, + 'auth_method' => $this->authMethod?->value, + 'api_key_last_4' => $this->apiKeyLast4, ]; } } diff --git a/lib/Resource/ConnectedAccountAuthMethod.php b/lib/Resource/ConnectedAccountAuthMethod.php new file mode 100644 index 00000000..4f480e02 --- /dev/null +++ b/lib/Resource/ConnectedAccountAuthMethod.php @@ -0,0 +1,13 @@ + $this->roleSlug, + 'resource_id' => $this->resourceId, + 'resource_external_id' => $this->resourceExternalId, + 'resource_type_slug' => $this->resourceTypeSlug, + ]; + } +} diff --git a/lib/Resource/DataIntegrationAccessTokenResponseAccessToken.php b/lib/Resource/DataIntegrationAccessTokenResponseAccessToken.php index 99490777..23f3a4b5 100644 --- a/lib/Resource/DataIntegrationAccessTokenResponseAccessToken.php +++ b/lib/Resource/DataIntegrationAccessTokenResponseAccessToken.php @@ -17,7 +17,7 @@ public function __construct( /** The OAuth access token for the connected integration. */ public string $accessToken, /** The ISO-8601 formatted timestamp indicating when the access token expires. */ - public ?string $expiresAt, + public ?\DateTimeImmutable $expiresAt, /** * The scopes granted to the access token. * @var array @@ -36,7 +36,7 @@ public static function fromArray(array $data): self return new self( object: $data['object'] ?? 'access_token', accessToken: $data['access_token'], - expiresAt: $data['expires_at'] ?? null, + expiresAt: isset($data['expires_at']) ? new \DateTimeImmutable($data['expires_at']) : null, scopes: $data['scopes'], missingScopes: $data['missing_scopes'], ); @@ -47,7 +47,7 @@ public function toArray(): array return [ 'object' => $this->object, 'access_token' => $this->accessToken, - 'expires_at' => $this->expiresAt, + 'expires_at' => $this->expiresAt?->format(\DateTimeInterface::RFC3339_EXTENDED), 'scopes' => $this->scopes, 'missing_scopes' => $this->missingScopes, ]; diff --git a/lib/Resource/DataIntegrationConfigurationListResponse.php b/lib/Resource/DataIntegrationConfigurationListResponse.php new file mode 100644 index 00000000..9c096488 --- /dev/null +++ b/lib/Resource/DataIntegrationConfigurationListResponse.php @@ -0,0 +1,39 @@ + + */ + public array $data, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + object: $data['object'] ?? 'list', + data: array_map(fn ($item) => DataIntegrationConfigurationResponse::fromArray($item), $data['data']), + ); + } + + public function toArray(): array + { + return [ + 'object' => $this->object, + 'data' => array_map(fn ($item) => $item->toArray(), $this->data), + ]; + } +} diff --git a/lib/Resource/DataIntegrationConfigurationResponse.php b/lib/Resource/DataIntegrationConfigurationResponse.php new file mode 100644 index 00000000..8761e44d --- /dev/null +++ b/lib/Resource/DataIntegrationConfigurationResponse.php @@ -0,0 +1,70 @@ +|null + */ + public ?array $scopes, + /** The timestamp when the configuration was created. */ + public string $createdAt, + /** The timestamp when the configuration was last updated. */ + public string $updatedAt, + public ?DataIntegrationCredentials $credentials = null, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + object: $data['object'] ?? 'data_integration_configuration', + id: $data['id'], + organizationId: $data['organization_id'], + slug: $data['slug'], + name: $data['name'], + enabled: $data['enabled'], + scopes: $data['scopes'] ?? null, + createdAt: $data['created_at'], + updatedAt: $data['updated_at'], + credentials: isset($data['credentials']) ? DataIntegrationCredentials::fromArray($data['credentials']) : null, + ); + } + + public function toArray(): array + { + return [ + 'object' => $this->object, + 'id' => $this->id, + 'organization_id' => $this->organizationId, + 'slug' => $this->slug, + 'name' => $this->name, + 'enabled' => $this->enabled, + 'scopes' => $this->scopes, + 'created_at' => $this->createdAt, + 'updated_at' => $this->updatedAt, + 'credentials' => $this->credentials?->toArray(), + ]; + } +} diff --git a/lib/Resource/DataIntegrationCredentials.php b/lib/Resource/DataIntegrationCredentials.php new file mode 100644 index 00000000..601e4d72 --- /dev/null +++ b/lib/Resource/DataIntegrationCredentials.php @@ -0,0 +1,49 @@ + $this->credentialsType->value, + 'has_credentials' => $this->hasCredentials, + 'client_id' => $this->clientId, + 'client_secret_last_four' => $this->clientSecretLastFour, + 'redirect_uri' => $this->redirectUri, + ]; + } +} diff --git a/lib/Resource/DataIntegrationCredentialsCredentialsType.php b/lib/Resource/DataIntegrationCredentialsCredentialsType.php new file mode 100644 index 00000000..83b920da --- /dev/null +++ b/lib/Resource/DataIntegrationCredentialsCredentialsType.php @@ -0,0 +1,14 @@ +|null + */ + public ?array $authMethods = null, ) { } @@ -56,6 +61,7 @@ public static function fromArray(array $data): self createdAt: $data['created_at'], updatedAt: $data['updated_at'], connectedAccount: isset($data['connected_account']) ? DataIntegrationsListResponseDataConnectedAccount::fromArray($data['connected_account']) : null, + authMethods: isset($data['auth_methods']) ? array_map(fn ($item) => ConnectedAccountAuthMethod::from($item), $data['auth_methods']) : null, ); } @@ -74,6 +80,7 @@ public function toArray(): array 'created_at' => $this->createdAt, 'updated_at' => $this->updatedAt, 'connected_account' => $this->connectedAccount?->toArray(), + 'auth_methods' => $this->authMethods !== null ? array_map(fn ($item) => $item->value, $this->authMethods) : null, ]; } } diff --git a/lib/Resource/DataIntegrationsListResponseDataConnectedAccount.php b/lib/Resource/DataIntegrationsListResponseDataConnectedAccount.php index 02e398c7..1aba143c 100644 --- a/lib/Resource/DataIntegrationsListResponseDataConnectedAccount.php +++ b/lib/Resource/DataIntegrationsListResponseDataConnectedAccount.php @@ -40,6 +40,10 @@ public function __construct( * @deprecated */ public ?string $userlandUserId, + /** The authentication method used for this connection (`oauth` or `api_key`). Defaults to `oauth` if absent. */ + public ?ConnectedAccountAuthMethod $authMethod = null, + /** The last four characters of the API key, or `null` for OAuth connections. */ + public ?string $apiKeyLast4 = null, ) { } @@ -55,6 +59,8 @@ public static function fromArray(array $data): self createdAt: $data['created_at'], updatedAt: $data['updated_at'], userlandUserId: $data['userlandUserId'] ?? null, + authMethod: isset($data['auth_method']) ? ConnectedAccountAuthMethod::from($data['auth_method']) : null, + apiKeyLast4: $data['api_key_last_4'] ?? null, ); } @@ -70,6 +76,8 @@ public function toArray(): array 'created_at' => $this->createdAt, 'updated_at' => $this->updatedAt, 'userlandUserId' => $this->userlandUserId, + 'auth_method' => $this->authMethod?->value, + 'api_key_last_4' => $this->apiKeyLast4, ]; } } diff --git a/lib/Resource/DeleteGroupRoleAssignmentsByCriteria.php b/lib/Resource/DeleteGroupRoleAssignmentsByCriteria.php new file mode 100644 index 00000000..e3fe7ca0 --- /dev/null +++ b/lib/Resource/DeleteGroupRoleAssignmentsByCriteria.php @@ -0,0 +1,44 @@ + $this->roleSlug, + 'resource_id' => $this->resourceId, + 'resource_external_id' => $this->resourceExternalId, + 'resource_type_slug' => $this->resourceTypeSlug, + ]; + } +} diff --git a/lib/Resource/GroupRoleAssignment.php b/lib/Resource/GroupRoleAssignment.php new file mode 100644 index 00000000..5907e990 --- /dev/null +++ b/lib/Resource/GroupRoleAssignment.php @@ -0,0 +1,56 @@ + $this->object, + 'id' => $this->id, + 'group_id' => $this->groupId, + 'role' => $this->role->toArray(), + 'resource' => $this->resource->toArray(), + 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), + 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), + ]; + } +} diff --git a/lib/Resource/GroupRoleAssignmentList.php b/lib/Resource/GroupRoleAssignmentList.php new file mode 100644 index 00000000..d1191215 --- /dev/null +++ b/lib/Resource/GroupRoleAssignmentList.php @@ -0,0 +1,42 @@ + + */ + public array $data, + public ListMetadata $listMetadata, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + object: $data['object'] ?? 'list', + data: array_map(fn ($item) => GroupRoleAssignment::fromArray($item), $data['data']), + listMetadata: ListMetadata::fromArray($data['list_metadata']), + ); + } + + public function toArray(): array + { + return [ + 'object' => $this->object, + 'data' => array_map(fn ($item) => $item->toArray(), $this->data), + 'list_metadata' => $this->listMetadata->toArray(), + ]; + } +} diff --git a/lib/Resource/GroupRoleAssignmentResource.php b/lib/Resource/GroupRoleAssignmentResource.php new file mode 100644 index 00000000..ceb52ca7 --- /dev/null +++ b/lib/Resource/GroupRoleAssignmentResource.php @@ -0,0 +1,41 @@ + $this->id, + 'external_id' => $this->externalId, + 'resource_type_slug' => $this->resourceTypeSlug, + ]; + } +} diff --git a/lib/Resource/RefreshTokenSessionAuthenticateRequest.php b/lib/Resource/RefreshTokenSessionAuthenticateRequest.php index a8182380..acacf657 100644 --- a/lib/Resource/RefreshTokenSessionAuthenticateRequest.php +++ b/lib/Resource/RefreshTokenSessionAuthenticateRequest.php @@ -13,11 +13,11 @@ public function __construct( /** The client ID of the application. */ public string $clientId, - /** The client secret of the application. */ - public string $clientSecret, public string $grantType, /** The refresh token to exchange for new tokens. */ public string $refreshToken, + /** The client secret of the application. May be omitted by public clients that authenticate through other means, such as a PKCE `code_verifier`. */ + public ?string $clientSecret = null, /** The ID of the organization to scope the session to. */ public ?string $organizationId = null, /** The IP address of the user's request. */ @@ -33,9 +33,9 @@ public static function fromArray(array $data): self { return new self( clientId: $data['client_id'], - clientSecret: $data['client_secret'], grantType: $data['grant_type'] ?? 'refresh_token', refreshToken: $data['refresh_token'], + clientSecret: $data['client_secret'] ?? null, organizationId: $data['organization_id'] ?? null, ipAddress: $data['ip_address'] ?? null, deviceId: $data['device_id'] ?? null, @@ -47,9 +47,9 @@ public function toArray(): array { return [ 'client_id' => $this->clientId, - 'client_secret' => $this->clientSecret, 'grant_type' => $this->grantType, 'refresh_token' => $this->refreshToken, + 'client_secret' => $this->clientSecret, 'organization_id' => $this->organizationId, 'ip_address' => $this->ipAddress, 'device_id' => $this->deviceId, diff --git a/lib/Resource/ReplaceGroupRoleAssignmentEntry.php b/lib/Resource/ReplaceGroupRoleAssignmentEntry.php new file mode 100644 index 00000000..5e4886b5 --- /dev/null +++ b/lib/Resource/ReplaceGroupRoleAssignmentEntry.php @@ -0,0 +1,44 @@ + $this->roleSlug, + 'resource_id' => $this->resourceId, + 'resource_external_id' => $this->resourceExternalId, + 'resource_type_slug' => $this->resourceTypeSlug, + ]; + } +} diff --git a/lib/Resource/ReplaceGroupRoleAssignments.php b/lib/Resource/ReplaceGroupRoleAssignments.php new file mode 100644 index 00000000..07b6cf7a --- /dev/null +++ b/lib/Resource/ReplaceGroupRoleAssignments.php @@ -0,0 +1,35 @@ + + */ + public array $roleAssignments, + ) { + } + + public static function fromArray(array $data): self + { + return new self( + roleAssignments: array_map(fn ($item) => ReplaceGroupRoleAssignmentEntry::fromArray($item), $data['role_assignments']), + ); + } + + public function toArray(): array + { + return [ + 'role_assignments' => array_map(fn ($item) => $item->toArray(), $this->roleAssignments), + ]; + } +} diff --git a/lib/Resource/WidgetSessionTokenScopes.php b/lib/Resource/WidgetSessionTokenScopes.php index fd1767a8..dc33e7db 100644 --- a/lib/Resource/WidgetSessionTokenScopes.php +++ b/lib/Resource/WidgetSessionTokenScopes.php @@ -14,4 +14,5 @@ enum WidgetSessionTokenScopes: string case WidgetsApiKeysManage = 'widgets:api-keys:manage'; case WidgetsDsyncManage = 'widgets:dsync:manage'; case WidgetsAuditLogStreamingManage = 'widgets:audit-log-streaming:manage'; + case WidgetsPipesManage = 'widgets:pipes:manage'; } diff --git a/lib/Service/Authorization.php b/lib/Service/Authorization.php index 3deb7979..41a6a807 100644 --- a/lib/Service/Authorization.php +++ b/lib/Service/Authorization.php @@ -9,6 +9,8 @@ use WorkOS\Resource\AuthorizationCheck; use WorkOS\Resource\AuthorizationPermission; use WorkOS\Resource\AuthorizationResource; +use WorkOS\Resource\GroupRoleAssignment; +use WorkOS\Resource\GroupRoleAssignmentList; use WorkOS\Resource\Permission; use WorkOS\Resource\Role; use WorkOS\Resource\RoleList; @@ -22,6 +24,179 @@ public function __construct( ) { } + /** + * List role assignments for a group + * + * List all role assignments granted to a group. Each assignment represents a role granted to the group on a resource. + * @param string $groupId The ID of the group. + * @param string|null $before An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + * @param string|null $after An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + * @param int|null $limit Upper limit on the number of objects to return, between `1` and `100`. Defaults to 10. + * @param \WorkOS\Resource\PaginationOrder $order Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to "desc". + * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\GroupRoleAssignment> + * @throws \WorkOS\Exception\WorkOSException + */ + public function listGroupRoleAssignments( + string $groupId, + ?string $before = null, + ?string $after = null, + ?int $limit = null, + \WorkOS\Resource\PaginationOrder $order = \WorkOS\Resource\PaginationOrder::Desc, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\PaginatedResponse { + $query = array_filter([ + 'before' => $before, + 'after' => $after, + 'limit' => $limit, + 'order' => $order->value, + ], fn ($v) => $v !== null); + return $this->client->requestPage( + method: 'GET', + path: 'authorization/groups/' . rawurlencode($groupId) . '/role_assignments', + query: $query, + modelClass: GroupRoleAssignment::class, + options: $options, + ); + } + + /** + * Assign a role to a group + * + * Assign a role to a group on a specific resource. + * @param string $groupId The ID of the group. + * @param string $roleSlug The slug of the role to assign to the group. + * @param string|null $resourceId The ID of the resource. Omit along with the external-id fields to target the organization itself. + * @param string|null $resourceExternalId The external ID of the resource. + * @param string|null $resourceTypeSlug The resource type slug. + * @return \WorkOS\Resource\GroupRoleAssignment + * @throws \WorkOS\Exception\WorkOSException + */ + public function createGroupRoleAssignment( + string $groupId, + string $roleSlug, + ?string $resourceId = null, + ?string $resourceExternalId = null, + ?string $resourceTypeSlug = null, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\GroupRoleAssignment { + $body = array_filter([ + 'role_slug' => $roleSlug, + 'resource_id' => $resourceId, + 'resource_external_id' => $resourceExternalId, + 'resource_type_slug' => $resourceTypeSlug, + ], fn ($v) => $v !== null); + $response = $this->client->request( + method: 'POST', + path: 'authorization/groups/' . rawurlencode($groupId) . '/role_assignments', + body: $body, + options: $options, + ); + return GroupRoleAssignment::fromArray($response); + } + + /** + * Replace all role assignments for a group + * + * Replace all role assignments for a group with the provided list. Existing assignments not in the list will be removed. + * @param string $groupId The ID of the group. + * @param array<\WorkOS\Resource\ReplaceGroupRoleAssignmentEntry> $roleAssignments The list of role assignments that should exist for the group. All existing assignments will be replaced. + * @return \WorkOS\Resource\GroupRoleAssignmentList + * @throws \WorkOS\Exception\WorkOSException + */ + public function updateGroupRoleAssignments( + string $groupId, + array $roleAssignments, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\GroupRoleAssignmentList { + $body = [ + 'role_assignments' => $roleAssignments, + ]; + $response = $this->client->request( + method: 'PUT', + path: 'authorization/groups/' . rawurlencode($groupId) . '/role_assignments', + body: $body, + options: $options, + ); + return GroupRoleAssignmentList::fromArray($response); + } + + /** + * Remove group role assignments by criteria + * + * Remove role assignments from a group that match the provided criteria. Returns 404 when no matching active assignment is found. + * @param string $groupId The ID of the group. + * @param string $roleSlug The slug of the role to remove assignments for. + * @param string|null $resourceId The ID of the resource. Mutually exclusive with `resource_external_id` and `resource_type_slug`. + * @param string|null $resourceExternalId The external ID of the resource. + * @param string|null $resourceTypeSlug The resource type slug. + * @return void + * @throws \WorkOS\Exception\WorkOSException + */ + public function deleteGroupRoleAssignments( + string $groupId, + string $roleSlug, + ?string $resourceId = null, + ?string $resourceExternalId = null, + ?string $resourceTypeSlug = null, + ?\WorkOS\RequestOptions $options = null, + ): void { + $body = array_filter([ + 'role_slug' => $roleSlug, + 'resource_id' => $resourceId, + 'resource_external_id' => $resourceExternalId, + 'resource_type_slug' => $resourceTypeSlug, + ], fn ($v) => $v !== null); + $this->client->request( + method: 'DELETE', + path: 'authorization/groups/' . rawurlencode($groupId) . '/role_assignments', + body: $body, + options: $options, + ); + } + + /** + * Get a group role assignment + * + * Get a specific role assignment for a group by its ID. + * @param string $groupId The ID of the group. + * @param string $roleAssignmentId The ID of the group role assignment. + * @return \WorkOS\Resource\GroupRoleAssignment + * @throws \WorkOS\Exception\WorkOSException + */ + public function getGroupRoleAssignment( + string $groupId, + string $roleAssignmentId, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\GroupRoleAssignment { + $response = $this->client->request( + method: 'GET', + path: 'authorization/groups/' . rawurlencode($groupId) . '/role_assignments/' . rawurlencode($roleAssignmentId), + options: $options, + ); + return GroupRoleAssignment::fromArray($response); + } + + /** + * Remove a group role assignment + * + * Remove a specific role assignment from a group by its ID. + * @param string $groupId The ID of the group. + * @param string $roleAssignmentId The ID of the group role assignment to remove. + * @return void + * @throws \WorkOS\Exception\WorkOSException + */ + public function deleteGroupRoleAssignment( + string $groupId, + string $roleAssignmentId, + ?\WorkOS\RequestOptions $options = null, + ): void { + $this->client->request( + method: 'DELETE', + path: 'authorization/groups/' . rawurlencode($groupId) . '/role_assignments/' . rawurlencode($roleAssignmentId), + options: $options, + ); + } + /** * Check authorization * diff --git a/lib/Service/ClientApi.php b/lib/Service/ClientApi.php new file mode 100644 index 00000000..089a2a93 --- /dev/null +++ b/lib/Service/ClientApi.php @@ -0,0 +1,44 @@ + $organizationId, + 'user_id' => $userId, + ]; + $response = $this->client->request( + method: 'POST', + path: 'client/token', + body: $body, + options: $options, + ); + return ClientApiTokenResponse::fromArray($response); + } +} diff --git a/lib/Service/Pipes.php b/lib/Service/Pipes.php index 398d251d..0f9ec382 100644 --- a/lib/Service/Pipes.php +++ b/lib/Service/Pipes.php @@ -54,14 +54,14 @@ public function authorizeDataIntegration( * Get an access token for a connected account * * Fetches a valid OAuth access token for a user's connected account. WorkOS automatically handles token refresh, ensuring you always receive a valid, non-expired token. - * @param string $slug The identifier of the integration. + * @param string $provider The identifier of the integration. * @param string $userId A [User](https://workos.com/docs/reference/authkit/user) identifier. * @param string|null $organizationId An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. * @return \WorkOS\Resource\DataIntegrationAccessTokenResponse * @throws \WorkOS\Exception\WorkOSException */ - public function createDataIntegrationToken( - string $slug, + public function getAccessToken( + string $provider, string $userId, ?string $organizationId = null, ?\WorkOS\RequestOptions $options = null, @@ -72,7 +72,7 @@ public function createDataIntegrationToken( ], fn ($v) => $v !== null); $response = $this->client->request( method: 'POST', - path: 'data-integrations/' . rawurlencode($slug) . '/token', + path: 'data-integrations/' . rawurlencode($provider) . '/token', body: $body, options: $options, ); @@ -135,7 +135,7 @@ public function deleteUserConnectedAccount( } /** - * List providers + * List providers for a user * * Retrieves a list of available providers and the user's connection status for each. Returns all providers configured for your environment, along with the user's [connected account](https://workos.com/docs/reference/pipes/connected-account) information where applicable. * @param string $userId A [User](https://workos.com/docs/reference/authkit/user) identifier to list providers and connected accounts for. diff --git a/lib/Service/PipesProvider.php b/lib/Service/PipesProvider.php new file mode 100644 index 00000000..81621480 --- /dev/null +++ b/lib/Service/PipesProvider.php @@ -0,0 +1,75 @@ +client->request( + method: 'GET', + path: 'organizations/' . rawurlencode($organizationId) . '/data_integration_configurations', + options: $options, + ); + return DataIntegrationConfigurationListResponse::fromArray($response); + } + + /** + * Configure a provider for an organization + * + * Creates or updates an organization's provider configuration. Use this endpoint to enable or disable a provider, set custom OAuth scopes, or supply organization-managed OAuth credentials. + * @param string $organizationId An [Organization](https://workos.com/docs/reference/organization) identifier to configure the provider for. + * @param string $slug The slug identifier of the provider to configure (e.g., `github`, `slack`, `notion`). + * @param bool|null $enabled Whether the provider is enabled for the organization. + * @param array|null $scopes The OAuth scopes to request for the organization. Pass `null` to inherit the provider scopes. + * @param string|null $clientId The OAuth client ID of the organization's own application. Must be provided together with `client_secret`, and only for providers whose credentials are supplied by the organization. + * @param string|null $clientSecret The OAuth client secret of the organization's own application. Must be provided together with `client_id`. + * @return \WorkOS\Resource\DataIntegrationConfigurationResponse + * @throws \WorkOS\Exception\WorkOSException + */ + public function updateOrganizationDataIntegrationConfiguration( + string $organizationId, + string $slug, + ?bool $enabled = null, + ?array $scopes = null, + ?string $clientId = null, + ?string $clientSecret = null, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\DataIntegrationConfigurationResponse { + $body = array_filter([ + 'enabled' => $enabled, + 'scopes' => $scopes, + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + ], fn ($v) => $v !== null); + $response = $this->client->request( + method: 'PUT', + path: 'organizations/' . rawurlencode($organizationId) . '/data_integration_configurations/' . rawurlencode($slug), + body: $body, + options: $options, + ); + return DataIntegrationConfigurationResponse::fromArray($response); + } +} diff --git a/lib/Service/UserManagement.php b/lib/Service/UserManagement.php index cca8b4b2..43abfd91 100644 --- a/lib/Service/UserManagement.php +++ b/lib/Service/UserManagement.php @@ -889,7 +889,7 @@ public function verifyEmail( /** * Send verification email * - * Sends an email that contains a one-time code used to verify a user’s email address. + * Sends an email that contains a one-time code used to verify a user's email address. * @param string $id The ID of the user. * @return \WorkOS\Resource\SendVerificationEmailResponse * @throws \WorkOS\Exception\WorkOSException @@ -1237,7 +1237,7 @@ public function getMagicAuth( /** * Create a redirect URI * - * Creates a new redirect URI for an environment. + * Creates a new redirect URI for an application. * @param string $uri The redirect URI to create. * @return \WorkOS\Resource\RedirectUri * @throws \WorkOS\Exception\WorkOSException diff --git a/lib/WorkOS.php b/lib/WorkOS.php index 1f4b67b0..13a641de 100644 --- a/lib/WorkOS.php +++ b/lib/WorkOS.php @@ -10,6 +10,7 @@ use WorkOS\Service\ApiKeys; use WorkOS\Service\AuditLogs; use WorkOS\Service\Authorization; +use WorkOS\Service\ClientApi; use WorkOS\Service\Connect; use WorkOS\Service\DirectorySync; use WorkOS\Service\Events; @@ -20,6 +21,7 @@ use WorkOS\Service\OrganizationMembershipService; use WorkOS\Service\Organizations; use WorkOS\Service\Pipes; +use WorkOS\Service\PipesProvider; use WorkOS\Service\Radar; use WorkOS\Service\SSO; use WorkOS\Service\UserManagement; @@ -55,6 +57,7 @@ public static function setClientId(?string $id): void private ?Service\MultiFactorAuth $multiFactorAuth = null; private ?Service\Connect $connect = null; private ?Service\Authorization $authorization = null; + private ?Service\ClientApi $clientApi = null; private ?Service\SSO $sso = null; private ?Service\Pipes $pipes = null; private ?Service\DirectorySync $directorySync = null; @@ -63,6 +66,7 @@ public static function setClientId(?string $id): void private ?Service\OrganizationDomains $organizationDomains = null; private ?Service\Organizations $organizations = null; private ?Service\ApiKeys $apiKeys = null; + private ?Service\PipesProvider $pipesProvider = null; private ?Service\Groups $groups = null; private ?Service\AdminPortal $adminPortal = null; private ?Service\Radar $radar = null; @@ -102,6 +106,11 @@ public function authorization(): Authorization return $this->authorization ??= new Service\Authorization($this->httpClient); } + public function clientApi(): ClientApi + { + return $this->clientApi ??= new Service\ClientApi($this->httpClient); + } + public function sso(): SSO { return $this->sso ??= new Service\SSO($this->httpClient); @@ -142,6 +151,11 @@ public function apiKeys(): ApiKeys return $this->apiKeys ??= new Service\ApiKeys($this->httpClient); } + public function pipesProvider(): PipesProvider + { + return $this->pipesProvider ??= new Service\PipesProvider($this->httpClient); + } + public function groups(): Groups { return $this->groups ??= new Service\Groups($this->httpClient); diff --git a/tests/Fixtures/client_api_token.json b/tests/Fixtures/client_api_token.json new file mode 100644 index 00000000..f3a41edc --- /dev/null +++ b/tests/Fixtures/client_api_token.json @@ -0,0 +1,4 @@ +{ + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5" +} diff --git a/tests/Fixtures/client_api_token_response.json b/tests/Fixtures/client_api_token_response.json new file mode 100644 index 00000000..0a78c2fd --- /dev/null +++ b/tests/Fixtures/client_api_token_response.json @@ -0,0 +1,3 @@ +{ + "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InNlc3Npb24..." +} diff --git a/tests/Fixtures/configure_data_integration_body.json b/tests/Fixtures/configure_data_integration_body.json new file mode 100644 index 00000000..081379da --- /dev/null +++ b/tests/Fixtures/configure_data_integration_body.json @@ -0,0 +1,9 @@ +{ + "enabled": true, + "scopes": [ + "repo", + "user:email" + ], + "client_id": "client_01EHZNVPK3SFK441A1RGBFSHRT", + "client_secret": "••••••••" +} diff --git a/tests/Fixtures/connected_account.json b/tests/Fixtures/connected_account.json index ffc4db77..4190b4b9 100644 --- a/tests/Fixtures/connected_account.json +++ b/tests/Fixtures/connected_account.json @@ -9,5 +9,7 @@ ], "state": "connected", "created_at": "2024-01-16T14:20:00.000Z", - "updated_at": "2024-01-16T14:20:00.000Z" + "updated_at": "2024-01-16T14:20:00.000Z", + "auth_method": "oauth", + "api_key_last_4": null } diff --git a/tests/Fixtures/create_group_role_assignment.json b/tests/Fixtures/create_group_role_assignment.json new file mode 100644 index 00000000..5feb6cee --- /dev/null +++ b/tests/Fixtures/create_group_role_assignment.json @@ -0,0 +1,6 @@ +{ + "role_slug": "admin", + "resource_id": "authz_resource_01HXYZ123456789ABCDEFGH", + "resource_external_id": "proj-456", + "resource_type_slug": "project" +} diff --git a/tests/Fixtures/data_integration_configuration_list_response.json b/tests/Fixtures/data_integration_configuration_list_response.json new file mode 100644 index 00000000..c5b0efa4 --- /dev/null +++ b/tests/Fixtures/data_integration_configuration_list_response.json @@ -0,0 +1,26 @@ +{ + "object": "list", + "data": [ + { + "object": "data_integration_configuration", + "id": "data_integration_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "slug": "github", + "name": "GitHub", + "enabled": true, + "scopes": [ + "repo", + "user:email" + ], + "created_at": "2024-01-15T10:30:00.000Z", + "updated_at": "2024-01-15T10:30:00.000Z", + "credentials": { + "credentials_type": "organization", + "has_credentials": true, + "client_id": "client_01EHZNVPK3SFK441A1RGBFSHRT", + "client_secret_last_four": "1a2b", + "redirect_uri": "https://api.workos.com/data-integrations/github/dik_01EHZNVPK3SFK441A1RGBFSHRT/callback" + } + } + ] +} diff --git a/tests/Fixtures/data_integration_configuration_response.json b/tests/Fixtures/data_integration_configuration_response.json new file mode 100644 index 00000000..e9df017d --- /dev/null +++ b/tests/Fixtures/data_integration_configuration_response.json @@ -0,0 +1,21 @@ +{ + "object": "data_integration_configuration", + "id": "data_integration_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "slug": "github", + "name": "GitHub", + "enabled": true, + "scopes": [ + "repo", + "user:email" + ], + "created_at": "2024-01-15T10:30:00.000Z", + "updated_at": "2024-01-15T10:30:00.000Z", + "credentials": { + "credentials_type": "organization", + "has_credentials": true, + "client_id": "client_01EHZNVPK3SFK441A1RGBFSHRT", + "client_secret_last_four": "1a2b", + "redirect_uri": "https://api.workos.com/data-integrations/github/dik_01EHZNVPK3SFK441A1RGBFSHRT/callback" + } +} diff --git a/tests/Fixtures/data_integration_credentials.json b/tests/Fixtures/data_integration_credentials.json new file mode 100644 index 00000000..8de5997d --- /dev/null +++ b/tests/Fixtures/data_integration_credentials.json @@ -0,0 +1,7 @@ +{ + "credentials_type": "organization", + "has_credentials": true, + "client_id": "client_01EHZNVPK3SFK441A1RGBFSHRT", + "client_secret_last_four": "1a2b", + "redirect_uri": "https://api.workos.com/data-integrations/github/dik_01EHZNVPK3SFK441A1RGBFSHRT/callback" +} diff --git a/tests/Fixtures/data_integrations_list_response.json b/tests/Fixtures/data_integrations_list_response.json index f395c192..a94f34ef 100644 --- a/tests/Fixtures/data_integrations_list_response.json +++ b/tests/Fixtures/data_integrations_list_response.json @@ -13,6 +13,9 @@ "repo", "user:email" ], + "auth_methods": [ + "oauth" + ], "ownership": "userland_user", "created_at": "2024-01-15T10:30:00.000Z", "updated_at": "2024-01-15T10:30:00.000Z", @@ -29,6 +32,8 @@ "repo", "user:email" ], + "auth_method": "oauth", + "api_key_last_4": null, "state": "connected", "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", diff --git a/tests/Fixtures/data_integrations_list_response_data.json b/tests/Fixtures/data_integrations_list_response_data.json index e86e6be1..99243a80 100644 --- a/tests/Fixtures/data_integrations_list_response_data.json +++ b/tests/Fixtures/data_integrations_list_response_data.json @@ -32,6 +32,11 @@ "userlandUserId": "test_userlandUserId", "organizationId": "test_organizationId", "createdAt": "test_createdAt", - "updatedAt": "test_updatedAt" - } + "updatedAt": "test_updatedAt", + "auth_method": "oauth", + "api_key_last_4": null + }, + "auth_methods": [ + "oauth" + ] } diff --git a/tests/Fixtures/data_integrations_list_response_data_connected_account.json b/tests/Fixtures/data_integrations_list_response_data_connected_account.json index 3fb20392..f699f361 100644 --- a/tests/Fixtures/data_integrations_list_response_data_connected_account.json +++ b/tests/Fixtures/data_integrations_list_response_data_connected_account.json @@ -13,5 +13,7 @@ "userlandUserId": "test_userlandUserId", "organizationId": "test_organizationId", "createdAt": "test_createdAt", - "updatedAt": "test_updatedAt" + "updatedAt": "test_updatedAt", + "auth_method": "oauth", + "api_key_last_4": null } diff --git a/tests/Fixtures/delete_group_role_assignments_by_criteria.json b/tests/Fixtures/delete_group_role_assignments_by_criteria.json new file mode 100644 index 00000000..5feb6cee --- /dev/null +++ b/tests/Fixtures/delete_group_role_assignments_by_criteria.json @@ -0,0 +1,6 @@ +{ + "role_slug": "admin", + "resource_id": "authz_resource_01HXYZ123456789ABCDEFGH", + "resource_external_id": "proj-456", + "resource_type_slug": "project" +} diff --git a/tests/Fixtures/group_role_assignment.json b/tests/Fixtures/group_role_assignment.json new file mode 100644 index 00000000..1970509b --- /dev/null +++ b/tests/Fixtures/group_role_assignment.json @@ -0,0 +1,15 @@ +{ + "object": "group_role_assignment", + "id": "gra_01HXYZ123456789ABCDEFGH", + "group_id": "group_01HXYZ123456789ABCDEFGHIJ", + "role": { + "slug": "admin" + }, + "resource": { + "id": "authz_resource_01HXYZ123456789ABCDEFGH", + "external_id": "proj-456", + "resource_type_slug": "project" + }, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/tests/Fixtures/group_role_assignment_resource.json b/tests/Fixtures/group_role_assignment_resource.json new file mode 100644 index 00000000..53f9d508 --- /dev/null +++ b/tests/Fixtures/group_role_assignment_resource.json @@ -0,0 +1,5 @@ +{ + "id": "authz_resource_01HXYZ123456789ABCDEFGH", + "external_id": "proj-456", + "resource_type_slug": "project" +} diff --git a/tests/Fixtures/list_group_role_assignment.json b/tests/Fixtures/list_group_role_assignment.json new file mode 100644 index 00000000..19590059 --- /dev/null +++ b/tests/Fixtures/list_group_role_assignment.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "object": "group_role_assignment", + "id": "gra_01HXYZ123456789ABCDEFGH", + "group_id": "group_01HXYZ123456789ABCDEFGHIJ", + "role": { + "slug": "admin" + }, + "resource": { + "id": "authz_resource_01HXYZ123456789ABCDEFGH", + "external_id": "proj-456", + "resource_type_slug": "project" + }, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/tests/Fixtures/replace_group_role_assignment_entry.json b/tests/Fixtures/replace_group_role_assignment_entry.json new file mode 100644 index 00000000..5feb6cee --- /dev/null +++ b/tests/Fixtures/replace_group_role_assignment_entry.json @@ -0,0 +1,6 @@ +{ + "role_slug": "admin", + "resource_id": "authz_resource_01HXYZ123456789ABCDEFGH", + "resource_external_id": "proj-456", + "resource_type_slug": "project" +} diff --git a/tests/Fixtures/replace_group_role_assignments.json b/tests/Fixtures/replace_group_role_assignments.json new file mode 100644 index 00000000..d0dd7c1a --- /dev/null +++ b/tests/Fixtures/replace_group_role_assignments.json @@ -0,0 +1,10 @@ +{ + "role_assignments": [ + { + "role_slug": "admin", + "resource_id": "authz_resource_01HXYZ123456789ABCDEFGH", + "resource_external_id": "proj-456", + "resource_type_slug": "project" + } + ] +} diff --git a/tests/Service/AuthorizationTest.php b/tests/Service/AuthorizationTest.php index 10dc6f8b..c75d4793 100644 --- a/tests/Service/AuthorizationTest.php +++ b/tests/Service/AuthorizationTest.php @@ -13,6 +13,84 @@ class AuthorizationTest extends TestCase { use TestHelper; + public function testListGroupRoleAssignments(): void + { + $fixture = $this->loadFixture('list_group_role_assignment'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->authorization()->listGroupRoleAssignments('test_group_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\PaginationOrder::Normal); + $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('authorization/groups/test_group_id/role_assignments', $request->getUri()->getPath()); + parse_str($request->getUri()->getQuery(), $query); + $this->assertSame('test_value', $query['before']); + $this->assertSame('test_value', $query['after']); + $this->assertArrayHasKey('limit', $query); + $this->assertSame('normal', $query['order']); + } + + public function testCreateGroupRoleAssignment(): void + { + $fixture = $this->loadFixture('group_role_assignment'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->authorization()->createGroupRoleAssignment('test_group_id', roleSlug: 'test_value'); + $this->assertInstanceOf(\WorkOS\Resource\GroupRoleAssignment::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['group_id'], $result->groupId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('POST', $request->getMethod()); + $this->assertStringEndsWith('authorization/groups/test_group_id/role_assignments', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['role_slug']); + } + + public function testUpdateGroupRoleAssignments(): void + { + $fixture = $this->loadFixture('group_role_assignment_list'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->authorization()->updateGroupRoleAssignments('test_group_id', roleAssignments: []); + $this->assertInstanceOf(\WorkOS\Resource\GroupRoleAssignmentList::class, $result); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('PUT', $request->getMethod()); + $this->assertStringEndsWith('authorization/groups/test_group_id/role_assignments', $request->getUri()->getPath()); + } + + public function testDeleteGroupRoleAssignments(): void + { + $client = $this->createMockClient([['status' => 204]]); + $client->authorization()->deleteGroupRoleAssignments('test_group_id', roleSlug: 'test_value'); + $request = $this->getLastRequest(); + $this->assertSame('DELETE', $request->getMethod()); + $this->assertStringEndsWith('authorization/groups/test_group_id/role_assignments', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['role_slug']); + } + + public function testGetGroupRoleAssignment(): void + { + $fixture = $this->loadFixture('group_role_assignment'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->authorization()->getGroupRoleAssignment('test_group_id', 'test_role_assignment_id'); + $this->assertInstanceOf(\WorkOS\Resource\GroupRoleAssignment::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['group_id'], $result->groupId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('authorization/groups/test_group_id/role_assignments/test_role_assignment_id', $request->getUri()->getPath()); + } + + public function testDeleteGroupRoleAssignment(): void + { + $client = $this->createMockClient([['status' => 204]]); + $client->authorization()->deleteGroupRoleAssignment('test_group_id', 'test_role_assignment_id'); + $request = $this->getLastRequest(); + $this->assertSame('DELETE', $request->getMethod()); + $this->assertStringEndsWith('authorization/groups/test_group_id/role_assignments/test_role_assignment_id', $request->getUri()->getPath()); + } + public function testCheck(): void { $fixture = $this->loadFixture('authorization_check'); @@ -574,12 +652,12 @@ public function testDeletePermission(): void public function testPaginationBoundary(): void { - $fixture = $this->loadFixture('list_authorization_resource'); + $fixture = $this->loadFixture('list_group_role_assignment'); // Ensure cursors are null (first/last page boundary) $fixture['list_metadata']['before'] = null; $fixture['list_metadata']['after'] = null; $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listResourcesForMembership('test_organization_membership_id', parentResource: new \WorkOS\Service\ParentResourceById(id: 'test_value'), permissionSlug: 'test_value'); + $result = $client->authorization()->listGroupRoleAssignments('test_group_id'); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); // Verify cursors are null on boundary page $this->assertNull($result->listMetadata['before']); diff --git a/tests/Service/ClientApiTest.php b/tests/Service/ClientApiTest.php new file mode 100644 index 00000000..89b558e7 --- /dev/null +++ b/tests/Service/ClientApiTest.php @@ -0,0 +1,31 @@ +loadFixture('client_api_token_response'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->clientApi()->createToken(organizationId: 'test_value', userId: 'test_value'); + $this->assertInstanceOf(\WorkOS\Resource\ClientApiTokenResponse::class, $result); + $this->assertSame($fixture['token'], $result->token); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('POST', $request->getMethod()); + $this->assertStringEndsWith('client/token', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['organization_id']); + $this->assertSame('test_value', $body['user_id']); + } +} diff --git a/tests/Service/PipesProviderTest.php b/tests/Service/PipesProviderTest.php new file mode 100644 index 00000000..3ec58530 --- /dev/null +++ b/tests/Service/PipesProviderTest.php @@ -0,0 +1,41 @@ +loadFixture('data_integration_configuration_list_response'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->pipesProvider()->listOrganizationDataIntegrationConfigurations('test_organizationId'); + $this->assertInstanceOf(\WorkOS\Resource\DataIntegrationConfigurationListResponse::class, $result); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/data_integration_configurations', $request->getUri()->getPath()); + } + + public function testUpdateOrganizationDataIntegrationConfiguration(): void + { + $fixture = $this->loadFixture('data_integration_configuration_response'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->pipesProvider()->updateOrganizationDataIntegrationConfiguration('test_organizationId', 'test_slug'); + $this->assertInstanceOf(\WorkOS\Resource\DataIntegrationConfigurationResponse::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['organization_id'], $result->organizationId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('PUT', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/data_integration_configurations/test_slug', $request->getUri()->getPath()); + } +} diff --git a/tests/Service/PipesTest.php b/tests/Service/PipesTest.php index 9f155a74..b1968423 100644 --- a/tests/Service/PipesTest.php +++ b/tests/Service/PipesTest.php @@ -28,16 +28,16 @@ public function testAuthorizeDataIntegration(): void $this->assertSame('test_value', $body['user_id']); } - public function testCreateDataIntegrationToken(): void + public function testGetAccessToken(): void { $fixture = $this->loadFixture('data_integration_access_token_response'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->pipes()->createDataIntegrationToken('test_slug', userId: 'test_value'); + $result = $client->pipes()->getAccessToken('test_provider', userId: 'test_value'); $this->assertInstanceOf(\WorkOS\Resource\DataIntegrationAccessTokenResponse::class, $result); $this->assertIsArray($result->toArray()); $request = $this->getLastRequest(); $this->assertSame('POST', $request->getMethod()); - $this->assertStringEndsWith('data-integrations/test_slug/token', $request->getUri()->getPath()); + $this->assertStringEndsWith('data-integrations/test_provider/token', $request->getUri()->getPath()); $body = json_decode((string) $request->getBody(), true); $this->assertSame('test_value', $body['user_id']); }