mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
patch to put: OAuth API
This commit is contained in:
@@ -28,7 +28,7 @@ class Update extends Action
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Should be PUT
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/auth-methods/:methodId')
|
||||
->httpAlias('/v1/projects/:projectId/auth/:methodId')
|
||||
->desc('Update project auth method status')
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -108,7 +107,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -129,11 +128,11 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param('keyId', null, new Nullable(new Text(256, 0)), '\'Key ID\' of Apple OAuth2 app. For example: P4000000N8', optional: true)
|
||||
->param('teamId', null, new Nullable(new Text(256, 0)), '\'Team ID\' of Apple OAuth2 app. For example: D4000000R6', optional: true)
|
||||
->param('p8File', null, new Nullable(new Text(4096, 0)), 'Contents of the Apple OAuth2 app .p8 private key file. The secret key wrapped by the PEM markers is 200 characters long. For example: -----BEGIN PRIVATE KEY-----MIGTAg...jy2Xbna-----END PRIVATE KEY-----', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param('keyId', null, new Text(256, 0), '\'Key ID\' of Apple OAuth2 app. For example: P4000000N8')
|
||||
->param('teamId', null, new Text(256, 0), '\'Team ID\' of Apple OAuth2 app. For example: D4000000R6')
|
||||
->param('p8File', null, new Text(4096, 0), 'Contents of the Apple OAuth2 app .p8 private key file. The secret key wrapped by the PEM markers is 200 characters long. For example: -----BEGIN PRIVATE KEY-----MIGTAg...jy2Xbna-----END PRIVATE KEY-----')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -166,11 +165,11 @@ class Update extends Base
|
||||
* avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $serviceId,
|
||||
?string $keyId,
|
||||
?string $teamId,
|
||||
?string $p8File,
|
||||
?bool $enabled,
|
||||
string $serviceId,
|
||||
string $keyId,
|
||||
string $teamId,
|
||||
string $p8File,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -180,23 +179,11 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"p8": "...", "keyID": "...", "teamID": "..."}`
|
||||
// to match the shape Apple's OAuth2 adapter expects in getAppSecret().
|
||||
// Merge new values with what's already stored so that submitting only
|
||||
// some of the fields leaves the rest untouched.
|
||||
$encodedSecret = null;
|
||||
if (!\is_null($keyId) || !\is_null($teamId) || !\is_null($p8File)) {
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'p8' => $p8File ?? ($existing['p8'] ?? ''),
|
||||
'keyID' => $keyId ?? ($existing['keyID'] ?? ''),
|
||||
'teamID' => $teamId ?? ($existing['teamID'] ?? ''),
|
||||
]);
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'p8' => $p8File,
|
||||
'keyID' => $keyId,
|
||||
'teamID' => $teamId,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $serviceId, $encodedSecret, $enabled);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -82,7 +81,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -103,10 +102,10 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('endpoint', null, new Nullable(new Text(256, 0)), 'Domain of Auth0 instance. For example: example.us.auth0.com', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('endpoint', null, new Text(256, 0), 'Domain of Auth0 instance. For example: example.us.auth0.com')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -136,10 +135,10 @@ class Update extends Base
|
||||
* differently to avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $endpoint,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
string $endpoint,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -149,22 +148,10 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "auth0Domain": "..."}`
|
||||
// to match the shape Auth0's OAuth2 adapter expects (getAuth0Domain()).
|
||||
// Merge new values with existing storage so that submitting only one of
|
||||
// `clientSecret`/`endpoint` leaves the other untouched.
|
||||
$encodedSecret = null;
|
||||
if (!\is_null($clientSecret) || !\is_null($endpoint)) {
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'auth0Domain' => $endpoint ?? ($existing['auth0Domain'] ?? ''),
|
||||
]);
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret,
|
||||
'auth0Domain' => $endpoint,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -82,7 +81,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -103,10 +102,10 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('endpoint', null, new Nullable(new Text(256, 0)), 'Domain of Authentik instance. For example: example.authentik.com', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('endpoint', null, new Text(256, 0), 'Domain of Authentik instance. For example: example.authentik.com')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -136,10 +135,10 @@ class Update extends Base
|
||||
* differently to avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $endpoint,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
string $endpoint,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -149,18 +148,9 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "authentikDomain": "..."}`
|
||||
// to match the shape Authentik's OAuth2 adapter expects (getAuthentikDomain()).
|
||||
// The `endpoint` param is optional; if omitted, the existing stored endpoint is preserved.
|
||||
// `clientSecret` is optional; if omitted, the existing stored secret is preserved.
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'authentikDomain' => $endpoint ?? ($existing['authentikDomain'] ?? ''),
|
||||
'clientSecret' => $clientSecret,
|
||||
'authentikDomain' => $endpoint,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
@@ -15,7 +15,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
abstract class Base extends Action
|
||||
@@ -234,7 +233,7 @@ abstract class Base extends Action
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -255,9 +254,9 @@ abstract class Base extends Action
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -372,9 +371,9 @@ abstract class Base extends Action
|
||||
Document $project,
|
||||
Database $dbForPlatform,
|
||||
Authorization $authorization,
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?bool $enabled
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
bool $enabled
|
||||
): Document {
|
||||
$providerId = static::getProviderId();
|
||||
if (!(\in_array($providerId, \array_keys(Config::getParam('oAuthProviders'))))) {
|
||||
@@ -387,19 +386,11 @@ abstract class Base extends Action
|
||||
$appSecretKey = $providerId . 'Secret';
|
||||
$enabledKey = $providerId . 'Enabled';
|
||||
|
||||
if (!\is_null($clientId)) {
|
||||
$oAuthProviders[$appIdKey] = $clientId;
|
||||
}
|
||||
$oAuthProviders[$appIdKey] = $clientId;
|
||||
$oAuthProviders[$appSecretKey] = $clientSecret;
|
||||
$oAuthProviders[$enabledKey] = $enabled;
|
||||
|
||||
if (!\is_null($clientSecret)) {
|
||||
$oAuthProviders[$appSecretKey] = $clientSecret;
|
||||
}
|
||||
|
||||
if (!\is_null($enabled)) {
|
||||
$oAuthProviders[$enabledKey] = $enabled;
|
||||
}
|
||||
|
||||
if ($enabled === true || \is_null($enabled)) {
|
||||
if ($enabled === true) {
|
||||
try {
|
||||
if (empty($oAuthProviders[$appIdKey]) || empty($oAuthProviders[$appSecretKey])) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Client ID and Client Secret are required when enabling OAuth2 provider.');
|
||||
@@ -412,12 +403,8 @@ abstract class Base extends Action
|
||||
if (\method_exists($providerInstance, 'verifyCredentials')) {
|
||||
$providerInstance->verifyCredentials();
|
||||
}
|
||||
|
||||
$oAuthProviders[$enabledKey] = true;
|
||||
} catch (\Throwable $err) {
|
||||
if ($enabled === true) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Could not enable OAuth2 provider: ' . $err->getMessage());
|
||||
}
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Could not enable OAuth2 provider: ' . $err->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,9 +416,9 @@ abstract class Base extends Action
|
||||
}
|
||||
|
||||
public function action(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -82,7 +81,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -103,10 +102,10 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('endpoint', null, new Nullable(new Text(256, 0)), 'Domain of FusionAuth instance. For example: example.fusionauth.io', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('endpoint', null, new Text(256, 0), 'Domain of FusionAuth instance. For example: example.fusionauth.io')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -136,10 +135,10 @@ class Update extends Base
|
||||
* differently to avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $endpoint,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
string $endpoint,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -149,18 +148,9 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "fusionAuthDomain": "..."}`
|
||||
// to match the shape FusionAuth's OAuth2 adapter expects (getFusionAuthDomain()).
|
||||
// The `endpoint` param is optional; if omitted, the existing stored endpoint is preserved.
|
||||
// `clientSecret` is optional; if omitted, the existing stored secret is preserved.
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'fusionAuthDomain' => $endpoint ?? ($existing['fusionAuthDomain'] ?? ''),
|
||||
'clientSecret' => $clientSecret,
|
||||
'fusionAuthDomain' => $endpoint,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
|
||||
@@ -93,7 +92,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -114,10 +113,10 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('endpoint', null, new Nullable(new URL(allowEmpty: true)), 'Endpoint URL of self-hosted GitLab instance. For example: https://gitlab.com', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('endpoint', null, new URL(allowEmpty: true), 'Endpoint URL of self-hosted GitLab instance. For example: https://gitlab.com')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -147,10 +146,10 @@ class Update extends Base
|
||||
* differently to avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $applicationId,
|
||||
?string $secret,
|
||||
?string $endpoint,
|
||||
?bool $enabled,
|
||||
string $applicationId,
|
||||
string $secret,
|
||||
string $endpoint,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -160,22 +159,10 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "endpoint": "..."}`
|
||||
// so that the Gitlab OAuth2 adapter can extract the endpoint via getEndpoint().
|
||||
// Merge the new values with what's already stored so that submitting only
|
||||
// one of `secret`/`endpoint` leaves the other untouched.
|
||||
$encodedSecret = null;
|
||||
if (!\is_null($secret) || !\is_null($endpoint)) {
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $secret ?? ($existing['clientSecret'] ?? ''),
|
||||
'endpoint' => $endpoint ?? ($existing['endpoint'] ?? ''),
|
||||
]);
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $secret,
|
||||
'endpoint' => $endpoint,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $applicationId, $encodedSecret, $enabled);
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
@@ -85,7 +84,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -106,10 +105,10 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('prompt', null, new Nullable(new ArrayList(new WhiteList(['none', 'consent', 'select_account'], true), 3)), 'Array of Google OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don\'t display any authentication or consent screens. Must not be specified with other values. "consent" means: prompt the user for consent. "select_account" means: prompt the user to select an account.', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('prompt', null, new ArrayList(new WhiteList(['none', 'consent', 'select_account'], true), 3), 'Array of Google OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don\'t display any authentication or consent screens. Must not be specified with other values. "consent" means: prompt the user for consent. "select_account" means: prompt the user to select an account.')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -139,10 +138,10 @@ class Update extends Base
|
||||
* differently to avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?array $prompt,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
array $prompt,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -152,28 +151,17 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
if ($prompt !== null) {
|
||||
if (empty($prompt)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Prompt array cannot be empty.');
|
||||
}
|
||||
|
||||
if (\in_array('none', $prompt) && \count($prompt) > 1) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When "none" is used as a prompt value, it must be the only element in the array.');
|
||||
}
|
||||
if (empty($prompt)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Prompt array cannot be empty.');
|
||||
}
|
||||
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = $this->decodeStoredSecret($project);
|
||||
|
||||
// Backwards compatibility: secrets stored before the prompt feature
|
||||
// were saved as plain strings. Treat the raw value as clientSecret.
|
||||
if (!empty($storedRaw) && empty($existing)) {
|
||||
$existing = ['clientSecret' => $storedRaw];
|
||||
if (\in_array('none', $prompt) && \count($prompt) > 1) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When "none" is used as a prompt value, it must be the only element in the array.');
|
||||
}
|
||||
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'prompt' => $prompt ?? ($existing['prompt'] ?? ['consent']),
|
||||
'clientSecret' => $clientSecret,
|
||||
'prompt' => $prompt,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -88,7 +87,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -109,11 +108,11 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('endpoint', null, new Nullable(new Text(256, 0)), 'Domain of Keycloak instance. For example: keycloak.example.com', optional: true)
|
||||
->param('realmName', null, new Nullable(new Text(256, 0)), 'Keycloak realm name. For example: appwrite-realm', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('endpoint', null, new Text(256, 0), 'Domain of Keycloak instance. For example: keycloak.example.com')
|
||||
->param('realmName', null, new Text(256, 0), 'Keycloak realm name. For example: appwrite-realm')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -145,11 +144,11 @@ class Update extends Base
|
||||
* Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $endpoint,
|
||||
?string $realmName,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
string $endpoint,
|
||||
string $realmName,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -159,19 +158,10 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "keycloakDomain": "...", "keycloakRealm": "..."}`
|
||||
// to match the shape Keycloak's OAuth2 adapter expects (getKeycloakDomain(), getKeycloakRealm()).
|
||||
// The `endpoint` and `realmName` params are optional; if omitted, existing stored values are preserved.
|
||||
// `clientSecret` is optional; if omitted, the existing stored secret is preserved.
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'keycloakDomain' => $endpoint ?? ($existing['keycloakDomain'] ?? ''),
|
||||
'keycloakRealm' => $realmName ?? ($existing['keycloakRealm'] ?? ''),
|
||||
'clientSecret' => $clientSecret,
|
||||
'keycloakDomain' => $endpoint,
|
||||
'keycloakRealm' => $realmName,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
@@ -14,7 +14,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -92,7 +91,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -113,10 +112,10 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('tenant', null, new Nullable(new Text(256, 0)), 'Microsoft Entra ID tenant identifier. Use \'common\', \'organizations\', \'consumers\' or a specific tenant ID. For example: common', true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('tenant', null, new Text(256, 0), 'Microsoft Entra ID tenant identifier. Use \'common\', \'organizations\', \'consumers\' or a specific tenant ID. For example: common')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -146,10 +145,10 @@ class Update extends Base
|
||||
* differently to avoid an LSP-incompatible override of Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $applicationId,
|
||||
?string $applicationSecret,
|
||||
?string $tenant,
|
||||
?bool $enabled,
|
||||
string $applicationId,
|
||||
string $applicationSecret,
|
||||
string $tenant,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -159,18 +158,9 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "tenantID": "..."}`
|
||||
// to match the shape Microsoft's OAuth2 adapter expects (getTenantID()).
|
||||
// The `tenant` param is optional; if omitted, the existing stored tenant is preserved.
|
||||
// `applicationSecret` is optional; if omitted, the existing stored secret is preserved.
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $applicationSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'tenantID' => $tenant ?? ($existing['tenantID'] ?? ''),
|
||||
'clientSecret' => $applicationSecret,
|
||||
'tenantID' => $tenant,
|
||||
]);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $applicationId, $encodedSecret, $enabled);
|
||||
|
||||
@@ -15,7 +15,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
|
||||
@@ -102,7 +101,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -123,13 +122,13 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('wellKnownURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect well-known configuration URL. When provided, authorization, token, and user info endpoints can be discovered automatically. For example: https://myoauth.com/.well-known/openid-configuration', optional: true)
|
||||
->param('authorizationURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect authorization endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/authorize', optional: true)
|
||||
->param('tokenURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect token endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/token', optional: true, aliases: ['tokenUrl'])
|
||||
->param('userInfoURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect user info endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/userinfo', optional: true, aliases: ['userInfoUrl'])
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('wellKnownURL', null, new URL(allowEmpty: true), 'OpenID Connect well-known configuration URL. When provided, authorization, token, and user info endpoints can be discovered automatically. For example: https://myoauth.com/.well-known/openid-configuration')
|
||||
->param('authorizationURL', null, new URL(allowEmpty: true), 'OpenID Connect authorization endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/authorize')
|
||||
->param('tokenURL', null, new URL(allowEmpty: true), 'OpenID Connect token endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/token', aliases: ['tokenUrl'])
|
||||
->param('userInfoURL', null, new URL(allowEmpty: true), 'OpenID Connect user info endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/userinfo', aliases: ['userInfoUrl'])
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -170,13 +169,13 @@ class Update extends Base
|
||||
* that were configured previously.
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $wellKnownURL,
|
||||
?string $authorizationURL,
|
||||
?string $tokenURL,
|
||||
?string $userInfoURL,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
string $wellKnownURL,
|
||||
string $authorizationURL,
|
||||
string $tokenURL,
|
||||
string $userInfoURL,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -186,41 +185,25 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON
|
||||
// `{"clientSecret": "...", "wellKnownEndpoint": "...", "authorizationEndpoint": "...", "tokenEndpoint": "...", "userInfoEndpoint": "..."}`
|
||||
// so that the OIDC OAuth2 adapter can extract each endpoint individually.
|
||||
// Merge new values with what's already stored so that submitting only a
|
||||
// subset of fields leaves the others untouched.
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret,
|
||||
'wellKnownEndpoint' => $wellKnownURL,
|
||||
'authorizationEndpoint' => $authorizationURL,
|
||||
'tokenEndpoint' => $tokenURL,
|
||||
'userInfoEndpoint' => $userInfoURL,
|
||||
]);
|
||||
|
||||
$merged = [
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'wellKnownEndpoint' => $wellKnownURL ?? ($existing['wellKnownEndpoint'] ?? ''),
|
||||
'authorizationEndpoint' => $authorizationURL ?? ($existing['authorizationEndpoint'] ?? ''),
|
||||
'tokenEndpoint' => $tokenURL ?? ($existing['tokenEndpoint'] ?? ''),
|
||||
'userInfoEndpoint' => $userInfoURL ?? ($existing['userInfoEndpoint'] ?? ''),
|
||||
];
|
||||
|
||||
// When enabling, require either wellKnownEndpoint alone, or all three
|
||||
// discovery URLs (authorization, token, user info). Skip this check
|
||||
// when disabling or when leaving the enabled flag unchanged.
|
||||
if ($enabled === true) {
|
||||
$hasWellKnown = !empty($merged['wellKnownEndpoint']);
|
||||
$hasAllDiscovery = !empty($merged['authorizationEndpoint'])
|
||||
&& !empty($merged['tokenEndpoint'])
|
||||
&& !empty($merged['userInfoEndpoint']);
|
||||
$hasWellKnown = !empty($wellKnownURL);
|
||||
$hasAllDiscovery = !empty($authorizationURL)
|
||||
&& !empty($tokenURL)
|
||||
&& !empty($userInfoURL);
|
||||
|
||||
if (!$hasWellKnown && !$hasAllDiscovery) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Enabling OpenID Connect requires either wellKnownURL, or all of authorizationURL, tokenURL, and userInfoURL.');
|
||||
}
|
||||
}
|
||||
|
||||
$encodedSecret = \json_encode($merged);
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
// Reuse buildReadResponse to keep PATCH/GET shapes identical and
|
||||
|
||||
@@ -16,7 +16,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Domain as ValidatorDomain;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -90,7 +89,7 @@ class Update extends Base
|
||||
$providerLabel = static::getProviderLabel();
|
||||
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/oauth2/' . $providerId)
|
||||
->desc('Update project OAuth2 ' . $providerLabel)
|
||||
->groups(['api', 'project'])
|
||||
@@ -111,11 +110,11 @@ class Update extends Base
|
||||
)
|
||||
],
|
||||
))
|
||||
->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true)
|
||||
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
|
||||
->param('domain', null, new Nullable(new ValidatorDomain(allowEmpty: true)), 'Okta company domain. Required when enabling the provider. For example: trial-6400025.okta.com. Example of wrong value: trial-6400025-admin.okta.com, or https://trial-6400025.okta.com/', optional: true)
|
||||
->param('authorizationServerId', null, new Nullable(new Text(256, 0)), 'Custom Authorization Servers. Optional, can be left empty or unconfigured. For example: aus000000000000000h7z', optional: true)
|
||||
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
|
||||
->param(static::getClientIdParamName(), null, new Text(256, 0), static::getClientIdDescription())
|
||||
->param(static::getClientSecretParamName(), null, new Text(512, 0), static::getClientSecretDescription())
|
||||
->param('domain', null, new ValidatorDomain(allowEmpty: true), 'Okta company domain. Required when enabling the provider. For example: trial-6400025.okta.com. Example of wrong value: trial-6400025-admin.okta.com, or https://trial-6400025.okta.com/')
|
||||
->param('authorizationServerId', null, new Text(256, 0), 'Custom Authorization Servers. Optional, can be left empty or unconfigured. For example: aus000000000000000h7z')
|
||||
->param('enabled', false, new Boolean(), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
@@ -147,11 +146,11 @@ class Update extends Base
|
||||
* Base::action().
|
||||
*/
|
||||
public function handle(
|
||||
?string $clientId,
|
||||
?string $clientSecret,
|
||||
?string $domain,
|
||||
?string $authorizationServerId,
|
||||
?bool $enabled,
|
||||
string $clientId,
|
||||
string $clientSecret,
|
||||
string $domain,
|
||||
string $authorizationServerId,
|
||||
bool $enabled,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
Document $project,
|
||||
@@ -161,32 +160,14 @@ class Update extends Base
|
||||
$providerId = static::getProviderId();
|
||||
$queueForEvents->setParam('providerId', $providerId);
|
||||
|
||||
// The secret is stored as JSON `{"clientSecret": "...", "oktaDomain": "...", "authorizationServerId": "..."}`
|
||||
// to match the shape Okta's OAuth2 adapter expects.
|
||||
// Merge new values with existing storage so that submitting only some of
|
||||
// the parameters leaves the others untouched.
|
||||
$storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? '';
|
||||
$existing = [];
|
||||
if (!empty($storedRaw)) {
|
||||
$existing = \json_decode($storedRaw, true) ?: [];
|
||||
}
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret,
|
||||
'oktaDomain' => $domain,
|
||||
'authorizationServerId' => $authorizationServerId,
|
||||
]);
|
||||
|
||||
$encodedSecret = null;
|
||||
if (!\is_null($clientSecret) || !\is_null($domain) || !\is_null($authorizationServerId)) {
|
||||
$encodedSecret = \json_encode([
|
||||
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
|
||||
'oktaDomain' => $domain ?? ($existing['oktaDomain'] ?? ''),
|
||||
'authorizationServerId' => $authorizationServerId ?? ($existing['authorizationServerId'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
// Domain is required when enabling the provider, since Okta builds its
|
||||
// authorization, token and userinfo URLs from it.
|
||||
if ($enabled === true) {
|
||||
$effectiveDomain = $domain ?? ($existing['oktaDomain'] ?? '');
|
||||
if (empty($effectiveDomain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain is required when enabling Okta OAuth2 provider.');
|
||||
}
|
||||
if ($enabled === true && empty($domain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain is required when enabling Okta OAuth2 provider.');
|
||||
}
|
||||
|
||||
$project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled);
|
||||
|
||||
@@ -30,7 +30,7 @@ class Update extends Action
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Should be PUT
|
||||
$this->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) // Behaves as PUT
|
||||
->setHttpPath('/v1/project/templates/email')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email')
|
||||
->httpAlias('/v1/projects/:projectId/templates/email/:templateId/:locale')
|
||||
|
||||
Reference in New Issue
Block a user