cff17db6cb
## 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.
108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
import { UseFilters, UseGuards, UsePipes } from '@nestjs/common';
|
|
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
|
|
|
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
|
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
|
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
|
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
|
|
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
|
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
|
import { CreateRemoteServerInput } from 'src/engine/metadata-modules/remote-server/dtos/create-remote-server.input';
|
|
import { RemoteServerIdInput } from 'src/engine/metadata-modules/remote-server/dtos/remote-server-id.input';
|
|
import { RemoteServerTypeInput } from 'src/engine/metadata-modules/remote-server/dtos/remote-server-type.input';
|
|
import { RemoteServerDTO } from 'src/engine/metadata-modules/remote-server/dtos/remote-server.dto';
|
|
import { UpdateRemoteServerInput } from 'src/engine/metadata-modules/remote-server/dtos/update-remote-server.input';
|
|
import { type RemoteServerType } from 'src/engine/metadata-modules/remote-server/remote-server.entity';
|
|
import { RemoteServerService } from 'src/engine/metadata-modules/remote-server/remote-server.service';
|
|
import { remoteServerGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/remote-server/utils/remote-server-graphql-api-exception-handler.util';
|
|
|
|
@UseGuards(WorkspaceAuthGuard)
|
|
@UsePipes(ResolverValidationPipe)
|
|
@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter)
|
|
@Resolver()
|
|
export class RemoteServerResolver {
|
|
constructor(
|
|
private readonly remoteServerService: RemoteServerService<RemoteServerType>,
|
|
) {}
|
|
|
|
@Mutation(() => RemoteServerDTO)
|
|
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
|
async createOneRemoteServer(
|
|
@Args('input') input: CreateRemoteServerInput<RemoteServerType>,
|
|
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
|
) {
|
|
try {
|
|
return await this.remoteServerService.createOneRemoteServer(
|
|
input,
|
|
workspaceId,
|
|
);
|
|
} catch (error) {
|
|
remoteServerGraphqlApiExceptionHandler(error);
|
|
}
|
|
}
|
|
|
|
@Mutation(() => RemoteServerDTO)
|
|
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
|
async updateOneRemoteServer(
|
|
@Args('input') input: UpdateRemoteServerInput<RemoteServerType>,
|
|
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
|
) {
|
|
try {
|
|
return await this.remoteServerService.updateOneRemoteServer(
|
|
input,
|
|
workspaceId,
|
|
);
|
|
} catch (error) {
|
|
remoteServerGraphqlApiExceptionHandler(error);
|
|
}
|
|
}
|
|
|
|
@Mutation(() => RemoteServerDTO)
|
|
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
|
async deleteOneRemoteServer(
|
|
@Args('input') { id }: RemoteServerIdInput,
|
|
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
|
) {
|
|
try {
|
|
return await this.remoteServerService.deleteOneRemoteServer(
|
|
id,
|
|
workspaceId,
|
|
);
|
|
} catch (error) {
|
|
remoteServerGraphqlApiExceptionHandler(error);
|
|
}
|
|
}
|
|
|
|
@Query(() => RemoteServerDTO)
|
|
async findOneRemoteServerById(
|
|
@Args('input') { id }: RemoteServerIdInput,
|
|
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
|
) {
|
|
try {
|
|
return await this.remoteServerService.findOneByIdWithinWorkspace(
|
|
id,
|
|
workspaceId,
|
|
);
|
|
} catch (error) {
|
|
remoteServerGraphqlApiExceptionHandler(error);
|
|
}
|
|
}
|
|
|
|
@Query(() => [RemoteServerDTO])
|
|
async findManyRemoteServersByType(
|
|
@Args('input')
|
|
{ foreignDataWrapperType }: RemoteServerTypeInput<RemoteServerType>,
|
|
@AuthWorkspace() { id: workspaceId }: WorkspaceEntity,
|
|
) {
|
|
try {
|
|
return await this.remoteServerService.findManyByTypeWithinWorkspace(
|
|
foreignDataWrapperType,
|
|
workspaceId,
|
|
);
|
|
} catch (error) {
|
|
remoteServerGraphqlApiExceptionHandler(error);
|
|
}
|
|
}
|
|
}
|