Browse Source

refactor: improved permission error

pull/9348/head
Pranav C 3 months ago
parent
commit
f96d78bb40
  1. 19
      packages/nocodb/src/helpers/catchError.ts
  2. 14
      packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts
  3. 201
      packages/nocodb/src/utils/acl.ts

19
packages/nocodb/src/helpers/catchError.ts

@ -1,5 +1,6 @@
import { NcErrorType } from 'nocodb-sdk'; import { NcErrorType } from 'nocodb-sdk';
import { Logger } from '@nestjs/common'; import { Logger } from '@nestjs/common';
import { generateReadablePermissionErr } from 'src/utils/acl';
import type { BaseType, SourceType } from 'nocodb-sdk'; import type { BaseType, SourceType } from 'nocodb-sdk';
import type { ErrorObject } from 'ajv'; import type { ErrorObject } from 'ajv';
import { defaultLimitConfig } from '~/helpers/extractLimitAndOffset'; import { defaultLimitConfig } from '~/helpers/extractLimitAndOffset';
@ -637,6 +638,24 @@ export class NcBaseErrorv2 extends NcBaseError {
} }
export class NcError { export class NcError {
static permissionDenied(
permissionName: string,
roles: Record<string, boolean>,
extendedScopeRoles: any,
) {
throw new NcBaseErrorv2(NcErrorType.PERMISSION_DENIED, {
customMessage: generateReadablePermissionErr(
permissionName,
roles,
extendedScopeRoles,
),
details: {
permissionName,
roles,
extendedScopeRoles,
},
});
}
static authenticationRequired(args?: NcErrorArgs) { static authenticationRequired(args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.AUTHENTICATION_REQUIRED, args); throw new NcBaseErrorv2(NcErrorType.AUTHENTICATION_REQUIRED, args);
} }

14
packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts

@ -497,11 +497,15 @@ export class AclMiddleware implements NestInterceptor {
); );
}))); })));
if (!isAllowed) { if (!isAllowed) {
NcError.forbidden( NcError.permissionDenied(permissionName, roles, extendedScopeRoles);
`${permissionName} - ${getRolesLabels(
Object.keys(roles).filter((k) => roles[k]), // NcError.forbidden(
)} : Not allowed`, //
); //
// `${permissionName} - ${getRolesLabels(
// Object.keys(roles).filter((k) => roles[k]),
// )} : Not allowed`,
// );
} }
// check if permission have source level permission restriction // check if permission have source level permission restriction

201
packages/nocodb/src/utils/acl.ts

