mirror of
https://github.com/Infisical/infisical.git
synced 2025-08-19 21:17:10 +00:00
Compare commits
4 Commits
feat/autom
...
disable-de
Author | SHA1 | Date | |
---|---|---|---|
|
81f3613393 | ||
|
cc7d0d752f | ||
|
b89212a0c9 | ||
|
d4c69d8e5d |
@@ -1,19 +0,0 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas/models";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds"))) {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||
t.specificType("adminIdentityIds", "text[]");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds")) {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||
t.dropColumn("adminIdentityIds");
|
||||
});
|
||||
}
|
||||
}
|
@@ -25,8 +25,7 @@ export const SuperAdminSchema = z.object({
|
||||
encryptedSlackClientId: zodBuffer.nullable().optional(),
|
||||
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
||||
authConsentContent: z.string().nullable().optional(),
|
||||
pageFrameContent: z.string().nullable().optional(),
|
||||
adminIdentityIds: z.string().array().nullable().optional()
|
||||
pageFrameContent: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
||||
import { daysToMillisecond } from "@app/lib/dates";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||
@@ -5,7 +6,6 @@ import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/pro
|
||||
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
||||
import { daysToMillisecond } from "@app/lib/dates";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { KmipClientsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { KmipPermission } from "@app/ee/services/kmip/kmip-enum";
|
||||
import { KmipClientOrderBy } from "@app/ee/services/kmip/kmip-types";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { OrderByDirection } from "@app/lib/types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
|
@@ -25,7 +25,7 @@ type TSAMLConfig = {
|
||||
callbackUrl: string;
|
||||
entryPoint: string;
|
||||
issuer: string;
|
||||
idpCert: string;
|
||||
cert: string;
|
||||
audience: string;
|
||||
wantAuthnResponseSigned?: boolean;
|
||||
wantAssertionsSigned?: boolean;
|
||||
@@ -72,7 +72,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`,
|
||||
entryPoint: ssoConfig.entryPoint,
|
||||
issuer: ssoConfig.issuer,
|
||||
idpCert: ssoConfig.cert,
|
||||
cert: ssoConfig.cert,
|
||||
audience: appCfg.SITE_URL || ""
|
||||
};
|
||||
if (ssoConfig.authProvider === SamlProviders.JUMPCLOUD_SAML) {
|
||||
@@ -302,21 +302,15 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { isActive, authProvider, issuer, entryPoint, cert } = req.body;
|
||||
const { permission } = req;
|
||||
|
||||
return server.services.saml.createSamlCfg({
|
||||
isActive,
|
||||
authProvider,
|
||||
issuer,
|
||||
entryPoint,
|
||||
idpCert: cert,
|
||||
actor: permission.type,
|
||||
actorId: permission.id,
|
||||
actorAuthMethod: permission.authMethod,
|
||||
actorOrgId: permission.orgId,
|
||||
orgId: req.body.organizationId
|
||||
const saml = await server.services.saml.createSamlCfg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.body.organizationId,
|
||||
...req.body
|
||||
});
|
||||
return saml;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -343,21 +337,15 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { isActive, authProvider, issuer, entryPoint, cert } = req.body;
|
||||
const { permission } = req;
|
||||
|
||||
return server.services.saml.updateSamlCfg({
|
||||
isActive,
|
||||
authProvider,
|
||||
issuer,
|
||||
entryPoint,
|
||||
idpCert: cert,
|
||||
actor: permission.type,
|
||||
actorId: permission.id,
|
||||
actorAuthMethod: permission.authMethod,
|
||||
actorOrgId: permission.orgId,
|
||||
orgId: req.body.organizationId
|
||||
const saml = await server.services.saml.updateSamlCfg({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
orgId: req.body.organizationId,
|
||||
...req.body
|
||||
});
|
||||
return saml;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
||||
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
isValidUserPattern
|
||||
} from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-validators";
|
||||
import { SSH_CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import msFn from "ms";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
@@ -247,7 +246,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
requesterEmail: requestedByUser.email,
|
||||
isTemporary,
|
||||
...(isTemporary && {
|
||||
expiresIn: msFn(ms(temporaryRange || ""), { long: true })
|
||||
expiresIn: ms(ms(temporaryRange || ""), { long: true })
|
||||
}),
|
||||
secretPath,
|
||||
environment: envSlug,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import { packRules } from "@casl/ability/extra";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||
import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
import crypto, { KeyObject } from "crypto";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||
import { isValidHostname, isValidIp } from "@app/lib/ip";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||
import {
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||
import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
|
@@ -63,7 +63,7 @@ export const samlConfigServiceFactory = ({
|
||||
kmsService
|
||||
}: TSamlConfigServiceFactoryDep) => {
|
||||
const createSamlCfg = async ({
|
||||
idpCert,
|
||||
cert,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
@@ -93,9 +93,9 @@ export const samlConfigServiceFactory = ({
|
||||
orgId,
|
||||
authProvider,
|
||||
isActive,
|
||||
encryptedSamlCertificate: encryptor({ plainText: Buffer.from(idpCert) }).cipherTextBlob,
|
||||
encryptedSamlIssuer: encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob,
|
||||
encryptedSamlEntryPoint: encryptor({ plainText: Buffer.from(entryPoint) }).cipherTextBlob,
|
||||
encryptedSamlIssuer: encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob
|
||||
encryptedSamlCertificate: encryptor({ plainText: Buffer.from(cert) }).cipherTextBlob
|
||||
});
|
||||
|
||||
return samlConfig;
|
||||
@@ -106,7 +106,7 @@ export const samlConfigServiceFactory = ({
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
idpCert,
|
||||
cert,
|
||||
actorId,
|
||||
issuer,
|
||||
isActive,
|
||||
@@ -136,8 +136,8 @@ export const samlConfigServiceFactory = ({
|
||||
updateQuery.encryptedSamlIssuer = encryptor({ plainText: Buffer.from(issuer) }).cipherTextBlob;
|
||||
}
|
||||
|
||||
if (idpCert !== undefined) {
|
||||
updateQuery.encryptedSamlCertificate = encryptor({ plainText: Buffer.from(idpCert) }).cipherTextBlob;
|
||||
if (cert !== undefined) {
|
||||
updateQuery.encryptedSamlCertificate = encryptor({ plainText: Buffer.from(cert) }).cipherTextBlob;
|
||||
}
|
||||
|
||||
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
|
||||
|
@@ -15,7 +15,7 @@ export type TCreateSamlCfgDTO = {
|
||||
isActive: boolean;
|
||||
entryPoint: string;
|
||||
issuer: string;
|
||||
idpCert: string;
|
||||
cert: string;
|
||||
} & TOrgPermission;
|
||||
|
||||
export type TUpdateSamlCfgDTO = Partial<{
|
||||
@@ -23,7 +23,7 @@ export type TUpdateSamlCfgDTO = Partial<{
|
||||
isActive: boolean;
|
||||
entryPoint: string;
|
||||
issuer: string;
|
||||
idpCert: string;
|
||||
cert: string;
|
||||
}> &
|
||||
TOrgPermission;
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
import { TSshCertificateAuthorityDALFactory } from "../ssh/ssh-certificate-authority-dal";
|
||||
import { TSshCertificateTemplateDALFactory } from "./ssh-certificate-template-dal";
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { execFile } from "child_process";
|
||||
import crypto from "crypto";
|
||||
import { promises as fs } from "fs";
|
||||
import ms from "ms";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { promisify } from "util";
|
||||
|
||||
import { TSshCertificateTemplates } from "@app/db/schemas";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||
|
||||
import {
|
||||
|
@@ -1725,7 +1725,8 @@ export const SecretSyncs = {
|
||||
SYNC_OPTIONS: (destination: SecretSync) => {
|
||||
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||
return {
|
||||
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`
|
||||
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
||||
disableSecretDeletion: `Enable this flag to prevent removal of secrets from the ${destinationName} destination when syncing.`
|
||||
};
|
||||
},
|
||||
ADDITIONAL_SYNC_OPTIONS: {
|
||||
|
@@ -1,15 +0,0 @@
|
||||
import msFn, { StringValue } from "ms";
|
||||
|
||||
import { BadRequestError } from "../errors";
|
||||
|
||||
export const ms = (val: string) => {
|
||||
if (typeof val !== "string") {
|
||||
throw new BadRequestError({ message: `Date must be string` });
|
||||
}
|
||||
|
||||
try {
|
||||
return msFn(val as StringValue);
|
||||
} catch {
|
||||
throw new BadRequestError({ message: `Invalid date format string: ${val}` });
|
||||
}
|
||||
};
|
@@ -8,7 +8,6 @@ import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
export type TAuthMode =
|
||||
| {
|
||||
@@ -44,7 +43,6 @@ export type TAuthMode =
|
||||
identityName: string;
|
||||
orgId: string;
|
||||
authMethod: null;
|
||||
isInstanceAdmin?: boolean;
|
||||
}
|
||||
| {
|
||||
authMode: AuthMode.SCIM_TOKEN;
|
||||
@@ -131,15 +129,13 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
||||
}
|
||||
case AuthMode.IDENTITY_ACCESS_TOKEN: {
|
||||
const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp);
|
||||
const serverCfg = await getServerCfg();
|
||||
req.auth = {
|
||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||
actor,
|
||||
orgId: identity.orgId,
|
||||
identityId: identity.identityId,
|
||||
identityName: identity.name,
|
||||
authMethod: null,
|
||||
isInstanceAdmin: serverCfg?.adminIdentityIds?.includes(identity.identityId)
|
||||
authMethod: null
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
@@ -1,18 +1,16 @@
|
||||
import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
|
||||
|
||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
|
||||
export const verifySuperAdmin = <T extends FastifyRequest>(
|
||||
req: T,
|
||||
_res: FastifyReply,
|
||||
done: HookHandlerDoneFunction
|
||||
) => {
|
||||
if (isSuperAdmin(req.auth)) {
|
||||
return done();
|
||||
}
|
||||
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Requires elevated super admin privileges"
|
||||
});
|
||||
if (req.auth.actor !== ActorType.USER || !req.auth.user.superAdmin)
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Requires elevated super admin privileges"
|
||||
});
|
||||
done();
|
||||
};
|
||||
|
@@ -637,9 +637,6 @@ export const registerRoutes = async (
|
||||
userDAL,
|
||||
identityDAL,
|
||||
userAliasDAL,
|
||||
identityTokenAuthDAL,
|
||||
identityAccessTokenDAL,
|
||||
identityOrgMembershipDAL,
|
||||
authService: loginService,
|
||||
serverCfgDAL: superAdminDAL,
|
||||
kmsRootConfigDAL,
|
||||
|
@@ -98,7 +98,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -139,7 +139,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -171,16 +171,12 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
identities: IdentitiesSchema.pick({
|
||||
name: true,
|
||||
id: true
|
||||
})
|
||||
.extend({
|
||||
isInstanceAdmin: z.boolean()
|
||||
})
|
||||
.array()
|
||||
}).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -210,7 +206,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -244,7 +240,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -269,7 +265,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -297,7 +293,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -320,7 +316,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifyAuth([AuthMode.JWT])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
@@ -398,141 +394,4 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/identity-management/identities/:identityId/super-admin-access",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
identityId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
identity: IdentitiesSchema.pick({
|
||||
name: true,
|
||||
id: true
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identity = await server.services.superAdmin.deleteIdentitySuperAdminAccess(
|
||||
req.params.identityId,
|
||||
req.permission.id
|
||||
);
|
||||
|
||||
return {
|
||||
identity
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/user-management/users/:userId/admin-access",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
userId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
id: true
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
handler: async (req) => {
|
||||
const user = await server.services.superAdmin.deleteUserSuperAdminAccess(req.params.userId);
|
||||
|
||||
return {
|
||||
user
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/bootstrap",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
email: z.string().email().trim().min(1),
|
||||
password: z.string().trim().min(1),
|
||||
organization: z.string().trim().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
id: true,
|
||||
superAdmin: true
|
||||
}),
|
||||
organization: OrganizationsSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true
|
||||
}),
|
||||
identity: IdentitiesSchema.pick({
|
||||
id: true,
|
||||
name: true
|
||||
}).extend({
|
||||
credentials: z.object({
|
||||
token: z.string()
|
||||
}) // would just be Token AUTH for now
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { user, organization, machineIdentity } = await server.services.superAdmin.bootstrapInstance({
|
||||
...req.body,
|
||||
organizationName: req.body.organization
|
||||
});
|
||||
|
||||
await server.services.telemetry.sendPostHogEvents({
|
||||
event: PostHogEventTypes.AdminInit,
|
||||
distinctId: user.user.username ?? "",
|
||||
properties: {
|
||||
username: user.user.username,
|
||||
email: user.user.email ?? "",
|
||||
lastName: user.user.lastName || "",
|
||||
firstName: user.user.firstName || ""
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully bootstrapped instance",
|
||||
user: user.user,
|
||||
organization,
|
||||
identity: machineIdentity
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-floating-promises */
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { CertificateAuthoritiesSchema, CertificateTemplatesSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { CertificatesSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { CERTIFICATE_AUTHORITIES, CERTIFICATES } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { CertificateTemplateEstConfigsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -11,7 +11,6 @@ import {
|
||||
validateAccountIds,
|
||||
validatePrincipalArns
|
||||
} from "@app/services/identity-aws-auth/identity-aws-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -131,8 +130,7 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -8,7 +8,8 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { validateAzureAuthField } from "@app/services/identity-azure-auth/identity-azure-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
import {} from "../sanitizedSchemas";
|
||||
|
||||
export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -126,8 +127,7 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -8,7 +8,6 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { validateGcpAuthField } from "@app/services/identity-gcp-auth/identity-gcp-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -122,8 +121,7 @@ export const registerIdentityGcpAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -12,7 +12,6 @@ import {
|
||||
validateJwtAuthAudiencesField,
|
||||
validateJwtBoundClaimsField
|
||||
} from "@app/services/identity-jwt-auth/identity-jwt-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
const IdentityJwtAuthResponseSchema = IdentityJwtAuthsSchema.omit({
|
||||
encryptedJwksCaCert: true,
|
||||
@@ -170,8 +169,7 @@ export const registerIdentityJwtAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -7,7 +7,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
const IdentityKubernetesAuthResponseSchema = IdentityKubernetesAuthsSchema.pick({
|
||||
id: true,
|
||||
@@ -148,8 +147,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -11,7 +11,6 @@ import {
|
||||
validateOidcAuthAudiencesField,
|
||||
validateOidcBoundClaimsField
|
||||
} from "@app/services/identity-oidc-auth/identity-oidc-auth-validators";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
const IdentityOidcAuthResponseSchema = IdentityOidcAuthsSchema.pick({
|
||||
id: true,
|
||||
@@ -147,8 +146,7 @@ export const registerIdentityOidcAuthRouter = async (server: FastifyZodProvider)
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -7,7 +7,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||
|
||||
import { SanitizedProjectSchema } from "../sanitizedSchemas";
|
||||
@@ -119,7 +118,6 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.body
|
||||
});
|
||||
|
||||
@@ -168,8 +166,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
id: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
|
@@ -7,7 +7,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
@@ -75,8 +74,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -159,8 +157,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -260,8 +257,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
@@ -316,7 +312,6 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.body
|
||||
});
|
||||
|
||||
@@ -375,7 +370,6 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.query
|
||||
});
|
||||
|
||||
@@ -427,7 +421,6 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
tokenId: req.params.tokenId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth),
|
||||
...req.body
|
||||
});
|
||||
|
||||
@@ -477,8 +470,7 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
tokenId: req.params.tokenId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
tokenId: req.params.tokenId
|
||||
});
|
||||
|
||||
return {
|
||||
|
@@ -7,7 +7,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||
import { isSuperAdmin } from "@app/services/super-admin/super-admin-fns";
|
||||
|
||||
export const sanitizedClientSecretSchema = IdentityUaClientSecretsSchema.pick({
|
||||
id: true,
|
||||
@@ -143,10 +142,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
...req.body,
|
||||
identityId: req.params.identityId,
|
||||
isActorSuperAdmin: isSuperAdmin(req.auth)
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: identityUniversalAuth.orgId,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECT_USERS } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
ProjectUserMembershipRolesSchema
|
||||
} from "@app/db/schemas";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
import { ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { OrderByDirection } from "@app/lib/types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
|
@@ -2,6 +2,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
import crypto, { KeyObject } from "crypto";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ActionProjectType, ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
||||
@@ -9,7 +10,6 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
|
||||
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import ms from "ms";
|
||||
|
||||
import { TCertificateTemplates } from "@app/db/schemas";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
export const validateCertificateDetailsAgainstTemplate = (
|
||||
cert: {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
@@ -8,7 +9,6 @@ import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { isUuidV4 } from "@app/lib/validator";
|
||||
|
||||
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
||||
|
@@ -16,7 +16,6 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityAwsAuthDALFactory } from "./identity-aws-auth-dal";
|
||||
import { extractPrincipalArn } from "./identity-aws-auth-fns";
|
||||
import {
|
||||
@@ -150,11 +149,8 @@ export const identityAwsAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachAwsAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
@@ -16,7 +16,6 @@ export type TAttachAwsAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateAwsAuthDTO = {
|
||||
|
@@ -14,7 +14,6 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityAzureAuthDALFactory } from "./identity-azure-auth-dal";
|
||||
import { validateAzureIdentity } from "./identity-azure-auth-fns";
|
||||
import {
|
||||
@@ -123,11 +122,8 @@ export const identityAzureAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachAzureAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
@@ -14,7 +14,6 @@ export type TAttachAzureAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateAzureAuthDTO = {
|
||||
|
@@ -14,7 +14,6 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityGcpAuthDALFactory } from "./identity-gcp-auth-dal";
|
||||
import { validateIamIdentity, validateIdTokenIdentity } from "./identity-gcp-auth-fns";
|
||||
import {
|
||||
@@ -163,11 +162,8 @@ export const identityGcpAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachGcpAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
@@ -15,7 +15,6 @@ export type TAttachGcpAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateGcpAuthDTO = {
|
||||
|
@@ -18,7 +18,6 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityJwtAuthDALFactory } from "./identity-jwt-auth-dal";
|
||||
import { doesFieldValueMatchJwtPolicy } from "./identity-jwt-auth-fns";
|
||||
import {
|
||||
@@ -249,11 +248,8 @@ export const identityJwtAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachJwtAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) {
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
@@ -19,7 +19,6 @@ export type TAttachJwtAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateJwtAuthDTO = {
|
||||
|
@@ -18,7 +18,6 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityKubernetesAuthDALFactory } from "./identity-kubernetes-auth-dal";
|
||||
import { extractK8sUsername } from "./identity-kubernetes-auth-fns";
|
||||
import {
|
||||
@@ -228,11 +227,8 @@ export const identityKubernetesAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachKubernetesAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
@@ -17,7 +17,6 @@ export type TAttachKubernetesAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateKubernetesAuthDTO = {
|
||||
|
@@ -19,7 +19,6 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
||||
import { doesAudValueMatchOidcPolicy, doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
|
||||
import {
|
||||
@@ -197,10 +196,8 @@ export const identityOidcAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachOidcAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) {
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
@@ -12,7 +12,6 @@ export type TAttachOidcAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateOidcAuthDTO = {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
@@ -6,7 +7,6 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
|
||||
import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
|
@@ -14,7 +14,6 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityTokenAuthDALFactory } from "./identity-token-auth-dal";
|
||||
import {
|
||||
TAttachTokenAuthDTO,
|
||||
@@ -60,11 +59,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachTokenAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -130,11 +126,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TUpdateTokenAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -225,11 +218,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TRevokeTokenAuthDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -281,11 +271,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
name,
|
||||
isActorSuperAdmin
|
||||
name
|
||||
}: TCreateTokenAuthTokenDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -363,11 +350,8 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TGetTokenAuthTokensDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
@@ -402,8 +386,7 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TUpdateTokenAuthTokenDTO) => {
|
||||
const foundToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
||||
@@ -415,8 +398,6 @@ export const identityTokenAuthServiceFactory = ({
|
||||
if (!identityMembershipOrg) {
|
||||
throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` });
|
||||
}
|
||||
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(foundToken.identityId, isActorSuperAdmin);
|
||||
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have Token Auth"
|
||||
@@ -465,22 +446,18 @@ export const identityTokenAuthServiceFactory = ({
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TRevokeTokenAuthTokenDTO) => {
|
||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
||||
[`${TableName.IdentityAccessToken}.isAccessTokenRevoked` as "isAccessTokenRevoked"]: false,
|
||||
[`${TableName.IdentityAccessToken}.authMethod` as "authMethod"]: IdentityAuthMethod.TOKEN_AUTH
|
||||
});
|
||||
|
||||
if (!identityAccessToken)
|
||||
throw new NotFoundError({
|
||||
message: `Token with ID ${tokenId} not found or already revoked`
|
||||
});
|
||||
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityAccessToken.identityId, isActorSuperAdmin);
|
||||
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({
|
||||
identityId: identityAccessToken.identityId
|
||||
});
|
||||
|
@@ -6,7 +6,6 @@ export type TAttachTokenAuthDTO = {
|
||||
accessTokenMaxTTL: number;
|
||||
accessTokenNumUsesLimit: number;
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateTokenAuthDTO = {
|
||||
@@ -15,7 +14,6 @@ export type TUpdateTokenAuthDTO = {
|
||||
accessTokenMaxTTL?: number;
|
||||
accessTokenNumUsesLimit?: number;
|
||||
accessTokenTrustedIps?: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetTokenAuthDTO = {
|
||||
@@ -24,29 +22,24 @@ export type TGetTokenAuthDTO = {
|
||||
|
||||
export type TRevokeTokenAuthDTO = {
|
||||
identityId: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TCreateTokenAuthTokenDTO = {
|
||||
identityId: string;
|
||||
name?: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetTokenAuthTokensDTO = {
|
||||
identityId: string;
|
||||
offset: number;
|
||||
limit: number;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateTokenAuthTokenDTO = {
|
||||
tokenId: string;
|
||||
name?: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TRevokeTokenAuthTokenDTO = {
|
||||
tokenId: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
@@ -17,7 +17,6 @@ import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityUaClientSecretDALFactory } from "./identity-ua-client-secret-dal";
|
||||
import { TIdentityUaDALFactory } from "./identity-ua-dal";
|
||||
import {
|
||||
@@ -151,11 +150,8 @@ export const identityUaServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId,
|
||||
isActorSuperAdmin
|
||||
actorOrgId
|
||||
}: TAttachUaDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
|
||||
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||
|
||||
|
@@ -7,7 +7,6 @@ export type TAttachUaDTO = {
|
||||
accessTokenNumUsesLimit: number;
|
||||
clientSecretTrustedIps: { ipAddress: string }[];
|
||||
accessTokenTrustedIps: { ipAddress: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateUaDTO = {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName, TIdentities } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
|
||||
export type TIdentityDALFactory = ReturnType<typeof identityDALFactory>;
|
||||
|
||||
|
@@ -9,7 +9,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
|
||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityDALFactory } from "./identity-dal";
|
||||
import { TIdentityMetadataDALFactory } from "./identity-metadata-dal";
|
||||
import { TIdentityOrgDALFactory } from "./identity-org-dal";
|
||||
@@ -113,11 +112,8 @@ export const identityServiceFactory = ({
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
metadata,
|
||||
isActorSuperAdmin
|
||||
metadata
|
||||
}: TUpdateIdentityDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(id, isActorSuperAdmin);
|
||||
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id });
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
@@ -213,16 +209,7 @@ export const identityServiceFactory = ({
|
||||
return identity;
|
||||
};
|
||||
|
||||
const deleteIdentity = async ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
id,
|
||||
isActorSuperAdmin
|
||||
}: TDeleteIdentityDTO) => {
|
||||
await validateIdentityUpdateForSuperAdminPrivileges(id, isActorSuperAdmin);
|
||||
|
||||
const deleteIdentity = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TDeleteIdentityDTO) => {
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id });
|
||||
if (!identityOrgMembership) throw new NotFoundError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
|
@@ -12,12 +12,10 @@ export type TUpdateIdentityDTO = {
|
||||
role?: string;
|
||||
name?: string;
|
||||
metadata?: { key: string; value: string }[];
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
export type TDeleteIdentityDTO = {
|
||||
id: string;
|
||||
isActorSuperAdmin?: boolean;
|
||||
} & Omit<TOrgPermission, "orgId">;
|
||||
|
||||
export type TGetIdentityByIdDTO = {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
@@ -10,7 +11,6 @@ import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
|
@@ -382,6 +382,8 @@ export const AwsParameterStoreSyncFns = {
|
||||
}
|
||||
}
|
||||
|
||||
if (syncOptions.disableSecretDeletion) return;
|
||||
|
||||
const parametersToDelete: AWS.SSM.Parameter[] = [];
|
||||
|
||||
for (const entry of Object.entries(awsParameterStoreSecretsRecord)) {
|
||||
|
@@ -396,6 +396,8 @@ export const AwsSecretsManagerSyncFns = {
|
||||
}
|
||||
}
|
||||
|
||||
if (syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const secretKey of Object.keys(awsSecretsRecord)) {
|
||||
if (!(secretKey in secretMap) || !secretMap[secretKey].value) {
|
||||
try {
|
||||
|
@@ -136,6 +136,8 @@ export const azureAppConfigurationSyncFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const key of Object.keys(azureAppConfigSecrets)) {
|
||||
const azureSecret = azureAppConfigSecrets[key];
|
||||
if (
|
||||
|
@@ -189,6 +189,8 @@ export const azureKeyVaultSyncFactory = ({ kmsService, appConnectionDAL }: TAzur
|
||||
});
|
||||
}
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const deleteSecretKey of deleteSecrets.filter(
|
||||
(secret) => !setSecrets.find((setSecret) => setSecret.key === secret)
|
||||
)) {
|
||||
|
@@ -112,6 +112,8 @@ export const databricksSyncFactory = ({ kmsService, appConnectionDAL }: TDatabri
|
||||
accessToken
|
||||
});
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const secret of databricksSecretKeys) {
|
||||
if (!(secret.key in secretMap)) {
|
||||
await deleteDatabricksSecrets({
|
||||
|
@@ -147,6 +147,9 @@ export const GcpSyncFns = {
|
||||
for await (const key of Object.keys(gcpSecrets)) {
|
||||
try {
|
||||
if (!(key in secretMap) || !secretMap[key].value) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (secretSync.syncOptions.disableSecretDeletion) continue;
|
||||
|
||||
// case: delete secret
|
||||
await request.delete(
|
||||
`${IntegrationUrls.GCP_SECRET_MANAGER_URL}/v1/projects/${destinationConfig.projectId}/secrets/${key}`,
|
||||
|
@@ -192,12 +192,6 @@ export const GithubSyncFns = {
|
||||
|
||||
const publicKey = await getPublicKey(client, secretSync);
|
||||
|
||||
for await (const encryptedSecret of encryptedSecrets) {
|
||||
if (!(encryptedSecret.name in secretMap)) {
|
||||
await deleteSecret(client, secretSync, encryptedSecret);
|
||||
}
|
||||
}
|
||||
|
||||
await sodium.ready.then(async () => {
|
||||
for await (const key of Object.keys(secretMap)) {
|
||||
// convert secret & base64 key to Uint8Array.
|
||||
@@ -224,6 +218,14 @@ export const GithubSyncFns = {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const encryptedSecret of encryptedSecrets) {
|
||||
if (!(encryptedSecret.name in secretMap)) {
|
||||
await deleteSecret(client, secretSync, encryptedSecret);
|
||||
}
|
||||
}
|
||||
},
|
||||
getSecrets: async (secretSync: TGitHubSyncWithCredentials) => {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
|
@@ -196,6 +196,8 @@ export const HumanitecSyncFns = {
|
||||
}
|
||||
}
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const humanitecSecret of humanitecSecrets) {
|
||||
if (!secretMap[humanitecSecret.key]) {
|
||||
await deleteSecret(secretSync, humanitecSecret);
|
||||
|
@@ -23,7 +23,8 @@ const BaseSyncOptionsSchema = <T extends AnyZodObject | undefined = undefined>({
|
||||
initialSyncBehavior: (canImportSecrets
|
||||
? z.nativeEnum(SecretSyncInitialSyncBehavior)
|
||||
: z.literal(SecretSyncInitialSyncBehavior.OverwriteDestination)
|
||||
).describe(SecretSyncs.SYNC_OPTIONS(destination).initialSyncBehavior)
|
||||
).describe(SecretSyncs.SYNC_OPTIONS(destination).initialSyncBehavior),
|
||||
disableSecretDeletion: z.boolean().optional().describe(SecretSyncs.SYNC_OPTIONS(destination).disableSecretDeletion)
|
||||
});
|
||||
|
||||
const schema = merge ? baseSchema.merge(merge) : baseSchema;
|
||||
|
@@ -1,30 +0,0 @@
|
||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||
|
||||
import { ActorType } from "../auth/auth-type";
|
||||
import { getServerCfg } from "./super-admin-service";
|
||||
|
||||
export const isSuperAdmin = (auth: TAuthMode) => {
|
||||
if (auth.actor === ActorType.USER && auth.user.superAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auth.actor === ActorType.IDENTITY && auth.isInstanceAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const validateIdentityUpdateForSuperAdminPrivileges = async (
|
||||
identityId: string,
|
||||
isActorSuperAdmin?: boolean
|
||||
) => {
|
||||
const serverCfg = await getServerCfg();
|
||||
if (serverCfg.adminIdentityIds?.includes(identityId) && !isActorSuperAdmin) {
|
||||
throw new ForbiddenRequestError({
|
||||
message:
|
||||
"You are attempting to modify an instance admin identity. This requires elevated instance admin privileges"
|
||||
});
|
||||
}
|
||||
};
|
@@ -1,21 +1,15 @@
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||
import { getUserPrivateKey } from "@app/lib/crypto/srp";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||
|
||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||
import { AuthMethod, AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { TIdentityTokenAuthDALFactory } from "../identity-token-auth/identity-token-auth-dal";
|
||||
import { AuthMethod } from "../auth/auth-type";
|
||||
import { KMS_ROOT_CONFIG_UUID } from "../kms/kms-fns";
|
||||
import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -25,19 +19,11 @@ import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||
import {
|
||||
LoginMethod,
|
||||
TAdminBootstrapInstanceDTO,
|
||||
TAdminGetIdentitiesDTO,
|
||||
TAdminGetUsersDTO,
|
||||
TAdminSignUpDTO
|
||||
} from "./super-admin-types";
|
||||
import { LoginMethod, TAdminGetIdentitiesDTO, TAdminGetUsersDTO, TAdminSignUpDTO } from "./super-admin-types";
|
||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||
|
||||
type TSuperAdminServiceFactoryDep = {
|
||||
identityDAL: TIdentityDALFactory;
|
||||
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||
identityDAL: Pick<TIdentityDALFactory, "getIdentitiesByFilter">;
|
||||
serverCfgDAL: TSuperAdminDALFactory;
|
||||
userDAL: TUserDALFactory;
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
||||
@@ -74,10 +60,7 @@ export const superAdminServiceFactory = ({
|
||||
keyStore,
|
||||
kmsRootConfigDAL,
|
||||
kmsService,
|
||||
licenseService,
|
||||
identityAccessTokenDAL,
|
||||
identityTokenAuthDAL,
|
||||
identityOrgMembershipDAL
|
||||
licenseService
|
||||
}: TSuperAdminServiceFactoryDep) => {
|
||||
const initServerCfg = async () => {
|
||||
// TODO(akhilmhdh): bad pattern time less change this later to me itself
|
||||
@@ -291,137 +274,6 @@ export const superAdminServiceFactory = ({
|
||||
return { token, user: userInfo, organization };
|
||||
};
|
||||
|
||||
const bootstrapInstance = async ({ email, password, organizationName }: TAdminBootstrapInstanceDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
|
||||
if (serverCfg?.initialized) {
|
||||
throw new BadRequestError({ message: "Instance has already been set up" });
|
||||
}
|
||||
|
||||
const existingUser = await userDAL.findOne({ email });
|
||||
if (existingUser) throw new BadRequestError({ name: "Instance initialization", message: "User already exists" });
|
||||
|
||||
const userInfo = await userDAL.transaction(async (tx) => {
|
||||
const newUser = await userDAL.create(
|
||||
{
|
||||
firstName: "Admin",
|
||||
lastName: "User",
|
||||
username: email,
|
||||
email,
|
||||
superAdmin: true,
|
||||
isGhost: false,
|
||||
isAccepted: true,
|
||||
authMethods: [AuthMethod.EMAIL],
|
||||
isEmailVerified: true
|
||||
},
|
||||
tx
|
||||
);
|
||||
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(password);
|
||||
const encKeys = await generateUserSrpKeys(email, password);
|
||||
|
||||
const userEnc = await userDAL.createUserEncryption(
|
||||
{
|
||||
userId: newUser.id,
|
||||
encryptionVersion: 2,
|
||||
protectedKey: encKeys.protectedKey,
|
||||
protectedKeyIV: encKeys.protectedKeyIV,
|
||||
protectedKeyTag: encKeys.protectedKeyTag,
|
||||
publicKey: encKeys.publicKey,
|
||||
encryptedPrivateKey: encKeys.encryptedPrivateKey,
|
||||
iv: encKeys.encryptedPrivateKeyIV,
|
||||
tag: encKeys.encryptedPrivateKeyTag,
|
||||
salt: encKeys.salt,
|
||||
verifier: encKeys.verifier,
|
||||
serverEncryptedPrivateKeyEncoding: encoding,
|
||||
serverEncryptedPrivateKeyTag: tag,
|
||||
serverEncryptedPrivateKeyIV: iv,
|
||||
serverEncryptedPrivateKey: ciphertext
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
return { user: newUser, enc: userEnc };
|
||||
});
|
||||
|
||||
const initialOrganizationName = organizationName ?? "Admin Org";
|
||||
|
||||
const organization = await orgService.createOrganization({
|
||||
userId: userInfo.user.id,
|
||||
userEmail: userInfo.user.email,
|
||||
orgName: initialOrganizationName
|
||||
});
|
||||
|
||||
const { identity, credentials } = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity = await identityDAL.create({ name: "Instance Admin Identity" }, tx);
|
||||
await identityOrgMembershipDAL.create(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
orgId: organization.id,
|
||||
role: OrgMembershipRole.Admin
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const tokenAuth = await identityTokenAuthDAL.create(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
accessTokenMaxTTL: 0,
|
||||
accessTokenTTL: 0,
|
||||
accessTokenNumUsesLimit: 0,
|
||||
accessTokenTrustedIps: JSON.stringify([
|
||||
{
|
||||
type: "ipv4",
|
||||
prefix: 0,
|
||||
ipAddress: "0.0.0.0"
|
||||
},
|
||||
{
|
||||
type: "ipv6",
|
||||
prefix: 0,
|
||||
ipAddress: "::"
|
||||
}
|
||||
])
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
isAccessTokenRevoked: false,
|
||||
accessTokenTTL: tokenAuth.accessTokenTTL,
|
||||
accessTokenMaxTTL: tokenAuth.accessTokenMaxTTL,
|
||||
accessTokenNumUses: 0,
|
||||
accessTokenNumUsesLimit: tokenAuth.accessTokenNumUsesLimit,
|
||||
name: "Instance Admin Token",
|
||||
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const generatedAccessToken = jwt.sign(
|
||||
{
|
||||
identityId: newIdentity.id,
|
||||
identityAccessTokenId: newToken.id,
|
||||
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
||||
} as TIdentityAccessTokenJwtPayload,
|
||||
appCfg.AUTH_SECRET
|
||||
);
|
||||
|
||||
return { identity: newIdentity, auth: tokenAuth, credentials: { token: generatedAccessToken } };
|
||||
});
|
||||
|
||||
await updateServerCfg({ initialized: true, adminIdentityIds: [identity.id] }, userInfo.user.id);
|
||||
|
||||
return {
|
||||
user: userInfo,
|
||||
organization,
|
||||
machineIdentity: {
|
||||
...identity,
|
||||
credentials
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const getUsers = ({ offset, limit, searchTerm, adminsOnly }: TAdminGetUsersDTO) => {
|
||||
return userDAL.getUsersByFilter({
|
||||
limit,
|
||||
@@ -437,46 +289,13 @@ export const superAdminServiceFactory = ({
|
||||
return user;
|
||||
};
|
||||
|
||||
const deleteIdentitySuperAdminAccess = async (identityId: string, actorId: string) => {
|
||||
const identity = await identityDAL.findById(identityId);
|
||||
if (!identity) {
|
||||
throw new NotFoundError({ name: "Identity", message: "Identity not found" });
|
||||
}
|
||||
|
||||
const currentAdminIdentityIds = (await getServerCfg()).adminIdentityIds ?? [];
|
||||
if (!currentAdminIdentityIds?.includes(identityId)) {
|
||||
throw new BadRequestError({ name: "Identity", message: "Identity does not have super admin access" });
|
||||
}
|
||||
|
||||
await updateServerCfg({ adminIdentityIds: currentAdminIdentityIds.filter((id) => id !== identityId) }, actorId);
|
||||
|
||||
return identity;
|
||||
};
|
||||
|
||||
const deleteUserSuperAdminAccess = async (userId: string) => {
|
||||
const user = await userDAL.findById(userId);
|
||||
if (!user) {
|
||||
throw new NotFoundError({ name: "User", message: "User not found" });
|
||||
}
|
||||
|
||||
const updatedUser = userDAL.updateById(userId, { superAdmin: false });
|
||||
|
||||
return updatedUser;
|
||||
};
|
||||
|
||||
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||
const identities = await identityDAL.getIdentitiesByFilter({
|
||||
const getIdentities = ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||
return identityDAL.getIdentitiesByFilter({
|
||||
limit,
|
||||
offset,
|
||||
searchTerm,
|
||||
sortBy: "name"
|
||||
});
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
return identities.map((identity) => ({
|
||||
...identity,
|
||||
isInstanceAdmin: Boolean(serverCfg?.adminIdentityIds?.includes(identity.id))
|
||||
}));
|
||||
};
|
||||
|
||||
const grantServerAdminAccessToUser = async (userId: string) => {
|
||||
@@ -574,15 +393,12 @@ export const superAdminServiceFactory = ({
|
||||
initServerCfg,
|
||||
updateServerCfg,
|
||||
adminSignUp,
|
||||
bootstrapInstance,
|
||||
getUsers,
|
||||
deleteUser,
|
||||
getIdentities,
|
||||
getAdminSlackConfig,
|
||||
updateRootEncryptionStrategy,
|
||||
getConfiguredEncryptionStrategies,
|
||||
grantServerAdminAccessToUser,
|
||||
deleteIdentitySuperAdminAccess,
|
||||
deleteUserSuperAdminAccess
|
||||
grantServerAdminAccessToUser
|
||||
};
|
||||
};
|
||||
|
@@ -16,12 +16,6 @@ export type TAdminSignUpDTO = {
|
||||
userAgent: string;
|
||||
};
|
||||
|
||||
export type TAdminBootstrapInstanceDTO = {
|
||||
email: string;
|
||||
password: string;
|
||||
organizationName: string;
|
||||
};
|
||||
|
||||
export type TAdminGetUsersDTO = {
|
||||
offset: number;
|
||||
limit: number;
|
||||
|
@@ -600,23 +600,3 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (map[string]interface{}, error) {
|
||||
var resBody map[string]interface{}
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&resBody).
|
||||
SetHeader("User-Agent", USER_AGENT).
|
||||
SetBody(request).
|
||||
Post(fmt.Sprintf("%v/v1/admin/bootstrap", request.Domain))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CallBootstrapInstance: Unable to complete api request [err=%w]", err)
|
||||
}
|
||||
|
||||
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 resBody, nil
|
||||
}
|
||||
|
@@ -654,10 +654,3 @@ type ExchangeRelayCertResponseV1 struct {
|
||||
Certificate string `json:"certificate"`
|
||||
CertificateChain string `json:"certificateChain"`
|
||||
}
|
||||
|
||||
type BootstrapInstanceRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Organization string `json:"organization"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var bootstrapCmd = &cobra.Command{
|
||||
Use: "bootstrap",
|
||||
Short: "Used to bootstrap your Infisical instance",
|
||||
DisableFlagsInUseLine: true,
|
||||
Example: "infisical bootstrap",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
email, _ := cmd.Flags().GetString("email")
|
||||
if email == "" {
|
||||
if envEmail, ok := os.LookupEnv("INFISICAL_ADMIN_EMAIL"); ok {
|
||||
email = envEmail
|
||||
}
|
||||
}
|
||||
|
||||
if email == "" {
|
||||
log.Error().Msg("email is required")
|
||||
return
|
||||
}
|
||||
|
||||
password, _ := cmd.Flags().GetString("password")
|
||||
if password == "" {
|
||||
if envPassword, ok := os.LookupEnv("INFISICAL_ADMIN_PASSWORD"); ok {
|
||||
password = envPassword
|
||||
}
|
||||
}
|
||||
|
||||
if password == "" {
|
||||
log.Error().Msg("password is required")
|
||||
return
|
||||
}
|
||||
|
||||
organization, _ := cmd.Flags().GetString("organization")
|
||||
if organization == "" {
|
||||
if envOrganization, ok := os.LookupEnv("INFISICAL_ADMIN_ORGANIZATION"); ok {
|
||||
organization = envOrganization
|
||||
}
|
||||
}
|
||||
|
||||
if organization == "" {
|
||||
log.Error().Msg("organization is required")
|
||||
return
|
||||
}
|
||||
|
||||
domain, _ := cmd.Flags().GetString("domain")
|
||||
if domain == "" {
|
||||
if envDomain, ok := os.LookupEnv("INFISICAL_API_URL"); ok {
|
||||
domain = envDomain
|
||||
}
|
||||
}
|
||||
|
||||
if domain == "" {
|
||||
log.Error().Msg("domain is required")
|
||||
return
|
||||
}
|
||||
|
||||
httpClient := resty.New().
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
bootstrapResponse, err := api.CallBootstrapInstance(httpClient, api.BootstrapInstanceRequest{
|
||||
Domain: util.AppendAPIEndpoint(domain),
|
||||
Email: email,
|
||||
Password: password,
|
||||
Organization: organization,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Error().Msgf("Failed to bootstrap instance: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ")
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("Failed to convert response to JSON: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(responseJSON))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
bootstrapCmd.Flags().String("domain", "", "The domain of your self-hosted Infisical instance")
|
||||
bootstrapCmd.Flags().String("email", "", "The desired email address of the instance admin")
|
||||
bootstrapCmd.Flags().String("password", "", "The desired password of the instance admin")
|
||||
bootstrapCmd.Flags().String("organization", "", "The name of the organization to create for the instance")
|
||||
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
}
|
@@ -15,3 +15,15 @@ Since Infisical's team is globally distributed, it is hard for us to keep track
|
||||
## Winter break
|
||||
|
||||
Every year, Infisical team goes on a company-wide vacation during winter holidays. This year, the winter break period starts on December 21st, 2024 and ends on January 5th, 2025. You should expect to do no scheduled work during this period, but we will have a rotation process for [high and urgent service disruptions](https://infisical.com/sla).
|
||||
|
||||
## Parental leave
|
||||
|
||||
At Infisical, we recognize that parental leave is a special and important time, significantly different from a typical vacation. We’re proud to offer parental leave to everyone, regardless of gender, and whether you’ve become a parent through childbirth or adoption.
|
||||
|
||||
For team members who have been with Infisical for over a year by the time of your child’s birth or adoption, you are eligible for up to 12 weeks of paid parental leave. This leave will be provided in one continuous block to allow you uninterrupted time with your family. If you have been with Infisical for less than a year, we will follow the parental leave provisions required by your local jurisdiction.
|
||||
|
||||
While we trust your judgment, parental leave is intended to be a distinct benefit and is not designed to be combined with our unlimited PTO policy. To ensure fairness and balance, we generally discourage combining parental leave with an extended vacation.
|
||||
|
||||
When you’re ready, please notify Maidul about your plans for parental leave, ideally at least four months in advance. This allows us to support you fully and arrange any necessary logistics, including salary adjustments and statutory paperwork.
|
||||
|
||||
We’re here to support you as you embark on this exciting new chapter in your life!
|
||||
|
@@ -1,132 +0,0 @@
|
||||
---
|
||||
title: "infisical bootstrap"
|
||||
description: "Automate the initial setup of a new Infisical instance for headless deployment and infrastructure-as-code workflows"
|
||||
---
|
||||
|
||||
```bash
|
||||
infisical bootstrap --domain=<domain> --email=<email> --password=<password> --organization=<organization>
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
The `infisical bootstrap` command is used when deploying Infisical in automated environments where manual UI setup is not feasible. It's ideal for:
|
||||
|
||||
- Containerized deployments in Kubernetes or Docker environments
|
||||
- Infrastructure-as-code pipelines with Terraform or similar tools
|
||||
- Continuous deployment workflows
|
||||
- DevOps automation scenarios
|
||||
|
||||
The command initializes a fresh Infisical instance by creating an admin user, organization, and instance admin machine identity, enabling subsequent programmatic configuration without human intervention.
|
||||
|
||||
<Warning>
|
||||
This command creates an instance admin machine identity with the highest level
|
||||
of privileges. The returned token should be treated with the utmost security,
|
||||
similar to a root credential. Unauthorized access to this token could
|
||||
compromise your entire Infisical instance.
|
||||
</Warning>
|
||||
|
||||
## Flags
|
||||
|
||||
<Accordion title="--domain" defaultOpen="true">
|
||||
The URL of your Infisical instance. This can be set using the `INFISICAL_API_URL` environment variable.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
infisical bootstrap --domain=https://your-infisical-instance.com
|
||||
```
|
||||
|
||||
This flag is required.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="--email">
|
||||
Email address for the admin user account that will be created. This can be set using the `INFISICAL_ADMIN_EMAIL` environment variable.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
infisical bootstrap --email=admin@example.com
|
||||
```
|
||||
|
||||
This flag is required.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="--password">
|
||||
Password for the admin user account. This can be set using the `INFISICAL_ADMIN_PASSWORD` environment variable.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
infisical bootstrap --password=your-secure-password
|
||||
```
|
||||
|
||||
This flag is required.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="--organization">
|
||||
Name of the organization that will be created within the instance. This can be set using the `INFISICAL_ADMIN_ORGANIZATION` environment variable.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
infisical bootstrap --organization=your-org-name
|
||||
```
|
||||
|
||||
This flag is required.
|
||||
|
||||
</Accordion>
|
||||
|
||||
## Response
|
||||
|
||||
The command returns a JSON response with details about the created user, organization, and machine identity:
|
||||
|
||||
```json
|
||||
{
|
||||
"identity": {
|
||||
"credentials": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eUlkIjoiZGIyMjQ3OTItZWQxOC00Mjc3LTlkYWUtNTdlNzUyMzE1ODU0IiwiaWRlbnRpdHlBY2Nlc3NUb2tlbklkIjoiZmVkZmZmMGEtYmU3Yy00NjViLWEwZWEtZjM5OTNjMTg4OGRlIiwiYXV0aFRva2VuVHlwZSI6ImlkZW50aXR5QWNjZXNzVG9rZW4iLCJpYXQiOjE3NDIzMjI0ODl9.mqcZZqIFqER1e9ubrQXp8FbzGYi8nqqZwfMvz09g-8Y"
|
||||
},
|
||||
"id": "db224792-ed18-4277-9dae-57e752315854",
|
||||
"name": "Instance Admin Identity"
|
||||
},
|
||||
"message": "Successfully bootstrapped instance",
|
||||
"organization": {
|
||||
"id": "b56bece0-42f5-4262-b25e-be7bf5f84957",
|
||||
"name": "dog",
|
||||
"slug": "dog-v-e5l"
|
||||
},
|
||||
"user": {
|
||||
"email": "admin@example.com",
|
||||
"firstName": "Admin",
|
||||
"id": "a418f355-c8da-453c-bbc8-6c07208eeb3c",
|
||||
"lastName": "User",
|
||||
"superAdmin": true,
|
||||
"username": "admin@example.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage with Automation
|
||||
|
||||
For automation purposes, you can extract just the machine identity token from the response:
|
||||
|
||||
```bash
|
||||
infisical bootstrap --domain=https://your-infisical-instance.com --email=admin@example.com --password=your-secure-password --organization=your-org-name | jq ".identity.credentials.token"
|
||||
```
|
||||
|
||||
This extracts only the token, which can be captured in a variable or piped to other commands.
|
||||
|
||||
## Example: Capture Token in a Variable
|
||||
|
||||
```bash
|
||||
TOKEN=$(infisical bootstrap --domain=https://your-infisical-instance.com --email=admin@example.com --password=your-secure-password --organization=your-org-name | jq -r ".identity.credentials.token")
|
||||
|
||||
# Now use the token for further automation
|
||||
echo "Token has been captured and can be used for authentication"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The bootstrap process can only be performed once on a fresh Infisical instance
|
||||
- All flags are required for the bootstrap process to complete successfully
|
||||
- Security controls prevent privilege escalation: instance admin identities cannot be managed by non-instance admin users and identities
|
||||
- The generated admin user account can be used to log in via the UI if needed
|
@@ -6,7 +6,7 @@ description: "Learn how to manage secrets in local development environments."
|
||||
|
||||
## Problem at hand
|
||||
|
||||
There are a number of issues that arise with secret management in local development environment:
|
||||
There is a number of issues that arise with secret management in local development environment:
|
||||
1. **Getting secrets onto local machines**. When new developers join or a new project is created, the process of getting the development set of secrets onto local machines is often unclear. As a result, developers end up spending a lot of time onboarding and risk potentially following insecure practices when sharing secrets from one developer to another.
|
||||
2. **Syncing secrets with teammates**. One of the problems with .env files is that they become unsynced when one of the developers updates a secret or configuration. Even if the rest of the team is notified, developers don't make all the right changes immediately, and later on end up spending a lot of time debugging an issue due to missing environment variables. This leads to a lot of inefficiencies and lost time.
|
||||
3. **Accidentally leaking secrets**. When developing locally, it's common for developers to accidentally leak a hardcoded secret as part of a commit. As soon as the secret is part of the git history, it becomes hard to get it removed and create a security vulnerability.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 726 KiB |
@@ -43,8 +43,11 @@ description: "Learn how to configure an AWS Parameter Store Sync for Infisical."
|
||||
- **KMS Key**: The AWS KMS key ID or alias to encrypt parameters with.
|
||||
- **Tags**: Optional resource tags to add to parameters synced by Infisical.
|
||||
- **Sync Secret Metadata as Resource Tags**: If enabled, metadata attached to secrets will be added as resource tags to parameters synced by Infisical.
|
||||
<Note>Manually configured tags from the **Tags** field will take precedence over secret metadata when tag keys conflict.</Note>
|
||||
<Note>
|
||||
Manually configured tags from the **Tags** field will take precedence over secret metadata when tag keys conflict.
|
||||
</Note>
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Parameter Store Sync, then click **Next**.
|
||||

|
||||
|
@@ -46,7 +46,11 @@ description: "Learn how to configure an AWS Secrets Manager Sync for Infisical."
|
||||
- **KMS Key**: The AWS KMS key ID or alias to encrypt secrets with.
|
||||
- **Tags**: Optional tags to add to secrets synced by Infisical.
|
||||
- **Sync Secret Metadata as Tags**: If enabled, metadata attached to secrets will be added as tags to secrets synced by Infisical.
|
||||
<Note>
|
||||
Manually configured tags from the **Tags** field will take precedence over secret metadata when tag keys conflict.
|
||||
</Note>
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Secrets Manager Sync, then click **Next**.
|
||||

|
||||
|
@@ -6,7 +6,7 @@ description: "Learn how to configure an Azure App Configuration Sync for Infisic
|
||||
**Prerequisites:**
|
||||
|
||||
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
||||
- Create a [Azure Connection](/integrations/app-connections/azure), configured for Azure App Configuration.
|
||||
- Create an [Azure App Configuration Connection](/integrations/app-connections/azure-app-configuration)
|
||||
|
||||
<Note>
|
||||
The Azure App Configuration Secret Sync requires the following permissions to be set on the user / service principal
|
||||
@@ -50,6 +50,7 @@ description: "Learn how to configure an Azure App Configuration Sync for Infisic
|
||||
- **Import Secrets (Prioritize Azure App Configuration)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Secrets Manager over Infisical when keys conflict.
|
||||
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Azure App Configuration Sync, then click **Next**.
|
||||

|
||||
|
@@ -6,7 +6,7 @@ description: "Learn how to configure a Azure Key Vault Sync for Infisical."
|
||||
**Prerequisites:**
|
||||
|
||||
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
||||
- Create a [Azure Connection](/integrations/app-connections/azure), configured for Azure Key Vault.
|
||||
- Create an [Azure Key Vault Connection](/integrations/app-connections/azure-key-vault)
|
||||
|
||||
<Note>
|
||||
The Azure Key Vault Secret Sync requires the following secrets permissions to be set on the user / service principal
|
||||
@@ -52,6 +52,7 @@ description: "Learn how to configure a Azure Key Vault Sync for Infisical."
|
||||
- **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over Secrets Manager when keys conflict.
|
||||
- **Import Secrets (Prioritize Azure Key Vault)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Secrets Manager over Infisical when keys conflict.
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Azure Key Vault Sync, then click **Next**.
|
||||

|
||||
|
@@ -47,6 +47,7 @@ description: "Learn how to configure a Databricks Sync for Infisical."
|
||||
Databricks does not support importing secrets.
|
||||
</Note>
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Databricks Sync, then click **Next**.
|
||||

|
||||
|
@@ -43,6 +43,7 @@ description: "Learn how to configure a GCP Secret Manager Sync for Infisical."
|
||||
- **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over GCP Secret Manager when keys conflict.
|
||||
- **Import Secrets (Prioritize GCP Secret Manager)**: Imports secrets from the destination endpoint before syncing, prioritizing values from GCP Secret Manager over Infisical when keys conflict.
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your GCP Secret Manager Sync, then click **Next**.
|
||||

|
||||
|
@@ -63,6 +63,7 @@ description: "Learn how to configure a GitHub Sync for Infisical."
|
||||
GitHub does not support importing secrets.
|
||||
</Note>
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your GitHub Sync, then click **Next**.
|
||||

|
||||
|
@@ -56,6 +56,7 @@ description: "Learn how to configure a Humanitec Sync for Infisical."
|
||||
Humanitec does not support importing secrets.
|
||||
</Note>
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Humanitec Sync, then click **Next**.
|
||||

|
||||
|
@@ -311,8 +311,7 @@
|
||||
"group": "Guides",
|
||||
"pages": [
|
||||
"self-hosting/guides/mongo-to-postgres",
|
||||
"self-hosting/guides/custom-certificates",
|
||||
"self-hosting/guides/automated-bootstrapping"
|
||||
"self-hosting/guides/custom-certificates"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -342,7 +341,6 @@
|
||||
"cli/commands/dynamic-secrets",
|
||||
"cli/commands/ssh",
|
||||
"cli/commands/gateway",
|
||||
"cli/commands/bootstrap",
|
||||
"cli/commands/export",
|
||||
"cli/commands/token",
|
||||
"cli/commands/service-token",
|
||||
|
@@ -1,150 +0,0 @@
|
||||
---
|
||||
title: "Programmatic Provisioning"
|
||||
description: "Learn how to provision and configure Infisical instances programmatically without UI interaction"
|
||||
---
|
||||
|
||||
Infisical's Automated Bootstrapping feature enables you to provision and configure an Infisical instance without using the UI, allowing for complete automation through static configuration files, API calls, or CLI commands. This is especially valuable for enterprise environments where automated deployment and infrastructure-as-code practices are essential.
|
||||
|
||||
## Overview
|
||||
|
||||
The Automated Bootstrapping workflow automates the following processes:
|
||||
- Creating an admin user account
|
||||
- Initializing an organization for the entire instance
|
||||
- Establishing an **instance admin machine identity** with full administrative permissions
|
||||
- Returning the machine identity credentials for further automation
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- **Instance Initialization**: Infisical requires [configuration variables](/self-hosting/configuration/envars) to be set during launch, after which the bootstrap process can be triggered.
|
||||
- **Instance Admin Machine Identity**: The bootstrapping process creates a machine identity with instance-level admin privileges, which can be used to programmatically manage all aspects of the Infisical instance.
|
||||

|
||||
- **Token Auth**: The instance admin machine identity uses [Token Auth](/documentation/platform/identities/token-auth), providing a JWT token that can be used directly to make authenticated requests to the Infisical API.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An Infisical instance launched with all required configuration variables
|
||||
- Access to the Infisical CLI or the ability to make API calls to the instance
|
||||
- Network connectivity to the Infisical instance
|
||||
|
||||
## Bootstrap Methods
|
||||
|
||||
You can bootstrap an Infisical instance using either the API or the CLI.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Using the API">
|
||||
Make a POST request to the bootstrap endpoint:
|
||||
|
||||
```
|
||||
POST: http://your-infisical-instance.com/api/v1/admin/bootstrap
|
||||
{
|
||||
"email": "admin@example.com",
|
||||
"password": "your-secure-password",
|
||||
"organization": "your-org-name"
|
||||
}
|
||||
```
|
||||
|
||||
Example using curl:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"admin@example.com","password":"your-secure-password","organization":"your-org-name"}' \
|
||||
http://your-infisical-instance.com/api/v1/admin/bootstrap
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Using the CLI">
|
||||
Use the [Infisical CLI](/cli/commands/bootstrap) to bootstrap the instance and extract the token for immediate use in automation:
|
||||
|
||||
```bash
|
||||
infisical bootstrap --domain="http://localhost:8080" --email="admin@example.com" --password="your-secure-password" --organization="your-org-name" | jq ".identity.credentials.token"
|
||||
```
|
||||
|
||||
This example command pipes the output through `jq` to extract only the machine identity token, making it easy to capture and use directly in automation scripts or export as an environment variable for tools like Terraform.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## API Response Structure
|
||||
|
||||
The bootstrap process returns a JSON response with details about the created user, organization, and machine identity:
|
||||
|
||||
```json
|
||||
{
|
||||
"identity": {
|
||||
"credentials": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eUlkIjoiZGIyMjQ3OTItZWQxOC00Mjc3LTlkYWUtNTdlNzUyMzE1ODU0IiwiaWRlbnRpdHlBY2Nlc3NUb2tlbklkIjoiZmVkZmZmMGEtYmU3Yy00NjViLWEwZWEtZjM5OTNjMTg4OGRlIiwiYXV0aFRva2VuVHlwZSI6ImlkZW50aXR5QWNjZXNzVG9rZW4iLCJpYXQiOjE3NDIzMjI0ODl9.mqcZZqIFqER1e9ubrQXp8FbzGYi8nqqZwfMvz09g-8Y"
|
||||
},
|
||||
"id": "db224792-ed18-4277-9dae-57e752315854",
|
||||
"name": "Instance Admin Identity"
|
||||
},
|
||||
"message": "Successfully bootstrapped instance",
|
||||
"organization": {
|
||||
"id": "b56bece0-42f5-4262-b25e-be7bf5f84957",
|
||||
"name": "dog",
|
||||
"slug": "dog-v-e5l"
|
||||
},
|
||||
"user": {
|
||||
"email": "admin@example.com",
|
||||
"firstName": "Admin",
|
||||
"id": "a418f355-c8da-453c-bbc8-6c07208eeb3c",
|
||||
"lastName": "User",
|
||||
"superAdmin": true,
|
||||
"username": "admin@example.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the Instance Admin Machine Identity Token
|
||||
|
||||
The bootstrap process automatically creates a machine identity with Token Auth configured. The returned token has instance-level admin privileges (the highest level of access) and should be treated with the same security considerations as a root credential.
|
||||
|
||||
The token enables full programmatic control of your Infisical instance and can be used in the following ways:
|
||||
|
||||
### 1. Infrastructure Automation
|
||||
|
||||
Store the token securely for use with infrastructure automation tools. Due to the sensitive nature of this token, ensure it's protected using appropriate secret management practices:
|
||||
|
||||
#### Kubernetes Secret (with appropriate RBAC restrictions)
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: infisical-admin-credentials
|
||||
type: Opaque
|
||||
data:
|
||||
token: <base64-encoded-token>
|
||||
```
|
||||
|
||||
#### Environment Variable for Terraform
|
||||
|
||||
```bash
|
||||
export INFISICAL_TOKEN=your-access-token
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### 2. Programmatic Resource Management
|
||||
|
||||
Use the token to authenticate API calls for creating and managing Infisical resources. The token works exactly like any other Token Auth access token in the Infisical API:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${INFISICAL_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"projectName": "New Project",
|
||||
"projectDescription": "A project created via API",
|
||||
"slug": "new-project-slug",
|
||||
"template": "default",
|
||||
"type": "SECRET_MANAGER"
|
||||
}' \
|
||||
https://your-infisical-instance.com/api/v2/projects
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Security Warning**: The instance admin machine identity has the highest level of privileges in your Infisical deployment. The token should be treated with the utmost security and handled like a root credential. Unauthorized access to this token could compromise your entire Infisical instance.
|
||||
- Security controls prevent privilege escalation: instance admin identities cannot be managed by non-instance admin users and identities
|
||||
- The instance admin permission of the generated identity can be revoked later in the server admin panel if needed
|
||||
- The generated admin user account can still be used for UI access if needed, or can be removed if you prefer to manage everything through the machine identity
|
||||
- This process is designed to work with future Crossplane providers and the existing Terraform provider for full infrastructure-as-code capabilities
|
||||
- All necessary configuration variables should be set during the initial launch of the Infisical instance
|
@@ -1,9 +1,9 @@
|
||||
import { ReactNode } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faQuestionCircle, faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { FormControl, Select, SelectItem } from "@app/components/v2";
|
||||
import { FormControl, Select, SelectItem, Switch, Tooltip } from "@app/components/v2";
|
||||
import { SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP, SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { SecretSync, useSecretSyncOption } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
@@ -116,6 +116,44 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
</>
|
||||
)}
|
||||
{AdditionalSyncOptionsFieldsComponent}
|
||||
<Controller
|
||||
control={control}
|
||||
name="syncOptions.disableSecretDeletion"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||
<Switch
|
||||
className="bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||
id="auto-sync-enabled"
|
||||
thumbClassName="bg-mineshaft-800"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
>
|
||||
<p className="w-[11rem]">
|
||||
Disable Secret Deletion{" "}
|
||||
<Tooltip
|
||||
className="max-w-md"
|
||||
content={
|
||||
<>
|
||||
<p>
|
||||
When enabled, Infisical will <span className="font-semibold">not</span>{" "}
|
||||
remove secrets from the destination during a sync.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
Enable this option if you intend to manage some secrets manually outside
|
||||
of Infisical.
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
|
||||
</Tooltip>
|
||||
</p>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{/* <Controller
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
|
@@ -36,6 +36,7 @@ export const SecretSyncReviewFields = () => {
|
||||
secretPath,
|
||||
syncOptions: {
|
||||
// appendSuffix, prependPrefix,
|
||||
disableSecretDeletion,
|
||||
initialSyncBehavior
|
||||
},
|
||||
destination,
|
||||
@@ -111,6 +112,11 @@ export const SecretSyncReviewFields = () => {
|
||||
{/* <SecretSyncLabel label="Prepend Prefix">{prependPrefix}</SecretSyncLabel>
|
||||
<SecretSyncLabel label="Append Suffix">{appendSuffix}</SecretSyncLabel> */}
|
||||
{AdditionalSyncOptionsFieldsComponent}
|
||||
{disableSecretDeletion && (
|
||||
<SecretSyncLabel label="Secret Deletion">
|
||||
<Badge variant="primary">Disabled</Badge>
|
||||
</SecretSyncLabel>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
|
@@ -7,7 +7,8 @@ export const BaseSecretSyncSchema = <T extends AnyZodObject | undefined = undefi
|
||||
additionalSyncOptions?: T
|
||||
) => {
|
||||
const baseSyncOptionsSchema = z.object({
|
||||
initialSyncBehavior: z.nativeEnum(SecretSyncInitialSyncBehavior)
|
||||
initialSyncBehavior: z.nativeEnum(SecretSyncInitialSyncBehavior),
|
||||
disableSecretDeletion: z.boolean().optional().default(false)
|
||||
// scott: removed temporarily for evaluation of template formatting
|
||||
// prependPrefix: z
|
||||
// .string()
|
||||
|
@@ -1,9 +1,7 @@
|
||||
export {
|
||||
useAdminDeleteUser,
|
||||
useAdminGrantServerAdminAccess,
|
||||
useAdminRemoveIdentitySuperAdminAccess,
|
||||
useCreateAdminUser,
|
||||
useRemoveUserServerAdminAccess,
|
||||
useUpdateAdminSlackConfig,
|
||||
useUpdateServerConfig,
|
||||
useUpdateServerEncryptionStrategy
|
||||
|
@@ -70,40 +70,6 @@ export const useAdminDeleteUser = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useAdminRemoveIdentitySuperAdminAccess = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (identityId: string) => {
|
||||
await apiRequest.delete(
|
||||
`/api/v1/admin/identity-management/identities/${identityId}/super-admin-access`
|
||||
);
|
||||
|
||||
return {};
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [adminStandaloneKeys.getIdentities]
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveUserServerAdminAccess = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (userId: string) => {
|
||||
await apiRequest.delete(`/api/v1/admin/user-management/users/${userId}/admin-access`);
|
||||
|
||||
return {};
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [adminStandaloneKeys.getUsers]
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useAdminGrantServerAdminAccess = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user