@holoyan/adonisjs-permissions 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +661 -0
  3. package/build/configure.d.ts +2 -0
  4. package/build/configure.js +33 -0
  5. package/build/index.d.ts +8 -0
  6. package/build/index.js +17 -0
  7. package/build/providers/role_permission_provider.d.ts +12 -0
  8. package/build/providers/role_permission_provider.js +12 -0
  9. package/build/src/acl.d.ts +11 -0
  10. package/build/src/acl.js +29 -0
  11. package/build/src/decorators.d.ts +4 -0
  12. package/build/src/decorators.js +18 -0
  13. package/build/src/mixins/has_permissions.d.ts +133 -0
  14. package/build/src/mixins/has_permissions.js +19 -0
  15. package/build/src/mixins/has_role_permissions.d.ts +128 -0
  16. package/build/src/mixins/has_role_permissions.js +10 -0
  17. package/build/src/mixins/has_roles.d.ts +1 -0
  18. package/build/src/mixins/has_roles.js +47 -0
  19. package/build/src/models/model_permission.d.ts +12 -0
  20. package/build/src/models/model_permission.js +35 -0
  21. package/build/src/models/model_role.d.ts +11 -0
  22. package/build/src/models/model_role.js +31 -0
  23. package/build/src/models/permission.d.ts +16 -0
  24. package/build/src/models/permission.js +48 -0
  25. package/build/src/models/role.d.ts +16 -0
  26. package/build/src/models/role.js +48 -0
  27. package/build/src/morph_map.d.ts +8 -0
  28. package/build/src/morph_map.js +33 -0
  29. package/build/src/services/base_service.d.ts +14 -0
  30. package/build/src/services/base_service.js +12 -0
  31. package/build/src/services/helper.d.ts +21 -0
  32. package/build/src/services/helper.js +69 -0
  33. package/build/src/services/model_has_role_permissions.d.ts +48 -0
  34. package/build/src/services/model_has_role_permissions.js +172 -0
  35. package/build/src/services/model_service.d.ts +8 -0
  36. package/build/src/services/model_service.js +31 -0
  37. package/build/src/services/permissions/permission_has_model_roles.d.ts +20 -0
  38. package/build/src/services/permissions/permission_has_model_roles.js +67 -0
  39. package/build/src/services/permissions/permissions_service.d.ts +92 -0
  40. package/build/src/services/permissions/permissions_service.js +438 -0
  41. package/build/src/services/roles/role_has_model_permissions.d.ts +66 -0
  42. package/build/src/services/roles/role_has_model_permissions.js +152 -0
  43. package/build/src/services/roles/roles_service.d.ts +16 -0
  44. package/build/src/services/roles/roles_service.js +89 -0
  45. package/build/src/types.d.ts +21 -0
  46. package/build/src/types.js +1 -0
  47. package/build/stubs/configs/permissions.stub +20 -0
  48. package/build/stubs/main.d.ts +5 -0
  49. package/build/stubs/main.js +7 -0
  50. package/build/stubs/migrations/create_db.stub +97 -0
  51. package/package.json +95 -0
