Resolve merge conflicts

This commit is contained in:
Tuan Dang
2024-05-09 12:51:24 -07:00
214 changed files with 404 additions and 13253 deletions
+2 -2
View File
@@ -32,7 +32,7 @@ import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-se
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
import { TIdentityAwsIamAuthServiceFactory } from "@app/services/identity-aws-iam-auth/identity-aws-iam-auth-service";
import { TIdentityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
@@ -118,7 +118,7 @@ declare module "fastify" {
identityProject: TIdentityProjectServiceFactory;
identityUa: TIdentityUaServiceFactory;
identityGcpAuth: TIdentityGcpAuthServiceFactory;
identityAwsIamAuth: TIdentityAwsIamAuthServiceFactory;
identityAwsAuth: TIdentityAwsAuthServiceFactory;
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
+7 -7
View File
@@ -59,9 +59,9 @@ import {
TIdentityAccessTokens,
TIdentityAccessTokensInsert,
TIdentityAccessTokensUpdate,
TIdentityAwsIamAuths,
TIdentityAwsIamAuthsInsert,
TIdentityAwsIamAuthsUpdate,
TIdentityAwsAuths,
TIdentityAwsAuthsInsert,
TIdentityAwsAuthsUpdate,
TIdentityGcpAuths,
TIdentityGcpAuthsInsert,
TIdentityGcpAuthsUpdate,
@@ -337,10 +337,10 @@ declare module "knex/types/tables" {
TIdentityGcpAuthsInsert,
TIdentityGcpAuthsUpdate
>;
[TableName.IdentityAwsIamAuth]: Knex.CompositeTableType<
TIdentityAwsIamAuths,
TIdentityAwsIamAuthsInsert,
TIdentityAwsIamAuthsUpdate
[TableName.IdentityAwsAuth]: Knex.CompositeTableType<
TIdentityAwsAuths,
TIdentityAwsAuthsInsert,
TIdentityAwsAuthsUpdate
>;
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
TIdentityUaClientSecrets,
@@ -4,8 +4,8 @@ import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityAwsIamAuth))) {
await knex.schema.createTable(TableName.IdentityAwsIamAuth, (t) => {
if (!(await knex.schema.hasTable(TableName.IdentityAwsAuth))) {
await knex.schema.createTable(TableName.IdentityAwsAuth, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
@@ -14,16 +14,17 @@ export async function up(knex: Knex): Promise<void> {
t.timestamps(true, true, true);
t.uuid("identityId").notNullable().unique();
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
t.string("type").notNullable();
t.string("stsEndpoint").notNullable();
t.string("allowedPrincipalArns").notNullable();
t.string("allowedAccountIds").notNullable();
});
}
await createOnUpdateTrigger(knex, TableName.IdentityAwsIamAuth);
await createOnUpdateTrigger(knex, TableName.IdentityAwsAuth);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IdentityAwsIamAuth);
await dropOnUpdateTrigger(knex, TableName.IdentityAwsIamAuth);
await knex.schema.dropTableIfExists(TableName.IdentityAwsAuth);
await dropOnUpdateTrigger(knex, TableName.IdentityAwsAuth);
}
@@ -7,7 +7,7 @@ import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const IdentityAwsIamAuthsSchema = z.object({
export const IdentityAwsAuthsSchema = z.object({
id: z.string().uuid(),
accessTokenTTL: z.coerce.number().default(7200),
accessTokenMaxTTL: z.coerce.number().default(7200),
@@ -16,11 +16,12 @@ export const IdentityAwsIamAuthsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid(),
type: z.string(),
stsEndpoint: z.string(),
allowedPrincipalArns: z.string(),
allowedAccountIds: z.string()
});
export type TIdentityAwsIamAuths = z.infer<typeof IdentityAwsIamAuthsSchema>;
export type TIdentityAwsIamAuthsInsert = Omit<z.input<typeof IdentityAwsIamAuthsSchema>, TImmutableDBKeys>;
export type TIdentityAwsIamAuthsUpdate = Partial<Omit<z.input<typeof IdentityAwsIamAuthsSchema>, TImmutableDBKeys>>;
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;
export type TIdentityAwsAuthsInsert = Omit<z.input<typeof IdentityAwsAuthsSchema>, TImmutableDBKeys>;
export type TIdentityAwsAuthsUpdate = Partial<Omit<z.input<typeof IdentityAwsAuthsSchema>, TImmutableDBKeys>>;
+1 -1
View File
@@ -17,7 +17,7 @@ export * from "./group-project-memberships";
export * from "./groups";
export * from "./identities";
export * from "./identity-access-tokens";
export * from "./identity-aws-iam-auths";
export * from "./identity-aws-auths";
export * from "./identity-gcp-auths";
export * from "./identity-org-memberships";
export * from "./identity-project-additional-privilege";
+2 -2
View File
@@ -46,7 +46,7 @@ export enum TableName {
IdentityUniversalAuth = "identity_universal_auths",
IdentityGcpAuth = "identity_gcp_auths",
IdentityUaClientSecret = "identity_ua_client_secrets",
IdentityAwsIamAuth = "identity_aws_iam_auths",
IdentityAwsAuth = "identity_aws_auths",
IdentityOrgMembership = "identity_org_memberships",
IdentityProjectMembership = "identity_project_memberships",
IdentityProjectMembershipRole = "identity_project_membership_role",
@@ -146,5 +146,5 @@ export enum ProjectUpgradeStatus {
export enum IdentityAuthMethod {
Univeral = "universal-auth",
GCP_AUTH = "gcp-auth",
AWS_IAM_AUTH = "aws-iam-auth"
AWS_AUTH = "aws-auth"
}
@@ -70,10 +70,10 @@ export enum EventType {
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp -auth",
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
LOGIN_IDENTITY_AWS_IAM_AUTH = "login-identity-aws-iam-auth",
ADD_IDENTITY_AWS_IAM_AUTH = "add-identity-aws-iam-auth",
UPDATE_IDENTITY_AWS_IAM_AUTH = "update-identity-aws-iam-auth",
GET_IDENTITY_AWS_IAM_AUTH = "get-identity-aws-iam-auth",
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
CREATE_ENVIRONMENT = "create-environment",
UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment",
@@ -460,17 +460,17 @@ interface GetIdentityGcpAuthEvent {
};
}
interface LoginIdentityAwsIamAuthEvent {
type: EventType.LOGIN_IDENTITY_AWS_IAM_AUTH;
interface LoginIdentityAwsAuthEvent {
type: EventType.LOGIN_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
identityAwsIamAuthId: string;
identityAwsAuthId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityAwsIamAuthEvent {
type: EventType.ADD_IDENTITY_AWS_IAM_AUTH;
interface AddIdentityAwsAuthEvent {
type: EventType.ADD_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
stsEndpoint: string;
@@ -483,8 +483,8 @@ interface AddIdentityAwsIamAuthEvent {
};
}
interface UpdateIdentityAwsIamAuthEvent {
type: EventType.UPDATE_IDENTITY_AWS_IAM_AUTH;
interface UpdateIdentityAwsAuthEvent {
type: EventType.UPDATE_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
stsEndpoint?: string;
@@ -497,8 +497,8 @@ interface UpdateIdentityAwsIamAuthEvent {
};
}
interface GetIdentityAwsIamAuthEvent {
type: EventType.GET_IDENTITY_AWS_IAM_AUTH;
interface GetIdentityAwsAuthEvent {
type: EventType.GET_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
};
@@ -762,10 +762,10 @@ export type Event =
| AddIdentityGcpAuthEvent
| UpdateIdentityGcpAuthEvent
| GetIdentityGcpAuthEvent
| LoginIdentityAwsIamAuthEvent
| AddIdentityAwsIamAuthEvent
| UpdateIdentityAwsIamAuthEvent
| GetIdentityAwsIamAuthEvent
| LoginIdentityAwsAuthEvent
| AddIdentityAwsAuthEvent
| UpdateIdentityAwsAuthEvent
| GetIdentityAwsAuthEvent
| CreateEnvironmentEvent
| UpdateEnvironmentEvent
| DeleteEnvironmentEvent
+6 -3
View File
@@ -92,7 +92,7 @@ export const UNIVERSAL_AUTH = {
}
} as const;
export const AWS_IAM_AUTH = {
export const AWS_AUTH = {
LOGIN: {
identityId: "The ID of the identity to login.",
iamHttpRequestMethod: "The HTTP request method used in the signed request.",
@@ -288,7 +288,8 @@ export const RAW_SECRETS = {
recursive:
"Whether or not to fetch all secrets from the specified base path, and all of its subdirectories. Note, the max depth is 20 deep.",
workspaceId: "The ID of the project to list secrets from.",
workspaceSlug: "The slug of the project to list secrets from. This parameter is only usable by machine identities.",
workspaceSlug:
"The slug of the project to list secrets from. This parameter is only applicable by machine identities.",
environment: "The slug of the environment to list secrets from.",
secretPath: "The secret path to list secrets from.",
includeImports: "Weather to include imported secrets or not."
@@ -307,6 +308,7 @@ export const RAW_SECRETS = {
GET: {
secretName: "The name of the secret to get.",
workspaceId: "The ID of the project to get the secret from.",
workspaceSlug: "The slug of the project to get the secret from.",
environment: "The slug of the environment to get the secret from.",
secretPath: "The path of the secret to get.",
version: "The version of the secret to get.",
@@ -625,7 +627,8 @@ export const INTEGRATION = {
shouldAutoRedeploy: "Used by Render to trigger auto deploy.",
secretGCPLabel: "The label for GCP secrets.",
secretAWSTag: "The tags for AWS secrets.",
kmsKeyId: "The ID of the encryption key from AWS KMS."
kmsKeyId: "The ID of the encryption key from AWS KMS.",
shouldDisableDelete: "The flag to disable deletion of secrets in AWS Parameter Store."
}
},
UPDATE: {
+6 -6
View File
@@ -78,8 +78,8 @@ import { identityOrgDALFactory } from "@app/services/identity/identity-org-dal";
import { identityServiceFactory } from "@app/services/identity/identity-service";
import { identityAccessTokenDALFactory } from "@app/services/identity-access-token/identity-access-token-dal";
import { identityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
import { identityAwsIamAuthDALFactory } from "@app/services/identity-aws-iam-auth/identity-aws-iam-auth-dal";
import { identityAwsIamAuthServiceFactory } from "@app/services/identity-aws-iam-auth/identity-aws-iam-auth-service";
import { identityAwsAuthDALFactory } from "@app/services/identity-aws-auth/identity-aws-auth-dal";
import { identityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
import { identityGcpAuthDALFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-dal";
import { identityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
@@ -205,7 +205,7 @@ export const registerRoutes = async (
const identityUaDAL = identityUaDALFactory(db);
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
const identityAwsIamAuthDAL = identityAwsIamAuthDALFactory(db);
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
@@ -716,9 +716,9 @@ export const registerRoutes = async (
licenseService
});
const identityAWSIAMAuthService = identityAwsIamAuthServiceFactory({
const identityAwsAuthService = identityAwsAuthServiceFactory({
identityAccessTokenDAL,
identityAwsIamAuthDAL,
identityAwsAuthDAL,
identityOrgMembershipDAL,
identityDAL,
licenseService,
@@ -794,7 +794,7 @@ export const registerRoutes = async (
identityProject: identityProjectService,
identityUa: identityUaService,
identityGcpAuth: identityGcpAuthService,
identityAwsIamAuth: identityAWSIAMAuthService,
identityAwsAuth: identityAwsAuthService,
secretApprovalPolicy: sapService,
accessApprovalPolicy: accessApprovalPolicyService,
accessApprovalRequest: accessApprovalRequestService,
@@ -1,8 +1,8 @@
import { z } from "zod";
import { IdentityAwsIamAuthsSchema } from "@app/db/schemas";
import { IdentityAwsAuthsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { AWS_IAM_AUTH } from "@app/lib/api-docs";
import { AWS_AUTH } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
@@ -10,22 +10,22 @@ import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
import {
validateAccountIds,
validatePrincipalArns
} from "@app/services/identity-aws-iam-auth/identity-aws-iam-auth-validators";
} from "@app/services/identity-aws-auth/identity-aws-auth-validators";
export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvider) => {
export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/aws-iam-auth/login",
url: "/aws-auth/login",
config: {
rateLimit: writeLimit
},
schema: {
description: "Login with AWS IAM Auth",
description: "Login with AWS Auth",
body: z.object({
identityId: z.string().describe(AWS_IAM_AUTH.LOGIN.identityId),
iamHttpRequestMethod: z.string().default("POST").describe(AWS_IAM_AUTH.LOGIN.iamHttpRequestMethod),
iamRequestBody: z.string().describe(AWS_IAM_AUTH.LOGIN.iamRequestBody),
iamRequestHeaders: z.string().describe(AWS_IAM_AUTH.LOGIN.iamRequestHeaders)
identityId: z.string().describe(AWS_AUTH.LOGIN.identityId),
iamHttpRequestMethod: z.string().default("POST").describe(AWS_AUTH.LOGIN.iamHttpRequestMethod),
iamRequestBody: z.string().describe(AWS_AUTH.LOGIN.iamRequestBody),
iamRequestHeaders: z.string().describe(AWS_AUTH.LOGIN.iamRequestHeaders)
}),
response: {
200: z.object({
@@ -37,18 +37,18 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
}
},
handler: async (req) => {
const { identityAwsIamAuth, accessToken, identityAccessToken, identityMembershipOrg } =
await server.services.identityAwsIamAuth.login(req.body);
const { identityAwsAuth, accessToken, identityAccessToken, identityMembershipOrg } =
await server.services.identityAwsAuth.login(req.body);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityMembershipOrg?.orgId,
event: {
type: EventType.LOGIN_IDENTITY_AWS_IAM_AUTH,
type: EventType.LOGIN_IDENTITY_AWS_AUTH,
metadata: {
identityId: identityAwsIamAuth.identityId,
identityId: identityAwsAuth.identityId,
identityAccessTokenId: identityAccessToken.id,
identityAwsIamAuthId: identityAwsIamAuth.id
identityAwsAuthId: identityAwsAuth.id
}
}
});
@@ -56,21 +56,21 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
return {
accessToken,
tokenType: "Bearer" as const,
expiresIn: identityAwsIamAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsIamAuth.accessTokenMaxTTL
expiresIn: identityAwsAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL
};
}
});
server.route({
method: "POST",
url: "/aws-iam-auth/identities/:identityId",
url: "/aws-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Attach AWS IAM Auth configuration onto identity",
description: "Attach AWS Auth configuration onto identity",
security: [
{
bearerAuth: []
@@ -109,12 +109,12 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
}),
response: {
200: z.object({
identityAwsIamAuth: IdentityAwsIamAuthsSchema
identityAwsAuth: IdentityAwsAuthsSchema
})
}
},
handler: async (req) => {
const identityAwsIamAuth = await server.services.identityAwsIamAuth.attachAwsIamAuth({
const identityAwsAuth = await server.services.identityAwsAuth.attachAwsAuth({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
@@ -125,35 +125,35 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAwsIamAuth.orgId,
orgId: identityAwsAuth.orgId,
event: {
type: EventType.ADD_IDENTITY_AWS_IAM_AUTH,
type: EventType.ADD_IDENTITY_AWS_AUTH,
metadata: {
identityId: identityAwsIamAuth.identityId,
stsEndpoint: identityAwsIamAuth.stsEndpoint,
allowedPrincipalArns: identityAwsIamAuth.allowedPrincipalArns,
allowedAccountIds: identityAwsIamAuth.allowedAccountIds,
accessTokenTTL: identityAwsIamAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsIamAuth.accessTokenMaxTTL,
accessTokenTrustedIps: identityAwsIamAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
accessTokenNumUsesLimit: identityAwsIamAuth.accessTokenNumUsesLimit
identityId: identityAwsAuth.identityId,
stsEndpoint: identityAwsAuth.stsEndpoint,
allowedPrincipalArns: identityAwsAuth.allowedPrincipalArns,
allowedAccountIds: identityAwsAuth.allowedAccountIds,
accessTokenTTL: identityAwsAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL,
accessTokenTrustedIps: identityAwsAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit
}
}
});
return { identityAwsIamAuth };
return { identityAwsAuth };
}
});
server.route({
method: "PATCH",
url: "/aws-iam-auth/identities/:identityId",
url: "/aws-auth/identities/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Update AWS IAM Auth configuration on identity",
description: "Update AWS Auth configuration on identity",
security: [
{
bearerAuth: []
@@ -185,12 +185,12 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
}),
response: {
200: z.object({
identityAwsIamAuth: IdentityAwsIamAuthsSchema
identityAwsAuth: IdentityAwsAuthsSchema
})
}
},
handler: async (req) => {
const identityAwsIamAuth = await server.services.identityAwsIamAuth.updateAwsIamAuth({
const identityAwsAuth = await server.services.identityAwsAuth.updateAwsAuth({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
@@ -201,35 +201,35 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAwsIamAuth.orgId,
orgId: identityAwsAuth.orgId,
event: {
type: EventType.UPDATE_IDENTITY_AWS_IAM_AUTH,
type: EventType.UPDATE_IDENTITY_AWS_AUTH,
metadata: {
identityId: identityAwsIamAuth.identityId,
stsEndpoint: identityAwsIamAuth.stsEndpoint,
allowedPrincipalArns: identityAwsIamAuth.allowedPrincipalArns,
allowedAccountIds: identityAwsIamAuth.allowedAccountIds,
accessTokenTTL: identityAwsIamAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsIamAuth.accessTokenMaxTTL,
accessTokenTrustedIps: identityAwsIamAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
accessTokenNumUsesLimit: identityAwsIamAuth.accessTokenNumUsesLimit
identityId: identityAwsAuth.identityId,
stsEndpoint: identityAwsAuth.stsEndpoint,
allowedPrincipalArns: identityAwsAuth.allowedPrincipalArns,
allowedAccountIds: identityAwsAuth.allowedAccountIds,
accessTokenTTL: identityAwsAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL,
accessTokenTrustedIps: identityAwsAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit
}
}
});
return { identityAwsIamAuth };
return { identityAwsAuth };
}
});
server.route({
method: "GET",
url: "/aws-iam-auth/identities/:identityId",
url: "/aws-auth/identities/:identityId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Retrieve AWS IAM Auth configuration on identity",
description: "Retrieve AWS Auth configuration on identity",
security: [
{
bearerAuth: []
@@ -240,12 +240,12 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
}),
response: {
200: z.object({
identityAwsIamAuth: IdentityAwsIamAuthsSchema
identityAwsAuth: IdentityAwsAuthsSchema
})
}
},
handler: async (req) => {
const identityAwsIamAuth = await server.services.identityAwsIamAuth.getAwsIamAuth({
const identityAwsAuth = await server.services.identityAwsAuth.getAwsAuth({
identityId: req.params.identityId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -255,15 +255,15 @@ export const registerIdentityAwsIamAuthRouter = async (server: FastifyZodProvide
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityAwsIamAuth.orgId,
orgId: identityAwsAuth.orgId,
event: {
type: EventType.GET_IDENTITY_AWS_IAM_AUTH,
type: EventType.GET_IDENTITY_AWS_AUTH,
metadata: {
identityId: identityAwsIamAuth.identityId
identityId: identityAwsAuth.identityId
}
}
});
return { identityAwsIamAuth };
return { identityAwsAuth };
}
});
};
+3 -2
View File
@@ -2,7 +2,7 @@ import { registerAdminRouter } from "./admin-router";
import { registerAuthRoutes } from "./auth-router";
import { registerProjectBotRouter } from "./bot-router";
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
import { registerIdentityAwsIamAuthRouter } from "./identity-aws-iam-auth-router";
import { registerIdentityAwsAuthRouter } from "./identity-aws-iam-auth-router";
import { registerIdentityGcpAuthRouter } from "./identity-gcp-auth-router";
import { registerIdentityRouter } from "./identity-router";
import { registerIdentityUaRouter } from "./identity-ua";
@@ -30,8 +30,9 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await authRouter.register(registerAuthRoutes);
await authRouter.register(registerIdentityUaRouter);
await authRouter.register(registerIdentityGcpAuthRouter);
await authRouter.register(registerIdentityAwsIamAuthRouter);
await authRouter.register(registerIdentityAccessTokenRouter);
await authRouter.register(registerIdentityAccessTokenRouter);
await authRouter.register(registerIdentityAwsAuthRouter);
},
{ prefix: "/auth" }
);
@@ -66,7 +66,8 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
)
.optional()
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId)
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete)
})
.default({})
}),
@@ -293,6 +293,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}),
querystring: z.object({
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceId),
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceSlug),
environment: z.string().trim().optional().describe(RAW_SECRETS.GET.environment),
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath),
version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version),
@@ -311,6 +312,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { workspaceSlug } = req.query;
let { secretPath, environment, workspaceId } = req.query;
if (req.auth.actor === ActorType.SERVICE) {
const scope = ServiceTokenScopes.parse(req.auth.serviceToken.scopes);
@@ -322,7 +324,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}
}
if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" });
if (!environment) throw new BadRequestError({ message: "Missing environment" });
if (!workspaceId && !workspaceSlug)
throw new BadRequestError({ message: "You must provide workspaceSlug or workspaceId" });
const secret = await server.services.secret.getSecretByNameRaw({
actorId: req.permission.id,
@@ -331,6 +335,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId,
environment,
projectId: workspaceId,
projectSlug: workspaceSlug,
path: secretPath,
secretName: req.params.secretName,
type: req.query.type,
@@ -339,7 +344,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
});
await server.services.auditLog.createAuditLog({
projectId: req.query.workspaceId,
projectId: secret.workspace,
...req.auditLogInfo,
event: {
type: EventType.GET_SECRET,
@@ -358,7 +363,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
distinctId: getTelemetryDistinctId(req),
properties: {
numberOfSecrets: 1,
workspaceId,
workspaceId: secret.workspace,
environment,
secretPath: req.query.secretPath,
channel: getUserAgentType(req.headers["user-agent"]),
@@ -0,0 +1,11 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TIdentityAwsAuthDALFactory = ReturnType<typeof identityAwsAuthDALFactory>;
export const identityAwsAuthDALFactory = (db: TDbClient) => {
const awsAuthOrm = ormify(db, TableName.IdentityAwsAuth);
return awsAuthOrm;
};
@@ -16,48 +16,43 @@ import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TIdentityAwsIamAuthDALFactory } from "./identity-aws-iam-auth-dal";
import { extractPrincipalArn } from "./identity-aws-iam-auth-fns";
import { TIdentityAwsAuthDALFactory } from "./identity-aws-auth-dal";
import { extractPrincipalArn } from "./identity-aws-auth-fns";
import {
TAttachAWSIAMAuthDTO,
TAWSGetCallerIdentityHeaders,
TGetAWSIAMAuthDTO,
TAttachAwsAuthDTO,
TAwsGetCallerIdentityHeaders,
TGetAwsAuthDTO,
TGetCallerIdentityResponse,
TLoginAWSIAMAuthDTO,
TUpdateAWSIAMAuthDTO
} from "./identity-aws-iam-auth-types";
TLoginAwsAuthDTO,
TUpdateAwsAuthDTO
} from "./identity-aws-auth-types";
type TIdentityAwsIamAuthServiceFactoryDep = {
type TIdentityAwsAuthServiceFactoryDep = {
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "create">;
identityAwsIamAuthDAL: Pick<TIdentityAwsIamAuthDALFactory, "findOne" | "transaction" | "create" | "updateById">;
identityAwsAuthDAL: Pick<TIdentityAwsAuthDALFactory, "findOne" | "transaction" | "create" | "updateById">;
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
identityDAL: Pick<TIdentityDALFactory, "updateById">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
};
export type TIdentityAwsIamAuthServiceFactory = ReturnType<typeof identityAwsIamAuthServiceFactory>;
export type TIdentityAwsAuthServiceFactory = ReturnType<typeof identityAwsAuthServiceFactory>;
export const identityAwsIamAuthServiceFactory = ({
export const identityAwsAuthServiceFactory = ({
identityAccessTokenDAL,
identityAwsIamAuthDAL,
identityAwsAuthDAL,
identityOrgMembershipDAL,
identityDAL,
licenseService,
permissionService
}: TIdentityAwsIamAuthServiceFactoryDep) => {
const login = async ({
identityId,
iamHttpRequestMethod,
iamRequestBody,
iamRequestHeaders
}: TLoginAWSIAMAuthDTO) => {
const identityAwsIamAuth = await identityAwsIamAuthDAL.findOne({ identityId });
if (!identityAwsIamAuth) throw new UnauthorizedError();
}: TIdentityAwsAuthServiceFactoryDep) => {
const login = async ({ identityId, iamHttpRequestMethod, iamRequestBody, iamRequestHeaders }: TLoginAwsAuthDTO) => {
const identityAwsAuth = await identityAwsAuthDAL.findOne({ identityId });
if (!identityAwsAuth) throw new UnauthorizedError();
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityAwsIamAuth.identityId });
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityAwsAuth.identityId });
const headers: TAWSGetCallerIdentityHeaders = JSON.parse(Buffer.from(iamRequestHeaders, "base64").toString());
const headers: TAwsGetCallerIdentityHeaders = JSON.parse(Buffer.from(iamRequestHeaders, "base64").toString());
const body: string = Buffer.from(iamRequestBody, "base64").toString();
const {
@@ -68,15 +63,15 @@ export const identityAwsIamAuthServiceFactory = ({
}
}: { data: TGetCallerIdentityResponse } = await axios({
method: iamHttpRequestMethod,
url: identityAwsIamAuth.stsEndpoint,
url: identityAwsAuth.stsEndpoint,
headers,
data: body
});
if (identityAwsIamAuth.allowedAccountIds) {
if (identityAwsAuth.allowedAccountIds) {
// validate if Account is in the list of allowed Account IDs
const isAccountAllowed = identityAwsIamAuth.allowedAccountIds
const isAccountAllowed = identityAwsAuth.allowedAccountIds
.split(",")
.map((accountId) => accountId.trim())
.some((accountId) => accountId === Account);
@@ -84,10 +79,10 @@ export const identityAwsIamAuthServiceFactory = ({
if (!isAccountAllowed) throw new UnauthorizedError();
}
if (identityAwsIamAuth.allowedPrincipalArns) {
if (identityAwsAuth.allowedPrincipalArns) {
// validate if Arn is in the list of allowed Principal ARNs
const isArnAllowed = identityAwsIamAuth.allowedPrincipalArns
const isArnAllowed = identityAwsAuth.allowedPrincipalArns
.split(",")
.map((principalArn) => principalArn.trim())
.some((principalArn) => {
@@ -100,15 +95,15 @@ export const identityAwsIamAuthServiceFactory = ({
if (!isArnAllowed) throw new UnauthorizedError();
}
const identityAccessToken = await identityAwsIamAuthDAL.transaction(async (tx) => {
const identityAccessToken = await identityAwsAuthDAL.transaction(async (tx) => {
const newToken = await identityAccessTokenDAL.create(
{
identityId: identityAwsIamAuth.identityId,
identityId: identityAwsAuth.identityId,
isAccessTokenRevoked: false,
accessTokenTTL: identityAwsIamAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsIamAuth.accessTokenMaxTTL,
accessTokenTTL: identityAwsAuth.accessTokenTTL,
accessTokenMaxTTL: identityAwsAuth.accessTokenMaxTTL,
accessTokenNumUses: 0,
accessTokenNumUsesLimit: identityAwsIamAuth.accessTokenNumUsesLimit
accessTokenNumUsesLimit: identityAwsAuth.accessTokenNumUsesLimit
},
tx
);
@@ -118,7 +113,7 @@ export const identityAwsIamAuthServiceFactory = ({
const appCfg = getConfig();
const accessToken = jwt.sign(
{
identityId: identityAwsIamAuth.identityId,
identityId: identityAwsAuth.identityId,
identityAccessTokenId: identityAccessToken.id,
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
} as TIdentityAccessTokenJwtPayload,
@@ -131,10 +126,10 @@ export const identityAwsIamAuthServiceFactory = ({
}
);
return { accessToken, identityAwsIamAuth, identityAccessToken, identityMembershipOrg };
return { accessToken, identityAwsAuth, identityAccessToken, identityMembershipOrg };
};
const attachAwsIamAuth = async ({
const attachAwsAuth = async ({
identityId,
stsEndpoint,
allowedPrincipalArns,
@@ -147,12 +142,12 @@ export const identityAwsIamAuthServiceFactory = ({
actorAuthMethod,
actor,
actorOrgId
}: TAttachAWSIAMAuthDTO) => {
}: TAttachAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity.authMethod)
throw new BadRequestError({
message: "Failed to add AWS IAM Auth to already configured identity"
message: "Failed to add AWS Auth to already configured identity"
});
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
@@ -186,10 +181,11 @@ export const identityAwsIamAuthServiceFactory = ({
return extractIPDetails(accessTokenTrustedIp.ipAddress);
});
const identityAwsIamAuth = await identityAwsIamAuthDAL.transaction(async (tx) => {
const doc = await identityAwsIamAuthDAL.create(
const identityAwsAuth = await identityAwsAuthDAL.transaction(async (tx) => {
const doc = await identityAwsAuthDAL.create(
{
identityId: identityMembershipOrg.identityId,
type: "iam",
stsEndpoint,
allowedPrincipalArns,
allowedAccountIds,
@@ -203,16 +199,16 @@ export const identityAwsIamAuthServiceFactory = ({
await identityDAL.updateById(
identityMembershipOrg.identityId,
{
authMethod: IdentityAuthMethod.AWS_IAM_AUTH
authMethod: IdentityAuthMethod.AWS_AUTH
},
tx
);
return doc;
});
return { ...identityAwsIamAuth, orgId: identityMembershipOrg.orgId };
return { ...identityAwsAuth, orgId: identityMembershipOrg.orgId };
};
const updateAwsIamAuth = async ({
const updateAwsAuth = async ({
identityId,
stsEndpoint,
allowedPrincipalArns,
@@ -225,20 +221,19 @@ export const identityAwsIamAuthServiceFactory = ({
actorAuthMethod,
actor,
actorOrgId
}: TUpdateAWSIAMAuthDTO) => {
}: TUpdateAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_IAM_AUTH)
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_AUTH)
throw new BadRequestError({
message: "Failed to update AWS IAM Auth"
message: "Failed to update AWS Auth"
});
const identityAwsIamAuth = await identityAwsIamAuthDAL.findOne({ identityId });
const identityAwsAuth = await identityAwsAuthDAL.findOne({ identityId });
if (
(accessTokenMaxTTL || identityAwsIamAuth.accessTokenMaxTTL) > 0 &&
(accessTokenTTL || identityAwsIamAuth.accessTokenMaxTTL) >
(accessTokenMaxTTL || identityAwsIamAuth.accessTokenMaxTTL)
(accessTokenMaxTTL || identityAwsAuth.accessTokenMaxTTL) > 0 &&
(accessTokenTTL || identityAwsAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || identityAwsAuth.accessTokenMaxTTL)
) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
}
@@ -270,7 +265,7 @@ export const identityAwsIamAuthServiceFactory = ({
return extractIPDetails(accessTokenTrustedIp.ipAddress);
});
const updatedAwsIamAuth = await identityAwsIamAuthDAL.updateById(identityAwsIamAuth.id, {
const updatedAwsAuth = await identityAwsAuthDAL.updateById(identityAwsAuth.id, {
stsEndpoint,
allowedPrincipalArns,
allowedAccountIds,
@@ -282,18 +277,18 @@ export const identityAwsIamAuthServiceFactory = ({
: undefined
});
return { ...updatedAwsIamAuth, orgId: identityMembershipOrg.orgId };
return { ...updatedAwsAuth, orgId: identityMembershipOrg.orgId };
};
const getAwsIamAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAWSIAMAuthDTO) => {
const getAwsAuth = async ({ identityId, actorId, actor, actorAuthMethod, actorOrgId }: TGetAwsAuthDTO) => {
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_IAM_AUTH)
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.AWS_AUTH)
throw new BadRequestError({
message: "The identity does not have AWS IAM Auth attached"
message: "The identity does not have AWS Auth attached"
});
const awsIamIdentityAuth = await identityAwsIamAuthDAL.findOne({ identityId });
const awsIdentityAuth = await identityAwsAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission(
actor,
@@ -303,13 +298,13 @@ export const identityAwsIamAuthServiceFactory = ({
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
return { ...awsIamIdentityAuth, orgId: identityMembershipOrg.orgId };
return { ...awsIdentityAuth, orgId: identityMembershipOrg.orgId };
};
return {
login,
attachAwsIamAuth,
updateAwsIamAuth,
getAwsIamAuth
attachAwsAuth,
updateAwsAuth,
getAwsAuth
};
};
@@ -1,13 +1,13 @@
import { TProjectPermission } from "@app/lib/types";
export type TLoginAWSIAMAuthDTO = {
export type TLoginAwsAuthDTO = {
identityId: string;
iamHttpRequestMethod: string;
iamRequestBody: string;
iamRequestHeaders: string;
};
export type TAttachAWSIAMAuthDTO = {
export type TAttachAwsAuthDTO = {
identityId: string;
stsEndpoint: string;
allowedPrincipalArns: string;
@@ -18,7 +18,7 @@ export type TAttachAWSIAMAuthDTO = {
accessTokenTrustedIps: { ipAddress: string }[];
} & Omit<TProjectPermission, "projectId">;
export type TUpdateAWSIAMAuthDTO = {
export type TUpdateAwsAuthDTO = {
identityId: string;
stsEndpoint?: string;
allowedPrincipalArns?: string;
@@ -29,11 +29,11 @@ export type TUpdateAWSIAMAuthDTO = {
accessTokenTrustedIps?: { ipAddress: string }[];
} & Omit<TProjectPermission, "projectId">;
export type TGetAWSIAMAuthDTO = {
export type TGetAwsAuthDTO = {
identityId: string;
} & Omit<TProjectPermission, "projectId">;
export type TAWSGetCallerIdentityHeaders = {
export type TAwsGetCallerIdentityHeaders = {
"Content-Type": string;
Host: string;
"X-Amz-Date": string;
@@ -1,11 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TIdentityAwsIamAuthDALFactory = ReturnType<typeof identityAwsIamAuthDALFactory>;
export const identityAwsIamAuthDALFactory = (db: TDbClient) => {
const awsIamAuthOrm = ormify(db, TableName.IdentityAwsIamAuth);
return awsIamAuthOrm;
};
@@ -52,7 +52,7 @@ export const identityUaServiceFactory = ({
}: TIdentityUaServiceFactoryDep) => {
const login = async (clientId: string, clientSecret: string, ip: string) => {
const identityUa = await identityUaDAL.findOne({ clientId });
if (!identityUa) throw new UnauthorizedError();
if (!identityUa) throw new UnauthorizedError({ message: "Invalid credentials" });
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityUa.identityId });
@@ -68,7 +68,7 @@ export const identityUaServiceFactory = ({
const validClientSecretInfo = clientSecrtInfo.find(({ clientSecretHash }) =>
bcrypt.compareSync(clientSecret, clientSecretHash)
);
if (!validClientSecretInfo) throw new UnauthorizedError();
if (!validClientSecretInfo) throw new UnauthorizedError({ message: "Invalid credentials" });
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
if (Number(clientSecretTTL) > 0) {
@@ -517,20 +517,22 @@ const syncSecretsAWSParameterStore = async ({
})
);
// Identify secrets to delete
await Promise.all(
Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
if (!(key in secrets)) {
// case:
// -> delete secret
await ssm
.deleteParameter({
Name: awsParameterStoreSecretsObj[key].Name as string
})
.promise();
}
})
);
if (!metadata.shouldDisableDelete) {
// Identify secrets to delete
await Promise.all(
Object.keys(awsParameterStoreSecretsObj).map(async (key) => {
if (!(key in secrets)) {
// case:
// -> delete secret
await ssm
.deleteParameter({
Name: awsParameterStoreSecretsObj[key].Name as string
})
.promise();
}
})
);
}
};
/**
@@ -27,6 +27,7 @@ export type TCreateIntegrationDTO = {
value: string;
}[];
kmsKeyId?: string;
shouldDisableDelete?: boolean;
};
} & Omit<TProjectPermission, "projectId">;
+4
View File
@@ -546,6 +546,10 @@ export const orgServiceFactory = ({
code
});
await userDAL.updateById(user.id, {
isEmailVerified: true
});
if (user.isAccepted) {
// this means user has already completed signup process
// isAccepted is set true when keys are exchanged
@@ -972,7 +972,8 @@ export const secretServiceFactory = ({
path,
actor,
environment,
projectId,
projectId: workspaceId,
projectSlug,
actorId,
actorOrgId,
actorAuthMethod,
@@ -980,6 +981,8 @@ export const secretServiceFactory = ({
includeImports,
version
}: TGetASecretRawDTO) => {
const projectId = workspaceId || (await projectDAL.findProjectBySlug(projectSlug as string, actorOrgId)).id;
const botKey = await projectBotService.getBotKey(projectId);
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
+3 -1
View File
@@ -152,7 +152,9 @@ export type TGetASecretRawDTO = {
type: "shared" | "personal";
includeImports?: boolean;
version?: number;
} & TProjectPermission;
projectSlug?: string;
projectId?: string;
} & Omit<TProjectPermission, "projectId">;
export type TCreateSecretRawDTO = TProjectPermission & {
secretPath: string;