casbin
Version:
An authorization library that supports access control models like ACL, RBAC, ABAC in Node.JS
593 lines (592 loc) • 21.2 kB
JavaScript
"use strict";
// Copyright 2018 The Casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.newEnforcer = exports.newEnforcerWithClass = exports.Enforcer = void 0;
const managementEnforcer_1 = require("./managementEnforcer");
const model_1 = require("./model");
const persist_1 = require("./persist");
const log_1 = require("./log");
const util_1 = require("./util");
/**
* Enforcer = ManagementEnforcer + RBAC API.
*/
class Enforcer extends managementEnforcer_1.ManagementEnforcer {
/**
* initWithFile initializes an enforcer with a model file and a policy file.
* @param modelPath model file path
* @param policyPath policy file path
* @param lazyLoad lazyLoad whether to load policy at initial time
*/
async initWithFile(modelPath, policyPath, lazyLoad = false) {
const a = new persist_1.FileAdapter(policyPath, this.fs);
await this.initWithAdapter(modelPath, a, lazyLoad);
}
/**
* initWithFile initializes an enforcer with a model file and a policy file.
* @param modelPath model file path
* @param policyString policy CSV string
* @param lazyLoad whether to load policy at initial time
*/
async initWithString(modelPath, policyString, lazyLoad = false) {
const a = new persist_1.StringAdapter(policyString);
await this.initWithAdapter(modelPath, a, lazyLoad);
}
/**
* initWithAdapter initializes an enforcer with a database adapter.
* @param modelPath model file path
* @param adapter current adapter instance
* @param lazyLoad whether to load policy at initial time
*/
async initWithAdapter(modelPath, adapter, lazyLoad = false) {
const m = (0, model_1.newModelFromFile)(modelPath, this.fs);
await this.initWithModelAndAdapter(m, adapter, lazyLoad);
this.modelPath = modelPath;
}
/**
* initWithModelAndAdapter initializes an enforcer with a model and a database adapter.
* @param m model instance
* @param adapter current adapter instance
* @param lazyLoad whether to load policy at initial time
*/
async initWithModelAndAdapter(m, adapter, lazyLoad = false) {
if (adapter) {
this.adapter = adapter;
}
this.model = m;
this.model.printModel();
this.initRmMap();
if (!lazyLoad && this.adapter) {
await this.loadPolicy();
}
}
/**
* getRolesForUser gets the roles that a user has.
*
* @param name the user.
* @param domain the domain.
* @return the roles that the user has.
*/
async getRolesForUser(name, domain) {
const rm = this.rmMap.get('g');
if (rm) {
if (domain === undefined) {
return rm.getRoles(name);
}
else {
return rm.getRoles(name, domain);
}
}
throw new Error("RoleManager didn't exist.");
}
/**
* getUsersForRole gets the users that has a role.
*
* @param name the role.
* @param domain the domain.
* @return the users that has the role.
*/
async getUsersForRole(name, domain) {
const rm = this.rmMap.get('g');
if (rm) {
if (domain === undefined) {
return rm.getUsers(name);
}
else {
return rm.getUsers(name, domain);
}
}
throw new Error("RoleManager didn't exist.");
}
/**
* hasRoleForUser determines whether a user has a role.
*
* @param name the user.
* @param role the role.
* @param domain the domain.
* @return whether the user has the role.
*/
async hasRoleForUser(name, role, domain) {
const roles = await this.getRolesForUser(name, domain);
let hasRole = false;
for (const r of roles) {
if (r === role) {
hasRole = true;
break;
}
}
return hasRole;
}
/**
* addRoleForUser adds a role for a user.
* Returns false if the user already has the role (aka not affected).
*
* @param user the user.
* @param role the role.
* @param domain the domain.
* @return succeeds or not.
*/
async addRoleForUser(user, role, domain) {
if (domain === undefined) {
return this.addGroupingPolicy(user, role);
}
else {
return this.addGroupingPolicy(user, role, domain);
}
}
/**
* deleteRoleForUser deletes a role for a user.
* Returns false if the user does not have the role (aka not affected).
*
* @param user the user.
* @param role the role.
* @param domain the domain.
* @return succeeds or not.
*/
async deleteRoleForUser(user, role, domain) {
if (!user) {
throw new Error('user must not be empty');
}
if (!role) {
throw new Error('role must not be empty');
}
if (domain === undefined) {
return this.removeGroupingPolicy(user, role);
}
else {
return this.removeGroupingPolicy(user, role, domain);
}
}
/**
* deleteRolesForUser deletes all roles for a user.
* Returns false if the user does not have any roles (aka not affected).
*
* @param user the user.
* @param domain the domain.
* @return succeeds or not.
*/
async deleteRolesForUser(user, domain) {
if (!user) {
throw new Error('user must not be empty');
}
if (domain === undefined) {
const subIndex = this.getFieldIndex('p', "sub" /* FieldIndex.Subject */);
return this.removeFilteredGroupingPolicy(subIndex, user);
}
else {
return this.removeFilteredGroupingPolicy(0, user, '', domain);
}
}
/**
* deleteUser deletes a user.
* Returns false if the user does not exist (aka not affected).
*
* @param user the user.
* @return succeeds or not.
*/
async deleteUser(user) {
if (!user) {
throw new Error('user must not be empty');
}
const subIndex = this.getFieldIndex('p', "sub" /* FieldIndex.Subject */);
const res1 = await this.removeFilteredGroupingPolicy(subIndex, user);
const res2 = await this.removeFilteredPolicy(subIndex, user);
return res1 || res2;
}
/**
* deleteRole deletes a role.
* Returns false if the role does not exist (aka not affected).
*
* @param role the role.
* @return succeeds or not.
*/
async deleteRole(role) {
if (!role) {
throw new Error('role must not be empty');
}
const subIndex = this.getFieldIndex('p', "sub" /* FieldIndex.Subject */);
const res1 = await this.removeFilteredGroupingPolicy(subIndex, role);
const res2 = await this.removeFilteredPolicy(subIndex, role);
return res1 || res2;
}
/**
* deletePermission deletes a permission.
* Returns false if the permission does not exist (aka not affected).
*
* @param permission the permission, usually be (obj, act). It is actually the rule without the subject.
* @return succeeds or not.
*/
async deletePermission(...permission) {
if (permission.length === 0) {
throw new Error('permission must not be empty');
}
return this.removeFilteredPolicy(1, ...permission);
}
/**
* addPermissionForUser adds a permission for a user or role.
* Returns false if the user or role already has the permission (aka not affected).
*
* @param user the user.
* @param permission the permission, usually be (obj, act). It is actually the rule without the subject.
* @return succeeds or not.
*/
async addPermissionForUser(user, ...permission) {
permission.unshift(user);
return this.addPolicy(...permission);
}
/**
* deletePermissionForUser deletes a permission for a user or role.
* Returns false if the user or role does not have the permission (aka not affected).
*
* @param user the user.
* @param permission the permission, usually be (obj, act). It is actually the rule without the subject.
* @return succeeds or not.
*/
async deletePermissionForUser(user, ...permission) {
if (!user) {
throw new Error('user must not be empty');
}
permission.unshift(user);
return this.removePolicy(...permission);
}
/**
* deletePermissionsForUser deletes permissions for a user or role.
* Returns false if the user or role does not have any permissions (aka not affected).
*
* @param user the user.
* @return succeeds or not.
*/
async deletePermissionsForUser(user) {
if (!user) {
throw new Error('user must not be empty');
}
const subIndex = this.getFieldIndex('p', "sub" /* FieldIndex.Subject */);
return this.removeFilteredPolicy(subIndex, user);
}
/**
* getPermissionsForUser gets permissions for a user or role.
*
* @param user the user.
* @return the permissions, a permission is usually like (obj, act). It is actually the rule without the subject.
*/
async getPermissionsForUser(user) {
const subIndex = this.getFieldIndex('p', "sub" /* FieldIndex.Subject */);
return this.getFilteredPolicy(subIndex, user);
}
/**
* hasPermissionForUser determines whether a user has a permission.
*
* @param user the user.
* @param permission the permission, usually be (obj, act). It is actually the rule without the subject.
* @return whether the user has the permission.
*/
async hasPermissionForUser(user, ...permission) {
permission.unshift(user);
return this.hasPolicy(...permission);
}
/**
* getImplicitRolesForUser gets implicit roles that a user has.
* Compared to getRolesForUser(), this function retrieves indirect roles besides direct roles.
* For example:
* g, alice, role:admin
* g, role:admin, role:user
*
* getRolesForUser("alice") can only get: ["role:admin"].
* But getImplicitRolesForUser("alice") will get: ["role:admin", "role:user"].
*/
async getImplicitRolesForUser(name, ...domain) {
const res = new Set();
const q = [name];
let n;
while ((n = q.shift()) !== undefined) {
for (const rm of this.rmMap.values()) {
const role = await rm.getRoles(n, ...domain);
role.forEach((r) => {
if (!res.has(r)) {
res.add(r);
q.push(r);
}
});
}
}
return Array.from(res);
}
/**
* getImplicitPermissionsForUser gets implicit permissions for a user or role.
* Compared to getPermissionsForUser(), this function retrieves permissions for inherited roles.
* For example:
* p, admin, data1, read
* p, alice, data2, read
* g, alice, admin
*
* getPermissionsForUser("alice") can only get: [["alice", "data2", "read"]].
* But getImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]].
*/
async getImplicitPermissionsForUser(user, ...domain) {
const roles = await this.getImplicitRolesForUser(user, ...domain);
roles.unshift(user);
const res = [];
const withDomain = domain && domain.length !== 0;
for (const n of roles) {
if (withDomain) {
const p = await this.getFilteredPolicy(0, n, ...domain);
res.push(...p);
}
else {
const p = await this.getPermissionsForUser(n);
res.push(...p);
}
}
return res;
}
/**
* getImplicitResourcesForUser returns all policies that user obtaining in domain.
*/
async getImplicitResourcesForUser(user, ...domain) {
const permissions = await this.getImplicitPermissionsForUser(user, ...domain);
const res = [];
for (const permission of permissions) {
if (permission[0] === user) {
res.push(permission);
continue;
}
let resLocal = [[user]];
const tokensLength = permission.length;
const t = [];
for (const token of permission) {
if (token === permission[0]) {
continue;
}
const tokens = await this.getImplicitUsersForRole(token, ...domain);
tokens.push(token);
t.push(tokens);
}
for (let i = 0; i < tokensLength - 1; i++) {
const n = [];
for (const tokens of t[i]) {
for (const policy of resLocal) {
const t = [...policy];
t.push(tokens);
n.push(t);
}
}
resLocal = n;
}
res.push(...resLocal);
}
return res;
}
/**
* getImplicitUsersForRole gets implicit users that a role has.
* Compared to getUsersForRole(), this function retrieves indirect users besides direct users.
* For example:
* g, alice, role:admin
* g, role:admin, role:user
*
* getUsersForRole("user") can only get: ["role:admin"].
* But getImplicitUsersForRole("user") will get: ["role:admin", "alice"].
*/
async getImplicitUsersForRole(role, ...domain) {
const res = new Set();
const q = [role];
let n;
while ((n = q.shift()) !== undefined) {
for (const rm of this.rmMap.values()) {
const user = await rm.getUsers(n, ...domain);
user.forEach((u) => {
if (!res.has(u)) {
res.add(u);
q.push(u);
}
});
}
}
return Array.from(res);
}
/**
* getRolesForUserInDomain gets the roles that a user has inside a domain
* An alias for getRolesForUser with the domain params.
*
* @param name the user.
* @param domain the domain.
* @return the roles that the user has.
*/
async getRolesForUserInDomain(name, domain) {
return this.getRolesForUser(name, domain);
}
/**
* getUsersForRoleInFomain gets the users that has a role inside a domain
* An alias for getUsesForRole with the domain params.
*
* @param name the role.
* @param domain the domain.
* @return the users that has the role.
*/
async getUsersForRoleInDomain(name, domain) {
return this.getUsersForRole(name, domain);
}
/**
* getImplicitUsersForPermission gets implicit users for a permission.
* For example:
* p, admin, data1, read
* p, bob, data1, read
* g, alice, admin
*
* getImplicitUsersForPermission("data1", "read") will get: ["alice", "bob"].
* Note: only users will be returned, roles (2nd arg in "g") will be excluded.
*/
async getImplicitUsersForPermission(...permission) {
const res = [];
const policySubjects = await this.getAllSubjects();
const subjects = (0, util_1.arrayRemoveDuplicates)([...policySubjects, ...this.model.getValuesForFieldInPolicyAllTypes('g', 0)]);
const inherits = this.model.getValuesForFieldInPolicyAllTypes('g', 1);
for (const user of subjects) {
const allowed = await this.enforce(user, ...permission);
if (allowed) {
res.push(user);
}
}
return res.filter((n) => !inherits.some((m) => n === m));
}
/**
* getDomainsForUser gets all domains that a user has.
*/
async getDomainsForUser(user) {
const domains = [];
for (const rm of this.rmMap.values()) {
const domain = await rm.getDomains(user);
domains.push(...domain);
}
return domains;
}
/**
* getAllDomains gets all domains.
*/
async getAllDomains() {
const domains = [];
for (const rm of this.rmMap.values()) {
const domain = await rm.getAllDomains();
domains.push(...domain);
}
return (0, util_1.arrayRemoveDuplicates)(domains);
}
}
exports.Enforcer = Enforcer;
async function newEnforcerWithClass(enforcer, ...params) {
var _a;
// inject the FS
if (!(0, persist_1.getDefaultFileSystem)()) {
try {
if (typeof process !== 'undefined' && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node)) {
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
const defaultFileSystem = {
readFileSync(path, encoding) {
return fs.readFileSync(path, { encoding });
},
writeFileSync(path, text, encoding) {
return fs.writeFileSync(path, text, encoding);
},
};
(0, persist_1.setDefaultFileSystem)(defaultFileSystem);
}
}
catch (ignored) { }
}
const e = new enforcer();
let parsedParamLen = 0;
if (params.length >= 1) {
const enableLog = params[params.length - 1];
if (typeof enableLog === 'boolean') {
(0, log_1.getLogger)().enableLog(enableLog);
parsedParamLen++;
}
}
if (params.length - parsedParamLen === 2) {
if (typeof params[0] === 'string') {
if (typeof params[1] === 'string') {
await e.initWithFile(params[0].toString(), params[1].toString());
}
else {
await e.initWithAdapter(params[0].toString(), params[1], params[2] === true);
}
}
else {
if (typeof params[1] === 'string') {
throw new Error('Invalid parameters for enforcer.');
}
else {
await e.initWithModelAndAdapter(params[0], params[1]);
}
}
}
else if (params.length - parsedParamLen === 1) {
if (typeof params[0] === 'string') {
await e.initWithFile(params[0], '');
}
else {
await e.initWithModelAndAdapter(params[0]);
}
}
else if (params.length === parsedParamLen) {
await e.initWithFile('', '');
}
else {
throw new Error('Invalid parameters for enforcer.');
}
return e;
}
exports.newEnforcerWithClass = newEnforcerWithClass;
/**
* newEnforcer creates an enforcer via file or DB.
*
* File:
* ```js
* const e = new Enforcer('path/to/basic_model.conf', 'path/to/basic_policy.csv');
* ```
*
* MySQL DB:
* ```js
* const a = new MySQLAdapter('mysql', 'mysql_username:mysql_password@tcp(127.0.0.1:3306)/');
* const e = new Enforcer('path/to/basic_model.conf', a);
* ```
*
* @param params
*/
async function newEnforcer(...params) {
return newEnforcerWithClass(Enforcer, ...params);
}
exports.newEnforcer = newEnforcer;