Enhance role-check system with stricter checks (#15392)
## Overview This PR strengthens our permission system by introducing more granular role-based access control across the platform. ## Changes ### New Permissions Added - **Applications** - Control who can install and manage applications - **Layouts** - Control who can customize page layouts and UI structure - **AI** - Control access to AI features and agents - **Upload File** - Separate permission for file uploads - **Download File** - Separate permission for file downloads (frontend visibility) ### Security Enhancements - Implemented whitelist-based validation for workspace field updates - Added explicit permission guards to core entity resolvers - Enhanced ESLint rule to enforce permission checks on all mutations - Created `CustomPermissionGuard` and `NoPermissionGuard` for better code documentation ### Affected Components - Core entity resolvers: webhooks, files, domains, applications, layouts, postgres credentials - Workspace update mutations now use whitelist validation - Settings UI updated with new permission controls ### Developer Experience - ESLint now catches missing permission guards during development - Explicit guard markers make permission requirements clear in code review - Comprehensive test coverage for new permission logic ## Testing - ✅ All TypeScript type checks pass - ✅ ESLint validation passes - ✅ New permission guards properly enforced - ✅ Frontend UI displays new permissions correctly ## Migration Notes Existing workspaces will need to assign the new permissions to roles as needed. By default, all new permissions are set to `false` for non-admin roles.
This commit is contained in:
+45
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewFieldPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args.input or args.inputs
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.viewId === 'string') {
|
||||
viewId = args.input.viewId;
|
||||
}
|
||||
|
||||
if (!viewId && Array.isArray(args?.inputs) && args.inputs.length > 0) {
|
||||
viewId =
|
||||
typeof args.inputs[0]?.viewId === 'string'
|
||||
? args.inputs[0].viewId
|
||||
: null;
|
||||
}
|
||||
|
||||
// For REST: extract from request body
|
||||
if (!viewId && typeof request.body?.viewId === 'string') {
|
||||
viewId = request.body.viewId;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewFilterGroupPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args.input
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.viewId === 'string') {
|
||||
viewId = args.input.viewId;
|
||||
}
|
||||
|
||||
// For REST: extract from request body
|
||||
if (!viewId && typeof request.body?.viewId === 'string') {
|
||||
viewId = request.body.viewId;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewFilterPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args.input
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.viewId === 'string') {
|
||||
viewId = args.input.viewId;
|
||||
}
|
||||
|
||||
// For REST: extract from request body
|
||||
if (!viewId && typeof request.body?.viewId === 'string') {
|
||||
viewId = request.body.viewId;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewGroupPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args.input or args.inputs
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.viewId === 'string') {
|
||||
viewId = args.input.viewId;
|
||||
}
|
||||
|
||||
if (!viewId && Array.isArray(args?.inputs) && args.inputs.length > 0) {
|
||||
viewId =
|
||||
typeof args.inputs[0]?.viewId === 'string'
|
||||
? args.inputs[0].viewId
|
||||
: null;
|
||||
}
|
||||
|
||||
// For REST: extract from request body
|
||||
if (!viewId && typeof request.body?.viewId === 'string') {
|
||||
viewId = request.body.viewId;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateViewSortPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args.input
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.viewId === 'string') {
|
||||
viewId = args.input.viewId;
|
||||
}
|
||||
|
||||
// For REST: extract from request body
|
||||
if (!viewId && typeof request.body?.viewId === 'string') {
|
||||
viewId = request.body.viewId;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewFieldPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewField',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewFilterGroupPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
entityId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewFilterGroup',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewFilterPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewFilter',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewGroupPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewGroup',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
viewId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!viewId && typeof request.params?.id === 'string') {
|
||||
viewId = request.params.id;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyView(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteViewSortPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
entityId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewSort',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyViewFieldPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewField',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyViewFilterGroupPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
entityId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewFilterGroup',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyViewFilterPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewFilter',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyViewGroupPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewGroup',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyViewPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
viewId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!viewId && typeof request.params?.id === 'string') {
|
||||
viewId = request.params.id;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyView(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class DestroyViewSortPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
entityId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewSort',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewFieldPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewField',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewFilterGroupPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
entityId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewFilterGroup',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewFilterPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewFilter',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewGroupPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.input.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.input?.id === 'string') {
|
||||
entityId = args.input.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewGroup',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewPermissionGuard implements CanActivate {
|
||||
constructor(private readonly viewAccessService: ViewAccessService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let viewId: string | null = null;
|
||||
|
||||
// For GraphQL: extract from args
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
viewId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!viewId && typeof request.params?.id === 'string') {
|
||||
viewId = request.params.id;
|
||||
}
|
||||
|
||||
return this.viewAccessService.canUserModifyView(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Injectable,
|
||||
type CanActivate,
|
||||
type ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateViewSortPermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly viewAccessService: ViewAccessService,
|
||||
private readonly viewEntityLookupService: ViewEntityLookupService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
let entityId = '';
|
||||
|
||||
// For GraphQL: extract from args.id
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (typeof args?.id === 'string') {
|
||||
entityId = args.id;
|
||||
}
|
||||
|
||||
// For REST: extract from URL params
|
||||
if (!entityId && typeof request.params?.id === 'string') {
|
||||
entityId = request.params.id;
|
||||
}
|
||||
|
||||
const viewId = entityId
|
||||
? await this.viewEntityLookupService.findViewIdByEntityIdAndKind(
|
||||
'viewSort',
|
||||
entityId,
|
||||
request.workspace.id,
|
||||
)
|
||||
: null;
|
||||
|
||||
return this.viewAccessService.canUserModifyViewByChildEntity(
|
||||
viewId,
|
||||
request.userWorkspaceId,
|
||||
request.workspace.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { ViewVisibility } from 'src/engine/metadata-modules/view/enums/view-visibility.enum';
|
||||
import {
|
||||
ViewException,
|
||||
ViewExceptionCode,
|
||||
ViewExceptionMessageKey,
|
||||
generateViewExceptionMessage,
|
||||
generateViewUserFriendlyExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/view/exceptions/view.exception';
|
||||
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
|
||||
|
||||
@Injectable()
|
||||
export class ViewAccessService {
|
||||
constructor(
|
||||
private readonly viewService: ViewService,
|
||||
private readonly permissionsService: PermissionsService,
|
||||
) {}
|
||||
|
||||
async canUserModifyView(
|
||||
viewId: string | null,
|
||||
userWorkspaceId: string | undefined,
|
||||
workspaceId: string,
|
||||
): Promise<boolean> {
|
||||
// If viewId is null, the entity doesn't exist - allow the operation
|
||||
// so the service can handle the NOT_FOUND error properly
|
||||
if (!viewId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const view = await this.viewService.findByIdIncludingDeleted(
|
||||
viewId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
// If view doesn't exist, allow through to service for proper error message
|
||||
if (!view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.checkViewAccess(view, userWorkspaceId, workspaceId);
|
||||
}
|
||||
|
||||
async canUserModifyViewByChildEntity(
|
||||
viewId: string | null,
|
||||
userWorkspaceId: string | undefined,
|
||||
workspaceId: string,
|
||||
): Promise<boolean> {
|
||||
// If viewId is null, the child entity doesn't exist
|
||||
// Allow through so the service can throw the proper entity-specific error
|
||||
// (e.g., "View field not found" instead of generic "View not found")
|
||||
if (!viewId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const view = await this.viewService.findByIdIncludingDeleted(
|
||||
viewId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
// If view doesn't exist, allow through to service for proper error message
|
||||
if (!view) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.checkViewAccess(view, userWorkspaceId, workspaceId);
|
||||
}
|
||||
|
||||
private async checkViewAccess(
|
||||
view: ViewEntity,
|
||||
userWorkspaceId: string | undefined,
|
||||
workspaceId: string,
|
||||
): Promise<boolean> {
|
||||
const permissions = isDefined(userWorkspaceId)
|
||||
? await this.permissionsService.getUserWorkspacePermissions({
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
})
|
||||
: null;
|
||||
|
||||
const hasViewsPermission =
|
||||
permissions?.permissionFlags[PermissionFlagType.VIEWS] ?? false;
|
||||
|
||||
// Users with VIEWS permission can manipulate all views
|
||||
if (hasViewsPermission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Users without VIEWS permission can only manipulate their own unlisted views
|
||||
const canAccess =
|
||||
view.visibility === ViewVisibility.UNLISTED &&
|
||||
view.createdByUserWorkspaceId === userWorkspaceId;
|
||||
|
||||
if (!canAccess) {
|
||||
throw new ViewException(
|
||||
generateViewExceptionMessage(
|
||||
ViewExceptionMessageKey.VIEW_MODIFY_PERMISSION_DENIED,
|
||||
),
|
||||
ViewExceptionCode.VIEW_MODIFY_PERMISSION_DENIED,
|
||||
{
|
||||
userFriendlyMessage: generateViewUserFriendlyExceptionMessage(
|
||||
ViewExceptionMessageKey.VIEW_MODIFY_PERMISSION_DENIED,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
import { type ViewChildEntityKind } from 'src/engine/metadata-modules/view-permissions/types/view-permissions.types';
|
||||
import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
|
||||
|
||||
@Injectable()
|
||||
export class ViewEntityLookupService {
|
||||
constructor(
|
||||
@InjectRepository(ViewFieldEntity)
|
||||
private readonly viewFieldRepository: Repository<ViewFieldEntity>,
|
||||
@InjectRepository(ViewFilterEntity)
|
||||
private readonly viewFilterRepository: Repository<ViewFilterEntity>,
|
||||
@InjectRepository(ViewFilterGroupEntity)
|
||||
private readonly viewFilterGroupRepository: Repository<ViewFilterGroupEntity>,
|
||||
@InjectRepository(ViewGroupEntity)
|
||||
private readonly viewGroupRepository: Repository<ViewGroupEntity>,
|
||||
@InjectRepository(ViewSortEntity)
|
||||
private readonly viewSortRepository: Repository<ViewSortEntity>,
|
||||
) {}
|
||||
|
||||
async findViewIdByEntityIdAndKind(
|
||||
kind: ViewChildEntityKind,
|
||||
entityId: string,
|
||||
workspaceId: string,
|
||||
): Promise<string | null> {
|
||||
switch (kind) {
|
||||
case 'viewField': {
|
||||
const row = await this.viewFieldRepository.findOne({
|
||||
where: { id: entityId, workspaceId },
|
||||
select: ['viewId'],
|
||||
});
|
||||
|
||||
if (row) return row.viewId;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'viewFilter': {
|
||||
const row = await this.viewFilterRepository.findOne({
|
||||
where: { id: entityId, workspaceId },
|
||||
select: ['viewId'],
|
||||
});
|
||||
|
||||
if (row) return row.viewId;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'viewFilterGroup': {
|
||||
const row = await this.viewFilterGroupRepository.findOne({
|
||||
where: { id: entityId, workspaceId },
|
||||
select: ['viewId'],
|
||||
});
|
||||
|
||||
if (row) return row.viewId;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'viewGroup': {
|
||||
const row = await this.viewGroupRepository.findOne({
|
||||
where: { id: entityId, workspaceId },
|
||||
select: ['viewId'],
|
||||
});
|
||||
|
||||
if (row) return row.viewId;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'viewSort': {
|
||||
const row = await this.viewSortRepository.findOne({
|
||||
where: { id: entityId, workspaceId },
|
||||
select: ['viewId'],
|
||||
});
|
||||
|
||||
if (row) return row.viewId;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export type ViewChildEntityKind =
|
||||
| 'viewField'
|
||||
| 'viewFilter'
|
||||
| 'viewFilterGroup'
|
||||
| 'viewGroup'
|
||||
| 'viewSort';
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { I18nModule } from 'src/engine/core-modules/i18n/i18n.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity';
|
||||
import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity';
|
||||
import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity';
|
||||
import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
import { CreateViewFieldPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-field-permission.guard';
|
||||
import { CreateViewFilterGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-filter-group-permission.guard';
|
||||
import { CreateViewFilterPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-filter-permission.guard';
|
||||
import { CreateViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-group-permission.guard';
|
||||
import { CreateViewSortPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-sort-permission.guard';
|
||||
import { DeleteViewFieldPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-field-permission.guard';
|
||||
import { DeleteViewFilterGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-filter-group-permission.guard';
|
||||
import { DeleteViewFilterPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-filter-permission.guard';
|
||||
import { DeleteViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-group-permission.guard';
|
||||
import { DeleteViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-permission.guard';
|
||||
import { DeleteViewSortPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-sort-permission.guard';
|
||||
import { DestroyViewFieldPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-field-permission.guard';
|
||||
import { DestroyViewFilterGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-filter-group-permission.guard';
|
||||
import { DestroyViewFilterPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-filter-permission.guard';
|
||||
import { DestroyViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-group-permission.guard';
|
||||
import { DestroyViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-permission.guard';
|
||||
import { DestroyViewSortPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-sort-permission.guard';
|
||||
import { UpdateViewFieldPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-field-permission.guard';
|
||||
import { UpdateViewFilterGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-filter-group-permission.guard';
|
||||
import { UpdateViewFilterPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-filter-permission.guard';
|
||||
import { UpdateViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-group-permission.guard';
|
||||
import { UpdateViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-permission.guard';
|
||||
import { UpdateViewSortPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-sort-permission.guard';
|
||||
import { ViewAccessService } from 'src/engine/metadata-modules/view-permissions/services/view-access.service';
|
||||
import { ViewEntityLookupService } from 'src/engine/metadata-modules/view-permissions/services/view-entity-lookup.service';
|
||||
import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
ViewEntity,
|
||||
ViewFieldEntity,
|
||||
ViewFilterEntity,
|
||||
ViewFilterGroupEntity,
|
||||
ViewGroupEntity,
|
||||
ViewSortEntity,
|
||||
]),
|
||||
I18nModule,
|
||||
PermissionsModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
],
|
||||
providers: [
|
||||
ViewService,
|
||||
ViewEntityLookupService,
|
||||
ViewAccessService,
|
||||
UpdateViewPermissionGuard,
|
||||
DeleteViewPermissionGuard,
|
||||
DestroyViewPermissionGuard,
|
||||
CreateViewFieldPermissionGuard,
|
||||
UpdateViewFieldPermissionGuard,
|
||||
DeleteViewFieldPermissionGuard,
|
||||
DestroyViewFieldPermissionGuard,
|
||||
CreateViewFilterPermissionGuard,
|
||||
UpdateViewFilterPermissionGuard,
|
||||
DeleteViewFilterPermissionGuard,
|
||||
DestroyViewFilterPermissionGuard,
|
||||
CreateViewFilterGroupPermissionGuard,
|
||||
UpdateViewFilterGroupPermissionGuard,
|
||||
DeleteViewFilterGroupPermissionGuard,
|
||||
DestroyViewFilterGroupPermissionGuard,
|
||||
CreateViewGroupPermissionGuard,
|
||||
UpdateViewGroupPermissionGuard,
|
||||
DeleteViewGroupPermissionGuard,
|
||||
DestroyViewGroupPermissionGuard,
|
||||
CreateViewSortPermissionGuard,
|
||||
UpdateViewSortPermissionGuard,
|
||||
DeleteViewSortPermissionGuard,
|
||||
DestroyViewSortPermissionGuard,
|
||||
],
|
||||
exports: [
|
||||
ViewService,
|
||||
ViewEntityLookupService,
|
||||
ViewAccessService,
|
||||
UpdateViewPermissionGuard,
|
||||
DeleteViewPermissionGuard,
|
||||
DestroyViewPermissionGuard,
|
||||
CreateViewFieldPermissionGuard,
|
||||
UpdateViewFieldPermissionGuard,
|
||||
DeleteViewFieldPermissionGuard,
|
||||
DestroyViewFieldPermissionGuard,
|
||||
CreateViewFilterPermissionGuard,
|
||||
UpdateViewFilterPermissionGuard,
|
||||
DeleteViewFilterPermissionGuard,
|
||||
DestroyViewFilterPermissionGuard,
|
||||
CreateViewFilterGroupPermissionGuard,
|
||||
UpdateViewFilterGroupPermissionGuard,
|
||||
DeleteViewFilterGroupPermissionGuard,
|
||||
DestroyViewFilterGroupPermissionGuard,
|
||||
CreateViewGroupPermissionGuard,
|
||||
UpdateViewGroupPermissionGuard,
|
||||
DeleteViewGroupPermissionGuard,
|
||||
DestroyViewGroupPermissionGuard,
|
||||
CreateViewSortPermissionGuard,
|
||||
UpdateViewSortPermissionGuard,
|
||||
DeleteViewSortPermissionGuard,
|
||||
DestroyViewSortPermissionGuard,
|
||||
],
|
||||
})
|
||||
export class ViewPermissionsModule {}
|
||||
Reference in New Issue
Block a user