Add PR suggestions

This commit is contained in:
carlosmonastyrski
2025-05-06 09:45:44 -03:00
parent 7bbe971a55
commit 2c40507f81
19 changed files with 520 additions and 411 deletions

View File

@@ -21,7 +21,7 @@
}, },
"author": "Infisical Inc, <daniel@infisical.com>", "author": "Infisical Inc, <daniel@infisical.com>",
"license": "ISC", "license": "ISC",
"description": "The Infisical SDK provides a convenient way to interact with the Infisical API.", "description": "The Infisical SDK provides a convenient way to programmatically interact with the Infisical API.",
"devDependencies": { "devDependencies": {
"@types/node": "^22.5.1", "@types/node": "^22.5.1",
"tsc": "^2.0.4", "tsc": "^2.0.4",

View File

@@ -30,6 +30,7 @@ export class ApiClient {
if (!config._retryCount) config._retryCount = 0; if (!config._retryCount) config._retryCount = 0;
// handle rate limits and network errors
if ( if (
(error.response?.status === 429 || (error.response?.status === 429 ||
error.response?.status === undefined) && error.response?.status === undefined) &&

View File

@@ -9,11 +9,7 @@ export class EnvironmentsApi {
): Promise<CreateEnvironmentResponse> { ): Promise<CreateEnvironmentResponse> {
return this.apiClient.post<CreateEnvironmentResponse>( return this.apiClient.post<CreateEnvironmentResponse>(
`/api/v1/workspace/${data.projectId}/environments`, `/api/v1/workspace/${data.projectId}/environments`,
{ data
name: data.name,
slug: data.slug,
position: data.position,
}
); );
} }
} }

View File

@@ -21,11 +21,7 @@ export class ProjectsApi {
): Promise<InviteMembersResponse> { ): Promise<InviteMembersResponse> {
return this.apiClient.post<InviteMembersResponse>( return this.apiClient.post<InviteMembersResponse>(
`/api/v2/workspace/${data.projectId}/memberships`, `/api/v2/workspace/${data.projectId}/memberships`,
{ data
emails: data.emails,
usernames: data.usernames,
roleSlugs: data.roleSlugs,
}
); );
} }
} }

View File

@@ -25,7 +25,7 @@ export class SecretsApi {
const { secretName, ...queryParams } = params; const { secretName, ...queryParams } = params;
return this.apiClient.get<GetSecretResponse>( return this.apiClient.get<GetSecretResponse>(
`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`, `/api/v3/secrets/raw/${encodeURIComponent(secretName)}`,
{ params: queryParams } { params }
); );
} }

View File

@@ -87,3 +87,43 @@ export interface RenewLeaseRequest {
export interface RenewLeaseResponse { export interface RenewLeaseResponse {
lease: Lease; lease: Lease;
} }
export type CreateDynamicSecretOptions = {
provider: TDynamicSecretProvider;
defaultTTL: string;
maxTTL: string;
name: string;
projectSlug: string;
environmentSlug: string;
path?: string;
metadata?: Record<string, any>;
};
export type DeleteDynamicSecretOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
};
export type CreateDynamicSecretLeaseOptions = {
dynamicSecretName: string;
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
};
export type DeleteDynamicSecretLeaseOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
};
export type RenewDynamicSecretLeaseOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
};

View File

@@ -16,5 +16,14 @@ export interface CreateEnvironmentRequest {
} }
export type CreateEnvironmentResponse = { export type CreateEnvironmentResponse = {
message: string;
workspace: string;
environment: Environment; environment: Environment;
}; };
export type CreateEnvironmentOptions = {
name: string;
projectId: string;
slug: string;
position?: number;
};

View File

