General improvements on auth templates

This commit is contained in:
Carlos Monastyrski
2025-08-04 15:29:07 -03:00
parent ebe05661d3
commit 3dde786621
29 changed files with 598 additions and 557 deletions
@@ -1352,6 +1352,7 @@ interface AddIdentityLdapAuthEvent {
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
allowedFields?: TAllowedFields[];
url: string;
templateId?: string | null;
};
}
@@ -1365,6 +1366,7 @@ interface UpdateIdentityLdapAuthEvent {
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
allowedFields?: TAllowedFields[];
url?: string;
templateId?: string | null;
};
}
@@ -33,7 +33,8 @@ export enum OrgPermissionMachineIdentityAuthTemplateActions {
EditTemplates = "edit-templates",
CreateTemplates = "create-templates",
DeleteTemplates = "delete-templates",
UnlinkTemplates = "unlink-templates"
UnlinkTemplates = "unlink-templates",
UseTemplates = "use-templates"
}
export enum OrgPermissionAdminConsoleAction {
@@ -382,6 +383,7 @@ const buildAdminPermission = () => {
OrgPermissionMachineIdentityAuthTemplateActions.UnlinkTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
can(OrgPermissionMachineIdentityAuthTemplateActions.UseTemplates, OrgPermissionSubjects.MachineIdentityAuthTemplate);
can(OrgPermissionSecretShareAction.ManageSettings, OrgPermissionSubjects.SecretShare);
@@ -423,6 +425,7 @@ const buildMemberPermission = () => {
OrgPermissionMachineIdentityAuthTemplateActions.UnlinkTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
can(OrgPermissionMachineIdentityAuthTemplateActions.UseTemplates, OrgPermissionSubjects.MachineIdentityAuthTemplate);
return rules;
};
+2 -38
View File
@@ -148,42 +148,6 @@ export const IDENTITIES = {
}
} as const;
export const IDENTITY_TEMPLATES = {
CREATE: {
name: "The name of the identity template to create.",
organizationId: "The organization ID to which the identity template belongs.",
authMethod: "The auth method of the template.",
credentials: "Set of credentials to reuse by this template"
},
UPDATE: {
identityTemplateId: "The ID of the identity to update.",
name: "The new name of the identity template.",
authMethod: "The auth method of the template."
},
DELETE: {
identityTemplateId: "The ID of the identity template to delete."
},
GET_BY_ID: {
identityTemplateId: "The ID of the identity template to get details.",
organizationId: "The organization ID to which the identity template belongs.",
authMethod: "The auth method of the template.",
credentials: "Set of credentials used by this template"
},
LIST: {
orgId: "The ID of the organization to list identity templates."
},
SEARCH: {
search: {
desc: "The filters to apply to the search.",
name: "The name of the identity template to filter by."
},
offset: "The offset to start from. If you enter 10, it will start from the 10th identity.",
limit: "The number of identity templates to return.",
orderBy: "The column to order identity templates by.",
orderDirection: "The direction to order identity templates in."
}
};
export const UNIVERSAL_AUTH = {
LOGIN: {
clientId: "Your Machine Identity Client ID.",
@@ -251,7 +215,7 @@ export const LDAP_AUTH = {
password: "The password of the LDAP user to login."
},
ATTACH: {
templateId: "The ID of the template to attach the configuration onto.",
templateId: "The ID of the identity auth template to attach the configuration onto.",
identityId: "The ID of the identity to attach the configuration onto.",
url: "The URL of the LDAP server.",
allowedFields:
@@ -279,7 +243,7 @@ export const LDAP_AUTH = {
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used.",
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from.",
templateId: "The ID of the template to update the configuration for."
templateId: "The ID of the identity auth template to update the configuration to."
},
RETRIEVE: {
identityId: "The ID of the identity to retrieve the configuration for."
+2 -1
View File
@@ -1469,7 +1469,8 @@ export const registerRoutes = async (
identityLdapAuthDAL,
permissionService,
kmsService,
licenseService
licenseService,
auditLogService
});
const identityAccessTokenService = identityAccessTokenServiceFactory({
@@ -200,55 +200,104 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider)
params: z.object({
identityId: z.string().trim().describe(LDAP_AUTH.ATTACH.identityId)
}),
body: z
.object({
templateId: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.templateId),
url: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.url),
bindDN: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.bindDN),
bindPass: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.bindPass),
searchBase: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.searchBase),
searchFilter: z
.string()
.trim()
.min(1)
.default("(uid={{username}})")
.refine(isValidLdapFilter, "Invalid LDAP search filter")
.describe(LDAP_AUTH.ATTACH.searchFilter),
allowedFields: AllowedFieldsSchema.array().optional().describe(LDAP_AUTH.ATTACH.allowedFields),
ldapCaCertificate: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.ldapCaCertificate),
accessTokenTrustedIps: z
.object({
ipAddress: z.string().trim()
})
.array()
.min(1)
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
.describe(LDAP_AUTH.ATTACH.accessTokenTrustedIps),
accessTokenTTL: z
.number()
.int()
.min(0)
.max(315360000)
.default(2592000)
.describe(LDAP_AUTH.ATTACH.accessTokenTTL),
accessTokenMaxTTL: z
.number()
.int()
.min(1)
.max(315360000)
.default(2592000)
.describe(LDAP_AUTH.ATTACH.accessTokenMaxTTL),
accessTokenNumUsesLimit: z.number().int().min(0).default(0).describe(LDAP_AUTH.ATTACH.accessTokenNumUsesLimit)
})
.refine(
(val) => val.accessTokenTTL <= val.accessTokenMaxTTL,
"Access Token TTL cannot be greater than Access Token Max TTL."
)
.refine((val) => {
const hasTemplateId = !!val.templateId;
const hasManualConfig = !!(val.url && val.bindDN && val.bindPass && val.searchBase);
return hasTemplateId || hasManualConfig;
}, "Either templateId must be provided, or all of url, bindDN, bindPass, and searchBase must be provided."),
body: z.union([
// Template-based configuration
z
.object({
templateId: z.string().trim().describe(LDAP_AUTH.ATTACH.templateId),
searchFilter: z
.string()
.trim()
.min(1)
.default("(uid={{username}})")
.refine(isValidLdapFilter, "Invalid LDAP search filter")
.describe(LDAP_AUTH.ATTACH.searchFilter),
allowedFields: AllowedFieldsSchema.array().optional().describe(LDAP_AUTH.ATTACH.allowedFields),
ldapCaCertificate: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.ldapCaCertificate),
accessTokenTrustedIps: z
.object({
ipAddress: z.string().trim()
})
.array()
.min(1)
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
.describe(LDAP_AUTH.ATTACH.accessTokenTrustedIps),
accessTokenTTL: z
.number()
.int()
.min(0)
.max(315360000)
.default(2592000)
.describe(LDAP_AUTH.ATTACH.accessTokenTTL),
accessTokenMaxTTL: z
.number()
.int()
.min(1)
.max(315360000)
.default(2592000)
.describe(LDAP_AUTH.ATTACH.accessTokenMaxTTL),
accessTokenNumUsesLimit: z
.number()
.int()
.min(0)
.default(0)
.describe(LDAP_AUTH.ATTACH.accessTokenNumUsesLimit)
})
.refine(
(val) => val.accessTokenTTL <= val.accessTokenMaxTTL,
"Access Token TTL cannot be greater than Access Token Max TTL."
),
// Manual configuration
z
.object({
url: z.string().trim().describe(LDAP_AUTH.ATTACH.url),
bindDN: z.string().trim().describe(LDAP_AUTH.ATTACH.bindDN),
bindPass: z.string().trim().describe(LDAP_AUTH.ATTACH.bindPass),
searchBase: z.string().trim().describe(LDAP_AUTH.ATTACH.searchBase),
searchFilter: z
.string()
.trim()
.min(1)
.default("(uid={{username}})")
.refine(isValidLdapFilter, "Invalid LDAP search filter")
.describe(LDAP_AUTH.ATTACH.searchFilter),
allowedFields: AllowedFieldsSchema.array().optional().describe(LDAP_AUTH.ATTACH.allowedFields),
ldapCaCertificate: z.string().trim().optional().describe(LDAP_AUTH.ATTACH.ldapCaCertificate),
accessTokenTrustedIps: z
.object({
ipAddress: z.string().trim()
})
.array()
.min(1)
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
.describe(LDAP_AUTH.ATTACH.accessTokenTrustedIps),
accessTokenTTL: z
.number()
.int()
.min(0)
.max(315360000)
.default(2592000)
.describe(LDAP_AUTH.ATTACH.accessTokenTTL),
accessTokenMaxTTL: z
.number()
.int()
.min(1)
.max(315360000)
.default(2592000)
.describe(LDAP_AUTH.ATTACH.accessTokenMaxTTL),
accessTokenNumUsesLimit: z
.number()
.int()
.min(0)
.default(0)
.describe(LDAP_AUTH.ATTACH.accessTokenNumUsesLimit)
})
.refine(
(val) => val.accessTokenTTL <= val.accessTokenMaxTTL,
"Access Token TTL cannot be greater than Access Token Max TTL."
)
]),
response: {
200: z.object({
identityLdapAuth: IdentityLdapAuthsSchema.omit({
@@ -281,7 +330,8 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider)
accessTokenMaxTTL: identityLdapAuth.accessTokenMaxTTL,
accessTokenTTL: identityLdapAuth.accessTokenTTL,
accessTokenNumUsesLimit: identityLdapAuth.accessTokenNumUsesLimit,
allowedFields: req.body.allowedFields
allowedFields: req.body.allowedFields,
templateId: identityLdapAuth.templateId
}
}
});
@@ -383,7 +433,8 @@ export const registerIdentityLdapAuthRouter = async (server: FastifyZodProvider)
accessTokenTTL: identityLdapAuth.accessTokenTTL,
accessTokenNumUsesLimit: identityLdapAuth.accessTokenNumUsesLimit,
accessTokenTrustedIps: identityLdapAuth.accessTokenTrustedIps as TIdentityTrustedIp[],
allowedFields: req.body.allowedFields
allowedFields: req.body.allowedFields,
templateId: identityLdapAuth.templateId
}
}
});
@@ -12,10 +12,11 @@ import {
} from "@app/services/identity-auth-template/identity-auth-template-enums";
const ldapTemplateFieldsSchema = z.object({
url: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.LDAP_URL_REQUIRED),
bindDN: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.BIND_DN_REQUIRED),
bindPass: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.BIND_PASSWORD_REQUIRED),
searchBase: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.SEARCH_BASE_REQUIRED)
url: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.LDAP.URL_REQUIRED),
bindDN: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.LDAP.BIND_DN_REQUIRED),
bindPass: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.LDAP.BIND_PASSWORD_REQUIRED),
searchBase: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.LDAP.SEARCH_BASE_REQUIRED),
ldapCaCertificate: z.string().trim().optional()
});
export const registerIdentityTemplateRouter = async (server: FastifyZodProvider) => {
@@ -44,8 +45,8 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
templateFields: ldapTemplateFieldsSchema
}),
response: {
200: z.object({
message: z.string()
200: IdentityAuthTemplatesSchema.extend({
templateFields: z.record(z.string(), z.unknown())
})
}
},
@@ -72,7 +73,7 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
}
});
return { message: TEMPLATE_SUCCESS_MESSAGES.CREATED };
return template;
}
});
@@ -104,8 +105,8 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
templateFields: ldapTemplateFieldsSchema.partial().optional()
}),
response: {
200: z.object({
message: z.string()
200: IdentityAuthTemplatesSchema.extend({
templateFields: z.record(z.string(), z.unknown())
})
}
},
@@ -132,7 +133,7 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
}
});
return { message: TEMPLATE_SUCCESS_MESSAGES.UPDATED };
return template;
}
});
@@ -204,10 +205,8 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
templateId: z.string().min(1, TEMPLATE_VALIDATION_MESSAGES.TEMPLATE_ID_REQUIRED)
}),
response: {
200: z.object({
template: IdentityAuthTemplatesSchema.extend({
templateFields: ldapTemplateFieldsSchema
})
200: IdentityAuthTemplatesSchema.extend({
templateFields: ldapTemplateFieldsSchema
})
}
},
@@ -220,7 +219,7 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
actorOrgId: req.permission.orgId
});
return { template };
return template;
}
});
@@ -347,7 +346,7 @@ export const registerIdentityTemplateRouter = async (server: FastifyZodProvider)
server.route({
method: "POST",
url: "/:templateId/usage",
url: "/:templateId/delete-usage",
config: {
rateLimit: writeLimit
},
+2 -7
View File
@@ -135,13 +135,8 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerIntegrationRouter, { prefix: "/integration" });
await server.register(registerIntegrationAuthRouter, { prefix: "/integration-auth" });
await server.register(registerWebhookRouter, { prefix: "/webhooks" });
await server.register(
async (identitiesRouter) => {
await identitiesRouter.register(registerIdentityRouter);
await identitiesRouter.register(registerIdentityTemplateRouter, { prefix: "/templates" });
},
{ prefix: "/identities" }
);
await server.register(registerIdentityRouter, { prefix: "/identities" });
await server.register(registerIdentityTemplateRouter, { prefix: "/identity-templates" });
await server.register(
async (secretSharingRouter) => {
@@ -7,10 +7,12 @@ export const TEMPLATE_VALIDATION_MESSAGES = {
TEMPLATE_NAME_MAX_LENGTH: "Template name must be at most 64 characters long",
AUTH_METHOD_REQUIRED: "Auth method is required",
TEMPLATE_ID_REQUIRED: "Template ID is required",
LDAP_URL_REQUIRED: "LDAP URL is required",
BIND_DN_REQUIRED: "Bind DN is required",
BIND_PASSWORD_REQUIRED: "Bind password is required",
SEARCH_BASE_REQUIRED: "Search base is required"
LDAP: {
URL_REQUIRED: "LDAP URL is required",
BIND_DN_REQUIRED: "Bind DN is required",
BIND_PASSWORD_REQUIRED: "Bind password is required",
SEARCH_BASE_REQUIRED: "Search base is required"
}
} as const;
export const TEMPLATE_SUCCESS_MESSAGES = {
@@ -1,5 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import {
OrgPermissionMachineIdentityAuthTemplateActions,
@@ -9,6 +10,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TOrgPermission } from "@app/lib/types";
import { ActorType } from "../auth/auth-type";
import { TIdentityLdapAuthDALFactory } from "../identity-ldap-auth/identity-ldap-auth-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
@@ -30,6 +32,7 @@ type TIdentityAuthTemplateServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "encryptWithInputKey" | "decryptWithInputKey">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
};
export type TIdentityAuthTemplateServiceFactory = ReturnType<typeof identityAuthTemplateServiceFactory>;
@@ -39,7 +42,8 @@ export const identityAuthTemplateServiceFactory = ({
identityLdapAuthDAL,
permissionService,
kmsService,
licenseService
licenseService,
auditLogService
}: TIdentityAuthTemplateServiceFactoryDep) => {
// Plan check
const $checkPlan = async (orgId: string) => {
@@ -87,7 +91,7 @@ export const identityAuthTemplateServiceFactory = ({
orgId: actorOrgId
});
return template;
return { ...template, templateFields };
};
const updateTemplate = async ({
@@ -104,7 +108,7 @@ export const identityAuthTemplateServiceFactory = ({
templateFields?: Record<string, unknown>;
} & Omit<TOrgPermission, "orgId">) => {
await $checkPlan(actorOrgId);
const template = await identityAuthTemplateDAL.findById(templateId);
const template = await identityAuthTemplateDAL.findByIdAndOrgId(templateId, actorOrgId);
if (!template) {
throw new NotFoundError({ message: "Template not found" });
}
@@ -126,6 +130,8 @@ export const identityAuthTemplateServiceFactory = ({
orgId: template.orgId
});
let finalTemplateFields: Record<string, unknown> = {};
const updatedTemplate = await identityAuthTemplateDAL.transaction(async (tx) => {
const authTemplate = await identityAuthTemplateDAL.updateById(
templateId,
@@ -149,12 +155,13 @@ export const identityAuthTemplateServiceFactory = ({
) as TLdapTemplateFields;
const mergedTemplateFields: TLdapTemplateFields = { ...currentTemplateFields, ...templateFields };
finalTemplateFields = mergedTemplateFields;
const ldapUpdateData: {
url?: string;
searchBase?: string;
encryptedBindDN?: Buffer;
encryptedBindPass?: Buffer;
encryptedLdapCaCertificate?: Buffer;
} = {};
if ("url" in templateFields) {
@@ -173,16 +180,38 @@ export const identityAuthTemplateServiceFactory = ({
plainText: Buffer.from(mergedTemplateFields.bindPass)
}).cipherTextBlob;
}
if ("ldapCaCertificate" in templateFields) {
ldapUpdateData.encryptedLdapCaCertificate = encryptor({
plainText: Buffer.from(mergedTemplateFields.ldapCaCertificate || "")
}).cipherTextBlob;
}
if (Object.keys(ldapUpdateData).length > 0) {
await identityLdapAuthDAL.update({ templateId }, ldapUpdateData, tx);
const updatedLdapAuths = await identityLdapAuthDAL.update({ templateId }, ldapUpdateData, tx);
await Promise.all(
updatedLdapAuths.map(async (updatedLdapAuth) => {
await auditLogService.createAuditLog({
actor: {
type: ActorType.PLATFORM,
metadata: {}
},
orgId: actorOrgId,
event: {
type: EventType.UPDATE_IDENTITY_LDAP_AUTH,
metadata: {
identityId: updatedLdapAuth.identityId,
templateId: template.id
}
}
});
})
);
}
}
return authTemplate;
});
return updatedTemplate;
return { ...updatedTemplate, templateFields: finalTemplateFields };
};
const deleteTemplate = async ({
@@ -193,7 +222,7 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
}: TDeleteIdentityAuthTemplateDTO) => {
await $checkPlan(actorOrgId);
const template = await identityAuthTemplateDAL.findById(templateId);
const template = await identityAuthTemplateDAL.findByIdAndOrgId(templateId, actorOrgId);
if (!template) {
throw new NotFoundError({ message: "Template not found" });
}
@@ -212,7 +241,25 @@ export const identityAuthTemplateServiceFactory = ({
const deletedTemplate = await identityAuthTemplateDAL.transaction(async (tx) => {
// Remove template reference from identityLdapAuth records
await identityLdapAuthDAL.update({ templateId }, { templateId: null }, tx);
const updatedLdapAuths = await identityLdapAuthDAL.update({ templateId }, { templateId: null }, tx);
await Promise.all(
updatedLdapAuths.map(async (updatedLdapAuth) => {
await auditLogService.createAuditLog({
actor: {
type: ActorType.PLATFORM,
metadata: {}
},
orgId: actorOrgId,
event: {
type: EventType.UPDATE_IDENTITY_LDAP_AUTH,
metadata: {
identityId: updatedLdapAuth.identityId,
templateId: template.id
}
}
});
})
);
// Delete the template
const [deletedTpl] = await identityAuthTemplateDAL.delete({ id: templateId }, tx);
@@ -230,7 +277,7 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
}: TGetIdentityAuthTemplateDTO) => {
await $checkPlan(actorOrgId);
const template = await identityAuthTemplateDAL.findById(templateId);
const template = await identityAuthTemplateDAL.findByIdAndOrgId(templateId, actorOrgId);
if (!template) {
throw new NotFoundError({ message: "Template not found" });
}
@@ -313,7 +360,7 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
OrgPermissionMachineIdentityAuthTemplateActions.UseTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
@@ -350,7 +397,7 @@ export const identityAuthTemplateServiceFactory = ({
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
const template = await identityAuthTemplateDAL.findById(templateId);
const template = await identityAuthTemplateDAL.findByIdAndOrgId(templateId, actorOrgId);
if (!template) {
throw new NotFoundError({ message: "Template not found" });
}
@@ -376,11 +423,11 @@ export const identityAuthTemplateServiceFactory = ({
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.ListTemplates,
OrgPermissionMachineIdentityAuthTemplateActions.UnlinkTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
const template = await identityAuthTemplateDAL.findById(templateId);
const template = await identityAuthTemplateDAL.findByIdAndOrgId(templateId, actorOrgId);
if (!template) {
throw new NotFoundError({ message: "Template not found" });
}
@@ -8,6 +8,7 @@ export type TLdapTemplateFields = {
bindDN: string;
bindPass: string;
searchBase: string;
ldapCaCertificate?: string;
};
// Union type for all template field types
@@ -4,7 +4,11 @@ import { ForbiddenError } from "@casl/ability";
import { IdentityAuthMethod } from "@app/db/schemas";
import { testLDAPConfig } from "@app/ee/services/ldap-config/ldap-fns";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import {
OrgPermissionIdentityActions,
OrgPermissionMachineIdentityAuthTemplateActions,
OrgPermissionSubjects
} from "@app/ee/services/permission/org-permission";
import {
constructPermissionErrorMessage,
validatePrivilegeChangeOperation
@@ -217,6 +221,14 @@ export const identityLdapAuthServiceFactory = ({
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
if (templateId) {
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.UseTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
}
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
if (!plan.ldap) {
@@ -254,7 +266,7 @@ export const identityLdapAuthServiceFactory = ({
? await identityAuthTemplateDAL.findByIdAndOrgId(templateId, identityMembershipOrg.orgId)
: undefined;
let ldapConfig: { bindDN: string; bindPass: string; searchBase: string; url: string };
let ldapConfig: { bindDN: string; bindPass: string; searchBase: string; url: string; ldapCaCertificate?: string };
if (template) {
ldapConfig = JSON.parse(decryptor({ cipherTextBlob: template.templateFields }).toString());
} else {
@@ -267,7 +279,8 @@ export const identityLdapAuthServiceFactory = ({
bindDN,
bindPass,
searchBase,
url
url,
ldapCaCertificate
};
}
@@ -280,9 +293,9 @@ export const identityLdapAuthServiceFactory = ({
});
let encryptedLdapCaCertificate: Buffer | undefined;
if (ldapCaCertificate) {
if (ldapConfig.ldapCaCertificate) {
const { cipherTextBlob: encryptedCertificate } = encryptor({
plainText: Buffer.from(ldapCaCertificate)
plainText: Buffer.from(ldapConfig.ldapCaCertificate)
});
encryptedLdapCaCertificate = encryptedCertificate;
@@ -291,7 +304,7 @@ export const identityLdapAuthServiceFactory = ({
const isConnected = await testLDAPConfig({
bindDN: ldapConfig.bindDN,
bindPass: ldapConfig.bindPass,
caCert: ldapCaCertificate || "",
caCert: ldapConfig.ldapCaCertificate || "",
url: ldapConfig.url
});
@@ -371,6 +384,13 @@ export const identityLdapAuthServiceFactory = ({
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
if (templateId) {
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionMachineIdentityAuthTemplateActions.UseTemplates,
OrgPermissionSubjects.MachineIdentityAuthTemplate
);
}
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
if (!plan.ldap) {
@@ -411,6 +431,7 @@ export const identityLdapAuthServiceFactory = ({
bindPass?: string;
searchBase?: string;
url?: string;
ldapCaCertificate?: string;
};
if (template) {
@@ -420,7 +441,8 @@ export const identityLdapAuthServiceFactory = ({
bindDN,
bindPass,
searchBase,
url
url,
ldapCaCertificate
};
}
@@ -434,9 +456,9 @@ export const identityLdapAuthServiceFactory = ({
}
let encryptedLdapCaCertificate: Buffer | undefined;
if (ldapCaCertificate) {
if (config.ldapCaCertificate) {
const { cipherTextBlob: ldapCaCertificateCiphertext } = encryptor({
plainText: Buffer.from(ldapCaCertificate)
plainText: Buffer.from(config.ldapCaCertificate)
});
encryptedLdapCaCertificate = ldapCaCertificateCiphertext;
@@ -456,7 +478,7 @@ export const identityLdapAuthServiceFactory = ({
const isConnected = await testLDAPConfig({
bindDN: config.bindDN || ldapConfig.bindDN,
bindPass: config.bindPass || ldapConfig.bindPass,
caCert: ldapCaCertificate || ldapConfig.caCert,
caCert: config.ldapCaCertificate || ldapConfig.caCert,
url: config.url || ldapConfig.url
});