Merge pull request #362 from appwrite/apple-oauth
Apple OAuth Adapter ⭐️
This commit is contained in:
commit
093bcad4f2
12 changed files with 327 additions and 52 deletions
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
- New OAuth adapter for sign-in with Apple
|
||||||
- New route in Locale API to fetch list of languages
|
- New route in Locale API to fetch list of languages
|
||||||
|
|
||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
|
|
@ -104,6 +104,7 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
||||||
|
|
||||||
if(!$originValidator->isValid($origin)
|
if(!$originValidator->isValid($origin)
|
||||||
&& in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
|
&& in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
|
||||||
|
&& $route->getLabel('origin', false) !== '*'
|
||||||
&& empty($request->getHeader('X-Appwrite-Key', ''))) {
|
&& empty($request->getHeader('X-Appwrite-Key', ''))) {
|
||||||
throw new Exception($originValidator->getDescription(), 403);
|
throw new Exception($originValidator->getDescription(), 403);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,138 +5,182 @@ return [
|
||||||
'developers' => 'https://developer.atlassian.com/bitbucket',
|
'developers' => 'https://developer.atlassian.com/bitbucket',
|
||||||
'icon' => 'icon-bitbucket',
|
'icon' => 'icon-bitbucket',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'facebook' => [
|
'facebook' => [
|
||||||
'developers' => 'https://developers.facebook.com/',
|
'developers' => 'https://developers.facebook.com/',
|
||||||
'icon' => 'icon-facebook',
|
'icon' => 'icon-facebook',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'github' => [
|
'github' => [
|
||||||
'developers' => 'https://developer.github.com/',
|
'developers' => 'https://developer.github.com/',
|
||||||
'icon' => 'icon-github-circled',
|
'icon' => 'icon-github-circled',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'gitlab' => [
|
'gitlab' => [
|
||||||
'developers' => 'https://docs.gitlab.com/ee/api/',
|
'developers' => 'https://docs.gitlab.com/ee/api/',
|
||||||
'icon' => 'icon-gitlab',
|
'icon' => 'icon-gitlab',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'google' => [
|
'google' => [
|
||||||
'developers' => 'https://developers.google.com/',
|
'developers' => 'https://developers.google.com/',
|
||||||
'icon' => 'icon-google',
|
'icon' => 'icon-google',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
// 'instagram' => [
|
// 'instagram' => [
|
||||||
// 'developers' => 'https://www.instagram.com/developer/',
|
// 'developers' => 'https://www.instagram.com/developer/',
|
||||||
// 'icon' => 'icon-instagram',
|
// 'icon' => 'icon-instagram',
|
||||||
// 'enabled' => false,
|
// 'enabled' => false,
|
||||||
|
// 'beta' => false,
|
||||||
// 'mock' => false,
|
// 'mock' => false,
|
||||||
// ],
|
// ],
|
||||||
'microsoft' => [
|
'microsoft' => [
|
||||||
'developers' => 'https://developer.microsoft.com/en-us/',
|
'developers' => 'https://developer.microsoft.com/en-us/',
|
||||||
'icon' => 'icon-windows',
|
'icon' => 'icon-windows',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
// 'twitter' => [
|
// 'twitter' => [
|
||||||
// 'developers' => 'https://developer.twitter.com/',
|
// 'developers' => 'https://developer.twitter.com/',
|
||||||
// 'icon' => 'icon-twitter',
|
// 'icon' => 'icon-twitter',
|
||||||
// 'enabled' => false,
|
// 'enabled' => false,
|
||||||
|
// 'beta' => false,
|
||||||
// 'mock' => false,
|
// 'mock' => false,
|
||||||
// ],
|
// ],
|
||||||
'linkedin' => [
|
'linkedin' => [
|
||||||
'developers' => 'https://developer.linkedin.com/',
|
'developers' => 'https://developer.linkedin.com/',
|
||||||
'icon' => 'icon-linkedin',
|
'icon' => 'icon-linkedin',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'slack' => [
|
'slack' => [
|
||||||
'developers' => 'https://api.slack.com/',
|
'developers' => 'https://api.slack.com/',
|
||||||
'icon' => 'icon-slack',
|
'icon' => 'icon-slack',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'dropbox' => [
|
'dropbox' => [
|
||||||
'developers' => 'https://www.dropbox.com/developers/documentation',
|
'developers' => 'https://www.dropbox.com/developers/documentation',
|
||||||
'icon' => 'icon-dropbox',
|
'icon' => 'icon-dropbox',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'salesforce' => [
|
'salesforce' => [
|
||||||
'developers' => 'https://developer.salesforce.com/docs/',
|
'developers' => 'https://developer.salesforce.com/docs/',
|
||||||
'icon' => 'icon-salesforce',
|
'icon' => 'icon-salesforce',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
|
'mock' => false,
|
||||||
|
],
|
||||||
|
'apple' => [
|
||||||
|
'developers' => 'https://developer.apple.com/',
|
||||||
|
'icon' => 'icon-apple',
|
||||||
|
'enabled' => true,
|
||||||
|
'form' => 'apple.phtml', // Perperation for adding ability to customized OAuth UI forms, currently handled hardcoded.
|
||||||
|
'beta' => true,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
// 'apple' => [
|
|
||||||
// 'developers' => 'https://developer.apple.com/',
|
|
||||||
// 'icon' => 'icon-apple',
|
|
||||||
// 'enabled' => false,
|
|
||||||
// 'mock' => false,
|
|
||||||
// ],
|
|
||||||
'amazon' => [
|
'amazon' => [
|
||||||
'developers' => 'https://developer.amazon.com/apps-and-games/services-and-apis',
|
'developers' => 'https://developer.amazon.com/apps-and-games/services-and-apis',
|
||||||
'icon' => 'icon-amazon',
|
'icon' => 'icon-amazon',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'vk' => [
|
'vk' => [
|
||||||
'developers' => 'https://vk.com/dev',
|
'developers' => 'https://vk.com/dev',
|
||||||
'icon' => 'icon-vk',
|
'icon' => 'icon-vk',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'discord' => [
|
'discord' => [
|
||||||
'developers' => 'https://discordapp.com/developers/docs/topics/oauth2',
|
'developers' => 'https://discordapp.com/developers/docs/topics/oauth2',
|
||||||
'icon' => 'icon-discord',
|
'icon' => 'icon-discord',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'twitch' => [
|
'twitch' => [
|
||||||
'developers' => 'https://dev.twitch.tv/docs/authentication',
|
'developers' => 'https://dev.twitch.tv/docs/authentication',
|
||||||
'icon' => 'icon-twitch',
|
'icon' => 'icon-twitch',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'spotify' => [
|
'spotify' => [
|
||||||
'developers' => 'https://developer.spotify.com/documentation/general/guides/authorization-guide/',
|
'developers' => 'https://developer.spotify.com/documentation/general/guides/authorization-guide/',
|
||||||
'icon' => 'icon-spotify',
|
'icon' => 'icon-spotify',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'yahoo' => [
|
'yahoo' => [
|
||||||
'developers' => 'https://developer.yahoo.com/oauth2/guide/flows_authcode/',
|
'developers' => 'https://developer.yahoo.com/oauth2/guide/flows_authcode/',
|
||||||
'icon' => 'icon-yahoo',
|
'icon' => 'icon-yahoo',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'yandex' => [
|
'yandex' => [
|
||||||
'developers' => 'https://tech.yandex.com/oauth/',
|
'developers' => 'https://tech.yandex.com/oauth/',
|
||||||
'icon' => 'icon-yandex',
|
'icon' => 'icon-yandex',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
'twitter' => [
|
'twitter' => [
|
||||||
'developers' => 'https://developer.twitter.com/',
|
'developers' => 'https://developer.twitter.com/',
|
||||||
'icon' => 'icon-twitter',
|
'icon' => 'icon-twitter',
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false
|
'mock' => false
|
||||||
],
|
],
|
||||||
'paypal' => [
|
'paypal' => [
|
||||||
'developers' => 'https://developer.paypal.com/docs/api/overview/',
|
'developers' => 'https://developer.paypal.com/docs/api/overview/',
|
||||||
'icon' => 'icon-paypal',
|
'icon' => 'icon-paypal',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false
|
'mock' => false
|
||||||
],
|
],
|
||||||
'bitly' => [
|
'bitly' => [
|
||||||
'developers' => 'https://dev.bitly.com/v4_documentation.html',
|
'developers' => 'https://dev.bitly.com/v4_documentation.html',
|
||||||
'icon' => 'icon-bitly',
|
'icon' => 'icon-bitly',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => false
|
'mock' => false
|
||||||
],
|
],
|
||||||
// Keep Last
|
// Keep Last
|
||||||
|
@ -144,6 +188,8 @@ return [
|
||||||
'developers' => 'https://appwrite.io',
|
'developers' => 'https://appwrite.io',
|
||||||
'icon' => 'icon-appwrite',
|
'icon' => 'icon-appwrite',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
|
'form' => false,
|
||||||
|
'beta' => false,
|
||||||
'mock' => true,
|
'mock' => true,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
|
@ -308,6 +308,29 @@ $utopia->get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$utopia->post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||||
|
->desc('OAuth2 Callback')
|
||||||
|
->label('error', __DIR__.'/../../views/general/error.phtml')
|
||||||
|
->label('scope', 'public')
|
||||||
|
->label('origin', '*')
|
||||||
|
->label('docs', false)
|
||||||
|
->param('projectId', '', function () { return new Text(1024); }, 'Project unique ID.')
|
||||||
|
->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
|
||||||
|
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
|
||||||
|
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
|
||||||
|
->action(
|
||||||
|
function ($projectId, $provider, $code, $state) use ($response) {
|
||||||
|
$domain = Config::getParam('domain');
|
||||||
|
$protocol = Config::getParam('protocol');
|
||||||
|
|
||||||
|
$response
|
||||||
|
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||||
|
->addHeader('Pragma', 'no-cache')
|
||||||
|
->redirect($protocol.'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
|
||||||
|
.http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
|
$utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
->desc('OAuth2 Redirect')
|
->desc('OAuth2 Redirect')
|
||||||
->label('error', __DIR__.'/../../views/general/error.phtml')
|
->label('error', __DIR__.'/../../views/general/error.phtml')
|
||||||
|
@ -361,6 +384,7 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||||
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
|
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
|
||||||
throw new Exception('Invalid redirect URL for failure login', 400);
|
throw new Exception('Invalid redirect URL for failure login', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$state['failure'] = null;
|
$state['failure'] = null;
|
||||||
$accessToken = $oauth2->getAccessToken($code);
|
$accessToken = $oauth2->getAccessToken($code);
|
||||||
|
|
||||||
|
|
|
@ -361,7 +361,7 @@ $utopia->patch('/v1/projects/:projectId/oauth2')
|
||||||
->param('projectId', '', function () { return new UID(); }, 'Project unique ID.')
|
->param('projectId', '', function () { return new UID(); }, 'Project unique ID.')
|
||||||
->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'Provider Name', false)
|
->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'Provider Name', false)
|
||||||
->param('appId', '', function () { return new Text(256); }, 'Provider app ID.', true)
|
->param('appId', '', function () { return new Text(256); }, 'Provider app ID.', true)
|
||||||
->param('secret', '', function () { return new text(256); }, 'Provider secret key.', true)
|
->param('secret', '', function () { return new text(512); }, 'Provider secret key.', true)
|
||||||
->action(
|
->action(
|
||||||
function ($projectId, $provider, $appId, $secret) use ($request, $response, $consoleDB) {
|
function ($projectId, $provider, $appId, $secret) use ($request, $response, $consoleDB) {
|
||||||
$project = $consoleDB->getDocument($projectId);
|
$project = $consoleDB->getDocument($projectId);
|
||||||
|
|
|
@ -337,12 +337,14 @@ $providers = $this->getParam('providers', []);
|
||||||
<?php foreach ($providers as $provider => $data):
|
<?php foreach ($providers as $provider => $data):
|
||||||
if (isset($data['enabled']) && !$data['enabled']) { continue; }
|
if (isset($data['enabled']) && !$data['enabled']) { continue; }
|
||||||
if (isset($data['mock']) && $data['mock']) { continue; }
|
if (isset($data['mock']) && $data['mock']) { continue; }
|
||||||
|
$form = (isset($data['form'])) ? $data['form'] : false;
|
||||||
|
$beta = (isset($data['beta'])) ? $data['beta'] : false;
|
||||||
?>
|
?>
|
||||||
<li class="clear <?php echo (isset($data['enabled']) && !$data['enabled']) ? 'dev-feature' : ''; ?>">
|
<li class="clear <?php echo (isset($data['enabled']) && !$data['enabled']) ? 'dev-feature' : ''; ?>">
|
||||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
|
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
|
||||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||||
|
|
||||||
<h1><?php echo ucfirst($provider); ?> OAuth2 Settings</h1>
|
<h1><?php echo $this->escape(ucfirst($provider)); ?> OAuth2 Settings</h1>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
data-analytics-event="submit"
|
data-analytics-event="submit"
|
||||||
|
@ -358,23 +360,30 @@ $providers = $this->getParam('providers', []);
|
||||||
data-failure="alert"
|
data-failure="alert"
|
||||||
data-failure-param-alert-text="Failed to update project OAuth2 settings"
|
data-failure-param-alert-text="Failed to update project OAuth2 settings"
|
||||||
data-failure-param-alert-classname="error">
|
data-failure-param-alert-classname="error">
|
||||||
<input name="provider" id="provider<?php echo ucfirst($provider); ?>" type="hidden" autocomplete="off" value="<?php echo $provider; ?>">
|
<input name="provider" id="provider<?php echo $this->escape(ucfirst($provider)); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($provider); ?>">
|
||||||
|
|
||||||
<label for="oauth2<?php echo ucfirst($provider); ?>Appid">App ID</label>
|
<?php if(!$form): ?>
|
||||||
<input name="appId" id="oauth2<?php echo ucfirst($provider); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Appid}}">
|
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">App ID</label>
|
||||||
|
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid}}">
|
||||||
|
|
||||||
|
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
|
||||||
|
<input name="secret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
|
||||||
|
<?php else: ?>
|
||||||
|
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Bundle ID <span class="tooltip" data-tooltip="Attribute internal display name"><i class="icon-info-circled"></i></span></label>
|
||||||
|
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="com.company.appname" />
|
||||||
|
|
||||||
<label for="oauth2<?php echo ucfirst($provider); ?>Secret">App Secret</label>
|
<input name="secret" data-forms-oauth-apple id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="hidden" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||||
<input name="secret" id="oauth2<?php echo ucfirst($provider); ?>Secret" type="text" autocomplete="off" data-ls-bind="{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Secret}}">
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="info row thin margin-bottom margin-top">
|
<div class="info row thin margin-bottom margin-top">
|
||||||
<div class="col span-1">
|
<div class="col span-1">
|
||||||
<i class="icon-info-circled text-sign"></i>
|
<i class="icon-info-circled text-sign"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="col span-11">
|
<div class="col span-11">
|
||||||
<p>To complete set up, add this OAuth2 redirect URI to your <?php echo ucfirst($provider); ?> app configuration.</p>
|
<p>To complete set up, add this OAuth2 redirect URI to your <?php echo $this->escape(ucfirst($provider)); ?> app configuration.</p>
|
||||||
|
|
||||||
<div class="input-copy">
|
<div class="input-copy">
|
||||||
<input data-forms-copy type="text" disabled data-ls-bind="{{env.PROTOCOL}}://{{env.DOMAIN}}/v1/account/sessions/oauth2/callback/<?php echo $provider; ?>/{{router.params.project}}" class="margin-bottom-no" />
|
<input data-forms-copy type="text" disabled data-ls-bind="{{env.PROTOCOL}}://{{env.DOMAIN}}/v1/account/sessions/oauth2/callback/<?php echo $this->escape($provider); ?>/{{router.params.project}}" class="margin-bottom-no" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -384,19 +393,19 @@ $providers = $this->getParam('providers', []);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span data-ls-if="
|
<span data-ls-if="
|
||||||
{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Appid}} &&
|
{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid}} &&
|
||||||
{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Secret}}">
|
{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
|
||||||
<button class="switch on pull-end margin-top-small" data-ls-ui-trigger="provider-update-<?php echo $provider; ?>"></button>
|
<button class="switch on pull-end margin-top-small" data-ls-ui-trigger="provider-update-<?php echo $provider; ?>"></button>
|
||||||
</span>
|
</span>
|
||||||
<span data-ls-if="
|
<span data-ls-if="
|
||||||
!{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Appid}} ||
|
!{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid}} ||
|
||||||
!{{console-project.usersOauth2<?php echo ucfirst($provider); ?>Secret}}">
|
!{{console-project.usersOauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
|
||||||
<button class="switch pull-end margin-top-small" data-ls-ui-trigger="provider-update-<?php echo $provider; ?>"></button>
|
<button class="switch pull-end margin-top-small" data-ls-ui-trigger="provider-update-<?php echo $this->escape($provider); ?>"></button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<img src="/images/oauth2/<?php echo strtolower($provider); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo ucfirst($provider); ?> Logo" class="pull-start provider margin-end" />
|
<img src="/images/oauth2/<?php echo $this->escape(strtolower($provider)); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo $this->escape(ucfirst($provider)); ?> Logo" class="pull-start provider margin-end" />
|
||||||
<span>
|
<span>
|
||||||
<?php echo ucfirst($provider); ?>
|
<?php echo ucfirst($provider); ?> <?php if($beta): ?>(beta)<?php endif; ?>
|
||||||
</span>
|
</span>
|
||||||
<p class="margin-bottom-no margin-top-small text-one-liner">
|
<p class="margin-bottom-no margin-top-small text-one-liner">
|
||||||
<a href="<?php echo $data['developers']; ?>" target="_blank" rel="noopener">OAuth2 Developer Docs<i class="icon-link-ext"></i></a>
|
<a href="<?php echo $data['developers']; ?>" target="_blank" rel="noopener">OAuth2 Developer Docs<i class="icon-link-ext"></i></a>
|
||||||
|
|
|
@ -47,6 +47,7 @@ const configApp = {
|
||||||
'public/scripts/views/forms/move-down.js',
|
'public/scripts/views/forms/move-down.js',
|
||||||
'public/scripts/views/forms/move-up.js',
|
'public/scripts/views/forms/move-up.js',
|
||||||
'public/scripts/views/forms/nav.js',
|
'public/scripts/views/forms/nav.js',
|
||||||
|
'public/scripts/views/forms/oauth-apple.js',
|
||||||
'public/scripts/views/forms/password-meter.js',
|
'public/scripts/views/forms/password-meter.js',
|
||||||
'public/scripts/views/forms/pell.js',
|
'public/scripts/views/forms/pell.js',
|
||||||
'public/scripts/views/forms/remove.js',
|
'public/scripts/views/forms/remove.js',
|
||||||
|
|
8
public/dist/scripts/app-all.js
vendored
8
public/dist/scripts/app-all.js
vendored
|
@ -2568,7 +2568,7 @@ return"< 1s";}).add("markdown",function($value,markdown){return markdown.render(
|
||||||
let thresh=1000;if(Math.abs($value)<thresh){return $value+" B";}
|
let thresh=1000;if(Math.abs($value)<thresh){return $value+" B";}
|
||||||
let units=["kB","MB","GB","TB","PB","EB","ZB","YB"];let u=-1;do{$value/=thresh;++u;}while(Math.abs($value)>=thresh&&u<units.length-1);return($value.toFixed(1)+'<span class="text-size-small unit">'+
|
let units=["kB","MB","GB","TB","PB","EB","ZB","YB"];let u=-1;do{$value/=thresh;++u;}while(Math.abs($value)>=thresh&&u<units.length-1);return($value.toFixed(1)+'<span class="text-size-small unit">'+
|
||||||
units[u]+"</span>");}).add("statsTotal",function($value){if(!$value){return 0;}
|
units[u]+"</span>");}).add("statsTotal",function($value){if(!$value){return 0;}
|
||||||
$value=abbreviate($value,1,false,false);return $value==="0"?"N/A":$value;}).add("isEmpty",function($value){return(!!$value);}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
|
$value=abbreviate($value,0,false,false);return $value==="0"?"N/A":$value;}).add("isEmpty",function($value){return(!!$value);}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
|
||||||
return result.length;}).add("documentAction",function(container){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&!document.$id){return'database.createDocument';}
|
return result.length;}).add("documentAction",function(container){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&!document.$id){return'database.createDocument';}
|
||||||
return'database.updateDocument';}).add("documentSuccess",function(container){let document=container.get('project-document');if(document&&!document.$id){return',redirect';}
|
return'database.updateDocument';}).add("documentSuccess",function(container){let document=container.get('project-document');if(document&&!document.$id){return',redirect';}
|
||||||
return'';}).add("firstElement",function($value){if($value&&$value[0]){return $value[0];}
|
return'';}).add("firstElement",function($value){if($value&&$value[0]){return $value[0];}
|
||||||
|
@ -2640,7 +2640,11 @@ list["filters-"+filter.key]=params[key][i];}}}}
|
||||||
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
||||||
element.setAttribute("data-event","none");}
|
element.setAttribute("data-event","none");}
|
||||||
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
||||||
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
|
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-apple",controller:function(element){let container=document.createElement("div");let row=document.createElement("div");let col1=document.createElement("div");let col2=document.createElement("div");let keyID=document.createElement("input");let keyLabel=document.createElement("label");let teamID=document.createElement("input");let teamLabel=document.createElement("label");let p8=document.createElement("textarea");let p8Label=document.createElement("label");keyLabel.textContent='Key ID';teamLabel.textContent='Team ID';p8Label.textContent='P8 File';row.classList.add('row');row.classList.add('thin');container.appendChild(row);container.appendChild(p8Label);container.appendChild(p8);row.appendChild(col1);row.appendChild(col2);col1.classList.add('col');col1.classList.add('span-6');col1.appendChild(keyLabel);col1.appendChild(keyID);col2.classList.add('col');col2.classList.add('span-6');col2.appendChild(teamLabel);col2.appendChild(teamID);keyID.type='text';keyID.placeholder='SHAB13ROFN';teamID.type='text';teamID.placeholder='ELA2CD3AED';p8.accept='.p8';p8.classList.add('margin-bottom-no');element.parentNode.insertBefore(container,element.nextSibling);element.addEventListener('change',sync);keyID.addEventListener('change',update);teamID.addEventListener('change',update);p8.addEventListener('change',update);function update(){let json={};json.keyID=keyID.value;json.teamID=teamID.value;json.p8=p8.value;element.value=JSON.stringify(json);}
|
||||||
|
function sync(){console.log('sync');if(!element.value){return;}
|
||||||
|
let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');}
|
||||||
|
teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';}
|
||||||
|
sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
|
||||||
var variations={digits:/\d/.test(password),lower:/[a-z]/.test(password),upper:/[A-Z]/.test(password),nonWords:/\W/.test(password)};var variationCount=0;for(var check in variations){if(variations.hasOwnProperty(check)){variationCount+=variations[check]===true?1:0;}}
|
var variations={digits:/\d/.test(password),lower:/[a-z]/.test(password),upper:/[A-Z]/.test(password),nonWords:/\W/.test(password)};var variationCount=0;for(var check in variations){if(variations.hasOwnProperty(check)){variationCount+=variations[check]===true?1:0;}}
|
||||||
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;}
|
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;}
|
||||||
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
|
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
|
||||||
|
|
8
public/dist/scripts/app.js
vendored
8
public/dist/scripts/app.js
vendored
|
@ -284,7 +284,7 @@ return"< 1s";}).add("markdown",function($value,markdown){return markdown.render(
|
||||||
let thresh=1000;if(Math.abs($value)<thresh){return $value+" B";}
|
let thresh=1000;if(Math.abs($value)<thresh){return $value+" B";}
|
||||||
let units=["kB","MB","GB","TB","PB","EB","ZB","YB"];let u=-1;do{$value/=thresh;++u;}while(Math.abs($value)>=thresh&&u<units.length-1);return($value.toFixed(1)+'<span class="text-size-small unit">'+
|
let units=["kB","MB","GB","TB","PB","EB","ZB","YB"];let u=-1;do{$value/=thresh;++u;}while(Math.abs($value)>=thresh&&u<units.length-1);return($value.toFixed(1)+'<span class="text-size-small unit">'+
|
||||||
units[u]+"</span>");}).add("statsTotal",function($value){if(!$value){return 0;}
|
units[u]+"</span>");}).add("statsTotal",function($value){if(!$value){return 0;}
|
||||||
$value=abbreviate($value,1,false,false);return $value==="0"?"N/A":$value;}).add("isEmpty",function($value){return(!!$value);}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
|
$value=abbreviate($value,0,false,false);return $value==="0"?"N/A":$value;}).add("isEmpty",function($value){return(!!$value);}).add("isEmptyObject",function($value){return((Object.keys($value).length===0&&$value.constructor===Object)||$value.length===0)}).add("activeDomainsCount",function($value){let result=[];if(Array.isArray($value)){result=$value.filter(function(node){return(node.verification&&node.certificateId);});}
|
||||||
return result.length;}).add("documentAction",function(container){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&!document.$id){return'database.createDocument';}
|
return result.length;}).add("documentAction",function(container){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&!document.$id){return'database.createDocument';}
|
||||||
return'database.updateDocument';}).add("documentSuccess",function(container){let document=container.get('project-document');if(document&&!document.$id){return',redirect';}
|
return'database.updateDocument';}).add("documentSuccess",function(container){let document=container.get('project-document');if(document&&!document.$id){return',redirect';}
|
||||||
return'';}).add("firstElement",function($value){if($value&&$value[0]){return $value[0];}
|
return'';}).add("firstElement",function($value){if($value&&$value[0]){return $value[0];}
|
||||||
|
@ -356,7 +356,11 @@ list["filters-"+filter.key]=params[key][i];}}}}
|
||||||
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
||||||
element.setAttribute("data-event","none");}
|
element.setAttribute("data-event","none");}
|
||||||
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
break;default:break;}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView(true);}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
||||||
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
|
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-apple",controller:function(element){let container=document.createElement("div");let row=document.createElement("div");let col1=document.createElement("div");let col2=document.createElement("div");let keyID=document.createElement("input");let keyLabel=document.createElement("label");let teamID=document.createElement("input");let teamLabel=document.createElement("label");let p8=document.createElement("textarea");let p8Label=document.createElement("label");keyLabel.textContent='Key ID';teamLabel.textContent='Team ID';p8Label.textContent='P8 File';row.classList.add('row');row.classList.add('thin');container.appendChild(row);container.appendChild(p8Label);container.appendChild(p8);row.appendChild(col1);row.appendChild(col2);col1.classList.add('col');col1.classList.add('span-6');col1.appendChild(keyLabel);col1.appendChild(keyID);col2.classList.add('col');col2.classList.add('span-6');col2.appendChild(teamLabel);col2.appendChild(teamID);keyID.type='text';keyID.placeholder='SHAB13ROFN';teamID.type='text';teamID.placeholder='ELA2CD3AED';p8.accept='.p8';p8.classList.add('margin-bottom-no');element.parentNode.insertBefore(container,element.nextSibling);element.addEventListener('change',sync);keyID.addEventListener('change',update);teamID.addEventListener('change',update);p8.addEventListener('change',update);function update(){let json={};json.keyID=keyID.value;json.teamID=teamID.value;json.p8=p8.value;element.value=JSON.stringify(json);}
|
||||||
|
function sync(){console.log('sync');if(!element.value){return;}
|
||||||
|
let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');}
|
||||||
|
teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';}
|
||||||
|
sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i<password.length;i++){letters[password[i]]=(letters[password[i]]||0)+1;score+=5.0/letters[password[i]];}
|
||||||
var variations={digits:/\d/.test(password),lower:/[a-z]/.test(password),upper:/[A-Z]/.test(password),nonWords:/\W/.test(password)};var variationCount=0;for(var check in variations){if(variations.hasOwnProperty(check)){variationCount+=variations[check]===true?1:0;}}
|
var variations={digits:/\d/.test(password),lower:/[a-z]/.test(password),upper:/[A-Z]/.test(password),nonWords:/\W/.test(password)};var variationCount=0;for(var check in variations){if(variations.hasOwnProperty(check)){variationCount+=variations[check]===true?1:0;}}
|
||||||
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;}
|
score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){var score=calc(this.value);if(""===this.value)return(meter.className="password-meter");if(score>60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:'<i class="icon-bold"></i>'},{name:"underline",icon:'<i class="icon-underline"></i>'},{name:"italic",icon:'<i class="icon-italic"></i>'},{name:"olist",icon:'<i class="icon-list-numbered"></i>'},{name:"ulist",icon:'<i class="icon-list-bullet"></i>'},{name:"link",icon:'<i class="icon-link"></i>'}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;}
|
||||||
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
|
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 3.5 KiB |
94
public/scripts/views/forms/oauth-apple.js
Normal file
94
public/scripts/views/forms/oauth-apple.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
(function(window) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
window.ls.container.get("view").add({
|
||||||
|
selector: "data-forms-oauth-apple",
|
||||||
|
controller: function(element) {
|
||||||
|
let container = document.createElement("div");
|
||||||
|
let row = document.createElement("div");
|
||||||
|
let col1 = document.createElement("div");
|
||||||
|
let col2 = document.createElement("div");
|
||||||
|
let keyID = document.createElement("input");
|
||||||
|
let keyLabel = document.createElement("label");
|
||||||
|
let teamID = document.createElement("input");
|
||||||
|
let teamLabel = document.createElement("label");
|
||||||
|
let p8 = document.createElement("textarea");
|
||||||
|
let p8Label = document.createElement("label");
|
||||||
|
|
||||||
|
keyLabel.textContent = 'Key ID';
|
||||||
|
teamLabel.textContent = 'Team ID';
|
||||||
|
p8Label.textContent = 'P8 File';
|
||||||
|
|
||||||
|
row.classList.add('row');
|
||||||
|
row.classList.add('thin');
|
||||||
|
container.appendChild(row);
|
||||||
|
container.appendChild(p8Label);
|
||||||
|
container.appendChild(p8);
|
||||||
|
|
||||||
|
row.appendChild(col1);
|
||||||
|
row.appendChild(col2);
|
||||||
|
|
||||||
|
col1.classList.add('col');
|
||||||
|
col1.classList.add('span-6');
|
||||||
|
col1.appendChild(keyLabel);
|
||||||
|
col1.appendChild(keyID);
|
||||||
|
|
||||||
|
col2.classList.add('col');
|
||||||
|
col2.classList.add('span-6');
|
||||||
|
col2.appendChild(teamLabel);
|
||||||
|
col2.appendChild(teamID);
|
||||||
|
|
||||||
|
keyID.type = 'text';
|
||||||
|
keyID.placeholder = 'SHAB13ROFN';
|
||||||
|
teamID.type = 'text';
|
||||||
|
teamID.placeholder = 'ELA2CD3AED';
|
||||||
|
p8.accept = '.p8';
|
||||||
|
p8.classList.add('margin-bottom-no');
|
||||||
|
|
||||||
|
element.parentNode.insertBefore(container, element.nextSibling);
|
||||||
|
|
||||||
|
element.addEventListener('change', sync);
|
||||||
|
keyID.addEventListener('change', update);
|
||||||
|
teamID.addEventListener('change', update);
|
||||||
|
p8.addEventListener('change', update);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
let json = {};
|
||||||
|
|
||||||
|
json.keyID = keyID.value;
|
||||||
|
json.teamID = teamID.value;
|
||||||
|
json.p8 = p8.value;
|
||||||
|
|
||||||
|
element.value = JSON.stringify(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sync() {
|
||||||
|
console.log('sync');
|
||||||
|
if(!element.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
json = JSON.parse(element.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to parse secret key');
|
||||||
|
}
|
||||||
|
|
||||||
|
teamID.value = json.teamID || '';
|
||||||
|
keyID.value = json.keyID || '';
|
||||||
|
p8.value = json.p8 || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// function syncB() {
|
||||||
|
// picker.value = element.value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// element.parentNode.insertBefore(preview, element);
|
||||||
|
|
||||||
|
// update();
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(window);
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Appwrite\Auth\OAuth2;
|
namespace Appwrite\Auth\OAuth2;
|
||||||
|
|
||||||
use Appwrite\Auth\OAuth2;
|
use Appwrite\Auth\OAuth2;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
// Reference Material
|
// Reference Material
|
||||||
// https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
|
// https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
|
||||||
|
@ -22,6 +23,11 @@ class Apple extends OAuth2
|
||||||
"email"
|
"email"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $claims = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -58,15 +64,18 @@ class Apple extends OAuth2
|
||||||
'https://appleid.apple.com/auth/token',
|
'https://appleid.apple.com/auth/token',
|
||||||
$headers,
|
$headers,
|
||||||
http_build_query([
|
http_build_query([
|
||||||
|
'grant_type' => 'authorization_code',
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'client_id' => $this->appID,
|
'client_id' => $this->appID,
|
||||||
'client_secret' => $this->appSecret,
|
'client_secret' => $this->getAppSecret(),
|
||||||
'redirect_uri' => $this->callback,
|
'redirect_uri' => $this->callback,
|
||||||
'grant_type' => 'authorization_code'
|
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
$accessToken = json_decode($accessToken, true);
|
$accessToken = json_decode($accessToken, true);
|
||||||
|
|
||||||
|
$this->claims = (isset($accessToken['id_token'])) ? explode('.', $accessToken['id_token']) : [0 => '', 1 => ''];
|
||||||
|
$this->claims = (isset($this->claims[1])) ? json_decode(base64_decode($this->claims[1]), true) : [];
|
||||||
|
|
||||||
if (isset($accessToken['access_token'])) {
|
if (isset($accessToken['access_token'])) {
|
||||||
return $accessToken['access_token'];
|
return $accessToken['access_token'];
|
||||||
|
@ -82,10 +91,8 @@ class Apple extends OAuth2
|
||||||
*/
|
*/
|
||||||
public function getUserID(string $accessToken): string
|
public function getUserID(string $accessToken): string
|
||||||
{
|
{
|
||||||
$user = $this->getUser($accessToken);
|
if (isset($this->claims['sub']) && !empty($this->claims['sub'])) {
|
||||||
|
return $this->claims['sub'];
|
||||||
if (isset($user['account_id'])) {
|
|
||||||
return $user['account_id'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -98,10 +105,11 @@ class Apple extends OAuth2
|
||||||
*/
|
*/
|
||||||
public function getUserEmail(string $accessToken): string
|
public function getUserEmail(string $accessToken): string
|
||||||
{
|
{
|
||||||
$user = $this->getUser($accessToken);
|
if (isset($this->claims['email']) &&
|
||||||
|
!empty($this->claims['email']) &&
|
||||||
if (isset($user['email'])) {
|
isset($this->claims['email_verified']) &&
|
||||||
return $user['email'];
|
$this->claims['email_verified'] === 'true') {
|
||||||
|
return $this->claims['email'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -114,28 +122,111 @@ class Apple extends OAuth2
|
||||||
*/
|
*/
|
||||||
public function getUserName(string $accessToken): string
|
public function getUserName(string $accessToken): string
|
||||||
{
|
{
|
||||||
$user = $this->getUser($accessToken);
|
if (isset($this->claims['email']) &&
|
||||||
|
!empty($this->claims['email']) &&
|
||||||
if (isset($user['name'])) {
|
isset($this->claims['email_verified']) &&
|
||||||
return $user['name']['display_name'];
|
$this->claims['email_verified'] === 'true') {
|
||||||
|
return $this->claims['email'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getAppSecret():string
|
||||||
* @param string $accessToken
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getUser(string $accessToken): array
|
|
||||||
{
|
{
|
||||||
if (empty($this->user)) {
|
try {
|
||||||
$headers[] = 'Authorization: Bearer '. urlencode($accessToken);
|
$secret = json_decode($this->appSecret, true);
|
||||||
$user = $this->request('POST', '', $headers);
|
} catch (\Throwable $th) {
|
||||||
$this->user = json_decode($user, true);
|
throw new Exception('Invalid secret');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->user;
|
$keyfile = (isset($secret['p8'])) ? $secret['p8'] : ''; // Your p8 Key file
|
||||||
|
$keyID = (isset($secret['keyID'])) ? $secret['keyID'] : ''; // Your Key ID
|
||||||
|
$teamID = (isset($secret['teamID'])) ? $secret['teamID'] : ''; // Your Team ID (see Developer Portal)
|
||||||
|
$bundleID = $this->appID; // Your Bundle ID
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'alg' => 'ES256',
|
||||||
|
'kid' => $keyID,
|
||||||
|
];
|
||||||
|
|
||||||
|
$claims = [
|
||||||
|
'iss' => $teamID,
|
||||||
|
'iat' => time(),
|
||||||
|
'exp' => time() + 86400*180,
|
||||||
|
'aud' => 'https://appleid.apple.com',
|
||||||
|
'sub' => $bundleID,
|
||||||
|
];
|
||||||
|
|
||||||
|
$pkey = openssl_pkey_get_private($keyfile);
|
||||||
|
|
||||||
|
$payload = $this->encode(json_encode($headers)).'.'.$this->encode(json_encode($claims));
|
||||||
|
|
||||||
|
$signature = '';
|
||||||
|
|
||||||
|
$success = openssl_sign($payload, $signature, $pkey, OPENSSL_ALGO_SHA256);
|
||||||
|
|
||||||
|
if (!$success) return '';
|
||||||
|
|
||||||
|
return $payload.'.'.$this->encode($this->fromDER($signature, 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $data
|
||||||
|
*/
|
||||||
|
protected function encode($data)
|
||||||
|
{
|
||||||
|
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $data
|
||||||
|
*/
|
||||||
|
protected function retrievePositiveInteger(string $data): string
|
||||||
|
{
|
||||||
|
while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') > '7f') {
|
||||||
|
$data = mb_substr($data, 2, null, '8bit');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $der
|
||||||
|
* @param int $partLength
|
||||||
|
*/
|
||||||
|
protected function fromDER(string $der, int $partLength):string
|
||||||
|
{
|
||||||
|
$hex = \unpack('H*', $der)[1];
|
||||||
|
|
||||||
|
if ('30' !== \mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE
|
||||||
|
throw new \RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('81' === \mb_substr($hex, 2, 2, '8bit')) { // LENGTH > 128
|
||||||
|
$hex = \mb_substr($hex, 6, null, '8bit');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hex = \mb_substr($hex, 4, null, '8bit');
|
||||||
|
}
|
||||||
|
if ('02' !== \mb_substr($hex, 0, 2, '8bit')) { // INTEGER
|
||||||
|
throw new \RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$Rl = \hexdec(\mb_substr($hex, 2, 2, '8bit'));
|
||||||
|
$R = $this->retrievePositiveInteger(\mb_substr($hex, 4, $Rl * 2, '8bit'));
|
||||||
|
$R = \str_pad($R, $partLength, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
$hex = \mb_substr($hex, 4 + $Rl * 2, null, '8bit');
|
||||||
|
|
||||||
|
if ('02' !== \mb_substr($hex, 0, 2, '8bit')) { // INTEGER
|
||||||
|
throw new \RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$Sl = \hexdec(\mb_substr($hex, 2, 2, '8bit'));
|
||||||
|
$S = $this->retrievePositiveInteger(\mb_substr($hex, 4, $Sl * 2, '8bit'));
|
||||||
|
$S = \str_pad($S, $partLength, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
return \pack('H*', $R.$S);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue