mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-19 11:42:52 +00:00
feat(app-connection): Okta App Connection
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
export * from "./okta-connection-enums";
|
||||
export * from "./okta-connection-fns";
|
||||
export * from "./okta-connection-schemas";
|
||||
export * from "./okta-connection-types";
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum OktaConnectionMethod {
|
||||
ApiToken = "api-token"
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import { OktaConnectionMethod } from "./okta-connection-enums";
|
||||
import { TOktaApp, TOktaConnection, TOktaConnectionConfig } from "./okta-connection-types";
|
||||
|
||||
export const getOktaConnectionListItem = () => {
|
||||
return {
|
||||
name: "Okta" as const,
|
||||
app: AppConnection.Okta as const,
|
||||
methods: Object.values(OktaConnectionMethod) as [OktaConnectionMethod.ApiToken]
|
||||
};
|
||||
};
|
||||
|
||||
export const getOktaInstanceUrl = async (config: TOktaConnectionConfig) => {
|
||||
const instanceUrl = removeTrailingSlash(config.credentials.instanceUrl);
|
||||
await blockLocalAndPrivateIpAddresses(instanceUrl);
|
||||
return instanceUrl;
|
||||
};
|
||||
|
||||
export const validateOktaConnectionCredentials = async (config: TOktaConnectionConfig) => {
|
||||
const { apiToken } = config.credentials;
|
||||
const instanceUrl = await getOktaInstanceUrl(config);
|
||||
|
||||
try {
|
||||
await request.get(`${instanceUrl}/api/v1/users/me`, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `SSWS ${apiToken}`
|
||||
},
|
||||
validateStatus: (status) => status === 200
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
throw new UnauthorizedError({
|
||||
message: "Unable to validate connection: invalid credentials"
|
||||
});
|
||||
}
|
||||
|
||||
return config.credentials;
|
||||
};
|
||||
|
||||
export const listOktaApps = async (appConnection: TOktaConnection) => {
|
||||
const { apiToken } = appConnection.credentials;
|
||||
const instanceUrl = await getOktaInstanceUrl(appConnection);
|
||||
|
||||
const { data } = await request.get<TOktaApp[]>(`${instanceUrl}/api/v1/apps`, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `SSWS ${apiToken}`
|
||||
}
|
||||
});
|
||||
|
||||
return data.filter((app) => app.status === "ACTIVE");
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import RE2 from "re2";
|
||||
import z from "zod";
|
||||
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
BaseAppConnectionSchema,
|
||||
GenericCreateAppConnectionFieldsSchema,
|
||||
GenericUpdateAppConnectionFieldsSchema
|
||||
} from "@app/services/app-connection/app-connection-schemas";
|
||||
|
||||
import { OktaConnectionMethod } from "./okta-connection-enums";
|
||||
|
||||
export const OktaConnectionApiTokenCredentialsSchema = z.object({
|
||||
instanceUrl: z
|
||||
.string()
|
||||
.trim()
|
||||
.url("Invalid Instance URL")
|
||||
.min(1, "Instance URL required")
|
||||
.max(255)
|
||||
.describe(AppConnections.CREDENTIALS.OKTA.instanceUrl),
|
||||
apiToken: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "API Token required")
|
||||
.refine((value) => new RE2("^00[a-zA-Z0-9_-]{40}$").test(value), "Invalid Okta API Token format")
|
||||
.describe(AppConnections.CREDENTIALS.OKTA.apiToken)
|
||||
});
|
||||
|
||||
const BaseOktaConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Okta) });
|
||||
|
||||
export const OktaConnectionSchema = BaseOktaConnectionSchema.extend({
|
||||
method: z.literal(OktaConnectionMethod.ApiToken),
|
||||
credentials: OktaConnectionApiTokenCredentialsSchema
|
||||
});
|
||||
|
||||
export const SanitizedOktaConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseOktaConnectionSchema.extend({
|
||||
method: z.literal(OktaConnectionMethod.ApiToken),
|
||||
credentials: OktaConnectionApiTokenCredentialsSchema.pick({
|
||||
instanceUrl: true
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
export const ValidateOktaConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(OktaConnectionMethod.ApiToken).describe(AppConnections.CREATE(AppConnection.Okta).method),
|
||||
credentials: OktaConnectionApiTokenCredentialsSchema.describe(AppConnections.CREATE(AppConnection.Okta).credentials)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateOktaConnectionSchema = ValidateOktaConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.Okta)
|
||||
);
|
||||
|
||||
export const UpdateOktaConnectionSchema = z
|
||||
.object({
|
||||
credentials: OktaConnectionApiTokenCredentialsSchema.optional().describe(
|
||||
AppConnections.UPDATE(AppConnection.Okta).credentials
|
||||
)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Okta));
|
||||
|
||||
export const OktaConnectionListItemSchema = z.object({
|
||||
name: z.literal("Okta"),
|
||||
app: z.literal(AppConnection.Okta),
|
||||
methods: z.nativeEnum(OktaConnectionMethod).array()
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { listOktaApps } from "./okta-connection-fns";
|
||||
import { TOktaConnection } from "./okta-connection-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
app: AppConnection,
|
||||
connectionId: string,
|
||||
actor: OrgServiceActor
|
||||
) => Promise<TOktaConnection>;
|
||||
|
||||
export const oktaConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||
const listApps = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
const appConnection = await getAppConnection(AppConnection.Okta, connectionId, actor);
|
||||
const apps = await listOktaApps(appConnection);
|
||||
return apps;
|
||||
};
|
||||
|
||||
return {
|
||||
listApps
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import z from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import {
|
||||
CreateOktaConnectionSchema,
|
||||
OktaConnectionSchema,
|
||||
ValidateOktaConnectionCredentialsSchema
|
||||
} from "./okta-connection-schemas";
|
||||
|
||||
export type TOktaConnection = z.infer<typeof OktaConnectionSchema>;
|
||||
|
||||
export type TOktaConnectionInput = z.infer<typeof CreateOktaConnectionSchema> & {
|
||||
app: AppConnection.Okta;
|
||||
};
|
||||
|
||||
export type TValidateOktaConnectionCredentialsSchema = typeof ValidateOktaConnectionCredentialsSchema;
|
||||
|
||||
export type TOktaConnectionConfig = DiscriminativePick<TOktaConnectionInput, "method" | "app" | "credentials"> & {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type TOktaApp = {
|
||||
id: string;
|
||||
label: string;
|
||||
status: "ACTIVE" | "INACTIVE";
|
||||
};
|
||||
Reference in New Issue
Block a user