@@ -1,12 +1,14 @@
export interface Folder { export interface Folder {
id: string; id: string;
name: string; name: string;
path: string; envId: string;
workspaceId: string;
environment: string;
description?: string; description?: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
parentId?: string;
isReserved?: boolean;
lastSecretModified?: string;
version?: number;
} }
export interface CreateFolderRequest { export interface CreateFolderRequest {
@@ -33,3 +35,18 @@ export interface ListFoldersResponse {
folders: Folder[]; folders: Folder[];
} }
export type CreateFolderOptions = {
name: string;
path: string;
projectId: string;
environment: string;
description?: string;
};
export type ListFoldersOptions = {
environment: string;
projectId: string;
path?: string;
recursive?: boolean;
lastSecretModified?: string;
};

View File

@@ -41,3 +41,19 @@ export interface Membership {
export interface InviteMembersResponse { export interface InviteMembersResponse {
memberships: Membership[]; memberships: Membership[];
} }
export type CreateProjectOptions = {
projectName: string;
type: string;
projectDescription?: string;
slug?: string;
template?: string;
kmsKeyId?: string;
};
export type InviteMemberToProjectOptions = {
projectId: string;
emails?: string[];
usernames?: string[];
roleSlugs?: string[];
};

View File

@@ -1,3 +1,5 @@
type SecretType = "shared" | "personal";
export interface Secret { export interface Secret {
id: string; id: string;
workspaceId: string; workspaceId: string;
@@ -6,7 +8,21 @@ export interface Secret {
secretValue: string; secretValue: string;
secretComment?: string; secretComment?: string;
secretPath?: string; secretPath?: string;
type: "shared" | "personal"; secretValueHidden: boolean;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean;
folderId?: string;
actor?: {
actorId?: string;
name?: string;
actorType?: string;
membershipId?: string;
}
isRotatedSecret: boolean;
rotationId?: string;
secretMetadata?: Record<string, any>;
type: SecretType;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
version: number; version: number;
@@ -29,6 +45,8 @@ export interface ListSecretsResponse {
imports?: Array<{ imports?: Array<{
secretPath: string; secretPath: string;
secrets: Secret[]; secrets: Secret[];
folderId?: string;
environment: string;
}>; }>;
} }
@@ -39,7 +57,7 @@ export interface GetSecretRequest {
expandSecretReferences?: string; expandSecretReferences?: string;
includeImports?: string; includeImports?: string;
secretPath?: string; secretPath?: string;
type?: "shared" | "personal"; type?: SecretType;
version?: number; version?: number;
viewSecretValue?: string; viewSecretValue?: string;
} }
@@ -58,7 +76,7 @@ export interface CreateSecretRequest {
secretReminderRepeatDays?: number; secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean; skipMultilineEncoding?: boolean;
tagIds?: string[]; tagIds?: string[];
type?: "shared" | "personal"; type?: SecretType;
} }
export interface UpdateSecretRequest { export interface UpdateSecretRequest {
@@ -72,7 +90,7 @@ export interface UpdateSecretRequest {
secretReminderRepeatDays?: number; secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean; skipMultilineEncoding?: boolean;
tagIds?: string[]; tagIds?: string[];
type?: "shared" | "personal"; type?: SecretType;
metadata?: Record<string, any>; metadata?: Record<string, any>;
} }
@@ -80,5 +98,58 @@ export interface DeleteSecretRequest {
workspaceId: string; workspaceId: string;
environment: string; environment: string;
secretPath?: string; secretPath?: string;
type?: "shared" | "personal"; type?: SecretType;
} }
export type ListSecretsOptions = {
environment: string;
projectId: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
recursive?: boolean;
secretPath?: string;
tagSlugs?: string[];
viewSecretValue?: boolean;
};
export type GetSecretOptions = {
environment: string;
secretName: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
secretPath?: string;
type?: SecretType;
version?: number;
projectId: string;
viewSecretValue?: boolean;
};
export type BaseSecretOptions = {
environment: string;
projectId: string;
secretComment?: string;
secretPath?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean;
tagIds?: string[];
type?: SecretType;
metadata?: Record<string, any>;
secretMetadata?: Record<string, any>[];
};
export type UpdateSecretOptions = {
secretValue?: string;
newSecretName?: string;
} & BaseSecretOptions;
export type CreateSecretOptions = {
secretValue: string;
} & BaseSecretOptions;
export type DeleteSecretOptions = {
environment: string;
projectId: string;
secretPath?: string;
type?: SecretType;
};

View File

@@ -27,19 +27,11 @@ export const renewToken = async (apiClient: AuthApi, token?: string) => {
}; };
export default class AuthClient { export default class AuthClient {
#sdkAuthenticator: AuthenticatorFunction;
#apiClient: AuthApi;
#accessToken?: string;
constructor( constructor(
authenticator: AuthenticatorFunction, private sdkAuthenticator: AuthenticatorFunction,
apiInstance: AuthApi, private apiClient: AuthApi,
accessToken?: string private _accessToken?: string
) { ) {}
this.#sdkAuthenticator = authenticator;
this.#apiClient = apiInstance;
this.#accessToken = accessToken;
}
awsIamAuth = { awsIamAuth = {
login: async (options?: AwsAuthLoginOptions) => { login: async (options?: AwsAuthLoginOptions) => {
@@ -53,7 +45,7 @@ export default class AuthClient {
} }
const iamRequest = await performAwsIamLogin(await getAwsRegion()); const iamRequest = await performAwsIamLogin(await getAwsRegion());
const res = await this.#apiClient.awsIamAuthLogin({ const res = await this.apiClient.awsIamAuthLogin({
iamHttpRequestMethod: iamRequest.iamHttpRequestMethod, iamHttpRequestMethod: iamRequest.iamHttpRequestMethod,
iamRequestBody: Buffer.from(iamRequest.iamRequestBody).toString( iamRequestBody: Buffer.from(iamRequest.iamRequestBody).toString(
"base64" "base64"
@@ -64,7 +56,7 @@ export default class AuthClient {
identityId, identityId,
}); });
return this.#sdkAuthenticator(res.accessToken); return this.sdkAuthenticator(res.accessToken);
} catch (err) { } catch (err) {
throw newInfisicalError(err); throw newInfisicalError(err);
} }
@@ -72,10 +64,10 @@ export default class AuthClient {
renew: async () => { renew: async () => {
try { try {
const refreshedToken = await renewToken( const refreshedToken = await renewToken(
this.#apiClient, this.apiClient,
this.#accessToken this._accessToken
); );
return this.#sdkAuthenticator(refreshedToken.accessToken); return this.sdkAuthenticator(refreshedToken.accessToken);
} catch (err) { } catch (err) {
throw newInfisicalError(err); throw newInfisicalError(err);
} }
@@ -85,8 +77,8 @@ export default class AuthClient {
universalAuth = { universalAuth = {
login: async (options: UniversalAuthLoginRequest) => { login: async (options: UniversalAuthLoginRequest) => {
try { try {
const res = await this.#apiClient.universalAuthLogin(options); const res = await this.apiClient.universalAuthLogin(options);
return this.#sdkAuthenticator(res.accessToken); return this.sdkAuthenticator(res.accessToken);
} catch (err) { } catch (err) {
throw newInfisicalError(err); throw newInfisicalError(err);
} }
@@ -94,10 +86,10 @@ export default class AuthClient {
renew: async () => { renew: async () => {
try { try {
const refreshedToken = await renewToken( const refreshedToken = await renewToken(
this.#apiClient, this.apiClient,
this.#accessToken this._accessToken
); );
return this.#sdkAuthenticator(refreshedToken.accessToken); return this.sdkAuthenticator(refreshedToken.accessToken);
} catch (err) { } catch (err) {
throw newInfisicalError(err); throw newInfisicalError(err);
} }
@@ -105,6 +97,6 @@ export default class AuthClient {
}; };
accessToken = (token: string) => { accessToken = (token: string) => {
return this.#sdkAuthenticator(token); return this.sdkAuthenticator(token);
}; };
} }

View File

@@ -1,46 +1,13 @@
import { DynamicSecretsApi } from "../api/endpoints/dynamic-secrets"; import { DynamicSecretsApi } from "../api/endpoints/dynamic-secrets";
import { TDynamicSecretProvider } from "./schemas/dynamic-secrets"; import { TDynamicSecretProvider } from "./schemas/dynamic-secrets";
import { newInfisicalError } from "./errors"; import { newInfisicalError } from "./errors";
import {
export type CreateDynamicSecretOptions = { CreateDynamicSecretOptions,
provider: TDynamicSecretProvider; DeleteDynamicSecretOptions,
defaultTTL: string; CreateDynamicSecretLeaseOptions,
maxTTL: string; DeleteDynamicSecretLeaseOptions,
name: string; RenewDynamicSecretLeaseOptions,
projectSlug: string; } from "../api/types/dynamic-secrets";
environmentSlug: string;
path?: string;
metadata?: Record<string, any>;
};
export type DeleteDynamicSecretOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
};
export type CreateDynamicSecretLeaseOptions = {
dynamicSecretName: string;
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
};
export type DeleteDynamicSecretLeaseOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
};
export type RenewDynamicSecretLeaseOptions = {
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
};
export default class DynamicSecretsClient { export default class DynamicSecretsClient {
constructor(private apiClient: DynamicSecretsApi) {} constructor(private apiClient: DynamicSecretsApi) {}

View File

@@ -1,12 +1,6 @@
import { EnvironmentsApi } from "../api/endpoints/environments"; import { EnvironmentsApi } from "../api/endpoints/environments";
import { newInfisicalError } from "./errors"; import { newInfisicalError } from "./errors";
import { CreateEnvironmentOptions } from "../api/types/environments";
export type CreateEnvironmentOptions = {
name: string;
projectId: string;
slug: string;
position?: number;
};
export default class EnvironmentsClient { export default class EnvironmentsClient {
constructor(private apiClient: EnvironmentsApi) {} constructor(private apiClient: EnvironmentsApi) {}

View File

@@ -1,3 +1,5 @@
import axios from "axios";
export class InfisicalSDKError extends Error { export class InfisicalSDKError extends Error {
constructor(message: string) { constructor(message: string) {
super(message); super(message);
@@ -22,7 +24,7 @@ export class InfisicalSDKRequestError extends Error {
} }
export const newInfisicalError = (error: any) => { export const newInfisicalError = (error: any) => {
if (error?.isAxiosError) { if (axios.isAxiosError(error)) {
const data = error?.response?.data; const data = error?.response?.data;
if (data?.message) { if (data?.message) {
@@ -39,6 +41,7 @@ export const newInfisicalError = (error: any) => {
} else if (error.message) { } else if (error.message) {
return new InfisicalSDKError(error.message); return new InfisicalSDKError(error.message);
} else if (error.code) { } else if (error.code) {
// If theres no message but a code is present, it's likely to be an aggregation error. This is not specific to Axios, but it falls under the AxiosError type
return new InfisicalSDKError(error.code); return new InfisicalSDKError(error.code);
} else { } else {
return new InfisicalSDKError("Request failed with unknown error"); return new InfisicalSDKError("Request failed with unknown error");

View File

@@ -1,21 +1,6 @@
import { FoldersApi } from "../api/endpoints/folders"; import { FoldersApi } from "../api/endpoints/folders";
import { newInfisicalError } from "./errors"; import { newInfisicalError } from "./errors";
import { CreateFolderOptions, ListFoldersOptions } from "../api/types/folders";
export type CreateFolderOptions = {
name: string;
path: string;
projectId: string;
environment: string;
description?: string;
};
export type ListFoldersOptions = {
environment: string;
projectId: string;
path?: string;
recursive?: boolean;
lastSecretModified?: string;
};
export default class FoldersClient { export default class FoldersClient {
constructor(private apiClient: FoldersApi) {} constructor(private apiClient: FoldersApi) {}

View File

@@ -1,21 +1,6 @@
import { ProjectsApi } from "../api/endpoints/projects"; import { ProjectsApi } from "../api/endpoints/projects";
import { newInfisicalError } from "./errors"; import { newInfisicalError } from "./errors";
import { CreateProjectOptions, InviteMemberToProjectOptions } from "../api/types/projects";
export type CreateProjectOptions = {
projectName: string;
type: string;
projectDescription?: string;
slug?: string;
template?: string;
kmsKeyId?: string;
};
export type InviteMemberToProjectOptions = {
projectId: string;
emails?: string[];
usernames?: string[];
roleSlugs?: string[];
};
export default class ProjectsClient { export default class ProjectsClient {
constructor(private apiClient: ProjectsApi) {} constructor(private apiClient: ProjectsApi) {}

View File

@@ -5,30 +5,45 @@ export enum SqlProviders {
MySQL = "mysql2", MySQL = "mysql2",
Oracle = "oracledb", Oracle = "oracledb",
MsSQL = "mssql", MsSQL = "mssql",
SapAse = "sap-ase" SapAse = "sap-ase",
} }
export enum ElasticSearchAuthTypes { export enum ElasticSearchAuthTypes {
User = "user", User = "user",
ApiKey = "api-key" ApiKey = "api-key",
} }
export enum LdapCredentialType { export enum LdapCredentialType {
Dynamic = "dynamic", Dynamic = "dynamic",
Static = "static" Static = "static",
} }
export enum TotpConfigType { export enum TotpConfigType {
URL = "url", URL = "url",
MANUAL = "manual" MANUAL = "manual",
} }
export enum TotpAlgorithm { export enum TotpAlgorithm {
SHA1 = "sha1", SHA1 = "sha1",
SHA256 = "sha256", SHA256 = "sha256",
SHA512 = "sha512" SHA512 = "sha512",
} }
const passwordRequirementsSchema = z.object({
length: z
.number()
.min(1, { message: "Password length must be at least 1" })
.max(250, { message: "Password length must be at most 250" }),
required: z.object({
minUppercase: z.number().min(0).optional(),
minLowercase: z.number().min(0).optional(),
minDigits: z.number().min(0).optional(),
minSymbols: z.number().min(0).optional(),
}),
allowedSymbols: z.string().optional(),
});
const DynamicSecretRedisDBSchema = z.object({ const DynamicSecretRedisDBSchema = z.object({
host: z.string().trim().toLowerCase(), host: z.string().trim().toLowerCase(),
port: z.number(), port: z.number(),
@@ -37,7 +52,7 @@ const DynamicSecretRedisDBSchema = z.object({
creationStatement: z.string().trim(), creationStatement: z.string().trim(),
revocationStatement: z.string().trim(), revocationStatement: z.string().trim(),
renewStatement: z.string().trim().optional(), renewStatement: z.string().trim().optional(),
ca: z.string().optional() ca: z.string().optional(),
}); });
const DynamicSecretAwsElastiCacheSchema = z.object({ const DynamicSecretAwsElastiCacheSchema = z.object({
@@ -48,7 +63,7 @@ const DynamicSecretAwsElastiCacheSchema = z.object({
region: z.string().trim(), region: z.string().trim(),
creationStatement: z.string().trim(), creationStatement: z.string().trim(),
revocationStatement: z.string().trim(), revocationStatement: z.string().trim(),
ca: z.string().optional() ca: z.string().optional(),
}); });
const DynamicSecretElasticSearchSchema = z.object({ const DynamicSecretElasticSearchSchema = z.object({
@@ -61,16 +76,16 @@ const DynamicSecretElasticSearchSchema = z.object({
z.object({ z.object({
type: z.literal(ElasticSearchAuthTypes.User), type: z.literal(ElasticSearchAuthTypes.User),
username: z.string().trim(), username: z.string().trim(),
password: z.string().trim() password: z.string().trim(),
}), }),
z.object({ z.object({
type: z.literal(ElasticSearchAuthTypes.ApiKey), type: z.literal(ElasticSearchAuthTypes.ApiKey),
apiKey: z.string().trim(), apiKey: z.string().trim(),
apiKeyId: z.string().trim() apiKeyId: z.string().trim(),
}) }),
]), ]),
ca: z.string().optional() ca: z.string().optional(),
}); });
const DynamicSecretRabbitMqSchema = z.object({ const DynamicSecretRabbitMqSchema = z.object({
@@ -88,9 +103,9 @@ const DynamicSecretRabbitMqSchema = z.object({
permissions: z.object({ permissions: z.object({
read: z.string().trim().min(1), read: z.string().trim().min(1),
write: z.string().trim().min(1), write: z.string().trim().min(1),
configure: z.string().trim().min(1) configure: z.string().trim().min(1),
}) }),
}) }),
}); });
const DynamicSecretSqlDBSchema = z.object({ const DynamicSecretSqlDBSchema = z.object({
@@ -103,7 +118,8 @@ const DynamicSecretSqlDBSchema = z.object({
creationStatement: z.string().trim(), creationStatement: z.string().trim(),
revocationStatement: z.string().trim(), revocationStatement: z.string().trim(),
renewStatement: z.string().trim().optional(), renewStatement: z.string().trim().optional(),
ca: z.string().optional() ca: z.string().optional(),
passwordRequirements: passwordRequirementsSchema.optional(),
}); });
const DynamicSecretCassandraSchema = z.object({ const DynamicSecretCassandraSchema = z.object({
@@ -116,7 +132,7 @@ const DynamicSecretCassandraSchema = z.object({
creationStatement: z.string().trim(), creationStatement: z.string().trim(),
revocationStatement: z.string().trim(), revocationStatement: z.string().trim(),
renewStatement: z.string().trim().optional(), renewStatement: z.string().trim().optional(),
ca: z.string().optional() ca: z.string().optional(),
}); });
const DynamicSecretSapAseSchema = z.object({ const DynamicSecretSapAseSchema = z.object({
@@ -126,7 +142,7 @@ const DynamicSecretSapAseSchema = z.object({
username: z.string().trim(), username: z.string().trim(),
password: z.string().trim(), password: z.string().trim(),
creationStatement: z.string().trim(), creationStatement: z.string().trim(),
revocationStatement: z.string().trim() revocationStatement: z.string().trim(),
}); });
const DynamicSecretAwsIamSchema = z.object({ const DynamicSecretAwsIamSchema = z.object({
@@ -137,23 +153,43 @@ const DynamicSecretAwsIamSchema = z.object({
permissionBoundaryPolicyArn: z.string().trim().optional(), permissionBoundaryPolicyArn: z.string().trim().optional(),
policyDocument: z.string().trim().optional(), policyDocument: z.string().trim().optional(),
userGroups: z.string().trim().optional(), userGroups: z.string().trim().optional(),
policyArns: z.string().trim().optional() policyArns: z.string().trim().optional(),
}); });
const DynamicSecretMongoAtlasSchema = z.object({ const DynamicSecretMongoAtlasSchema = z.object({
adminPublicKey: z.string().trim().min(1).describe("Admin user public api key"), adminPublicKey: z
adminPrivateKey: z.string().trim().min(1).describe("Admin user private api key"), .string()
groupId: z.string().trim().min(1).describe("Unique 24-hexadecimal digit string that identifies your project. This is same as project id"), .trim()
.min(1)
.describe("Admin user public api key"),
adminPrivateKey: z
.string()
.trim()
.min(1)
.describe("Admin user private api key"),
groupId: z
.string()
.trim()
.min(1)
.describe(
"Unique 24-hexadecimal digit string that identifies your project. This is same as project id"
),
roles: z roles: z
.object({ .object({
collectionName: z.string().optional().describe("Collection on which this role applies."), collectionName: z
databaseName: z.string().min(1).describe("Database to which the user is granted access privileges."), .string()
.optional()
.describe("Collection on which this role applies."),
databaseName: z
.string()
.min(1)
.describe("Database to which the user is granted access privileges."),
roleName: z roleName: z
.string() .string()
.min(1) .min(1)
.describe( .describe(
' Enum: "atlasAdmin" "backup" "clusterMonitor" "dbAdmin" "dbAdminAnyDatabase" "enableSharding" "read" "readAnyDatabase" "readWrite" "readWriteAnyDatabase" "<a custom role name>".Human-readable label that identifies a group of privileges assigned to a database user. This value can either be a built-in role or a custom role.' ' Enum: "atlasAdmin" "backup" "clusterMonitor" "dbAdmin" "dbAdminAnyDatabase" "enableSharding" "read" "readAnyDatabase" "readWrite" "readWriteAnyDatabase" "<a custom role name>".Human-readable label that identifies a group of privileges assigned to a database user. This value can either be a built-in role or a custom role.'
) ),
}) })
.array() .array()
.min(1), .min(1),
@@ -162,10 +198,17 @@ const DynamicSecretMongoAtlasSchema = z.object({
name: z name: z
.string() .string()
.min(1) .min(1)
.describe("Human-readable label that identifies the cluster or MongoDB Atlas Data Lake that this database user can access."), .describe(
type: z.string().min(1).describe("Category of resource that this database user can access. Enum: CLUSTER, DATA_LAKE, STREAM") "Human-readable label that identifies the cluster or MongoDB Atlas Data Lake that this database user can access."
),
type: z
.string()
.min(1)
.describe(
"Category of resource that this database user can access. Enum: CLUSTER, DATA_LAKE, STREAM"
),
}) })
.array() .array(),
}); });
const DynamicSecretMongoDBSchema = z.object({ const DynamicSecretMongoDBSchema = z.object({
@@ -181,7 +224,7 @@ const DynamicSecretMongoDBSchema = z.object({
.min(1) .min(1)
.describe( .describe(
'Enum: "atlasAdmin" "backup" "clusterMonitor" "dbAdmin" "dbAdminAnyDatabase" "enableSharding" "read" "readAnyDatabase" "readWrite" "readWriteAnyDatabase" "<a custom role name>".Human-readable label that identifies a group of privileges assigned to a database user. This value can either be a built-in role or a custom role.' 'Enum: "atlasAdmin" "backup" "clusterMonitor" "dbAdmin" "dbAdminAnyDatabase" "enableSharding" "read" "readAnyDatabase" "readWrite" "readWriteAnyDatabase" "<a custom role name>".Human-readable label that identifies a group of privileges assigned to a database user. This value can either be a built-in role or a custom role.'
) ),
}); });
const DynamicSecretSapHanaSchema = z.object({ const DynamicSecretSapHanaSchema = z.object({
@@ -192,7 +235,7 @@ const DynamicSecretSapHanaSchema = z.object({
creationStatement: z.string().trim(), creationStatement: z.string().trim(),
revocationStatement: z.string().trim(), revocationStatement: z.string().trim(),
renewStatement: z.string().trim().optional(), renewStatement: z.string().trim().optional(),
ca: z.string().optional() ca: z.string().optional(),
}); });
const DynamicSecretSnowflakeSchema = z.object({ const DynamicSecretSnowflakeSchema = z.object({
@@ -202,7 +245,7 @@ const DynamicSecretSnowflakeSchema = z.object({
password: z.string().trim().min(1), password: z.string().trim().min(1),
creationStatement: z.string().trim().min(1), creationStatement: z.string().trim().min(1),
revocationStatement: z.string().trim().min(1), revocationStatement: z.string().trim().min(1),
renewStatement: z.string().trim().optional() renewStatement: z.string().trim().optional(),
}); });
const AzureEntraIDSchema = z.object({ const AzureEntraIDSchema = z.object({
@@ -210,7 +253,7 @@ const AzureEntraIDSchema = z.object({
userId: z.string().trim().min(1), userId: z.string().trim().min(1),
email: z.string().trim().min(1), email: z.string().trim().min(1),
applicationId: z.string().trim().min(1), applicationId: z.string().trim().min(1),
clientSecret: z.string().trim().min(1) clientSecret: z.string().trim().min(1),
}); });
const LdapSchema = z.union([ const LdapSchema = z.union([
@@ -219,10 +262,13 @@ const LdapSchema = z.union([
binddn: z.string().trim().min(1), binddn: z.string().trim().min(1),
bindpass: z.string().trim().min(1), bindpass: z.string().trim().min(1),
ca: z.string().optional(), ca: z.string().optional(),
credentialType: z.literal(LdapCredentialType.Dynamic).optional().default(LdapCredentialType.Dynamic), credentialType: z
.literal(LdapCredentialType.Dynamic)
.optional()
.default(LdapCredentialType.Dynamic),
creationLdif: z.string().min(1), creationLdif: z.string().min(1),
revocationLdif: z.string().min(1), revocationLdif: z.string().min(1),
rollbackLdif: z.string().optional() rollbackLdif: z.string().optional(),
}), }),
z.object({ z.object({
url: z.string().trim().min(1), url: z.string().trim().min(1),
@@ -230,8 +276,8 @@ const LdapSchema = z.union([
bindpass: z.string().trim().min(1), bindpass: z.string().trim().min(1),
ca: z.string().optional(), ca: z.string().optional(),
credentialType: z.literal(LdapCredentialType.Static), credentialType: z.literal(LdapCredentialType.Static),
rotationLdif: z.string().min(1) rotationLdif: z.string().min(1),
}) }),
]); ]);
const DynamicSecretTotpSchema = z.discriminatedUnion("configType", [ const DynamicSecretTotpSchema = z.discriminatedUnion("configType", [
@@ -242,12 +288,12 @@ const DynamicSecretTotpSchema = z.discriminatedUnion("configType", [
.url() .url()
.trim() .trim()
.min(1) .min(1)
.refine(val => { .refine((val) => {
const urlObj = new URL(val); const urlObj = new URL(val);
const secret = urlObj.searchParams.get("secret"); const secret = urlObj.searchParams.get("secret");
return Boolean(secret); return Boolean(secret);
}, "OTP URL must contain secret field") }, "OTP URL must contain secret field"),
}), }),
z.object({ z.object({
configType: z.literal(TotpConfigType.MANUAL), configType: z.literal(TotpConfigType.MANUAL),
@@ -255,11 +301,11 @@ const DynamicSecretTotpSchema = z.discriminatedUnion("configType", [
.string() .string()
.trim() .trim()
.min(1) .min(1)
.transform(val => val.replace(/\s+/g, "")), .transform((val) => val.replace(/\s+/g, "")),
period: z.number().optional(), period: z.number().optional(),
algorithm: z.nativeEnum(TotpAlgorithm).optional(), algorithm: z.nativeEnum(TotpAlgorithm).optional(),
digits: z.number().optional() digits: z.number().optional(),
}) }),
]); ]);
export enum DynamicSecretProviders { export enum DynamicSecretProviders {
@@ -277,25 +323,72 @@ export enum DynamicSecretProviders {
SapHana = "sap-hana", SapHana = "sap-hana",
Snowflake = "snowflake", Snowflake = "snowflake",
Totp = "totp", Totp = "totp",
SapAse = "sap-ase" SapAse = "sap-ase",
} }
const DynamicSecretProviderSchema = z.discriminatedUnion("type", [ const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal(DynamicSecretProviders.SqlDatabase), inputs: DynamicSecretSqlDBSchema }), z.object({
z.object({ type: z.literal(DynamicSecretProviders.Cassandra), inputs: DynamicSecretCassandraSchema }), type: z.literal(DynamicSecretProviders.SqlDatabase),
z.object({ type: z.literal(DynamicSecretProviders.SapAse), inputs: DynamicSecretSapAseSchema }), inputs: DynamicSecretSqlDBSchema,
z.object({ type: z.literal(DynamicSecretProviders.AwsIam), inputs: DynamicSecretAwsIamSchema }), }),
z.object({ type: z.literal(DynamicSecretProviders.Redis), inputs: DynamicSecretRedisDBSchema }), z.object({
z.object({ type: z.literal(DynamicSecretProviders.SapHana), inputs: DynamicSecretSapHanaSchema }), type: z.literal(DynamicSecretProviders.Cassandra),
z.object({ type: z.literal(DynamicSecretProviders.AwsElastiCache), inputs: DynamicSecretAwsElastiCacheSchema }), inputs: DynamicSecretCassandraSchema,
z.object({ type: z.literal(DynamicSecretProviders.MongoAtlas), inputs: DynamicSecretMongoAtlasSchema }), }),
z.object({ type: z.literal(DynamicSecretProviders.ElasticSearch), inputs: DynamicSecretElasticSearchSchema }), z.object({
z.object({ type: z.literal(DynamicSecretProviders.MongoDB), inputs: DynamicSecretMongoDBSchema }), type: z.literal(DynamicSecretProviders.SapAse),
z.object({ type: z.literal(DynamicSecretProviders.RabbitMq), inputs: DynamicSecretRabbitMqSchema }), inputs: DynamicSecretSapAseSchema,
z.object({ type: z.literal(DynamicSecretProviders.AzureEntraID), inputs: AzureEntraIDSchema }), }),
z.object({ type: z.literal(DynamicSecretProviders.Ldap), inputs: LdapSchema }), z.object({
z.object({ type: z.literal(DynamicSecretProviders.Snowflake), inputs: DynamicSecretSnowflakeSchema }), type: z.literal(DynamicSecretProviders.AwsIam),
z.object({ type: z.literal(DynamicSecretProviders.Totp), inputs: DynamicSecretTotpSchema }) inputs: DynamicSecretAwsIamSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.Redis),
inputs: DynamicSecretRedisDBSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.SapHana),
inputs: DynamicSecretSapHanaSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.AwsElastiCache),
inputs: DynamicSecretAwsElastiCacheSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.MongoAtlas),
inputs: DynamicSecretMongoAtlasSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.ElasticSearch),
inputs: DynamicSecretElasticSearchSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.MongoDB),
inputs: DynamicSecretMongoDBSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.RabbitMq),
inputs: DynamicSecretRabbitMqSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.AzureEntraID),
inputs: AzureEntraIDSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.Ldap),
inputs: LdapSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.Snowflake),
inputs: DynamicSecretSnowflakeSchema,
}),
z.object({
type: z.literal(DynamicSecretProviders.Totp),
inputs: DynamicSecretTotpSchema,
}),
]); ]);
export type TDynamicSecretProvider = z.infer<typeof DynamicSecretProviderSchema>; export type TDynamicSecretProvider = z.infer<
typeof DynamicSecretProviderSchema
>;

View File

@@ -1,61 +1,7 @@
import { SecretsApi } from "../api/endpoints/secrets"; import { SecretsApi } from "../api/endpoints/secrets";
import { Secret } from "../api/types"; import { Secret } from "../api/types";
import { newInfisicalError } from "./errors"; import { newInfisicalError } from "./errors";
import { ListSecretsOptions, GetSecretOptions, UpdateSecretOptions, CreateSecretOptions, DeleteSecretOptions } from "../api/types/secrets";
type SecretType = "shared" | "personal";
type ListSecretsOptions = {
environment: string;
projectId: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
recursive?: boolean;
secretPath?: string;
tagSlugs?: string[];
viewSecretValue?: boolean;
};
type GetSecretOptions = {
environment: string;
secretName: string;
expandSecretReferences?: boolean;
includeImports?: boolean;
secretPath?: string;
type?: SecretType;
version?: number;
projectId: string;
viewSecretValue?: boolean;
};
type BaseSecretOptions = {
environment: string;
projectId: string;
secretComment?: string;
secretPath?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean;
tagIds?: string[];
type?: SecretType;
metadata?: Record<string, any>;
secretMetadata?: Record<string, any>[];
};
export type UpdateSecretOptions = {
secretValue?: string;
newSecretName?: string;
} & BaseSecretOptions;
export type CreateSecretOptions = {
secretValue: string;
} & BaseSecretOptions;
export type DeleteSecretOptions = {
environment: string;
projectId: string;
secretPath?: string;
type?: SecretType;
};
const convertBool = (value?: boolean) => (value ? "true" : "false"); const convertBool = (value?: boolean) => (value ? "true" : "false");
@@ -100,6 +46,11 @@ export default class SecretsClient {
if (imports) { if (imports) {
for (const imp of imports) { for (const imp of imports) {
for (const importedSecret of imp.secrets) { for (const importedSecret of imp.secrets) {
// CASE: We need to ensure that the imported values don't override the "base" secrets.
// Priority order is:
// Local/Preset variables -> Actual secrets -> Imported secrets (high->low)
// Check if the secret already exists in the secrets list
if (!secrets.find((s) => s.secretKey === importedSecret.secretKey)) { if (!secrets.find((s) => s.secretKey === importedSecret.secretKey)) {
secrets.push({ secrets.push({
...importedSecret, ...importedSecret,

View File

@@ -97,10 +97,3 @@ export {
TDynamicSecretProvider, TDynamicSecretProvider,
DynamicSecretProviders, DynamicSecretProviders,
} from "./custom/schemas"; } from "./custom/schemas";
// Export domain-specific types
export * from "./custom/secrets";
export * from "./custom/dynamic-secrets";
export * from "./custom/environments";
export * from "./custom/projects";
export * from "./custom/folders";