From 1a81f10d89d047b687332650517511c0822a46c9 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 30 Nov 2021 16:11:45 +0000 Subject: [PATCH 001/134] Add a DNS warning during install --- app/config/variables.php | 2 +- app/tasks/install.php | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/config/variables.php b/app/config/variables.php index e6313a9776..e57f4fb420 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -68,7 +68,7 @@ return [ 'default' => 'localhost', 'required' => true, 'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.' . PHP_EOL . 'You can use the same value as used for the Appwrite hostname.', - 'filter' => '' + 'filter' => 'domainTarget' ], [ 'name' => '_APP_CONSOLE_WHITELIST_ROOT', diff --git a/app/tasks/install.php b/app/tasks/install.php index 0898ae67c1..24743403ae 100644 --- a/app/tasks/install.php +++ b/app/tasks/install.php @@ -11,6 +11,19 @@ use Utopia\Config\Config; use Utopia\View; use Utopia\Validator\Text; +function formatArray(array $arr) { + $mask = "%10.10s %-10.10s %10.10s\n"; + printf($mask, "Type", "Name", "Value"); + + // array_walk($arr, function(&$key) use ($descriptionColumnLimit){ + // $key = explode("\n", wordwrap($key, $descriptionColumnLimit)); + // }); + + foreach($arr as $key => $value) { + printf($mask, $key, $value[0], $value[1]); + } +} + $cli ->task('install') ->desc('Install Appwrite') @@ -167,6 +180,16 @@ $cli if(empty($input[$var['name']])) { $input[$var['name']] = $var['default']; } + + if ($var['filter'] === 'domainTarget') { + if ($input[$var['name']] !== 'localhost') { + Console::warning("If you haven't already done so, make sure you create an 'A' or 'AAAA' DNS record for '".$input[$var['name']]."' pointing to your external server IP. \nYour DNS Table should look like so: "); + formatArray([ + 'A/AAAA' => ['@', 'Your IP'], + ]); + Console::warning("Use 'AAAA' if you have an IPv6 address and 'A' if you have an IPv4 address."); + } + } } $templateForCompose = new View(__DIR__.'/../views/install/compose.phtml'); From c1ef83eec74058e8837770a1ac3964f8e27449cf Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 1 Dec 2021 02:39:42 +0400 Subject: [PATCH 002/134] feat: update PR --- app/tasks/install.php | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/app/tasks/install.php b/app/tasks/install.php index 24743403ae..9f3f138dd9 100644 --- a/app/tasks/install.php +++ b/app/tasks/install.php @@ -11,19 +11,6 @@ use Utopia\Config\Config; use Utopia\View; use Utopia\Validator\Text; -function formatArray(array $arr) { - $mask = "%10.10s %-10.10s %10.10s\n"; - printf($mask, "Type", "Name", "Value"); - - // array_walk($arr, function(&$key) use ($descriptionColumnLimit){ - // $key = explode("\n", wordwrap($key, $descriptionColumnLimit)); - // }); - - foreach($arr as $key => $value) { - printf($mask, $key, $value[0], $value[1]); - } -} - $cli ->task('install') ->desc('Install Appwrite') @@ -183,11 +170,11 @@ $cli if ($var['filter'] === 'domainTarget') { if ($input[$var['name']] !== 'localhost') { - Console::warning("If you haven't already done so, make sure you create an 'A' or 'AAAA' DNS record for '".$input[$var['name']]."' pointing to your external server IP. \nYour DNS Table should look like so: "); - formatArray([ - 'A/AAAA' => ['@', 'Your IP'], - ]); - Console::warning("Use 'AAAA' if you have an IPv6 address and 'A' if you have an IPv4 address."); + Console::info("\nIf you haven't already done so, set the following record for {$input[$var['name']]} on your DNS provider: \n"); + $mask = "%-15.15s %-10.10s %-30.30s\n"; + printf($mask, "Type", "Name", "Value"); + printf($mask, "A or AAAA", "@", ""); + Console::info("\nUse 'AAAA' if you're using an IPv6 address and 'A' if you're using an IPv4 address.\n"); } } } From 859444703faeaee9fb2123aef719cb9c989a3c73 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 1 Dec 2021 02:41:53 +0400 Subject: [PATCH 003/134] feat: update PR --- app/tasks/install.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tasks/install.php b/app/tasks/install.php index 9f3f138dd9..6ada31d09e 100644 --- a/app/tasks/install.php +++ b/app/tasks/install.php @@ -170,11 +170,11 @@ $cli if ($var['filter'] === 'domainTarget') { if ($input[$var['name']] !== 'localhost') { - Console::info("\nIf you haven't already done so, set the following record for {$input[$var['name']]} on your DNS provider: \n"); + Console::warning("\nIf you haven't already done so, set the following record for {$input[$var['name']]} on your DNS provider:\n"); $mask = "%-15.15s %-10.10s %-30.30s\n"; printf($mask, "Type", "Name", "Value"); printf($mask, "A or AAAA", "@", ""); - Console::info("\nUse 'AAAA' if you're using an IPv6 address and 'A' if you're using an IPv4 address.\n"); + Console::warning("\nUse 'AAAA' if you're using an IPv6 address and 'A' if you're using an IPv4 address.\n"); } } } From af599ceba84aac33b389681fc5081eb473d87a8c Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 5 May 2022 15:07:32 +0200 Subject: [PATCH 004/134] Add authentik as a OAuth provider --- app/config/providers.php | 10 + app/views/console/users/oauth/authentik.phtml | 12 + public/dist/scripts/app-all.js | 4 +- public/dist/scripts/app.js | 4 +- public/images/users/authentik.png | Bin 0 -> 827 bytes public/scripts/views/forms/oauth-custom.js | 4 + src/Appwrite/Auth/OAuth2/Authentik.php | 209 ++++++++++++++++++ 7 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 app/views/console/users/oauth/authentik.phtml create mode 100644 public/images/users/authentik.png create mode 100644 src/Appwrite/Auth/OAuth2/Authentik.php diff --git a/app/config/providers.php b/app/config/providers.php index 1d364a3fcf..e92c0e90a2 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -31,6 +31,16 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false, ], + 'authentik' => [ + 'name' => 'authentik', + 'developers' => 'https://goauthentik.io/docs/', + 'icon' => 'icon-authentik', + 'enabled' => true, + 'sandbox' => false, + 'form' => 'authentik.phtml', + 'beta' => true, + 'mock' => false, + ], 'bitbucket' => [ 'name' => 'BitBucket', 'developers' => 'https://developer.atlassian.com/bitbucket', diff --git a/app/views/console/users/oauth/authentik.phtml b/app/views/console/users/oauth/authentik.phtml new file mode 100644 index 0000000000..ba1119879c --- /dev/null +++ b/app/views/console/users/oauth/authentik.phtml @@ -0,0 +1,12 @@ +getParam('provider', ''); +?> + + + + + + + + + \ No newline at end of file diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 23968d7bbe..45f30ad083 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -3835,8 +3835,8 @@ 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=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-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")} +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"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"},"Authentik":{"clientSecret":"oauth2AuthentikClientSecret","authentikDomain":"oauth2AuthentikDomain"}} +let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")} 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);} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 255f4767a0..79126cb407 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -785,8 +785,8 @@ 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=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-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")} +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"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"},"Authentik":{"clientSecret":"oauth2AuthentikClientSecret","authentikDomain":"oauth2AuthentikDomain"}} +let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")} 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);} diff --git a/public/images/users/authentik.png b/public/images/users/authentik.png new file mode 100644 index 0000000000000000000000000000000000000000..883e05422133bcb6d92c713470ec3bbfc1a138c3 GIT binary patch literal 827 zcmV-B1H}A^P)EX>4Tx04R}tkv&MmKp2MK{!x{RgB?U10#+vrq9Tr3g(6f4wL+^7CYSy}lZGV4 z#ZhoAIQX+zb#QUk)xlK|1b;vrotzY1q{Q!%LW>yhIPT%S?|r%Z4)8ZBOf`e!fT~$W zI++l%xm6+jiV)3i3?nEpQ=b#X6gi}Nh+x<5y+k~bOP6NqP-Zdk+{#M7IW z&Uv3W%1V+#d`>)O&;^Mfxh}i>#<}RQpJzsmOnRO;N-PvRSngm}GF0Lz;+Udpl<&{F ztZ?4qtX68Qbx;1nNM2i6<~pq*B(aDkh!7y7hB7L!5T{)u#YCFU6CVCS$1jpgCRZ7Z z91EyIh2;3b|KN9T&BEk_n-oa^p%>f!7y-h&K(lV!-^aGyJOKjFz?IhaR~x|0C+YRJ z7CQp^wt zj20<--Q(R|oqhYarq#b6055WRXbb0%00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3labT3lag+-G2N4000McNliru<^=@_ItDQ8ZN2~i0XRuSK~#9!?c7VUf-n#TP$O)| zmAGD3!g?qjS>*skt0Z(M=L{VME;pflpUVXR0000000000fKHXji(BruN2K)((_Fd? zBw7$PgDJv@6-p6CtWd6wQHOdH?Kr9>8WM~^1cE>Wf zyu!%7$EQEk*8&)(xoiceA%W%sTi3yX>^<=sVKD4F5OYYPhB(wu3S@0!7sj?y>*;S* zieCfzb&=XpVaxjVCJUqDq`vlJRY9B>h?{RP6bJ$l2#p=;8D1Eo46_0@~G zSGXFYCAHUj?d3wVtZ|Hd!9D)-82|tP000000002G^#p64g7^DSR#yN3002ovPDHLk FV1lAAVwwN| literal 0 HcmV?d00001 diff --git a/public/scripts/views/forms/oauth-custom.js b/public/scripts/views/forms/oauth-custom.js index ca2d3b2759..f8d9a4ba1b 100644 --- a/public/scripts/views/forms/oauth-custom.js +++ b/public/scripts/views/forms/oauth-custom.js @@ -25,6 +25,10 @@ "Auth0": { "clientSecret": "oauth2Auth0ClientSecret", "auth0Domain": "oauth2Auth0Domain" + }, + "Authentik": { + "clientSecret": "oauth2AuthentikClientSecret", + "authentikDomain": "oauth2AuthentikDomain" } } let provider = element.getAttribute("data-forms-oauth-custom"); diff --git a/src/Appwrite/Auth/OAuth2/Authentik.php b/src/Appwrite/Auth/OAuth2/Authentik.php new file mode 100644 index 0000000000..b4c6d1cfbd --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/Authentik.php @@ -0,0 +1,209 @@ +getAuthentikDomain().'/application/o/authorize?'.\http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'state'=> \json_encode($this->state), + 'scope'=> \implode(' ', $this->getScopes()), + 'response_type' => 'code' + ]); + } + + /** + * @param string $code + * + * @return array + */ + protected function getTokens(string $code): array + { + if(empty($this->tokens)) { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + 'https://'.$this->getAuthentikDomain().'/application/o/token/', + $headers, + \http_build_query([ + 'code' => $code, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'grant_type' => 'authorization_code' + ]) + ), true); + } + return $this->tokens; + } + + + /** + * @param string $refreshToken + * + * @return array + */ + public function refreshTokens(string $refreshToken): array + { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + 'https://'.$this->getAuthentikDomain().'/application/o/token/', + $headers, + \http_build_query([ + 'refresh_token' => $refreshToken, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'grant_type' => 'refresh_token' + ]) + ), true); + + if(empty($this->tokens['refresh_token'])) { + $this->tokens['refresh_token'] = $refreshToken; + } + + return $this->tokens; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserID(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['sub'])) { + return $user['sub']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserEmail(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['email'])) { + return $user['email']; + } + + return ''; + } + + /** + * @param string $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): array + { + if (empty($this->user)) { + $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; + $user = $this->request('GET', 'https://'.$this->getAuthentikDomain().'/application/o/userinfo/', $headers); + $this->user = \json_decode($user, true); + } + + return $this->user; + } + + /** + * Extracts the Client Secret from the JSON stored in appSecret + * + * @return string + */ + protected function getClientSecret(): string + { + $secret = $this->getAppSecret(); + + return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + } + + /** + * Extracts the authentik Domain from the JSON stored in appSecret + * + * @return string + */ + protected function getAuthentikDomain(): string + { + $secret = $this->getAppSecret(); + return (isset($secret['authentikDomain'])) ? $secret['authentikDomain'] : ''; + } + + /** + * Decode the JSON stored in appSecret + * + * @return array + */ + protected function getAppSecret(): array + { + try { + $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); + } catch (\Throwable $th) { + throw new \Exception('Invalid secret'); + } + return $secret; + } +} \ No newline at end of file From 4e26ec98b6fc467bf8464d5f1c37092319668980 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 5 May 2022 18:29:00 +0200 Subject: [PATCH 005/134] remove beta flag as this is a stable-ish implementation --- app/config/providers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/providers.php b/app/config/providers.php index e92c0e90a2..6cb4357f96 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -38,7 +38,7 @@ return [ // Ordered by ABC. 'enabled' => true, 'sandbox' => false, 'form' => 'authentik.phtml', - 'beta' => true, + 'beta' => false, 'mock' => false, ], 'bitbucket' => [ From f70e2479062b4c47da0ceff0e3ddf0a49c1c5803 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Mon, 9 May 2022 13:13:41 +0100 Subject: [PATCH 006/134] Update app/tasks/install.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matej Bačo --- app/tasks/install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tasks/install.php b/app/tasks/install.php index 6ada31d09e..da2ad8d5a6 100644 --- a/app/tasks/install.php +++ b/app/tasks/install.php @@ -173,7 +173,7 @@ $cli Console::warning("\nIf you haven't already done so, set the following record for {$input[$var['name']]} on your DNS provider:\n"); $mask = "%-15.15s %-10.10s %-30.30s\n"; printf($mask, "Type", "Name", "Value"); - printf($mask, "A or AAAA", "@", ""); + printf($mask, "A or AAAA", "@", ""); Console::warning("\nUse 'AAAA' if you're using an IPv6 address and 'A' if you're using an IPv4 address.\n"); } } From c3ab01afd4251361124bcceb659c597b6fe72a60 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 18 May 2022 10:25:52 +0200 Subject: [PATCH 007/134] Add types and missing function --- src/Appwrite/Auth/OAuth2/Authentik.php | 27 ++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Authentik.php b/src/Appwrite/Auth/OAuth2/Authentik.php index b4c6d1cfbd..6ef701fc32 100644 --- a/src/Appwrite/Auth/OAuth2/Authentik.php +++ b/src/Appwrite/Auth/OAuth2/Authentik.php @@ -9,10 +9,10 @@ use Appwrite\Auth\OAuth2; class Authentik extends OAuth2 { - /** + /** * @var array */ - protected $scopes = [ + protected array $scopes = [ 'openid', 'profile', 'email', @@ -22,12 +22,12 @@ class Authentik extends OAuth2 /** * @var array */ - protected $user = []; + protected array $user = []; /** * @var array */ - protected $tokens = []; + protected array $tokens = []; /** * @return string @@ -137,6 +137,25 @@ class Authentik extends OAuth2 return ''; } + /** + * Check if the User email is verified + * + * + * @param string $accessToken + * + * @return bool + */ + public function isEmailVerified(string $accessToken): bool + { + $user = $this->getUser($accessToken); + + if ($user['email_verified'] ?? false) { + return true; + } + + return false; + } + /** * @param string $accessToken * From 0c55a608971e8dd82a1d4a19b2894c343cedab65 Mon Sep 17 00:00:00 2001 From: Simon Giesel Date: Thu, 9 Jun 2022 10:51:27 +0200 Subject: [PATCH 008/134] Apply suggestions from code review Co-authored-by: Christy Jacob --- src/Appwrite/Auth/OAuth2/Authentik.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Authentik.php b/src/Appwrite/Auth/OAuth2/Authentik.php index 6ef701fc32..f07800bfd6 100644 --- a/src/Appwrite/Auth/OAuth2/Authentik.php +++ b/src/Appwrite/Auth/OAuth2/Authentik.php @@ -140,7 +140,6 @@ class Authentik extends OAuth2 /** * Check if the User email is verified * - * * @param string $accessToken * * @return bool @@ -197,7 +196,7 @@ class Authentik extends OAuth2 { $secret = $this->getAppSecret(); - return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + return $secret['clientSecret'] ?? ''; } /** @@ -208,7 +207,7 @@ class Authentik extends OAuth2 protected function getAuthentikDomain(): string { $secret = $this->getAppSecret(); - return (isset($secret['authentikDomain'])) ? $secret['authentikDomain'] : ''; + return $secret['authentikDomain'] ?? ''; } /** From 47a35417875adc2921cd618495c85dcb17ae04f5 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 13 Jun 2022 21:21:54 +0200 Subject: [PATCH 009/134] Fix linting issues --- src/Appwrite/Auth/OAuth2/Authentik.php | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Authentik.php b/src/Appwrite/Auth/OAuth2/Authentik.php index f07800bfd6..16822e2c9f 100644 --- a/src/Appwrite/Auth/OAuth2/Authentik.php +++ b/src/Appwrite/Auth/OAuth2/Authentik.php @@ -8,7 +8,7 @@ use Appwrite\Auth\OAuth2; // https://goauthentik.io/docs/providers/oauth2/ class Authentik extends OAuth2 -{ +{ /** * @var array */ @@ -18,17 +18,17 @@ class Authentik extends OAuth2 'email', 'offline_access' ]; - + /** * @var array */ protected array $user = []; - + /** * @var array */ protected array $tokens = []; - + /** * @return string */ @@ -42,11 +42,11 @@ class Authentik extends OAuth2 */ public function getLoginURL(): string { - return 'https://'.$this->getAuthentikDomain().'/application/o/authorize?'.\http_build_query([ + return 'https://' . $this->getAuthentikDomain() . '/application/o/authorize?' . \http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, - 'state'=> \json_encode($this->state), - 'scope'=> \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state), + 'scope' => \implode(' ', $this->getScopes()), 'response_type' => 'code' ]); } @@ -58,11 +58,11 @@ class Authentik extends OAuth2 */ protected function getTokens(string $code): array { - if(empty($this->tokens)) { + if (empty($this->tokens)) { $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', - 'https://'.$this->getAuthentikDomain().'/application/o/token/', + 'https://' . $this->getAuthentikDomain() . '/application/o/token/', $headers, \http_build_query([ 'code' => $code, @@ -76,8 +76,8 @@ class Authentik extends OAuth2 } return $this->tokens; } - - + + /** * @param string $refreshToken * @@ -88,7 +88,7 @@ class Authentik extends OAuth2 $headers = ['Content-Type: application/x-www-form-urlencoded']; $this->tokens = \json_decode($this->request( 'POST', - 'https://'.$this->getAuthentikDomain().'/application/o/token/', + 'https://' . $this->getAuthentikDomain() . '/application/o/token/', $headers, \http_build_query([ 'refresh_token' => $refreshToken, @@ -98,7 +98,7 @@ class Authentik extends OAuth2 ]) ), true); - if(empty($this->tokens['refresh_token'])) { + if (empty($this->tokens['refresh_token'])) { $this->tokens['refresh_token'] = $refreshToken; } @@ -113,11 +113,11 @@ class Authentik extends OAuth2 public function getUserID(string $accessToken): string { $user = $this->getUser($accessToken); - + if (isset($user['sub'])) { return $user['sub']; } - + return ''; } @@ -129,19 +129,19 @@ class Authentik extends OAuth2 public function getUserEmail(string $accessToken): string { $user = $this->getUser($accessToken); - + if (isset($user['email'])) { return $user['email']; } - + return ''; } /** * Check if the User email is verified - * + * * @param string $accessToken - * + * * @return bool */ public function isEmailVerified(string $accessToken): bool @@ -163,14 +163,14 @@ class Authentik extends OAuth2 public function getUserName(string $accessToken): string { $user = $this->getUser($accessToken); - + if (isset($user['name'])) { return $user['name']; } - + return ''; } - + /** * @param string $accessToken * @@ -179,8 +179,8 @@ class Authentik extends OAuth2 protected function getUser(string $accessToken): array { if (empty($this->user)) { - $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; - $user = $this->request('GET', 'https://'.$this->getAuthentikDomain().'/application/o/userinfo/', $headers); + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; + $user = $this->request('GET', 'https://' . $this->getAuthentikDomain() . '/application/o/userinfo/', $headers); $this->user = \json_decode($user, true); } @@ -189,34 +189,34 @@ class Authentik extends OAuth2 /** * Extracts the Client Secret from the JSON stored in appSecret - * + * * @return string */ protected function getClientSecret(): string { $secret = $this->getAppSecret(); - return $secret['clientSecret'] ?? ''; + return $secret['clientSecret'] ?? ''; } /** * Extracts the authentik Domain from the JSON stored in appSecret - * + * * @return string */ protected function getAuthentikDomain(): string { $secret = $this->getAppSecret(); - return $secret['authentikDomain'] ?? ''; + return $secret['authentikDomain'] ?? ''; } /** * Decode the JSON stored in appSecret - * + * * @return array */ protected function getAppSecret(): array - { + { try { $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); } catch (\Throwable $th) { @@ -224,4 +224,4 @@ class Authentik extends OAuth2 } return $secret; } -} \ No newline at end of file +} From cdace54b7a27e582486f1f0ec0ea3559410d571e Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 28 Jun 2022 17:01:14 +0200 Subject: [PATCH 010/134] feat: node sdk version 7.0.1 --- app/config/platforms.php | 2 +- composer.json | 2 +- composer.lock | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/config/platforms.php b/app/config/platforms.php index b31ea27f02..851b9210a6 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -208,7 +208,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '7.0.0', + 'version' => '7.0.1', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, diff --git a/composer.json b/composer.json index e33cc16373..a729f5f0e9 100644 --- a/composer.json +++ b/composer.json @@ -71,7 +71,7 @@ } ], "require-dev": { - "appwrite/sdk-generator": "0.19.2", + "appwrite/sdk-generator": "0.19.3", "phpunit/phpunit": "9.5.20", "squizlabs/php_codesniffer": "^3.6", "swoole/ide-helper": "4.8.9", diff --git a/composer.lock b/composer.lock index af31b62121..6f95736652 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "27f63bdbc1b54f0f5cee981afe6da9b8", + "content-hash": "5f84d34aaf40746cf5a5ea3d231b9aee", "packages": [ { "name": "adhocore/jwt", @@ -2828,16 +2828,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.19.2", + "version": "0.19.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "91892b98880767019f340dee0074dcdc75329f94" + "reference": "a27cdc6b7e60e776a175be023e59cfd0e85bf4fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/91892b98880767019f340dee0074dcdc75329f94", - "reference": "91892b98880767019f340dee0074dcdc75329f94", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/a27cdc6b7e60e776a175be023e59cfd0e85bf4fa", + "reference": "a27cdc6b7e60e776a175be023e59cfd0e85bf4fa", "shasum": "" }, "require": { @@ -2872,9 +2872,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.19.2" + "source": "https://github.com/appwrite/sdk-generator/tree/0.19.3" }, - "time": "2022-06-28T11:15:16+00:00" + "time": "2022-06-28T14:56:03+00:00" }, { "name": "doctrine/instantiator", From d02bb73fe87c00e26d628d588b22b14e65686fbb Mon Sep 17 00:00:00 2001 From: Everly Precia Suresh <77877486+everly-gif@users.noreply.github.com> Date: Tue, 28 Jun 2022 22:31:00 +0530 Subject: [PATCH 011/134] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6d160efed..96dd2856c2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ English | [简体中文](README-CN.md) -[**Appwrite 0.14 has been released! Learn what's new!**](https://dev.to/appwrite/announcing-appwrite-014-with-11-cloud-function-runtimes-36f5) +[**Appwrite 0.15 has been released! Learn what's new!**](https://dev.to/appwrite/announcing-appwrite-015-with-phone-authentication-more-5cjj) Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster. From 9fdaa92bd1ea23c53f408c39f15d9d008c3fb4fe Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 29 Jun 2022 01:04:08 +0200 Subject: [PATCH 012/134] feat: trigger sms for createVerification --- app/controllers/api/account.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index f61490b116..5144441a1f 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2301,7 +2301,9 @@ App::post('/v1/account/verification/phone') $messaging ->setRecipient($user->getAttribute('phone')) - ->setMessage($secret); + ->setMessage($secret) + ->trigger() + ; $events ->setParam('userId', $user->getId()) From 8ee78137e6428f6ef858a02cb69612a7fff93b72 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 29 Jun 2022 01:07:48 +0200 Subject: [PATCH 013/134] fix: internal attribute and index key on migration --- src/Appwrite/Migration/Version/V14.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Migration/Version/V14.php b/src/Appwrite/Migration/Version/V14.php index 308850d2c9..6f80bc2e60 100644 --- a/src/Appwrite/Migration/Version/V14.php +++ b/src/Appwrite/Migration/Version/V14.php @@ -583,11 +583,11 @@ class V14 extends Migration break; case 'attributes': case 'indexes': + $internalId = $this->projectDB->getDocument('database_1', $document->getAttribute('collectionId'))->getInternalId(); /** * Add Internal ID 'collectionId' for Subqueries. */ if (!empty($document->getAttribute('collectionId')) && is_null($document->getAttribute('collectionInternalId'))) { - $internalId = $this->projectDB->getDocument('database_1', $document->getAttribute('collectionId'))->getInternalId(); $document->setAttribute('collectionInternalId', $internalId); } /** @@ -608,7 +608,7 @@ class V14 extends Migration * Re-create Collection Document */ $this->projectDB->deleteDocument($document->getCollection(), $document->getId()); - $this->projectDB->createDocument($document->getCollection(), $document->setAttribute('$id', "1_{$document->getInternalId()}_{$document->getAttribute('key')}")); + $this->projectDB->createDocument($document->getCollection(), $document->setAttribute('$id', "1_{$internalId}_{$document->getAttribute('key')}")); } catch (\Throwable $th) { Console::warning("Create Collection Document - {$th->getMessage()}"); } From 33850dcfffa11366168d3cabf30fafb1dc3db048 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 29 Jun 2022 02:37:52 +0200 Subject: [PATCH 014/134] feat: update docs --- docs/references/account/create-phone-session.md | 2 +- docs/references/account/create-phone-verification.md | 2 +- docs/references/account/update-phone-session.md | 4 +--- docs/references/account/update-phone.md | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/references/account/create-phone-session.md b/docs/references/account/create-phone-session.md index a03c7adc17..677c315ac2 100644 --- a/docs/references/account/create-phone-session.md +++ b/docs/references/account/create-phone-session.md @@ -1 +1 @@ -Sends the user a SMS with a secret key for creating a session. Use the returned user ID and the secret to submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes. \ No newline at end of file +Sends the user an SMS with a secret key for creating a session. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes. \ No newline at end of file diff --git a/docs/references/account/create-phone-verification.md b/docs/references/account/create-phone-verification.md index 0acc8f6d30..28627b439f 100644 --- a/docs/references/account/create-phone-verification.md +++ b/docs/references/account/create-phone-verification.md @@ -1 +1 @@ -Use this endpoint to send a verification message to your user's phone number to confirm they are the valid owners of that address. The provided secret should allow you to complete the verification process by verifying both the **userId** and **secret** parameters. Learn more about how to [complete the verification process](/docs/client/account#accountUpdatePhoneVerification). The verification link sent to the user's phone number is valid for 15 minutes. \ No newline at end of file +Use this endpoint to send a verification SMS to the currently logged in user. This endpoint is meant for use after updating a user's phone number using the [accountUpdatePhone](/docs/client/account#accountUpdatePhone) endpoint. Learn more about how to [complete the verification process](/docs/client/account#accountUpdatePhoneVerification). The verification code sent to the user's phone number is valid for 15 minutes. \ No newline at end of file diff --git a/docs/references/account/update-phone-session.md b/docs/references/account/update-phone-session.md index b85036082f..7bd8e61617 100644 --- a/docs/references/account/update-phone-session.md +++ b/docs/references/account/update-phone-session.md @@ -1,3 +1 @@ -Use this endpoint to complete creating the session with the Magic URL. Both the **userId** and **secret** arguments will be passed as query parameters to the redirect URL you have provided when sending your request to the [POST /account/sessions/magic-url](/docs/client/account#accountCreateMagicURLSession) endpoint. - -Please note that in order to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface. \ No newline at end of file +Use this endpoint to complete creating a session with SMS. Use the **userId** from the [createPhoneSession](/docs/client/account#accountCreatePhoneSession) endpoint and the **secret** received via SMS to successfully update and confirm the phone session. \ No newline at end of file diff --git a/docs/references/account/update-phone.md b/docs/references/account/update-phone.md index 442fc06209..934d9cad7f 100644 --- a/docs/references/account/update-phone.md +++ b/docs/references/account/update-phone.md @@ -1 +1 @@ -Update currently logged in user account phone number. After changing phone number, the user confirmation status will get reset. A new confirmation SMS is not sent automatically however you can use the phone confirmation endpoint again to send the confirmation SMS. \ No newline at end of file +Update the currently logged in user's phone number. After updating the phone number, the phone verification status will be reset. A confirmation SMS is not sent automatically, however you can use the [POST /account/verification/phone](/docs/client/account#accountCreatePhoneVerification) endpoint to send a confirmation SMS. \ No newline at end of file From f387ce08bcb985ae32e21a5c1c8ea4c2f3e1946c Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 29 Jun 2022 12:11:45 +0200 Subject: [PATCH 015/134] fix: ui issues --- app/views/console/databases/collection.phtml | 2 +- public/dist/scripts/app-all.js | 2 +- public/dist/scripts/app-dep.js | 2 +- public/scripts/dependencies/appwrite.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/console/databases/collection.phtml b/app/views/console/databases/collection.phtml index 5121cf7141..e8ede3df5d 100644 --- a/app/views/console/databases/collection.phtml +++ b/app/views/console/databases/collection.phtml @@ -92,7 +92,7 @@ $logs = $this->getParam('logs', null);