@dataroadinc/setup-auth
Version:
CLI tool and programmatic API for automated OAuth setup across cloud platforms
116 lines (115 loc) • 4.87 kB
JavaScript
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 });
}
}
}