mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 20:51:50 +00:00
improvements: address feedback
This commit is contained in:
+3
-2
@@ -8,6 +8,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.createTable(TableName.AppConnection, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("name", 32).notNullable();
|
||||
t.string("description");
|
||||
t.string("app").notNullable();
|
||||
t.string("method").notNullable();
|
||||
t.binary("encryptedCredentials").notNullable();
|
||||
@@ -16,9 +17,9 @@ export async function up(knex: Knex): Promise<void> {
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.AppConnection);
|
||||
await createOnUpdateTrigger(knex, TableName.AppConnection);
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
@@ -12,6 +12,7 @@ import { TImmutableDBKeys } from "./models";
|
||||
export const AppConnectionsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
app: z.string(),
|
||||
method: z.string(),
|
||||
encryptedCredentials: zodBuffer,
|
||||
|
||||
@@ -4,9 +4,10 @@ import {
|
||||
} from "@app/ee/services/project-template/project-template-types";
|
||||
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
||||
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
|
||||
import { AppConnection, TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/lib/app-connections";
|
||||
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
||||
@@ -1875,8 +1876,10 @@ interface ApplyProjectTemplateEvent {
|
||||
|
||||
interface GetAppConnectionsEvent {
|
||||
type: EventType.GET_APP_CONNECTIONS;
|
||||
metadata?: {
|
||||
app: AppConnection;
|
||||
metadata: {
|
||||
app?: AppConnection;
|
||||
count: number;
|
||||
connectionIds: string[];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
pkiEst: false,
|
||||
enforceMfa: false,
|
||||
projectTemplates: false,
|
||||
appConnections: false
|
||||
appConnections: true
|
||||
});
|
||||
|
||||
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppConnection } from "@app/lib/app-connections";
|
||||
import { APP_CONNECTION_NAME_MAP } from "@app/lib/app-connections/maps";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||
|
||||
export const GROUPS = {
|
||||
CREATE: {
|
||||
@@ -1620,6 +1620,7 @@ export const AppConnections = {
|
||||
const appName = APP_CONNECTION_NAME_MAP[app];
|
||||
return {
|
||||
name: `The name of the ${appName} Connection to create. Must be slug-friendly.`,
|
||||
description: `An optional description for the ${appName} Connection.`,
|
||||
credentials: `The credentials used to connect with ${appName}.`,
|
||||
method: `The method used to authenticate with ${appName}.`
|
||||
};
|
||||
@@ -1629,11 +1630,12 @@ export const AppConnections = {
|
||||
return {
|
||||
connectionId: `The ID of the ${appName} Connection to be updated.`,
|
||||
name: `The updated name of the ${appName} Connection. Must be slug-friendly.`,
|
||||
description: `The updated description of the ${appName} Connection.`,
|
||||
credentials: `The credentials used to connect with ${appName}.`,
|
||||
method: `The method used to authenticate with ${appName}.`
|
||||
};
|
||||
},
|
||||
DELETE: (app: AppConnection) => ({
|
||||
connectionId: `The ID of the ${app} connection to be deleted.`
|
||||
connectionId: `The ID of the ${APP_CONNECTION_NAME_MAP[app]} connection to be deleted.`
|
||||
})
|
||||
};
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { TAwsConnection } from "@app/lib/app-connections/aws/aws-connection-types";
|
||||
import { TGitHubConnection, TGitHubConnectionInput } from "@app/lib/app-connections/github";
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "./app-connection-enums";
|
||||
|
||||
export type AppConnectionListItem = {
|
||||
app: AppConnection;
|
||||
name: string;
|
||||
methods: string[];
|
||||
};
|
||||
|
||||
export type TAppConnection = { id: string } & (TAwsConnection | TGitHubConnection);
|
||||
|
||||
export type TAppConnectionInput = { id: string } & (TAwsConnection | TGitHubConnectionInput);
|
||||
|
||||
export type TCreateAppConnectionDTO = Pick<TAppConnectionInput, "credentials" | "method" | "name" | "app">;
|
||||
|
||||
export type TUpdateAppConnectionDTO = Partial<Omit<TCreateAppConnectionDTO, "method" | "app">> & {
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
export type TAppConnectionConfig = { orgId: string } & DiscriminativePick<
|
||||
TAppConnectionInput,
|
||||
"app" | "method" | "credentials"
|
||||
>;
|
||||
@@ -1,69 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { BaseAppConnectionSchema } from "@app/services/app-connection/app-connection-schemas";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AwsConnectionMethod } from "./aws-connection-enums";
|
||||
|
||||
export const AwsConnectionAssumeRoleCredentialsSchema = z.object({
|
||||
roleArn: z.string().min(1, "Role ARN required")
|
||||
});
|
||||
|
||||
export const AwsConnectionAccessTokenCredentialsSchema = z.object({
|
||||
accessKeyId: z.string().min(1, "Access Key ID required"),
|
||||
secretAccessKey: z.string().min(1, "Secret Access Key required")
|
||||
});
|
||||
|
||||
const BaseAwsConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.AWS) });
|
||||
|
||||
export const AwsConnectionSchema = z.intersection(
|
||||
BaseAwsConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AssumeRole),
|
||||
credentials: AwsConnectionAssumeRoleCredentialsSchema
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AccessKey),
|
||||
credentials: AwsConnectionAccessTokenCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedAwsConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseAwsConnectionSchema.extend({
|
||||
method: z.literal(AwsConnectionMethod.AssumeRole),
|
||||
credentials: AwsConnectionAssumeRoleCredentialsSchema.omit({ roleArn: true })
|
||||
}),
|
||||
BaseAwsConnectionSchema.extend({
|
||||
method: z.literal(AwsConnectionMethod.AccessKey),
|
||||
credentials: AwsConnectionAccessTokenCredentialsSchema.omit({ secretAccessKey: true })
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateAwsConnectionSchema = z
|
||||
.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AssumeRole).describe(AppConnections.CREATE(AppConnection.AWS).method),
|
||||
credentials: AwsConnectionAssumeRoleCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.AWS).credentials
|
||||
)
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AccessKey).describe(AppConnections.CREATE(AppConnection.AWS).method),
|
||||
credentials: AwsConnectionAccessTokenCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.AWS).credentials
|
||||
)
|
||||
})
|
||||
])
|
||||
.and(z.object({ name: slugSchema({ field: "name" }).describe(AppConnections.CREATE(AppConnection.AWS).name) }));
|
||||
|
||||
export const UpdateAwsConnectionSchema = z.object({
|
||||
name: slugSchema({ field: "name" }).optional().describe(AppConnections.UPDATE(AppConnection.AWS).name),
|
||||
credentials: z
|
||||
.union([AwsConnectionAccessTokenCredentialsSchema, AwsConnectionAssumeRoleCredentialsSchema])
|
||||
.optional()
|
||||
.describe(AppConnections.UPDATE(AppConnection.AWS).credentials)
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AwsConnectionSchema } from "./aws-connection-schemas";
|
||||
|
||||
export type TAwsConnection = z.infer<typeof AwsConnectionSchema>;
|
||||
|
||||
export type TAwsConnectionConfig = DiscriminativePick<TAwsConnection, "orgId" | "method" | "app" | "credentials">;
|
||||
@@ -1,77 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/lib/app-connections";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { BaseAppConnectionSchema } from "@app/services/app-connection/app-connection-schemas";
|
||||
|
||||
import { GitHubConnectionMethod } from "./github-connection-enums";
|
||||
|
||||
export const GitHubConnectionOAuthInputCredentialsSchema = z.object({
|
||||
code: z.string().min(1, "OAuth code required")
|
||||
});
|
||||
|
||||
export const GitHubConnectionAppInputCredentialsSchema = z.object({
|
||||
code: z.string().min(1, "GitHub App code required"),
|
||||
installationId: z.string().min(1, "GitHub App Installation ID required")
|
||||
});
|
||||
|
||||
export const GitHubConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
accessToken: z.string()
|
||||
});
|
||||
|
||||
export const GitHubConnectionAppOutputCredentialsSchema = z.object({
|
||||
installationId: z.string()
|
||||
});
|
||||
|
||||
export const CreateGitHubConnectionSchema = z
|
||||
.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.App).describe(AppConnections.CREATE(AppConnection.GitHub).method),
|
||||
credentials: GitHubConnectionAppInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.GitHub).credentials
|
||||
)
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth).describe(AppConnections.CREATE(AppConnection.GitHub).method),
|
||||
credentials: GitHubConnectionOAuthInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.GitHub).credentials
|
||||
)
|
||||
})
|
||||
])
|
||||
.and(z.object({ name: slugSchema({ field: "name" }).describe(AppConnections.CREATE(AppConnection.GitHub).name) }));
|
||||
|
||||
export const UpdateGitHubConnectionSchema = z.object({
|
||||
name: slugSchema({ field: "name" }).optional().describe(AppConnections.UPDATE(AppConnection.GitHub).name),
|
||||
credentials: z
|
||||
.union([GitHubConnectionAppInputCredentialsSchema, GitHubConnectionOAuthInputCredentialsSchema])
|
||||
.optional()
|
||||
.describe(AppConnections.UPDATE(AppConnection.GitHub).credentials)
|
||||
});
|
||||
|
||||
const BaseGitHubConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.GitHub) });
|
||||
|
||||
export const GitHubAppConnectionSchema = z.intersection(
|
||||
BaseGitHubConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.App),
|
||||
credentials: GitHubConnectionAppOutputCredentialsSchema
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth),
|
||||
credentials: GitHubConnectionOAuthOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedGitHubConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseGitHubConnectionSchema.extend({
|
||||
method: z.literal(GitHubConnectionMethod.App),
|
||||
credentials: GitHubConnectionAppOutputCredentialsSchema.omit({ installationId: true })
|
||||
}),
|
||||
BaseGitHubConnectionSchema.extend({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth),
|
||||
credentials: GitHubConnectionOAuthOutputCredentialsSchema.omit({ accessToken: true })
|
||||
})
|
||||
]);
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from "./app-connection-enums";
|
||||
export * from "./app-connection-types";
|
||||
@@ -1,17 +0,0 @@
|
||||
import { TAppConnection } from "@app/lib/app-connections/app-connection-types";
|
||||
|
||||
import { AppConnection } from "./app-connection-enums";
|
||||
import { AwsConnectionMethod } from "./aws/aws-connection-enums";
|
||||
import { GitHubConnectionMethod } from "./github/github-connection-enums";
|
||||
|
||||
export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.AWS]: "AWS",
|
||||
[AppConnection.GitHub]: "GitHub"
|
||||
};
|
||||
|
||||
export const APP_CONNECTION_METHOD_NAME_MAP: Record<TAppConnection["method"], string> = {
|
||||
[AwsConnectionMethod.AssumeRole]: "Assume Role",
|
||||
[AwsConnectionMethod.AccessKey]: "Access Key",
|
||||
[GitHubConnectionMethod.App]: "Github App",
|
||||
[GitHubConnectionMethod.OAuth]: "OAuth"
|
||||
};
|
||||
@@ -14,3 +14,5 @@ export const prefixWithSlash = (str: string) => {
|
||||
if (str.startsWith("/")) return str;
|
||||
return `/${str}`;
|
||||
};
|
||||
|
||||
export const startsWithVowel = (str: string) => /^[aeiou]/i.test(str);
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { AwsConnectionListItemSchema, SanitizedAwsConnectionSchema } from "src/services/app-connection/aws";
|
||||
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "src/services/app-connection/github";
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { AppConnection } from "@app/lib/app-connections";
|
||||
import { SanitizedAwsConnectionSchema } from "@app/lib/app-connections/aws";
|
||||
import { SanitizedGitHubConnectionSchema } from "@app/lib/app-connections/github";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
// can't use discriminated due to multiple schemas for certain apps
|
||||
export const SanitizedAppConnectionSchema = z.union([
|
||||
const SanitizedAppConnectionSchema = z.union([
|
||||
...SanitizedAwsConnectionSchema.options,
|
||||
...SanitizedGitHubConnectionSchema.options
|
||||
]);
|
||||
|
||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
AwsConnectionListItemSchema,
|
||||
GitHubConnectionListItemSchema
|
||||
]);
|
||||
|
||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
@@ -25,18 +29,11 @@ export const registerAppConnectionRouter = async (server: FastifyZodProvider) =>
|
||||
description: "List the available App Connection Options.",
|
||||
response: {
|
||||
200: z.object({
|
||||
appConnectionOptions: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
app: z.nativeEnum(AppConnection),
|
||||
methods: z.string().array()
|
||||
})
|
||||
.passthrough()
|
||||
.array()
|
||||
appConnectionOptions: AppConnectionOptionsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: () => {
|
||||
const appConnectionOptions = server.services.appConnection.listAppConnectionOptions();
|
||||
return { appConnectionOptions };
|
||||
@@ -55,7 +52,7 @@ export const registerAppConnectionRouter = async (server: FastifyZodProvider) =>
|
||||
200: z.object({ appConnections: SanitizedAppConnectionSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const appConnections = await server.services.appConnection.listAppConnectionsByOrg(req.permission);
|
||||
|
||||
@@ -63,7 +60,11 @@ export const registerAppConnectionRouter = async (server: FastifyZodProvider) =>
|
||||
...req.auditLogInfo,
|
||||
orgId: req.permission.orgId,
|
||||
event: {
|
||||
type: EventType.GET_APP_CONNECTIONS
|
||||
type: EventType.GET_APP_CONNECTIONS,
|
||||
metadata: {
|
||||
count: appConnections.length,
|
||||
connectionIds: appConnections.map((connection) => connection.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+28
-16
@@ -2,10 +2,12 @@ import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { AppConnection, TAppConnection, TAppConnectionInput } from "@app/lib/app-connections";
|
||||
import { APP_CONNECTION_NAME_MAP } from "@app/lib/app-connections/maps";
|
||||
import { startsWithVowel } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||
import { TAppConnection, TAppConnectionInput } from "@app/services/app-connection/app-connection-types";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerAppConnectionEndpoints = <T extends TAppConnection, I extends TAppConnectionInput>({
|
||||
@@ -17,8 +19,13 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
}: {
|
||||
app: AppConnection;
|
||||
server: FastifyZodProvider;
|
||||
createSchema: z.ZodType<{ name: string; method: I["method"]; credentials: I["credentials"] }>;
|
||||
updateSchema: z.ZodType<{ name?: string; credentials?: I["credentials"] }>;
|
||||
createSchema: z.ZodType<{
|
||||
name: string;
|
||||
method: I["method"];
|
||||
credentials: I["credentials"];
|
||||
description?: string | null;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{ name?: string; credentials?: I["credentials"]; description?: string | null }>;
|
||||
responseSchema: z.ZodTypeAny;
|
||||
}) => {
|
||||
const appName = APP_CONNECTION_NAME_MAP[app];
|
||||
@@ -35,7 +42,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
200: z.object({ appConnections: responseSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const appConnections = (await server.services.appConnection.listAppConnectionsByOrg(req.permission, app)) as T[];
|
||||
|
||||
@@ -45,7 +52,9 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
event: {
|
||||
type: EventType.GET_APP_CONNECTIONS,
|
||||
metadata: {
|
||||
app
|
||||
app,
|
||||
count: appConnections.length,
|
||||
connectionIds: appConnections.map((connection) => connection.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -69,7 +78,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
@@ -112,7 +121,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { connectionName } = req.params;
|
||||
|
||||
@@ -144,18 +153,20 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Create an ${appName} Connection for the current organization.`,
|
||||
description: `Create ${
|
||||
startsWithVowel(appName) ? "an" : "a"
|
||||
} ${appName} Connection for the current organization.`,
|
||||
body: createSchema,
|
||||
response: {
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { name, method, credentials } = req.body;
|
||||
const { name, method, credentials, description } = req.body;
|
||||
|
||||
const appConnection = (await server.services.appConnection.createAppConnection(
|
||||
{ name, method, app, credentials },
|
||||
{ name, method, app, credentials, description },
|
||||
req.permission
|
||||
)) as TAppConnection;
|
||||
|
||||
@@ -193,13 +204,13 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { name, credentials } = req.body;
|
||||
const { name, credentials, description } = req.body;
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const appConnection = (await server.services.appConnection.updateAppConnection(
|
||||
{ name, credentials, connectionId },
|
||||
{ name, credentials, connectionId, description },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
@@ -210,6 +221,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
type: EventType.UPDATE_APP_CONNECTION,
|
||||
metadata: {
|
||||
name,
|
||||
description,
|
||||
credentialsUpdated: Boolean(credentials),
|
||||
connectionId
|
||||
}
|
||||
@@ -235,7 +247,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { AppConnection } from "@app/lib/app-connections";
|
||||
import {
|
||||
CreateAwsConnectionSchema,
|
||||
SanitizedAwsConnectionSchema,
|
||||
UpdateAwsConnectionSchema
|
||||
} from "@app/lib/app-connections/aws";
|
||||
} from "src/services/app-connection/aws";
|
||||
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
|
||||
+5
-4
@@ -1,9 +1,10 @@
|
||||
import { AppConnection } from "@app/lib/app-connections";
|
||||
import {
|
||||
CreateGitHubConnectionSchema,
|
||||
GitHubAppConnectionSchema,
|
||||
SanitizedGitHubConnectionSchema,
|
||||
UpdateGitHubConnectionSchema
|
||||
} from "@app/lib/app-connections/github";
|
||||
} from "src/services/app-connection/github";
|
||||
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
@@ -11,7 +12,7 @@ export const registerGitHubConnectionRouter = async (server: FastifyZodProvider)
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.GitHub,
|
||||
server,
|
||||
responseSchema: GitHubAppConnectionSchema,
|
||||
responseSchema: SanitizedGitHubConnectionSchema,
|
||||
createSchema: CreateGitHubConnectionSchema,
|
||||
updateSchema: UpdateGitHubConnectionSchema
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AppConnection } from "@app/lib/app-connections";
|
||||
import { registerAwsConnectionRouter } from "@app/server/routes/v1/app-connection-routers/apps/aws-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "@app/server/routes/v1/app-connection-routers/apps/github-connection-router";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
export const APP_CONNECTION_REGISTER_MAP: Record<AppConnection, (server: FastifyZodProvider) => Promise<void>> = {
|
||||
[AppConnection.AWS]: registerAwsConnectionRouter,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ormify } from "@app/lib/knex";
|
||||
export type TAppConnectionDALFactory = ReturnType<typeof appConnectionDALFactory>;
|
||||
|
||||
export const appConnectionDALFactory = (db: TDbClient) => {
|
||||
const appConnection = ormify(db, TableName.AppConnection);
|
||||
const appConnectionOrm = ormify(db, TableName.AppConnection);
|
||||
|
||||
return { ...appConnection };
|
||||
return { ...appConnectionOrm };
|
||||
};
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import { AppConnection, AppConnectionListItem, TAppConnection, TAppConnectionConfig } from "@app/lib/app-connections";
|
||||
import { getAwsAppConnectionListItem, validateAwsConnectionCredentials } from "@app/lib/app-connections/aws";
|
||||
import { getGitHubConnectionListItem, validateGitHubConnectionCredentials } from "@app/lib/app-connections/github";
|
||||
import {
|
||||
AwsConnectionMethod,
|
||||
getAwsAppConnectionListItem,
|
||||
validateAwsConnectionCredentials
|
||||
} from "src/services/app-connection/aws";
|
||||
import {
|
||||
getGitHubConnectionListItem,
|
||||
GitHubConnectionMethod,
|
||||
validateGitHubConnectionCredentials
|
||||
} from "src/services/app-connection/github";
|
||||
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { TAppConnectionServiceFactoryDep } from "@app/services/app-connection/app-connection-service";
|
||||
import { TAppConnection, TAppConnectionConfig } from "@app/services/app-connection/app-connection-types";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
|
||||
export const listAppConnectionOptions = (): (AppConnectionListItem & Record<string, unknown>)[] => {
|
||||
export const listAppConnectionOptions = () => {
|
||||
return [getAwsAppConnectionListItem(), getGitHubConnectionListItem()].sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
@@ -65,3 +75,19 @@ export const validateAppConnectionCredentials = async (
|
||||
throw new Error(`Unhandled App Connection ${app}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const getAppConnectionMethodName = (method: TAppConnection["method"]) => {
|
||||
switch (method) {
|
||||
case GitHubConnectionMethod.App:
|
||||
return "GitHub App";
|
||||
case GitHubConnectionMethod.OAuth:
|
||||
return "OAuth";
|
||||
case AwsConnectionMethod.AccessKey:
|
||||
return "Access Key";
|
||||
case AwsConnectionMethod.AssumeRole:
|
||||
return "Assume Role";
|
||||
default:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Unhandled App Connection Method: ${method}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { AppConnection } from "./app-connection-enums";
|
||||
|
||||
export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.AWS]: "AWS",
|
||||
[AppConnection.GitHub]: "GitHub"
|
||||
};
|
||||
@@ -1,7 +1,35 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AppConnectionsSchema } from "@app/db/schemas/app-connections";
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
|
||||
import { AppConnection } from "./app-connection-enums";
|
||||
|
||||
export const BaseAppConnectionSchema = AppConnectionsSchema.omit({
|
||||
encryptedCredentials: true,
|
||||
app: true,
|
||||
method: true
|
||||
});
|
||||
|
||||
export const GenericCreateAppConnectionFieldsSchema = (app: AppConnection) =>
|
||||
z.object({
|
||||
name: slugSchema({ field: "name" }).describe(AppConnections.CREATE(app).name),
|
||||
description: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(256, "Description cannot exceed 256 characters")
|
||||
.nullish()
|
||||
.describe(AppConnections.CREATE(app).description)
|
||||
});
|
||||
|
||||
export const GenericUpdateAppConnectionFieldsSchema = (app: AppConnection) =>
|
||||
z.object({
|
||||
name: slugSchema({ field: "name" }).describe(AppConnections.UPDATE(app).name).optional(),
|
||||
description: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(256, "Description cannot exceed 256 characters")
|
||||
.nullish()
|
||||
.describe(AppConnections.UPDATE(app).description)
|
||||
});
|
||||
|
||||
@@ -3,21 +3,26 @@ import { ForbiddenError } from "@casl/ability";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
AppConnection,
|
||||
TAppConnection,
|
||||
TAppConnectionConfig,
|
||||
TCreateAppConnectionDTO,
|
||||
TUpdateAppConnectionDTO
|
||||
} from "@app/lib/app-connections";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { DiscriminativePick, OrgServiceActor } from "@app/lib/types";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
decryptAppConnectionCredentials,
|
||||
encryptAppConnectionCredentials,
|
||||
getAppConnectionMethodName,
|
||||
listAppConnectionOptions,
|
||||
validateAppConnectionCredentials
|
||||
} from "@app/services/app-connection/app-connection-fns";
|
||||
import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||
import {
|
||||
TAppConnection,
|
||||
TAppConnectionConfig,
|
||||
TCreateAppConnectionDTO,
|
||||
TUpdateAppConnectionDTO,
|
||||
TValidateAppConnectionCredentials
|
||||
} from "@app/services/app-connection/app-connection-types";
|
||||
import { ValidateAwsConnectionCredentialsSchema } from "@app/services/app-connection/aws";
|
||||
import { ValidateGitHubConnectionCredentialsSchema } from "@app/services/app-connection/github";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { TAppConnectionDALFactory } from "./app-connection-dal";
|
||||
@@ -31,6 +36,11 @@ export type TAppConnectionServiceFactoryDep = {
|
||||
|
||||
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
||||
|
||||
const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAppConnectionCredentials> = {
|
||||
[AppConnection.AWS]: ValidateAwsConnectionCredentialsSchema,
|
||||
[AppConnection.GitHub]: ValidateGitHubConnectionCredentialsSchema
|
||||
};
|
||||
|
||||
export const appConnectionServiceFactory = ({
|
||||
appConnectionDAL,
|
||||
permissionService,
|
||||
@@ -160,40 +170,53 @@ export const appConnectionServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.AppConnections);
|
||||
|
||||
const isConflictingName = Boolean(
|
||||
await appConnectionDAL.findOne({
|
||||
name: params.name,
|
||||
orgId: actor.orgId
|
||||
})
|
||||
);
|
||||
const appConnection = await appConnectionDAL.transaction(async (tx) => {
|
||||
const isConflictingName = Boolean(
|
||||
await appConnectionDAL.findOne(
|
||||
{
|
||||
name: params.name,
|
||||
orgId: actor.orgId
|
||||
},
|
||||
tx
|
||||
)
|
||||
);
|
||||
|
||||
if (isConflictingName)
|
||||
throw new BadRequestError({
|
||||
message: `An App Connection with the name "${params.name}" already exists`
|
||||
if (isConflictingName)
|
||||
throw new BadRequestError({
|
||||
message: `An App Connection with the name "${params.name}" already exists`
|
||||
});
|
||||
|
||||
const validatedCredentials = await validateAppConnectionCredentials({
|
||||
app,
|
||||
credentials,
|
||||
method,
|
||||
orgId: actor.orgId
|
||||
} as TAppConnectionConfig);
|
||||
|
||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: validatedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const validatedCredentials = await validateAppConnectionCredentials({
|
||||
app,
|
||||
credentials,
|
||||
method,
|
||||
orgId: actor.orgId
|
||||
} as TAppConnectionConfig);
|
||||
const connection = await appConnectionDAL.create(
|
||||
{
|
||||
orgId: actor.orgId,
|
||||
encryptedCredentials,
|
||||
method,
|
||||
app,
|
||||
...params
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: validatedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
return {
|
||||
...connection,
|
||||
credentials: validatedCredentials
|
||||
};
|
||||
});
|
||||
|
||||
const appConnection = await appConnectionDAL.create({
|
||||
orgId: actor.orgId,
|
||||
encryptedCredentials,
|
||||
method,
|
||||
app,
|
||||
...params
|
||||
});
|
||||
|
||||
return { ...appConnection, credentials: validatedCredentials };
|
||||
return appConnection;
|
||||
};
|
||||
|
||||
const updateAppConnection = async (
|
||||
@@ -216,44 +239,69 @@ export const appConnectionServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.AppConnections);
|
||||
|
||||
if (params.name && appConnection.name !== params.name) {
|
||||
const isConflictingName = Boolean(
|
||||
await appConnectionDAL.findOne({
|
||||
name: params.name,
|
||||
orgId: appConnection.orgId
|
||||
})
|
||||
const updatedAppConnection = await appConnectionDAL.transaction(async (tx) => {
|
||||
if (params.name && appConnection.name !== params.name) {
|
||||
const isConflictingName = Boolean(
|
||||
await appConnectionDAL.findOne(
|
||||
{
|
||||
name: params.name,
|
||||
orgId: appConnection.orgId
|
||||
},
|
||||
tx
|
||||
)
|
||||
);
|
||||
|
||||
if (isConflictingName)
|
||||
throw new BadRequestError({
|
||||
message: `An App Connection with the name "${params.name}" already exists`
|
||||
});
|
||||
}
|
||||
|
||||
let encryptedCredentials: undefined | Buffer;
|
||||
|
||||
if (credentials) {
|
||||
const { app, method } = appConnection as DiscriminativePick<TAppConnectionConfig, "app" | "method">;
|
||||
|
||||
if (
|
||||
!VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[app].safeParse({
|
||||
method,
|
||||
credentials
|
||||
}).success
|
||||
)
|
||||
throw new BadRequestError({
|
||||
message: `Invalid credential format for ${
|
||||
APP_CONNECTION_NAME_MAP[app]
|
||||
} Connection with method ${getAppConnectionMethodName(method)}`
|
||||
});
|
||||
|
||||
const validatedCredentials = await validateAppConnectionCredentials({
|
||||
app,
|
||||
orgId: actor.orgId,
|
||||
credentials,
|
||||
method
|
||||
} as TAppConnectionConfig);
|
||||
|
||||
if (!validatedCredentials)
|
||||
throw new BadRequestError({ message: "Unable to validate connection - check credentials" });
|
||||
|
||||
encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: validatedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
}
|
||||
|
||||
const updatedConnection = await appConnectionDAL.updateById(
|
||||
connectionId,
|
||||
{
|
||||
orgId: actor.orgId,
|
||||
encryptedCredentials,
|
||||
...params
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
if (isConflictingName)
|
||||
throw new BadRequestError({
|
||||
message: `An App Connection with the name "${params.name}" already exists`
|
||||
});
|
||||
}
|
||||
|
||||
let encryptedCredentials: undefined | Buffer;
|
||||
|
||||
if (credentials) {
|
||||
const validatedCredentials = await validateAppConnectionCredentials({
|
||||
app: appConnection.app,
|
||||
credentials,
|
||||
method: appConnection.method,
|
||||
orgId: actor.orgId
|
||||
} as TAppConnectionConfig);
|
||||
|
||||
if (!validatedCredentials)
|
||||
throw new BadRequestError({ message: "Unable to validate connection - check credentials" });
|
||||
|
||||
encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: validatedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
}
|
||||
|
||||
const updatedAppConnection = await appConnectionDAL.updateById(connectionId, {
|
||||
orgId: actor.orgId,
|
||||
encryptedCredentials,
|
||||
...params
|
||||
return updatedConnection;
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
TAwsConnection,
|
||||
TAwsConnectionConfig,
|
||||
TAwsConnectionInput,
|
||||
TValidateAwsConnectionCredentials
|
||||
} from "@app/services/app-connection/aws";
|
||||
import {
|
||||
TGitHubConnection,
|
||||
TGitHubConnectionConfig,
|
||||
TGitHubConnectionInput,
|
||||
TValidateGitHubConnectionCredentials
|
||||
} from "@app/services/app-connection/github";
|
||||
|
||||
export type TAppConnection = { id: string } & (TAwsConnection | TGitHubConnection);
|
||||
|
||||
export type TAppConnectionInput = { id: string } & (TAwsConnectionInput | TGitHubConnectionInput);
|
||||
|
||||
export type TCreateAppConnectionDTO = Pick<
|
||||
TAppConnectionInput,
|
||||
"credentials" | "method" | "name" | "app" | "description"
|
||||
>;
|
||||
|
||||
export type TUpdateAppConnectionDTO = Partial<Omit<TCreateAppConnectionDTO, "method" | "app">> & {
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
export type TAppConnectionConfig = TAwsConnectionConfig | TGitHubConnectionConfig;
|
||||
|
||||
export type TValidateAppConnectionCredentials =
|
||||
| TValidateAwsConnectionCredentials
|
||||
| TValidateGitHubConnectionCredentials;
|
||||
+5
-5
@@ -2,20 +2,20 @@ import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
|
||||
import AWS from "aws-sdk";
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
import { AppConnection } from "@app/lib/app-connections/app-connection-enums";
|
||||
import { TAwsConnectionConfig } from "@app/lib/app-connections/aws/aws-connection-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import { AwsConnectionMethod } from "./aws-connection-enums";
|
||||
import { TAwsConnectionConfig } from "./aws-connection-types";
|
||||
|
||||
export const getAwsAppConnectionListItem = () => {
|
||||
const { INF_APP_CONNECTION_AWS_ACCESS_KEY_ID } = getConfig();
|
||||
|
||||
return {
|
||||
name: "AWS",
|
||||
app: AppConnection.AWS,
|
||||
methods: Object.values(AwsConnectionMethod),
|
||||
name: "AWS" as const,
|
||||
app: AppConnection.AWS as const,
|
||||
methods: Object.values(AwsConnectionMethod) as [AwsConnectionMethod.AssumeRole, AwsConnectionMethod.AccessKey],
|
||||
accessKeyId: INF_APP_CONNECTION_AWS_ACCESS_KEY_ID
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
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 { AwsConnectionMethod } from "./aws-connection-enums";
|
||||
|
||||
export const AwsConnectionAssumeRoleCredentialsSchema = z.object({
|
||||
roleArn: z.string().trim().min(1, "Role ARN required")
|
||||
});
|
||||
|
||||
export const AwsConnectionAccessTokenCredentialsSchema = z.object({
|
||||
accessKeyId: z.string().trim().min(1, "Access Key ID required"),
|
||||
secretAccessKey: z.string().trim().min(1, "Secret Access Key required")
|
||||
});
|
||||
|
||||
const BaseAwsConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.AWS) });
|
||||
|
||||
export const AwsConnectionSchema = z.intersection(
|
||||
BaseAwsConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AssumeRole),
|
||||
credentials: AwsConnectionAssumeRoleCredentialsSchema
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AccessKey),
|
||||
credentials: AwsConnectionAccessTokenCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedAwsConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseAwsConnectionSchema.extend({
|
||||
method: z.literal(AwsConnectionMethod.AssumeRole),
|
||||
credentials: AwsConnectionAssumeRoleCredentialsSchema.omit({ roleArn: true })
|
||||
}),
|
||||
BaseAwsConnectionSchema.extend({
|
||||
method: z.literal(AwsConnectionMethod.AccessKey),
|
||||
credentials: AwsConnectionAccessTokenCredentialsSchema.omit({ secretAccessKey: true })
|
||||
})
|
||||
]);
|
||||
|
||||
export const ValidateAwsConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AssumeRole).describe(AppConnections?.CREATE(AppConnection.AWS).method),
|
||||
credentials: AwsConnectionAssumeRoleCredentialsSchema.describe(AppConnections.CREATE(AppConnection.AWS).credentials)
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(AwsConnectionMethod.AccessKey).describe(AppConnections?.CREATE(AppConnection.AWS).method),
|
||||
credentials: AwsConnectionAccessTokenCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.AWS).credentials
|
||||
)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateAwsConnectionSchema = ValidateAwsConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.AWS)
|
||||
);
|
||||
|
||||
export const UpdateAwsConnectionSchema = z
|
||||
.object({
|
||||
credentials: z
|
||||
.union([AwsConnectionAccessTokenCredentialsSchema, AwsConnectionAssumeRoleCredentialsSchema])
|
||||
.optional()
|
||||
.describe(AppConnections.UPDATE(AppConnection.AWS).credentials)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.AWS));
|
||||
|
||||
export const AwsConnectionListItemSchema = z.object({
|
||||
name: z.literal("AWS"),
|
||||
app: z.literal(AppConnection.AWS),
|
||||
// the below is preferable but currently breaks mintlify
|
||||
// methods: z.tuple([z.literal(AwsConnectionMethod.AssumeRole), z.literal(AwsConnectionMethod.AccessKey)]),
|
||||
methods: z.nativeEnum(AwsConnectionMethod).array(),
|
||||
accessKeyId: z.string().optional()
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import {
|
||||
AwsConnectionSchema,
|
||||
CreateAwsConnectionSchema,
|
||||
ValidateAwsConnectionCredentialsSchema
|
||||
} from "./aws-connection-schemas";
|
||||
|
||||
export type TAwsConnection = z.infer<typeof AwsConnectionSchema>;
|
||||
|
||||
export type TAwsConnectionInput = z.infer<typeof CreateAwsConnectionSchema> & {
|
||||
app: AppConnection.AWS;
|
||||
};
|
||||
|
||||
export type TValidateAwsConnectionCredentials = typeof ValidateAwsConnectionCredentialsSchema;
|
||||
|
||||
export type TAwsConnectionConfig = DiscriminativePick<TAwsConnectionInput, "method" | "app" | "credentials"> & {
|
||||
orgId: string;
|
||||
};
|
||||
+5
-5
@@ -3,10 +3,10 @@ import { AxiosResponse } from "axios";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError, ForbiddenRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { getAppConnectionMethodName } from "@app/services/app-connection/app-connection-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { APP_CONNECTION_METHOD_NAME_MAP } from "../maps";
|
||||
import { GitHubConnectionMethod } from "./github-connection-enums";
|
||||
import { TGitHubConnectionConfig } from "./github-connection-types";
|
||||
|
||||
@@ -14,9 +14,9 @@ export const getGitHubConnectionListItem = () => {
|
||||
const { INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID, INF_APP_CONNECTION_GITHUB_APP_SLUG } = getConfig();
|
||||
|
||||
return {
|
||||
name: "GitHub",
|
||||
app: AppConnection.GitHub,
|
||||
methods: Object.values(GitHubConnectionMethod),
|
||||
name: "GitHub" as const,
|
||||
app: AppConnection.GitHub as const,
|
||||
methods: Object.values(GitHubConnectionMethod) as [GitHubConnectionMethod.App, GitHubConnectionMethod.OAuth],
|
||||
oauthClientId: INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID,
|
||||
appClientSlug: INF_APP_CONNECTION_GITHUB_APP_SLUG
|
||||
};
|
||||
@@ -53,7 +53,7 @@ export const validateGitHubConnectionCredentials = async (config: TGitHubConnect
|
||||
|
||||
if (!clientId || !clientSecret) {
|
||||
throw new InternalServerError({
|
||||
message: `GitHub ${APP_CONNECTION_METHOD_NAME_MAP[method]} environment variables have not been configured`
|
||||
message: `GitHub ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
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 { GitHubConnectionMethod } from "./github-connection-enums";
|
||||
|
||||
export const GitHubConnectionOAuthInputCredentialsSchema = z.object({
|
||||
code: z.string().trim().min(1, "OAuth code required")
|
||||
});
|
||||
|
||||
export const GitHubConnectionAppInputCredentialsSchema = z.object({
|
||||
code: z.string().trim().min(1, "GitHub App code required"),
|
||||
installationId: z.string().min(1, "GitHub App Installation ID required")
|
||||
});
|
||||
|
||||
export const GitHubConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
accessToken: z.string()
|
||||
});
|
||||
|
||||
export const GitHubConnectionAppOutputCredentialsSchema = z.object({
|
||||
installationId: z.string()
|
||||
});
|
||||
|
||||
export const ValidateGitHubConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.App).describe(AppConnections.CREATE(AppConnection.GitHub).method),
|
||||
credentials: GitHubConnectionAppInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.GitHub).credentials
|
||||
)
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth).describe(AppConnections.CREATE(AppConnection.GitHub).method),
|
||||
credentials: GitHubConnectionOAuthInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.GitHub).credentials
|
||||
)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateGitHubConnectionSchema = ValidateGitHubConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.GitHub)
|
||||
);
|
||||
|
||||
export const UpdateGitHubConnectionSchema = z
|
||||
.object({
|
||||
credentials: z
|
||||
.union([GitHubConnectionAppInputCredentialsSchema, GitHubConnectionOAuthInputCredentialsSchema])
|
||||
.optional()
|
||||
.describe(AppConnections.UPDATE(AppConnection.GitHub).credentials)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.GitHub));
|
||||
|
||||
const BaseGitHubConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.GitHub) });
|
||||
|
||||
export const GitHubAppConnectionSchema = z.intersection(
|
||||
BaseGitHubConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.App),
|
||||
credentials: GitHubConnectionAppOutputCredentialsSchema
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth),
|
||||
credentials: GitHubConnectionOAuthOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedGitHubConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseGitHubConnectionSchema.extend({
|
||||
method: z.literal(GitHubConnectionMethod.App),
|
||||
credentials: GitHubConnectionAppOutputCredentialsSchema.omit({ installationId: true })
|
||||
}),
|
||||
BaseGitHubConnectionSchema.extend({
|
||||
method: z.literal(GitHubConnectionMethod.OAuth),
|
||||
credentials: GitHubConnectionOAuthOutputCredentialsSchema.omit({ accessToken: true })
|
||||
})
|
||||
]);
|
||||
|
||||
export const GitHubConnectionListItemSchema = z.object({
|
||||
name: z.literal("GitHub"),
|
||||
app: z.literal(AppConnection.GitHub),
|
||||
// the below is preferable but currently breaks mintlify
|
||||
// methods: z.tuple([z.literal(GitHubConnectionMethod.GitHubApp), z.literal(GitHubConnectionMethod.OAuth)]),
|
||||
methods: z.nativeEnum(GitHubConnectionMethod).array(),
|
||||
oauthClientId: z.string().optional(),
|
||||
appClientSlug: z.string().optional()
|
||||
});
|
||||
+9
-3
@@ -3,12 +3,18 @@ import { z } from "zod";
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { CreateGitHubConnectionSchema, GitHubAppConnectionSchema } from "./github-connection-schemas";
|
||||
|
||||
export type TGitHubConnectionConfig = DiscriminativePick<TGitHubConnectionInput, "method" | "app" | "credentials">;
|
||||
import {
|
||||
CreateGitHubConnectionSchema,
|
||||
GitHubAppConnectionSchema,
|
||||
ValidateGitHubConnectionCredentialsSchema
|
||||
} from "./github-connection-schemas";
|
||||
|
||||
export type TGitHubConnection = z.infer<typeof GitHubAppConnectionSchema>;
|
||||
|
||||
export type TGitHubConnectionInput = z.infer<typeof CreateGitHubConnectionSchema> & {
|
||||
app: AppConnection.GitHub;
|
||||
};
|
||||
|
||||
export type TValidateGitHubConnectionCredentials = typeof ValidateGitHubConnectionCredentialsSchema;
|
||||
|
||||
export type TGitHubConnectionConfig = DiscriminativePick<TGitHubConnectionInput, "method" | "app" | "credentials">;
|
||||
Reference in New Issue
Block a user