@holoyan/adonisjs-permissions 1.3.0 → 1.3.1

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.
package/README.md CHANGED
@@ -14,8 +14,8 @@ Checkout other AdonisJS packages
14
14
 
15
15
  ## Release Notes
16
16
 
17
- Version: >= v1.3.0
18
- * Added [query helpers](#permissionqueryhelpers-mixin) mixin. To make it easier to query users(models) based on their roles and permissions
17
+ Version: >= v1.3.1
18
+ * Added [canPartially](#checking-partial-permissions) method
19
19
 
20
20
  ## Table of Contents
21
21
 
@@ -504,6 +504,21 @@ await Acl.role(role).hasAnyPermission(['update', 'read'])
504
504
 
505
505
  ```
506
506
 
507
+ ### Checking partial permissions
508
+
509
+ Sometimes you might want to check if a user has a specific permission on any instance of a model class, rather than checking for global permissions or permissions on a specific instance. For this purpose, you can use the `canPartially` method:
510
+
511
+ ```typescript
512
+
513
+ const postWithId = await Post.find(id)
514
+ await Acl.model(user).allow('create', postWithId) // allow 'create' on post instance
515
+
516
+ // This is useful when you want to know if a user has permission on at least one instance
517
+ Acl.model(user).can('create') // will return false
518
+ Acl.model(user).can('create', Post) // will return false
519
+ Acl.model(user).canPartially('create', Post) // will return true because user has a 'create' permission on postWithId instance
520
+ ```
521
+
507
522
  ### Middleware
508
523
 
509
524
  You are free to do your check anywhere, for example we can create [named](https://docs.adonisjs.com/guides/middleware#named-middleware-collection) middleware and do checking
@@ -142,6 +142,12 @@ export declare function hasPermissions(): <Model extends NormalizeConstructor<ty
142
142
  */
143
143
  canAll(permissions: string[], target?: AclModel | Function): Promise<boolean>;
144
144
  canAny(permissions: string[], target?: AclModel | Function): Promise<boolean>;
145
+ /**
146
+ * Check if a model has permission on any instance of a model class
147
+ * @param permission
148
+ * @param targetClass
149
+ */
150
+ canPartially(permission: string, targetClass: Function): Promise<boolean>;
145
151
  /**
146
152
  * Check if model has any permission
147
153
  * @param permission
@@ -204,6 +204,14 @@ export function hasPermissions() {
204
204
  canAny(permissions, target) {
205
205
  return Acl.model(this).canAny(permissions, target);
206
206
  }
207
+ /**
208
+ * Check if a model has permission on any instance of a model class
209
+ * @param permission
210
+ * @param targetClass
211
+ */
212
+ canPartially(permission, targetClass) {
213
+ return Acl.model(this).canPartially(permission, targetClass);
214
+ }
207
215
  /**
208
216
  * Check if model has any permission
209
217
  * @param permission
@@ -22,3 +22,8 @@ export declare function destructTarget(map: MorphInterface, target?: AclModel |
22
22
  targetId: ModelIdType | null;
23
23
  };
24
24
  export declare function applyTargetRestriction(table: string, q: ManyToManySubQueryBuilderContract<typeof Permission> | ModelQueryBuilderContract<typeof Permission, PermissionInterface> | RelationSubQueryBuilderContract<typeof ModelRole>, entityType: string | null, entityId: ModelIdType | null): void;
25
+ /**
26
+ * Apply target restriction for partial permission checks
27
+ * This function doesn't check entity_id when only entityType is provided
28
+ */
29
+ export declare function applyPartialTargetRestriction(table: string, q: ManyToManySubQueryBuilderContract<typeof Permission> | ModelQueryBuilderContract<typeof Permission, PermissionInterface> | RelationSubQueryBuilderContract<typeof ModelRole>, entityType: string): void;
@@ -80,3 +80,10 @@ export function applyTargetRestriction(table, q, entityType, entityId) {
80
80
  q.where(table + '.entity_type', '*').whereNull(table + '.entity_id');
81
81
  }
82
82
  }
83
+ /**
84
+ * Apply target restriction for partial permission checks
85
+ * This function doesn't check entity_id when only entityType is provided
86
+ */
87
+ export function applyPartialTargetRestriction(table, q, entityType) {
88
+ q.where(table + '.entity_type', entityType).whereNotNull(table + '.entity_id');
89
+ }
@@ -102,6 +102,24 @@ export declare class ModelHasRolePermissions extends BaseAdapter {
102
102
  can(permission: string, target?: AclModel | Function): Promise<boolean>;
103
103
  canAll(permissions: string[], target?: AclModel | Function): Promise<boolean>;
104
104
  canAny(permissions: string[], target?: AclModel | Function): Promise<boolean>;
105
+ /**
106
+ * Check if model has permission on any instance of a model class
107
+ * @param permission
108
+ * @param targetClass
109
+ */
110
+ hasPartialPermission(permission: string, targetClass: Function): Promise<boolean>;
111
+ /**
112
+ * Check if model has any of the permissions on any instance of a model class
113
+ * @param permissions
114
+ * @param targetClass
115
+ */
116
+ hasAnyPartialPermission(permissions: string[], targetClass: Function): Promise<boolean>;
117
+ /**
118
+ * Check if a model has permission on any instance of a model class
119
+ * @param permission
120
+ * @param targetClass
121
+ */
122
+ canPartially(permission: string, targetClass: Function): Promise<boolean>;
105
123
  /**
106
124
  * calls assignDirectAllPermissions()
107
125
  * @param permission
@@ -213,6 +213,31 @@ export class ModelHasRolePermissions extends BaseAdapter {
213
213
  canAny(permissions, target) {
214
214
  return this.hasAnyPermission(permissions, target);
215
215
  }
216
+ /**
217
+ * Check if model has permission on any instance of a model class
218
+ * @param permission
219
+ * @param targetClass
220
+ */
221
+ async hasPartialPermission(permission, targetClass) {
222
+ return this.hasAnyPartialPermission([permission], targetClass);
223
+ }
224
+ /**
225
+ * Check if model has any of the permissions on any instance of a model class
226
+ * @param permissions
227
+ * @param targetClass
228
+ */
229
+ async hasAnyPartialPermission(permissions, targetClass) {
230
+ const entityType = this.map.getAlias(targetClass);
231
+ return await this.permissionService.hasAnyPartial(this.map.getAlias(this.model), this.model.getModelId(), permissions, entityType);
232
+ }
233
+ /**
234
+ * Check if a model has permission on any instance of a model class
235
+ * @param permission
236
+ * @param targetClass
237
+ */
238
+ canPartially(permission, targetClass) {
239
+ return this.hasPartialPermission(permission, targetClass);
240
+ }
216
241
  /**
217
242
  * calls assignDirectAllPermissions()
218
243
  * @param permission
@@ -57,6 +57,10 @@ export default class PermissionsService extends BaseService {
57
57
  * has any of permissions
58
58
  */
59
59
  hasAny(modelType: string, modelId: ModelIdType, permission: string[], entityType: string | null, entityId: ModelIdType | null): Promise<boolean>;
60
+ /**
61
+ * has any of permissions on any instance of a model class
62
+ */
63
+ hasAnyPartial(modelType: string, modelId: ModelIdType, permissions: string[], entityType: string): Promise<boolean>;
60
64
  /**
61
65
  * has all permissions
62
66
  */
@@ -109,6 +113,7 @@ export default class PermissionsService extends BaseService {
109
113
  reverseModelPermissionQuery(conditions: Partial<ModelPermissionsQuery>): ModelQueryBuilderContract<typeof import("../../models/model_permission.js").default, import("../../models/model_permission.js").default>;
110
114
  findAssignableEntity(permission: string[], entityClass: string | null, entityId: ModelIdType | null, allowed: boolean): ModelQueryBuilderContract<typeof Permission, Permission>;
111
115
  private applyTargetRestriction;
116
+ private applyPartialTargetRestriction;
112
117
  private applyScopes;
113
118
  private applyModelPermissionScopes;
114
119
  }
@@ -1,5 +1,5 @@
1
1
  import BaseService from '../base_service.js';
2
- import { applyTargetRestriction } from '../helper.js';
2
+ import { applyTargetRestriction, applyPartialTargetRestriction } from '../helper.js';
3
3
  export default class PermissionsService extends BaseService {
4
4
  options;
5
5
  scope;
@@ -204,6 +204,27 @@ export default class PermissionsService extends BaseService {
204
204
  const r = await q.distinct(this.permissionTable + '.id').select(this.permissionTable + '.id');
205
205
  return r.length > 0;
206
206
  }
207
+ /**
208
+ * has any of permissions on any instance of a model class
209
+ */
210
+ async hasAnyPartial(modelType, modelId, permissions, entityType) {
211
+ const { slugs, ids } = this.formatList(permissions);
212
+ const q = this.modelPermissionQueryWithForbiddenCheck({
213
+ modelType,
214
+ modelId,
215
+ directPermissions: this.map.getAlias(this.roleClassName) === modelType,
216
+ permissionSlugs: slugs,
217
+ permissionIds: ids,
218
+ entity: {
219
+ type: entityType,
220
+ id: null,
221
+ },
222
+ });
223
+ // We use applyPartialTargetRestriction which doesn't check entity_id when only entityType is provided
224
+ this.applyPartialTargetRestriction(this.permissionTable, q, entityType);
225
+ const r = await q.distinct(this.permissionTable + '.id').select(this.permissionTable + '.id');
226
+ return r.length > 0;
227
+ }
207
228
  /**
208
229
  * has all permissions
209
230
  */
@@ -496,6 +517,9 @@ export default class PermissionsService extends BaseService {
496
517
  applyTargetRestriction(table, q, entityType, entityId) {
497
518
  applyTargetRestriction(table, q, entityType, entityId);
498
519
  }
520
+ applyPartialTargetRestriction(table, q, entityType) {
521
+ applyPartialTargetRestriction(table, q, entityType);
522
+ }
499
523
  applyScopes(q) {
500
524
  q.where(this.permissionTable + '.scope', this.scope.get());
501
525
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@holoyan/adonisjs-permissions",
3
3
  "description": "AdonisJs roles and permissions system",
4
- "version": "1.3.0",
4
+ "version": "1.3.1",
5
5
  "engines": {
6
6
  "node": ">=18.16.0"
7
7
  },