-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
522 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers' | ||
import PermissionsService from '../services/permissions_service.js' | ||
import { HasPermissions as HasPermissionsContract } from '../../types/has_roles.js' | ||
import Permission from '../models/permission.js' | ||
|
||
const HasPermissions = < | ||
Model extends NormalizeConstructor<import('@adonisjs/lucid/types/model').LucidModel>, | ||
>( | ||
superclass: Model | ||
) => { | ||
class HasPermissionsMixin extends superclass implements HasPermissionsContract { | ||
getMorphMapName(): string { | ||
throw new Error( | ||
'method getMorphMapName must be implemented in target model, which will return string alias for model class' | ||
) | ||
} | ||
|
||
getModelId(): number { | ||
throw new Error( | ||
'method getModelId must be implemented in target model, which will return key for current object' | ||
) | ||
} | ||
|
||
/** | ||
* return all permissions including global, direct | ||
*/ | ||
permissions(): Promise<Permission[] | null> { | ||
const service = new PermissionsService() | ||
return service.all(this.getMorphMapName(), this.getModelId()) | ||
} | ||
} | ||
|
||
return HasPermissionsMixin | ||
} | ||
|
||
export default HasPermissions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import Role from '../models/role.js' | ||
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers' | ||
import RolesService from '../services/roles_service.js' | ||
import { HasRoles as HasRolesContract } from '../../types/has_roles.js' | ||
|
||
const HasRoles = < | ||
Model extends NormalizeConstructor<import('@adonisjs/lucid/types/model').LucidModel>, | ||
>( | ||
superclass: Model | ||
) => { | ||
class HasRolesMixin extends superclass implements HasRolesContract { | ||
getMorphMapName(): string { | ||
throw new Error( | ||
'method getMorphMapName must be implemented in target model, which will return string alias for model class' | ||
) | ||
} | ||
|
||
getModelId(): number | null { | ||
throw new Error( | ||
'method getModelId must be implemented in target model, which will return key for current object' | ||
) | ||
} | ||
|
||
roles(): Promise<Role[] | null> { | ||
const service = new RolesService() | ||
return service.all(this.getMorphMapName(), this.getModelId()) | ||
} | ||
|
||
hasRole(role: string | Role) { | ||
const service = new RolesService() | ||
return service.has(this.getMorphMapName(), this.getModelId(), role) | ||
} | ||
|
||
hasAllRoles(role: (string | Role)[]) { | ||
const service = new RolesService() | ||
return service.hasAll(this.getMorphMapName(), this.getModelId(), role) | ||
} | ||
|
||
hasAnyRole(role: (string | Role)[]) { | ||
const service = new RolesService() | ||
return service.hasAny(this.getMorphMapName(), this.getModelId(), role) | ||
} | ||
|
||
assigneRole(role: string | Role) { | ||
const service = new RolesService() | ||
return service.assigne(role, this) | ||
} | ||
|
||
revokeRole(role: string | Role) { | ||
const service = new RolesService() | ||
return service.revoke(role, this) | ||
} | ||
} | ||
|
||
return HasRolesMixin | ||
} | ||
|
||
export default HasRoles |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { DateTime } from 'luxon' | ||
import { BaseModel, column, scope } from '@adonisjs/lucid/orm' | ||
|
||
export default class ModelPermission extends BaseModel { | ||
@column({ isPrimary: true }) | ||
declare id: number | ||
|
||
@column() | ||
declare permissionId: number | ||
|
||
@column() | ||
declare modelType: string | ||
|
||
@column() | ||
declare modelId: number | ||
|
||
@column.dateTime({ autoCreate: true }) | ||
declare createdAt: DateTime | ||
|
||
@column.dateTime({ autoCreate: true, autoUpdate: true }) | ||
declare updatedAt: DateTime | ||
|
||
static forModel = scope((query, modelType: string, modelId: number | null) => { | ||
query.where('model_type', modelType) | ||
modelId === null ? query.whereNull('model_id') : query.where('model_id', modelId) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { DateTime } from 'luxon' | ||
import { BaseModel, column } from '@adonisjs/lucid/orm' | ||
|
||
export default class ModelRole extends BaseModel { | ||
@column({ isPrimary: true }) | ||
declare id: number | ||
|
||
@column() | ||
declare roleId: number | ||
|
||
@column() | ||
declare modelType: string | ||
|
||
@column() | ||
declare modelId: number | null | ||
|
||
@column.dateTime({ autoCreate: true }) | ||
declare createdAt: DateTime | ||
|
||
@column.dateTime({ autoCreate: true, autoUpdate: true }) | ||
declare updatedAt: DateTime | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { DateTime } from 'luxon' | ||
import { BaseModel, column } from '@adonisjs/lucid/orm' | ||
|
||
export default class Permission extends BaseModel { | ||
@column({ isPrimary: true }) | ||
declare id: number | ||
|
||
@column() | ||
declare slug: string | ||
|
||
@column() | ||
declare title: string | ||
|
||
@column() | ||
declare entityType: string | ||
|
||
@column() | ||
declare entityId: number | null | ||
|
||
@column() | ||
declare allowed: boolean | ||
|
||
@column() | ||
declare scope: number | ||
|
||
@column.dateTime({ autoCreate: true }) | ||
declare createdAt: DateTime | ||
|
||
@column.dateTime({ autoCreate: true, autoUpdate: true }) | ||
declare updatedAt: DateTime | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { DateTime } from 'luxon' | ||
import { BaseModel, column } from '@adonisjs/lucid/orm' | ||
|
||
export default class Role extends BaseModel { | ||
@column({ isPrimary: true }) | ||
declare id: number | ||
|
||
@column() | ||
declare slug: string | ||
|
||
@column() | ||
declare title: string | ||
|
||
@column() | ||
declare entityType: string | ||
|
||
@column() | ||
declare entityId: number | null | ||
|
||
@column() | ||
declare scope: number | ||
|
||
@column() | ||
declare allowed: boolean | ||
|
||
@column.dateTime({ autoCreate: true }) | ||
declare createdAt: DateTime | ||
|
||
@column.dateTime({ autoCreate: true, autoUpdate: true }) | ||
declare updatedAt: DateTime | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import Permission from '../models/permission.js' | ||
|
||
export default class PermissionsService { | ||
/** | ||
* return all permissions | ||
*/ | ||
async all(modelType: string, modelId: number) { | ||
const p = await this.permissionQuery(modelType, modelId) | ||
.groupBy('permissions.id') | ||
.select('permissions.*') | ||
|
||
return p | ||
} | ||
|
||
/** | ||
* return only global assigned permissions, through role or direct | ||
*/ | ||
async global(modelType: string, modelId: number) { | ||
const p = await this.permissionQuery(modelType, modelId) | ||
.where('permissions.entity_type', '*') | ||
.whereNull('permissions.entity_id') | ||
.groupBy('permissions.id') | ||
.select('permissions.*') | ||
|
||
return p | ||
} | ||
|
||
/** | ||
* get all permissions which is assigned to concrete resource | ||
*/ | ||
async onResource(modelType: string, modelId: number) { | ||
const p = await this.permissionQuery(modelType, modelId) | ||
.where('permissions.entity_type', '*') | ||
.whereNotNull('permissions.entity_id') | ||
.groupBy('permissions.id') | ||
.select('permissions.*') | ||
|
||
return p | ||
} | ||
|
||
/** | ||
* all direct permissions | ||
*/ | ||
async direct(modelType: string, modelId: number) { | ||
const p = await this.directPermissionQuery(modelType, modelId) | ||
.groupBy('permissions.id') | ||
.select('permissions.*') | ||
|
||
return p | ||
} | ||
|
||
/** | ||
* return direct and resource assigned permissions | ||
*/ | ||
directResource() {} | ||
|
||
/** | ||
* check if it has permission | ||
*/ | ||
has() {} | ||
|
||
/** | ||
* has all permissions | ||
*/ | ||
hasAll() {} | ||
|
||
/** | ||
* has any of permissions | ||
*/ | ||
hasAny() {} | ||
|
||
/** | ||
* give permission to model | ||
*/ | ||
give() {} | ||
|
||
/** | ||
* give permissions to model | ||
*/ | ||
giveAll() {} | ||
|
||
/** | ||
* sync permissions, remove everything outside of the list | ||
*/ | ||
sync() {} | ||
|
||
/** | ||
* forbid permission on model | ||
*/ | ||
forbid() {} | ||
|
||
/** | ||
* to remove forbidden permission on model | ||
*/ | ||
unforbid() {} | ||
|
||
/** | ||
* check if permission is forbidden | ||
*/ | ||
forbidden() {} | ||
|
||
private permissionQuery(modelType: string, modelId: number) { | ||
const q = Permission.query() | ||
.join('model_permissions as mp', 'mp.permission_id', '=', 'permissions.id') | ||
.join('model_roles as mr', (joinQuery) => { | ||
joinQuery.onVal('mr.model_type', modelType).onVal('mr.model_id', modelId) | ||
}) | ||
.where((subQuery) => { | ||
subQuery | ||
.where((query) => { | ||
query.where('mp.model_type', modelType) | ||
modelId === null ? query.whereNull('mp.model_id') : query.where('mp.model_id', modelId) | ||
}) | ||
.orWhere((query) => { | ||
query.whereRaw('mr.role_id=mp.model_id').where('mp.model_type', 'roles') | ||
}) | ||
}) | ||
|
||
return q | ||
} | ||
private directPermissionQuery(modelType: string, modelId: number) { | ||
const q = Permission.query() | ||
.join('model_permissions as mp', 'mp.permission_id', '=', 'permissions.id') | ||
.where('mp.model_type', modelType) | ||
.where('mp.model_id', modelId) | ||
|
||
return q | ||
} | ||
} |
Oops, something went wrong.