From 5742fc648b81abc29d85057139526b1c5f1a687c Mon Sep 17 00:00:00 2001 From: x032205 Date: Fri, 9 May 2025 22:33:02 -0400 Subject: [PATCH] add tenancy OCID requirement --- .../20250508210717_identity-oci-auth.ts | 1 + backend/src/db/schemas/identity-oci-auths.ts | 1 + .../ee/services/audit-log/audit-log-types.ts | 2 ++ backend/src/lib/api-docs/constants.ts | 2 ++ .../routes/v1/identity-oci-auth-router.ts | 6 +++++- .../identity-oci-auth-service.ts | 10 ++++++++++ .../identity-oci-auth-types.ts | 2 ++ .../identity-oci-auth-validators.ts | 9 +++++++++ frontend/src/hooks/api/identities/mutations.tsx | 4 ++++ frontend/src/hooks/api/identities/types.ts | 3 +++ .../IdentitySection/IdentityOciAuthForm.tsx | 17 +++++++++++++++++ .../ViewIdentityOciAuthContent.tsx | 3 +++ 12 files changed, 59 insertions(+), 1 deletion(-) diff --git a/backend/src/db/migrations/20250508210717_identity-oci-auth.ts b/backend/src/db/migrations/20250508210717_identity-oci-auth.ts index 73ffe1d8e..958f61ae2 100644 --- a/backend/src/db/migrations/20250508210717_identity-oci-auth.ts +++ b/backend/src/db/migrations/20250508210717_identity-oci-auth.ts @@ -16,6 +16,7 @@ export async function up(knex: Knex): Promise { t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE"); t.string("type").notNullable(); + t.string("tenancyOcid").notNullable(); t.string("allowedUsernames").notNullable(); }); } diff --git a/backend/src/db/schemas/identity-oci-auths.ts b/backend/src/db/schemas/identity-oci-auths.ts index 604dc1925..045f42795 100644 --- a/backend/src/db/schemas/identity-oci-auths.ts +++ b/backend/src/db/schemas/identity-oci-auths.ts @@ -17,6 +17,7 @@ export const IdentityOciAuthsSchema = z.object({ updatedAt: z.date(), identityId: z.string().uuid(), type: z.string(), + tenancyOcid: z.string(), allowedUsernames: z.string() }); diff --git a/backend/src/ee/services/audit-log/audit-log-types.ts b/backend/src/ee/services/audit-log/audit-log-types.ts index 5396b359b..cb1143e60 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -1021,6 +1021,7 @@ interface AddIdentityOciAuthEvent { type: EventType.ADD_IDENTITY_OCI_AUTH; metadata: { identityId: string; + tenancyOcid: string; allowedUsernames: string; accessTokenTTL: number; accessTokenMaxTTL: number; @@ -1040,6 +1041,7 @@ interface UpdateIdentityOciAuthEvent { type: EventType.UPDATE_IDENTITY_OCI_AUTH; metadata: { identityId: string; + tenancyOcid?: string; allowedUsernames?: string; accessTokenTTL?: number; accessTokenMaxTTL?: number; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 7ff4449af..77bde39d6 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -279,6 +279,7 @@ export const OCI_AUTH = { }, ATTACH: { identityId: "The ID of the identity to attach the configuration onto.", + tenancyOcid: "The OCID of your tenancy.", allowedUsernames: "The comma-separated list of trusted OCI account usernames that are allowed to authenticate with Infisical.", accessTokenTTL: "The lifetime for an access token in seconds.", @@ -288,6 +289,7 @@ export const OCI_AUTH = { }, UPDATE: { identityId: "The ID of the identity to update the auth method for.", + tenancyOcid: "The OCID of your tenancy.", allowedUsernames: "The comma-separated list of trusted OCI account usernames that are allowed to authenticate with Infisical.", accessTokenTTL: "The new lifetime for an access token in seconds.", diff --git a/backend/src/server/routes/v1/identity-oci-auth-router.ts b/backend/src/server/routes/v1/identity-oci-auth-router.ts index c6f009d95..a5f330143 100644 --- a/backend/src/server/routes/v1/identity-oci-auth-router.ts +++ b/backend/src/server/routes/v1/identity-oci-auth-router.ts @@ -7,7 +7,7 @@ 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"; import { TIdentityTrustedIp } from "@app/services/identity/identity-types"; -import { validateUsernames } from "@app/services/identity-oci-auth/identity-oci-auth-validators"; +import { validateTenancy, validateUsernames } from "@app/services/identity-oci-auth/identity-oci-auth-validators"; import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns"; export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) => { @@ -88,6 +88,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) }), body: z .object({ + tenancyOcid: validateTenancy.describe(OCI_AUTH.ATTACH.tenancyOcid), allowedUsernames: validateUsernames.describe(OCI_AUTH.ATTACH.allowedUsernames), accessTokenTrustedIps: z .object({ @@ -141,6 +142,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) type: EventType.ADD_IDENTITY_OCI_AUTH, metadata: { identityId: identityOciAuth.identityId, + tenancyOcid: identityOciAuth.tenancyOcid, allowedUsernames: identityOciAuth.allowedUsernames, accessTokenTTL: identityOciAuth.accessTokenTTL, accessTokenMaxTTL: identityOciAuth.accessTokenMaxTTL, @@ -175,6 +177,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) }), body: z .object({ + tenancyOcid: validateTenancy.describe(OCI_AUTH.UPDATE.tenancyOcid), allowedUsernames: validateUsernames.describe(OCI_AUTH.UPDATE.allowedUsernames), accessTokenTrustedIps: z .object({ @@ -221,6 +224,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) type: EventType.UPDATE_IDENTITY_OCI_AUTH, metadata: { identityId: identityOciAuth.identityId, + tenancyOcid: identityOciAuth.tenancyOcid, allowedUsernames: identityOciAuth.allowedUsernames, accessTokenTTL: identityOciAuth.accessTokenTTL, accessTokenMaxTTL: identityOciAuth.accessTokenMaxTTL, diff --git a/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts b/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts index 685ce6950..55561f3db 100644 --- a/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts +++ b/backend/src/services/identity-oci-auth/identity-oci-auth-service.ts @@ -70,6 +70,12 @@ export const identityOciAuthServiceFactory = ({ headers }); + if (data.compartmentId !== identityOciAuth.tenancyOcid) { + throw new UnauthorizedError({ + message: "Access denied: OCI account isn't part of tenancy." + }); + } + if (identityOciAuth.allowedUsernames) { const isAccountAllowed = identityOciAuth.allowedUsernames.split(",").some((name) => name.trim() === data.name); @@ -121,6 +127,7 @@ export const identityOciAuthServiceFactory = ({ const attachOciAuth = async ({ identityId, + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, @@ -179,6 +186,7 @@ export const identityOciAuthServiceFactory = ({ { identityId: identityMembershipOrg.identityId, type: "iam", + tenancyOcid, allowedUsernames, accessTokenMaxTTL, accessTokenTTL, @@ -194,6 +202,7 @@ export const identityOciAuthServiceFactory = ({ const updateOciAuth = async ({ identityId, + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, @@ -250,6 +259,7 @@ export const identityOciAuthServiceFactory = ({ }); const updatedOciAuth = await identityOciAuthDAL.updateById(identityOciAuth.id, { + tenancyOcid, allowedUsernames, accessTokenMaxTTL, accessTokenTTL, diff --git a/backend/src/services/identity-oci-auth/identity-oci-auth-types.ts b/backend/src/services/identity-oci-auth/identity-oci-auth-types.ts index 1a46400d1..25b59e4bf 100644 --- a/backend/src/services/identity-oci-auth/identity-oci-auth-types.ts +++ b/backend/src/services/identity-oci-auth/identity-oci-auth-types.ts @@ -12,6 +12,7 @@ export type TLoginOciAuthDTO = { export type TAttachOciAuthDTO = { identityId: string; + tenancyOcid: string; allowedUsernames: string; accessTokenTTL: number; accessTokenMaxTTL: number; @@ -22,6 +23,7 @@ export type TAttachOciAuthDTO = { export type TUpdateOciAuthDTO = { identityId: string; + tenancyOcid?: string; allowedUsernames?: string; accessTokenTTL?: number; accessTokenMaxTTL?: number; diff --git a/backend/src/services/identity-oci-auth/identity-oci-auth-validators.ts b/backend/src/services/identity-oci-auth/identity-oci-auth-validators.ts index 753bb5ea0..d881014b3 100644 --- a/backend/src/services/identity-oci-auth/identity-oci-auth-validators.ts +++ b/backend/src/services/identity-oci-auth/identity-oci-auth-validators.ts @@ -19,3 +19,12 @@ export const validateUsernames = z message: "One or more usernames are invalid" }) .transform((arr) => arr.join(", ")); + +export const validateTenancy = z + .string() + .trim() + .min(1, "Tenancy OCID cannot be empty.") + .refine( + (val) => new RE2("^ocid1\\.tenancy\\.oc1\\..+$").test(val), + "Invalid Tenancy OCID format. Must start with ocid1.tenancy.oc1." + ); diff --git a/frontend/src/hooks/api/identities/mutations.tsx b/frontend/src/hooks/api/identities/mutations.tsx index d3eab60f9..a76b7ecac 100644 --- a/frontend/src/hooks/api/identities/mutations.tsx +++ b/frontend/src/hooks/api/identities/mutations.tsx @@ -461,6 +461,7 @@ export const useAddIdentityOciAuth = () => { return useMutation({ mutationFn: async ({ identityId, + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, @@ -472,6 +473,7 @@ export const useAddIdentityOciAuth = () => { } = await apiRequest.post<{ identityOciAuth: IdentityOciAuth }>( `/api/v1/auth/oci-auth/identities/${identityId}`, { + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, @@ -497,6 +499,7 @@ export const useUpdateIdentityOciAuth = () => { return useMutation({ mutationFn: async ({ identityId, + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, @@ -508,6 +511,7 @@ export const useUpdateIdentityOciAuth = () => { } = await apiRequest.patch<{ identityOciAuth: IdentityOciAuth }>( `/api/v1/auth/oci-auth/identities/${identityId}`, { + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, diff --git a/frontend/src/hooks/api/identities/types.ts b/frontend/src/hooks/api/identities/types.ts index acc56cfb0..59c288309 100644 --- a/frontend/src/hooks/api/identities/types.ts +++ b/frontend/src/hooks/api/identities/types.ts @@ -293,6 +293,7 @@ export type DeleteIdentityAwsAuthDTO = { export type IdentityOciAuth = { identityId: string; type: "iam"; + tenancyOcid: string; allowedUsernames: string; accessTokenTTL: number; accessTokenMaxTTL: number; @@ -303,6 +304,7 @@ export type IdentityOciAuth = { export type AddIdentityOciAuthDTO = { organizationId: string; identityId: string; + tenancyOcid: string; allowedUsernames: string; accessTokenTTL: number; accessTokenMaxTTL: number; @@ -315,6 +317,7 @@ export type AddIdentityOciAuthDTO = { export type UpdateIdentityOciAuthDTO = { organizationId: string; identityId: string; + tenancyOcid?: string; allowedUsernames?: string; accessTokenTTL?: number; accessTokenMaxTTL?: number; diff --git a/frontend/src/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOciAuthForm.tsx b/frontend/src/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOciAuthForm.tsx index d6a8bd0ba..fd77d1bea 100644 --- a/frontend/src/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOciAuthForm.tsx +++ b/frontend/src/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOciAuthForm.tsx @@ -29,6 +29,7 @@ import { IdentityFormTab } from "./types"; const schema = z .object({ + tenancyOcid: z.string().trim().min(1, "Tenancy OCID is required."), allowedUsernames: z.string(), accessTokenTTL: z .string() @@ -90,6 +91,7 @@ export const IdentityOciAuthForm = ({ } = useForm({ resolver: zodResolver(schema), defaultValues: { + tenancyOcid: "", allowedUsernames: "", accessTokenTTL: "2592000", accessTokenMaxTTL: "2592000", @@ -107,6 +109,7 @@ export const IdentityOciAuthForm = ({ useEffect(() => { if (data) { reset({ + tenancyOcid: data.tenancyOcid, allowedUsernames: data.allowedUsernames, accessTokenTTL: String(data.accessTokenTTL), accessTokenMaxTTL: String(data.accessTokenMaxTTL), @@ -121,6 +124,7 @@ export const IdentityOciAuthForm = ({ }); } else { reset({ + tenancyOcid: "", allowedUsernames: "", accessTokenTTL: "2592000", accessTokenMaxTTL: "2592000", @@ -131,6 +135,7 @@ export const IdentityOciAuthForm = ({ }, [data]); const onFormSubmit = async ({ + tenancyOcid, allowedUsernames, accessTokenTTL, accessTokenMaxTTL, @@ -143,6 +148,7 @@ export const IdentityOciAuthForm = ({ if (data) { await updateMutateAsync({ organizationId: orgId, + tenancyOcid, allowedUsernames, identityId, accessTokenTTL: Number(accessTokenTTL), @@ -154,6 +160,7 @@ export const IdentityOciAuthForm = ({ await addMutateAsync({ organizationId: orgId, identityId, + tenancyOcid, allowedUsernames: allowedUsernames || "", accessTokenTTL: Number(accessTokenTTL), accessTokenMaxTTL: Number(accessTokenMaxTTL), @@ -194,12 +201,22 @@ export const IdentityOciAuthForm = ({ Advanced + ( + + + + )} + /> ( diff --git a/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityOciAuthContent.tsx b/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityOciAuthContent.tsx index 4a243d6c5..fb3bd4fa8 100644 --- a/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityOciAuthContent.tsx +++ b/frontend/src/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityOciAuthContent.tsx @@ -59,6 +59,9 @@ export const ViewIdentityOciAuthContent = ({ {data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")} + + {data.tenancyOcid} + {data.allowedUsernames ?.split(",")