@lcsf/acl
Version:
权限控制模块
456 lines (446 loc) • 13.7 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Directive, ElementRef, Renderer2, Input, TemplateRef, ViewContainerRef, NgModule } from '@angular/core';
import { filter } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs';
import { CommonModule } from '@angular/common';
import * as i2 from '@angular/router';
import { Router } from '@angular/router';
/**
*
* 务必在根目录注册 `LcAclModule.forRoot()` 才能使用服务
*/
class LcAClService {
constructor() {
this.roles = [];
this.types = [];
this.permissionGroups = [];
this.authPaths = new Map();
this.full = false;
this.aclChange = new BehaviorSubject(null);
}
/** ACL变更通知 */
get change() {
return this.aclChange.asObservable();
}
/** 获取所有数据 */
get data() {
return {
full: this.full,
roles: this.roles,
permissionGroups: this.permissionGroups,
authPaths: this.authPaths,
};
}
parseLcACLType(val) {
let t;
if (typeof val === 'string') {
if (val.indexOf('::') > -1) {
// permissionGroup
t = { permissions: [val] };
}
else {
// role
t = { role: [val] };
}
}
else if (typeof val === 'object' && !Array.isArray(val)) {
t = Object.assign({}, val);
}
else if (Array.isArray(val)) {
if (val[0].toString().indexOf('::') > -1) {
t = { permissions: val };
}
else {
t = { role: val };
}
}
else {
t = { role: val == null ? [] : [val] };
}
return Object.assign({ except: false }, t);
}
/**
* 设置当前用户角色或权限能力(会先清除所有)
*/
set(value) {
this.full = false;
this.roles = [];
this.types = [];
this.permissionGroups = [];
this.authPaths = new Map();
this.add(value);
this.aclChange.next(value);
}
/**
* 标识当前用户为全量,即不受限
*/
setFull(val) {
this.full = val;
this.aclChange.next(val);
}
/**
* 设置当前用户角色(会先清除所有)
*/
setRole(roles) {
this.set({ role: roles });
}
/**
* 为当前用户增加角色或权限能力
*/
add(value) {
if (value.role && value.role.length > 0) {
this.roles.push(...value.role);
}
if (value.type && value.type.length > 0) {
this.types.push(...value.type);
}
if (value.permissionGroups && value.permissionGroups.length > 0) {
this.permissionGroups.push(...value.permissionGroups);
}
if (value.menus && value.menus.length > 0) {
this.authPaths = this.getAuthPaths(value.menus);
}
}
/**
* 为当前用户附加角色
*/
attachRole(roles) {
for (const val of roles) {
if (!this.roles.includes(val)) {
this.roles.push(val);
}
}
this.aclChange.next(this.data);
}
/**
* 为当前用户移除角色
*/
removeRole(roles) {
for (const val of roles) {
const idx = this.roles.indexOf(val);
if (idx !== -1) {
this.roles.splice(idx, 1);
}
}
this.aclChange.next(this.data);
}
/**
* 当前用户是否有对应角色,其实 `number` 表示Ability
*
* - 当 `full: true` 或参数 `null` 时返回 `true`
* - 若使用 `LcACLType` 参数,可以指定 `mode` 校验模式
*/
can(roleOrAbility) {
// debugger
const t = this.parseLcACLType(roleOrAbility);
let result = false;
if (this.full === true || !roleOrAbility) {
result = true;
}
else {
if (t.role && t.role.length > 0) {
if (typeof t.role === 'string') {
t.role = [t.role];
}
if (t.mode === 'allOf') {
result = t.role.every(v => {
if (v.toString().indexOf('-') > -1) {
let arr = v.split('-');
return this.roles.includes(arr[0]) && this.types.includes(arr[1]);
}
return this.roles.includes(v);
});
}
else {
result = t.role.some(v => {
if (v.toString().indexOf('-') > -1) {
let arr = v.split('-');
return this.roles.includes(arr[0]) && this.types.includes(arr[1]);
}
return this.roles.includes(v);
});
}
}
if (t.permissions && t.permissions.length > 0) {
if (typeof t.permissions === 'string') {
t.permissions = [t.permissions];
}
if (t.mode === 'allOf') {
result = t.permissions.every(v => this.permissionGroups.includes(v));
}
else {
result = t.permissions.some(v => this.permissionGroups.includes(v));
}
}
// 用户自定义额外字段
if (typeof t.extraAll !== 'undefined') {
result = result && t.extraAll;
}
if (typeof t.extraOne !== 'undefined') {
result = result || t.extraOne;
}
}
return t.except === true ? !result : result;
}
/**
* 路由权限拦截
* @param url
* @returns
*/
canAuthUrl(url) {
return this.authPaths.has(url);
}
/**
* 获取当前url对应是哪一个model_id
* @param url
* @returns
*/
getUrlModeId(url) {
return this.authPaths.get(url);
}
getAuthPaths(value) {
let reuslt = new Map();
value.forEach(authModel => {
let model_id = authModel.model_id;
reuslt.set(authModel.url, model_id);
let stack = [...authModel.menu_list];
while (stack.length !== 0) {
const item = stack.pop();
if (item) {
reuslt.set(item.url, model_id);
if (item.children) {
for (let i = item.children.length - 1; i >= 0; i--) {
stack.push(Object.assign({}, item.children[i]));
}
}
}
}
});
return reuslt;
}
}
LcAClService.decorators = [
{ type: Injectable }
];
LcAClService.ctorParameters = () => [];
class LcAClDirective {
constructor(el, renderer, srv) {
this.el = el;
this.renderer = renderer;
this.srv = srv;
this.change$ = this.srv.change.pipe(filter(r => r != null)).subscribe(() => this.set(this._value));
}
set lcAcl(value) {
this.set(value);
}
set(value) {
this._value = value;
const CLS = 'lcAcl__hide';
const el = this.el.nativeElement;
if (this.srv.can(this._value)) {
this.renderer.removeClass(el, CLS);
}
else {
this.renderer.addClass(el, CLS);
}
}
ngOnDestroy() {
this.change$.unsubscribe();
}
}
LcAClDirective.decorators = [
{ type: Directive, args: [{
selector: '[lcAcl]',
exportAs: 'lcAcl'
},] }
];
LcAClDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: LcAClService }
];
LcAClDirective.propDecorators = {
lcAcl: [{ type: Input, args: ['lcAcl',] }]
};
class LcACLIfDirective {
constructor(templateRef, srv, _viewContainer) {
this.srv = srv;
this._viewContainer = _viewContainer;
this._thenTemplateRef = null;
this._elseTemplateRef = null;
this._thenViewRef = null;
this._elseViewRef = null;
this._except = false;
this._change$ = this.srv.change.pipe(filter(r => r != null)).subscribe(() => this._updateView());
this._thenTemplateRef = templateRef;
}
set lcAclIf(value) {
this._value = value;
this._updateView();
}
set lcAclIfThen(templateRef) {
this._thenTemplateRef = templateRef;
this._thenViewRef = null;
this._updateView();
}
set lcAclIfElse(templateRef) {
this._elseTemplateRef = templateRef;
this._elseViewRef = null;
this._updateView();
}
set except(value) {
this._except = value != null && `${value}` !== 'false';
}
get except() {
return this._except;
}
_updateView() {
const res = this.srv.can(this._value);
if ((res && !this.except) || (!res && this.except)) {
if (!this._thenViewRef) {
this._viewContainer.clear();
this._elseViewRef = null;
if (this._thenTemplateRef) {
this._thenViewRef = this._viewContainer.createEmbeddedView(this._thenTemplateRef);
}
}
}
else {
if (!this._elseViewRef) {
this._viewContainer.clear();
this._thenViewRef = null;
if (this._elseTemplateRef) {
this._elseViewRef = this._viewContainer.createEmbeddedView(this._elseTemplateRef);
}
}
}
}
ngOnDestroy() {
this._change$.unsubscribe();
}
}
LcACLIfDirective.decorators = [
{ type: Directive, args: [{
selector: '[lcAclIf]',
exportAs: 'lcAclIf'
},] }
];
LcACLIfDirective.ctorParameters = () => [
{ type: TemplateRef },
{ type: LcAClService },
{ type: ViewContainerRef }
];
LcACLIfDirective.propDecorators = {
lcAclIf: [{ type: Input }],
lcAclIfThen: [{ type: Input }],
lcAclIfElse: [{ type: Input }],
except: [{ type: Input }]
};
const COMPONENTS = [LcAClDirective, LcACLIfDirective];
class LcAclModule {
// constructor(@Optional() @SkipSelf() parentModule?: LcAclModule) {
// if (parentModule) {
// throw new Error('LcAclModule is already loaded. Import it in the AppModule only');
// }
// }
static forRoot() {
return {
ngModule: LcAclModule,
providers: [LcAClService],
};
}
}
LcAclModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule],
declarations: COMPONENTS,
exports: COMPONENTS,
},] }
];
const ACL_DEFAULT_CONFIG = {
guard_url: `/auth/403`,
};
/**
* 目前用不到,startup.service.js 启动的时候已经判断当前的url是否是合法的
*/
class LcAclGuard {
constructor(srv, router) {
this.srv = srv;
this.router = router;
}
process(route, state) {
if (this.srv.canAuthUrl(state.url)) {
return true;
}
let guard_url = route.data.guard_url || '/auth/403';
this.router.navigateByUrl(guard_url);
}
canActivate(route, state) {
return this.process(route, state);
}
// all children route
canActivateChild(childRoute, state) {
return this.process(childRoute, state);
}
}
LcAclGuard.ɵprov = i0.ɵɵdefineInjectable({ factory: function LcAclGuard_Factory() { return new LcAclGuard(i0.ɵɵinject(LcAClService), i0.ɵɵinject(i2.Router)); }, token: LcAclGuard, providedIn: "root" });
LcAclGuard.decorators = [
{ type: Injectable, args: [{
providedIn: 'root',
},] }
];
LcAclGuard.ctorParameters = () => [
{ type: LcAClService },
{ type: Router }
];
/**
* 提供一个方法方便查找菜单树中第一个最深层次的菜单url
* @param menus
* @returns
*/
function findFirstUrl(menus) {
let firstMenu = menus[0];
let list = firstMenu.menu_list || firstMenu.children;
if (!list || !list.length) {
return firstMenu.url;
}
return findFirstUrl(list);
}
/**
* 根据当前模块路径获取当前模块可以访问的第一个路由
* @param model_path
* @returns
*/
function getModelFirstAuthPath(model_path, menu_storage_key = 'LCmenus') {
const LCmenus = JSON.parse(localStorage.getItem(menu_storage_key) || '[]');
let result_path = '/auth/403';
if (LCmenus.length > 0) {
LCmenus.forEach(model_item => {
if (model_item.url === model_path) {
result_path = findFirstUrl([model_item]);
}
});
}
if (result_path !== '/auth/403') {
let path = result_path.replace(new RegExp(model_path + '/'), '');
path && (result_path = path);
}
return result_path;
}
/**
* 获取当前menu第一个授权的模块path
* @param menu_storage_key
*/
function getMenuFirstAuthModel(menu_storage_key = 'LCmenus') {
const LCmenus = JSON.parse(localStorage.getItem(menu_storage_key) || '[]');
let result_path = '';
if (LCmenus.length) {
result_path = LCmenus[0].url;
}
return result_path;
}
/**
* Generated bundle index. Do not edit.
*/
export { ACL_DEFAULT_CONFIG, LcACLIfDirective, LcAClDirective, LcAClService, LcAclGuard, LcAclModule, findFirstUrl, getMenuFirstAuthModel, getModelFirstAuthPath };
//# sourceMappingURL=acl.js.map