Compare commits
4 Commits
misc/updat
...
daniel/cli
Author | SHA1 | Date | |
---|---|---|---|
22f32e060b | |||
b4f26aac25 | |||
b634a6c371 | |||
080ae5ce6f |
@ -1,21 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const hasColumn = await knex.schema.hasColumn(TableName.DynamicSecret, "usernameTemplate");
|
|
||||||
if (!hasColumn) {
|
|
||||||
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
|
||||||
t.string("usernameTemplate").nullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const hasColumn = await knex.schema.hasColumn(TableName.DynamicSecret, "usernameTemplate");
|
|
||||||
if (hasColumn) {
|
|
||||||
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
|
||||||
t.dropColumn("usernameTemplate");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,8 +28,7 @@ export const DynamicSecretsSchema = z.object({
|
|||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedInput: zodBuffer,
|
encryptedInput: zodBuffer,
|
||||||
projectGatewayId: z.string().uuid().nullable().optional(),
|
projectGatewayId: z.string().uuid().nullable().optional(),
|
||||||
gatewayId: z.string().uuid().nullable().optional(),
|
gatewayId: z.string().uuid().nullable().optional()
|
||||||
usernameTemplate: z.string().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
||||||
|
@ -6,8 +6,6 @@ import { ApiDocsTags, DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
|||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
import { isValidHandleBarTemplate } from "@app/lib/template/validate-handlebars";
|
|
||||||
import { CharacterType, characterValidator } from "@app/lib/validator/validate-string";
|
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@ -15,28 +13,6 @@ import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchema
|
|||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
|
|
||||||
const validateUsernameTemplateCharacters = characterValidator([
|
|
||||||
CharacterType.AlphaNumeric,
|
|
||||||
CharacterType.Underscore,
|
|
||||||
CharacterType.Hyphen,
|
|
||||||
CharacterType.OpenBrace,
|
|
||||||
CharacterType.CloseBrace,
|
|
||||||
CharacterType.CloseBracket,
|
|
||||||
CharacterType.OpenBracket,
|
|
||||||
CharacterType.Fullstop
|
|
||||||
]);
|
|
||||||
|
|
||||||
const userTemplateSchema = z
|
|
||||||
.string()
|
|
||||||
.trim()
|
|
||||||
.max(255)
|
|
||||||
.refine((el) => validateUsernameTemplateCharacters(el))
|
|
||||||
.refine((el) =>
|
|
||||||
isValidHandleBarTemplate(el, {
|
|
||||||
allowedExpressions: (val) => ["randomUsername", "unixTimestamp"].includes(val)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -76,8 +52,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
|
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
|
||||||
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
|
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
|
||||||
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name),
|
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name),
|
||||||
metadata: ResourceMetadataSchema.optional(),
|
metadata: ResourceMetadataSchema.optional()
|
||||||
usernameTemplate: userTemplateSchema.optional()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -98,6 +73,39 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/entra-id/users",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
tenantId: z.string().min(1).describe("The tenant ID of the Azure Entra ID"),
|
||||||
|
applicationId: z.string().min(1).describe("The application ID of the Azure Entra ID App Registration"),
|
||||||
|
clientSecret: z.string().min(1).describe("The client secret of the Azure Entra ID App Registration")
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z
|
||||||
|
.object({
|
||||||
|
name: z.string().min(1).describe("The name of the user"),
|
||||||
|
id: z.string().min(1).describe("The ID of the user"),
|
||||||
|
email: z.string().min(1).describe("The email of the user")
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const data = await server.services.dynamicSecret.fetchAzureEntraIdUsers({
|
||||||
|
tenantId: req.body.tenantId,
|
||||||
|
applicationId: req.body.applicationId,
|
||||||
|
clientSecret: req.body.clientSecret
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:name",
|
url: "/:name",
|
||||||
@ -142,8 +150,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
})
|
})
|
||||||
.nullable(),
|
.nullable(),
|
||||||
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional(),
|
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional(),
|
||||||
metadata: ResourceMetadataSchema.optional(),
|
metadata: ResourceMetadataSchema.optional()
|
||||||
usernameTemplate: userTemplateSchema.nullable().optional()
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@ -321,37 +328,4 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
return { leases };
|
return { leases };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "POST",
|
|
||||||
url: "/entra-id/users",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
body: z.object({
|
|
||||||
tenantId: z.string().min(1).describe("The tenant ID of the Azure Entra ID"),
|
|
||||||
applicationId: z.string().min(1).describe("The application ID of the Azure Entra ID App Registration"),
|
|
||||||
clientSecret: z.string().min(1).describe("The client secret of the Azure Entra ID App Registration")
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z
|
|
||||||
.object({
|
|
||||||
name: z.string().min(1).describe("The name of the user"),
|
|
||||||
id: z.string().min(1).describe("The ID of the user"),
|
|
||||||
email: z.string().min(1).describe("The email of the user")
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const data = await server.services.dynamicSecret.fetchAzureEntraIdUsers({
|
|
||||||
tenantId: req.body.tenantId,
|
|
||||||
applicationId: req.body.applicationId,
|
|
||||||
clientSecret: req.body.clientSecret
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -132,11 +132,7 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
|
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
result = await selectedProvider.create({
|
result = await selectedProvider.create(decryptedStoredInput, expireAt.getTime());
|
||||||
inputs: decryptedStoredInput,
|
|
||||||
expireAt: expireAt.getTime(),
|
|
||||||
usernameTemplate: dynamicSecretCfg.usernameTemplate
|
|
||||||
});
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
|
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
|
||||||
throw new BadRequestError({ message: error.sqlMessage as string });
|
throw new BadRequestError({ message: error.sqlMessage as string });
|
||||||
|
@ -11,8 +11,6 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
|
|||||||
|
|
||||||
if (appCfg.isDevelopmentMode) return [host];
|
if (appCfg.isDevelopmentMode) return [host];
|
||||||
|
|
||||||
if (isGateway) return [host];
|
|
||||||
|
|
||||||
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
|
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
|
||||||
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
|
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
|
||||||
getDbConnectionHost(appCfg.REDIS_URL),
|
getDbConnectionHost(appCfg.REDIS_URL),
|
||||||
@ -60,7 +58,7 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
|
if (!isGateway && !(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
|
||||||
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
|
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
|
||||||
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
|
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
metadata,
|
metadata
|
||||||
usernameTemplate
|
|
||||||
}: TCreateDynamicSecretDTO) => {
|
}: TCreateDynamicSecretDTO) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
@ -164,8 +163,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name,
|
name,
|
||||||
gatewayId: selectedGatewayId,
|
gatewayId: selectedGatewayId
|
||||||
usernameTemplate
|
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@ -201,8 +199,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
newName,
|
newName,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
metadata,
|
metadata
|
||||||
usernameTemplate
|
|
||||||
}: TUpdateDynamicSecretDTO) => {
|
}: TUpdateDynamicSecretDTO) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
@ -314,8 +311,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
name: newName ?? name,
|
name: newName ?? name,
|
||||||
status: null,
|
status: null,
|
||||||
gatewayId: selectedGatewayId,
|
gatewayId: selectedGatewayId
|
||||||
usernameTemplate
|
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
@ -22,7 +22,6 @@ export type TCreateDynamicSecretDTO = {
|
|||||||
name: string;
|
name: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
metadata?: ResourceMetadataDTO;
|
metadata?: ResourceMetadataDTO;
|
||||||
usernameTemplate?: string | null;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateDynamicSecretDTO = {
|
export type TUpdateDynamicSecretDTO = {
|
||||||
@ -35,7 +34,6 @@ export type TUpdateDynamicSecretDTO = {
|
|||||||
inputs?: TProvider["inputs"];
|
inputs?: TProvider["inputs"];
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
metadata?: ResourceMetadataDTO;
|
metadata?: ResourceMetadataDTO;
|
||||||
usernameTemplate?: string | null;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeleteDynamicSecretDTO = {
|
export type TDeleteDynamicSecretDTO = {
|
||||||
|
@ -132,15 +132,9 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
|
||||||
const randomUsername = `inf-${customAlphabet(charset, 32)()}`;
|
return `inf-${customAlphabet(charset, 32)()}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||||
@ -174,14 +168,13 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
if (!(await validateConnection(providerInputs))) {
|
if (!(await validateConnection(providerInputs))) {
|
||||||
throw new BadRequestError({ message: "Failed to establish connection" });
|
throw new BadRequestError({ message: "Failed to establish connection" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const leaseUsername = generateUsername(usernameTemplate);
|
const leaseUsername = generateUsername();
|
||||||
const leasePassword = generatePassword();
|
const leasePassword = generatePassword();
|
||||||
const leaseExpiration = new Date(expireAt).toISOString();
|
const leaseExpiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import {
|
|||||||
PutUserPolicyCommand,
|
PutUserPolicyCommand,
|
||||||
RemoveUserFromGroupCommand
|
RemoveUserFromGroupCommand
|
||||||
} from "@aws-sdk/client-iam";
|
} from "@aws-sdk/client-iam";
|
||||||
import handlebars from "handlebars";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
@ -24,14 +23,8 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|||||||
|
|
||||||
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32);
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AwsIamProvider = (): TDynamicProviderFns => {
|
export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||||
@ -60,13 +53,11 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const { inputs, usernameTemplate } = data;
|
|
||||||
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
||||||
const createUserRes = await client.send(
|
const createUserRes = await client.send(
|
||||||
new CreateUserCommand({
|
new CreateUserCommand({
|
||||||
|
@ -55,7 +55,7 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
|
|||||||
return data.success;
|
return data.success;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async ({ inputs }: { inputs: unknown }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
|
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
@ -88,7 +88,7 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
|
|||||||
|
|
||||||
const revoke = async (inputs: unknown, entityId: string) => {
|
const revoke = async (inputs: unknown, entityId: string) => {
|
||||||
// Creates a new password
|
// Creates a new password
|
||||||
await create({ inputs });
|
await create(inputs);
|
||||||
return { entityId };
|
return { entityId };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,14 +14,8 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CassandraProvider = (): TDynamicProviderFns => {
|
export const CassandraProvider = (): TDynamicProviderFns => {
|
||||||
@ -75,12 +69,11 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const { keyspace } = providerInputs;
|
const { keyspace } = providerInputs;
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
|
import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
|
||||||
import handlebars from "handlebars";
|
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@ -13,14 +12,8 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||||
@ -71,12 +64,11 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
|||||||
return infoResponse;
|
return infoResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const { inputs, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const connection = await $getClient(providerInputs);
|
const connection = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
await connection.security.putUser({
|
await connection.security.putUser({
|
||||||
|
@ -116,7 +116,7 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async ({ inputs, expireAt }: { inputs: unknown; expireAt: number }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const tokenRequestCallback = async (host: string, port: number) => {
|
const tokenRequestCallback = async (host: string, port: number) => {
|
||||||
|
@ -22,14 +22,8 @@ const encodePassword = (password?: string) => {
|
|||||||
return base64Password;
|
return base64Password;
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(20);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateLDIF = ({
|
const generateLDIF = ({
|
||||||
@ -196,8 +190,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
return dnArray;
|
return dnArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const { inputs, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
@ -224,7 +217,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
|
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
|
||||||
|
|
||||||
|
@ -360,11 +360,7 @@ export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export type TDynamicProviderFns = {
|
export type TDynamicProviderFns = {
|
||||||
create: (arg: {
|
create: (inputs: unknown, expireAt: number) => Promise<{ entityId: string; data: unknown }>;
|
||||||
inputs: unknown;
|
|
||||||
expireAt: number;
|
|
||||||
usernameTemplate?: string | null;
|
|
||||||
}) => Promise<{ entityId: string; data: unknown }>;
|
|
||||||
validateConnection: (inputs: unknown) => Promise<boolean>;
|
validateConnection: (inputs: unknown) => Promise<boolean>;
|
||||||
validateProviderInputs: (inputs: object) => Promise<unknown>;
|
validateProviderInputs: (inputs: object) => Promise<unknown>;
|
||||||
revoke: (inputs: unknown, entityId: string) => Promise<{ entityId: string }>;
|
revoke: (inputs: unknown, entityId: string) => Promise<{ entityId: string }>;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
import handlebars from "handlebars";
|
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@ -13,14 +12,8 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32);
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||||
@ -64,12 +57,11 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
await client({
|
await client({
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import handlebars from "handlebars";
|
|
||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -13,14 +12,8 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32);
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MongoDBProvider = (): TDynamicProviderFns => {
|
export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||||
@ -60,12 +53,11 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const { inputs, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
const db = client.db(providerInputs.database);
|
const db = client.db(providerInputs.database);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import axios, { Axios } from "axios";
|
import axios, { Axios } from "axios";
|
||||||
import handlebars from "handlebars";
|
|
||||||
import https from "https";
|
import https from "https";
|
||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -15,14 +14,8 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type TCreateRabbitMQUser = {
|
type TCreateRabbitMQUser = {
|
||||||
@ -117,12 +110,11 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
|
|||||||
return infoResponse;
|
return infoResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const { inputs, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const connection = await $getClient(providerInputs);
|
const connection = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
await createRabbitMqUser({
|
await createRabbitMqUser({
|
||||||
|
@ -15,14 +15,8 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const executeTransactions = async (connection: Redis, commands: string[]): Promise<(string | null)[] | null> => {
|
const executeTransactions = async (connection: Redis, commands: string[]): Promise<(string | null)[] | null> => {
|
||||||
@ -121,12 +115,11 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
return pingResponse;
|
return pingResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const connection = await $getClient(providerInputs);
|
const connection = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
|
@ -15,14 +15,8 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = `inf_${alphaNumericNanoId(25)}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(25);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SapCommands {
|
enum SapCommands {
|
||||||
@ -87,12 +81,11 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown) => {
|
||||||
const { inputs, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = `inf_${generateUsername()}`;
|
||||||
const password = generatePassword();
|
const password = `${generatePassword()}`;
|
||||||
|
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
const masterClient = await $getClient(providerInputs, true);
|
const masterClient = await $getClient(providerInputs, true);
|
||||||
|
@ -21,14 +21,8 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return alphaNumericNanoId(32);
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SapHanaProvider = (): TDynamicProviderFns => {
|
export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||||
@ -97,11 +91,10 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
|||||||
return testResult;
|
return testResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
const expiration = new Date(expireAt).toISOString();
|
const expiration = new Date(expireAt).toISOString();
|
||||||
|
|
||||||
|
@ -17,14 +17,8 @@ const generatePassword = (size = 48) => {
|
|||||||
return customAlphabet(charset, 48)(size);
|
return customAlphabet(charset, 48)(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = () => {
|
||||||
const randomUsername = `infisical_${alphaNumericNanoId(32)}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
|
return `infisical_${alphaNumericNanoId(32)}`; // username must start with alpha character, hence prefix
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDaysToExpiry = (expiryDate: Date) => {
|
const getDaysToExpiry = (expiryDate: Date) => {
|
||||||
@ -88,13 +82,12 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
|||||||
return isValidConnection;
|
return isValidConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
|
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername();
|
||||||
const password = generatePassword();
|
const password = generatePassword();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -104,21 +104,11 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (provider: SqlProviders, usernameTemplate?: string | null) => {
|
const generateUsername = (provider: SqlProviders) => {
|
||||||
let randomUsername = "";
|
|
||||||
|
|
||||||
// For oracle, the client assumes everything is upper case when not using quotes around the password
|
// For oracle, the client assumes everything is upper case when not using quotes around the password
|
||||||
if (provider === SqlProviders.Oracle) {
|
if (provider === SqlProviders.Oracle) return alphaNumericNanoId(32).toUpperCase();
|
||||||
randomUsername = alphaNumericNanoId(32).toUpperCase();
|
|
||||||
} else {
|
|
||||||
randomUsername = alphaNumericNanoId(32);
|
|
||||||
}
|
|
||||||
if (!usernameTemplate) return randomUsername;
|
|
||||||
|
|
||||||
return handlebars.compile(usernameTemplate)({
|
return alphaNumericNanoId(32);
|
||||||
randomUsername,
|
|
||||||
unixTimestamp: Math.floor(Date.now() / 100)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type TSqlDatabaseProviderDTO = {
|
type TSqlDatabaseProviderDTO = {
|
||||||
@ -220,12 +210,9 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const { inputs, expireAt, usernameTemplate } = data;
|
|
||||||
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const username = generateUsername(providerInputs.client, usernameTemplate);
|
const username = generateUsername(providerInputs.client);
|
||||||
|
|
||||||
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({ ...providerInputs, port, host });
|
||||||
|
@ -44,7 +44,7 @@ const createQuicConnection = async (
|
|||||||
if (!certs || certs.length === 0) return quic.native.CryptoError.CertificateRequired;
|
if (!certs || certs.length === 0) return quic.native.CryptoError.CertificateRequired;
|
||||||
const serverCertificate = new crypto.X509Certificate(Buffer.from(certs[0]));
|
const serverCertificate = new crypto.X509Certificate(Buffer.from(certs[0]));
|
||||||
const caCertificate = new crypto.X509Certificate(tlsOptions.ca);
|
const caCertificate = new crypto.X509Certificate(tlsOptions.ca);
|
||||||
const isValidServerCertificate = serverCertificate.verify(caCertificate.publicKey);
|
const isValidServerCertificate = serverCertificate.checkIssued(caCertificate);
|
||||||
if (!isValidServerCertificate) return quic.native.CryptoError.BadCertificate;
|
if (!isValidServerCertificate) return quic.native.CryptoError.BadCertificate;
|
||||||
|
|
||||||
const subjectDetails = parseSubjectDetails(serverCertificate.subject);
|
const subjectDetails = parseSubjectDetails(serverCertificate.subject);
|
||||||
|
@ -19,15 +19,3 @@ export const validateHandlebarTemplate = (templateName: string, template: string
|
|||||||
throw new BadRequestError({ message: `Template sanitization failed: ${templateName}` });
|
throw new BadRequestError({ message: `Template sanitization failed: ${templateName}` });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isValidHandleBarTemplate = (template: string, dto: SanitizationArg) => {
|
|
||||||
const parsedAst = handlebars.parse(template);
|
|
||||||
return parsedAst.body.every((el) => {
|
|
||||||
if (el.type === "ContentStatement") return true;
|
|
||||||
if (el.type === "MustacheStatement" && "path" in el) {
|
|
||||||
const { path } = el as { type: "MustacheStatement"; path: { type: "PathExpression"; original: string } };
|
|
||||||
if (path.type === "PathExpression" && dto?.allowedExpressions?.(path.original)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
@ -741,14 +741,12 @@ export const registerRoutes = async (
|
|||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
authService: loginService,
|
authService: loginService,
|
||||||
serverCfgDAL: superAdminDAL,
|
serverCfgDAL: superAdminDAL,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
orgService,
|
orgService,
|
||||||
keyStore,
|
keyStore,
|
||||||
orgDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
microsoftTeamsService,
|
microsoftTeamsService,
|
||||||
|
@ -235,9 +235,11 @@ export const SanitizedDynamicSecretSchema = DynamicSecretsSchema.omit({
|
|||||||
inputIV: true,
|
inputIV: true,
|
||||||
inputTag: true,
|
inputTag: true,
|
||||||
algorithm: true
|
algorithm: true
|
||||||
}).extend({
|
}).merge(
|
||||||
|
z.object({
|
||||||
metadata: ResourceMetadataSchema.optional()
|
metadata: ResourceMetadataSchema.optional()
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export const SanitizedAuditLogStreamSchema = z.object({
|
export const SanitizedAuditLogStreamSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
import DOMPurify from "isomorphic-dompurify";
|
import DOMPurify from "isomorphic-dompurify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import { IdentitiesSchema, OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
||||||
IdentitiesSchema,
|
|
||||||
OrganizationsSchema,
|
|
||||||
OrgMembershipsSchema,
|
|
||||||
SuperAdminSchema,
|
|
||||||
UsersSchema
|
|
||||||
} from "@app/db/schemas";
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@ -167,129 +161,6 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "GET",
|
|
||||||
url: "/organization-management/organizations",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
querystring: z.object({
|
|
||||||
searchTerm: z.string().default(""),
|
|
||||||
offset: z.coerce.number().default(0),
|
|
||||||
limit: z.coerce.number().max(100).default(20)
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
organizations: OrganizationsSchema.extend({
|
|
||||||
members: z
|
|
||||||
.object({
|
|
||||||
user: z.object({
|
|
||||||
id: z.string(),
|
|
||||||
email: z.string().nullish(),
|
|
||||||
username: z.string(),
|
|
||||||
firstName: z.string().nullish(),
|
|
||||||
lastName: z.string().nullish()
|
|
||||||
}),
|
|
||||||
membershipId: z.string(),
|
|
||||||
role: z.string(),
|
|
||||||
roleId: z.string().nullish()
|
|
||||||
})
|
|
||||||
.array(),
|
|
||||||
projects: z
|
|
||||||
.object({
|
|
||||||
name: z.string(),
|
|
||||||
id: z.string(),
|
|
||||||
slug: z.string(),
|
|
||||||
createdAt: z.date()
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
}).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: (req, res, done) => {
|
|
||||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
|
||||||
verifySuperAdmin(req, res, done);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
const organizations = await server.services.superAdmin.getOrganizations({
|
|
||||||
...req.query
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
organizations
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "DELETE",
|
|
||||||
url: "/organization-management/organizations/:organizationId/memberships/:membershipId",
|
|
||||||
config: {
|
|
||||||
rateLimit: writeLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
organizationId: z.string(),
|
|
||||||
membershipId: z.string()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
organizationMembership: OrgMembershipsSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: (req, res, done) => {
|
|
||||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
|
||||||
verifySuperAdmin(req, res, done);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
const organizationMembership = await server.services.superAdmin.deleteOrganizationMembership(
|
|
||||||
req.params.organizationId,
|
|
||||||
req.params.membershipId,
|
|
||||||
req.permission.id,
|
|
||||||
req.permission.type
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
organizationMembership
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "DELETE",
|
|
||||||
url: "/organization-management/organizations/:organizationId",
|
|
||||||
config: {
|
|
||||||
rateLimit: writeLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
organizationId: z.string()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
organization: OrganizationsSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: (req, res, done) => {
|
|
||||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
|
||||||
verifySuperAdmin(req, res, done);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
const organization = await server.services.superAdmin.deleteOrganization(req.params.organizationId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
organization
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/identity-management/identities",
|
url: "/identity-management/identities",
|
||||||
|
@ -196,10 +196,8 @@ export const orgAdminServiceFactory = ({
|
|||||||
.filter(
|
.filter(
|
||||||
(member) => member.roles.some((role) => role.role === ProjectMembershipRole.Admin) && member.userId !== actorId
|
(member) => member.roles.some((role) => role.role === ProjectMembershipRole.Admin) && member.userId !== actorId
|
||||||
)
|
)
|
||||||
.map((el) => el.user.email!)
|
.map((el) => el.user.email!);
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
if (filteredProjectMembers.length) {
|
|
||||||
await smtpService.sendMail({
|
await smtpService.sendMail({
|
||||||
template: SmtpTemplates.OrgAdminProjectDirectAccess,
|
template: SmtpTemplates.OrgAdminProjectDirectAccess,
|
||||||
recipients: filteredProjectMembers,
|
recipients: filteredProjectMembers,
|
||||||
@ -209,7 +207,6 @@ export const orgAdminServiceFactory = ({
|
|||||||
email: projectMembers.find((el) => el.userId === actorId)?.user?.username
|
email: projectMembers.find((el) => el.userId === actorId)?.user?.username
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return { isExistingMember: false, membership: updatedMembership };
|
return { isExistingMember: false, membership: updatedMembership };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import {
|
import {
|
||||||
OrganizationsSchema,
|
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
TableName,
|
TableName,
|
||||||
TOrganizations,
|
TOrganizations,
|
||||||
@ -13,15 +12,7 @@ import {
|
|||||||
TUserEncryptionKeys
|
TUserEncryptionKeys
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import {
|
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt, withTransaction } from "@app/lib/knex";
|
||||||
buildFindFilter,
|
|
||||||
ormify,
|
|
||||||
selectAllTableCols,
|
|
||||||
sqlNestRelationships,
|
|
||||||
TFindFilter,
|
|
||||||
TFindOpt,
|
|
||||||
withTransaction
|
|
||||||
} from "@app/lib/knex";
|
|
||||||
import { generateKnexQueryFromScim } from "@app/lib/knex/scim";
|
import { generateKnexQueryFromScim } from "@app/lib/knex/scim";
|
||||||
|
|
||||||
import { OrgAuthMethod } from "./org-types";
|
import { OrgAuthMethod } from "./org-types";
|
||||||
@ -31,110 +22,6 @@ export type TOrgDALFactory = ReturnType<typeof orgDALFactory>;
|
|||||||
export const orgDALFactory = (db: TDbClient) => {
|
export const orgDALFactory = (db: TDbClient) => {
|
||||||
const orgOrm = ormify(db, TableName.Organization);
|
const orgOrm = ormify(db, TableName.Organization);
|
||||||
|
|
||||||
const findOrganizationsByFilter = async ({
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
searchTerm,
|
|
||||||
sortBy
|
|
||||||
}: {
|
|
||||||
limit: number;
|
|
||||||
offset: number;
|
|
||||||
searchTerm: string;
|
|
||||||
sortBy?: keyof TOrganizations;
|
|
||||||
}) => {
|
|
||||||
try {
|
|
||||||
const query = db.replicaNode()(TableName.Organization);
|
|
||||||
|
|
||||||
// Build the subquery for limited organization IDs
|
|
||||||
const orgSubquery = db.replicaNode().select("id").from(TableName.Organization);
|
|
||||||
|
|
||||||
if (searchTerm) {
|
|
||||||
void orgSubquery.where((qb) => {
|
|
||||||
void qb.whereILike(`${TableName.Organization}.name`, `%${searchTerm}%`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sortBy) {
|
|
||||||
void orgSubquery.orderBy(sortBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void orgSubquery.limit(limit).offset(offset);
|
|
||||||
|
|
||||||
// Main query with joins, limited to the subquery results
|
|
||||||
const docs = await query
|
|
||||||
.whereIn(`${TableName.Organization}.id`, orgSubquery)
|
|
||||||
.leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
|
|
||||||
.leftJoin(TableName.OrgMembership, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`)
|
|
||||||
.leftJoin(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
|
||||||
.where((qb) => {
|
|
||||||
void qb.where(`${TableName.Users}.isGhost`, false).orWhereNull(`${TableName.Users}.id`);
|
|
||||||
})
|
|
||||||
.select(selectAllTableCols(TableName.Organization))
|
|
||||||
.select(db.ref("name").withSchema(TableName.Project).as("projectName"))
|
|
||||||
.select(db.ref("id").withSchema(TableName.Project).as("projectId"))
|
|
||||||
.select(db.ref("slug").withSchema(TableName.Project).as("projectSlug"))
|
|
||||||
.select(db.ref("createdAt").withSchema(TableName.Project).as("projectCreatedAt"))
|
|
||||||
.select(db.ref("email").withSchema(TableName.Users).as("userEmail"))
|
|
||||||
.select(db.ref("username").withSchema(TableName.Users).as("username"))
|
|
||||||
.select(db.ref("firstName").withSchema(TableName.Users).as("firstName"))
|
|
||||||
.select(db.ref("lastName").withSchema(TableName.Users).as("lastName"))
|
|
||||||
.select(db.ref("id").withSchema(TableName.Users).as("userId"))
|
|
||||||
.select(db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"))
|
|
||||||
.select(db.ref("role").withSchema(TableName.OrgMembership).as("orgMembershipRole"))
|
|
||||||
.select(db.ref("roleId").withSchema(TableName.OrgMembership).as("orgMembershipRoleId"))
|
|
||||||
.select(db.ref("name").withSchema(TableName.OrgRoles).as("orgMembershipRoleName"));
|
|
||||||
|
|
||||||
const formattedDocs = sqlNestRelationships({
|
|
||||||
data: docs,
|
|
||||||
key: "id",
|
|
||||||
parentMapper: (data) => OrganizationsSchema.parse(data),
|
|
||||||
childrenMapper: [
|
|
||||||
{
|
|
||||||
key: "projectId",
|
|
||||||
label: "projects" as const,
|
|
||||||
mapper: ({ projectId, projectName, projectSlug, projectCreatedAt }) => ({
|
|
||||||
id: projectId,
|
|
||||||
name: projectName,
|
|
||||||
slug: projectSlug,
|
|
||||||
createdAt: projectCreatedAt
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "userId",
|
|
||||||
label: "members" as const,
|
|
||||||
mapper: ({
|
|
||||||
userId,
|
|
||||||
userEmail,
|
|
||||||
username,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
orgMembershipId,
|
|
||||||
orgMembershipRole,
|
|
||||||
orgMembershipRoleName,
|
|
||||||
orgMembershipRoleId
|
|
||||||
}) => ({
|
|
||||||
user: {
|
|
||||||
id: userId,
|
|
||||||
email: userEmail,
|
|
||||||
username,
|
|
||||||
firstName,
|
|
||||||
lastName
|
|
||||||
},
|
|
||||||
membershipId: orgMembershipId,
|
|
||||||
role: orgMembershipRoleName || orgMembershipRole, // custom role name or pre-defined role name
|
|
||||||
roleId: orgMembershipRoleId
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
return formattedDocs;
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: "Find organizations by filter" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const findOrgById = async (orgId: string) => {
|
const findOrgById = async (orgId: string) => {
|
||||||
try {
|
try {
|
||||||
const org = (await db
|
const org = (await db
|
||||||
@ -620,7 +507,6 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
findOrgById,
|
findOrgById,
|
||||||
findOrgBySlug,
|
findOrgBySlug,
|
||||||
findAllOrgsByUserId,
|
findAllOrgsByUserId,
|
||||||
findOrganizationsByFilter,
|
|
||||||
ghostUserExists,
|
ghostUserExists,
|
||||||
findOrgMembersByUsername,
|
findOrgMembersByUsername,
|
||||||
findOrgMembersByRole,
|
findOrgMembersByRole,
|
||||||
|
@ -11,7 +11,7 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
|||||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
|
|
||||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||||
import { ActorType, AuthMethod, AuthTokenType } from "../auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
@ -21,9 +21,7 @@ import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
|||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { RootKeyEncryptionStrategy } from "../kms/kms-types";
|
import { RootKeyEncryptionStrategy } from "../kms/kms-types";
|
||||||
import { TMicrosoftTeamsServiceFactory } from "../microsoft-teams/microsoft-teams-service";
|
import { TMicrosoftTeamsServiceFactory } from "../microsoft-teams/microsoft-teams-service";
|
||||||
import { TOrgDALFactory } from "../org/org-dal";
|
|
||||||
import { TOrgServiceFactory } from "../org/org-service";
|
import { TOrgServiceFactory } from "../org/org-service";
|
||||||
import { TOrgMembershipDALFactory } from "../org-membership/org-membership-dal";
|
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
||||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||||
@ -35,8 +33,7 @@ import {
|
|||||||
TAdminBootstrapInstanceDTO,
|
TAdminBootstrapInstanceDTO,
|
||||||
TAdminGetIdentitiesDTO,
|
TAdminGetIdentitiesDTO,
|
||||||
TAdminGetUsersDTO,
|
TAdminGetUsersDTO,
|
||||||
TAdminSignUpDTO,
|
TAdminSignUpDTO
|
||||||
TGetOrganizationsDTO
|
|
||||||
} from "./super-admin-types";
|
} from "./super-admin-types";
|
||||||
|
|
||||||
type TSuperAdminServiceFactoryDep = {
|
type TSuperAdminServiceFactoryDep = {
|
||||||
@ -44,8 +41,6 @@ type TSuperAdminServiceFactoryDep = {
|
|||||||
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
||||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||||
orgDAL: TOrgDALFactory;
|
|
||||||
orgMembershipDAL: TOrgMembershipDALFactory;
|
|
||||||
serverCfgDAL: TSuperAdminDALFactory;
|
serverCfgDAL: TSuperAdminDALFactory;
|
||||||
userDAL: TUserDALFactory;
|
userDAL: TUserDALFactory;
|
||||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
||||||
@ -78,8 +73,6 @@ export const superAdminServiceFactory = ({
|
|||||||
serverCfgDAL,
|
serverCfgDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
orgDAL,
|
|
||||||
orgMembershipDAL,
|
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
authService,
|
authService,
|
||||||
orgService,
|
orgService,
|
||||||
@ -528,47 +521,6 @@ export const superAdminServiceFactory = ({
|
|||||||
return updatedUser;
|
return updatedUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrganizations = async ({ offset, limit, searchTerm }: TGetOrganizationsDTO) => {
|
|
||||||
const organizations = await orgDAL.findOrganizationsByFilter({
|
|
||||||
offset,
|
|
||||||
searchTerm,
|
|
||||||
sortBy: "name",
|
|
||||||
limit
|
|
||||||
});
|
|
||||||
return organizations;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteOrganization = async (organizationId: string) => {
|
|
||||||
const organization = await orgDAL.deleteById(organizationId);
|
|
||||||
return organization;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteOrganizationMembership = async (
|
|
||||||
organizationId: string,
|
|
||||||
membershipId: string,
|
|
||||||
actorId: string,
|
|
||||||
actorType: ActorType
|
|
||||||
) => {
|
|
||||||
if (actorType === ActorType.USER) {
|
|
||||||
const orgMembership = await orgMembershipDAL.findById(membershipId);
|
|
||||||
if (!orgMembership) {
|
|
||||||
throw new NotFoundError({ name: "Organization Membership", message: "Organization membership not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orgMembership.userId === actorId) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "You cannot remove yourself from the organization from the instance management panel."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [organizationMembership] = await orgMembershipDAL.delete({
|
|
||||||
orgId: organizationId,
|
|
||||||
id: membershipId
|
|
||||||
});
|
|
||||||
return organizationMembership;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||||
const identities = await identityDAL.getIdentitiesByFilter({
|
const identities = await identityDAL.getIdentitiesByFilter({
|
||||||
limit,
|
limit,
|
||||||
@ -711,9 +663,6 @@ export const superAdminServiceFactory = ({
|
|||||||
deleteIdentitySuperAdminAccess,
|
deleteIdentitySuperAdminAccess,
|
||||||
deleteUserSuperAdminAccess,
|
deleteUserSuperAdminAccess,
|
||||||
invalidateCache,
|
invalidateCache,
|
||||||
checkIfInvalidatingCache,
|
checkIfInvalidatingCache
|
||||||
getOrganizations,
|
|
||||||
deleteOrganization,
|
|
||||||
deleteOrganizationMembership
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -35,12 +35,6 @@ export type TAdminGetIdentitiesDTO = {
|
|||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetOrganizationsDTO = {
|
|
||||||
offset: number;
|
|
||||||
limit: number;
|
|
||||||
searchTerm: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum LoginMethod {
|
export enum LoginMethod {
|
||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
|
15
cli/go.mod
@ -5,8 +5,6 @@ go 1.23.0
|
|||||||
toolchain go1.23.5
|
toolchain go1.23.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BobuSumisu/aho-corasick v1.0.3
|
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0
|
|
||||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
|
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
|
||||||
github.com/charmbracelet/lipgloss v0.9.1
|
github.com/charmbracelet/lipgloss v0.9.1
|
||||||
github.com/creack/pty v1.1.21
|
github.com/creack/pty v1.1.21
|
||||||
@ -21,10 +19,10 @@ require (
|
|||||||
github.com/muesli/mango-cobra v1.2.0
|
github.com/muesli/mango-cobra v1.2.0
|
||||||
github.com/muesli/reflow v0.3.0
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/muesli/roff v0.1.0
|
github.com/muesli/roff v0.1.0
|
||||||
|
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9
|
||||||
github.com/pion/dtls/v3 v3.0.4
|
github.com/pion/dtls/v3 v3.0.4
|
||||||
github.com/pion/logging v0.2.3
|
github.com/pion/logging v0.2.3
|
||||||
github.com/pion/turn/v4 v4.0.0
|
github.com/pion/turn/v4 v4.0.0
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
|
||||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a
|
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a
|
||||||
github.com/quic-go/quic-go v0.50.0
|
github.com/quic-go/quic-go v0.50.0
|
||||||
github.com/rs/cors v1.11.0
|
github.com/rs/cors v1.11.0
|
||||||
@ -32,9 +30,7 @@ require (
|
|||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/viper v1.8.1
|
github.com/spf13/viper v1.8.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/wasilibs/go-re2 v1.10.0
|
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.36.0
|
||||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7
|
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.31.0
|
||||||
golang.org/x/term v0.30.0
|
golang.org/x/term v0.30.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
@ -47,8 +43,10 @@ require (
|
|||||||
cloud.google.com/go/compute/metadata v0.4.0 // indirect
|
cloud.google.com/go/compute/metadata v0.4.0 // indirect
|
||||||
cloud.google.com/go/iam v1.1.11 // indirect
|
cloud.google.com/go/iam v1.1.11 // indirect
|
||||||
dario.cat/mergo v1.0.1 // indirect
|
dario.cat/mergo v1.0.1 // indirect
|
||||||
|
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||||
|
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||||
github.com/alessio/shellescape v1.4.1 // indirect
|
github.com/alessio/shellescape v1.4.1 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
|
||||||
@ -67,7 +65,7 @@ require (
|
|||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/chzyer/readline v1.5.1 // indirect
|
github.com/chzyer/readline v1.5.1 // indirect
|
||||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
@ -107,15 +105,13 @@ require (
|
|||||||
github.com/pion/stun/v3 v3.0.0 // indirect
|
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/subosito/gotenv v1.2.0 // indirect
|
github.com/subosito/gotenv v1.2.0 // indirect
|
||||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
|
||||||
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
|
|
||||||
github.com/wlynxg/anet v0.0.5 // indirect
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||||
go.mongodb.org/mongo-driver v1.10.0 // indirect
|
go.mongodb.org/mongo-driver v1.10.0 // indirect
|
||||||
@ -126,6 +122,7 @@ require (
|
|||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 // indirect
|
||||||
golang.org/x/mod v0.23.0 // indirect
|
golang.org/x/mod v0.23.0 // indirect
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.35.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
|
26
cli/go.sum
@ -127,9 +127,8 @@ github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
|
|||||||
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
||||||
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||||
github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY=
|
github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY=
|
||||||
@ -148,11 +147,11 @@ github.com/fatih/semgroup v1.2.0 h1:h/OLXwEM+3NNyAdZEpMiH1OzfplU09i2qXPVThGZvyg=
|
|||||||
github.com/fatih/semgroup v1.2.0/go.mod h1:1KAD4iIYfXjE4U13B48VM4z9QUwV5Tt8O4rS879kgm8=
|
github.com/fatih/semgroup v1.2.0/go.mod h1:1KAD4iIYfXjE4U13B48VM4z9QUwV5Tt8O4rS879kgm8=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gitleaks/go-gitdiff v0.8.0 h1:7aExTZm+K/M/EQKOyYcub8rIAdWK6ONxPGuRzxmWW+0=
|
||||||
|
github.com/gitleaks/go-gitdiff v0.8.0/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA=
|
||||||
github.com/gitleaks/go-gitdiff v0.9.1 h1:ni6z6/3i9ODT685OLCTf+s/ERlWUNWQF4x1pvoNICw0=
|
github.com/gitleaks/go-gitdiff v0.9.1 h1:ni6z6/3i9ODT685OLCTf+s/ERlWUNWQF4x1pvoNICw0=
|
||||||
github.com/gitleaks/go-gitdiff v0.9.1/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA=
|
github.com/gitleaks/go-gitdiff v0.9.1/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
@ -308,8 +307,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
|||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@ -376,6 +373,8 @@ github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlR
|
|||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 h1:lL+y4Xv20pVlCGyLzNHRC0I0rIHhIL1lTvHizoS/dU8=
|
||||||
|
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw=
|
||||||
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
|
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
|
||||||
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
|
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
|
||||||
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
||||||
@ -386,15 +385,12 @@ github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
|||||||
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a h1:Ey0XWvrg6u6hyIn1Kd/jCCmL+bMv9El81tvuGBbxZGg=
|
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a h1:Ey0XWvrg6u6hyIn1Kd/jCCmL+bMv9El81tvuGBbxZGg=
|
||||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
|
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
|
||||||
@ -406,8 +402,6 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
|
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
|
||||||
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
@ -426,6 +420,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
|
|||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
|
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
@ -456,15 +451,9 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
|
||||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
|
||||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/wasilibs/go-re2 v1.10.0 h1:vQZEBYZOCA9jdBMmrO4+CvqyCj0x4OomXTJ4a5/urQ0=
|
|
||||||
github.com/wasilibs/go-re2 v1.10.0/go.mod h1:k+5XqO2bCJS+QpGOnqugyfwC04nw0jaglmjrrkG8U6o=
|
|
||||||
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4=
|
|
||||||
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY=
|
|
||||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
@ -672,7 +661,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
@ -12,6 +12,35 @@ import (
|
|||||||
|
|
||||||
const USER_AGENT = "cli"
|
const USER_AGENT = "cli"
|
||||||
|
|
||||||
|
const (
|
||||||
|
operationCallGetRawSecretsV3 = "CallGetRawSecretsV3"
|
||||||
|
operationCallGetEncryptedWorkspaceKey = "CallGetEncryptedWorkspaceKey"
|
||||||
|
operationCallGetServiceTokenDetails = "CallGetServiceTokenDetails"
|
||||||
|
operationCallLogin1V3 = "CallLogin1V3"
|
||||||
|
operationCallVerifyMfaToken = "CallVerifyMfaToken"
|
||||||
|
operationCallLogin2V3 = "CallLogin2V3"
|
||||||
|
operationCallGetAllOrganizations = "CallGetAllOrganizations"
|
||||||
|
operationCallSelectOrganization = "CallSelectOrganization"
|
||||||
|
operationCallGetAllWorkSpacesUserBelongsTo = "CallGetAllWorkSpacesUserBelongsTo"
|
||||||
|
operationCallGetProjectById = "CallGetProjectById"
|
||||||
|
operationCallIsAuthenticated = "CallIsAuthenticated"
|
||||||
|
operationCallGetNewAccessTokenWithRefreshToken = "CallGetNewAccessTokenWithRefreshToken"
|
||||||
|
operationCallGetFoldersV1 = "CallGetFoldersV1"
|
||||||
|
operationCallCreateFolderV1 = "CallCreateFolderV1"
|
||||||
|
operationCallDeleteFolderV1 = "CallDeleteFolderV1"
|
||||||
|
operationCallDeleteSecretsV3 = "CallDeleteSecretsV3"
|
||||||
|
operationCallCreateServiceToken = "CallCreateServiceToken"
|
||||||
|
operationCallUniversalAuthLogin = "CallUniversalAuthLogin"
|
||||||
|
operationCallMachineIdentityRefreshAccessToken = "CallMachineIdentityRefreshAccessToken"
|
||||||
|
operationCallFetchSingleSecretByName = "CallFetchSingleSecretByName"
|
||||||
|
operationCallCreateRawSecretsV3 = "CallCreateRawSecretsV3"
|
||||||
|
operationCallUpdateRawSecretsV3 = "CallUpdateRawSecretsV3"
|
||||||
|
operationCallRegisterGatewayIdentityV1 = "CallRegisterGatewayIdentityV1"
|
||||||
|
operationCallExchangeRelayCertV1 = "CallExchangeRelayCertV1"
|
||||||
|
operationCallGatewayHeartBeatV1 = "CallGatewayHeartBeatV1"
|
||||||
|
operationCallBootstrapInstance = "CallBootstrapInstance"
|
||||||
|
)
|
||||||
|
|
||||||
func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) {
|
func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) {
|
||||||
endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", config.INFISICAL_URL, request.WorkspaceId)
|
endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", config.INFISICAL_URL, request.WorkspaceId)
|
||||||
var result GetEncryptedWorkspaceKeyResponse
|
var result GetEncryptedWorkspaceKeyResponse
|
||||||
@ -22,11 +51,11 @@ func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncrypted
|
|||||||
Get(endpoint)
|
Get(endpoint)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unable to complete api request [err=%s]", err)
|
return GetEncryptedWorkspaceKeyResponse{}, NewGenericRequestError(operationCallGetEncryptedWorkspaceKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetEncryptedWorkspaceKeyResponse{}, fmt.Errorf("CallGetEncryptedWorkspaceKey: Unsuccessful response [%v %v] [status-code=%v]", response.Request.Method, response.Request.URL, response.StatusCode())
|
return GetEncryptedWorkspaceKeyResponse{}, NewAPIErrorWithResponse(operationCallGetEncryptedWorkspaceKey, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -41,11 +70,11 @@ func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDeta
|
|||||||
Get(fmt.Sprintf("%v/v2/service-token", config.INFISICAL_URL))
|
Get(fmt.Sprintf("%v/v2/service-token", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unable to complete api request [err=%s]", err)
|
return GetServiceTokenDetailsResponse{}, NewGenericRequestError(operationCallGetServiceTokenDetails, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unsuccessful response: [response=%s]", response)
|
return GetServiceTokenDetailsResponse{}, NewAPIErrorWithResponse(operationCallGetServiceTokenDetails, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenDetailsResponse, nil
|
return tokenDetailsResponse, nil
|
||||||
@ -61,11 +90,11 @@ func CallLogin1V2(httpClient *resty.Client, request GetLoginOneV2Request) (GetLo
|
|||||||
Post(fmt.Sprintf("%v/v3/auth/login1", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v3/auth/login1", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetLoginOneV2Response{}, fmt.Errorf("CallLogin1V3: Unable to complete api request [err=%s]", err)
|
return GetLoginOneV2Response{}, NewGenericRequestError(operationCallLogin1V3, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetLoginOneV2Response{}, fmt.Errorf("CallLogin1V3: Unsuccessful response: [response=%s]", response)
|
return GetLoginOneV2Response{}, NewAPIErrorWithResponse(operationCallLogin1V3, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return loginOneV2Response, nil
|
return loginOneV2Response, nil
|
||||||
@ -99,7 +128,7 @@ func CallVerifyMfaToken(httpClient *resty.Client, request VerifyMfaTokenRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("CallVerifyMfaToken: Unable to complete api request [err=%s]", err)
|
return nil, nil, NewGenericRequestError(operationCallVerifyMfaToken, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
@ -135,11 +164,11 @@ func CallLogin2V2(httpClient *resty.Client, request GetLoginTwoV2Request) (GetLo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V3: Unable to complete api request [err=%s]", err)
|
return GetLoginTwoV2Response{}, NewGenericRequestError(operationCallLogin2V3, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V3: Unsuccessful response: [response=%s]", response)
|
return GetLoginTwoV2Response{}, NewAPIErrorWithResponse(operationCallLogin2V3, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return loginTwoV2Response, nil
|
return loginTwoV2Response, nil
|
||||||
@ -154,11 +183,11 @@ func CallGetAllOrganizations(httpClient *resty.Client) (GetOrganizationsResponse
|
|||||||
Get(fmt.Sprintf("%v/v1/organization", config.INFISICAL_URL))
|
Get(fmt.Sprintf("%v/v1/organization", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetOrganizationsResponse{}, err
|
return GetOrganizationsResponse{}, NewGenericRequestError(operationCallGetAllOrganizations, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetOrganizationsResponse{}, fmt.Errorf("CallGetAllOrganizations: Unsuccessful response: [response=%v]", response)
|
return GetOrganizationsResponse{}, NewAPIErrorWithResponse(operationCallGetAllOrganizations, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return orgResponse, nil
|
return orgResponse, nil
|
||||||
@ -175,11 +204,11 @@ func CallSelectOrganization(httpClient *resty.Client, request SelectOrganization
|
|||||||
Post(fmt.Sprintf("%v/v3/auth/select-organization", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v3/auth/select-organization", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SelectOrganizationResponse{}, err
|
return SelectOrganizationResponse{}, NewGenericRequestError(operationCallSelectOrganization, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return SelectOrganizationResponse{}, fmt.Errorf("CallSelectOrganization: Unsuccessful response: [response=%v]", response)
|
return SelectOrganizationResponse{}, NewAPIErrorWithResponse(operationCallSelectOrganization, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectOrgResponse, nil
|
return selectOrgResponse, nil
|
||||||
@ -214,11 +243,11 @@ func CallGetProjectById(httpClient *resty.Client, id string) (Project, error) {
|
|||||||
Get(fmt.Sprintf("%v/v1/workspace/%s", config.INFISICAL_URL, id))
|
Get(fmt.Sprintf("%v/v1/workspace/%s", config.INFISICAL_URL, id))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Project{}, err
|
return Project{}, NewGenericRequestError(operationCallGetProjectById, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return Project{}, fmt.Errorf("CallGetProjectById: Unsuccessful response: [response=%v]", response)
|
return Project{}, NewAPIErrorWithResponse(operationCallGetProjectById, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectResponse.Project, nil
|
return projectResponse.Project, nil
|
||||||
@ -237,7 +266,7 @@ func CallIsAuthenticated(httpClient *resty.Client) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
log.Debug().Msgf("CallIsAuthenticated: Unsuccessful response: [response=%v]", response)
|
log.Debug().Msgf("%s: Unsuccessful response: [response=%v]", operationCallIsAuthenticated, response)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,11 +286,11 @@ func CallGetNewAccessTokenWithRefreshToken(httpClient *resty.Client, refreshToke
|
|||||||
Post(fmt.Sprintf("%v/v1/auth/token", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v1/auth/token", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetNewAccessTokenWithRefreshTokenResponse{}, err
|
return GetNewAccessTokenWithRefreshTokenResponse{}, NewGenericRequestError(operationCallGetNewAccessTokenWithRefreshToken, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetNewAccessTokenWithRefreshTokenResponse{}, fmt.Errorf("CallGetNewAccessTokenWithRefreshToken: Unsuccessful response: [response=%v]", response)
|
return GetNewAccessTokenWithRefreshTokenResponse{}, NewAPIErrorWithResponse(operationCallGetNewAccessTokenWithRefreshToken, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newAccessToken, nil
|
return newAccessToken, nil
|
||||||
@ -280,11 +309,11 @@ func CallGetFoldersV1(httpClient *resty.Client, request GetFoldersV1Request) (Ge
|
|||||||
response, err := httpRequest.Get(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL))
|
response, err := httpRequest.Get(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetFoldersV1Response{}, fmt.Errorf("CallGetFoldersV1: Unable to complete api request [err=%v]", err)
|
return GetFoldersV1Response{}, NewGenericRequestError(operationCallGetFoldersV1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetFoldersV1Response{}, fmt.Errorf("CallGetFoldersV1: Unsuccessful [response=%s]", response)
|
return GetFoldersV1Response{}, NewAPIErrorWithResponse(operationCallGetFoldersV1, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return foldersResponse, nil
|
return foldersResponse, nil
|
||||||
@ -300,11 +329,11 @@ func CallCreateFolderV1(httpClient *resty.Client, request CreateFolderV1Request)
|
|||||||
|
|
||||||
response, err := httpRequest.Post(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL))
|
response, err := httpRequest.Post(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CreateFolderV1Response{}, fmt.Errorf("CallCreateFolderV1: Unable to complete api request [err=%s]", err)
|
return CreateFolderV1Response{}, NewGenericRequestError(operationCallCreateFolderV1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return CreateFolderV1Response{}, fmt.Errorf("CallCreateFolderV1: Unsuccessful [response=%s]", response.String())
|
return CreateFolderV1Response{}, NewAPIErrorWithResponse(operationCallCreateFolderV1, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return folderResponse, nil
|
return folderResponse, nil
|
||||||
@ -321,11 +350,11 @@ func CallDeleteFolderV1(httpClient *resty.Client, request DeleteFolderV1Request)
|
|||||||
|
|
||||||
response, err := httpRequest.Delete(fmt.Sprintf("%v/v1/folders/%v", config.INFISICAL_URL, request.FolderName))
|
response, err := httpRequest.Delete(fmt.Sprintf("%v/v1/folders/%v", config.INFISICAL_URL, request.FolderName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DeleteFolderV1Response{}, fmt.Errorf("CallDeleteFolderV1: Unable to complete api request [err=%s]", err)
|
return DeleteFolderV1Response{}, NewGenericRequestError(operationCallDeleteFolderV1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return DeleteFolderV1Response{}, fmt.Errorf("CallDeleteFolderV1: Unsuccessful [response=%s]", response.String())
|
return DeleteFolderV1Response{}, NewAPIErrorWithResponse(operationCallDeleteFolderV1, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return folderResponse, nil
|
return folderResponse, nil
|
||||||
@ -342,11 +371,12 @@ func CallDeleteSecretsRawV3(httpClient *resty.Client, request DeleteSecretV3Requ
|
|||||||
Delete(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
Delete(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("CallDeleteSecretsV3: Unable to complete api request [err=%s]", err)
|
return NewGenericRequestError(operationCallDeleteSecretsV3, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return fmt.Errorf("CallDeleteSecretsV3: Unsuccessful response. Please make sure your secret path, workspace and environment name are all correct [response=%s]", response)
|
additionalContext := "Please make sure your secret path, workspace and environment name are all correct."
|
||||||
|
return NewAPIErrorWithResponse(operationCallDeleteSecretsV3, response, &additionalContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -362,11 +392,11 @@ func CallCreateServiceToken(httpClient *resty.Client, request CreateServiceToken
|
|||||||
Post(fmt.Sprintf("%v/v2/service-token/", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v2/service-token/", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CreateServiceTokenResponse{}, fmt.Errorf("CallCreateServiceToken: Unable to complete api request [err=%s]", err)
|
return CreateServiceTokenResponse{}, NewGenericRequestError(operationCallCreateServiceToken, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return CreateServiceTokenResponse{}, fmt.Errorf("CallCreateServiceToken: Unsuccessful response [%v %v] [status-code=%v]", response.Request.Method, response.Request.URL, response.StatusCode())
|
return CreateServiceTokenResponse{}, NewAPIErrorWithResponse(operationCallCreateServiceToken, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return createServiceTokenResponse, nil
|
return createServiceTokenResponse, nil
|
||||||
@ -382,11 +412,11 @@ func CallUniversalAuthLogin(httpClient *resty.Client, request UniversalAuthLogin
|
|||||||
Post(fmt.Sprintf("%v/v1/auth/universal-auth/login/", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v1/auth/universal-auth/login/", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UniversalAuthLoginResponse{}, fmt.Errorf("CallUniversalAuthLogin: Unable to complete api request [err=%s]", err)
|
return UniversalAuthLoginResponse{}, NewGenericRequestError(operationCallUniversalAuthLogin, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return UniversalAuthLoginResponse{}, fmt.Errorf("CallUniversalAuthLogin: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return UniversalAuthLoginResponse{}, NewAPIErrorWithResponse(operationCallUniversalAuthLogin, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return universalAuthLoginResponse, nil
|
return universalAuthLoginResponse, nil
|
||||||
@ -402,11 +432,11 @@ func CallMachineIdentityRefreshAccessToken(httpClient *resty.Client, request Uni
|
|||||||
Post(fmt.Sprintf("%v/v1/auth/token/renew", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v1/auth/token/renew", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return UniversalAuthRefreshResponse{}, fmt.Errorf("CallMachineIdentityRefreshAccessToken: Unable to complete api request [err=%s]", err)
|
return UniversalAuthRefreshResponse{}, NewGenericRequestError(operationCallMachineIdentityRefreshAccessToken, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return UniversalAuthRefreshResponse{}, fmt.Errorf("CallMachineIdentityRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return UniversalAuthRefreshResponse{}, NewAPIErrorWithResponse(operationCallMachineIdentityRefreshAccessToken, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return universalAuthRefreshResponse, nil
|
return universalAuthRefreshResponse, nil
|
||||||
@ -441,19 +471,19 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques
|
|||||||
response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL))
|
response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unable to complete api request [err=%w]", err)
|
return GetRawSecretsV3Response{}, NewGenericRequestError(operationCallGetRawSecretsV3, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() &&
|
if response.IsError() &&
|
||||||
(strings.Contains(response.String(), "bot_not_found_error") ||
|
(strings.Contains(response.String(), "bot_not_found_error") ||
|
||||||
strings.Contains(strings.ToLower(response.String()), "failed to find bot key") ||
|
strings.Contains(strings.ToLower(response.String()), "failed to find bot key") ||
|
||||||
strings.Contains(strings.ToLower(response.String()), "bot is not active")) {
|
strings.Contains(strings.ToLower(response.String()), "bot is not active")) {
|
||||||
return GetRawSecretsV3Response{}, fmt.Errorf(`Project with id %s is incompatible with your current CLI version. Upgrade your project by visiting the project settings page. If you're self-hosting and project upgrade option isn't yet available, contact your administrator to upgrade your Infisical instance to the latest release.
|
additionalContext := fmt.Sprintf(`Project with id %s is incompatible with your current CLI version. Upgrade your project by visiting the project settings page. If you're self-hosting and project upgrade option isn't yet available, contact your administrator to upgrade your Infisical instance to the latest release.`, request.WorkspaceId)
|
||||||
`, request.WorkspaceId)
|
return GetRawSecretsV3Response{}, NewAPIErrorWithResponse(operationCallGetRawSecretsV3, response, &additionalContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return GetRawSecretsV3Response{}, NewAPIErrorWithResponse(operationCallGetRawSecretsV3, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRawSecretsV3Response.ETag = response.Header().Get(("etag"))
|
getRawSecretsV3Response.ETag = response.Header().Get(("etag"))
|
||||||
@ -477,11 +507,11 @@ func CallFetchSingleSecretByName(httpClient *resty.Client, request GetRawSecretV
|
|||||||
Get(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
Get(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GetRawSecretV3ByNameResponse{}, fmt.Errorf("CallFetchSingleSecretByName: Unable to complete api request [err=%w]", err)
|
return GetRawSecretV3ByNameResponse{}, NewGenericRequestError(operationCallFetchSingleSecretByName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return GetRawSecretV3ByNameResponse{}, fmt.Errorf("CallFetchSingleSecretByName: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return GetRawSecretV3ByNameResponse{}, NewAPIErrorWithResponse(operationCallFetchSingleSecretByName, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
getRawSecretV3ByNameResponse.ETag = response.Header().Get(("etag"))
|
getRawSecretV3ByNameResponse.ETag = response.Header().Get(("etag"))
|
||||||
@ -517,11 +547,11 @@ func CallCreateRawSecretsV3(httpClient *resty.Client, request CreateRawSecretV3R
|
|||||||
Post(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
Post(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("CallCreateRawSecretsV3: Unable to complete api request [err=%w]", err)
|
return NewGenericRequestError(operationCallCreateRawSecretsV3, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return fmt.Errorf("CallCreateRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return NewAPIErrorWithResponse(operationCallCreateRawSecretsV3, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -535,11 +565,11 @@ func CallUpdateRawSecretsV3(httpClient *resty.Client, request UpdateRawSecretByN
|
|||||||
Patch(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
Patch(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("CallUpdateRawSecretsV3: Unable to complete api request [err=%w]", err)
|
return NewGenericRequestError(operationCallUpdateRawSecretsV3, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return fmt.Errorf("CallUpdateRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return NewAPIErrorWithResponse(operationCallUpdateRawSecretsV3, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -554,11 +584,11 @@ func CallRegisterGatewayIdentityV1(httpClient *resty.Client) (*GetRelayCredentia
|
|||||||
Post(fmt.Sprintf("%v/v1/gateways/register-identity", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v1/gateways/register-identity", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("CallRegisterGatewayIdentityV1: Unable to complete api request [err=%w]", err)
|
return nil, NewGenericRequestError(operationCallRegisterGatewayIdentityV1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return nil, fmt.Errorf("CallRegisterGatewayIdentityV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return nil, NewAPIErrorWithResponse(operationCallRegisterGatewayIdentityV1, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &resBody, nil
|
return &resBody, nil
|
||||||
@ -574,11 +604,11 @@ func CallExchangeRelayCertV1(httpClient *resty.Client, request ExchangeRelayCert
|
|||||||
Post(fmt.Sprintf("%v/v1/gateways/exchange-cert", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v1/gateways/exchange-cert", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("CallExchangeRelayCertV1: Unable to complete api request [err=%w]", err)
|
return nil, NewGenericRequestError(operationCallExchangeRelayCertV1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return nil, fmt.Errorf("CallExchangeRelayCertV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return nil, NewAPIErrorWithResponse(operationCallExchangeRelayCertV1, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &resBody, nil
|
return &resBody, nil
|
||||||
@ -591,11 +621,11 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
|
|||||||
Post(fmt.Sprintf("%v/v1/gateways/heartbeat", config.INFISICAL_URL))
|
Post(fmt.Sprintf("%v/v1/gateways/heartbeat", config.INFISICAL_URL))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("CallGatewayHeartBeatV1: Unable to complete api request [err=%w]", err)
|
return NewGenericRequestError(operationCallGatewayHeartBeatV1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return fmt.Errorf("CallGatewayHeartBeatV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return NewAPIErrorWithResponse(operationCallGatewayHeartBeatV1, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -611,11 +641,11 @@ func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRe
|
|||||||
Post(fmt.Sprintf("%v/v1/admin/bootstrap", request.Domain))
|
Post(fmt.Sprintf("%v/v1/admin/bootstrap", request.Domain))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("CallBootstrapInstance: Unable to complete api request [err=%w]", err)
|
return nil, NewGenericRequestError(operationCallBootstrapInstance, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.IsError() {
|
if response.IsError() {
|
||||||
return nil, fmt.Errorf("CallBootstrapInstance: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
return nil, NewAPIErrorWithResponse(operationCallBootstrapInstance, response, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resBody, nil
|
return resBody, nil
|
||||||
|
80
cli/packages/api/errors.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/infisical/go-sdk/packages/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GenericRequestError struct {
|
||||||
|
err error
|
||||||
|
operation string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GenericRequestError) Error() string {
|
||||||
|
return fmt.Sprintf("%s: Unable to complete api request [err=%v]", e.operation, e.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenericRequestError(operation string, err error) *GenericRequestError {
|
||||||
|
return &GenericRequestError{err: err, operation: operation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIError represents an error response from the API
|
||||||
|
type APIError struct {
|
||||||
|
AdditionalContext string `json:"additionalContext,omitempty"`
|
||||||
|
Operation string `json:"operation"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
ErrorMessage string `json:"message,omitempty"`
|
||||||
|
ReqId string `json:"reqId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *APIError) Error() string {
|
||||||
|
msg := fmt.Sprintf(
|
||||||
|
"%s Unsuccessful response [%v %v] [status-code=%v] [request-id=%v]",
|
||||||
|
e.Operation,
|
||||||
|
e.Method,
|
||||||
|
e.URL,
|
||||||
|
e.StatusCode,
|
||||||
|
e.ReqId,
|
||||||
|
)
|
||||||
|
|
||||||
|
if e.ErrorMessage != "" {
|
||||||
|
msg = fmt.Sprintf("%s [message=\"%s\"]", msg, e.ErrorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.AdditionalContext != "" {
|
||||||
|
msg = fmt.Sprintf("%s [additional-context=\"%s\"]", msg, e.AdditionalContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPIErrorWithResponse(operation string, res *resty.Response, additionalContext *string) error {
|
||||||
|
errorMessage := util.TryParseErrorBody(res)
|
||||||
|
reqId := util.TryExtractReqId(res)
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
return NewGenericRequestError(operation, fmt.Errorf("response is nil"))
|
||||||
|
}
|
||||||
|
|
||||||
|
apiError := &APIError{
|
||||||
|
Operation: operation,
|
||||||
|
Method: res.Request.Method,
|
||||||
|
URL: res.Request.URL,
|
||||||
|
StatusCode: res.StatusCode(),
|
||||||
|
ReqId: reqId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if additionalContext != nil && *additionalContext != "" {
|
||||||
|
apiError.AdditionalContext = *additionalContext
|
||||||
|
}
|
||||||
|
|
||||||
|
if errorMessage != "" {
|
||||||
|
apiError.ErrorMessage = errorMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiError
|
||||||
|
}
|
@ -80,9 +80,8 @@ func getDynamicSecretList(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +197,7 @@ func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
@ -329,9 +328,8 @@ func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,9 +435,8 @@ func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +541,7 @@ func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ var initCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if userCreds.LoginExpired {
|
if userCreds.LoginExpired {
|
||||||
userCreds = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient, err := util.GetRestyClientWithCustomHeaders()
|
httpClient, err := util.GetRestyClientWithCustomHeaders()
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -21,8 +20,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
browser "github.com/pkg/browser"
|
|
||||||
|
|
||||||
"github.com/Infisical/infisical-merge/packages/api"
|
"github.com/Infisical/infisical-merge/packages/api"
|
||||||
"github.com/Infisical/infisical-merge/packages/config"
|
"github.com/Infisical/infisical-merge/packages/config"
|
||||||
"github.com/Infisical/infisical-merge/packages/crypto"
|
"github.com/Infisical/infisical-merge/packages/crypto"
|
||||||
@ -984,17 +981,7 @@ func browserCliLogin() (models.UserCredentials, error) {
|
|||||||
callbackPort := listener.Addr().(*net.TCPAddr).Port
|
callbackPort := listener.Addr().(*net.TCPAddr).Port
|
||||||
url := fmt.Sprintf("%s?callback_port=%d", config.INFISICAL_LOGIN_URL, callbackPort)
|
url := fmt.Sprintf("%s?callback_port=%d", config.INFISICAL_LOGIN_URL, callbackPort)
|
||||||
|
|
||||||
defaultPrintStatement := fmt.Sprintf("\n\nTo complete your login, open this address in your browser: %v \n", url)
|
fmt.Printf("\n\nTo complete your login, open this address in your browser: %v \n", url)
|
||||||
|
|
||||||
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
|
|
||||||
if err := browser.OpenURL(url); err != nil {
|
|
||||||
fmt.Print(defaultPrintStatement)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("\n\nPlease proceed to your browser to complete the login process.\nIf the browser doesn't open automatically, please open this address in your browser: %v \n", url)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Print(defaultPrintStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
//flow channels
|
//flow channels
|
||||||
success := make(chan models.UserCredentials)
|
success := make(chan models.UserCredentials)
|
||||||
|
@ -237,7 +237,7 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
secretOperations, err = util.SetRawSecrets(processedArgs, secretType, environmentName, secretsPath, projectId, &models.TokenDetails{
|
secretOperations, err = util.SetRawSecrets(processedArgs, secretType, environmentName, secretsPath, projectId, &models.TokenDetails{
|
||||||
@ -325,7 +325,7 @@ var secretsDeleteCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient.SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken)
|
httpClient.SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken)
|
||||||
|
@ -184,7 +184,7 @@ func issueCredentials(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
@ -417,7 +417,7 @@ func signKey(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
@ -626,7 +626,7 @@ func sshConnect(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
@ -879,7 +879,7 @@ func sshAddHost(cmd *cobra.Command, args []string) {
|
|||||||
util.HandleError(err, "Unable to authenticate")
|
util.HandleError(err, "Unable to authenticate")
|
||||||
}
|
}
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login]")
|
||||||
}
|
}
|
||||||
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ var tokensCreateCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenOnly, err := cmd.Flags().GetBool("token-only")
|
tokenOnly, err := cmd.Flags().GetBool("token-only")
|
||||||
|
@ -111,9 +111,8 @@ var userGetTokenCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
|
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = util.EstablishUserLoginSession()
|
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleError(err, "[infisical user get token]: Unable to get logged in user token")
|
util.HandleError(err, "[infisical user get token]: Unable to get logged in user token")
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,5 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AuthStrategyType string
|
type AuthStrategyType string
|
||||||
|
|
||||||
var AuthStrategy = struct {
|
var AuthStrategy = struct {
|
||||||
@ -51,36 +43,3 @@ func IsAuthMethodValid(authMethod string, allowUserAuth bool) (isValid bool, str
|
|||||||
}
|
}
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// EstablishUserLoginSession handles the login flow to either create a new session or restore an expired one.
|
|
||||||
// It returns fresh user details if login is successful.
|
|
||||||
func EstablishUserLoginSession() LoggedInUserDetails {
|
|
||||||
log.Info().Msg("No valid login session found, triggering login flow")
|
|
||||||
|
|
||||||
exePath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
PrintErrorMessageAndExit(fmt.Sprintf("Failed to determine executable path: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn infisical login command
|
|
||||||
loginCmd := exec.Command(exePath, "login", "--silent")
|
|
||||||
loginCmd.Stdin = os.Stdin
|
|
||||||
loginCmd.Stdout = os.Stdout
|
|
||||||
loginCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
err = loginCmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
PrintErrorMessageAndExit(fmt.Sprintf("Failed to automatically trigger login flow. Please run [infisical login] manually to login."))
|
|
||||||
}
|
|
||||||
|
|
||||||
loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true)
|
|
||||||
if err != nil {
|
|
||||||
PrintErrorMessageAndExit("You must be logged in to run this command. To login, run [infisical login]")
|
|
||||||
}
|
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
|
||||||
PrintErrorMessageAndExit("Your login session has expired. Please run [infisical login]")
|
|
||||||
}
|
|
||||||
|
|
||||||
return loggedInUserDetails
|
|
||||||
}
|
|
||||||
|
@ -25,7 +25,7 @@ func GetAllFolders(params models.GetAllFoldersParameters) ([]models.SingleFolder
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = EstablishUserLoginSession()
|
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceFile, err := GetWorkSpaceFromFile()
|
workspaceFile, err := GetWorkSpaceFromFile()
|
||||||
@ -194,7 +194,7 @@ func CreateFolder(params models.CreateFolderParameters) (models.SingleFolder, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = EstablishUserLoginSession()
|
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
params.InfisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
params.InfisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
@ -244,7 +244,7 @@ func DeleteFolder(params models.DeleteFolderParameters) ([]models.SingleFolder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loggedInUserDetails.LoginExpired {
|
if loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = EstablishUserLoginSession()
|
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
params.InfisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
params.InfisicalToken = loggedInUserDetails.UserCredentials.JTWToken
|
||||||
|
@ -174,7 +174,7 @@ func RequireLogin() {
|
|||||||
configFile, _ := GetConfigFile()
|
configFile, _ := GetConfigFile()
|
||||||
|
|
||||||
if configFile.LoggedInUserEmail == "" {
|
if configFile.LoggedInUserEmail == "" {
|
||||||
EstablishUserLoginSession()
|
PrintErrorMessageAndExit("You must be logged in to run this command. To login, run [infisical login]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isConnected && loggedInUserDetails.LoginExpired {
|
if isConnected && loggedInUserDetails.LoginExpired {
|
||||||
loggedInUserDetails = EstablishUserLoginSession()
|
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
|
||||||
}
|
}
|
||||||
|
|
||||||
var infisicalDotJson models.WorkspaceConfigFile
|
var infisicalDotJson models.WorkspaceConfigFile
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: CallGetRawSecretsV3: Unsuccessful response [GET https://app.infisical.com/api/v3/secrets/raw?environment=invalid-env&expandSecretReferences=true&include_imports=true&recursive=true&secretPath=%2F&workspaceId=bef697d4-849b-4a75-b284-0922f87f8ba2] [status-code=404] [response={"error":"NotFound","message":"Environment with slug 'invalid-env' in project with ID bef697d4-849b-4a75-b284-0922f87f8ba2 not found","statusCode":404}]
|
error: CallGetRawSecretsV3 Unsuccessful response [GET https://app.infisical.com/api/v3/secrets/raw?environment=invalid-env&expandSecretReferences=true&include_imports=true&recursive=true&secretPath=%2F&workspaceId=bef697d4-849b-4a75-b284-0922f87f8ba2] [status-code=404] [request-id=<unknown-value>] [message="Environment with slug 'invalid-env' in project with ID bef697d4-849b-4a75-b284-0922f87f8ba2 not found"]
|
||||||
|
|
||||||
|
|
||||||
If this issue continues, get support at https://infisical.com/slack
|
If this issue continues, get support at https://infisical.com/slack
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -71,7 +72,11 @@ func SetupCli() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FilterRequestID(input string) string {
|
func FilterRequestID(input string) string {
|
||||||
// Find the JSON part of the error message
|
requestIDPattern := regexp.MustCompile(`\[request-id=[^\]]+\]`)
|
||||||
|
reqIDPattern := regexp.MustCompile(`\[reqId=[^\]]+\]`)
|
||||||
|
input = requestIDPattern.ReplaceAllString(input, "[request-id=<unknown-value>]")
|
||||||
|
input = reqIDPattern.ReplaceAllString(input, "[reqId=<unknown-value>]")
|
||||||
|
|
||||||
start := strings.Index(input, "{")
|
start := strings.Index(input, "{")
|
||||||
end := strings.LastIndex(input, "}") + 1
|
end := strings.LastIndex(input, "}") + 1
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ The Infisical AWS ElastiCache dynamic secret allows you to generate AWS ElastiCa
|
|||||||
<Step title="Click on the 'Add Dynamic Secret' button">
|
<Step title="Click on the 'Add Dynamic Secret' button">
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Select AWS ElastiCache">
|
<Step title="Select 'AWS ElastiCache'">
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Provide the inputs for dynamic secret parameters">
|
<Step title="Provide the inputs for dynamic secret parameters">
|
||||||
@ -94,17 +94,9 @@ The Infisical AWS ElastiCache dynamic secret allows you to generate AWS ElastiCa
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify ElastiCache Statements">
|
<Step title="(Optional) Modify ElastiCache Statements">
|
||||||

|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the ElastiCache statement to your needs. This is useful if you want to only give access to a specific table(s).
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|

|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize ElastiCache Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the ElastiCache statement to your needs. This is useful if you want to only give access to a specific resource.
|
|
||||||
</ParamField>
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click `Submit`">
|
<Step title="Click `Submit`">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
@ -105,14 +105,6 @@ Replace **\<account id\>** with your AWS account id and **\<aws-scope-path\>** w
|
|||||||
The AWS IAM inline policy that should be attached to the created users. Multiple values can be provided by separating them with commas
|
The AWS IAM inline policy that should be attached to the created users. Multiple values can be provided by separating them with commas
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
@ -78,23 +78,15 @@ The above configuration allows user creation and granting permissions.
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify CQL Statements">
|
<Step title="(Optional) Modify CQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize CQL Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the CQL statement to your needs. This is useful if you want to only give access to a specific key-space(s).
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the CQL statement to your needs. This is useful if you want to only give access to a specific key-space(s).
|
||||||
</ParamField>
|
|
||||||
|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
If this step fails, you may have to add the CA certificate.
|
If this step fails, you may have to add the CA certficate.
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||

|

|
||||||
|
@ -7,14 +7,13 @@ The Infisical Elasticsearch dynamic secret allows you to generate Elasticsearch
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1. Create a role with at least `manage_security` and `monitor` permissions.
|
1. Create a role with at least `manage_security` and `monitor` permissions.
|
||||||
2. Assign the newly created role to your API key or user that you'll use later in the dynamic secret configuration.
|
2. Assign the newly created role to your API key or user that you'll use later in the dynamic secret configuration.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
For testing purposes, you can also use a highly privileged role like
|
For testing purposes, you can also use a highly privileged role like `superuser`, that will have full control over the cluster. This is not recommended in production environments following the principle of least privilege.
|
||||||
`superuser`, that will have full control over the cluster. This is not
|
|
||||||
recommended in production environments following the principle of least
|
|
||||||
privilege.
|
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
## Set up Dynamic Secrets with Elasticsearch
|
## Set up Dynamic Secrets with Elasticsearch
|
||||||
@ -47,55 +46,39 @@ The Infisical Elasticsearch dynamic secret allows you to generate Elasticsearch
|
|||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="Port" type="string" required>
|
<ParamField path="Port" type="string" required>
|
||||||
|
The port that your Elasticsearch instance is running on. _(Example: 9200)_
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
The port that your Elasticsearch instance is running on. _(Example: 9200)_
|
<ParamField path="Roles" type="string[]" required>
|
||||||
</ParamField>
|
The roles that the new user that is created when a lease is provisioned will be assigned to. This is a required field. This defaults to `superuser`, which is highly privileged. It is recommended to create a new role with the least privileges required for the lease.
|
||||||
|
</ParamField>
|
||||||
<ParamField path="Roles" type="string[]" required>
|
|
||||||
The roles that the new user that is created when a lease is provisioned will
|
|
||||||
be assigned to. This is a required field. This defaults to `superuser`, which
|
|
||||||
is highly privileged. It is recommended to create a new role with the least
|
|
||||||
privileges required for the lease.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="Authentication Method" type="API Key | Username/Password" required>
|
<ParamField path="Authentication Method" type="API Key | Username/Password" required>
|
||||||
Select the authentication method you want to use to connect to your Elasticsearch instance.
|
Select the authentication method you want to use to connect to your Elasticsearch instance.
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="Username" type="string" required>
|
<ParamField path="Username" type="string" required>
|
||||||
The username of the user that will be used to provision new dynamic secret
|
The username of the user that will be used to provision new dynamic secret leases. Only required if you selected the `Username/Password` authentication method.
|
||||||
leases. Only required if you selected the `Username/Password` authentication
|
</ParamField>
|
||||||
method.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="Password" type="string" required>
|
<ParamField path="Password" type="string" required>
|
||||||
The password of the user that will be used to provision new dynamic secret
|
The password of the user that will be used to provision new dynamic secret leases. Only required if you selected the `Username/Password` authentication method.
|
||||||
leases. Only required if you selected the `Username/Password` authentication
|
</ParamField>
|
||||||
method.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="API Key ID" required>
|
<ParamField path="API Key ID" required>
|
||||||
The ID of the API key that will be used to provision new dynamic secret
|
The ID of the API key that will be used to provision new dynamic secret leases. Only required if you selected the `API Key` authentication method.
|
||||||
leases. Only required if you selected the `API Key` authentication method.
|
</ParamField>
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="API Key" required>
|
<ParamField path="API Key" required>
|
||||||
The API key that will be used to provision new dynamic secret leases. Only
|
The API key that will be used to provision new dynamic secret leases. Only required if you selected the `API Key` authentication method.
|
||||||
required if you selected the `API Key` authentication method.
|
</ParamField>
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="CA(SSL)" type="string">
|
<ParamField path="CA(SSL)" type="string">
|
||||||
A CA may be required if your DB requires it for incoming connections. This is often the case when connecting to a managed service.
|
A CA may be required if your DB requires it for incoming connections. This is often the case when connecting to a managed service.
|
||||||
</ParamField>
|
</ParamField>
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|

|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click `Submit`">
|
<Step title="Click `Submit`">
|
||||||
@ -126,23 +109,19 @@ The port that your Elasticsearch instance is running on. _(Example: 9200)_
|
|||||||
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you.
|
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Audit or Revoke Leases
|
## Audit or Revoke Leases
|
||||||
|
|
||||||
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
|
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
|
||||||
This will allow you to see the expiration time of the lease or delete a lease before it's set time to live.
|
This will allow you to see the expiration time of the lease or delete a lease before it's set time to live.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Renew Leases
|
## Renew Leases
|
||||||
|
|
||||||
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below.
|
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below.
|
||||||

|

|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic
|
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret
|
||||||
secret
|
|
||||||
</Warning>
|
</Warning>
|
||||||
|
@ -122,13 +122,6 @@ The Infisical LDAP dynamic secret allows you to generate user credentials on dem
|
|||||||
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
dn: CN={{Username}},OU=Test Create,DC=infisical,DC=com
|
||||||
changetype: delete
|
changetype: delete
|
||||||
```
|
```
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
</ParamField>
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
|
@ -6,10 +6,11 @@ description: "Learn how to dynamically generate Mongo Atlas Database user creden
|
|||||||
The Infisical Mongo Atlas dynamic secret allows you to generate Mongo Atlas Database credentials on demand based on configured role.
|
The Infisical Mongo Atlas dynamic secret allows you to generate Mongo Atlas Database credentials on demand based on configured role.
|
||||||
|
|
||||||
## Prerequisite
|
## Prerequisite
|
||||||
|
Create a project scopped API Key with the required permission in your Mongo Atlas following the [official doc](https://www.mongodb.com/docs/atlas/configure-api-access/#grant-programmatic-access-to-a-project).
|
||||||
|
|
||||||
Create a project scoped API Key with the required permission in your Mongo Atlas following the [official doc](https://www.mongodb.com/docs/atlas/configure-api-access/#grant-programmatic-access-to-a-project).
|
<Info>
|
||||||
|
The API Key must have permission to manage users in the project.
|
||||||
<Info>The API Key must have permission to manage users in the project.</Info>
|
</Info>
|
||||||
|
|
||||||
## Set up Dynamic Secrets with Mongo Atlas
|
## Set up Dynamic Secrets with Mongo Atlas
|
||||||
|
|
||||||
@ -60,34 +61,20 @@ Create a project scoped API Key with the required permission in your Mongo Atlas
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify Access Scope">
|
<Step title="(Optional) Modify Access Scope">
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize Scope" type="string">
|
|
||||||
|
|
||||||
List that contains clusters, MongoDB Atlas Data Lakes, and MongoDB Atlas Streams Instances that this database user can access. If omitted, MongoDB Cloud grants the database user access to all the clusters, MongoDB Atlas Data Lakes, and MongoDB Atlas Streams Instances in the project.
|
List that contains clusters, MongoDB Atlas Data Lakes, and MongoDB Atlas Streams Instances that this database user can access. If omitted, MongoDB Cloud grants the database user access to all the clusters, MongoDB Atlas Data Lakes, and MongoDB Atlas Streams Instances in the project.
|
||||||
|
|
||||||
|

|
||||||
- **Label**: Human-readable label that identifies the cluster or MongoDB Atlas Data Lake that this database user can access.
|
- **Label**: Human-readable label that identifies the cluster or MongoDB Atlas Data Lake that this database user can access.
|
||||||
- **Type**: Category of resource that this database user can access.
|
- **Type**: Category of resource that this database user can access.
|
||||||
|
|
||||||
</ParamField>
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
If this step fails, you may have to add the CA certificate.
|
If this step fails, you may have to add the CA certficate.
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Generate dynamic secrets">
|
<Step title="Generate dynamic secrets">
|
||||||
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
|
Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials.
|
||||||
@ -102,30 +89,26 @@ Create a project scoped API Key with the required permission in your Mongo Atlas
|
|||||||

|

|
||||||
|
|
||||||
<Tip>
|
<Tip>
|
||||||
Ensure that the TTL for the lease falls within the maximum TTL defined when configuring the dynamic secret.
|
Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret.
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
|
|
||||||
Once you click the `Submit` button, a new secret lease will be generated and the credentials for it will be shown to you.
|
Once you click the `Submit` button, a new secret lease will be generated and the credentials for it will be shown to you.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Audit or Revoke Leases
|
## Audit or Revoke Leases
|
||||||
|
|
||||||
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
|
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
|
||||||
This will allow you to see the expiration time of the lease or delete a lease before it's set time to live.
|
This will allow you to see the expiration time of the lease or delete a lease before it's set time to live.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Renew Leases
|
## Renew Leases
|
||||||
|
|
||||||
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below.
|
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below.
|
||||||

|

|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic
|
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret
|
||||||
secret
|
|
||||||
</Warning>
|
</Warning>
|
||||||
|
@ -66,13 +66,6 @@ Create a user with the required permission in your MongoDB instance. This user w
|
|||||||
<ParamField path="CA(SSL)" type="string">
|
<ParamField path="CA(SSL)" type="string">
|
||||||
A CA may be required if your DB requires it for incoming connections.
|
A CA may be required if your DB requires it for incoming connections.
|
||||||
</ParamField>
|
</ParamField>
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
|
@ -71,23 +71,15 @@ Create a user with the required permission in your SQL instance. This user will
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize SQL Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
||||||
</ParamField>
|
|
||||||
|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
If this step fails, you may have to add the CA certificate.
|
If this step fails, you may have to add the CA certficate.
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||

|

|
||||||
|
@ -68,17 +68,9 @@ Create a user with the required permission in your SQL instance. This user will
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize SQL Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
||||||
</ParamField>
|
|
||||||
|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click `Submit`">
|
<Step title="Click `Submit`">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
@ -70,23 +70,13 @@ Create a user with the required permission in your SQL instance. This user will
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize SQL Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
||||||
</ParamField>
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
If this step fails, you may have to add the CA certificate.
|
If this step fails, you may have to add the CA certficate.
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||

|

|
||||||
|
@ -71,23 +71,15 @@ Create a user with the required permission in your SQL instance. This user will
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize SQL Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s).
|
||||||
</ParamField>
|
|
||||||
|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
If this step fails, you may have to add the CA certificate.
|
If this step fails, you may have to add the CA certficate.
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||

|

|
||||||
|
@ -9,6 +9,7 @@ The Infisical RabbitMQ dynamic secret allows you to generate RabbitMQ credential
|
|||||||
|
|
||||||
1. Ensure that the `management` plugin is enabled on your RabbitMQ instance. This is required for the dynamic secret to work.
|
1. Ensure that the `management` plugin is enabled on your RabbitMQ instance. This is required for the dynamic secret to work.
|
||||||
|
|
||||||
|
|
||||||
## Set up Dynamic Secrets with RabbitMQ
|
## Set up Dynamic Secrets with RabbitMQ
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
@ -18,8 +19,8 @@ The Infisical RabbitMQ dynamic secret allows you to generate RabbitMQ credential
|
|||||||
<Step title="Click on the 'Add Dynamic Secret' button">
|
<Step title="Click on the 'Add Dynamic Secret' button">
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Select RabbitMQ">
|
<Step title="Select 'RabbitMQ'">
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Provide the inputs for dynamic secret parameters">
|
<Step title="Provide the inputs for dynamic secret parameters">
|
||||||
<ParamField path="Secret Name" type="string" required>
|
<ParamField path="Secret Name" type="string" required>
|
||||||
@ -39,45 +40,34 @@ The Infisical RabbitMQ dynamic secret allows you to generate RabbitMQ credential
|
|||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="Port" type="string" required>
|
<ParamField path="Port" type="string" required>
|
||||||
|
The port that the RabbitMQ management plugin is listening on. This is `15672` by default.
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
The port that the RabbitMQ management plugin is listening on. This is `15672` by default.
|
<ParamField path="Virtual host name" type="string" required>
|
||||||
</ParamField>
|
The name of the virtual host that the user will be assigned to. This defaults to `/`.
|
||||||
|
</ParamField>
|
||||||
<ParamField path="Virtual host name" type="string" required>
|
|
||||||
The name of the virtual host that the user will be assigned to. This defaults
|
|
||||||
to `/`.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="Virtual host permissions (Read/Write/Configure)" type="string" required>
|
<ParamField path="Virtual host permissions (Read/Write/Configure)" type="string" required>
|
||||||
The permissions that the user will have on the virtual host. This defaults to `.*`.
|
The permissions that the user will have on the virtual host. This defaults to `.*`.
|
||||||
|
|
||||||
The three permission fields all take a regular expression _(regex)_, that should match resource names for which the user is granted read / write / configuration permissions
|
The three permission fields all take a regular expression _(regex)_, that should match resource names for which the user is granted read / write / configuration permissions
|
||||||
|
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="Username" type="string" required>
|
|
||||||
The username of the user that will be used to provision new dynamic secret
|
|
||||||
leases.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="Password" type="string" required>
|
<ParamField path="Username" type="string" required>
|
||||||
The password of the user that will be used to provision new dynamic secret
|
The username of the user that will be used to provision new dynamic secret leases.
|
||||||
leases.
|
</ParamField>
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
<ParamField path="Password" type="string" required>
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
The password of the user that will be used to provision new dynamic secret leases.
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
Allowed template variables are
|
<ParamField path="CA(SSL)" type="string">
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
<ParamField path="CA(SSL)" type="string">
|
|
||||||
A CA may be required if your DB requires it for incoming connections. This is often the case when connecting to a managed service.
|
A CA may be required if your DB requires it for incoming connections. This is often the case when connecting to a managed service.
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click `Submit`">
|
<Step title="Click `Submit`">
|
||||||
@ -108,23 +98,19 @@ Allowed template variables are
|
|||||||
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you.
|
Once you click the `Submit` button, a new secret lease will be generated and the credentials from it will be shown to you.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Audit or Revoke Leases
|
## Audit or Revoke Leases
|
||||||
|
|
||||||
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
|
Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard.
|
||||||
This will allow you to see the expiration time of the lease or delete a lease before it's set time to live.
|
This will allow you to see the expiration time of the lease or delete a lease before it's set time to live.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Renew Leases
|
## Renew Leases
|
||||||
|
|
||||||
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below.
|
To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below.
|
||||||

|

|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic
|
Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret
|
||||||
secret
|
|
||||||
</Warning>
|
</Warning>
|
||||||
|
@ -56,17 +56,9 @@ Create a user with the required permission in your Redis instance. This user wil
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify Redis Statements">
|
<Step title="(Optional) Modify Redis Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize Redis Statement" type="string">
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the Redis statement to your needs. This is useful if you want to only give access to a specific table(s).
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the Redis statement to your needs. This is useful if you want to only give access to a specific table(s).
|
||||||
</ParamField>
|
|
||||||
|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click `Submit`">
|
<Step title="Click `Submit`">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
@ -62,22 +62,13 @@ The Infisical SAP ASE dynamic secret allows you to generate SAP ASE database cre
|
|||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SAP SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs.
|
||||||

|

|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
<Warning>
|
||||||
- `{{randomUsername}}`: Random username string
|
Due to SAP ASE limitations, the attached SQL statements are not executed as a transaction.
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
</Warning>
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize Statement" type="string">
|
|
||||||
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs.
|
|
||||||
<Warning>
|
|
||||||
Due to SAP ASE limitations, the attached SQL statements are not executed as a transaction.
|
|
||||||
</Warning>
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
|
@ -62,25 +62,14 @@ The Infisical SAP HANA dynamic secret allows you to generate SAP HANA database c
|
|||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SAP SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize Statement" type="string">
|
|
||||||
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs.
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs.
|
||||||
|

|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
Due to SAP HANA limitations, the attached SQL statements are not executed as a transaction.
|
Due to SAP HANA limitations, the attached SQL statements are not executed as a transaction.
|
||||||
</Warning>
|
</Warning>
|
||||||
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
|
@ -8,26 +8,21 @@ Infisical's Snowflake dynamic secrets allow you to generate Snowflake user crede
|
|||||||
## Snowflake Prerequisites
|
## Snowflake Prerequisites
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
Infisical requires a Snowflake user in your account with the USERADMIN role.
|
Infisical requires a Snowflake user in your account with the USERADMIN role. This user will act as a service account for Infisical and facilitate the creation of new users as needed.
|
||||||
This user will act as a service account for Infisical and facilitate the
|
|
||||||
creation of new users as needed.
|
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Navigate to Snowflake's User Dashboard and press the '+ User' button">
|
<Step title="Navigate to Snowflake's User Dashboard and press the '+ User' button">
|
||||||

|
||||||
Dashboard](/images/platform/dynamic-secrets/snowflake/dynamic-secret-snowflake-users-page.png)
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Create a Snowflake user with the USERADMIN role for Infisical">
|
<Step title="Create a Snowflake user with the USERADMIN role for Infisical">
|
||||||
<Warning>
|
<Warning>
|
||||||
Be sure to uncheck "Force user to change password on first time login"
|
Be sure to uncheck "Force user to change password on first time login"
|
||||||
</Warning>
|
</Warning>
|
||||||

|
||||||
User](/images/platform/dynamic-secrets/snowflake/dynamic-secret-snowflake-create-service-user.png)
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Click on the Account Menu in the bottom left and take note of your Account and Organization identifiers">
|
<Step title="Click on the Account Menu in the bottom left and take note of your Account and Organization identifiers">
|
||||||

|
||||||
Identifiers](/images/platform/dynamic-secrets/snowflake/dynamic-secret-snowflake-identifiers.png)
|
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
@ -76,23 +71,10 @@ Infisical's Snowflake dynamic secrets allow you to generate Snowflake user crede
|
|||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="(Optional) Modify SQL Statements">
|
<Step title="(Optional) Modify SQL Statements">
|
||||||

|
|
||||||
<ParamField path="Username Template" type="string" default="{{randomUsername}}">
|
|
||||||
Specifies a template for generating usernames. This field allows customization of how usernames are automatically created.
|
|
||||||
|
|
||||||
Allowed template variables are
|
|
||||||
- `{{randomUsername}}`: Random username string
|
|
||||||
- `{{unixTimestamp}}`: Current Unix timestamp
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="Customize Statement" type="string">
|
|
||||||
|
|
||||||
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL
|
If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL
|
||||||
statement to your needs.
|
statement to your needs.
|
||||||
|

|
||||||
</ParamField>
|
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Click 'Submit'">
|
<Step title="Click 'Submit'">
|
||||||
After submitting the form, you will see a dynamic secret created in the dashboard.
|
After submitting the form, you will see a dynamic secret created in the dashboard.
|
||||||
</Step>
|
</Step>
|
||||||
@ -122,7 +104,6 @@ Infisical's Snowflake dynamic secrets allow you to generate Snowflake user crede
|
|||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Audit or Revoke Leases
|
## Audit or Revoke Leases
|
||||||
|
Before Width: | Height: | Size: 482 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 459 KiB After Width: | Height: | Size: 173 KiB |
Before Width: | Height: | Size: 445 KiB After Width: | Height: | Size: 419 KiB |
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 526 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 501 KiB After Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 555 KiB After Width: | Height: | Size: 607 KiB |
Before Width: | Height: | Size: 566 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 577 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 572 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 595 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 568 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 546 KiB After Width: | Height: | Size: 666 KiB |
@ -1,2 +1,16 @@
|
|||||||
export * from "./mutation";
|
export {
|
||||||
export * from "./queries";
|
useAdminDeleteUser,
|
||||||
|
useAdminGrantServerAdminAccess,
|
||||||
|
useAdminRemoveIdentitySuperAdminAccess,
|
||||||
|
useCreateAdminUser,
|
||||||
|
useInvalidateCache,
|
||||||
|
useRemoveUserServerAdminAccess,
|
||||||
|
useUpdateServerConfig,
|
||||||
|
useUpdateServerEncryptionStrategy
|
||||||
|
} from "./mutation";
|
||||||
|
export {
|
||||||
|
useAdminGetUsers,
|
||||||
|
useGetAdminIntegrationsConfig,
|
||||||
|
useGetServerConfig,
|
||||||
|
useGetServerRootKmsEncryptionDetails
|
||||||
|
} from "./queries";
|
||||||
|
@ -63,41 +63,6 @@ export const useAdminDeleteUser = () => {
|
|||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: [adminStandaloneKeys.getUsers]
|
queryKey: [adminStandaloneKeys.getUsers]
|
||||||
});
|
});
|
||||||
queryClient.invalidateQueries({ queryKey: adminStandaloneKeys.getOrganizations });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useAdminDeleteOrganizationMembership = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return useMutation<object, object, { organizationId: string; membershipId: string }>({
|
|
||||||
mutationFn: async ({ organizationId, membershipId }) => {
|
|
||||||
await apiRequest.delete(
|
|
||||||
`/api/v1/admin/organization-management/organizations/${organizationId}/memberships/${membershipId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: [adminStandaloneKeys.getOrganizations]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useAdminDeleteOrganization = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: async (organizationId: string) => {
|
|
||||||
await apiRequest.delete(
|
|
||||||
`/api/v1/admin/organization-management/organizations/${organizationId}`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
queryKey: [adminStandaloneKeys.getOrganizations]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -6,10 +6,8 @@ import { Identity } from "@app/hooks/api/identities/types";
|
|||||||
import { User } from "../types";
|
import { User } from "../types";
|
||||||
import {
|
import {
|
||||||
AdminGetIdentitiesFilters,
|
AdminGetIdentitiesFilters,
|
||||||
AdminGetOrganizationsFilters,
|
|
||||||
AdminGetUsersFilters,
|
AdminGetUsersFilters,
|
||||||
AdminIntegrationsConfig,
|
AdminIntegrationsConfig,
|
||||||
OrganizationWithProjects,
|
|
||||||
TGetInvalidatingCacheStatus,
|
TGetInvalidatingCacheStatus,
|
||||||
TGetServerRootKmsEncryptionDetails,
|
TGetServerRootKmsEncryptionDetails,
|
||||||
TServerConfig
|
TServerConfig
|
||||||
@ -17,15 +15,12 @@ import {
|
|||||||
|
|
||||||
export const adminStandaloneKeys = {
|
export const adminStandaloneKeys = {
|
||||||
getUsers: "get-users",
|
getUsers: "get-users",
|
||||||
getOrganizations: "get-organizations",
|
|
||||||
getIdentities: "get-identities"
|
getIdentities: "get-identities"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const adminQueryKeys = {
|
export const adminQueryKeys = {
|
||||||
serverConfig: () => ["server-config"] as const,
|
serverConfig: () => ["server-config"] as const,
|
||||||
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
|
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
|
||||||
getOrganizations: (filters: AdminGetOrganizationsFilters) =>
|
|
||||||
[adminStandaloneKeys.getOrganizations, { filters }] as const,
|
|
||||||
getIdentities: (filters: AdminGetIdentitiesFilters) =>
|
getIdentities: (filters: AdminGetIdentitiesFilters) =>
|
||||||
[adminStandaloneKeys.getIdentities, { filters }] as const,
|
[adminStandaloneKeys.getIdentities, { filters }] as const,
|
||||||
getAdminSlackConfig: () => ["admin-slack-config"] as const,
|
getAdminSlackConfig: () => ["admin-slack-config"] as const,
|
||||||
@ -39,28 +34,6 @@ export const fetchServerConfig = async () => {
|
|||||||
return data.config;
|
return data.config;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAdminGetOrganizations = (filters: AdminGetOrganizationsFilters) => {
|
|
||||||
return useInfiniteQuery({
|
|
||||||
initialPageParam: 0,
|
|
||||||
queryKey: adminQueryKeys.getOrganizations(filters),
|
|
||||||
queryFn: async ({ pageParam }) => {
|
|
||||||
const { data } = await apiRequest.get<{ organizations: OrganizationWithProjects[] }>(
|
|
||||||
"/api/v1/admin/organization-management/organizations",
|
|
||||||
{
|
|
||||||
params: {
|
|
||||||
...filters,
|
|
||||||
offset: pageParam
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return data.organizations;
|
|
||||||
},
|
|
||||||
getNextPageParam: (lastPage, pages) =>
|
|
||||||
lastPage.length !== 0 ? pages.length * filters.limit : undefined
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetServerConfig = ({
|
export const useGetServerConfig = ({
|
||||||
options = {}
|
options = {}
|
||||||
}: {
|
}: {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { Organization } from "../types";
|
|
||||||
|
|
||||||
export enum LoginMethod {
|
export enum LoginMethod {
|
||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
@ -10,27 +8,6 @@ export enum LoginMethod {
|
|||||||
OIDC = "oidc"
|
OIDC = "oidc"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationWithProjects = Organization & {
|
|
||||||
members: {
|
|
||||||
user: {
|
|
||||||
id: string;
|
|
||||||
email: string | null;
|
|
||||||
username: string;
|
|
||||||
firstName: string | null;
|
|
||||||
lastName: string | null;
|
|
||||||
};
|
|
||||||
membershipId: string;
|
|
||||||
role: string;
|
|
||||||
roleId: string | null;
|
|
||||||
}[];
|
|
||||||
projects: {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
slug: string;
|
|
||||||
createdAt: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TServerConfig = {
|
export type TServerConfig = {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
allowSignUp: boolean;
|
allowSignUp: boolean;
|
||||||
@ -74,11 +51,6 @@ export type TCreateAdminUserDTO = {
|
|||||||
salt: string;
|
salt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdminGetOrganizationsFilters = {
|
|
||||||
limit: number;
|
|
||||||
searchTerm: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AdminGetUsersFilters = {
|
export type AdminGetUsersFilters = {
|
||||||
limit: number;
|
limit: number;
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
|
@ -13,7 +13,6 @@ export type TDynamicSecret = {
|
|||||||
status?: DynamicSecretStatus;
|
status?: DynamicSecretStatus;
|
||||||
statusDetails?: string;
|
statusDetails?: string;
|
||||||
maxTTL: string;
|
maxTTL: string;
|
||||||
usernameTemplate?: string | null;
|
|
||||||
metadata?: { key: string; value: string }[];
|
metadata?: { key: string; value: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -288,7 +287,6 @@ export type TCreateDynamicSecretDTO = {
|
|||||||
environmentSlug: string;
|
environmentSlug: string;
|
||||||
name: string;
|
name: string;
|
||||||
metadata?: { key: string; value: string }[];
|
metadata?: { key: string; value: string }[];
|
||||||
usernameTemplate?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateDynamicSecretDTO = {
|
export type TUpdateDynamicSecretDTO = {
|
||||||
@ -302,7 +300,6 @@ export type TUpdateDynamicSecretDTO = {
|
|||||||
defaultTTL?: string;
|
defaultTTL?: string;
|
||||||
maxTTL?: string | null;
|
maxTTL?: string | null;
|
||||||
inputs?: unknown;
|
inputs?: unknown;
|
||||||
usernameTemplate?: string | null;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { MutationCache, QueryClient } from "@tanstack/react-query";
|
import { MutationCache, QueryClient } from "@tanstack/react-query";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
@ -19,15 +18,22 @@ export const SIGNUP_TEMP_TOKEN_CACHE_KEY = ["infisical__signup-temp-token"];
|
|||||||
export const MFA_TEMP_TOKEN_CACHE_KEY = ["infisical__mfa-temp-token"];
|
export const MFA_TEMP_TOKEN_CACHE_KEY = ["infisical__mfa-temp-token"];
|
||||||
export const AUTH_TOKEN_CACHE_KEY = ["infisical__auth-token"];
|
export const AUTH_TOKEN_CACHE_KEY = ["infisical__auth-token"];
|
||||||
|
|
||||||
function ValidationErrorModal({ serverResponse }: { serverResponse: TApiErrors }) {
|
export const onRequestError = (error: unknown) => {
|
||||||
const [open, setOpen] = useState(true);
|
if (axios.isAxiosError(error)) {
|
||||||
|
const serverResponse = error.response?.data as TApiErrors;
|
||||||
if (serverResponse.error !== ApiErrorTypes.ValidationError) {
|
if (serverResponse?.error === ApiErrorTypes.ValidationError) {
|
||||||
return null;
|
createNotification(
|
||||||
}
|
{
|
||||||
|
title: "Validation Error",
|
||||||
return (
|
type: "error",
|
||||||
<Modal isOpen={open} onOpenChange={setOpen}>
|
text: "Please check the input and try again.",
|
||||||
|
callToAction: (
|
||||||
|
<Modal>
|
||||||
|
<ModalTrigger asChild>
|
||||||
|
<Button variant="outline_bg" size="xs">
|
||||||
|
Show more
|
||||||
|
</Button>
|
||||||
|
</ModalTrigger>
|
||||||
<ModalContent title="Validation Error Details">
|
<ModalContent title="Validation Error Details">
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
@ -49,19 +55,7 @@ function ValidationErrorModal({ serverResponse }: { serverResponse: TApiErrors }
|
|||||||
</TableContainer>
|
</TableContainer>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
),
|
||||||
}
|
|
||||||
|
|
||||||
export const onRequestError = (error: unknown) => {
|
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
const serverResponse = error.response?.data as TApiErrors;
|
|
||||||
if (serverResponse?.error === ApiErrorTypes.ValidationError) {
|
|
||||||
createNotification(
|
|
||||||
{
|
|
||||||
title: "Validation Error",
|
|
||||||
type: "error",
|
|
||||||
text: "Please check the input and try again.",
|
|
||||||
callToAction: <ValidationErrorModal serverResponse={serverResponse} />,
|
|
||||||
copyActions: [
|
copyActions: [
|
||||||
{
|
{
|
||||||
value: serverResponse.reqId,
|
value: serverResponse.reqId,
|
||||||
|
@ -1,23 +1,27 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { faMobile } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowLeft, faInfo, faMobile, faQuestion } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { Outlet, useRouterState } from "@tanstack/react-router";
|
import { Link, Outlet } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { WishForm } from "@app/components/features/WishForm";
|
||||||
import { Banner } from "@app/components/page-frames/Banner";
|
import { Banner } from "@app/components/page-frames/Banner";
|
||||||
import { BreadcrumbContainer, TBreadcrumbFormat } from "@app/components/v2";
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { envConfig } from "@app/config/env";
|
||||||
import { useServerConfig } from "@app/context";
|
import { useServerConfig } from "@app/context";
|
||||||
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner";
|
import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner";
|
||||||
import { AdminSidebar } from "./Sidebar";
|
import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar";
|
||||||
|
|
||||||
export const AdminLayout = () => {
|
export const AdminLayout = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { config } = useServerConfig();
|
const { config } = useServerConfig();
|
||||||
|
|
||||||
const matches = useRouterState({ select: (s) => s.matches.at(-1)?.context });
|
|
||||||
|
|
||||||
const breadcrumbs = matches && "breadcrumbs" in matches ? matches.breadcrumbs : undefined;
|
|
||||||
|
|
||||||
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -26,11 +30,54 @@ export const AdminLayout = () => {
|
|||||||
<div className={`dark hidden ${containerHeight} w-full flex-col overflow-x-hidden md:flex`}>
|
<div className={`dark hidden ${containerHeight} w-full flex-col overflow-x-hidden md:flex`}>
|
||||||
{!window.isSecureContext && <InsecureConnectionBanner />}
|
{!window.isSecureContext && <InsecureConnectionBanner />}
|
||||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
||||||
<AdminSidebar />
|
<aside className="dark w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
|
||||||
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 px-4 pb-4 dark:[color-scheme:dark]">
|
<nav className="items-between flex h-full flex-col justify-between overflow-y-auto dark:[color-scheme:dark]">
|
||||||
{breadcrumbs ? (
|
<div className="flex-grow">
|
||||||
<BreadcrumbContainer breadcrumbs={breadcrumbs as TBreadcrumbFormat[]} />
|
<Link to={`/organization/${ProjectType.SecretManager}/overview` as const}>
|
||||||
) : null}
|
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
|
||||||
|
<FontAwesomeIcon icon={faArrowLeft} className="pr-3" />
|
||||||
|
Back to organization
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="relative mt-10 flex w-full cursor-default flex-col items-center px-3 text-sm text-mineshaft-400">
|
||||||
|
{(window.location.origin.includes("https://app.infisical.com") ||
|
||||||
|
window.location.origin.includes("https://gamma.infisical.com")) && <WishForm />}
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<div className="mb-2 w-full pl-5 duration-200 hover:text-mineshaft-200">
|
||||||
|
<FontAwesomeIcon icon={faQuestion} className="mr-3 px-[0.1rem]" />
|
||||||
|
Help & Support
|
||||||
|
</div>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
|
{INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => (
|
||||||
|
<DropdownMenuItem key={url as string}>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={String(url)}
|
||||||
|
className="flex w-full items-center rounded-md font-normal text-mineshaft-300 duration-200"
|
||||||
|
>
|
||||||
|
<div className="relative flex w-full cursor-pointer select-none items-center justify-start rounded-md">
|
||||||
|
{icon}
|
||||||
|
<div className="text-sm">{text}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
{envConfig.PLATFORM_VERSION && (
|
||||||
|
<div className="mb-2 mt-2 w-full cursor-default pl-5 text-sm duration-200 hover:text-mineshaft-200">
|
||||||
|
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
|
||||||
|
Version: {envConfig.PLATFORM_VERSION}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 dark:[color-scheme:dark]">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
import { faArrowLeft, faInfo, faQuestion } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { Link, useMatchRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
Menu,
|
|
||||||
MenuGroup,
|
|
||||||
MenuItem
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { envConfig } from "@app/config/env";
|
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
|
||||||
|
|
||||||
import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar";
|
|
||||||
|
|
||||||
const generalTabs = [
|
|
||||||
{
|
|
||||||
label: "General",
|
|
||||||
icon: "settings-cog",
|
|
||||||
link: "/admin/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Encryption",
|
|
||||||
icon: "lock-closed",
|
|
||||||
link: "/admin/encryption"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Authentication",
|
|
||||||
icon: "check",
|
|
||||||
link: "/admin/authentication"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Integrations",
|
|
||||||
icon: "sliding-carousel",
|
|
||||||
link: "/admin/integrations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Caching",
|
|
||||||
icon: "note",
|
|
||||||
link: "/admin/caching"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const resourceTabs = [
|
|
||||||
{
|
|
||||||
label: "Organizations",
|
|
||||||
icon: "groups",
|
|
||||||
link: "/admin/resources/organizations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "User Identities",
|
|
||||||
icon: "user",
|
|
||||||
link: "/admin/resources/user-identities"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Machine Identities",
|
|
||||||
icon: "key-user",
|
|
||||||
link: "/admin/resources/machine-identities"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const AdminSidebar = () => {
|
|
||||||
const matchRoute = useMatchRoute();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<aside className="dark w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
|
|
||||||
<nav className="items-between flex h-full flex-col justify-between overflow-y-auto dark:[color-scheme:dark]">
|
|
||||||
<div className="flex-grow">
|
|
||||||
<Link to={`/organization/${ProjectType.SecretManager}/overview` as const}>
|
|
||||||
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
|
|
||||||
<FontAwesomeIcon icon={faArrowLeft} className="pr-3" />
|
|
||||||
Back to organization
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<Menu>
|
|
||||||
<MenuGroup title="General">
|
|
||||||
{generalTabs.map((tab) => {
|
|
||||||
const isActive = matchRoute({ to: tab.link, fuzzy: false });
|
|
||||||
return (
|
|
||||||
<Link key={tab.link} to={tab.link}>
|
|
||||||
<MenuItem isSelected={Boolean(isActive)} icon={tab.icon}>
|
|
||||||
{tab.label}
|
|
||||||
</MenuItem>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</MenuGroup>
|
|
||||||
<MenuGroup title="Resources">
|
|
||||||
{resourceTabs.map((tab) => {
|
|
||||||
const isActive = matchRoute({ to: tab.link, fuzzy: false });
|
|
||||||
return (
|
|
||||||
<Link key={tab.link} to={tab.link}>
|
|
||||||
<MenuItem isSelected={Boolean(isActive)} icon={tab.icon}>
|
|
||||||
{tab.label}
|
|
||||||
</MenuItem>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</MenuGroup>
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
<div className="relative mb-4 mt-10 flex w-full cursor-default flex-col items-center px-3 text-sm text-mineshaft-400">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<div className="mb-2 w-full pl-5 duration-200 hover:text-mineshaft-200">
|
|
||||||
<FontAwesomeIcon icon={faQuestion} className="mr-3 px-[0.1rem]" />
|
|
||||||
Help & Support
|
|
||||||
</div>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="start" className="p-1">
|
|
||||||
{INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => (
|
|
||||||
<DropdownMenuItem key={url as string}>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={String(url)}
|
|
||||||
className="flex w-full items-center rounded-md font-normal text-mineshaft-300 duration-200"
|
|
||||||
>
|
|
||||||
<div className="relative flex w-full cursor-pointer select-none items-center justify-start rounded-md">
|
|
||||||
{icon}
|
|
||||||
<div className="text-sm">{text}</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
))}
|
|
||||||
{envConfig.PLATFORM_VERSION && (
|
|
||||||
<div className="mb-2 mt-2 w-full cursor-default pl-5 text-sm duration-200 hover:text-mineshaft-200">
|
|
||||||
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
|
|
||||||
Version: {envConfig.PLATFORM_VERSION}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,27 +0,0 @@
|
|||||||
import { Helmet } from "react-helmet";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { PageHeader } from "@app/components/v2";
|
|
||||||
|
|
||||||
import { AuthenticationPageForm } from "./components";
|
|
||||||
|
|
||||||
export const AuthenticationPage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full bg-bunker-800">
|
|
||||||
<Helmet>
|
|
||||||
<title>{t("common.head-title", { title: "Admin" })}</title>
|
|
||||||
</Helmet>
|
|
||||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
|
||||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
|
||||||
<PageHeader
|
|
||||||
title="Authentication"
|
|
||||||
description="Manage authentication settings for your Infisical instance."
|
|
||||||
/>
|
|
||||||
<AuthenticationPageForm />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export { AuthenticationPageForm } from "./AuthenticationPageForm";
|
|
@ -1,25 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { AuthenticationPage } from "./AuthenticationPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/admin/_admin-layout/authentication"
|
|
||||||
)({
|
|
||||||
component: AuthenticationPage,
|
|
||||||
beforeLoad: async () => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
label: "Admin",
|
|
||||||
link: linkOptions({ to: "/admin" })
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Authentication",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/admin/authentication"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,24 +0,0 @@
|
|||||||
import { Helmet } from "react-helmet";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { PageHeader } from "@app/components/v2";
|
|
||||||
|
|
||||||
import { CachingPageForm } from "./components";
|
|
||||||
|
|
||||||
export const CachingPage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full bg-bunker-800">
|
|
||||||
<Helmet>
|
|
||||||
<title>{t("common.head-title", { title: "Admin" })}</title>
|
|
||||||
</Helmet>
|
|
||||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
|
||||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
|
||||||
<PageHeader title="Caching" description="Manage caching for your Infisical instance." />
|
|
||||||
<CachingPageForm />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export { CachingPageForm } from "./CachingPageForm";
|
|
@ -1,25 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { CachingPage } from "./CachingPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/admin/_admin-layout/caching"
|
|
||||||
)({
|
|
||||||
component: CachingPage,
|
|
||||||
beforeLoad: async () => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
label: "Admin",
|
|
||||||
link: linkOptions({ to: "/admin" })
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Caching",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/admin/caching"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
import { Helmet } from "react-helmet";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { PageHeader } from "@app/components/v2";
|
|
||||||
|
|
||||||
import { EncryptionPageForm } from "./components";
|
|
||||||
|
|
||||||
export const EncryptionPage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full bg-bunker-800">
|
|
||||||
<Helmet>
|
|
||||||
<title>{t("common.head-title", { title: "Admin" })}</title>
|
|
||||||
</Helmet>
|
|
||||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
|
||||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
|
||||||
<PageHeader
|
|
||||||
title="Encryption"
|
|
||||||
description="Manage encryption settings for your Infisical instance."
|
|
||||||
/>
|
|
||||||
<EncryptionPageForm />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export { EncryptionPageForm } from "./EncryptionPageForm";
|
|
@ -1,25 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { EncryptionPage } from "./EncryptionPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
"/_authenticate/_inject-org-details/admin/_admin-layout/encryption"
|
|
||||||
)({
|
|
||||||
component: EncryptionPage,
|
|
||||||
beforeLoad: async () => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
label: "Admin",
|
|
||||||
link: linkOptions({ to: "/admin" })
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Encryption",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/admin/encryption"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
import { Helmet } from "react-helmet";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { PageHeader } from "@app/components/v2";
|
|
||||||
|
|
||||||
import { GeneralPageForm } from "./components";
|
|
||||||
|
|
||||||
export const GeneralPage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-full bg-bunker-800">
|
|
||||||
<Helmet>
|
|
||||||
<title>{t("common.head-title", { title: "Admin" })}</title>
|
|
||||||
</Helmet>
|
|
||||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
|
||||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
|
||||||
<PageHeader
|
|
||||||
title="General"
|
|
||||||
description="Manage general settings for your Infisical instance."
|
|
||||||
/>
|
|
||||||
<GeneralPageForm />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,309 +0,0 @@
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import { faAt } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
FormControl,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
SelectClear,
|
|
||||||
SelectItem,
|
|
||||||
Switch,
|
|
||||||
TextArea
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useServerConfig } from "@app/context";
|
|
||||||
import { useGetOrganizations, useUpdateServerConfig } from "@app/hooks/api";
|
|
||||||
|
|
||||||
enum SignUpModes {
|
|
||||||
Disabled = "disabled",
|
|
||||||
Anyone = "anyone"
|
|
||||||
}
|
|
||||||
|
|
||||||
const formSchema = z.object({
|
|
||||||
signUpMode: z.nativeEnum(SignUpModes),
|
|
||||||
allowedSignUpDomain: z.string().optional().nullable(),
|
|
||||||
trustSamlEmails: z.boolean(),
|
|
||||||
trustLdapEmails: z.boolean(),
|
|
||||||
trustOidcEmails: z.boolean(),
|
|
||||||
defaultAuthOrgId: z.string(),
|
|
||||||
authConsentContent: z.string().optional().default(""),
|
|
||||||
pageFrameContent: z.string().optional().default("")
|
|
||||||
});
|
|
||||||
|
|
||||||
type TDashboardForm = z.infer<typeof formSchema>;
|
|
||||||
|
|
||||||
export const GeneralPageForm = () => {
|
|
||||||
const data = useServerConfig();
|
|
||||||
const { config } = data;
|
|
||||||
|
|
||||||
const {
|
|
||||||
control,
|
|
||||||
handleSubmit,
|
|
||||||
watch,
|
|
||||||
formState: { isSubmitting, isDirty }
|
|
||||||
} = useForm<TDashboardForm>({
|
|
||||||
resolver: zodResolver(formSchema),
|
|
||||||
values: {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
signUpMode: config.allowSignUp ? SignUpModes.Anyone : SignUpModes.Disabled,
|
|
||||||
allowedSignUpDomain: config.allowedSignUpDomain,
|
|
||||||
trustSamlEmails: config.trustSamlEmails,
|
|
||||||
trustLdapEmails: config.trustLdapEmails,
|
|
||||||
trustOidcEmails: config.trustOidcEmails,
|
|
||||||
defaultAuthOrgId: config.defaultAuthOrgId ?? "",
|
|
||||||
authConsentContent: config.authConsentContent ?? "",
|
|
||||||
pageFrameContent: config.pageFrameContent ?? ""
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const signUpMode = watch("signUpMode");
|
|
||||||
const defaultAuthOrgId = watch("defaultAuthOrgId");
|
|
||||||
|
|
||||||
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
|
|
||||||
|
|
||||||
const organizations = useGetOrganizations();
|
|
||||||
|
|
||||||
const onFormSubmit = async (formData: TDashboardForm) => {
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
allowedSignUpDomain,
|
|
||||||
trustSamlEmails,
|
|
||||||
trustLdapEmails,
|
|
||||||
trustOidcEmails,
|
|
||||||
authConsentContent,
|
|
||||||
pageFrameContent
|
|
||||||
} = formData;
|
|
||||||
|
|
||||||
await updateServerConfig({
|
|
||||||
defaultAuthOrgId: defaultAuthOrgId || null,
|
|
||||||
allowSignUp: signUpMode !== SignUpModes.Disabled,
|
|
||||||
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
|
|
||||||
trustSamlEmails,
|
|
||||||
trustLdapEmails,
|
|
||||||
trustOidcEmails,
|
|
||||||
authConsentContent,
|
|
||||||
pageFrameContent
|
|
||||||
});
|
|
||||||
createNotification({
|
|
||||||
text: "Successfully changed sign up setting.",
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
createNotification({
|
|
||||||
type: "error",
|
|
||||||
text: "Failed to update sign up setting."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form
|
|
||||||
className="space-y-8 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Allow user signups</div>
|
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
|
||||||
Select if you want users to be able to signup freely into your Infisical instance.
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="signUpMode"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl className="max-w-sm" errorText={error?.message} isError={Boolean(error)}>
|
|
||||||
<Select
|
|
||||||
className="w-full bg-mineshaft-700"
|
|
||||||
dropdownContainerClassName="bg-mineshaft-800"
|
|
||||||
defaultValue={field.value}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
{...field}
|
|
||||||
>
|
|
||||||
<SelectItem value={SignUpModes.Disabled}>Disabled</SelectItem>
|
|
||||||
<SelectItem value={SignUpModes.Anyone}>Anyone</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{signUpMode === "anyone" && (
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-4 text-xl font-semibold text-mineshaft-100">
|
|
||||||
Restrict signup by email domain(s)
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="allowedSignUpDomain"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Leave blank to allow any email domains"
|
|
||||||
className="w-72"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
value={field.value || ""}
|
|
||||||
placeholder="gmail.com, aws.com, redhat.com"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faAt} />}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Default organization</div>
|
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
|
||||||
Select the default organization you want to set for SAML/LDAP/OIDC/Github logins. When
|
|
||||||
selected, user logins will be automatically scoped to the selected organization.
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="defaultAuthOrgId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl className="max-w-sm" errorText={error?.message} isError={Boolean(error)}>
|
|
||||||
<Select
|
|
||||||
placeholder="Allow all organizations"
|
|
||||||
className="w-full bg-mineshaft-700"
|
|
||||||
dropdownContainerClassName="bg-mineshaft-800"
|
|
||||||
defaultValue={field.value ?? " "}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
{...field}
|
|
||||||
>
|
|
||||||
<SelectClear
|
|
||||||
selectValue={defaultAuthOrgId}
|
|
||||||
onClear={() => {
|
|
||||||
onChange("");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Allow all organizations
|
|
||||||
</SelectClear>
|
|
||||||
{organizations.data?.map((org) => (
|
|
||||||
<SelectItem key={org.id} value={org.id}>
|
|
||||||
{org.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Trust emails</div>
|
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
|
||||||
Select if you want Infisical to trust external emails from SAML/LDAP/OIDC identity
|
|
||||||
providers. If set to false, then Infisical will prompt SAML/LDAP/OIDC provisioned users to
|
|
||||||
verify their email upon their first login.
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="trustSamlEmails"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Switch
|
|
||||||
id="trust-saml-emails"
|
|
||||||
onCheckedChange={(value) => field.onChange(value)}
|
|
||||||
isChecked={field.value}
|
|
||||||
>
|
|
||||||
<p className="w-full">Trust SAML emails</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="trustLdapEmails"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Switch
|
|
||||||
id="trust-ldap-emails"
|
|
||||||
onCheckedChange={(value) => field.onChange(value)}
|
|
||||||
isChecked={field.value}
|
|
||||||
>
|
|
||||||
<p className="w-full">Trust LDAP emails</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="trustOidcEmails"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Switch
|
|
||||||
id="trust-oidc-emails"
|
|
||||||
onCheckedChange={(value) => field.onChange(value)}
|
|
||||||
isChecked={field.value}
|
|
||||||
>
|
|
||||||
<p className="w-full">Trust OIDC emails</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Notices</div>
|
|
||||||
<div className="mb-4 max-w-lg text-sm text-mineshaft-400">
|
|
||||||
Configure system-wide notification banners and security messages. These settings control
|
|
||||||
the text displayed to users during authentication and throughout their session
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Auth Consent Content"
|
|
||||||
tooltipText="Formatting supported: HTML, Markdown, Plain text"
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
placeholder="**Auth Consent Message**"
|
|
||||||
{...field}
|
|
||||||
rows={3}
|
|
||||||
className="thin-scrollbar h-48 max-w-lg !resize-none bg-mineshaft-800"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
control={control}
|
|
||||||
name="authConsentContent"
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Page Frame Content"
|
|
||||||
tooltipText="Formatting supported: HTML, Markdown, Plain text"
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
placeholder='<div style="background-color: red">TOP SECRET</div>'
|
|
||||||
{...field}
|
|
||||||
rows={3}
|
|
||||||
className="thin-scrollbar h-48 max-w-lg !resize-none bg-mineshaft-800"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
control={control}
|
|
||||||
name="pageFrameContent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button type="submit" isLoading={isSubmitting} isDisabled={isSubmitting || !isDirty}>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export { GeneralPageForm } from "./GeneralPageForm";
|
|
@ -1,23 +0,0 @@
|
|||||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { GeneralPage } from "./GeneralPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authenticate/_inject-org-details/admin/_admin-layout/")({
|
|
||||||
component: GeneralPage,
|
|
||||||
beforeLoad: async () => {
|
|
||||||
return {
|
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
label: "Admin",
|
|
||||||
link: linkOptions({ to: "/admin" })
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "General",
|
|
||||||
link: linkOptions({
|
|
||||||
to: "/admin"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|