2019-05-09 18:54:39 +12:00
< ? php
2021-05-07 10:31:05 +12:00
use Appwrite\Auth\Auth ;
2021-07-19 18:35:49 +12:00
use Appwrite\Database\Validator\CustomId ;
2021-05-07 10:31:05 +12:00
use Appwrite\Detector\Detector ;
use Appwrite\Template\Template ;
use Appwrite\Utopia\Response ;
use Appwrite\Network\Validator\Email ;
use Appwrite\Network\Validator\Host ;
2020-06-29 05:31:21 +12:00
use Utopia\App ;
2019-05-09 18:54:39 +12:00
use Utopia\Exception ;
2020-03-29 01:42:16 +13:00
use Utopia\Config\Config ;
2019-05-09 18:54:39 +12:00
use Utopia\Validator\Text ;
use Utopia\Validator\Range ;
use Utopia\Validator\ArrayList ;
use Utopia\Validator\WhiteList ;
2021-05-07 10:31:05 +12:00
use Utopia\Database\Document ;
use Utopia\Database\Exception\Duplicate ;
use Utopia\Database\Query ;
use Utopia\Database\Validator\Authorization ;
use Utopia\Database\Validator\Key ;
2021-07-26 02:47:18 +12:00
use Utopia\Database\Validator\UID ;
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' ])
2020-12-03 11:15:20 +13:00
-> label ( 'event' , 'teams.create' )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'teams.write' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_TEAM )
2021-07-19 18:35:49 +12:00
-> param ( 'teamId' , '' , new CustomId (), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.' )
2020-09-11 02:40:14 +12:00
-> param ( 'name' , null , new Text ( 128 ), 'Team name. Max length: 128 chars.' )
2020-09-12 00:00:41 +12:00
-> param ( 'roles' , [ 'owner' ], new ArrayList ( new Key ()), '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). Max length for each role is 32 chars.' , true )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
-> inject ( 'user' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
2021-07-29 20:09:24 +12:00
-> action ( function ( $teamId , $name , $roles , $response , $user , $dbForInternal ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForInternal */
2020-06-30 23:09:28 +12:00
Authorization :: disable ();
2021-03-02 10:04:53 +13:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: $roles );
2020-11-20 19:48:15 +13:00
$isAppUser = Auth :: isAppUser ( Authorization :: $roles );
2021-07-19 18:35:49 +12:00
$teamId = $teamId == 'unique()' ? $dbForInternal -> getId () : $teamId ;
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> createDocument ( 'teams' , new Document ([
'$id' => $teamId ,
2021-05-10 06:37:47 +12:00
'$read' => [ 'team:' . $teamId ],
2021-05-07 10:31:05 +12:00
'$write' => [ 'team:' . $teamId . '/owner' ],
2020-06-30 23:09:28 +12:00
'name' => $name ,
2021-03-02 10:04:53 +13:00
'sum' => ( $isPrivilegedUser || $isAppUser ) ? 0 : 1 ,
2020-06-30 23:09:28 +12:00
'dateCreated' => \time (),
2021-05-07 10:31:05 +12:00
]));
2020-06-30 23:09:28 +12:00
Authorization :: reset ();
2021-03-02 10:04:53 +13:00
if ( ! $isPrivilegedUser && ! $isAppUser ) { // Don't add user on server mode
2020-06-30 23:09:28 +12:00
$membership = new Document ([
2021-05-07 10:31:05 +12:00
'$read' => [ 'user:' . $user -> getId (), 'team:' . $team -> getId ()],
'$write' => [ 'user:' . $user -> getId (), 'team:' . $team -> getId () . '/owner' ],
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
]);
2021-05-07 10:31:05 +12:00
$membership = $dbForInternal -> createDocument ( 'memberships' , $membership );
2020-06-30 23:09:28 +12:00
// Attach user to team
$user -> setAttribute ( 'memberships' , $membership , Document :: SET_TYPE_APPEND );
2021-05-07 10:31:05 +12:00
$user = $dbForInternal -> updateDocument ( 'users' , $user -> getId (), $user );
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
2021-05-07 10:31:05 +12:00
$response -> setStatusCode ( Response :: STATUS_CODE_CREATED );
2021-07-26 02:47:18 +12:00
$response -> dynamic ( $team , Response :: MODEL_TEAM );
2020-12-27 05:48:43 +13:00
});
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-02-01 11:34:07 +13:00
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'list' )
-> label ( 'sdk.description' , '/docs/references/teams/list-teams.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_TEAM_LIST )
2020-09-11 02:40:14 +12:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
-> param ( 'limit' , 25 , 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 , new Range ( 0 , 2000 ), 'Results offset. The default value is 0. Use this param to manage pagination.' , true )
-> param ( 'orderType' , 'ASC' , new WhiteList ([ 'ASC' , 'DESC' ], true ), 'Order result by ASC or DESC order.' , true )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $search , $limit , $offset , $orderType , $response , $dbForInternal ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-05-27 22:09:14 +12:00
$queries = ( $search ) ? [ new Query ( 'name' , Query :: TYPE_SEARCH , [ $search ])] : [];
$results = $dbForInternal -> find ( 'teams' , $queries , $limit , $offset , [ '_id' ], [ $orderType ]);
$sum = $dbForInternal -> count ( 'teams' , $queries , APP_LIMIT_COUNT );
2020-06-30 23:09:28 +12:00
2021-07-26 02:47:18 +12:00
$response -> dynamic ( new Document ([
2021-05-27 22:09:14 +12:00
'teams' => $results ,
2021-05-07 10:31:05 +12:00
'sum' => $sum ,
2020-08-07 02:49:29 +12:00
]), Response :: MODEL_TEAM_LIST );
2020-12-27 05:48:43 +13:00
});
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-02-01 11:34:07 +13:00
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'get' )
-> label ( 'sdk.description' , '/docs/references/teams/get-team.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_TEAM )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $teamId , $response , $dbForInternal ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-02-01 11:34:07 +13:00
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2020-02-01 11:34:07 +13:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Team not found' , 404 );
2020-02-01 11:34:07 +13:00
}
2020-06-30 23:09:28 +12:00
2021-07-26 02:47:18 +12:00
$response -> dynamic ( $team , Response :: MODEL_TEAM );
2020-12-27 05:48:43 +13:00
});
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' ])
2020-12-03 11:15:20 +13:00
-> label ( 'event' , 'teams.update' )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'teams.write' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_TEAM )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
-> param ( 'name' , null , new Text ( 128 ), 'Team name. Max length: 128 chars.' )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $teamId , $name , $response , $dbForInternal ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2019-05-09 18:54:39 +12:00
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2019-05-09 18:54:39 +12:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> updateDocument ( 'teams' , $team -> getId (), $team -> setAttribute ( 'name' , $name ));
2019-05-09 18:54:39 +12:00
2021-07-26 02:47:18 +12:00
$response -> dynamic ( $team , Response :: MODEL_TEAM );
2020-12-27 05:48:43 +13:00
});
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' ])
2020-12-03 11:15:20 +13:00
-> label ( 'event' , 'teams.delete' )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'teams.write' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
2020-12-27 05:48:43 +13:00
-> inject ( 'events' )
2021-05-12 05:55:38 +12:00
-> inject ( 'deletes' )
2021-06-27 07:47:53 +12:00
-> action ( function ( $teamId , $response , $dbForInternal , $events , $deletes ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-12-07 11:14:57 +13:00
/** @var Appwrite\Event\Event $events */
2021-06-27 07:47:53 +12:00
/** @var Appwrite\Event\Event $deletes */
2019-05-09 18:54:39 +12:00
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2019-05-09 18:54:39 +12:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2021-05-07 10:31:05 +12:00
$memberships = $dbForInternal -> find ( 'memberships' , [
new Query ( 'teamId' , Query :: TYPE_EQUAL , [ $teamId ]),
], 2000 , 0 ); // TODO fix members limit
// TODO delete all members individually from the user object
2021-05-10 06:37:47 +12:00
foreach ( $memberships as $membership ) {
if ( ! $dbForInternal -> deleteDocument ( 'memberships' , $membership -> getId ())) {
2020-06-30 23:09:28 +12:00
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
2021-05-07 10:31:05 +12:00
if ( ! $dbForInternal -> deleteDocument ( 'teams' , $teamId )) {
2020-06-30 23:09:28 +12:00
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
2021-05-12 05:55:38 +12:00
$deletes
-> setParam ( 'type' , DELETE_TYPE_DOCUMENT )
-> setParam ( 'document' , $team )
;
2020-12-07 11:14:57 +13:00
$events
2021-07-26 02:47:18 +12:00
-> setParam ( 'eventData' , $response -> output ( $team , Response :: MODEL_TEAM ))
2020-12-03 11:15:20 +13:00
;
2020-06-30 23:09:28 +12:00
$response -> noContent ();
2020-12-27 05:48:43 +13:00
});
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' )
2021-03-01 07:36:13 +13:00
-> groups ([ 'api' , 'teams' , 'auth' ])
2020-12-03 11:15:20 +13:00
-> label ( 'event' , 'teams.memberships.create' )
2020-02-10 05:53:33 +13:00
-> label ( 'scope' , 'teams.write' )
2021-03-01 07:36:13 +13:00
-> label ( 'auth.type' , 'invites' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_MEMBERSHIP )
2021-04-13 21:38:40 +12:00
-> label ( 'abuse-limit' , 10 )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
-> param ( 'email' , '' , new Email (), 'New team member email.' )
-> param ( 'name' , '' , new Text ( 128 ), 'New team member name. Max length: 128 chars.' , true )
2020-09-12 00:00:41 +12:00
-> param ( 'roles' , [], new ArrayList ( new Key ()), '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). Max length for each role is 32 chars.' )
2021-07-28 02:16:12 +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' ])
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'user' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
2020-12-27 05:48:43 +13:00
-> inject ( 'locale' )
-> inject ( 'audits' )
-> inject ( 'mails' )
2021-05-07 10:31:05 +12:00
-> action ( function ( $teamId , $email , $name , $roles , $url , $response , $project , $user , $dbForInternal , $locale , $audits , $mails ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */
2020-11-20 19:48:15 +13:00
2021-03-02 10:04:53 +13:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: $roles );
2020-11-20 19:48:15 +13:00
$isAppUser = Auth :: isAppUser ( Authorization :: $roles );
2021-06-04 07:05:11 +12:00
$email = \strtolower ( $email );
2020-06-30 23:09:28 +12:00
$name = ( empty ( $name )) ? $email : $name ;
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2020-06-30 23:09:28 +12:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2021-08-04 08:22:03 +12:00
$invitee = $dbForInternal -> findOne ( 'users' , [ new Query ( 'email' , Query :: TYPE_EQUAL , [ $email ])]); // Get user by email address
2020-06-30 23:09:28 +12:00
if ( empty ( $invitee )) { // Create new user if no user with same email found
2019-05-09 18:54:39 +12:00
2021-08-06 20:34:17 +12:00
$limit = $project -> getAttribute ( 'auths' , [])[ 'limit' ] ? ? 0 ;
2021-03-01 07:36:13 +13:00
if ( $limit !== 0 && $project -> getId () !== 'console' ) { // check users limit, console invites are allways allowed.
2021-05-10 06:37:47 +12:00
$sum = $dbForInternal -> count ( 'users' , [], APP_LIMIT_USERS );
2021-03-01 07:36:13 +13:00
if ( $sum >= $limit ) {
throw new Exception ( 'Project registration is restricted. Contact your administrator for more information.' , 501 );
}
}
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 {
2021-05-07 10:31:05 +12:00
$userId = $dbForInternal -> getId ();
$invitee = $dbForInternal -> createDocument ( 'users' , new Document ([
'$id' => $userId ,
2021-06-12 06:23:16 +12:00
'$read' => [ 'user:' . $userId , 'role:all' ],
2021-05-07 10:31:05 +12:00
'$write' => [ 'user:' . $userId ],
2020-06-30 23:09:28 +12:00
'email' => $email ,
'emailVerification' => false ,
2021-07-14 23:02:12 +12:00
'status' => true ,
2020-06-30 23:09:28 +12:00
'password' => Auth :: passwordHash ( Auth :: passwordGenerator ()),
2021-05-05 06:52:46 +12:00
/**
2021-05-07 04:30:44 +12:00
* Set the password update time to 0 for users created using
2021-05-07 04:30:57 +12:00
* team invite and OAuth to allow password updates without an
2021-05-07 04:30:44 +12:00
* old password
2021-05-05 06:52:46 +12:00
*/
'passwordUpdate' => 0 ,
2020-06-30 23:09:28 +12:00
'registration' => \time (),
'reset' => false ,
'name' => $name ,
2021-05-10 06:37:47 +12:00
'prefs' => [],
2021-02-20 02:59:36 +13:00
'sessions' => [],
2020-06-30 23:09:28 +12:00
'tokens' => [],
2021-05-10 06:37:47 +12:00
'memberships' => [],
2021-05-07 10:31:05 +12:00
]));
2020-06-30 23:09:28 +12:00
} 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
2021-05-10 06:37:47 +12:00
$isOwner = Authorization :: isRole ( 'team:' . $team -> getId () . '/owner' );;
2019-05-09 18:54:39 +12:00
2021-03-02 10:04:53 +13:00
if ( ! $isOwner && ! $isPrivilegedUser && ! $isAppUser ) { // Not owner, not admin, not app (server)
2020-06-30 23:09:28 +12:00
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 ([
2021-05-07 10:31:05 +12:00
'$id' => $dbForInternal -> getId (),
2021-06-12 06:23:16 +12:00
'$read' => [ 'role:all' ],
2021-05-07 10:31:05 +12:00
'$write' => [ 'user:' . $invitee -> getId (), 'team:' . $team -> getId () . '/owner' ],
2020-06-30 23:09:28 +12:00
'userId' => $invitee -> getId (),
'teamId' => $team -> getId (),
'roles' => $roles ,
'invited' => \time (),
2021-03-02 10:04:53 +13:00
'joined' => ( $isPrivilegedUser || $isAppUser ) ? \time () : 0 ,
'confirm' => ( $isPrivilegedUser || $isAppUser ),
2020-06-30 23:09:28 +12:00
'secret' => Auth :: hash ( $secret ),
]);
2021-03-02 10:04:53 +13:00
if ( $isPrivilegedUser || $isAppUser ) { // Allow admin to create membership
2020-06-30 23:09:28 +12:00
Authorization :: disable ();
2021-05-10 06:37:47 +12:00
try {
$membership = $dbForInternal -> createDocument ( 'memberships' , $membership );
} catch ( Duplicate $th ) {
throw new Exception ( 'User has already been invited or is already a member of this team' , 409 );
}
2019-05-09 18:54:39 +12:00
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> updateDocument ( 'teams' , $team -> getId (), $team -> setAttribute ( 'sum' , $team -> getAttribute ( 'sum' , 0 ) + 1 ));
2019-05-09 18:54:39 +12:00
2020-07-10 23:54:00 +12:00
// Attach user to team
$invitee -> setAttribute ( 'memberships' , $membership , Document :: SET_TYPE_APPEND );
2020-07-10 21:06:30 +12:00
2021-05-07 10:31:05 +12:00
$invitee = $dbForInternal -> updateDocument ( 'users' , $invitee -> getId (), $invitee );
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
Authorization :: reset ();
} else {
2021-05-10 06:37:47 +12:00
try {
$membership = $dbForInternal -> createDocument ( 'memberships' , $membership );
} catch ( Duplicate $th ) {
throw new Exception ( 'User has already been invited or is already a member of this team' , 409 );
}
2020-06-30 23:09:28 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 23:09:28 +12:00
$url = Template :: parseURL ( $url );
2021-05-08 03:49:23 +12:00
$url [ 'query' ] = Template :: mergeQuery ((( isset ( $url [ 'query' ])) ? $url [ 'query' ] : '' ), [ 'membershipId' => $membership -> getId (), 'teamId' => $team -> getId (), 'userId' => $invitee -> getId (), 'secret' => $secret , 'teamId' => $teamId ]);
2020-06-30 23:09:28 +12:00
$url = Template :: unParseURL ( $url );
2021-05-13 01:44:41 +12:00
if ( ! $isPrivilegedUser && ! $isAppUser ) { // No need of confirmation when in admin or app mode
2020-07-06 02:19:59 +12:00
$mails
2021-05-13 05:20:10 +12:00
-> setParam ( 'event' , 'teams.memberships.create' )
2021-07-30 06:46:06 +12:00
-> setParam ( 'from' , $project -> getId ())
2020-06-30 23:09:28 +12:00
-> setParam ( 'recipient' , $email )
-> setParam ( 'name' , $name )
2021-07-28 08:31:19 +12:00
-> setParam ( 'url' , $url )
-> setParam ( 'locale' , $locale -> default )
-> setParam ( 'project' , $project -> getAttribute ( 'name' , [ '[APP-NAME]' ]))
-> setParam ( 'owner' , $user -> getAttribute ( 'name' , '' ))
-> setParam ( 'team' , $team -> getAttribute ( 'name' , '[TEAM-NAME]' ))
-> setParam ( 'type' , MAIL_TYPE_INVITATION )
2021-02-09 22:25:09 +13:00
-> trigger ()
2019-05-09 18:54:39 +12:00
;
}
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 23:09:28 +12:00
-> setParam ( 'userId' , $invitee -> getId ())
2021-05-13 05:20:10 +12:00
-> setParam ( 'event' , 'teams.memberships.create' )
2020-06-30 23:09:28 +12:00
-> setParam ( 'resource' , 'teams/' . $teamId )
;
2021-05-07 10:31:05 +12:00
$response -> setStatusCode ( Response :: STATUS_CODE_CREATED );
2021-07-26 02:47:18 +12:00
$response -> dynamic ( $membership
2021-05-07 10:31:05 +12:00
-> setAttribute ( 'email' , $email )
-> setAttribute ( 'name' , $name )
, Response :: MODEL_MEMBERSHIP );
2020-12-27 05:48:43 +13:00
});
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' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-02-01 11:34:07 +13:00
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'getMemberships' )
-> label ( 'sdk.description' , '/docs/references/teams/get-team-members.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_MEMBERSHIP_LIST )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
-> param ( 'limit' , 25 , 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 , new Range ( 0 , 2000 ), 'Results offset. The default value is 0. Use this param to manage pagination.' , true )
-> param ( 'orderType' , 'ASC' , new WhiteList ([ 'ASC' , 'DESC' ], true ), 'Order result by ASC or DESC order.' , true )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
-> action ( function ( $teamId , $search , $limit , $offset , $orderType , $response , $dbForInternal ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-02-01 11:34:07 +13:00
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2020-02-01 11:34:07 +13:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Team not found' , 404 );
}
2020-02-01 11:34:07 +13:00
2021-06-13 02:33:23 +12:00
$memberships = $dbForInternal -> find ( 'memberships' , [ new Query ( 'teamId' , Query :: TYPE_EQUAL , [ $teamId ])], $limit , $offset , [ '_id' ], [ $orderType ]);
2021-05-10 06:37:47 +12:00
$sum = $dbForInternal -> count ( 'memberships' , [ new Query ( 'teamId' , Query :: TYPE_EQUAL , [ $teamId ])], APP_LIMIT_COUNT );
2020-06-30 23:09:28 +12:00
$users = [];
foreach ( $memberships as $membership ) {
if ( empty ( $membership -> getAttribute ( 'userId' , null ))) {
continue ;
}
2021-05-07 10:31:05 +12:00
$temp = $dbForInternal -> getDocument ( 'users' , $membership -> getAttribute ( 'userId' , null )) -> getArrayCopy ([ 'email' , 'name' ]);
2020-06-30 23:09:28 +12:00
2020-08-07 02:49:29 +12:00
$users [] = new Document ( \array_merge ( $temp , $membership -> getArrayCopy ()));
2020-02-01 11:34:07 +13:00
}
2020-06-30 23:09:28 +12:00
2021-07-26 02:47:18 +12:00
$response -> dynamic ( new Document ([
2021-05-27 22:09:14 +12:00
'memberships' => $users ,
2021-05-07 10:31:05 +12:00
'sum' => $sum ,
]), Response :: MODEL_MEMBERSHIP_LIST );
2020-12-27 05:48:43 +13:00
});
2020-02-01 11:34:07 +13:00
2021-08-04 18:57:30 +12:00
App :: get ( '/v1/teams/:teamId/memberships/:membershipId' )
-> desc ( 'Get Team Membership' )
-> groups ([ 'api' , 'teams' ])
-> label ( 'scope' , 'teams.read' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'getMembership' )
2021-08-04 19:29:10 +12:00
-> label ( 'sdk.description' , '/docs/references/teams/get-team-member.md' )
2021-08-04 18:57:30 +12:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_MEMBERSHIP_LIST )
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
-> param ( 'membershipId' , '' , new UID (), 'membership unique ID.' )
-> inject ( 'response' )
-> inject ( 'dbForInternal' )
-> action ( function ( $teamId , $membershipId , $response , $dbForInternal ) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
if ( $team -> isEmpty ()) {
throw new Exception ( 'Team not found' , 404 );
}
$membership = $dbForInternal -> getDocument ( 'memberships' , $membershipId );
if ( $membership -> isEmpty () || empty ( $membership -> getAttribute ( 'userId' , null ))) {
throw new Exception ( 'Membership not found' , 404 );
}
$temp = $dbForInternal -> getDocument ( 'users' , $membership -> getAttribute ( 'userId' , null )) -> getArrayCopy ([ 'email' , 'name' ]);
$response -> dynamic ( new Document ( \array_merge ( $temp , $membership -> getArrayCopy ())), Response :: MODEL_MEMBERSHIP );
});
2021-05-13 01:44:41 +12:00
App :: patch ( '/v1/teams/:teamId/memberships/:membershipId' )
-> desc ( 'Update Membership Roles' )
-> groups ([ 'api' , 'teams' ])
-> label ( 'event' , 'teams.memberships.update' )
2021-05-14 02:47:35 +12:00
-> label ( 'scope' , 'teams.write' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2021-05-13 01:44:41 +12:00
-> label ( 'sdk.namespace' , 'teams' )
-> label ( 'sdk.method' , 'updateMembershipRoles' )
-> label ( 'sdk.description' , '/docs/references/teams/update-team-membership-roles.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_MEMBERSHIP )
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
-> param ( 'membershipId' , '' , new UID (), 'Membership ID.' )
-> param ( 'roles' , [], new ArrayList ( new Key ()), '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). Max length for each role is 32 chars.' )
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'user' )
2021-05-15 13:20:12 +12:00
-> inject ( 'dbForInternal' )
2021-05-13 01:44:41 +12:00
-> inject ( 'audits' )
2021-05-15 13:20:12 +12:00
-> action ( function ( $teamId , $membershipId , $roles , $request , $response , $user , $dbForInternal , $audits ) {
2021-05-13 01:44:41 +12:00
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $user */
2021-05-15 13:20:12 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2021-05-13 01:44:41 +12:00
/** @var Appwrite\Event\Event $audits */
2021-05-15 13:20:12 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
if ( $team -> isEmpty ()) {
2021-05-13 02:47:56 +12:00
throw new Exception ( 'Team not found' , 404 );
}
2021-05-15 13:20:12 +12:00
$membership = $dbForInternal -> getDocument ( 'memberships' , $membershipId );
if ( $membership -> isEmpty ()) {
2021-05-13 01:44:41 +12:00
throw new Exception ( 'Membership not found' , 404 );
}
2021-05-15 13:20:12 +12:00
$profile = $dbForInternal -> getDocument ( 'users' , $membership -> getAttribute ( 'userId' ));
if ( $profile -> isEmpty ()) {
throw new Exception ( 'User not found' , 404 );
}
2021-05-13 01:44:41 +12:00
2021-05-13 05:00:22 +12:00
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: $roles );
$isAppUser = Auth :: isAppUser ( Authorization :: $roles );
2021-05-14 02:01:52 +12:00
$isOwner = Authorization :: isRole ( 'team:' . $team -> getId () . '/owner' );;
2021-05-13 05:00:22 +12:00
if ( ! $isOwner && ! $isPrivilegedUser && ! $isAppUser ) { // Not owner, not admin, not app (server)
throw new Exception ( 'User is not allowed to modify roles' , 401 );
}
2021-05-13 01:44:41 +12:00
2021-05-13 05:00:22 +12:00
// Update the roles
$membership -> setAttribute ( 'roles' , $roles );
2021-05-15 13:20:12 +12:00
$membership = $dbForInternal -> updateDocument ( 'memberships' , $membership -> getId (), $membership );
2021-05-13 01:44:41 +12:00
2021-07-28 02:16:12 +12:00
// TODO sync updated membership in the user $profile object using TYPE_REPLACE
2021-05-13 01:44:41 +12:00
$audits
-> setParam ( 'userId' , $user -> getId ())
2021-05-13 05:00:22 +12:00
-> setParam ( 'event' , 'teams.memberships.update' )
2021-05-13 01:44:41 +12:00
-> setParam ( 'resource' , 'teams/' . $teamId )
;
2021-07-26 02:47:18 +12:00
$response -> dynamic ( $membership , Response :: MODEL_MEMBERSHIP );
2020-12-27 05:48:43 +13:00
});
2020-02-01 11:34:07 +13:00
2021-05-08 03:49:23 +12:00
App :: patch ( '/v1/teams/:teamId/memberships/:membershipId/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-12-03 11:15:20 +13:00
-> label ( 'event' , 'teams.memberships.update.status' )
2020-01-20 01:22:54 +13:00
-> label ( 'scope' , 'public' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_JWT ])
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' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_MEMBERSHIP )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
2021-05-08 03:49:23 +12:00
-> param ( 'membershipId' , '' , new UID (), 'Membership ID.' )
2020-09-11 02:40:14 +12:00
-> param ( 'userId' , '' , new UID (), 'User unique ID.' )
-> param ( 'secret' , '' , new Text ( 256 ), 'Secret key.' )
2020-12-27 05:48:43 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'user' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
2020-12-27 05:48:43 +13:00
-> inject ( 'geodb' )
-> inject ( 'audits' )
2021-05-11 04:53:02 +12:00
-> action ( function ( $teamId , $membershipId , $userId , $secret , $request , $response , $user , $dbForInternal , $geodb , $audits ) {
2020-08-15 23:39:44 +12:00
/** @var Utopia\Swoole\Request $request */
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $user */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-10-27 14:03:54 +13:00
/** @var MaxMind\Db\Reader $geodb */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2020-06-30 23:09:28 +12:00
$protocol = $request -> getProtocol ();
2021-05-11 04:53:02 +12:00
$membership = $dbForInternal -> getDocument ( 'memberships' , $membershipId );
2020-06-30 23:09:28 +12:00
2021-06-21 01:59:36 +12:00
if ( $membership -> isEmpty ()) {
2021-05-10 06:37:47 +12:00
throw new Exception ( 'Membership not found' , 404 );
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 ( $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
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2020-06-30 23:09:28 +12:00
Authorization :: reset ();
2019-05-09 18:54:39 +12:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
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' )) {
2021-05-05 06:52:46 +12:00
throw new Exception ( 'Invite does not belong to current user (' . $user -> getAttribute ( 'email' ) . ')' , 401 );
2020-06-30 23:09:28 +12:00
}
2019-05-09 18:54:39 +12:00
2021-06-21 01:59:36 +12:00
if ( $user -> isEmpty ()) {
2021-05-07 10:31:05 +12:00
$user = $dbForInternal -> getDocument ( 'users' , $userId ); // Get user
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 ( $membership -> getAttribute ( 'userId' ) !== $user -> getId ()) {
2021-05-05 06:52:46 +12:00
throw new Exception ( 'Invite does not belong to current user (' . $user -> getAttribute ( 'email' ) . ')' , 401 );
2020-06-30 23:09:28 +12:00
}
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
2020-08-12 02:28:51 +12:00
2021-07-17 22:04:43 +12:00
Authorization :: setRole ( 'user:' . $user -> getId ());
2021-02-15 06:28:54 +13:00
$detector = new Detector ( $request -> getUserAgent ( 'UNKNOWN' ));
$record = $geodb -> get ( $request -> getIP ());
2020-06-30 23:09:28 +12:00
$expiry = \time () + Auth :: TOKEN_EXPIRATION_LOGIN_LONG ;
$secret = Auth :: tokenGenerator ();
2021-02-15 06:28:54 +13:00
$session = new Document ( array_merge ([
2021-05-07 10:31:05 +12:00
'$id' => $dbForInternal -> getId (),
2020-11-26 19:12:24 +13:00
'userId' => $user -> getId (),
2021-02-20 01:12:47 +13:00
'provider' => Auth :: SESSION_PROVIDER_EMAIL ,
2021-02-19 23:02:02 +13:00
'providerUid' => $user -> getAttribute ( 'email' ),
2020-11-13 00:54:16 +13:00
'secret' => Auth :: hash ( $secret ), // One way hash encryption to protect DB leak
2020-06-30 23:09:28 +12:00
'expire' => $expiry ,
2020-07-04 03:14:51 +12:00
'userAgent' => $request -> getUserAgent ( 'UNKNOWN' ),
2020-06-30 23:09:28 +12:00
'ip' => $request -> getIP (),
2021-02-15 06:28:54 +13:00
'countryCode' => ( $record ) ? \strtolower ( $record [ 'country' ][ 'iso_code' ]) : '--' ,
], $detector -> getOS (), $detector -> getClient (), $detector -> getDevice ()));
2020-08-12 02:28:51 +12:00
2021-07-17 22:04:43 +12:00
$session = $dbForInternal -> createDocument ( 'sessions' , $session
-> setAttribute ( '$read' , [ 'user:' . $user -> getId ()])
-> setAttribute ( '$write' , [ 'user:' . $user -> getId ()])
);
2021-02-20 01:12:47 +13:00
$user -> setAttribute ( 'sessions' , $session , 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
2021-05-07 10:31:05 +12:00
$user = $dbForInternal -> updateDocument ( 'users' , $user -> getId (), $user );
2021-05-10 08:34:32 +12:00
$membership = $dbForInternal -> updateDocument ( 'memberships' , $membership -> getId (), $membership );
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
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> updateDocument ( 'teams' , $team -> getId (), $team -> setAttribute ( '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-07-06 02:19:59 +12:00
$audits
2020-06-30 23:09:28 +12:00
-> setParam ( 'userId' , $user -> getId ())
2021-05-13 05:20:10 +12:00
-> setParam ( 'event' , 'teams.memberships.update.status' )
2020-06-30 23:09:28 +12:00
-> 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
2020-07-01 18:35:57 +12:00
-> addCookie ( Auth :: $cookieName . '_legacy' , Auth :: encodeSession ( $user -> getId (), $secret ), $expiry , '/' , Config :: getParam ( 'cookieDomain' ), ( 'https' == $protocol ), true , null )
-> addCookie ( Auth :: $cookieName , Auth :: encodeSession ( $user -> getId (), $secret ), $expiry , '/' , Config :: getParam ( 'cookieDomain' ), ( 'https' == $protocol ), true , Config :: getParam ( 'cookieSamesite' ))
2020-06-30 23:09:28 +12:00
;
2020-07-03 09:48:02 +12:00
2021-07-26 02:47:18 +12:00
$response -> dynamic ( $membership
2021-05-07 10:31:05 +12:00
-> setAttribute ( 'email' , $user -> getAttribute ( 'email' ))
-> setAttribute ( 'name' , $user -> getAttribute ( 'name' ))
, Response :: MODEL_MEMBERSHIP );
2020-12-27 05:48:43 +13:00
});
2019-05-09 18:54:39 +12:00
2021-05-08 03:49:23 +12:00
App :: delete ( '/v1/teams/:teamId/memberships/:membershipId' )
2019-05-09 18:54:39 +12:00
-> desc ( 'Delete Team Membership' )
2020-06-26 06:32:12 +12:00
-> groups ([ 'api' , 'teams' ])
2020-12-03 11:15:20 +13:00
-> label ( 'event' , 'teams.memberships.delete' )
2020-02-10 05:53:33 +13:00
-> label ( 'scope' , 'teams.write' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
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' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2020-09-11 02:40:14 +12:00
-> param ( 'teamId' , '' , new UID (), 'Team unique ID.' )
2021-05-08 03:49:23 +12:00
-> param ( 'membershipId' , '' , new UID (), 'Membership ID.' )
2020-12-27 05:48:43 +13:00
-> inject ( 'response' )
2021-05-07 10:31:05 +12:00
-> inject ( 'dbForInternal' )
2020-12-27 05:48:43 +13:00
-> inject ( 'audits' )
-> inject ( 'events' )
2021-05-11 04:53:02 +12:00
-> action ( function ( $teamId , $membershipId , $response , $dbForInternal , $audits , $events ) {
2020-08-15 09:56:33 +12:00
/** @var Appwrite\Utopia\Response $response */
2021-05-07 10:31:05 +12:00
/** @var Utopia\Database\Database $dbForInternal */
2020-07-06 02:19:59 +12:00
/** @var Appwrite\Event\Event $audits */
2020-12-07 11:14:57 +13:00
/** @var Appwrite\Event\Event $events */
2019-05-09 18:54:39 +12:00
2021-05-11 04:53:02 +12:00
$membership = $dbForInternal -> getDocument ( 'memberships' , $membershipId );
2019-05-09 18:54:39 +12:00
2021-06-21 01:59:36 +12:00
if ( $membership -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
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
2021-05-10 09:20:57 +12:00
$user = $dbForInternal -> getDocument ( 'users' , $membership -> getAttribute ( 'userId' ));
if ( $user -> isEmpty ()) {
throw new Exception ( 'User not found' , 404 );
}
2021-05-07 10:31:05 +12:00
$team = $dbForInternal -> getDocument ( 'teams' , $teamId );
2019-05-09 18:54:39 +12:00
2021-06-21 01:59:36 +12:00
if ( $team -> isEmpty ()) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Team not found' , 404 );
}
2019-05-09 18:54:39 +12:00
2021-05-07 10:31:05 +12:00
if ( ! $dbForInternal -> deleteDocument ( 'memberships' , $membership -> getId ())) {
2020-06-30 23:09:28 +12:00
throw new Exception ( 'Failed to remove membership from DB' , 500 );
}
2019-05-09 18:54:39 +12:00
2021-05-10 09:20:57 +12:00
$memberships = $user -> getAttribute ( 'memberships' , []);
foreach ( $memberships as $key => $child ) {
/** @var Document $child */
2021-05-11 04:53:02 +12:00
if ( $membershipId == $child -> getId ()) {
2021-05-10 09:20:57 +12:00
unset ( $memberships [ $key ]);
break ;
}
}
Authorization :: disable ();
$dbForInternal -> updateDocument ( 'users' , $user -> getId (), $user -> setAttribute ( 'memberships' , $memberships ));
Authorization :: reset ();
2020-06-30 23:09:28 +12:00
if ( $membership -> getAttribute ( 'confirm' )) { // Count only confirmed members
2021-06-04 08:19:04 +12:00
$team = $dbForInternal -> updateDocument ( 'teams' , $team -> getId (), $team -> setAttribute ( 'sum' , \max ( $team -> getAttribute ( 'sum' , 0 ) - 1 , 0 )));
2019-05-09 18:54:39 +12:00
}
2020-06-30 23:09:28 +12:00
2020-07-06 02:19:59 +12:00
$audits
2020-06-30 23:09:28 +12:00
-> setParam ( 'userId' , $membership -> getAttribute ( 'userId' ))
2021-05-13 05:20:10 +12:00
-> setParam ( 'event' , 'teams.memberships.delete' )
2020-06-30 23:09:28 +12:00
-> setParam ( 'resource' , 'teams/' . $teamId )
;
2020-12-07 11:14:57 +13:00
$events
2021-07-26 02:47:18 +12:00
-> setParam ( 'eventData' , $response -> output ( $membership , Response :: MODEL_MEMBERSHIP ))
2020-12-03 11:15:20 +13:00
;
2020-06-30 23:09:28 +12:00
$response -> noContent ();
2020-12-27 05:48:43 +13:00
});