Implement IAM Custom Roles
This commit is contained in:
parent
a0cd65b94b
commit
47b10fa82b
4 changed files with 114 additions and 38 deletions
|
@ -562,7 +562,7 @@ App::get('/v1/migrations/firebase/report/oauth')
|
||||||
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
|
$dbForConsole->updateDocument('identities', $identity->getId(), $identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
$firebase = new Firebase(array_merge($serviceAccount, ['project_id' => $projectId]));
|
$firebase = new Firebase($serviceAccount);
|
||||||
|
|
||||||
$report = $firebase->report($resources);
|
$report = $firebase->report($resources);
|
||||||
|
|
||||||
|
|
|
@ -76,12 +76,16 @@
|
||||||
"webonyx/graphql-php": "14.11.*",
|
"webonyx/graphql-php": "14.11.*",
|
||||||
"slickdeals/statsd": "3.1.0",
|
"slickdeals/statsd": "3.1.0",
|
||||||
"league/csv": "9.7.1",
|
"league/csv": "9.7.1",
|
||||||
"utopia-php/migration": "^0.2.0"
|
"utopia-php/migration": "^0.3.0"
|
||||||
},
|
},
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
"url": "https://github.com/appwrite/runtimes.git",
|
"url": "https://github.com/appwrite/runtimes.git",
|
||||||
"type": "git"
|
"type": "git"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/utopia-php/vcs.git",
|
||||||
|
"type": "git"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|
42
composer.lock
generated
42
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "a7de7a77dd4c58b74bc8fe9c0387979e",
|
"content-hash": "b87e28f6f096af1fd3b1ddee62fe2f13",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/jwt",
|
"name": "adhocore/jwt",
|
||||||
|
@ -1963,16 +1963,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/migration",
|
"name": "utopia-php/migration",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/utopia-php/migration.git",
|
"url": "https://github.com/utopia-php/migration.git",
|
||||||
"reference": "9dc59bbe0d126e20434580a5aa7cae5793bab024"
|
"reference": "8a1d4de19002e4c8eabf368edff6b653b6722880"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/9dc59bbe0d126e20434580a5aa7cae5793bab024",
|
"url": "https://api.github.com/repos/utopia-php/migration/zipball/8a1d4de19002e4c8eabf368edff6b653b6722880",
|
||||||
"reference": "9dc59bbe0d126e20434580a5aa7cae5793bab024",
|
"reference": "8a1d4de19002e4c8eabf368edff6b653b6722880",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2015,9 +2015,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/utopia-php/migration/issues",
|
"issues": "https://github.com/utopia-php/migration/issues",
|
||||||
"source": "https://github.com/utopia-php/migration/tree/0.2.0"
|
"source": "https://github.com/utopia-php/migration/tree/0.3.0"
|
||||||
},
|
},
|
||||||
"time": "2023-08-09T16:28:43+00:00"
|
"time": "2023-08-16T11:57:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "utopia-php/mongo",
|
"name": "utopia-php/mongo",
|
||||||
|
@ -2844,7 +2844,7 @@
|
||||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||||
"source": "https://github.com/appwrite/sdk-generator/tree/master"
|
"source": "https://github.com/appwrite/sdk-generator/tree/0.34.0"
|
||||||
},
|
},
|
||||||
"time": "2023-07-25T01:15:31+00:00"
|
"time": "2023-07-25T01:15:31+00:00"
|
||||||
},
|
},
|
||||||
|
@ -3150,16 +3150,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v4.16.0",
|
"version": "v4.17.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "19526a33fb561ef417e822e85f08a00db4059c17"
|
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
|
||||||
"reference": "19526a33fb561ef417e822e85f08a00db4059c17",
|
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3200,9 +3200,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0"
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
|
||||||
},
|
},
|
||||||
"time": "2023-06-25T14:52:30+00:00"
|
"time": "2023-08-13T19:53:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phar-io/manifest",
|
"name": "phar-io/manifest",
|
||||||
|
@ -3427,16 +3427,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpdocumentor/type-resolver",
|
"name": "phpdocumentor/type-resolver",
|
||||||
"version": "1.7.2",
|
"version": "1.7.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||||
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
|
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
|
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
|
||||||
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
|
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -3479,9 +3479,9 @@
|
||||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
|
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
|
||||||
},
|
},
|
||||||
"time": "2023-05-30T18:13:47+00:00"
|
"time": "2023-08-12T11:01:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpspec/prophecy",
|
"name": "phpspec/prophecy",
|
||||||
|
@ -5444,5 +5444,5 @@
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.0"
|
"php": "8.0"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.3.0"
|
"plugin-api-version": "2.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,38 @@ class Firebase extends OAuth2
|
||||||
'https://www.googleapis.com/auth/userinfo.profile'
|
'https://www.googleapis.com/auth/userinfo.profile'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected array $iamPermissions = [
|
||||||
|
// Database
|
||||||
|
'datastore.databases.get',
|
||||||
|
'datastore.databases.list',
|
||||||
|
'datastore.entities.get',
|
||||||
|
'datastore.entities.list',
|
||||||
|
'datastore.indexes.get',
|
||||||
|
'datastore.indexes.list',
|
||||||
|
// Generic Firebase permissions
|
||||||
|
'firebase.projects.get',
|
||||||
|
|
||||||
|
// Auth
|
||||||
|
'firebaseauth.configs.get',
|
||||||
|
'firebaseauth.configs.getHashConfig',
|
||||||
|
'firebaseauth.configs.getSecret',
|
||||||
|
'firebaseauth.users.get',
|
||||||
|
'identitytoolkit.tenants.get',
|
||||||
|
'identitytoolkit.tenants.list',
|
||||||
|
|
||||||
|
// IAM Assignment
|
||||||
|
'iam.serviceAccounts.list',
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
'storage.buckets.get',
|
||||||
|
'storage.buckets.list',
|
||||||
|
'storage.objects.get',
|
||||||
|
'storage.objects.list'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -198,7 +230,7 @@ class Firebase extends OAuth2
|
||||||
/*
|
/*
|
||||||
Be careful with the setIAMPolicy method, it will overwrite all existing policies
|
Be careful with the setIAMPolicy method, it will overwrite all existing policies
|
||||||
**/
|
**/
|
||||||
public function assignIAMRoles(string $accessToken, string $email, string $projectId)
|
public function assignIAMRole(string $accessToken, string $email, string $projectId, array $role)
|
||||||
{
|
{
|
||||||
// Get IAM Roles
|
// Get IAM Roles
|
||||||
$iamRoles = $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':getIamPolicy', [
|
$iamRoles = $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':getIamPolicy', [
|
||||||
|
@ -209,14 +241,7 @@ class Firebase extends OAuth2
|
||||||
$iamRoles = \json_decode($iamRoles, true);
|
$iamRoles = \json_decode($iamRoles, true);
|
||||||
|
|
||||||
$iamRoles['bindings'][] = [
|
$iamRoles['bindings'][] = [
|
||||||
'role' => 'roles/identitytoolkit.admin',
|
'role' => $role['name'],
|
||||||
'members' => [
|
|
||||||
'serviceAccount:' . $email
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
$iamRoles['bindings'][] = [
|
|
||||||
'role' => 'roles/firebase.admin',
|
|
||||||
'members' => [
|
'members' => [
|
||||||
'serviceAccount:' . $email
|
'serviceAccount:' . $email
|
||||||
]
|
]
|
||||||
|
@ -226,7 +251,7 @@ class Firebase extends OAuth2
|
||||||
$this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':setIamPolicy', [
|
$this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':setIamPolicy', [
|
||||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||||
'Content-Type: application/json'
|
'Content-Type: application/json'
|
||||||
], json_encode([
|
], \json_encode([
|
||||||
'policy' => $iamRoles
|
'policy' => $iamRoles
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
@ -242,6 +267,48 @@ class Firebase extends OAuth2
|
||||||
return $randomString;
|
return $randomString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createCustomRole(string $accessToken, string $projectId): array
|
||||||
|
{
|
||||||
|
// Check if role already exists
|
||||||
|
try {
|
||||||
|
$role = $this->request('GET', 'https://iam.googleapis.com/v1/projects/' . $projectId . '/roles/appwriteMigrations', [
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$role = \json_decode($role, true);
|
||||||
|
|
||||||
|
return $role;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($e->getCode() !== 404) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create role if doesn't exist or isn't correct
|
||||||
|
$role = $this->request(
|
||||||
|
'POST',
|
||||||
|
'https://iam.googleapis.com/v1/projects/' . $projectId . '/roles/',
|
||||||
|
[
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||||
|
],
|
||||||
|
\json_encode(
|
||||||
|
[
|
||||||
|
'roleId' => 'appwriteMigrations',
|
||||||
|
'role' => [
|
||||||
|
'title' => 'Appwrite Migrations',
|
||||||
|
'description' => 'A helper role for Appwrite Migrations',
|
||||||
|
'includedPermissions' => $this->iamPermissions,
|
||||||
|
'stage' => 'GA'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return json_decode($role, true);
|
||||||
|
}
|
||||||
|
|
||||||
public function createServiceAccount(string $accessToken, string $projectId): array
|
public function createServiceAccount(string $accessToken, string $projectId): array
|
||||||
{
|
{
|
||||||
// Create Service Account
|
// Create Service Account
|
||||||
|
@ -254,7 +321,7 @@ class Firebase extends OAuth2
|
||||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||||
'Content-Type: application/json'
|
'Content-Type: application/json'
|
||||||
],
|
],
|
||||||
json_encode([
|
\json_encode([
|
||||||
'accountId' => 'appwrite-' . $uid,
|
'accountId' => 'appwrite-' . $uid,
|
||||||
'serviceAccount' => [
|
'serviceAccount' => [
|
||||||
'displayName' => 'Appwrite Migrations ' . $uid
|
'displayName' => 'Appwrite Migrations ' . $uid
|
||||||
|
@ -264,7 +331,12 @@ class Firebase extends OAuth2
|
||||||
|
|
||||||
$response = json_decode($response, true);
|
$response = json_decode($response, true);
|
||||||
|
|
||||||
$this->assignIAMRoles($accessToken, $response['email'], $projectId);
|
// Create and assign IAM Roles
|
||||||
|
$role = $this->createCustomRole($accessToken, $projectId);
|
||||||
|
|
||||||
|
\sleep(1); // Wait for IAM to propagate changes.
|
||||||
|
|
||||||
|
$this->assignIAMRole($accessToken, $response['email'], $projectId, $role);
|
||||||
|
|
||||||
// Create Service Account Key
|
// Create Service Account Key
|
||||||
$responseKey = $this->request(
|
$responseKey = $this->request(
|
||||||
|
@ -286,10 +358,10 @@ class Firebase extends OAuth2
|
||||||
// List Service Accounts
|
// List Service Accounts
|
||||||
$response = $this->request(
|
$response = $this->request(
|
||||||
'GET',
|
'GET',
|
||||||
'https://iam.googleapis.com/v1/projects/'.$projectId.'/serviceAccounts',
|
'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts',
|
||||||
[
|
[
|
||||||
'Authorization: Bearer ' . \urlencode($accessToken),
|
'Authorization: Bearer ' . \urlencode($accessToken),
|
||||||
'Content-Type: application/json'
|
'Content-Type: application/json'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue