1
0
Fork 0
mirror of synced 2024-06-27 18:50:47 +12:00

Add Service Account Role Assignment

This commit is contained in:
Bradley Schofield 2023-08-09 23:46:23 +01:00
parent fead8eb47a
commit d001f14249
No known key found for this signature in database
GPG key ID: CDDF1217D7D66C9D
5 changed files with 107 additions and 65 deletions

View file

@ -2732,7 +2732,7 @@ $projectCollections = array_merge([
'required' => false,
'default' => [],
'array' => false,
'filters' => ['json'],
'filters' => ['json', 'encrypt'],
],
[
'$id' => ID::custom('resources'),

View file

@ -84,56 +84,6 @@ App::post('/v1/migrations/appwrite')
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Migrate Firebase Data (Service Account)')
->label('scope', 'migrations.write')
->label('event', 'migrations.create')
->label('audits.event', 'migration.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'migrations')
->label('sdk.method', 'createFirebaseMigration')
->label('sdk.description', '/docs/references/migrations/migration-firebase.md')
->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MIGRATION)
->param('resources', [], new ArrayList(new WhiteList(Firebase::getSupportedResources())), 'List of resources to migrate')
->param('serviceAccount', '', new Text(65536), 'JSON of the Firebase service account credentials')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->inject('events')
->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, Document $user, Event $events) {
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => ID::unique(),
'status' => 'pending',
'stage' => 'init',
'source' => Firebase::getName(),
'credentials' => [
'serviceAccount' => $serviceAccount,
],
'resources' => $resources,
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => [],
]));
$events->setParam('migrationId', $migration->getId());
// Trigger Transfer
$event = new Migration();
$event
->setMigration($migration)
->setProject($project)
->setUser($user)
->trigger();
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::post('/v1/migrations/firebase/oauth')
->groups(['api', 'migrations'])
->desc('Migrate Firebase Data (OAuth)')
@ -168,8 +118,7 @@ App::post('/v1/migrations/firebase/oauth')
Query::equal('userInternalId', [$user->getInternalId()]),
]);
if ($identity === false || $identity->isEmpty()) {
throw new Exception(Exception::GENERAL_SERVER_ERROR); //TODO: REMOVE
// throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}
$accessToken = $identity->getAttribute('providerAccessToken');
@ -213,7 +162,7 @@ App::post('/v1/migrations/firebase/oauth')
'stage' => 'init',
'source' => Firebase::getName(),
'credentials' => [
'serviceAccount' => $serviceAccount,
'serviceAccount' => json_encode($serviceAccount),
],
'resources' => $resources,
'statusCounters' => '{}',
@ -224,7 +173,56 @@ App::post('/v1/migrations/firebase/oauth')
$events->setParam('migrationId', $migration->getId());
// Trigger Transfer
var_dump($project);
$event = new Migration();
$event
->setMigration($migration)
->setProject($project)
->setUser($user)
->trigger();
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Migrate Firebase Data (Service Account)')
->label('scope', 'migrations.write')
->label('event', 'migrations.create')
->label('audits.event', 'migration.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'migrations')
->label('sdk.method', 'createFirebaseMigration')
->label('sdk.description', '/docs/references/migrations/migration-firebase.md')
->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MIGRATION)
->param('resources', [], new ArrayList(new WhiteList(Firebase::getSupportedResources())), 'List of resources to migrate')
->param('serviceAccount', '', new Text(65536), 'JSON of the Firebase service account credentials')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->inject('events')
->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, Document $user, Event $events) {
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => ID::unique(),
'status' => 'pending',
'stage' => 'init',
'source' => Firebase::getName(),
'credentials' => [
'serviceAccount' => $serviceAccount,
],
'resources' => $resources,
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => [],
]));
$events->setParam('migrationId', $migration->getId());
// Trigger Transfer
$event = new Migration();
$event
->setMigration($migration)
@ -515,8 +513,7 @@ App::get('/v1/migrations/firebase/report/oauth')
Query::equal('userInternalId', [$user->getInternalId()]),
]);
if ($identity === false || $identity->isEmpty()) {
throw new Exception(Exception::GENERAL_SERVER_ERROR); //TODO: REMOVE
// throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}
$accessToken = $identity->getAttribute('providerAccessToken');
@ -749,7 +746,7 @@ App::get('/v1/migrations/firebase/projects')
Query::equal('userInternalId', [$user->getInternalId()]),
]);
if ($identity === false || $identity->isEmpty()) {
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN, 'Not authenticated with Firebase'); //TODO: Replace with USER_IDENTITY_NOT_FOUND
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
}
$accessToken = $identity->getAttribute('providerAccessToken');
@ -766,7 +763,11 @@ App::get('/v1/migrations/firebase/projects')
$isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now');
if ($isExpired) {
$firebase->refreshTokens($refreshToken);
try {
$firebase->refreshTokens($refreshToken);
} catch (\Exception $e) {
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN, 'Failed to refresh Firebase access token');
}
$accessToken = $firebase->getAccessToken('');
$refreshToken = $firebase->getRefreshToken('');

