frontend permissions
* rework backend workspace permissions
This commit is contained in:
@@ -1,61 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
AbilityBuilder,
|
||||
createMongoAbility,
|
||||
ExtractSubjectType,
|
||||
MongoAbility,
|
||||
} from '@casl/ability';
|
||||
import { Action } from '../ability.action';
|
||||
import { UserRole } from '../../../helpers/types/permission';
|
||||
import { User, Workspace } from '@docmost/db/types/entity.types';
|
||||
|
||||
export type Subjects =
|
||||
| 'Workspace'
|
||||
| 'Space'
|
||||
| 'SpaceMember'
|
||||
| 'Group'
|
||||
| 'GroupUser'
|
||||
| 'Attachment'
|
||||
| 'Comment'
|
||||
| 'Page'
|
||||
| 'User'
|
||||
| 'WorkspaceUser'
|
||||
| 'all';
|
||||
export type AppAbility = MongoAbility<[Action, Subjects]>;
|
||||
|
||||
@Injectable()
|
||||
export default class CaslAbilityFactory {
|
||||
createForUser(user: User, workspace: Workspace) {
|
||||
const { can, build } = new AbilityBuilder<AppAbility>(createMongoAbility);
|
||||
|
||||
const userRole = user.role;
|
||||
|
||||
if (userRole === UserRole.OWNER || userRole === UserRole.ADMIN) {
|
||||
// Workspace Users
|
||||
can([Action.Manage], 'Workspace');
|
||||
can([Action.Manage], 'WorkspaceUser');
|
||||
|
||||
// Groups
|
||||
can([Action.Manage], 'Group');
|
||||
can([Action.Manage], 'GroupUser');
|
||||
|
||||
// Attachments
|
||||
can([Action.Manage], 'Attachment');
|
||||
}
|
||||
|
||||
if (userRole === UserRole.MEMBER) {
|
||||
can([Action.Read], 'WorkspaceUser');
|
||||
|
||||
// Groups
|
||||
can([Action.Read], 'Group');
|
||||
can([Action.Read], 'GroupUser');
|
||||
|
||||
// Attachments
|
||||
can([Action.Read, Action.Create], 'Attachment');
|
||||
}
|
||||
|
||||
return build({
|
||||
detectSubjectType: (item) => item as ExtractSubjectType<Subjects>,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { User } from '@docmost/db/types/entity.types';
|
||||
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
|
||||
import {
|
||||
SpaceCaslAction,
|
||||
SpaceAbility,
|
||||
ISpaceAbility,
|
||||
SpaceCaslSubject,
|
||||
} from '../interfaces/space-ability.type';
|
||||
import { findHighestUserSpaceRole } from '@docmost/db/repos/space/utils';
|
||||
@@ -39,7 +39,7 @@ export default class SpaceAbilityFactory {
|
||||
}
|
||||
|
||||
function buildSpaceAdminAbility() {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<SpaceAbility>>(
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<ISpaceAbility>>(
|
||||
createMongoAbility,
|
||||
);
|
||||
can(SpaceCaslAction.Manage, SpaceCaslSubject.Settings);
|
||||
@@ -49,7 +49,7 @@ function buildSpaceAdminAbility() {
|
||||
}
|
||||
|
||||
function buildSpaceWriterAbility() {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<SpaceAbility>>(
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<ISpaceAbility>>(
|
||||
createMongoAbility,
|
||||
);
|
||||
can(SpaceCaslAction.Read, SpaceCaslSubject.Settings);
|
||||
@@ -59,7 +59,7 @@ function buildSpaceWriterAbility() {
|
||||
}
|
||||
|
||||
function buildSpaceReaderAbility() {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<SpaceAbility>>(
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<ISpaceAbility>>(
|
||||
createMongoAbility,
|
||||
);
|
||||
can(SpaceCaslAction.Read, SpaceCaslSubject.Settings);
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
AbilityBuilder,
|
||||
createMongoAbility,
|
||||
MongoAbility,
|
||||
} from '@casl/ability';
|
||||
import { UserRole } from '../../../helpers/types/permission';
|
||||
import { User, Workspace } from '@docmost/db/types/entity.types';
|
||||
import {
|
||||
IWorkspaceAbility,
|
||||
WorkspaceCaslAction,
|
||||
WorkspaceCaslSubject,
|
||||
} from '../interfaces/workspace-ability.type';
|
||||
|
||||
@Injectable()
|
||||
export default class WorkspaceAbilityFactory {
|
||||
createForUser(user: User, workspace: Workspace) {
|
||||
const userRole = user.role;
|
||||
|
||||
switch (userRole) {
|
||||
case UserRole.OWNER:
|
||||
return buildWorkspaceOwnerAbility();
|
||||
case UserRole.ADMIN:
|
||||
return buildWorkspaceAdminAbility();
|
||||
case UserRole.MEMBER:
|
||||
return buildWorkspaceMemberAbility();
|
||||
default:
|
||||
throw new NotFoundException('Workspace permissions not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildWorkspaceOwnerAbility() {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<IWorkspaceAbility>>(
|
||||
createMongoAbility,
|
||||
);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Settings);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Member);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Space);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Group);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Member);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Attachment);
|
||||
|
||||
return build();
|
||||
}
|
||||
|
||||
function buildWorkspaceAdminAbility() {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<IWorkspaceAbility>>(
|
||||
createMongoAbility,
|
||||
);
|
||||
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Settings);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Member);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Space);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Group);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Member);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Attachment);
|
||||
|
||||
return build();
|
||||
}
|
||||
|
||||
function buildWorkspaceMemberAbility() {
|
||||
const { can, build } = new AbilityBuilder<MongoAbility<IWorkspaceAbility>>(
|
||||
createMongoAbility,
|
||||
);
|
||||
can(WorkspaceCaslAction.Read, WorkspaceCaslSubject.Settings);
|
||||
can(WorkspaceCaslAction.Read, WorkspaceCaslSubject.Member);
|
||||
can(WorkspaceCaslAction.Read, WorkspaceCaslSubject.Space);
|
||||
can(WorkspaceCaslAction.Read, WorkspaceCaslSubject.Group);
|
||||
can(WorkspaceCaslAction.Manage, WorkspaceCaslSubject.Attachment);
|
||||
|
||||
return build();
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum Action {
|
||||
Manage = 'manage',
|
||||
Create = 'create',
|
||||
Read = 'read',
|
||||
Update = 'update',
|
||||
Delete = 'delete',
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import CaslAbilityFactory from './abilities/casl-ability.factory';
|
||||
import SpaceAbilityFactory from './abilities/space-ability.factory';
|
||||
import WorkspaceAbilityFactory from './abilities/workspace-ability.factory';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [CaslAbilityFactory, SpaceAbilityFactory],
|
||||
exports: [CaslAbilityFactory, SpaceAbilityFactory],
|
||||
providers: [WorkspaceAbilityFactory, SpaceAbilityFactory],
|
||||
exports: [WorkspaceAbilityFactory, SpaceAbilityFactory],
|
||||
})
|
||||
export class CaslModule {}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { PolicyHandler } from '../interfaces/policy-handler.interface';
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const CHECK_POLICIES_KEY = 'check_policy';
|
||||
export const CheckPolicies = (...handlers: PolicyHandler[]) =>
|
||||
SetMetadata(CHECK_POLICIES_KEY, handlers);
|
||||
@@ -1,40 +0,0 @@
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import CaslAbilityFactory, {
|
||||
AppAbility,
|
||||
} from '../abilities/casl-ability.factory';
|
||||
import { PolicyHandler } from '../interfaces/policy-handler.interface';
|
||||
import { CHECK_POLICIES_KEY } from '../decorators/policies.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class PoliciesGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private caslAbilityFactory: CaslAbilityFactory,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const policyHandlers =
|
||||
this.reflector.get<PolicyHandler[]>(
|
||||
CHECK_POLICIES_KEY,
|
||||
context.getHandler(),
|
||||
) || [];
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user.user;
|
||||
const workspace = request.user.workspace;
|
||||
|
||||
const ability = this.caslAbilityFactory.createForUser(user, workspace);
|
||||
|
||||
return policyHandlers.every((handler) =>
|
||||
this.execPolicyHandler(handler, ability),
|
||||
);
|
||||
}
|
||||
|
||||
private execPolicyHandler(handler: PolicyHandler, ability: AppAbility) {
|
||||
if (typeof handler === 'function') {
|
||||
return handler(ability);
|
||||
}
|
||||
return handler.handle(ability);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { AppAbility } from '../abilities/casl-ability.factory';
|
||||
|
||||
interface IPolicyHandler {
|
||||
handle(ability: AppAbility): boolean;
|
||||
}
|
||||
|
||||
type PolicyHandlerCallback = (ability: AppAbility) => boolean;
|
||||
|
||||
export type PolicyHandler = IPolicyHandler | PolicyHandlerCallback;
|
||||
@@ -11,7 +11,7 @@ export enum SpaceCaslSubject {
|
||||
Page = 'page',
|
||||
}
|
||||
|
||||
export type SpaceAbility =
|
||||
export type ISpaceAbility =
|
||||
| [SpaceCaslAction, SpaceCaslSubject.Settings]
|
||||
| [SpaceCaslAction, SpaceCaslSubject.Member]
|
||||
| [SpaceCaslAction, SpaceCaslSubject.Page];
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
export enum WorkspaceCaslAction {
|
||||
Manage = 'manage',
|
||||
Create = 'create',
|
||||
Read = 'read',
|
||||
Edit = 'edit',
|
||||
Delete = 'delete',
|
||||
}
|
||||
export enum WorkspaceCaslSubject {
|
||||
Settings = 'settings',
|
||||
Member = 'member',
|
||||
Space = 'space',
|
||||
Group = 'group',
|
||||
Attachment = 'attachment',
|
||||
}
|
||||
|
||||
export type IWorkspaceAbility =
|
||||
| [WorkspaceCaslAction, WorkspaceCaslSubject.Settings]
|
||||
| [WorkspaceCaslAction, WorkspaceCaslSubject.Member]
|
||||
| [WorkspaceCaslAction, WorkspaceCaslSubject.Space]
|
||||
| [WorkspaceCaslAction, WorkspaceCaslSubject.Group]
|
||||
| [WorkspaceCaslAction, WorkspaceCaslSubject.Attachment];
|
||||
Reference in New Issue
Block a user