Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions backend/src/ee/routes/v1/dynamic-secret-lease-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { z } from "zod";

import { DynamicSecretLeasesSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { DynamicSecretLeaseResultSchema } from "@app/ee/services/dynamic-secret/providers/models";
import { ApiDocsTags, DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn";
import { ms } from "@app/lib/ms";
Expand Down Expand Up @@ -42,24 +43,25 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
config: z.any().optional()
}),
response: {
200: z.object({
lease: DynamicSecretLeasesSchema,
dynamicSecret: SanitizedDynamicSecretSchema,
data: z.unknown()
})
200: DynamicSecretLeaseResultSchema.and(
z.object({
lease: DynamicSecretLeasesSchema,
dynamicSecret: SanitizedDynamicSecretSchema
})
)
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { data, lease, dynamicSecret, projectId, environment, secretPath } =
await server.services.dynamicSecretLease.create({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.body.dynamicSecretName,
...req.body
});
const leaseResult = await server.services.dynamicSecretLease.create({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
name: req.body.dynamicSecretName,
...req.body
});
const { lease, dynamicSecret, projectId, environment, secretPath } = leaseResult;

await server.services.telemetry
.sendPostHogEvents({
Expand Down Expand Up @@ -96,7 +98,7 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
}
});

return { lease, data, dynamicSecret };
return leaseResult;
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type ActorIdentityAttributes = {
name: string;
};

export type TDynamicSecretKubernetesLeaseConfig = {
namespace?: string;
};

export type TDynamicSecretSshLeaseConfig = {
principals?: string[];
};

export type TDynamicSecretLeaseConfig = TDynamicSecretKubernetesLeaseConfig & TDynamicSecretSshLeaseConfig;
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import { SecretValidationRuleType } from "@app/services/secret-validation-rule/s
import { TUserDALFactory } from "@app/services/user/user-dal";

import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal";
import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models";
import {
DynamicSecretLeaseResultSchema,
DynamicSecretProviders,
TDynamicProviderFns
} from "../dynamic-secret/providers/models";
import { toSafeUsername } from "../dynamic-secret/providers/templateUtils";
import { TDynamicSecretLeaseDALFactory } from "./dynamic-secret-lease-dal";
import { TDynamicSecretLeaseQueueServiceFactory } from "./dynamic-secret-lease-queue";
Expand Down Expand Up @@ -208,10 +212,13 @@ export const dynamicSecretLeaseServiceFactory = ({
});

await dynamicSecretQueueService.setLeaseRevocation(dynamicSecretLease.id, dynamicSecretCfg.id, expireAt);

const leaseResult = DynamicSecretLeaseResultSchema.parse({ type: dynamicSecretCfg.type, data });

