Skip to content

Commit ced89c2

Browse files
authored
Merge branch 'main' into sale-34-policy-sort
2 parents 788fdb0 + ff7b2ec commit ced89c2

File tree

4 files changed

+650
-227
lines changed

4 files changed

+650
-227
lines changed

apps/api/src/cloud-security/cloud-security.controller.ts

Lines changed: 103 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import { CloudSecurityQueryService } from './cloud-security-query.service';
2525
import { CloudSecurityLegacyService } from './cloud-security-legacy.service';
2626
import { logCloudSecurityActivity } from './cloud-security-audit';
2727
import { CloudSecurityActivityService } from './cloud-security-activity.service';
28-
import { GCPSecurityService } from './providers/gcp-security.service';
28+
import {
29+
GCPSecurityService,
30+
type GcpSetupStep,
31+
type GcpSetupStepId,
32+
} from './providers/gcp-security.service';
2933
import { AzureSecurityService } from './providers/azure-security.service';
3034

3135
@Controller({ path: 'cloud-security', version: '1' })
@@ -226,55 +230,119 @@ export class CloudSecurityController {
226230
@OrganizationId() organizationId: string,
227231
) {
228232
try {
229-
const connection = await this.cloudSecurityService.getConnectionForDetect(
230-
connectionId,
231-
organizationId,
232-
);
233+
const context = await this.resolveGcpSetupContext(connectionId, organizationId);
233234

234-
const credentials = connection.credentials as Record<string, unknown>;
235-
const accessToken = credentials?.access_token as string;
236-
if (!accessToken) {
237-
throw new Error('No access token found. Reconnect the GCP integration.');
238-
}
235+
const result = await this.gcpSecurityService.autoSetup({
236+
accessToken: context.accessToken,
237+
organizationId: context.organizationId ?? '',
238+
projectId: context.projectId,
239+
});
239240

240-
const variables = (connection.variables ?? {}) as Record<string, unknown>;
241-
const gcpOrgId = variables.organization_id as string | undefined;
242-
243-
// Auto-detect org if not set
244-
let orgId = gcpOrgId;
245-
if (!orgId) {
246-
const orgs = await this.gcpSecurityService.detectOrganizations(accessToken);
247-
if (orgs.length > 0) {
248-
orgId = orgs[0].id;
249-
await this.cloudSecurityService.saveConnectionVariable(connectionId, 'organization_id', orgId, organizationId);
250-
}
251-
}
241+
return {
242+
...result,
243+
steps: this.withGcpResolveActions(result.steps, connectionId),
244+
organizationId: context.organizationId,
245+
projectId: context.projectId,
246+
};
247+
} catch (error) {
248+
const message =
249+
error instanceof Error ? error.message : 'GCP setup failed';
250+
throw new HttpException(message, HttpStatus.BAD_REQUEST);
251+
}
252+
}
252253

253-
// Auto-detect project if not known
254-
const projects = await this.gcpSecurityService.detectProjects(accessToken);
255-
const projectId = projects[0]?.id;
256-
if (!projectId) {
257-
throw new Error('No GCP projects found. Ensure your account has access to at least one project.');
254+
@Post('setup-gcp/:connectionId/resolve-step')
255+
@UseGuards(HybridAuthGuard, PermissionGuard)
256+
@RequirePermission('integration', 'update')
257+
async resolveGcpSetupStep(
258+
@Param('connectionId') connectionId: string,
259+
@Body() body: { stepId: GcpSetupStepId },
260+
@OrganizationId() organizationId: string,
261+
) {
262+
try {
263+
if (!body?.stepId) {
264+
throw new Error('stepId is required');
258265
}
259266

260-
const result = await this.gcpSecurityService.autoSetup({
261-
accessToken,
262-
organizationId: orgId ?? '',
263-
projectId,
267+
const context = await this.resolveGcpSetupContext(connectionId, organizationId);
268+
const result = await this.gcpSecurityService.resolveSetupStep({
269+
stepId: body.stepId,
270+
accessToken: context.accessToken,
271+
organizationId: context.organizationId ?? '',
272+
projectId: context.projectId,
264273
});
265274

266275
return {
267-
...result,
268-
organizationId: orgId,
269-
projectId,
276+
email: result.email,
277+
step: this.withGcpResolveActions([result.step], connectionId)[0],
278+
organizationId: context.organizationId,
279+
projectId: context.projectId,
270280
};
271281
} catch (error) {
272282
const message =
273-
error instanceof Error ? error.message : 'GCP setup failed';
283+
error instanceof Error ? error.message : 'Failed to resolve setup step';
274284
throw new HttpException(message, HttpStatus.BAD_REQUEST);
275285
}
276286
}
277287

288+
private withGcpResolveActions(steps: GcpSetupStep[], connectionId: string): GcpSetupStep[] {
289+
return steps.map((step) => {
290+
if (step.success) return step;
291+
return {
292+
...step,
293+
resolveAction: {
294+
label: 'Resolve this',
295+
method: 'POST',
296+
endpoint: `/v1/cloud-security/setup-gcp/${connectionId}/resolve-step`,
297+
body: { stepId: step.id },
298+
},
299+
};
300+
});
301+
}
302+
303+
private async resolveGcpSetupContext(connectionId: string, organizationId: string) {
304+
const connection = await this.cloudSecurityService.getConnectionForDetect(
305+
connectionId,
306+
organizationId,
307+
);
308+
309+
const credentials = connection.credentials as Record<string, unknown>;
310+
const accessToken = credentials?.access_token as string;
311+
if (!accessToken) {
312+
throw new Error('No access token found. Reconnect the GCP integration.');
313+
}
314+
315+
const variables = (connection.variables ?? {}) as Record<string, unknown>;
316+
let gcpOrgId = variables.organization_id as string | undefined;
317+
318+
if (!gcpOrgId) {
319+
const orgs = await this.gcpSecurityService.detectOrganizations(accessToken);
320+
if (orgs.length > 0) {
321+
gcpOrgId = orgs[0].id;
322+
await this.cloudSecurityService.saveConnectionVariable(
323+
connectionId,
324+
'organization_id',
325+
gcpOrgId,
326+
organizationId,
327+
);
328+
}
329+
}
330+
331+
const projects = await this.gcpSecurityService.detectProjects(accessToken);
332+
const projectId = projects[0]?.id;
333+
if (!projectId) {
334+
throw new Error(
335+
'No GCP projects found. Ensure your account has access to at least one project.',
336+
);
337+
}
338+
339+
return {
340+
accessToken,
341+
organizationId: gcpOrgId,
342+
projectId,
343+
};
344+
}
345+
278346
@Post('setup-azure/:connectionId')
279347
@UseGuards(HybridAuthGuard, PermissionGuard)
280348
@RequirePermission('integration', 'update')

0 commit comments

Comments
 (0)