fix(editor): Handle permission edge cases (empty scopes) (#7723)

This commit is contained in:
Csaba Tuncsik 2023-11-16 18:08:23 +01:00 committed by GitHub
parent 9d6a68d1c7
commit e2ffd397fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 33 deletions

View file

@ -1,42 +1,28 @@
import type { Scope, ScopeLevels } from './types';
export type HasScopeMode = 'oneOf' | 'allOf';
export interface HasScopeOptions {
mode: HasScopeMode;
}
import type { Scope, ScopeLevels, GlobalScopes, ScopeOptions } from './types';
export function hasScope(
scope: Scope | Scope[],
userScopes: GlobalScopes,
options?: ScopeOptions,
): boolean;
export function hasScope(
scope: Scope | Scope[],
userScopes: ScopeLevels,
options?: HasScopeOptions,
options?: ScopeOptions,
): boolean;
export function hasScope(
scope: Scope | Scope[],
userScopes: Pick<ScopeLevels, 'global'>,
options?: HasScopeOptions,
): boolean;
export function hasScope(
scope: Scope | Scope[],
userScopes: Omit<ScopeLevels, 'resource'>,
options?: HasScopeOptions,
): boolean;
export function hasScope(
scope: Scope | Scope[],
userScopes: Pick<ScopeLevels, 'global'> & Partial<ScopeLevels>,
options: HasScopeOptions = { mode: 'oneOf' },
userScopes: unknown,
options: ScopeOptions = { mode: 'oneOf' },
): boolean {
if (!Array.isArray(scope)) {
scope = [scope];
}
const userScopeSet = new Set([
...userScopes.global,
...(userScopes.project ?? []),
...(userScopes.resource ?? []),
]);
const userScopeSet = new Set(Object.values(userScopes ?? {}).flat());
if (options.mode === 'allOf') {
return scope.every((s) => userScopeSet.has(s));
return !!scope.length && scope.every((s) => userScopeSet.has(s));
}
return scope.some((s) => userScopeSet.has(s));

View file

@ -34,5 +34,11 @@ export type Scope =
| SourceControlScope
| ExternalSecretStoreScope;
export type ScopeLevel = 'global' | 'project' | 'resource';
export type ScopeLevels = Record<ScopeLevel, Scope[]>;
export type ScopeLevel<T extends 'global' | 'project' | 'resource'> = Record<T, Scope[]>;
export type GlobalScopes = ScopeLevel<'global'>;
export type ProjectScopes = ScopeLevel<'project'>;
export type ResourceScopes = ScopeLevel<'resource'>;
export type ScopeLevels = GlobalScopes & (ProjectScopes | (ProjectScopes & ResourceScopes));
export type ScopeMode = 'oneOf' | 'allOf';
export type ScopeOptions = { mode: ScopeMode };

View file

@ -80,6 +80,12 @@ describe('hasScope', () => {
global: memberPermissions,
}),
).toBe(false);
expect(
hasScope([], {
global: memberPermissions,
}),
).toBe(false);
});
test('should work with allOf mode', () => {
@ -112,5 +118,15 @@ describe('hasScope', () => {
{ mode: 'allOf' },
),
).toBe(false);
expect(
hasScope(
[],
{
global: memberPermissions,
},
{ mode: 'allOf' },
),
).toBe(false);
});
});

View file

@ -21,7 +21,7 @@ import { WithTimestamps, jsonColumnType } from './AbstractEntity';
import type { IPersonalizationSurveyAnswers } from '@/Interfaces';
import type { AuthIdentity } from './AuthIdentity';
import { ownerPermissions, memberPermissions } from '@/permissions/roles';
import { hasScope, type HasScopeOptions, type Scope } from '@n8n/permissions';
import { hasScope, type ScopeOptions, type Scope } from '@n8n/permissions';
export const MIN_PASSWORD_LENGTH = 8;
@ -137,16 +137,13 @@ export class User extends WithTimestamps implements IUser {
return STATIC_SCOPE_MAP[this.globalRole?.name] ?? [];
}
async hasGlobalScope(
scope: Scope | Scope[],
hasScopeOptions?: HasScopeOptions,
): Promise<boolean> {
async hasGlobalScope(scope: Scope | Scope[], scopeOptions?: ScopeOptions): Promise<boolean> {
return hasScope(
scope,
{
global: this.globalScopes,
},
hasScopeOptions,
scopeOptions,
);
}
}