mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
feat(core): Allow enforcement of MFA usage on instance (#16556)
Co-authored-by: Marc Littlemore <marc@n8n.io> Co-authored-by: Csaba Tuncsik <csaba.tuncsik@gmail.com>
This commit is contained in:
@@ -137,6 +137,7 @@ export interface FrontendSettings {
|
||||
ldap: boolean;
|
||||
saml: boolean;
|
||||
oidc: boolean;
|
||||
mfaEnforcement: boolean;
|
||||
logStreaming: boolean;
|
||||
advancedExecutionFilters: boolean;
|
||||
variables: boolean;
|
||||
@@ -167,6 +168,7 @@ export interface FrontendSettings {
|
||||
};
|
||||
mfa: {
|
||||
enabled: boolean;
|
||||
enforced: boolean;
|
||||
};
|
||||
folders: {
|
||||
enabled: boolean;
|
||||
|
||||
@@ -63,6 +63,10 @@ export class LicenseState {
|
||||
return this.isLicensed('feat:oidc');
|
||||
}
|
||||
|
||||
isMFAEnforcementLicensed() {
|
||||
return this.isLicensed('feat:mfaEnforcement');
|
||||
}
|
||||
|
||||
isApiKeyScopesLicensed() {
|
||||
return this.isLicensed('feat:apiKeyScopes');
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export const LICENSE_FEATURES = {
|
||||
LDAP: 'feat:ldap',
|
||||
SAML: 'feat:saml',
|
||||
OIDC: 'feat:oidc',
|
||||
MFA_ENFORCEMENT: 'feat:mfaEnforcement',
|
||||
LOG_STREAMING: 'feat:logStreaming',
|
||||
ADVANCED_EXECUTION_FILTERS: 'feat:advancedExecutionFilters',
|
||||
VARIABLES: 'feat:variables',
|
||||
|
||||
@@ -114,6 +114,7 @@ export interface PublicUser {
|
||||
isOwner?: boolean;
|
||||
featureFlags?: FeatureFlags; // External type from n8n-workflow
|
||||
lastActiveAt?: Date | null;
|
||||
mfaAuthenticated?: boolean;
|
||||
}
|
||||
|
||||
export type UserSettings = Pick<User, 'id' | 'settings'>;
|
||||
@@ -367,6 +368,10 @@ export type APIRequest<
|
||||
browserId?: string;
|
||||
};
|
||||
|
||||
export type AuthenticationInformation = {
|
||||
usedMfa: boolean;
|
||||
};
|
||||
|
||||
export type AuthenticatedRequest<
|
||||
RouteParams = {},
|
||||
ResponseBody = {},
|
||||
@@ -374,6 +379,7 @@ export type AuthenticatedRequest<
|
||||
RequestQuery = {},
|
||||
> = Omit<APIRequest<RouteParams, ResponseBody, RequestBody, RequestQuery>, 'user' | 'cookies'> & {
|
||||
user: User;
|
||||
authInfo?: AuthenticationInformation;
|
||||
cookies: Record<string, string | undefined>;
|
||||
headers: express.Request['headers'] & {
|
||||
'push-ref': string;
|
||||
|
||||
@@ -9,6 +9,8 @@ interface RouteOptions {
|
||||
usesTemplates?: boolean;
|
||||
/** When this flag is set to true, auth cookie isn't validated, and req.user will not be set */
|
||||
skipAuth?: boolean;
|
||||
/** When this flag is set to true, the auth cookie does not enforce MFA to be used in the token */
|
||||
allowSkipMFA?: boolean;
|
||||
/** When these options are set, calls to this endpoint are rate limited using the options */
|
||||
rateLimit?: boolean | RateLimit;
|
||||
}
|
||||
@@ -26,6 +28,7 @@ const RouteFactory =
|
||||
routeMetadata.middlewares = options.middlewares ?? [];
|
||||
routeMetadata.usesTemplates = options.usesTemplates ?? false;
|
||||
routeMetadata.skipAuth = options.skipAuth ?? false;
|
||||
routeMetadata.allowSkipMFA = options.allowSkipMFA ?? false;
|
||||
routeMetadata.rateLimit = options.rateLimit;
|
||||
};
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface RouteMetadata {
|
||||
middlewares: RequestHandler[];
|
||||
usesTemplates: boolean;
|
||||
skipAuth: boolean;
|
||||
allowSkipMFA: boolean;
|
||||
rateLimit?: boolean | RateLimit;
|
||||
licenseFeature?: BooleanLicenseFeature;
|
||||
accessScope?: AccessScope;
|
||||
|
||||
@@ -19,7 +19,7 @@ export const RESOURCES = {
|
||||
securityAudit: ['generate'] as const,
|
||||
sourceControl: ['pull', 'push', 'manage'] as const,
|
||||
tag: [...DEFAULT_OPERATIONS] as const,
|
||||
user: ['resetPassword', 'changeRole', ...DEFAULT_OPERATIONS] as const,
|
||||
user: ['resetPassword', 'changeRole', 'enforceMfa', ...DEFAULT_OPERATIONS] as const,
|
||||
variable: [...DEFAULT_OPERATIONS] as const,
|
||||
workersView: ['manage'] as const,
|
||||
workflow: ['share', 'execute', 'move', ...DEFAULT_OPERATIONS] as const,
|
||||
@@ -34,7 +34,7 @@ export const API_KEY_RESOURCES = {
|
||||
variable: ['create', 'update', 'delete', 'list'] as const,
|
||||
securityAudit: ['generate'] as const,
|
||||
project: ['create', 'update', 'delete', 'list'] as const,
|
||||
user: ['read', 'list', 'create', 'changeRole', 'delete'] as const,
|
||||
user: ['read', 'list', 'create', 'changeRole', 'delete', 'enforceMfa'] as const,
|
||||
execution: ['delete', 'read', 'list', 'get'] as const,
|
||||
credential: ['create', 'move', 'delete'] as const,
|
||||
sourceControl: ['pull'] as const,
|
||||
|
||||
@@ -6,6 +6,7 @@ export const OWNER_API_KEY_SCOPES: ApiKeyScope[] = [
|
||||
'user:create',
|
||||
'user:changeRole',
|
||||
'user:delete',
|
||||
'user:enforceMfa',
|
||||
'sourceControl:pull',
|
||||
'securityAudit:generate',
|
||||
'project:create',
|
||||
|
||||
@@ -56,6 +56,7 @@ export const GLOBAL_OWNER_SCOPES: Scope[] = [
|
||||
'user:list',
|
||||
'user:resetPassword',
|
||||
'user:changeRole',
|
||||
'user:enforceMfa',
|
||||
'variable:create',
|
||||
'variable:read',
|
||||
'variable:update',
|
||||
|
||||
Reference in New Issue
Block a user