From 44970cc628a869caf817b9d03ba66cef6f896d94 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sun, 28 Aug 2022 11:01:46 +1200 Subject: [PATCH] Sync master --- app/config/providers.php | 10 + app/controllers/api/teams.php | 4 + public/images/users/autodesk.png | Bin 882 -> 1278 bytes public/images/users/etsy.png | Bin 0 -> 5731 bytes src/Appwrite/Auth/OAuth2/Etsy.php | 200 +++++++++++++++++++ tests/e2e/Services/Teams/TeamsBase.php | 3 +- tests/e2e/Services/Teams/TeamsBaseClient.php | 9 +- 7 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 public/images/users/etsy.png create mode 100644 src/Appwrite/Auth/OAuth2/Etsy.php diff --git a/app/config/providers.php b/app/config/providers.php index 205d07123..98ad93b30 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -121,6 +121,16 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false, ], + 'etsy' => [ + 'name' => 'Etsy', + 'developers' => 'https://developers.etsy.com/', + 'icon' => 'icon-etsy', + 'enabled' => true, + 'sandbox' => false, + 'form' => false, + 'beta' => false, + 'mock' => false, + ], 'facebook' => [ 'name' => 'Facebook', 'developers' => 'https://developers.facebook.com/', diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index d0c9f6901..f5b1de2a7 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -74,6 +74,10 @@ App::post('/v1/teams') ]))); if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode + if (!\in_array('owner', $roles)) { + $roles[] = 'owner'; + } + $membershipId = ID::unique(); $membership = new Document([ '$id' => $membershipId, diff --git a/public/images/users/autodesk.png b/public/images/users/autodesk.png index c7ad94f61acf7ad88f069dfbcede47b3d014efa3..2ab801606858ff753f1a492a789a13b28895ebb9 100644 GIT binary patch literal 1278 zcmeAS@N?(olHy`uVBq!ia0vp^DImVALvfLhz{&kvUwhoY|8vgf@0RVS)G=pgEC*E5KNJ(`aJLdM|*Doo%hd+N> zvaz#&{P^%!{%oJ=0{r~^SFT)HuzR<3U0q#UVuZAcipql*FHU6s%`r6+ov>w#iI1`Ax<%YEJ@2(ySlnc$jZ8Ucy!EF z{vzw9VE;o*QnItNlk>^5XRnHH90V#lY@n~NZ(?e?aPMB(d-v|~-CFg(fa&M4-Dl4D z`1tuL+1bVI-)!YBbVB?w6Em}+fkDHiOFHkg z^VZqBA3aJcR9Mq=M@cbj_Tn{bc=qny8*p2G(&;Y_Lc+p^wzj-m6<>7Sx_{q2HC5Hm z-@kObzn|Z+XU~?I-P_`DWm-jgx_VZ2_QRJiSKii%W|o(i-@3bEt5bD-z4;Y;eSLmy zZS7~QljhIw|M;=6rL|Sk=+w0bPoJhH2>c19bS5!!3J-$?>oVBWjg@q-d zu1@Zi{PV6|d-k}LmYRlzhx2X?KmBIcE~}D~l0WYsPFvZqWXX~T4;~zt5P6@suDbg0 zi4z>VcJ2D`!W5G*N?aQW@*n>KE2?CRoLQ6jPO%a@Yeld{6X$xC{7zPlnD`R(4_ zyP2Pl96Hpr&@+1H`fZ=|_4EWpL{d1~q~4Tq22TF==FOUIO}qB*H_us+HDTh!#JeS-j~NG^)|Ok z;nn=;(9or^k7GlD$?oR*$-lmS-MUSA_m(YBwD{)Naz4>_C@U}D${w_K?b?9zI(3UV zjNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^> z;_2(k{)|mbKwLQc!O15;`6^Er$B>MBZ*Tf~ganE(Jg833@e#Nb!o{*s%u~QIpo3eH z>0)9YSjdvuY2})&iA|b%kNb$cKrMI(QQU&0Y?@=7bx-1b;$uWjwzRJ-nM%G z(~RRqZ^WG&*M5J0#(%+VJ)d;HbGF;vxt||01nJgw-q%07&+YB{Ka65`w+cx{Jaqjq z@o2&mbK{oIhPW4%JI<{Y=V$!8IJ;!>-3l?LU$5tWRJr{?@mKP5mH7!u?{CN5lIW=x zUvd3x8QXGI{k*yj#w&Cgr`BA{$qo>F>$By|4XKcn&z{+5ZYd}(5K^9N|CaaEf#x__ zsfbSr0RnqPxYqCbHg)>a&lBSQ?c+U>6mRnGR;fvaMeh&0Sf}?U9!*|{{(OynH)EFh zGtKqit}1wPpY7S$UAMjP)7;4JmBJZmM<(3dT+b7Dm`CMu%jYiw^Xd$j+|T_n+fKUu zy8CL8zJ%8*59eu2Kg=GS%%^2|=^yWW%NK8oY`3zm`xdt3TVtFY&%|r|`hUf?mA&`; z%eMEz|AbzVV@_T9_qEP#DSY>ChI9901=YLeT?%)@H_J}GQgYrw>(@FKqhlq%i;Glb zSMr#Nr93+ra@%BYRmzhCdy9`~J|zWAIF_;X`2KDKmYVBIq4#bz`ais@Yr8Wpx3*6C z!a=)c?ym z#;-Gg2~)MiHKHUXu_V85mgeXFNpF ckei>9nO2EggICanSfB<5Pgg&ebxsLQ0Jr3Ir~m)} diff --git a/public/images/users/etsy.png b/public/images/users/etsy.png new file mode 100644 index 0000000000000000000000000000000000000000..ac038ec97e0b368273ec8417289da774a250b3c1 GIT binary patch literal 5731 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 literal 0 HcmV?d00001 diff --git a/src/Appwrite/Auth/OAuth2/Etsy.php b/src/Appwrite/Auth/OAuth2/Etsy.php new file mode 100644 index 000000000..7ff16fcb7 --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/Etsy.php @@ -0,0 +1,200 @@ +pkce)) { + $this->pkce = \bin2hex(\random_bytes(rand(43, 128))); + } + + return $this->pkce; + } + + /** + * @return string + */ + public function getName(): string + { + return 'etsy'; + } + + /** + * @return string + */ + public function getLoginURL(): string + { + return 'https://www.etsy.com/oauth/connect/oauth/authorize?' . \http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'response_type' => 'code', + 'state' => \json_encode($this->state), + 'scope' => $this->scopes, + 'code_challenge' => $this->getPKCE(), + 'code_challenge_method' => 'S256', + ]); + } + + /** + * @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', + $this->endpoint . '/oauth/token', + $headers, + \http_build_query([ + 'grant_type' => 'authorization_code', + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'code' => $code, + 'code_verifier' => $this->getPKCE(), + ]) + ), 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', + $this->endpoint . '/oauth/token', + $headers, + \http_build_query([ + 'grant_type' => 'refresh_token', + 'client_id' => $this->appID, + 'refresh_token' => $refreshToken, + ]) + ), true); + + if (empty($this->tokens['refresh_token'])) { + $this->tokens['refresh_token'] = $refreshToken; + } + + return $this->tokens; + } + + /** + * @param $accessToken + * + * @return string + */ + public function getUserID(string $accessToken): string + { + $components = explode('.', $accessToken); + + return $components[0]; + } + + /** + * @param $accessToken + * + * @return string + */ + public function getUserEmail(string $accessToken): string + { + return $this->getUser($accessToken)['primary_email']; + } + + /** + * Check if the OAuth email is verified + * + * OAuth is only allowed if account has been verified through Etsy, itself. + * + * @param string $accessToken + * + * @return bool + */ + public function isEmailVerified(string $accessToken): bool + { + $email = $this->getUserEmail($accessToken); + + return !empty($email); + } + + /** + * @param $accessToken + * + * @return string + */ + public function getUserName(string $accessToken): string + { + return $this->getUser($accessToken)['login_name']; + } + + /** + * @param string $accessToken + * + * @return array + */ + protected function getUser(string $accessToken): array + { + if (!empty($this->user)) { + return $this->user; + } + + $headers = ['Authorization: Bearer ' . $accessToken]; + + $this->user = \json_decode($this->request( + 'GET', + 'https://api.etsy.com/v3/application/users/' . $this->getUserID($accessToken), + ), true); + + return $this->user; + } +} diff --git a/tests/e2e/Services/Teams/TeamsBase.php b/tests/e2e/Services/Teams/TeamsBase.php index 2404ca324..addfcfa63 100644 --- a/tests/e2e/Services/Teams/TeamsBase.php +++ b/tests/e2e/Services/Teams/TeamsBase.php @@ -19,7 +19,8 @@ trait TeamsBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'teamId' => ID::unique(), - 'name' => 'Arsenal' + 'name' => 'Arsenal', + 'roles' => ['player'], ]); $this->assertEquals(201, $response1['headers']['status-code']); diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index e07e9fbb1..5fe5c370a 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -30,7 +30,8 @@ trait TeamsBaseClient $this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']); $this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']); $this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']); - $this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]); + $this->assertContains('owner', $response['body']['memberships'][0]['roles']); + $this->assertContains('player', $response['body']['memberships'][0]['roles']); $membershipId = $response['body']['memberships'][0]['$id']; @@ -47,7 +48,8 @@ trait TeamsBaseClient $this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']); $this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']); $this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']); - $this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]); + $this->assertContains('owner', $response['body']['memberships'][0]['roles']); + $this->assertContains('player', $response['body']['memberships'][0]['roles']); $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', @@ -62,7 +64,8 @@ trait TeamsBaseClient $this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']); $this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']); $this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']); - $this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]); + $this->assertContains('owner', $response['body']['memberships'][0]['roles']); + $this->assertContains('player', $response['body']['memberships'][0]['roles']); $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json',