View file

@ -5,7 +5,6 @@ use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Permission;
use Appwrite\Resque\Worker;
use Appwrite\Role;
use Appwrite\Utopia\Response\Model\Migration;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
@ -61,7 +60,7 @@ class MigrationsV1 extends Worker
return;
}
$this->dbForProject = $this->getProjectDB($this->args['project']['$id']);
$this->dbForProject = $this->getProjectDB(new Document($this->args['project']));
$this->processMigration();
}

View file

@ -618,6 +618,11 @@ services:
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_CONNECTIONS_DB_PROJECT
- _APP_CONNECTIONS_DB_CONSOLE
- _APP_CONNECTIONS_CACHE
- _APP_CONNECTIONS_QUEUE
- _APP_CONNECTIONS_PUBSUB
appwrite-maintenance:
entrypoint: maintenance

View file

@ -87,7 +87,7 @@ class Firebase extends OAuth2
{
$response = $this->request(
'POST',
'https://github.com/login/oauth/access_token',
'https://oauth2.googleapis.com/token',
[],
\http_build_query([
'client_id' => $this->appID,
@ -195,12 +195,47 @@ class Firebase extends OAuth2
return $projects['results'];
}
public function createServiceAccount(string $accessToken, string $projectID): array
/*
Be careful with the setIAMPolicy method, it will overwrite all existing policies
**/
public function assignIAMRoles(string $accessToken, string $email, string $projectId) {
// Get IAM Roles
$iamRoles = $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/'.$projectId.':getIamPolicy', [
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
]);
$iamRoles = \json_decode($iamRoles, true);
$iamRoles['bindings'][] = [
'role' => 'roles/identitytoolkit.admin',
'members' => [
'serviceAccount:'.$email
]
];
$iamRoles['bindings'][] = [
'role' => 'roles/firebase.admin',
'members' => [
'serviceAccount:'.$email
]
];
// Set IAM Roles
$this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/'.$projectId.':setIamPolicy', [
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
], json_encode([
'policy' => $iamRoles
]));
}
public function createServiceAccount(string $accessToken, string $projectId): array
{
// Create Service Account
$response = $this->request(
'POST',
'https://iam.googleapis.com/v1/projects/' . $projectID . '/serviceAccounts',
'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts',
[
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'
@ -215,10 +250,12 @@ class Firebase extends OAuth2
$response = json_decode($response, true);
$this->assignIAMRoles($accessToken, $response['email'], $projectId);
// Create Service Account Key
$responseKey = $this->request(
'POST',
'https://iam.googleapis.com/v1/projects/' . $projectID . '/serviceAccounts/' . $response['email'] . '/keys',
'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts/' . $response['email'] . '/keys',
[
'Authorization: Bearer ' . \urlencode($accessToken),
'Content-Type: application/json'