2023-11-02 17:05:51 +00:00
< ? php
global $utopia , $request , $response ;
use Appwrite\Extend\Exception ;
2024-10-08 07:54:40 +00:00
use Appwrite\Utopia\Request ;
2023-11-02 17:05:51 +00:00
use Appwrite\Utopia\Response ;
2024-05-16 08:39:15 +00:00
use Utopia\Config\Config ;
2023-11-02 17:05:51 +00:00
use Utopia\Database\Database ;
2024-03-06 17:34:21 +00:00
use Utopia\Database\Document ;
2023-11-02 17:05:51 +00:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2024-03-06 17:34:21 +00:00
use Utopia\Database\Validator\UID ;
2026-02-10 05:04:24 +00:00
use Utopia\Http\Http ;
2025-08-12 13:13:08 +00:00
use Utopia\Locale\Locale ;
2024-04-01 11:08:46 +00:00
use Utopia\System\System ;
2024-10-08 07:54:40 +00:00
use Utopia\Validator\Text ;
use Utopia\Validator\WhiteList ;
2024-03-06 17:34:21 +00:00
use Utopia\VCS\Adapter\Git\GitHub ;
2023-11-02 17:05:51 +00:00
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/general/oauth2' )
2023-11-02 17:05:51 +00:00
-> desc ( 'OAuth Login' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
2025-01-17 04:31:39 +00:00
-> label ( 'mock' , true )
2023-11-02 17:05:51 +00:00
-> param ( 'client_id' , '' , new Text ( 100 ), 'OAuth2 Client ID.' )
2025-12-07 20:29:45 +00:00
-> param ( 'redirect_uri' , '' , fn ( $redirectValidator ) => $redirectValidator , 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s 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.' , true , [ 'redirectValidator' ]) // Important to deny an open redirect attack
2023-11-02 17:05:51 +00:00
-> param ( 'scope' , '' , new Text ( 100 ), 'OAuth2 scope list.' )
-> param ( 'state' , '' , new Text ( 1024 ), 'OAuth2 state.' )
-> inject ( 'response' )
-> action ( function ( string $client_id , string $redirectURI , string $scope , string $state , Response $response ) {
$response -> redirect ( $redirectURI . '?' . \http_build_query ([ 'code' => 'abcdef' , 'state' => $state ]));
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/locale' )
2025-08-12 13:13:08 +00:00
-> desc ( 'Mock locale translation key' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> label ( 'mock' , true )
-> inject ( 'locale' )
-> inject ( 'localeCodes' )
-> inject ( 'request' )
-> inject ( 'response' )
-> action ( function ( Locale $locale , array $localeCodes , Request $request , Response $response ) {
$localeParam = ( string ) $request -> getParam ( 'locale' , $request -> getHeader ( 'x-appwrite-locale' , '' ));
if ( \in_array ( $localeParam , $localeCodes )) {
$locale -> setDefault ( $localeParam );
}
$response -> send ( $locale -> getText ( 'mock' ));
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/general/oauth2/token' )
2023-11-02 17:05:51 +00:00
-> desc ( 'OAuth2 Token' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
2025-01-17 04:31:39 +00:00
-> label ( 'mock' , true )
2023-11-02 17:05:51 +00:00
-> param ( 'client_id' , '' , new Text ( 100 ), 'OAuth2 Client ID.' )
-> param ( 'client_secret' , '' , new Text ( 100 ), 'OAuth2 scope list.' )
-> param ( 'grant_type' , 'authorization_code' , new WhiteList ([ 'refresh_token' , 'authorization_code' ]), 'OAuth2 Grant Type.' , true )
2025-12-07 20:29:45 +00:00
-> param ( 'redirect_uri' , '' , fn ( $redirectValidator ) => $redirectValidator , 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s 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.' , true , [ 'redirectValidator' ])
2023-11-02 17:05:51 +00:00
-> param ( 'code' , '' , new Text ( 100 ), 'OAuth2 state.' , true )
-> param ( 'refresh_token' , '' , new Text ( 100 ), 'OAuth2 refresh token.' , true )
-> inject ( 'response' )
-> action ( function ( string $client_id , string $client_secret , string $grantType , string $redirectURI , string $code , string $refreshToken , Response $response ) {
if ( $client_id != '1' ) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid client ID' );
}
if ( $client_secret != '123456' ) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid client secret' );
}
$responseJson = [
'access_token' => '123456' ,
'refresh_token' => 'tuvwxyz' ,
'expires_in' => 14400
];
if ( $grantType === 'authorization_code' ) {
if ( $code !== 'abcdef' ) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid token' );
}
$response -> json ( $responseJson );
} elseif ( $grantType === 'refresh_token' ) {
if ( $refreshToken !== 'tuvwxyz' ) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid refresh token' );
}
$response -> json ( $responseJson );
} else {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid grant type' );
}
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/general/oauth2/user' )
2023-11-02 17:05:51 +00:00
-> desc ( 'OAuth2 User' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> param ( 'token' , '' , new Text ( 100 ), 'OAuth2 Access Token.' )
-> inject ( 'response' )
-> action ( function ( string $token , Response $response ) {
if ( $token != '123456' ) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid token' );
}
$response -> json ([
'id' => 1 ,
'name' => 'User Name' ,
'email' => 'useroauth@localhost.test' ,
2025-12-18 12:44:04 +00:00
'verified' => true ,
]);
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/general/oauth2/user-unverified' )
2025-12-18 12:44:04 +00:00
-> desc ( 'OAuth2 User Unverified' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> param ( 'token' , '' , new Text ( 100 ), 'OAuth2 Access Token.' )
-> inject ( 'response' )
-> action ( function ( string $token , Response $response ) {
if ( $token != '123456' ) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Invalid token' );
}
$response -> json ([
'id' => 2 ,
'name' => 'User Name Unverified' ,
'email' => 'useroauthunverified@localhost.test' ,
'verified' => false ,
2023-11-02 17:05:51 +00:00
]);
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/general/oauth2/success' )
2023-11-02 17:05:51 +00:00
-> desc ( 'OAuth2 Success' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> inject ( 'response' )
-> action ( function ( Response $response ) {
$response -> json ([
'result' => 'success' ,
]);
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/tests/general/oauth2/failure' )
2023-11-02 17:05:51 +00:00
-> desc ( 'OAuth2 Failure' )
-> groups ([ 'mock' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> inject ( 'response' )
-> action ( function ( Response $response ) {
$response
-> setStatusCode ( Response :: STATUS_CODE_BAD_REQUEST )
-> json ([
'result' => 'failure' ,
]);
});
2026-02-04 05:30:22 +00:00
Http :: post ( '/v1/mock/api-key-unprefixed' )
2024-05-16 08:39:15 +00:00
-> desc ( 'Create API Key (without standard prefix)' )
-> groups ([ 'mock' , 'api' , 'projects' ])
2024-09-05 03:28:17 +00:00
-> label ( 'scope' , 'public' )
2024-05-16 08:39:15 +00:00
-> label ( 'docs' , false )
-> param ( 'projectId' , '' , new UID (), 'Project ID.' )
-> inject ( 'response' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $projectId , Response $response , Database $dbForPlatform ) {
2024-05-16 08:39:15 +00:00
$isDevelopment = System :: getEnv ( '_APP_ENV' , 'development' ) === 'development' ;
if ( ! $isDevelopment ) {
throw new Exception ( Exception :: GENERAL_NOT_IMPLEMENTED );
}
2024-12-12 10:30:26 +00:00
$project = $dbForPlatform -> getDocument ( 'projects' , $projectId );
2024-05-16 08:39:15 +00:00
if ( $project -> isEmpty ()) {
throw new Exception ( Exception :: PROJECT_NOT_FOUND );
}
2025-12-23 12:06:19 +00:00
$scopes = array_keys ( Config :: getParam ( 'projectScopes' ));
2024-05-16 08:39:15 +00:00
$key = new Document ([
'$id' => ID :: unique (),
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
2025-12-19 12:40:32 +00:00
'resourceInternalId' => $project -> getSequence (),
'resourceId' => $project -> getId (),
2026-01-06 12:19:32 +00:00
'resourceType' => 'projects' ,
2024-05-16 08:39:15 +00:00
'name' => 'Outdated key' ,
'scopes' => $scopes ,
'expire' => null ,
'sdks' => [],
'accessedAt' => null ,
'secret' => \bin2hex ( \random_bytes ( 128 )),
]);
2024-12-12 10:30:26 +00:00
$key = $dbForPlatform -> createDocument ( 'keys' , $key );
2024-05-16 08:39:15 +00:00
2024-12-12 10:30:26 +00:00
$dbForPlatform -> purgeCachedDocument ( 'projects' , $project -> getId ());
2024-05-16 08:39:15 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $key , Response :: MODEL_KEY );
});
2026-02-04 05:30:22 +00:00
Http :: get ( '/v1/mock/github/callback' )
2023-11-02 17:05:51 +00:00
-> desc ( 'Create installation document using GitHub installation id' )
-> groups ([ 'mock' , 'api' , 'vcs' ])
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> param ( 'providerInstallationId' , '' , new UID (), 'GitHub installation ID' )
-> param ( 'projectId' , '' , new UID (), 'Project ID of the project where app is to be installed' )
-> inject ( 'gitHub' )
-> inject ( 'project' )
-> inject ( 'response' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $providerInstallationId , string $projectId , GitHub $github , Document $project , Response $response , Database $dbForPlatform ) {
2024-04-01 11:02:47 +00:00
$isDevelopment = System :: getEnv ( '_APP_ENV' , 'development' ) === 'development' ;
2023-11-02 17:05:51 +00:00
if ( ! $isDevelopment ) {
throw new Exception ( Exception :: GENERAL_NOT_IMPLEMENTED );
}
2024-12-12 10:30:26 +00:00
$project = $dbForPlatform -> getDocument ( 'projects' , $projectId );
2023-11-02 17:05:51 +00:00
if ( $project -> isEmpty ()) {
$error = 'Project with the ID from state could not be found.' ;
throw new Exception ( Exception :: PROJECT_NOT_FOUND , $error );
}
if ( ! empty ( $providerInstallationId )) {
2024-04-01 11:02:47 +00:00
$privateKey = System :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = System :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-11-02 17:05:51 +00:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
$owner = $github -> getOwnerName ( $providerInstallationId ) ? ? '' ;
2025-05-26 05:42:11 +00:00
$projectInternalId = $project -> getSequence ();
2023-11-02 17:05:51 +00:00
$teamId = $project -> getAttribute ( 'teamId' , '' );
$installation = new Document ([
'$id' => ID :: unique (),
'$permissions' => [
Permission :: read ( Role :: team ( ID :: custom ( $teamId ))),
Permission :: update ( Role :: team ( ID :: custom ( $teamId ), 'owner' )),
Permission :: update ( Role :: team ( ID :: custom ( $teamId ), 'developer' )),
Permission :: delete ( Role :: team ( ID :: custom ( $teamId ), 'owner' )),
Permission :: delete ( Role :: team ( ID :: custom ( $teamId ), 'developer' )),
],
'providerInstallationId' => $providerInstallationId ,
'projectId' => $projectId ,
'projectInternalId' => $projectInternalId ,
'provider' => 'github' ,
'organization' => $owner ,
'personal' => false
]);
2024-12-12 10:30:26 +00:00
$installation = $dbForPlatform -> createDocument ( 'installations' , $installation );
2023-11-02 17:05:51 +00:00
}
$response -> json ([
'installationId' => $installation -> getId (),
]);
});
2026-02-04 05:30:22 +00:00
Http :: shutdown ()
2023-11-02 17:05:51 +00:00
-> groups ([ 'mock' ])
2024-10-08 07:54:40 +00:00
-> inject ( 'utopia' )
2023-11-02 17:05:51 +00:00
-> inject ( 'response' )
2024-10-08 07:54:40 +00:00
-> inject ( 'request' )
2026-02-04 05:30:22 +00:00
-> action ( function ( Http $utopia , Response $response , Request $request ) {
2023-11-02 17:05:51 +00:00
$result = [];
2024-10-08 07:54:40 +00:00
$route = $utopia -> getRoute ();
2023-11-02 17:05:51 +00:00
$path = APP_STORAGE_CACHE . '/tests.json' ;
$tests = ( \file_exists ( $path )) ? \json_decode ( \file_get_contents ( $path ), true ) : [];
if ( ! \is_array ( $tests )) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Failed to read results' , 500 );
}
$result [ $route -> getMethod () . ':' . $route -> getPath ()] = true ;
$tests = \array_merge ( $tests , $result );
if ( ! \file_put_contents ( $path , \json_encode ( $tests ), LOCK_EX )) {
throw new Exception ( Exception :: GENERAL_MOCK , 'Failed to save results' , 500 );
}
$response -> dynamic ( new Document ([ 'result' => $route -> getMethod () . ':' . $route -> getPath () . ':passed' ]), Response :: MODEL_MOCK );
});