return {
...leaseResult,
lease: dynamicSecretLease,
dynamicSecret: dynamicSecretCfg,
data,
projectId,
environment: environmentSlug,
secretPath: path
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { TDynamicSecretLeases } from "@app/db/schemas";
import { TDynamicSecretWithMetadata, TProjectPermission } from "@app/lib/types";

import { TDynamicSecretLeaseResult } from "../dynamic-secret/providers/models";
import {
ActorIdentityAttributes,
TDynamicSecretKubernetesLeaseConfig,
TDynamicSecretLeaseConfig,
TDynamicSecretSshLeaseConfig
} from "./dynamic-secret-lease-config-types";

export type {
ActorIdentityAttributes,
TDynamicSecretKubernetesLeaseConfig,
TDynamicSecretLeaseConfig,
TDynamicSecretSshLeaseConfig
};

export enum DynamicSecretLeaseStatus {
FailedDeletion = "Failed to delete"
}

export type ActorIdentityAttributes = {
name: string;
};

export type TCreateDynamicSecretLeaseDTO = {
name: string;
path: string;
Expand Down Expand Up @@ -48,25 +59,16 @@ export type TRenewDynamicSecretLeaseDTO = {
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;

export type TDynamicSecretKubernetesLeaseConfig = {
namespace?: string;
};

export type TDynamicSecretSshLeaseConfig = {
principals?: string[];
};

export type TDynamicSecretLeaseConfig = TDynamicSecretKubernetesLeaseConfig & TDynamicSecretSshLeaseConfig;

export type TDynamicSecretLeaseServiceFactory = {
create: (arg: TCreateDynamicSecretLeaseDTO) => Promise<{
lease: TDynamicSecretLeases;
dynamicSecret: TDynamicSecretWithMetadata;
data: unknown;
projectId: string;
environment: string;
secretPath: string;
}>;
create: (arg: TCreateDynamicSecretLeaseDTO) => Promise<
TDynamicSecretLeaseResult & {
lease: TDynamicSecretLeases;
dynamicSecret: TDynamicSecretWithMetadata;
projectId: string;
environment: string;
secretPath: string;
}
>;
listLeases: (arg: TListDynamicSecretLeasesDTO) => Promise<{
leases: TDynamicSecretLeases[];
dynamicSecret: TDynamicSecretWithMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { sanitizeString } from "@app/lib/fn";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";

import { ActorIdentityAttributes } from "../../dynamic-secret-lease/dynamic-secret-lease-types";
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
import { DynamicSecretAwsElastiCacheSchema, TAwsElastiCacheLeaseData, TDynamicProviderFns } from "./models";
import { generateUsername } from "./templateUtils";

const CreateElastiCacheUserSchema = z.object({
Expand Down Expand Up @@ -141,7 +141,7 @@ const generatePassword = () => {
return customAlphabet(charset, 64)();
};

export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns<TAwsElastiCacheLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = DynamicSecretAwsElastiCacheSchema.parse(inputs);

Expand Down
17 changes: 14 additions & 3 deletions backend/src/ee/services/dynamic-secret/providers/aws-iam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid";

import { ActorIdentityAttributes } from "../../dynamic-secret-lease/dynamic-secret-lease-types";
import { AwsIamAuthType, AwsIamCredentialType, DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
import {
AwsIamAuthType,
AwsIamCredentialType,
DynamicSecretAwsIamSchema,
TAwsIamLeaseData,
TDynamicProviderFns
} from "./models";
import { generateUsername } from "./templateUtils";

// AWS STS duration constants (in seconds)
const AWS_STS_MIN_DURATION = 900;

export const AwsIamProvider = (): TDynamicProviderFns => {
export const AwsIamProvider = (): TDynamicProviderFns<TAwsIamLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretAwsIamSchema.parseAsync(inputs);
return providerInputs;
Expand Down Expand Up @@ -436,8 +442,13 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
UserName: createUserRes.User.UserName
})
);
if (!createAccessKeyRes.AccessKey)
if (
!createAccessKeyRes.AccessKey ||
!createAccessKeyRes.AccessKey.AccessKeyId ||
!createAccessKeyRes.AccessKey.SecretAccessKey
) {
throw new BadRequestError({ message: "Failed to create AWS IAM User access key" });
}

return {
entityId: username,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import { sanitizeString } from "@app/lib/fn";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";

import { ActorIdentityAttributes } from "../../dynamic-secret-lease/dynamic-secret-lease-types";
import { AwsMemoryDbAuthType, DynamicSecretAwsMemoryDbSchema, TDynamicProviderFns } from "./models";
import {
AwsMemoryDbAuthType,
DynamicSecretAwsMemoryDbSchema,
TAwsMemoryDbLeaseData,
TDynamicProviderFns
} from "./models";
import { generateUsername } from "./templateUtils";

const CreateMemoryDbUserSchema = z.object({
Expand Down Expand Up @@ -124,7 +129,7 @@ const $getAwsCredentials = (providerInputs: z.infer<typeof DynamicSecretAwsMemor
throw new BadRequestError({ message: "Unsupported MemoryDB auth type" });
};

export const AwsMemoryDbDatabaseProvider = (): TDynamicProviderFns => {
export const AwsMemoryDbDatabaseProvider = (): TDynamicProviderFns<TAwsMemoryDbLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = DynamicSecretAwsMemoryDbSchema.parse(inputs);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";

import { AzureEntraIDSchema, TDynamicProviderFns } from "./models";
import { AzureEntraIDSchema, TAzureEntraIDLeaseData, TDynamicProviderFns } from "./models";

const MSFT_GRAPH_API_URL = "https://graph.microsoft.com/v1.0/";
const MSFT_LOGIN_URL = "https://login.microsoftonline.com";
Expand All @@ -16,7 +16,7 @@ const generatePassword = () => {

type User = { name: string; id: string; email: string };

export const AzureEntraIDProvider = (): TDynamicProviderFns & {
export const AzureEntraIDProvider = (): TDynamicProviderFns<TAzureEntraIDLeaseData> & {
fetchAzureEntraIdUsers: (tenantId: string, applicationId: string, clientSecret: string) => Promise<User[]>;
} => {
const validateProviderInputs = async (inputs: unknown) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import { TGatewayServiceFactory } from "../../gateway/gateway-service";
import { TGatewayPoolServiceFactory } from "../../gateway-pool/gateway-pool-service";
import { TGatewayV2ServiceFactory } from "../../gateway-v2/gateway-v2-service";
import { verifyHostInputValidity } from "../dynamic-secret-fns";
import { DynamicSecretAzureSqlDBSchema, PasswordRequirements, SqlProviders, TDynamicProviderFns } from "./models";
import {
DynamicSecretAzureSqlDBSchema,
PasswordRequirements,
SqlProviders,
TAzureSqlDatabaseLeaseData,
TDynamicProviderFns
} from "./models";
import { generateUsername } from "./templateUtils";

const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
Expand Down Expand Up @@ -116,7 +122,7 @@ export const AzureSqlDatabaseProvider = ({
gatewayService,
gatewayV2Service,
gatewayPoolService
}: TAzureSqlDatabaseProviderDTO): TDynamicProviderFns => {
}: TAzureSqlDatabaseProviderDTO): TDynamicProviderFns<TAzureSqlDatabaseLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretAzureSqlDBSchema.parseAsync(inputs);

Expand Down
4 changes: 2 additions & 2 deletions backend/src/ee/services/dynamic-secret/providers/cassandra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars

import { ActorIdentityAttributes } from "../../dynamic-secret-lease/dynamic-secret-lease-types";
import { verifyHostInputValidity } from "../dynamic-secret-fns";
import { DynamicSecretCassandraSchema, TDynamicProviderFns } from "./models";
import { DynamicSecretCassandraSchema, TCassandraLeaseData, TDynamicProviderFns } from "./models";
import { generateUsername } from "./templateUtils";

const generatePassword = (size = 48) => {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*";
return customAlphabet(charset, 48)(size);
};

export const CassandraProvider = (): TDynamicProviderFns => {
export const CassandraProvider = (): TDynamicProviderFns<TCassandraLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretCassandraSchema.parseAsync(inputs);
await Promise.all(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import { TGatewayServiceFactory } from "../../gateway/gateway-service";
import { TGatewayPoolServiceFactory } from "../../gateway-pool/gateway-pool-service";
import { TGatewayV2ServiceFactory } from "../../gateway-v2/gateway-v2-service";
import { verifyHostInputValidity } from "../dynamic-secret-fns";
import { DynamicSecretClickhouseSchema, PasswordRequirements, TDynamicProviderFns } from "./models";
import {
DynamicSecretClickhouseSchema,
PasswordRequirements,
TClickhouseLeaseData,
TDynamicProviderFns
} from "./models";
import { generateUsername } from "./templateUtils";

const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
Expand Down Expand Up @@ -108,7 +113,7 @@ export const ClickhouseProvider = ({
gatewayService,
gatewayV2Service,
gatewayPoolService
}: TClickhouseProviderDTO): TDynamicProviderFns => {
}: TClickhouseProviderDTO): TDynamicProviderFns<TClickhouseLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretClickhouseSchema.parseAsync(inputs);

Expand Down
4 changes: 2 additions & 2 deletions backend/src/ee/services/dynamic-secret/providers/couchbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator/validate-url";

import { ActorIdentityAttributes } from "../../dynamic-secret-lease/dynamic-secret-lease-types";
import { DynamicSecretCouchbaseSchema, PasswordRequirements, TDynamicProviderFns } from "./models";
import { DynamicSecretCouchbaseSchema, PasswordRequirements, TCouchbaseLeaseData, TDynamicProviderFns } from "./models";
import { generateUsername } from "./templateUtils";

type TCreateCouchbaseUser = {
Expand Down Expand Up @@ -183,7 +183,7 @@ const couchbaseApiRequest = async (
}
};

export const CouchbaseProvider = (): TDynamicProviderFns => {
export const CouchbaseProvider = (): TDynamicProviderFns<TCouchbaseLeaseData> => {
const validateProviderInputs = async (inputs: object) => {
const providerInputs = DynamicSecretCouchbaseSchema.parse(inputs);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ import { sanitizeString } from "@app/lib/fn";

import { ActorIdentityAttributes } from "../../dynamic-secret-lease/dynamic-secret-lease-types";
import { verifyHostInputValidity } from "../dynamic-secret-fns";
import { DynamicSecretElasticSearchSchema, ElasticSearchAuthTypes, TDynamicProviderFns } from "./models";
import {
DynamicSecretElasticSearchSchema,
ElasticSearchAuthTypes,
TDynamicProviderFns,
TElasticSearchLeaseData
} from "./models";
import { generateUsername } from "./templateUtils";

const generatePassword = () => {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*";
return customAlphabet(charset, 64)();
};

export const ElasticSearchProvider = (): TDynamicProviderFns => {
export const ElasticSearchProvider = (): TDynamicProviderFns<TElasticSearchLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretElasticSearchSchema.parseAsync(inputs);
await verifyHostInputValidity({ host: providerInputs.host, isDynamicSecret: true });
Expand Down
4 changes: 2 additions & 2 deletions backend/src/ee/services/dynamic-secret/providers/gcp-iam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { buildGcpSourceCredential } from "@app/services/app-connection/gcp/gcp-connection-fns";

import { DynamicSecretGcpIamSchema, TDynamicProviderFns } from "./models";
import { DynamicSecretGcpIamSchema, TDynamicProviderFns, TGcpIamLeaseData } from "./models";

export const GcpIamProvider = (): TDynamicProviderFns => {
export const GcpIamProvider = (): TDynamicProviderFns<TGcpIamLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretGcpIamSchema.parseAsync(inputs);
return providerInputs;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/ee/services/dynamic-secret/providers/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";

import { DynamicSecretGithubSchema, TDynamicProviderFns } from "./models";
import { DynamicSecretGithubSchema, TDynamicProviderFns, TGithubLeaseData } from "./models";

interface GitHubInstallationTokenResponse {
token: string;
Expand All @@ -23,7 +23,7 @@ interface TGithubProviderInputs {
privateKey: string;
}

export const GithubProvider = (): TDynamicProviderFns => {
export const GithubProvider = (): TDynamicProviderFns<TGithubLeaseData> => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretGithubSchema.parseAsync(inputs);
return providerInputs;
Expand Down
Loading
Loading