@ -11,6 +11,7 @@ const roleScopes = {
], ],
}; };
// todo: convert to enum
const permissionScopes = { const permissionScopes = {
org: [ org: [
// API Tokens // API Tokens
@ -231,7 +232,7 @@ const rolePermissions:
commentsCount: true, commentsCount: true,
auditListRow: true, auditListRow: true,
userInvite: true, // userInvite: true,
}, },
}, },
[ProjectRoles.COMMENTER]: { [ProjectRoles.COMMENTER]: {
@ -506,3 +507,201 @@ export const sourceRestrictions = {
}; };
export default rolePermissions; export default rolePermissions;
const permissionDescriptions: Record<string, string> = {
// cloudOrg permissions
orgUserAdd: 'add users to the organization',
orgUserRemove: 'remove users from the organization',
orgUserRoleUpdate: 'update user roles in the organization',
orgUpdate: 'update organization details',
orgDomainList: 'view organization domains',
orgDomainAdd: 'add a new domain to the organization',
orgDomainVerify: 'verify a domain in the organization',
orgDomainUpdate: 'update domain details in the organization',
orgDomainDelete: 'remove a domain from the organization',
orgSsoClientCreate: 'create a new SSO client',
orgSsoClientUpdate: 'update SSO client details',
orgSsoClientDelete: 'delete an SSO client',
orgWorkspaceUpdate: 'update workspace details',
orgWorkspaceAdd: 'add a new workspace',
orgGet: 'view organization details',
orgWorkspaceList: 'view list of workspaces in the organization',
orgUserList: 'view list of users in the organization',
orgBaseList: 'view list of bases in the organization',
orgSsoClientList: 'view list of SSO clients in the organization',
// org permissions
ssoClientList: 'view list of SSO clients',
ssoClientCreate: 'create a new SSO client',
ssoClientUpdate: 'update SSO client details',
ssoClientDelete: 'delete an SSO client',
ssoClientGet: 'view SSO client details',
ssoClientTest: 'test an SSO client',
apiTokenList: 'view list of API tokens',
apiTokenCreate: 'create a new API token',
apiTokenDelete: 'delete an API token',
passwordChange: 'change your password',
workspaceList: 'view list of workspaces',
workspaceCreate: 'create a new workspace',
isPluginActive: 'check if a plugin is active',
pluginList: 'view list of plugins',
pluginTest: 'test a plugin',
pluginRead: 'read plugin configuration',
pluginUpdate: 'update plugin configuration',
commandPalette: 'access the command palette',
testConnection: 'test connection to a service',
genericGPT: 'use generic GPT functionality',
upload: 'upload files',
uploadViaURL: 'upload files via URL',
notification: 'send notifications',
// workspace permissions
integrationCreate: 'create a new integration',
integrationDelete: 'delete an integration',
integrationUpdate: 'update integration details',
integrationList: 'view list of integrations',
// base permissions
formViewGet: 'view forms',
baseGet: 'view base details',
tableGet: 'view table details',
dataList: 'view data',
dataRead: 'read data',
dataExist: 'check if data exists',
dataFindOne: 'find a single data record',
dataGroupBy: 'group data by a specific field',
exportCsv: 'export data to CSV',
exportExcel: 'export data to Excel',
sortList: 'view list of sorts',
filterList: 'view list of filters',
baseInfoGet: 'view base information',
baseUserMetaUpdate: 'update user metadata for the base',
galleryViewGet: 'view gallery',
kanbanViewGet: 'view Kanban board',
calendarViewGet: 'view calendar',
gridViewUpdate: 'update grid view',
formViewUpdate: 'update form view',
groupedDataList: 'view grouped data',
mmList: 'view many-to-many relationships',
hmList: 'view hierarchical relationships',
commentRow: 'comment on a row',
baseList: 'view list of bases',
baseCost: 'view base cost',
tableList: 'view list of tables',
viewList: 'view list of views',
functionList: 'view list of functions',
sequenceList: 'view list of sequences',
procedureList: 'view list of procedures',
columnList: 'view list of columns',
triggerList: 'view list of triggers',
relationList: 'view list of relations',
relationListAll: 'view all relations',
indexList: 'view list of indexes',
list: 'view list of items',
dataCount: 'view data count',
dataAggregate: 'view data aggregates',
swaggerJson: 'view Swagger JSON',
commentList: 'view list of comments',
commentsCount: 'view comment count',
commentDelete: 'delete comments',
commentUpdate: 'update comments',
hideAllColumns: 'hide all columns',
showAllColumns: 'show all columns',
auditListRow: 'view audit log for a row',
auditRowUpdate: 'update audit log for a row',
dataUpdate: 'update data',
dataDelete: 'delete data',
dataInsert: 'insert new data',
viewColumnUpdate: 'update view columns',
sortCreate: 'create a new sort',
sortUpdate: 'update an existing sort',
sortDelete: 'delete a sort',
filterCreate: 'create a new filter',
filterUpdate: 'update an existing filter',
filterDelete: 'delete a filter',
filterGet: 'view filter details',
filterChildrenRead: 'view child filters',
mmExcludedList: 'view excluded many-to-many relationships',
hmExcludedList: 'view excluded hierarchical relationships',
btExcludedList: 'view excluded relationships',
ooExcludedList: 'view excluded one-to-one relationships',
gridColumnUpdate: 'update grid columns',
bulkDataInsert: 'bulk insert data',
bulkDataUpdate: 'bulk update data',
bulkDataUpdateAll: 'bulk update all data',
bulkDataDelete: 'bulk delete data',
bulkDataDeleteAll: 'bulk delete all data',
relationDataRemove: 'remove related data',
relationDataAdd: 'add related data',
baseUserList: 'view list of users in the base',
baseApiTokenList: 'view list of base API tokens',
baseApiTokenCreate: 'create a new base API token',
baseApiTokenDelete: 'delete a base API token',
createBase: 'create a new base',
baseDelete: 'delete a base',
sourceCreate: 'create a new source',
pageGet: 'view page details',
pageList: 'view list of pages',
pageSearch: 'search pages',
pageParents: 'view parent pages',
pageCreate: 'create a new page',
pageUpdate: 'update a page',
pageDelete: 'delete a page',
pageGpt: 'use GPT to assist with pages',
docsMagicCreatePages: 'use Docs Magic to create pages',
pagePaginate: 'paginate pages',
pageDirectoryImport: 'import a page directory',
layoutGet: 'view layout details',
layoutList: 'view list of layouts',
layoutCreate: 'create a new layout',
widgetsList: 'view list of widgets',
widgetGet: 'view widget details',
widgetCreate: 'create a new widget',
widgetUpdate: 'update a widget',
widgetDelete: 'delete a widget',
widgetFilterList: 'view list of widget filters',
widgetFilterCreate: 'create a new widget filter',
userInvite: 'invite a user',
jobList: 'view list of jobs',
hookTrigger: 'trigger a webhook',
};
// Human-readable descriptions for roles
const roleDescriptions: Record<string, string> = {
// Base roles
[ProjectRoles.VIEWER]: 'Viewer',
[ProjectRoles.COMMENTER]: 'Commenter',
[ProjectRoles.EDITOR]: 'Editor',
[ProjectRoles.CREATOR]: 'Creator',
[ProjectRoles.OWNER]: 'Owner',
};
export function generateReadablePermissionErr(
permissionName: string,
roles: Record<string, boolean>,
_scope: string,
): string {
const roleLabels = Object.keys(roles)
.filter((key) => roles[key])
.map((role) => roleDescriptions[role])
.join(', ');
const permissionDescription =
permissionDescriptions[permissionName] ||
`perform the action "${permissionName}"`;
return `You do not have permission to ${permissionDescription} with the roles: ${roleLabels}. Please contact support if you need further assistance.`;
}

Loading…
Cancel
Save