@@ -0,0 +1,152 @@
1
+ import { destructTarget, morphMap } from '../helper.js';
2
+ export class RoleHasModelPermissions {
3
+ role;
4
+ permissionService;
5
+ modelService;
6
+ constructor(role, permissionService, modelService) {
7
+ this.role = role;
8
+ this.permissionService = permissionService;
9
+ this.modelService = modelService;
10
+ }
11
+ models() {
12
+ return this.modelService.all(this.role.getModelId());
13
+ }
14
+ modelsFor(modelType) {
15
+ return this.modelService.allFor(modelType, this.role.getModelId());
16
+ }
17
+ /**
18
+ * todo
19
+ * @param model
20
+ */
21
+ // attachTo(model: LucidModel) {}
22
+ // permissions related BEGIN
23
+ async permissions() {
24
+ // for roles direct and all permissions are same
25
+ const map = await morphMap();
26
+ return this.permissionService.direct(map.getAlias(this.role), this.role.getModelId());
27
+ }
28
+ async globalPermissions() {
29
+ const map = await morphMap();
30
+ return this.permissionService.directGlobal(map.getAlias(this.role), this.role.getModelId());
31
+ }
32
+ async onResourcePermissions() {
33
+ const map = await morphMap();
34
+ return this.permissionService.directResource(map.getAlias(this.role), this.role.getModelId());
35
+ }
36
+ async containsPermission(permisison) {
37
+ const map = await morphMap();
38
+ const result = await this.permissionService.containsAny(map.getAlias(this.role), this.role.getModelId(), [permisison]);
39
+ return result;
40
+ }
41
+ /**
42
+ *
43
+ * @param permisisons
44
+ * @returns
45
+ */
46
+ async containsAllPermissions(permisisons) {
47
+ const map = await morphMap();
48
+ const result = await this.permissionService.containsAll(map.getAlias(this.role), this.role.getModelId(), permisisons);
49
+ return result;
50
+ }
51
+ /**
52
+ *
53
+ * @param permisisons
54
+ * @returns
55
+ */
56
+ async containsAnyPermissions(permisisons) {
57
+ const map = await morphMap();
58
+ const result = await this.permissionService.containsAny(map.getAlias(this.role), this.role.getModelId(), permisisons);
59
+ return result;
60
+ }
61
+ async hasPermission(permisison, target) {
62
+ const map = await morphMap();
63
+ const entity = await destructTarget(target);
64
+ const result = await this.permissionService.hasAny(map.getAlias(this.role), this.role.getModelId(), [permisison], entity.targetClass, entity.targetId);
65
+ return result;
66
+ }
67
+ /**
68
+ *
69
+ * @param permisisons
70
+ * @returns
71
+ */
72
+ async hasAllPermissions(permisisons, target) {
73
+ const map = await morphMap();
74
+ const entity = await destructTarget(target);
75
+ const result = await this.permissionService.hasAll(map.getAlias(this.role), this.role.getModelId(), permisisons, entity.targetClass, entity.targetId);
76
+ return result;
77
+ }
78
+ /**
79
+ *
80
+ * @param permisisons
81
+ * @returns
82
+ */
83
+ async hasAnyPermissions(permisisons, target) {
84
+ const map = await morphMap();
85
+ const entity = await destructTarget(target);
86
+ const result = await this.permissionService.hasAny(map.getAlias(this.role), this.role.getModelId(), permisisons, entity.targetClass, entity.targetId);
87
+ return result;
88
+ }
89
+ /**
90
+ *
91
+ * @param permisison
92
+ * @returns
93
+ */
94
+ can(permisison) {
95
+ return this.hasPermission(permisison);
96
+ }
97
+ canAll(permisisons) {
98
+ return this.hasAllPermissions(permisisons);
99
+ }
100
+ canAny(permisisons) {
101
+ return this.hasAnyPermissions(permisisons);
102
+ }
103
+ async forbidden(permisison, target) {
104
+ const map = await morphMap();
105
+ const entity = await destructTarget(target);
106
+ return this.permissionService.forbidden(map.getAlias(this.role), this.role.getModelId(), permisison, entity.targetClass, entity.targetId);
107
+ }
108
+ assign(permisison, target) {
109
+ return this.give(permisison, target);
110
+ }
111
+ async give(permisison, target) {
112
+ const map = await morphMap();
113
+ const entity = await destructTarget(target);
114
+ return this.permissionService.giveAll(map.getAlias(this.role), this.role.getModelId(), [permisison], entity.targetClass, entity.targetId, true);
115
+ }
116
+ async giveAll(permisisons, target) {
117
+ const map = await morphMap();
118
+ const entity = await destructTarget(target);
119
+ return this.permissionService.giveAll(map.getAlias(this.role), this.role.getModelId(), permisisons, entity.targetClass, entity.targetId, true);
120
+ }
121
+ assingAll(permisisons, target) {
122
+ return this.giveAll(permisisons, target);
123
+ }
124
+ async revokePermission(permisison) {
125
+ return this.revoke(permisison);
126
+ }
127
+ async revoke(permisison) {
128
+ return this.revokeAll([permisison]);
129
+ }
130
+ async revokeAll(permisisons, target) {
131
+ const map = await morphMap();
132
+ const entity = await destructTarget(target);
133
+ return this.permissionService.revokeAll(map.getAlias(this.role), this.role.getModelId(), permisisons, entity.targetClass, entity.targetId);
134
+ }
135
+ async revokeAllPermissions(permisisons) {
136
+ return this.revokeAll(permisisons);
137
+ }
138
+ async flush() {
139
+ const map = await morphMap();
140
+ return this.permissionService.flush(map.getAlias(this.role), this.role.getModelId());
141
+ }
142
+ async forbid(permisison, target) {
143
+ const map = await morphMap();
144
+ const entity = await destructTarget(target);
145
+ return this.permissionService.forbid(map.getAlias(this.role), this.role.getModelId(), permisison, entity.targetClass, entity.targetId);
146
+ }
147
+ async unforbid(permisison, target) {
148
+ const map = await morphMap();
149
+ const entity = await destructTarget(target);
150
+ return this.permissionService.unforbidAll(map.getAlias(this.role), this.role.getModelId(), [permisison], entity.targetClass, entity.targetId);
151
+ }
152
+ }
@@ -0,0 +1,16 @@
1
+ import Role from '../../models/role.js';
2
+ import { AclModel } from '../../types.js';
3
+ import BaseService from '../base_service.js';
4
+ export default class RolesService extends BaseService {
5
+ private modelRolesQuery;
6
+ all(modelType: string, modelId: number): import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<typeof Role, Role>;
7
+ has(modelType: string, modelId: number, role: string | Role): Promise<boolean>;
8
+ hasAll(modelType: string, modelId: number, roles: (string | Role)[]): Promise<boolean>;
9
+ hasAny(modelType: string, modelId: number, roles: (string | Role)[]): Promise<boolean>;
10
+ assign(role: string | Role, modelType: string, modelId: number): Promise<boolean>;
11
+ revoke(role: string | number, model: AclModel): Promise<boolean>;
12
+ revokeAll(roles: (string | number)[], model: AclModel): Promise<boolean>;
13
+ private extractRoleModel;
14
+ roleModelPermissionQuery(modelType: string): import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<typeof Role, Role>;
15
+ flush(modelType: string, modelId: number): Promise<void>;
16
+ }
@@ -0,0 +1,89 @@
1
+ import Role from '../../models/role.js';
2
+ import ModelRole from '../../models/model_role.js';
3
+ import BaseService from '../base_service.js';
4
+ import ModelPermission from '../../models/model_permission.js';
5
+ import { morphMap } from '../helper.js';
6
+ export default class RolesService extends BaseService {
7
+ modelRolesQuery(modelType, modelId) {
8
+ return Role.query()
9
+ .join(ModelRole.table + ' as mr', 'mr.role_id', '=', Role.table + '.id')
10
+ .where('mr.model_type', modelType)
11
+ .where('mr.model_id', modelId);
12
+ }
13
+ all(modelType, modelId) {
14
+ return this.modelRolesQuery(modelType, modelId).select(Role.table + '.*');
15
+ }
16
+ async has(modelType, modelId, role) {
17
+ return this.hasAll(modelType, modelId, [role]);
18
+ }
19
+ async hasAll(modelType, modelId, roles) {
20
+ const rolesQuery = this.modelRolesQuery(modelType, modelId);
21
+ let { slugs, ids } = this.formatList(roles);
22
+ if (slugs.length) {
23
+ rolesQuery.whereIn(Role.table + '.slug', slugs);
24
+ }
25
+ if (ids.length) {
26
+ rolesQuery.whereIn(Role.table + '.id', ids);
27
+ }
28
+ const r = await rolesQuery.count('* as total');
29
+ // @ts-ignore
30
+ return r[0].total === roles.length;
31
+ }
32
+ async hasAny(modelType, modelId, roles) {
33
+ // if is string then we are going to check against slug
34
+ // map roles
35
+ const rolesQuery = this.modelRolesQuery(modelType, modelId);
36
+ let { slugs, ids } = this.formatList(roles);
37
+ if (slugs.length) {
38
+ rolesQuery.whereIn(Role.table + '.slug', slugs);
39
+ }
40
+ if (ids.length) {
41
+ rolesQuery.whereIn(Role.table + '.id', ids);
42
+ }
43
+ const r = await rolesQuery.count('* as total');
44
+ // @ts-ignore
45
+ return r[0].total > 0;
46
+ }
47
+ async assign(role, modelType, modelId) {
48
+ const r = await this.extractRoleModel(role);
49
+ if (!r) {
50
+ throw new Error('Role not found');
51
+ }
52
+ await ModelRole.create({
53
+ modelType,
54
+ modelId,
55
+ roleId: r.id,
56
+ });
57
+ return true;
58
+ }
59
+ async revoke(role, model) {
60
+ return this.revokeAll([role], model);
61
+ }
62
+ async revokeAll(roles, model) {
63
+ const map = await morphMap();
64
+ const { slugs, ids } = this.formatListStringNumbers(roles);
65
+ await ModelRole.query()
66
+ .leftJoin(Role.table + ' as r', 'r.id', '=', ModelRole.table + '.role_id')
67
+ .where('model_type', map.getAlias(model))
68
+ .where('model_id', model.getModelId())
69
+ .where((query) => {
70
+ query.whereIn('r.id', ids).orWhereIn('r.slug', slugs);
71
+ })
72
+ .delete();
73
+ return true;
74
+ }
75
+ async extractRoleModel(role) {
76
+ if (typeof role === 'string') {
77
+ return await Role.query().where('slug', role).first();
78
+ }
79
+ return role;
80
+ }
81
+ roleModelPermissionQuery(modelType) {
82
+ return Role.query()
83
+ .leftJoin(ModelPermission.table + ' as mp', 'mp.model_id', '=', Role.table + '.id')
84
+ .where('mp.model_type', modelType);
85
+ }
86
+ async flush(modelType, modelId) {
87
+ ModelRole.query().where('model_type', modelType).where('model_id', modelId).delete();
88
+ }
89
+ }
@@ -0,0 +1,21 @@
1
+ import { LucidModel } from '@adonisjs/lucid/types/model';
2
+ export interface Permissions {
3
+ tables: Object;
4
+ }
5
+ export interface AclModelInterface {
6
+ getModelId(): number;
7
+ }
8
+ export type AclModel = LucidModel & AclModelInterface;
9
+ export interface AclModelQuery {
10
+ modelType: string;
11
+ modelId: number;
12
+ }
13
+ export interface ModelPermissionsQuery extends AclModelQuery {
14
+ permissionSlugs: string[];
15
+ permissionIds: number[];
16
+ directPermissions: boolean;
17
+ includeForbiddens: boolean;
18
+ }
19
+ export interface MorphMapInterface {
20
+ [key: string]: any;
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ {{{
2
+ exports({
3
+ to: app.configPath('permissions.ts')
4
+ })
5
+ }}}
6
+
7
+ import { Permissions } from '@holoyan/adonisjs-permissions/types'
8
+
9
+ export const permissionsConfig: Permissions = {
10
+ tables: {
11
+ roles: 'roles',
12
+ modelRoles: 'model_roles',
13
+ permissions: 'permissions',
14
+ modelPermissions: 'model_permissions',
15
+ },
16
+ morphMaps: {
17
+ roles: 'roles',
18
+ permissions: 'permissions',
19
+ }
20
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Path to the root directory where the stubs are stored. We use
3
+ * this path within commands and the configure hook
4
+ */
5
+ export declare const stubsRoot: string;
@@ -0,0 +1,7 @@
1
+ import { dirname } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ /**
4
+ * Path to the root directory where the stubs are stored. We use
5
+ * this path within commands and the configure hook
6
+ */
7
+ export const stubsRoot = dirname(fileURLToPath(import.meta.url));
@@ -0,0 +1,97 @@
1
+ {{{
2
+ exports({
3
+ to: app.makePath('database', 'migrations', prefix + '_create_role_permissions_table.ts')
4
+ })
5
+ }}}
6
+
7
+ import { BaseSchema } from '@adonisjs/lucid/schema'
8
+ import config from "@adonisjs/core/services/config";
9
+
10
+ export default class extends BaseSchema {
11
+
12
+ async up() {
13
+ this.schema.createTable(config.get('permissions.permissionsConfig.tables.roles'), (table) => {
14
+ table.increments('id')
15
+
16
+ table.string('slug').index()
17
+ table.string('title')
18
+ table.string('entity_type')
19
+ table.bigint('entity_id').unsigned()
20
+ table.integer('scope').unsigned()
21
+ table.boolean('allowed').defaultTo(true)
22
+
23
+ /**
24
+ * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
25
+ */
26
+ table.timestamp('created_at', { useTz: true })
27
+ table.timestamp('updated_at', { useTz: true })
28
+
29
+ table.unique(['slug', 'scope'])
30
+ table.index(['entity_type', 'entity_id'])
31
+ })
32
+
33
+ this.schema.createTable(config.get('permissions.permissionsConfig.tables.modelRoles'), (table) => {
34
+ table.increments('id')
35
+
36
+ table.string('model_type')
37
+ table.bigint('model_id').unsigned()
38
+ table.bigInteger('role_id').unsigned()
39
+
40
+ /**
41
+ * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
42
+ */
43
+ table.timestamp('created_at', { useTz: true })
44
+ table.timestamp('updated_at', { useTz: true })
45
+
46
+ table.index(['model_type', 'model_id'])
47
+
48
+ table.foreign('role_id').references('roles.id').onDelete('CASCADE')
49
+ })
50
+
51
+ this.schema.createTable(config.get('permissions.permissionsConfig.tables.permissions'), (table) => {
52
+ table.increments('id')
53
+
54
+ table.string('slug').index()
55
+ table.string('title')
56
+ table.string('entity_type')
57
+ table.bigint('entity_id').unsigned()
58
+ table.integer('scope').unsigned()
59
+ table.boolean('allowed').defaultTo(true)
60
+
61
+ /**
62
+ * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
63
+ */
64
+ table.timestamp('created_at', { useTz: true })
65
+ table.timestamp('updated_at', { useTz: true })
66
+
67
+ table.unique(['slug', 'scope'])
68
+ table.index(['entity_type', 'entity_id'])
69
+ })
70
+
71
+ this.schema.createTable(config.get('permissions.permissionsConfig.tables.modelPermissions'), (table) => {
72
+ table.increments('id')
73
+
74
+ table.string('model_type')
75
+ table.bigint('model_id').unsigned()
76
+ table.bigInteger('permission_id').unsigned()
77
+
78
+ /**
79
+ * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
80
+ */
81
+ table.timestamp('created_at', { useTz: true })
82
+ table.timestamp('updated_at', { useTz: true })
83
+
84
+ table.index(['model_type', 'model_id'])
85
+
86
+ table.foreign('permission_id').references('permissions.id').onDelete('CASCADE')
87
+ })
88
+
89
+ }
90
+
91
+ async down() {
92
+ this.schema.dropTable(config.get('permissions.permissionsConfig.tables.modelRoles'))
93
+ this.schema.dropTable(config.get('permissions.permissionsConfig.tables.roles'))
94
+ this.schema.dropTable(config.get('permissions.permissionsConfig.tables.modelPermissions'))
95
+ this.schema.dropTable(config.get('permissions.permissionsConfig.tables.permissions'))
96
+ }
97
+ }
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@holoyan/adonisjs-permissions",
3
+ "description": "Adonisjs roles and permissions system",
4
+ "version": "0.1.0",
5
+ "engines": {
6
+ "node": ">=18.16.0"
7
+ },
8
+ "type": "module",
9
+ "files": [
10
+ "build/src",
11
+ "build/providers",
12
+ "build/stubs",
13
+ "build/index.d.ts",
14
+ "build/index.js",
15
+ "build/configure.d.ts",
16
+ "build/configure.js"
17
+ ],
18
+ "exports": {
19
+ ".": "./build/index.js",
20
+ "./role_permission_provider": "./build/providers/role_permission_provider.js",
21
+ "./types": "./build/src/types.js"
22
+ },
23
+ "scripts": {
24
+ "clean": "del-cli build",
25
+ "copy:templates": "copyfiles \"stubs/**/*.stub\" build",
26
+ "typecheck": "tsc --noEmit",
27
+ "lint": "eslint . --ext=.ts",
28
+ "format": "prettier --write .",
29
+ "quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
30
+ "pretest": "npm run lint",
31
+ "test": "c8 npm run quick:test",
32
+ "prebuild": "npm run lint && npm run clean",
33
+ "build": "tsc",
34
+ "postbuild": "npm run copy:templates",
35
+ "release": "np",
36
+ "version": "npm run build",
37
+ "prepublishOnly": "npm run build"
38
+ },
39
+ "keywords": [],
40
+ "author": "holoyan",
41
+ "license": "MIT",
42
+ "devDependencies": {
43
+ "@adonisjs/assembler": "^7.0.0",
44
+ "@adonisjs/core": "^6.2.0",
45
+ "@adonisjs/eslint-config": "^1.2.1",
46
+ "@adonisjs/lucid": "^20.1.0",
47
+ "@adonisjs/prettier-config": "^1.2.1",
48
+ "@adonisjs/tsconfig": "^1.2.1",
49
+ "@japa/assert": "^2.1.0",
50
+ "@japa/runner": "^3.1.1",
51
+ "@swc/core": "^1.3.102",
52
+ "@types/chance": "^1.1.6",
53
+ "@types/luxon": "^3.4.2",
54
+ "@types/node": "^20.10.7",
55
+ "c8": "^9.0.0",
56
+ "chance": "^1.1.11",
57
+ "copyfiles": "^2.4.1",
58
+ "del-cli": "^5.0.0",
59
+ "eslint": "^8.38.0",
60
+ "np": "^9.2.0",
61
+ "pg": "^8.11.3",
62
+ "prettier": "^3.1.1",
63
+ "sqlite3": "^5.1.7",
64
+ "ts-node": "^10.9.2",
65
+ "typescript": "^5.3.3"
66
+ },
67
+ "peerDependencies": {
68
+ "@adonisjs/core": "^6.2.0",
69
+ "@adonisjs/lucid": "^20.1.0",
70
+ "luxon": "^3.4.4"
71
+ },
72
+ "publishConfig": {
73
+ "access": "public",
74
+ "tag": "latest"
75
+ },
76
+ "np": {
77
+ "message": "chore(release): %s",
78
+ "tag": "latest",
79
+ "branch": "main",
80
+ "anyBranch": false
81
+ },
82
+ "c8": {
83
+ "reporter": [
84
+ "text",
85
+ "html"
86
+ ],
87
+ "exclude": [
88
+ "tests/**"
89
+ ]
90
+ },
91
+ "eslintConfig": {
92
+ "extends": "@adonisjs/eslint-config/package"
93
+ },
94
+ "prettier": "@adonisjs/prettier-config"
95
+ }