add tenancy OCID requirement

This commit is contained in:
x032205
2025-05-09 22:33:02 -04:00
parent 578a0d7d93
commit 5742fc648b
12 changed files with 59 additions and 1 deletions
@@ -16,6 +16,7 @@ export async function up(knex: Knex): Promise<void> {
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE"); t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
t.string("type").notNullable(); t.string("type").notNullable();
t.string("tenancyOcid").notNullable();
t.string("allowedUsernames").notNullable(); t.string("allowedUsernames").notNullable();
}); });
} }
@@ -17,6 +17,7 @@ export const IdentityOciAuthsSchema = z.object({
updatedAt: z.date(), updatedAt: z.date(),
identityId: z.string().uuid(), identityId: z.string().uuid(),
type: z.string(), type: z.string(),
tenancyOcid: z.string(),
allowedUsernames: z.string() allowedUsernames: z.string()
}); });
@@ -1021,6 +1021,7 @@ interface AddIdentityOciAuthEvent {
type: EventType.ADD_IDENTITY_OCI_AUTH; type: EventType.ADD_IDENTITY_OCI_AUTH;
metadata: { metadata: {
identityId: string; identityId: string;
tenancyOcid: string;
allowedUsernames: string; allowedUsernames: string;
accessTokenTTL: number; accessTokenTTL: number;
accessTokenMaxTTL: number; accessTokenMaxTTL: number;
@@ -1040,6 +1041,7 @@ interface UpdateIdentityOciAuthEvent {
type: EventType.UPDATE_IDENTITY_OCI_AUTH; type: EventType.UPDATE_IDENTITY_OCI_AUTH;
metadata: { metadata: {
identityId: string; identityId: string;
tenancyOcid?: string;
allowedUsernames?: string; allowedUsernames?: string;
accessTokenTTL?: number; accessTokenTTL?: number;
accessTokenMaxTTL?: number; accessTokenMaxTTL?: number;
+2
View File
@@ -279,6 +279,7 @@ export const OCI_AUTH = {
}, },
ATTACH: { ATTACH: {
identityId: "The ID of the identity to attach the configuration onto.", identityId: "The ID of the identity to attach the configuration onto.",
tenancyOcid: "The OCID of your tenancy.",
allowedUsernames: allowedUsernames:
"The comma-separated list of trusted OCI account usernames that are allowed to authenticate with Infisical.", "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.", accessTokenTTL: "The lifetime for an access token in seconds.",
@@ -288,6 +289,7 @@ export const OCI_AUTH = {
}, },
UPDATE: { UPDATE: {
identityId: "The ID of the identity to update the auth method for.", identityId: "The ID of the identity to update the auth method for.",
tenancyOcid: "The OCID of your tenancy.",
allowedUsernames: allowedUsernames:
"The comma-separated list of trusted OCI account usernames that are allowed to authenticate with Infisical.", "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.", accessTokenTTL: "The new lifetime for an access token in seconds.",
@@ -7,7 +7,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types"; 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"; import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) => { export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider) => {
@@ -88,6 +88,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider)
}), }),
body: z body: z
.object({ .object({
tenancyOcid: validateTenancy.describe(OCI_AUTH.ATTACH.tenancyOcid),
allowedUsernames: validateUsernames.describe(OCI_AUTH.ATTACH.allowedUsernames), allowedUsernames: validateUsernames.describe(OCI_AUTH.ATTACH.allowedUsernames),
accessTokenTrustedIps: z accessTokenTrustedIps: z
.object({ .object({
@@ -141,6 +142,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider)
type: EventType.ADD_IDENTITY_OCI_AUTH, type: EventType.ADD_IDENTITY_OCI_AUTH,
metadata: { metadata: {
identityId: identityOciAuth.identityId, identityId: identityOciAuth.identityId,
tenancyOcid: identityOciAuth.tenancyOcid,
allowedUsernames: identityOciAuth.allowedUsernames, allowedUsernames: identityOciAuth.allowedUsernames,
accessTokenTTL: identityOciAuth.accessTokenTTL, accessTokenTTL: identityOciAuth.accessTokenTTL,
accessTokenMaxTTL: identityOciAuth.accessTokenMaxTTL, accessTokenMaxTTL: identityOciAuth.accessTokenMaxTTL,
@@ -175,6 +177,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider)
}), }),
body: z body: z
.object({ .object({
tenancyOcid: validateTenancy.describe(OCI_AUTH.UPDATE.tenancyOcid),
allowedUsernames: validateUsernames.describe(OCI_AUTH.UPDATE.allowedUsernames), allowedUsernames: validateUsernames.describe(OCI_AUTH.UPDATE.allowedUsernames),
accessTokenTrustedIps: z accessTokenTrustedIps: z
.object({ .object({
@@ -221,6 +224,7 @@ export const registerIdentityOciAuthRouter = async (server: FastifyZodProvider)
type: EventType.UPDATE_IDENTITY_OCI_AUTH, type: EventType.UPDATE_IDENTITY_OCI_AUTH,
metadata: { metadata: {
identityId: identityOciAuth.identityId, identityId: identityOciAuth.identityId,
tenancyOcid: identityOciAuth.tenancyOcid,
allowedUsernames: identityOciAuth.allowedUsernames, allowedUsernames: identityOciAuth.allowedUsernames,
accessTokenTTL: identityOciAuth.accessTokenTTL, accessTokenTTL: identityOciAuth.accessTokenTTL,
accessTokenMaxTTL: identityOciAuth.accessTokenMaxTTL, accessTokenMaxTTL: identityOciAuth.accessTokenMaxTTL,
@@ -70,6 +70,12 @@ export const identityOciAuthServiceFactory = ({
headers headers
}); });
if (data.compartmentId !== identityOciAuth.tenancyOcid) {
throw new UnauthorizedError({
message: "Access denied: OCI account isn't part of tenancy."
});
}
if (identityOciAuth.allowedUsernames) { if (identityOciAuth.allowedUsernames) {
const isAccountAllowed = identityOciAuth.allowedUsernames.split(",").some((name) => name.trim() === data.name); const isAccountAllowed = identityOciAuth.allowedUsernames.split(",").some((name) => name.trim() === data.name);
@@ -121,6 +127,7 @@ export const identityOciAuthServiceFactory = ({
const attachOciAuth = async ({ const attachOciAuth = async ({
identityId, identityId,
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -179,6 +186,7 @@ export const identityOciAuthServiceFactory = ({
{ {
identityId: identityMembershipOrg.identityId, identityId: identityMembershipOrg.identityId,
type: "iam", type: "iam",
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenMaxTTL, accessTokenMaxTTL,
accessTokenTTL, accessTokenTTL,
@@ -194,6 +202,7 @@ export const identityOciAuthServiceFactory = ({
const updateOciAuth = async ({ const updateOciAuth = async ({
identityId, identityId,
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -250,6 +259,7 @@ export const identityOciAuthServiceFactory = ({
}); });
const updatedOciAuth = await identityOciAuthDAL.updateById(identityOciAuth.id, { const updatedOciAuth = await identityOciAuthDAL.updateById(identityOciAuth.id, {
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenMaxTTL, accessTokenMaxTTL,
accessTokenTTL, accessTokenTTL,
@@ -12,6 +12,7 @@ export type TLoginOciAuthDTO = {
export type TAttachOciAuthDTO = { export type TAttachOciAuthDTO = {
identityId: string; identityId: string;
tenancyOcid: string;
allowedUsernames: string; allowedUsernames: string;
accessTokenTTL: number; accessTokenTTL: number;
accessTokenMaxTTL: number; accessTokenMaxTTL: number;
@@ -22,6 +23,7 @@ export type TAttachOciAuthDTO = {
export type TUpdateOciAuthDTO = { export type TUpdateOciAuthDTO = {
identityId: string; identityId: string;
tenancyOcid?: string;
allowedUsernames?: string; allowedUsernames?: string;
accessTokenTTL?: number; accessTokenTTL?: number;
accessTokenMaxTTL?: number; accessTokenMaxTTL?: number;
@@ -19,3 +19,12 @@ export const validateUsernames = z
message: "One or more usernames are invalid" message: "One or more usernames are invalid"
}) })
.transform((arr) => arr.join(", ")); .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."
);
@@ -461,6 +461,7 @@ export const useAddIdentityOciAuth = () => {
return useMutation<IdentityOciAuth, object, AddIdentityOciAuthDTO>({ return useMutation<IdentityOciAuth, object, AddIdentityOciAuthDTO>({
mutationFn: async ({ mutationFn: async ({
identityId, identityId,
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -472,6 +473,7 @@ export const useAddIdentityOciAuth = () => {
} = await apiRequest.post<{ identityOciAuth: IdentityOciAuth }>( } = await apiRequest.post<{ identityOciAuth: IdentityOciAuth }>(
`/api/v1/auth/oci-auth/identities/${identityId}`, `/api/v1/auth/oci-auth/identities/${identityId}`,
{ {
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -497,6 +499,7 @@ export const useUpdateIdentityOciAuth = () => {
return useMutation<IdentityOciAuth, object, UpdateIdentityOciAuthDTO>({ return useMutation<IdentityOciAuth, object, UpdateIdentityOciAuthDTO>({
mutationFn: async ({ mutationFn: async ({
identityId, identityId,
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -508,6 +511,7 @@ export const useUpdateIdentityOciAuth = () => {
} = await apiRequest.patch<{ identityOciAuth: IdentityOciAuth }>( } = await apiRequest.patch<{ identityOciAuth: IdentityOciAuth }>(
`/api/v1/auth/oci-auth/identities/${identityId}`, `/api/v1/auth/oci-auth/identities/${identityId}`,
{ {
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -293,6 +293,7 @@ export type DeleteIdentityAwsAuthDTO = {
export type IdentityOciAuth = { export type IdentityOciAuth = {
identityId: string; identityId: string;
type: "iam"; type: "iam";
tenancyOcid: string;
allowedUsernames: string; allowedUsernames: string;
accessTokenTTL: number; accessTokenTTL: number;
accessTokenMaxTTL: number; accessTokenMaxTTL: number;
@@ -303,6 +304,7 @@ export type IdentityOciAuth = {
export type AddIdentityOciAuthDTO = { export type AddIdentityOciAuthDTO = {
organizationId: string; organizationId: string;
identityId: string; identityId: string;
tenancyOcid: string;
allowedUsernames: string; allowedUsernames: string;
accessTokenTTL: number; accessTokenTTL: number;
accessTokenMaxTTL: number; accessTokenMaxTTL: number;
@@ -315,6 +317,7 @@ export type AddIdentityOciAuthDTO = {
export type UpdateIdentityOciAuthDTO = { export type UpdateIdentityOciAuthDTO = {
organizationId: string; organizationId: string;
identityId: string; identityId: string;
tenancyOcid?: string;
allowedUsernames?: string; allowedUsernames?: string;
accessTokenTTL?: number; accessTokenTTL?: number;
accessTokenMaxTTL?: number; accessTokenMaxTTL?: number;
@@ -29,6 +29,7 @@ import { IdentityFormTab } from "./types";
const schema = z const schema = z
.object({ .object({
tenancyOcid: z.string().trim().min(1, "Tenancy OCID is required."),
allowedUsernames: z.string(), allowedUsernames: z.string(),
accessTokenTTL: z accessTokenTTL: z
.string() .string()
@@ -90,6 +91,7 @@ export const IdentityOciAuthForm = ({
} = useForm<FormData>({ } = useForm<FormData>({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
tenancyOcid: "",
allowedUsernames: "", allowedUsernames: "",
accessTokenTTL: "2592000", accessTokenTTL: "2592000",
accessTokenMaxTTL: "2592000", accessTokenMaxTTL: "2592000",
@@ -107,6 +109,7 @@ export const IdentityOciAuthForm = ({
useEffect(() => { useEffect(() => {
if (data) { if (data) {
reset({ reset({
tenancyOcid: data.tenancyOcid,
allowedUsernames: data.allowedUsernames, allowedUsernames: data.allowedUsernames,
accessTokenTTL: String(data.accessTokenTTL), accessTokenTTL: String(data.accessTokenTTL),
accessTokenMaxTTL: String(data.accessTokenMaxTTL), accessTokenMaxTTL: String(data.accessTokenMaxTTL),
@@ -121,6 +124,7 @@ export const IdentityOciAuthForm = ({
}); });
} else { } else {
reset({ reset({
tenancyOcid: "",
allowedUsernames: "", allowedUsernames: "",
accessTokenTTL: "2592000", accessTokenTTL: "2592000",
accessTokenMaxTTL: "2592000", accessTokenMaxTTL: "2592000",
@@ -131,6 +135,7 @@ export const IdentityOciAuthForm = ({
}, [data]); }, [data]);
const onFormSubmit = async ({ const onFormSubmit = async ({
tenancyOcid,
allowedUsernames, allowedUsernames,
accessTokenTTL, accessTokenTTL,
accessTokenMaxTTL, accessTokenMaxTTL,
@@ -143,6 +148,7 @@ export const IdentityOciAuthForm = ({
if (data) { if (data) {
await updateMutateAsync({ await updateMutateAsync({
organizationId: orgId, organizationId: orgId,
tenancyOcid,
allowedUsernames, allowedUsernames,
identityId, identityId,
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
@@ -154,6 +160,7 @@ export const IdentityOciAuthForm = ({
await addMutateAsync({ await addMutateAsync({
organizationId: orgId, organizationId: orgId,
identityId, identityId,
tenancyOcid,
allowedUsernames: allowedUsernames || "", allowedUsernames: allowedUsernames || "",
accessTokenTTL: Number(accessTokenTTL), accessTokenTTL: Number(accessTokenTTL),
accessTokenMaxTTL: Number(accessTokenMaxTTL), accessTokenMaxTTL: Number(accessTokenMaxTTL),
@@ -194,12 +201,22 @@ export const IdentityOciAuthForm = ({
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab> <Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
</TabList> </TabList>
<TabPanel value={IdentityFormTab.Configuration}> <TabPanel value={IdentityFormTab.Configuration}>
<Controller
control={control}
name="tenancyOcid"
render={({ field, fieldState: { error } }) => (
<FormControl label="Tenancy OCID" isError={Boolean(error)} errorText={error?.message}>
<Input {...field} placeholder="ocid1.tenancy.oc1..example" />
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="allowedUsernames" name="allowedUsernames"
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl <FormControl
label="Allowed Usernames" label="Allowed Usernames"
isOptional
isError={Boolean(error)} isError={Boolean(error)}
errorText={error?.message} errorText={error?.message}
> >
@@ -59,6 +59,9 @@ export const ViewIdentityOciAuthContent = ({
<IdentityAuthFieldDisplay label="Access Token Trusted IPs"> <IdentityAuthFieldDisplay label="Access Token Trusted IPs">
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")} {data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
</IdentityAuthFieldDisplay> </IdentityAuthFieldDisplay>
<IdentityAuthFieldDisplay className="col-span-2" label="Tenancy OCID">
{data.tenancyOcid}
</IdentityAuthFieldDisplay>
<IdentityAuthFieldDisplay className="col-span-2" label="Allowed Usernames"> <IdentityAuthFieldDisplay className="col-span-2" label="Allowed Usernames">
{data.allowedUsernames {data.allowedUsernames
?.split(",") ?.split(",")