Merge branch 'master' of https://github.com/appwrite/appwrite
This commit is contained in:
commit
22290c21dd
25 changed files with 1479 additions and 1047 deletions
|
@ -9,9 +9,10 @@
|
|||
</p>
|
||||
|
||||
<!-- [![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=friendly&color=90a88b&style=flat-square)](https://hacktoberfest.appwrite.io) -->
|
||||
<!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) -->
|
||||
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
|
||||
[![Docker Pulls](https://img.shields.io/docker/pulls/appwrite/appwrite?color=f02e65&style=flat-square)](https://hub.docker.com/r/appwrite/appwrite)
|
||||
[![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite)
|
||||
[![Build Status](https://img.shields.io/github/checks-status/appwrite/appwrite/master?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions)
|
||||
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
|
||||
[![Translate](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md)
|
||||
[![Swag Store](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io)
|
||||
|
@ -171,4 +172,4 @@ Join our growing community around the world! See our official [Blog](https://med
|
|||
|
||||
## License
|
||||
|
||||
This repository is available under the [BSD 3-Clause License](./LICENSE).
|
||||
This repository is available under the [BSD 3-Clause License](./LICENSE).
|
||||
|
|
|
@ -99,8 +99,8 @@ $collections = [
|
|||
'$id' => '_fulltext_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [1024],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -574,8 +574,8 @@ $collections = [
|
|||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -1106,8 +1106,8 @@ $collections = [
|
|||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => '_key_deleted_email',
|
||||
|
@ -1422,8 +1422,8 @@ $collections = [
|
|||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -1720,8 +1720,8 @@ $collections = [
|
|||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -1892,8 +1892,8 @@ $collections = [
|
|||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -1983,8 +1983,8 @@ $collections = [
|
|||
'$id' => '_key_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [2048],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -2118,8 +2118,8 @@ $collections = [
|
|||
'$id' => '_fulltext_search',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['search'],
|
||||
'lengths' => [16384],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
|
@ -127,7 +127,7 @@ return [ // Ordered by ABC.
|
|||
'icon' => 'icon-windows',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'form' => 'microsoft.phtml',
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
|
@ -287,6 +287,16 @@ return [ // Ordered by ABC.
|
|||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
'stripe' => [
|
||||
'name' => 'Stripe',
|
||||
'developers' => 'https://stripe.com/docs/api',
|
||||
'icon' => 'icon-stripe',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false
|
||||
],
|
||||
// Keep Last
|
||||
'mock' => [
|
||||
'name' => 'Mock',
|
||||
|
|
|
@ -315,7 +315,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->label('docs', false)
|
||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
@ -342,7 +342,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->label('docs', false)
|
||||
->param('projectId', '', new Text(1024), 'Project ID.')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
@ -370,7 +370,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
->label('abuse-key', 'ip:{ip}')
|
||||
->label('docs', false)
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
|
||||
->param('code', '', new Text(1024), 'OAuth2 code.')
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.')
|
||||
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
|
|
@ -2011,14 +2011,18 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
if(!is_null($read)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400);
|
||||
if(!is_null($write)) {
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -491,11 +491,12 @@ $register->set('geodb', function () {
|
|||
});
|
||||
$register->set('db', function () { // This is usually for our workers or CLI commands scope
|
||||
$dbHost = App::getEnv('_APP_DB_HOST', '');
|
||||
$dbPort = App::getEnv('_APP_DB_PORT', '');
|
||||
$dbUser = App::getEnv('_APP_DB_USER', '');
|
||||
$dbPass = App::getEnv('_APP_DB_PASS', '');
|
||||
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
|
||||
|
||||
$pdo = new PDO("mysql:host={$dbHost};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
||||
$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
|
||||
PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||
PDO::ATTR_PERSISTENT => true,
|
||||
|
|
|
@ -527,15 +527,6 @@ $logs = $this->getParam('logs', null);
|
|||
|
||||
<hr class="margin-top-small" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Document Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="collection" type="radio" class="margin-top-tiny" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
|
@ -554,6 +545,15 @@ $logs = $this->getParam('logs', null);
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
|
||||
<div class="col span-11">
|
||||
<b>Document Level</b>
|
||||
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
|
||||
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="margin-top-no" />
|
||||
|
||||
<button>Update</button>
|
||||
|
@ -880,10 +880,10 @@ $logs = $this->getParam('logs', null);
|
|||
|
||||
<div class="margin-bottom-large">
|
||||
<template x-if="!(array || required)">
|
||||
<input name="xdefault" class="button switch" type="checkbox" />
|
||||
<input name="xdefault" class="button switch" type="checkbox" />
|
||||
</template>
|
||||
<template x-if="(array || required)">
|
||||
<input name="xdefault" class="button switch" type="checkbox" disabled />
|
||||
<input name="" class="button switch" type="checkbox" disabled />
|
||||
</template>
|
||||
Default Value <span class="tooltip" data-tooltip="Whether this attribute is set to true or false on creation"><i class="icon-info-circled"></i></span>
|
||||
</div>
|
||||
|
|
|
@ -169,8 +169,11 @@ $logs = $this->getParam('logs', null);
|
|||
</template>
|
||||
<template x-if="attr.format === 'enum'">
|
||||
<select
|
||||
:required="attr.required"
|
||||
:name="attr.key"
|
||||
data-cast-to="string">
|
||||
<option :disabled="attr.required" selected label=" "></option>
|
||||
|
||||
<template x-for="element in attr.elements">
|
||||
<option
|
||||
:value="element"
|
||||
|
@ -261,9 +264,12 @@ $logs = $this->getParam('logs', null);
|
|||
</template>
|
||||
<template x-if="attr.format === 'enum'">
|
||||
<select
|
||||
:required="attr.required"
|
||||
:name="attr.key"
|
||||
data-cast-to="string">
|
||||
<template x-for="element in attr.elements">
|
||||
<option :disabled="attr.required" selected label=" "></option>
|
||||
|
||||
<option
|
||||
:value="element"
|
||||
x-text="element"
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
<label for="collection-name">Name</label>
|
||||
<input type="text" class="full-width" id="collection-name" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
<input type="hidden" id="collection-permission" name="permission" required value="document" />
|
||||
<input type="hidden" id="collection-permission" name="permission" required value="collection" />
|
||||
<input type="hidden" id="collection-read" name="read" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
<input type="hidden" id="collection-write" name="write" required data-cast-to="json" value="<?php echo htmlentities(json_encode([])); ?>" />
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?php
|
||||
use Appwrite\Utopia\View;
|
||||
|
||||
$providers = $this->getParam('providers', []);
|
||||
$auth = $this->getParam('auth', []);
|
||||
$smtpEnabled = $this->getParam('smtpEnabled', false);
|
||||
|
@ -475,9 +477,12 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
|
||||
<input name="secret" data-forms-show-secret id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="password" autocomplete="off" data-ls-bind="{{console-project.provider<?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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="com.company.appname" />
|
||||
<input name="secret" data-forms-oauth-apple id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||
<?php
|
||||
$form = new View(__DIR__.'/oauth/'.$this->escape($form));
|
||||
echo $form
|
||||
->setParam("provider", $provider)
|
||||
->render();
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="info row thin margin-bottom margin-top">
|
||||
|
|
13
app/views/console/users/oauth/apple.phtml
Normal file
13
app/views/console/users/oauth/apple.phtml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
$provider = $this->getParam('provider', '');
|
||||
?>
|
||||
|
||||
<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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="com.company.appname" />
|
||||
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||
<div>
|
||||
<div class="row thin">
|
||||
<div class="col span-6"><label>Key ID</label><input id="oauth2AppleKeyId" type="text" placeholder="SHAB13ROFN"></div>
|
||||
<div class="col span-6"><label>Team ID</label><input id="oauth2AppleTeamId" type="text" placeholder="ELA2CD3AED"></div>
|
||||
</div><label>P8 File</label><textarea id="oauth2AppleP8" class="margin-bottom-no"></textarea>
|
||||
</div>
|
12
app/views/console/users/oauth/microsoft.phtml
Normal file
12
app/views/console/users/oauth/microsoft.phtml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
$provider = $this->getParam('provider', '');
|
||||
?>
|
||||
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Application (Client) ID<span class="tooltip" data-tooltip="Provided by AzureAD"><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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="Application ID" />
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret">Client Secret <span class="tooltip" data-tooltip="Created by you in AzureAD Portal"><i class="icon-info-circled"></i></span></label>
|
||||
<input name="appSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret" type="password" autocomplete="off" placeholder="Client Secret" />
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>TenantId">Target Tenant<span class="tooltip" data-tooltip="'common', 'organizations', 'consumers' or your TenantId"><i class="icon-info-circled"></i></span><a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints">More info</a></label>
|
||||
<input name="appSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>TenantId" type="text" autocomplete="off" placeholder="'common', 'organizations', 'consumers' or your TenantId" />
|
||||
<?php /*Hidden input for the final secret. Gets filled with a JSON via JS. */ ?>
|
||||
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
|
@ -175,4 +175,34 @@ If everything goes well, raise a pull request and be ready to respond to any fee
|
|||
First of all, commit the changes with the message `Added XXX OAuth2 Provider` and push it. This will publish a new branch to your forked version of Appwrite. If you visit it at `github.com/YOUR_USERNAME/appwrite`, you will see a new alert saying you are ready to submit a pull request. Follow the steps GitHub provides, and at the end, you will have your pull request submitted.
|
||||
|
||||
## 🤕 Stuck ?
|
||||
|
||||
If you need any help with the contribution, feel free to head over to [our discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
||||
|
||||
## 😉 Need more freedom
|
||||
|
||||
If your OAuth provider requires special configuration apart from `clientId` and `clientSecret` you can create a custom form. Currently this is being realized through putting all custom fields as JSON into the `clientSecret` field to keep the project API stable. You can implement your custom form following these steps:
|
||||
|
||||
1. Add your custom form in `app/views/console/users/oauth/[PROVIDER].phtml`. Below is a template you can use. Add the filename to `app/config/providers.php`.
|
||||
|
||||
```php
|
||||
<?php
|
||||
$provider = $this->getParam('provider', '');
|
||||
?>
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">Application (Client) ID<span class="tooltip" data-tooltip="Provided by AzureAD"><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.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" placeholder="Application ID" />
|
||||
<?php /*Hidden input for the final secret. Gets filled with a JSON via JS. */ ?>
|
||||
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
||||
<!-- [Your custom form inputs go here] -->
|
||||
```
|
||||
|
||||
2. Add the config for creating the JSON in `public/scripts/views/forms/oauth-custom.js` using this template
|
||||
```js
|
||||
{
|
||||
"[Provider]":{
|
||||
"[JSON property name 1]":"[html element Id 1]",
|
||||
"[JSON property name 2]":"[html element Id 2]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. In your provider class `src/Appwrite/Auth/OAuth2/[Provider].php` add logic to decode the JSON using the same property names.
|
||||
|
|
|
@ -57,7 +57,7 @@ const configApp = {
|
|||
'public/scripts/views/forms/move-down.js',
|
||||
'public/scripts/views/forms/move-up.js',
|
||||
'public/scripts/views/forms/nav.js',
|
||||
'public/scripts/views/forms/oauth-apple.js',
|
||||
'public/scripts/views/forms/oauth-custom.js',
|
||||
'public/scripts/views/forms/password-meter.js',
|
||||
'public/scripts/views/forms/pell.js',
|
||||
'public/scripts/views/forms/required.js',
|
||||
|
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -12,7 +12,7 @@
|
|||
"chart.js": "^3.7.0",
|
||||
"markdown-it": "^12.3.2",
|
||||
"pell": "^1.0.6",
|
||||
"prismjs": "^1.25.0",
|
||||
"prismjs": "^1.26.0",
|
||||
"turndown": "^7.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -3566,9 +3566,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.25.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
|
||||
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg=="
|
||||
"version": "1.26.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz",
|
||||
"integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
|
@ -7978,9 +7981,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.25.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
|
||||
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg=="
|
||||
"version": "1.26.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz",
|
||||
"integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ=="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"chart.js": "^3.7.0",
|
||||
"markdown-it": "^12.3.2",
|
||||
"pell": "^1.0.6",
|
||||
"prismjs": "^1.25.0",
|
||||
"prismjs": "^1.26.0",
|
||||
"turndown": "^7.1.1"
|
||||
}
|
||||
}
|
||||
|
|
934
public/dist/scripts/app-all.js
vendored
934
public/dist/scripts/app-all.js
vendored
File diff suppressed because it is too large
Load diff
926
public/dist/scripts/app-dep.js
vendored
926
public/dist/scripts/app-dep.js
vendored
File diff suppressed because it is too large
Load diff
8
public/dist/scripts/app.js
vendored
8
public/dist/scripts/app.js
vendored
|
@ -778,10 +778,14 @@ 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{}
|
||||
element.setAttribute("data-event","none");}
|
||||
break;default:break;}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-key-value",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";key.required=true;value.type="text";value.className="margin-bottom-no";value.placeholder="Value";value.required=true;wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.name=key.value;element.value=value.value;};let syncB=function(){key.value=element.name||"";value.value=element.value||"";};syncB();}});})(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){console.log('down',element.offsetHeight);element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView({block:'center'});}});});}});})(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){console.log('up',element);element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView({block:'center'});}});});}});})(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-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);}
|
||||
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-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"},"Apple":{"keyId":"oauth2AppleKeyId","teamId":"oauth2AppleTeamId","p8":"oauth2AppleP8"}}
|
||||
let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")}
|
||||
let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}}
|
||||
function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}}
|
||||
element.value=JSON.stringify(json);}
|
||||
function 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||'';}
|
||||
for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){elements[key].value=json[key]||'';}}}
|
||||
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;}}
|
||||
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;}
|
||||
|
|
BIN
public/images/users/stripe.png
Normal file
BIN
public/images/users/stripe.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
|
@ -1,93 +0,0 @@
|
|||
(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() {
|
||||
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);
|
73
public/scripts/views/forms/oauth-custom.js
Normal file
73
public/scripts/views/forms/oauth-custom.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
(function (window) {
|
||||
"use strict";
|
||||
|
||||
//TODO: Make this generic
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-oauth-custom",
|
||||
controller: function (element) {
|
||||
// provider configuration for custom forms. Keys will be property names in JSON, values the elementIDs for the according inputs
|
||||
let providers = {
|
||||
"Microsoft": {
|
||||
"clientSecret": "oauth2MicrosoftClientSecret",
|
||||
"tenantId": "oauth2MicrosoftTenantId"
|
||||
},
|
||||
"Apple": {
|
||||
"keyId": "oauth2AppleKeyId",
|
||||
"teamId": "oauth2AppleTeamId",
|
||||
"p8": "oauth2AppleP8"
|
||||
}
|
||||
}
|
||||
let provider = element.getAttribute("data-forms-oauth-custom");
|
||||
if (!provider || !providers.hasOwnProperty(provider)) { console.error("Provider for custom form not set or unkown") }
|
||||
let config = providers[provider];
|
||||
|
||||
// Add Change Listeners for element
|
||||
element.addEventListener('change', sync);
|
||||
|
||||
// Get all inputs by id and register change event listener
|
||||
let elements = {};
|
||||
for (const key in config) {
|
||||
if (Object.hasOwnProperty.call(config, key)) {
|
||||
elements[key] = document.getElementById(config[key]);
|
||||
elements[key].addEventListener('change', update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Build the JSON based on input in custom input fields
|
||||
function update() {
|
||||
let json = {};
|
||||
for (const key in elements) {
|
||||
if (Object.hasOwnProperty.call(elements, key)) {
|
||||
json[key] = elements[key].value
|
||||
}
|
||||
}
|
||||
|
||||
element.value = JSON.stringify(json);
|
||||
}
|
||||
|
||||
// When the JSON changes (on load) change values in custom input fields
|
||||
function sync() {
|
||||
if (!element.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let json = {};
|
||||
|
||||
try {
|
||||
json = JSON.parse(element.value);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse secret key');
|
||||
}
|
||||
|
||||
for (const key in elements) {
|
||||
if (Object.hasOwnProperty.call(elements, key)) {
|
||||
elements[key].value = json[key] || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
sync();
|
||||
}
|
||||
});
|
||||
})(window);
|
|
@ -36,7 +36,7 @@ class Microsoft extends OAuth2
|
|||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?'.\http_build_query([
|
||||
return 'https://login.microsoftonline.com/'.$this->getTenantId().'/oauth2/v2.0/authorize?'.\http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'state'=> \json_encode($this->state),
|
||||
|
@ -57,12 +57,12 @@ class Microsoft extends OAuth2
|
|||
|
||||
$accessToken = $this->request(
|
||||
'POST',
|
||||
'https://login.microsoftonline.com/common/oauth2/v2.0/token',
|
||||
'https://login.microsoftonline.com/'.$this->getTenantId().'/oauth2/v2.0/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'code' => $code,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'grant_type' => 'authorization_code'
|
||||
|
@ -141,4 +141,39 @@ class Microsoft extends OAuth2
|
|||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
{
|
||||
$secret = $this->decodeJson();
|
||||
|
||||
return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
* @return array
|
||||
*/
|
||||
protected function decodeJson(): array
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true);
|
||||
} catch (\Throwable $th) {
|
||||
throw new Exception('Invalid secret');
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
|
||||
* @return string
|
||||
*/
|
||||
protected function getTenantId(): string
|
||||
{
|
||||
$secret = $this->decodeJson();
|
||||
return (isset($secret['tenantId'])) ? $secret['tenantId'] : 'common';
|
||||
}
|
||||
}
|
||||
|
|
157
src/Appwrite/Auth/OAuth2/Stripe.php
Normal file
157
src/Appwrite/Auth/OAuth2/Stripe.php
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Stripe extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $user = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $stripeAccountId = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
'read_write',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
||||
protected $grantType = [
|
||||
'authorize' => 'authorization_code',
|
||||
'refresh' => 'refresh_token'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName():string
|
||||
{
|
||||
return 'stripe';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL():string
|
||||
{
|
||||
return 'https://connect.stripe.com/oauth/authorize?'. \http_build_query([
|
||||
'response_type' => 'code', // The only option at the moment is "code."
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken(string $code):string
|
||||
{
|
||||
$response = $this->request(
|
||||
'POST',
|
||||
'https://connect.stripe.com/oauth/token',
|
||||
[],
|
||||
\http_build_query([
|
||||
'grant_type' => $this->grantType['authorize'],
|
||||
'code' => $code
|
||||
])
|
||||
);
|
||||
|
||||
$response = \json_decode($response, true);
|
||||
|
||||
if (isset($response['stripe_user_id'])) {
|
||||
$this->stripeAccountId = $response['stripe_user_id'];
|
||||
}
|
||||
|
||||
if (isset($response['access_token'])) {
|
||||
return $response['access_token'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken):string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['id'])) {
|
||||
return $user['id'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken):string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if(empty($user)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $user['email'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken):string
|
||||
{
|
||||
$user = $this->getUser($accessToken);
|
||||
|
||||
if (isset($user['name'])) {
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken)
|
||||
{
|
||||
if (empty($this->user) && !empty($this->stripeAccountId)) {
|
||||
$this->user = \json_decode(
|
||||
$this->request(
|
||||
'GET',
|
||||
'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId,
|
||||
['Authorization: Bearer '.\urlencode($accessToken)]
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Database;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
|
@ -11,4 +12,106 @@ class DatabaseCustomClientTest extends Scope
|
|||
use DatabaseBase;
|
||||
use ProjectCustom;
|
||||
use SideClient;
|
||||
|
||||
public function testUpdateWithoutPermission(): array
|
||||
{
|
||||
// If document has been created by server and client tried to update it without adjusting permissions, permission validation should be skipped
|
||||
|
||||
// As a part of preparation, we get ID of currently logged-in user
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
$userId = $response['body']['$id'];
|
||||
|
||||
// Create collection
|
||||
$response = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'permissionCheck',
|
||||
'name' => 'permissionCheck',
|
||||
'read' => [],
|
||||
'write' => [],
|
||||
'permission' => 'document'
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Add attribute to collection
|
||||
$response = $this->client->call(Client::METHOD_POST, '/database/collections/permissionCheck/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'name',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Wait for database worker to finish creating attributes
|
||||
sleep(2);
|
||||
|
||||
// Creating document by server, give read permission to our user + some other user
|
||||
$response = $this->client->call(Client::METHOD_POST, '/database/collections/permissionCheck/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'documentId' => 'permissionCheckDocument',
|
||||
'data' => [
|
||||
'name' => 'AppwriteBeginner',
|
||||
],
|
||||
'read' => ['user:' . $userId, 'user:user2'],
|
||||
'write' => ['user:' . $userId],
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Update document
|
||||
// This is the point of this test. We should be allowed to do this action, and it should not fail on permission check
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/database/collections/permissionCheck/documents/permissionCheckDocument', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'name' => 'AppwriteExpert',
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Get name of the document, should be the new one
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/collections/permissionCheck/documents/permissionCheckDocument', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("AppwriteExpert", $response['body']['name']);
|
||||
|
||||
// Cleanup to prevent collision with other tests
|
||||
// Delete collection
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/database/collections/permissionCheck', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
|
||||
// Wait for database worker to finish deleting collection
|
||||
sleep(2);
|
||||
|
||||
// Make sure collection has been deleted
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/collections/permissionCheck', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue