get('/v1/users') ->desc('List Users') ->label('scope', 'users.read') ->label('sdk.namespace', 'users') ->label('sdk.method', 'listUsers') ->label('sdk.description', 'Get a list of all the project users. You can use the query params to filter your results.') ->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) ->action( function($search, $limit, $offset, $orderType) use ($response, $projectDB) { $results = $projectDB->getCollection([ 'limit' => $limit, 'offset' => $offset, 'orderField' => 'registration', 'orderType' => $orderType, 'orderCast' => 'int', 'search' => $search, 'filters' => [ '$collection=' . Database::SYSTEM_COLLECTION_USERS ], ]); $results = array_map(function ($value) { /* @var $value \Database\Document */ return $value->getArrayCopy([ '$uid', '$permissions', 'status', 'email', 'registration', 'confirm', 'name', 'oauthBitbucket', 'oauthBitBucketAccessToken', 'oauthFacebook', 'oauthFacebookAccessToken', 'oauthGithub', 'oauthGithubAccessToken', 'oauthGitlab', 'oauthGitlabAccessToken', 'oauthGoogle', 'oauthGoogleAccessToken', 'oauthInstagram', 'oauthInstagramAccessToken', 'oauthLinkedin', 'oauthLinkedinAccessToken', 'oauthMicrosoft', 'oauthMicrosoftAccessToken', 'oauthTwitter', 'oauthTwitterAccessToken', ]); }, $results); $response->json(['sum' => $projectDB->getSum(), 'users' => $results]); } ); $utopia->get('/v1/users/:userId') ->desc('Get User') ->label('scope', 'users.read') ->label('sdk.namespace', 'users') ->label('sdk.method', 'getUser') ->label('sdk.description', 'Get user by its unique ID.') ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->action( function($userId) use ($response, $projectDB) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $response->json(array_merge($user->getArrayCopy([ '$uid', 'status', 'email', 'registration', 'confirm', 'name', 'oauthBitbucket', 'oauthBitBucketAccessToken', 'oauthFacebook', 'oauthFacebookAccessToken', 'oauthGithub', 'oauthGithubAccessToken', 'oauthGitlab', 'oauthGitlabAccessToken', 'oauthGoogle', 'oauthGoogleAccessToken', 'oauthInstagram', 'oauthInstagramAccessToken', 'oauthLinkedin', 'oauthLinkedinAccessToken', 'oauthMicrosoft', 'oauthMicrosoftAccessToken', 'oauthTwitter', 'oauthTwitterAccessToken', ]), ['roles' => Authorization::getRoles()])); } ); $utopia->get('/v1/users/:userId/prefs') ->desc('Get User Prefs') ->label('scope', 'users.read') ->label('sdk.namespace', 'users') ->label('sdk.method', 'getUserPrefs') ->label('sdk.description', 'Get user preferences by its unique ID.') ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->action( function($userId) use ($response, $projectDB) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $prefs = $user->getAttribute('prefs', ''); if(empty($prefs)) { $prefs = []; } try { $prefs = json_decode($prefs, true); } catch (\Exception $error) { throw new Exception('Failed to parse prefs', 500); } $response->json($prefs); } ); $utopia->get('/v1/users/:userId/sessions') ->desc('Get User Sessions') ->label('scope', 'users.read') ->label('sdk.namespace', 'users') ->label('sdk.method', 'getUserSessions') ->label('sdk.description', 'Get user sessions list by its unique ID.') ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->action( function($userId) use ($response, $projectDB) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $tokens = $user->getAttribute('tokens', []); $reader = new Reader('../app/db/GeoLite2/GeoLite2-Country.mmdb'); $sessions = []; $index = 0; $countries = Locale::getText('countries'); foreach($tokens as $token) { /* @var $token Document */ if(Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { continue; } $userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN'; $dd = new DeviceDetector($userAgent); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) // $dd->skipBotDetection(); $dd->parse(); $sessions[$index] = [ '$uid' => $token->getUid(), 'OS' => $dd->getOs(), 'client' => $dd->getClient(), 'device' => $dd->getDevice(), 'brand' => $dd->getBrand(), 'model' => $dd->getModel(), 'ip' => $token->getAttribute('ip', ''), 'geo' => [], ]; try { $record = $reader->country($token->getAttribute('ip', '')); $sessions[$index]['geo']['isoCode'] = strtolower($record->country->isoCode); $sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown'); } catch(\Exception $e) { $sessions[$index]['geo']['isoCode'] = '--'; $sessions[$index]['geo']['country'] = Locale::getText('locale.country.unknown'); } $index++; } $response->json($sessions); } ); $utopia->get('/v1/users/:userId/logs') ->desc('Get User Logs') ->label('scope', 'users.read') ->label('sdk.namespace', 'users') ->label('sdk.method', 'getUserLogs') ->label('sdk.description', 'Get user activity logs list by its unique ID.') ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->action( function($userId) use ($response, $register, $projectDB, $project) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $ad = new \Audit\Adapter\MySQL($register->get('db')); $ad->setNamespace('app_' . $project->getUid()); $au = new \Audit\Audit($ad, $user->getUid(), $user->getAttribute('type'), '', '', ''); $countries = Locale::getText('countries'); $logs = $au->getLogsByUser($user->getUid(), $user->getAttribute('type', 0)); $reader = new Reader('../app/db/GeoLite2/GeoLite2-Country.mmdb'); $output = []; foreach($logs as $i => &$log) { $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; $dd = new DeviceDetector($log['userAgent']); $dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) $dd->parse(); $output[$i] = [ 'event' => $log['event'], 'ip' => $log['ip'], 'time' => strtotime($log['time']), 'OS' => $dd->getOs(), 'client' => $dd->getClient(), 'device' => $dd->getDevice(), 'brand' => $dd->getBrand(), 'model' => $dd->getModel(), 'geo' => [], ]; try { $record = $reader->country($log['ip']); $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode); $output[$i]['geo']['country'] = $record->country->name; $output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown'); } catch(\Exception $e) { $output[$i]['geo']['isoCode'] = '--'; $output[$i]['geo']['country'] = Locale::getText('locale.country.unknown');; } } $response->json($output); } ); $utopia->post('/v1/users') ->desc('Create User') ->label('scope', 'users.write') ->label('sdk.namespace', 'users') ->label('sdk.method', 'createUser') ->label('sdk.description', 'Create a new user.') ->param('email', '', function () {return new Email();}, 'User account email.') ->param('password', '', function () {return new Password();}, 'User account password.') ->param('name', '', function () {return new Text(100);}, 'User account name.', true) ->action( function($email, $password, $name) use ($response, $register, $projectDB, $project) { $profile = $projectDB->getCollection([ // Get user by email address 'limit' => 1, 'first' => true, 'filters' => [ '$collection=' . Database::SYSTEM_COLLECTION_USERS, 'email=' . $email ] ]); if(!empty($profile)) { throw new Exception('User already registered', 400); } $user = $projectDB->createDocument([ '$collection' => Database::SYSTEM_COLLECTION_USERS, '$permissions' => [ 'read' => ['*'], 'write' => ['user:{self}'], ], 'email' => $email, 'status' => Auth::USER_STATUS_UNACTIVATED, 'password' => Auth::passwordHash($password), 'password-update' => time(), 'registration' => time(), 'confirm' => false, 'reset' => false, 'name' => $name, ]); $response->json(array_merge($user->getArrayCopy([ '$uid', 'status', 'email', 'registration', 'confirm', 'name', 'oauthBitbucket', 'oauthBitBucketAccessToken', 'oauthFacebook', 'oauthFacebookAccessToken', 'oauthGithub', 'oauthGithubAccessToken', 'oauthGitlab', 'oauthGitlabAccessToken', 'oauthGoogle', 'oauthGoogleAccessToken', 'oauthInstagram', 'oauthInstagramAccessToken', 'oauthLinkedin', 'oauthLinkedinAccessToken', 'oauthMicrosoft', 'oauthMicrosoftAccessToken', 'oauthTwitter', 'oauthTwitterAccessToken', ]), ['roles' => Authorization::getRoles()])); } ); $utopia->patch('/v1/users/:userId/status') ->desc('Block User') ->label('scope', 'users.write') ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateUserStatus') ->label('sdk.description', 'Update user status by its unique ID.') ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->param('status', '', function () {return new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED]);}, 'User Status code. To activate the user pass ' . Auth::USER_STATUS_ACTIVATED . ', to blocking the user pass ' . Auth::USER_STATUS_BLOCKED . ' and for disabling the user pass ' . Auth::USER_STATUS_UNACTIVATED) ->action( function($userId, $status) use ($response, $projectDB) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [ 'status' => $status, ])); if(false === $user) { throw new Exception('Failed saving user to DB', 500); } $response ->json(array('result' => 'success')); } ); $utopia->delete('/v1/users/:userId/sessions/:session') ->desc('Delete User Session') ->label('scope', 'users.write') ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteUsersSession') ->label('sdk.description', 'Delete user sessions by its unique ID.') ->label('abuse-limit', 100) ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->param('sessionId', null, function () {return new UID();}, 'User unique session ID.') ->action( function($userId, $sessionId) use ($response, $request, $projectDB) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $tokens = $user->getAttribute('tokens', []); foreach($tokens as $token) { /* @var $token Document */ if($sessionId == $token->getUid()) { if(!$projectDB->deleteDocument($token->getUid())) { throw new Exception('Failed to remove token from DB', 500); } } } $response->json(array('result' => 'success')); } ); $utopia->delete('/v1/users/:userId/sessions') ->desc('Delete User Sessions') ->label('scope', 'users.write') ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteUserSessions') ->label('sdk.description', 'Delete all user sessions by its unique ID.') ->label('abuse-limit', 100) ->param('userId', '', function () {return new UID();}, 'User unique ID.') ->action( function($userId) use ($response, $request, $projectDB) { $user = $projectDB->getDocument($userId); if(empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { throw new Exception('User not found', 404); } $tokens = $user->getAttribute('tokens', []); foreach($tokens as $token) { /* @var $token Document */ if(!$projectDB->deleteDocument($token->getUid())) { throw new Exception('Failed to remove token from DB', 500); } } $response->json(array('result' => 'success')); } );