2019-05-09 18:54:39 +12:00
< ? php
2020-06-29 05:31:21 +12:00
use Utopia\App ;
2019-05-09 18:54:39 +12:00
use Utopia\Exception ;
2019-06-09 20:51:10 +12:00
use Utopia\Response ;
2020-03-29 01:42:16 +13:00
use Utopia\Config\Config ;
2019-05-09 18:54:39 +12:00
use Utopia\Validator\Email ;
use Utopia\Validator\Text ;
use Utopia\Validator\Host ;
use Utopia\Validator\Range ;
use Utopia\Validator\ArrayList ;
use Utopia\Validator\WhiteList ;
2020-03-25 06:56:32 +13:00
use Appwrite\Auth\Auth ;
use Appwrite\Database\Database ;
use Appwrite\Database\Document ;
use Appwrite\Database\Validator\UID ;
use Appwrite\Database\Validator\Authorization ;
use Appwrite\Database\Exception\Duplicate ;
use Appwrite\Template\Template ;
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: post ( '/v1/teams' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Create Team' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'teams.write' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'teams' )
2020-01-31 05:18:46 +13:00
-> label ( 'sdk.method' , 'create' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/teams/create-team.md' )
2019-10-07 09:10:52 +13:00
-> param ( 'name' , null , function () { return new Text ( 100 ); }, 'Team name.' )
2020-02-03 08:30:40 +13:00
-> param ( 'roles' , [ 'owner' ], function () { return new ArrayList ( new Text ( 128 )); }, 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions).' , true )
2020-06-30 23:09:28 +12:00
-> action ( function ( $name , $roles , $response , $user , $projectDB , $mode ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var bool $mode */
Authorization :: disable ();
$team = $projectDB -> createDocument ([
'$collection' => Database :: SYSTEM_COLLECTION_TEAMS ,
'$permissions' => [
'read' => [ 'team:{self}' ],
'write' => [ 'team:{self}/owner' ],
],
'name' => $name ,
'sum' => ( $mode !== APP_MODE_ADMIN && $user -> getId ()) ? 1 : 0 ,
'dateCreated' => \time (),
]);
Authorization :: reset ();
if ( false === $team ) {
throw new Exception ( 'Failed saving team to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $mode !== APP_MODE_ADMIN && $user -> getId ()) { // Don't add user on server mode
$membership = new Document ([
'$collection' => Database :: SYSTEM_COLLECTION_MEMBERSHIPS ,
2019-09-07 05:10:41 +12:00
'$permissions' => [
2020-06-30 23:09:28 +12:00
'read' => [ 'user:' . $user -> getId (), 'team:' . $team -> getId ()],
'write' => [ 'user:' . $user -> getId (), 'team:' . $team -> getId () . '/owner' ],
2019-05-09 18:54:39 +12:00
],
2020-06-30 23:09:28 +12:00
'userId' => $user -> getId (),
'teamId' => $team -> getId (),
'roles' => $roles ,
'invited' => \time (),
'joined' => \time (),
'confirm' => true ,
'secret' => '' ,
2019-05-09 18:54:39 +12:00
]);
2020-06-30 23:09:28 +12:00
// Attach user to team
$user -> setAttribute ( 'memberships' , $membership , Document :: SET_TYPE_APPEND );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$user = $projectDB -> updateDocument ( $user -> getArrayCopy ());
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $user ) {
throw new Exception ( 'Failed saving user to DB' , 500 );
2019-05-09 18:54:39 +12:00
}
}
2020-06-30 23:09:28 +12:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> json ( $team -> getArrayCopy ())
;
}, [ 'response' , 'user' , 'projectDB' , 'mode' ]);
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/teams' )
2020-02-01 11:34:07 +13:00
-> desc ( 'List Teams' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'teams.read' )
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'list' )
-> label ( 'sdk.description' , '/docs/references/teams/list-teams.md' )
-> param ( 'search' , '' , function () { return new Text ( 256 ); }, 'Search term to filter your list results.' , true )
-> param ( 'limit' , 25 , function () { return new Range ( 0 , 100 ); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.' , true )
-> param ( 'offset' , 0 , function () { return new Range ( 0 , 2000 ); }, 'Results offset. The default value is 0. Use this param to manage pagination.' , true )
-> param ( 'orderType' , 'ASC' , function () { return new WhiteList ([ 'ASC' , 'DESC' ]); }, 'Order result by ASC or DESC order.' , true )
2020-06-30 23:09:28 +12:00
-> action ( function ( $search , $limit , $offset , $orderType , $response , $projectDB ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB -> getCollection ([
'limit' => $limit ,
'offset' => $offset ,
'orderField' => 'dateCreated' ,
'orderType' => $orderType ,
'orderCast' => 'int' ,
'search' => $search ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_TEAMS ,
],
]);
$response -> json ([ 'sum' => $projectDB -> getSum (), 'teams' => $results ]);
}, [ 'response' , 'projectDB' ]);
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/teams/:teamId' )
2020-02-01 11:34:07 +13:00
-> desc ( 'Get Team' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'teams.read' )
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'get' )
-> label ( 'sdk.description' , '/docs/references/teams/get-team.md' )
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $teamId , $response , $projectDB ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
2020-02-01 11:34:07 +13:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> getDocument ( $teamId );
2020-02-01 11:34:07 +13:00
2020-06-30 23:09:28 +12:00
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
2020-02-01 11:34:07 +13:00
}
2020-06-30 23:09:28 +12:00
$response -> json ( $team -> getArrayCopy ([]));
}, [ 'response' , 'projectDB' ]);
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: put ( '/v1/teams/:teamId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Update Team' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'teams.write' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'teams' )
2020-01-31 05:18:46 +13:00
-> label ( 'sdk.method' , 'update' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/teams/update-team.md' )
2019-10-07 09:10:52 +13:00
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
-> param ( 'name' , null , function () { return new Text ( 100 ); }, 'Team name.' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $teamId , $name , $response , $projectDB ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> getDocument ( $teamId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> updateDocument ( \array_merge ( $team -> getArrayCopy (), [
'name' => $name ,
]));
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $team ) {
throw new Exception ( 'Failed saving team to DB' , 500 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
$response -> json ( $team -> getArrayCopy ());
}, [ 'response' , 'projectDB' ]);
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: delete ( '/v1/teams/:teamId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Delete Team' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'teams.write' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'teams' )
2020-01-31 05:18:46 +13:00
-> label ( 'sdk.method' , 'delete' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/teams/delete-team.md' )
2019-10-07 09:10:52 +13:00
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $teamId , $response , $projectDB ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> getDocument ( $teamId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$memberships = $projectDB -> getCollection ([
'limit' => 2000 , // TODO add members limit
'offset' => 0 ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_MEMBERSHIPS ,
'teamId=' . $teamId ,
],
]);
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
foreach ( $memberships as $member ) {
if ( ! $projectDB -> deleteDocument ( $member -> getId ())) {
throw new Exception ( 'Failed to remove membership for team from DB' , 500 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! $projectDB -> deleteDocument ( $teamId )) {
throw new Exception ( 'Failed to remove team from DB' , 500 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
$response -> noContent ();
}, [ 'response' , 'projectDB' ]);
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: post ( '/v1/teams/:teamId/memberships' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Create Team Membership' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-02-10 05:53:33 +13:00
-> label ( 'scope' , 'teams.write' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'teams' )
2020-01-31 05:18:46 +13:00
-> label ( 'sdk.method' , 'createMembership' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/teams/create-team-membership.md' )
2019-10-07 09:10:52 +13:00
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
2020-02-10 18:58:29 +13:00
-> param ( 'email' , '' , function () { return new Email (); }, 'New team member email.' )
2019-10-07 09:10:52 +13:00
-> param ( 'name' , '' , function () { return new Text ( 100 ); }, 'New team member name.' , true )
2020-02-03 08:30:40 +13:00
-> param ( 'roles' , [], function () { return new ArrayList ( new Text ( 128 )); }, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions).' )
2020-06-30 23:09:28 +12:00
-> param ( 'url' , '' , function ( $clients ) { return new Host ( $clients ); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.' , false , [ 'clients' ]) // TODO add our own built-in confirm page
-> action ( function ( $teamId , $email , $name , $roles , $url , $response , $project , $user , $projectDB , $locale , $audit , $mail , $mode ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $mail */
/** @var bool $mode */
$name = ( empty ( $name )) ? $email : $name ;
$team = $projectDB -> getDocument ( $teamId );
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$memberships = $projectDB -> getCollection ([
'limit' => 50 ,
'offset' => 0 ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_MEMBERSHIPS ,
'teamId=' . $team -> getId (),
],
]);
$invitee = $projectDB -> getCollectionFirst ([ // Get user by email address
'limit' => 1 ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_USERS ,
'email=' . $email ,
],
]);
if ( empty ( $invitee )) { // Create new user if no user with same email found
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
Authorization :: disable ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
try {
$invitee = $projectDB -> createDocument ([
'$collection' => Database :: SYSTEM_COLLECTION_USERS ,
'$permissions' => [
'read' => [ 'user:{self}' , '*' ],
'write' => [ 'user:{self}' ],
],
'email' => $email ,
'emailVerification' => false ,
'status' => Auth :: USER_STATUS_UNACTIVATED ,
'password' => Auth :: passwordHash ( Auth :: passwordGenerator ()),
'password-update' => \time (),
'registration' => \time (),
'reset' => false ,
'name' => $name ,
'tokens' => [],
], [ 'email' => $email ]);
} catch ( Duplicate $th ) {
throw new Exception ( 'Account already exists' , 409 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
Authorization :: reset ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $invitee ) {
throw new Exception ( 'Failed saving user to DB' , 500 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$isOwner = false ;
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
foreach ( $memberships as $member ) {
if ( $member -> getAttribute ( 'userId' ) == $invitee -> getId ()) {
throw new Exception ( 'User has already been invited or is already a member of this team' , 409 );
2020-06-09 04:15:35 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $member -> getAttribute ( 'userId' ) == $user -> getId () && \in_array ( 'owner' , $member -> getAttribute ( 'roles' , []))) {
$isOwner = true ;
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! $isOwner && ( APP_MODE_ADMIN !== $mode )) {
throw new Exception ( 'User is not allowed to send invitations for this team' , 401 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$secret = Auth :: tokenGenerator ();
$membership = new Document ([
'$collection' => Database :: SYSTEM_COLLECTION_MEMBERSHIPS ,
'$permissions' => [
'read' => [ '*' ],
'write' => [ 'user:' . $invitee -> getId (), 'team:' . $team -> getId () . '/owner' ],
],
'userId' => $invitee -> getId (),
'teamId' => $team -> getId (),
'roles' => $roles ,
'invited' => \time (),
'joined' => 0 ,
'confirm' => ( APP_MODE_ADMIN === $mode ),
'secret' => Auth :: hash ( $secret ),
]);
if ( APP_MODE_ADMIN === $mode ) { // Allow admin to create membership
Authorization :: disable ();
$membership = $projectDB -> createDocument ( $membership -> getArrayCopy ());
Authorization :: reset ();
} else {
$membership = $projectDB -> createDocument ( $membership -> getArrayCopy ());
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $membership ) {
throw new Exception ( 'Failed saving membership to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$url = Template :: parseURL ( $url );
$url [ 'query' ] = Template :: mergeQuery ((( isset ( $url [ 'query' ])) ? $url [ 'query' ] : '' ), [ 'inviteId' => $membership -> getId (), 'teamId' => $team -> getId (), 'userId' => $invitee -> getId (), 'secret' => $secret , 'teamId' => $teamId ]);
$url = Template :: unParseURL ( $url );
$body = new Template ( __DIR__ . '/../../config/locale/templates/email-base.tpl' );
$content = new Template ( __DIR__ . '/../../config/locale/translations/templates/' . $locale -> getText ( 'account.emails.invitation.body' ));
$cta = new Template ( __DIR__ . '/../../config/locale/templates/email-cta.tpl' );
$body
-> setParam ( '{{content}}' , $content -> render ())
-> setParam ( '{{cta}}' , $cta -> render ())
-> setParam ( '{{title}}' , $locale -> getText ( 'account.emails.invitation.title' ))
-> setParam ( '{{direction}}' , $locale -> getText ( 'settings.direction' ))
-> setParam ( '{{project}}' , $project -> getAttribute ( 'name' , [ '[APP-NAME]' ]))
-> setParam ( '{{team}}' , $team -> getAttribute ( 'name' , '[TEAM-NAME]' ))
-> setParam ( '{{owner}}' , $user -> getAttribute ( 'name' , '' ))
-> setParam ( '{{redirect}}' , $url )
-> setParam ( '{{bg-body}}' , '#f6f6f6' )
-> setParam ( '{{bg-content}}' , '#ffffff' )
-> setParam ( '{{bg-cta}}' , '#3498db' )
-> setParam ( '{{bg-cta-hover}}' , '#34495e' )
-> setParam ( '{{text-content}}' , '#000000' )
-> setParam ( '{{text-cta}}' , '#ffffff' )
;
if ( APP_MODE_ADMIN !== $mode ) { // No need in comfirmation when in admin mode
$mail
-> setParam ( 'event' , 'teams.membership.create' )
-> setParam ( 'recipient' , $email )
-> setParam ( 'name' , $name )
-> setParam ( 'subject' , \sprintf ( $locale -> getText ( 'account.emails.invitation.title' ), $team -> getAttribute ( 'name' , '[TEAM-NAME]' ), $project -> getAttribute ( 'name' , [ '[APP-NAME]' ])))
-> setParam ( 'body' , $body -> render ())
-> trigger ();
2020-01-19 03:47:08 +13:00
;
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
$audit
-> setParam ( 'userId' , $invitee -> getId ())
-> setParam ( 'event' , 'teams.membership.create' )
-> setParam ( 'resource' , 'teams/' . $teamId )
;
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED ) // TODO change response of this endpoint
-> json ( \array_merge ( $membership -> getArrayCopy ([
'$id' ,
'userId' ,
'teamId' ,
'roles' ,
'invited' ,
'joined' ,
'confirm' ,
]), [
'email' => $email ,
'name' => $name ,
]))
;
}, [ 'response' , 'project' , 'user' , 'projectDB' , 'locale' , 'audit' , 'mail' , 'mode' ]);
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: get ( '/v1/teams/:teamId/memberships' )
2020-02-01 11:34:07 +13:00
-> desc ( 'Get Team Memberships' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-02-01 11:34:07 +13:00
-> label ( 'scope' , 'teams.read' )
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'getMemberships' )
-> label ( 'sdk.description' , '/docs/references/teams/get-team-members.md' )
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
2020-06-07 21:29:51 +12:00
-> param ( 'search' , '' , function () { return new Text ( 256 ); }, 'Search term to filter your list results.' , true )
-> param ( 'limit' , 25 , function () { return new Range ( 0 , 100 ); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.' , true )
-> param ( 'offset' , 0 , function () { return new Range ( 0 , 2000 ); }, 'Results offset. The default value is 0. Use this param to manage pagination.' , true )
-> param ( 'orderType' , 'ASC' , function () { return new WhiteList ([ 'ASC' , 'DESC' ]); }, 'Order result by ASC or DESC order.' , true )
2020-06-30 23:09:28 +12:00
-> action ( function ( $teamId , $search , $limit , $offset , $orderType , $response , $projectDB ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
2020-02-01 11:34:07 +13:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> getDocument ( $teamId );
2020-02-01 11:34:07 +13:00
2020-06-30 23:09:28 +12:00
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
}
2020-02-01 11:34:07 +13:00
2020-06-30 23:09:28 +12:00
$memberships = $projectDB -> getCollection ([
'limit' => $limit ,
'offset' => $offset ,
'orderField' => 'joined' ,
'orderType' => $orderType ,
'orderCast' => 'int' ,
'search' => $search ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_MEMBERSHIPS ,
'teamId=' . $teamId ,
],
]);
$users = [];
foreach ( $memberships as $membership ) {
if ( empty ( $membership -> getAttribute ( 'userId' , null ))) {
continue ;
}
$temp = $projectDB -> getDocument ( $membership -> getAttribute ( 'userId' , null )) -> getArrayCopy ([ 'email' , 'name' ]);
$users [] = \array_merge ( $temp , $membership -> getArrayCopy ([
'$id' ,
'userId' ,
'teamId' ,
'roles' ,
'invited' ,
'joined' ,
'confirm' ,
]));
2020-02-01 11:34:07 +13:00
}
2020-06-30 23:09:28 +12:00
$response -> json ([ 'sum' => $projectDB -> getSum (), 'memberships' => $users ]);
}, [ 'response' , 'projectDB' ]);
2020-02-01 11:34:07 +13:00
2020-06-29 05:31:21 +12:00
App :: patch ( '/v1/teams/:teamId/memberships/:inviteId/status' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Update Team Membership Status' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-01-20 01:22:54 +13:00
-> label ( 'scope' , 'public' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'teams' )
2020-01-31 05:18:46 +13:00
-> label ( 'sdk.method' , 'updateMembershipStatus' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/teams/update-team-membership-status.md' )
2019-10-07 09:10:52 +13:00
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
2020-02-10 18:54:26 +13:00
-> param ( 'inviteId' , '' , function () { return new UID (); }, 'Invite unique ID.' )
-> param ( 'userId' , '' , function () { return new UID (); }, 'User unique ID.' )
-> param ( 'secret' , '' , function () { return new Text ( 256 ); }, 'Secret key.' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $teamId , $inviteId , $userId , $secret , $request , $response , $user , $projectDB , $audit ) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
$protocol = $request -> getProtocol ();
$membership = $projectDB -> getDocument ( $inviteId );
if ( empty ( $membership -> getId ()) || Database :: SYSTEM_COLLECTION_MEMBERSHIPS != $membership -> getCollection ()) {
throw new Exception ( 'Invite not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $membership -> getAttribute ( 'teamId' ) !== $teamId ) {
throw new Exception ( 'Team IDs don\'t match' , 404 );
}
2020-01-20 01:22:54 +13:00
2020-06-30 23:09:28 +12:00
Authorization :: disable ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> getDocument ( $teamId );
Authorization :: reset ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( Auth :: hash ( $secret ) !== $membership -> getAttribute ( 'secret' )) {
throw new Exception ( 'Secret key not valid' , 401 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $userId != $membership -> getAttribute ( 'userId' )) {
throw new Exception ( 'Invite not belong to current user (' . $user -> getAttribute ( 'email' ) . ')' , 401 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $user -> getId ())) {
$user = $projectDB -> getCollectionFirst ([ // Get user
'limit' => 1 ,
'filters' => [
'$collection=' . Database :: SYSTEM_COLLECTION_USERS ,
'$id=' . $userId ,
],
]);
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $membership -> getAttribute ( 'userId' ) !== $user -> getId ()) {
throw new Exception ( 'Invite not belong to current user (' . $user -> getAttribute ( 'email' ) . ')' , 401 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$membership // Attach user to team
-> setAttribute ( 'joined' , \time ())
-> setAttribute ( 'confirm' , true )
;
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$user
-> setAttribute ( 'emailVerification' , true )
-> setAttribute ( 'memberships' , $membership , Document :: SET_TYPE_APPEND )
;
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
// Log user in
$expiry = \time () + Auth :: TOKEN_EXPIRATION_LOGIN_LONG ;
$secret = Auth :: tokenGenerator ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$user -> setAttribute ( 'tokens' , new Document ([
'$collection' => Database :: SYSTEM_COLLECTION_TOKENS ,
'$permissions' => [ 'read' => [ 'user:' . $user -> getId ()], 'write' => [ 'user:' . $user -> getId ()]],
'type' => Auth :: TOKEN_TYPE_LOGIN ,
'secret' => Auth :: hash ( $secret ), // On way hash encryption to protect DB leak
'expire' => $expiry ,
'userAgent' => $request -> getServer ( 'HTTP_USER_AGENT' , 'UNKNOWN' ),
'ip' => $request -> getIP (),
]), Document :: SET_TYPE_APPEND );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
Authorization :: setRole ( 'user:' . $userId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$user = $projectDB -> updateDocument ( $user -> getArrayCopy ());
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $user ) {
throw new Exception ( 'Failed saving user to DB' , 500 );
}
2020-01-20 01:22:54 +13:00
2020-06-30 23:09:28 +12:00
Authorization :: disable ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> updateDocument ( \array_merge ( $team -> getArrayCopy (), [
'sum' => $team -> getAttribute ( 'sum' , 0 ) + 1 ,
]));
2020-01-20 09:02:50 +13:00
2020-06-30 23:09:28 +12:00
Authorization :: reset ();
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $team ) {
throw new Exception ( 'Failed saving team to DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$audit
-> setParam ( 'userId' , $user -> getId ())
-> setParam ( 'event' , 'teams.membership.update' )
-> setParam ( 'resource' , 'teams/' . $teamId )
;
2020-03-18 00:36:13 +13:00
2020-06-30 23:09:28 +12:00
if ( ! Config :: getParam ( 'domainVerification' )) {
2020-01-15 09:50:49 +13:00
$response
2020-06-30 23:09:28 +12:00
-> addHeader ( 'X-Fallback-Cookies' , \json_encode ([ Auth :: $cookieName => Auth :: encodeSession ( $user -> getId (), $secret )]))
2020-01-15 09:50:49 +13:00
;
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
$response
-> addCookie ( Auth :: $cookieName . '_legacy' , Auth :: encodeSession ( $user -> getId (), $secret ), $expiry , '/' , COOKIE_DOMAIN , ( 'https' == $protocol ), true , null )
-> addCookie ( Auth :: $cookieName , Auth :: encodeSession ( $user -> getId (), $secret ), $expiry , '/' , COOKIE_DOMAIN , ( 'https' == $protocol ), true , COOKIE_SAMESITE )
-> json ( \array_merge ( $membership -> getArrayCopy ([
'$id' ,
'userId' ,
'teamId' ,
'roles' ,
'invited' ,
'joined' ,
'confirm' ,
]), [
'email' => $user -> getAttribute ( 'email' ),
'name' => $user -> getAttribute ( 'name' ),
]))
;
}, [ 'request' , 'response' , 'user' , 'projectDB' , 'audit' ]);
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App :: delete ( '/v1/teams/:teamId/memberships/:inviteId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Delete Team Membership' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-02-10 05:53:33 +13:00
-> label ( 'scope' , 'teams.write' )
2020-01-27 19:14:14 +13:00
-> label ( 'sdk.platform' , [ APP_PLATFORM_CLIENT , APP_PLATFORM_SERVER ])
2019-05-09 18:54:39 +12:00
-> label ( 'sdk.namespace' , 'teams' )
2020-01-31 05:18:46 +13:00
-> label ( 'sdk.method' , 'deleteMembership' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/teams/delete-team-membership.md' )
2019-10-07 09:10:52 +13:00
-> param ( 'teamId' , '' , function () { return new UID (); }, 'Team unique ID.' )
2020-02-10 18:54:26 +13:00
-> param ( 'inviteId' , '' , function () { return new UID (); }, 'Invite unique ID.' )
2020-06-30 23:09:28 +12:00
-> action ( function ( $teamId , $inviteId , $response , $projectDB , $audit ) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$membership = $projectDB -> getDocument ( $inviteId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $membership -> getId ()) || Database :: SYSTEM_COLLECTION_MEMBERSHIPS != $membership -> getCollection ()) {
throw new Exception ( 'Invite not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $membership -> getAttribute ( 'teamId' ) !== $teamId ) {
throw new Exception ( 'Team IDs don\'t match' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$team = $projectDB -> getDocument ( $teamId );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( empty ( $team -> getId ()) || Database :: SYSTEM_COLLECTION_TEAMS != $team -> getCollection ()) {
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( ! $projectDB -> deleteDocument ( $membership -> getId ())) {
throw new Exception ( 'Failed to remove membership from DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( $membership -> getAttribute ( 'confirm' )) { // Count only confirmed members
$team = $projectDB -> updateDocument ( \array_merge ( $team -> getArrayCopy (), [
'sum' => $team -> getAttribute ( 'sum' , 0 ) - 1 ,
]));
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
if ( false === $team ) {
throw new Exception ( 'Failed saving team to DB' , 500 );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
$audit
-> setParam ( 'userId' , $membership -> getAttribute ( 'userId' ))
-> setParam ( 'event' , 'teams.membership.delete' )
-> setParam ( 'resource' , 'teams/' . $teamId )
;
$response -> noContent ();
}, [ 'response' , 'projectDB' , 'audit' ]);