UNPKG

@dataroadinc/setup-auth

Version:

CLI tool and programmatic API for automated OAuth setup across cloud platforms

116 lines (115 loc) 4.87 kB
import { SetupAuthError } from "../../../utils/error.js"; import { OrganizationsClient } from "@google-cloud/resource-manager"; import { BaseGcpIamManager } from "./base-iam.js"; import { GLOBAL_PERMISSIONS } from "./constants.js"; export class GcpGlobalIamManager extends BaseGcpIamManager { constructor(identity) { super(identity); } formatResourceName() { throw new SetupAuthError("Global permissions do not use resource names"); } async initializeSpecific() { this.organizationsClient = new OrganizationsClient({ auth: await this.identity.getGaxAuthClient(), }); } async testGlobalPermissions() { await this.initialize(); if (!this.organizationsClient) { throw new SetupAuthError("OrganizationsClient not initialized"); } const grantedPermissions = new Set(); try { await this.organizationsClient.searchOrganizations({}); grantedPermissions.add(GLOBAL_PERMISSIONS.LIST_ORGANIZATIONS); } catch (error) { if (!(error instanceof Error && error.message.includes("permission denied"))) { console.error("Unexpected error testing LIST_ORGANIZATIONS permission:", error); } } return grantedPermissions; } async checkPermissions() { await this.initialize(); console.log("Checking global permissions..."); try { const grantedPermissions = await this.testGlobalPermissions(); const missingPermissions = Object.values(GLOBAL_PERMISSIONS).filter(permission => !grantedPermissions.has(permission)); return { missingPermissions }; } catch (error) { console.error("Error checking global permissions:", error); return { missingPermissions: Object.values(GLOBAL_PERMISSIONS), }; } } async ensurePermissions() { await this.initialize(); if (!this.organizationsClient) { throw new SetupAuthError("OrganizationsClient not initialized"); } const { missingPermissions } = await this.checkPermissions(); if (missingPermissions.length === 0) { console.log("All required global permissions are already granted."); return; } const rolesToGrant = new Set(); const perm = GLOBAL_PERMISSIONS; if (missingPermissions.includes(perm.LIST_ORGANIZATIONS) || missingPermissions.includes(perm.LIST_FOLDERS)) { rolesToGrant.add("roles/resourcemanager.organizationViewer"); } if (missingPermissions.includes(perm.CREATE_PROJECT)) { rolesToGrant.add("roles/resourcemanager.projectCreator"); } if (missingPermissions.includes(perm.LIST_BILLING_ACCOUNTS)) { rolesToGrant.add("roles/billing.viewer"); } if (rolesToGrant.size === 0) { console.log("No corresponding roles found for missing permissions"); return; } const member = `user:${this.userEmail}`; try { const [orgs] = await this.organizationsClient.searchOrganizations({}); if (!orgs || orgs.length === 0) { throw new SetupAuthError("No organizations found for the current credentials."); } for (const org of orgs) { if (!org.name) continue; console.log(`Attempting to grant global roles in ${org.displayName || org.name} …`); const [policy] = await this.organizationsClient.getIamPolicy({ resource: org.name, }); policy.bindings = policy.bindings ?? []; for (const role of rolesToGrant) { let binding = policy.bindings.find(b => b.role === role); if (binding) { binding.members = binding.members ?? []; if (!binding.members.includes(member)) { binding.members.push(member); } } else { policy.bindings.push({ role, members: [member] }); } } await this.organizationsClient.setIamPolicy({ resource: org.name, policy, }); console.log(`✅ Granted roles on ${org.displayName || org.name} to ${member}:`); for (const role of rolesToGrant) { console.log(` - ${role}`); } } } catch (error) { throw new SetupAuthError("Failed to grant global permissions automatically", { cause: error }); } } }