1
0
Fork 0
mirror of synced 2024-06-02 10:54:44 +12:00

Merge branch '0.13.x' into feat-storage-buckets

This commit is contained in:
Damodar Lohani 2022-01-31 15:37:08 +05:45
commit 21dc234a94
21 changed files with 500 additions and 141 deletions

View file

@ -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).

View file

@ -108,8 +108,8 @@ $collections = [
'$id' => '_fulltext_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [1024],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
],
],
@ -583,8 +583,8 @@ $collections = [
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
],
],
@ -1115,8 +1115,8 @@ $collections = [
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
[
'$id' => '_key_deleted_email',
@ -1431,8 +1431,8 @@ $collections = [
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
],
],
@ -1711,8 +1711,8 @@ $collections = [
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
],
],
@ -1824,8 +1824,8 @@ $collections = [
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
],
],
@ -1959,8 +1959,8 @@ $collections = [
'$id' => '_fulltext_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [16384],
'orders' => [Database::ORDER_ASC],
'lengths' => [],
'orders' => [],
],
],
],

View file

@ -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',

View file

@ -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')

View file

@ -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);
}
}
}
}

View file

@ -502,11 +502,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,

View file

@ -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>

View file

@ -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([])); ?>" />

View file

@ -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">

View 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>

View 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}}" />

View file

@ -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.

View file

@ -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',

View file

@ -3810,10 +3810,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;}

View file

@ -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;}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -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);

View 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);

View file

@ -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';
}
}

View 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;
}
}

View file

@ -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 [];
}
}