@abtnode/rbac 1.16.8-beta-186fd5aa → 1.16.8-next-d1e52353
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/lib/core/rbac.js +4 -1
- package/lib/core/storage.js +4 -0
- package/lib/index.js +5 -3
- package/lib/store/sequelize.js +218 -0
- package/package.json +3 -3
package/lib/core/rbac.js
CHANGED
|
@@ -32,7 +32,7 @@ module.exports = class RBAC {
|
|
|
32
32
|
* RBAC constructor
|
|
33
33
|
* @constructor RBAC
|
|
34
34
|
* @param {Object} options Options for RBAC
|
|
35
|
-
* @param {
|
|
35
|
+
* @param {import('./storage')} [options.storage] Storage of grants
|
|
36
36
|
* @param {Array} [options.roles] List of role names (String)
|
|
37
37
|
* @param {Object} [options.permissions] List of permissions
|
|
38
38
|
* @param {Object} [options.grants] List of grants
|
|
@@ -179,6 +179,7 @@ module.exports = class RBAC {
|
|
|
179
179
|
* @method RBAC#createRole
|
|
180
180
|
* @param {String} roleName Name of new Role
|
|
181
181
|
* @param {Boolean} [add] True if you need to add it to the storage
|
|
182
|
+
* @param {object} opt
|
|
182
183
|
* @param {String} opt.title Title of new Role
|
|
183
184
|
* @param {String} opt.description Description of new Role
|
|
184
185
|
* @param {String} opt.extra Extra info of new Role
|
|
@@ -199,6 +200,7 @@ module.exports = class RBAC {
|
|
|
199
200
|
* @param {String} action Name of action
|
|
200
201
|
* @param {String} resource Name of resource
|
|
201
202
|
* @param {Boolean} [add] True if you need to add it to the storage
|
|
203
|
+
* @param {object} opt
|
|
202
204
|
* @param {String} opt.title Title of permission
|
|
203
205
|
* @param {String} opt.description Description of permission
|
|
204
206
|
* @param {String} opt.extra Extra info of permission
|
|
@@ -540,6 +542,7 @@ module.exports = class RBAC {
|
|
|
540
542
|
* Update role or permission from RBAC
|
|
541
543
|
* @method RBAC#updateByName
|
|
542
544
|
* @param {String} name Name of role or permission
|
|
545
|
+
* @param {object} opt
|
|
543
546
|
* @param {String} opt.title Title of role or permission
|
|
544
547
|
* @param {String} opt.description Description of role or permission
|
|
545
548
|
* @param {String} opt.extra Extra Info of role or permission
|
package/lib/core/storage.js
CHANGED
|
@@ -72,6 +72,10 @@ module.exports = class Storage {
|
|
|
72
72
|
throw new Error('Storage method getRoles is not implemented');
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
async updateGrants() {
|
|
76
|
+
throw new Error('Storage method updateGrants is not implemented');
|
|
77
|
+
}
|
|
78
|
+
|
|
75
79
|
/**
|
|
76
80
|
* Get all instances of Permissions
|
|
77
81
|
* @method Storage#getPermissions
|
package/lib/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
const { RBAC } = require('./core');
|
|
2
2
|
|
|
3
3
|
const NedbStorage = require('./store/nedb');
|
|
4
|
+
const SequelizeStorage = require('./store/sequelize');
|
|
4
5
|
const MemoryStorage = require('./store/memory');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {object} options
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {object} options.data
|
|
10
|
-
* @param {
|
|
9
|
+
* @param {import('./core/storage')} options.storage
|
|
10
|
+
* @param {object} [options.data]
|
|
11
|
+
* @param {Array<string>} options.data.roles
|
|
11
12
|
* @param {object} options.data.permissions
|
|
12
13
|
* @param {object} options.data.grant
|
|
13
14
|
*/
|
|
@@ -78,5 +79,6 @@ const createRBAC = async ({ storage, data = {} } = {}) => {
|
|
|
78
79
|
module.exports = {
|
|
79
80
|
NedbStorage,
|
|
80
81
|
MemoryStorage,
|
|
82
|
+
SequelizeStorage,
|
|
81
83
|
createRBAC,
|
|
82
84
|
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
|
|
3
|
+
const Storage = require('../core/storage');
|
|
4
|
+
const Permission = require('../core/permission');
|
|
5
|
+
const Role = require('../core/role');
|
|
6
|
+
|
|
7
|
+
const ItemTypes = Object.freeze({
|
|
8
|
+
Role: 'role',
|
|
9
|
+
Permission: 'permission',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
class SequelizeStorage extends Storage {
|
|
13
|
+
constructor(db) {
|
|
14
|
+
super();
|
|
15
|
+
this.db = db;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async add(item) {
|
|
19
|
+
const { name } = item;
|
|
20
|
+
|
|
21
|
+
if (await this.db.count({ name })) {
|
|
22
|
+
throw new Error(`Item ${name} already exists`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const doc = {
|
|
26
|
+
name: item.name,
|
|
27
|
+
title: item.title,
|
|
28
|
+
description: item.description,
|
|
29
|
+
extra: item.extra,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (item instanceof Role) {
|
|
33
|
+
doc.type = ItemTypes.Role;
|
|
34
|
+
doc.grants = [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (item instanceof Permission) {
|
|
38
|
+
doc.type = ItemTypes.Permission;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await this.db.insert(doc);
|
|
42
|
+
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async remove(item) {
|
|
47
|
+
const { name } = item;
|
|
48
|
+
|
|
49
|
+
if (!(await this.db.count({ name }))) {
|
|
50
|
+
throw new Error(`Item ${name} is not presented in storage`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await this.db.remove({ name });
|
|
54
|
+
const docs = await this.db.find({ type: ItemTypes.Role });
|
|
55
|
+
await Promise.all(
|
|
56
|
+
docs.map((doc) => this.db.update({ id: doc.id }, { $set: { grants: doc.grants.filter((x) => x !== name) } }))
|
|
57
|
+
);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async update(item, { title, description, extra } = {}) {
|
|
62
|
+
const { name } = item;
|
|
63
|
+
|
|
64
|
+
if (!(await this.db.count({ name }))) {
|
|
65
|
+
throw new Error(`Item ${name} is not presented in storage`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await this.db.update({ name }, pick({ title, description, extra }, 'title', 'description', 'extra'));
|
|
69
|
+
return this.get(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async grant(role, child) {
|
|
73
|
+
const { name } = role;
|
|
74
|
+
const { name: childName } = child;
|
|
75
|
+
|
|
76
|
+
if (!(await this.db.count({ name }))) {
|
|
77
|
+
throw new Error(`Role ${name} is not exist`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!(await this.db.count({ name: childName }))) {
|
|
81
|
+
throw new Error(`Base ${childName} is not exist`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!(role instanceof Role)) {
|
|
85
|
+
throw new Error('Role is not instance of Role');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (name === childName) {
|
|
89
|
+
throw new Error(`You can grant yourself ${name}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const docs = await this.db.find({ name });
|
|
93
|
+
await Promise.all(
|
|
94
|
+
docs.map((doc) =>
|
|
95
|
+
this.db.update({ id: doc.id }, { grants: [...doc.grants.filter((x) => x !== childName), childName] })
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async revoke(role, child) {
|
|
103
|
+
const { name } = role;
|
|
104
|
+
const { name: childName } = child;
|
|
105
|
+
|
|
106
|
+
if (!(await this.db.count({ name })) || !(await this.db.count({ name: childName }))) {
|
|
107
|
+
throw new Error('Role is not exist');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const docs = await this.db.find({ name });
|
|
111
|
+
await Promise.all(
|
|
112
|
+
docs.map((doc) => this.db.update({ id: doc.id }, { grants: doc.grants.filter((x) => x !== childName) }))
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async get(name) {
|
|
119
|
+
const item = await this.db.findOne({ name });
|
|
120
|
+
|
|
121
|
+
if (!item) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (item.type === ItemTypes.Role) {
|
|
126
|
+
return this.resolveRole(item);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (item.type === ItemTypes.Permission) {
|
|
130
|
+
return this.resolvePermission(item);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async getRoles() {
|
|
137
|
+
const roles = await this.db.find({ type: ItemTypes.Role }, {}, { createdAt: -1 });
|
|
138
|
+
return roles.map((item) => this.resolveRole(item));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async getPermissions() {
|
|
142
|
+
const permissions = await this.db.find({ type: ItemTypes.Permission }, {}, { createdAt: -1 });
|
|
143
|
+
return permissions.map((item) => this.resolvePermission(item));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async getGrants(name) {
|
|
147
|
+
const item = await this.db.findOne({ name });
|
|
148
|
+
|
|
149
|
+
if (item) {
|
|
150
|
+
const grants = await Promise.all((item.grants || []).map((x) => this.get(x)));
|
|
151
|
+
return grants;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// custom
|
|
158
|
+
|
|
159
|
+
async updateGrants(roleName, grantNames = []) {
|
|
160
|
+
const role = await this.db.findOne({ name: roleName });
|
|
161
|
+
|
|
162
|
+
if (!role) {
|
|
163
|
+
throw new Error(`Role ${roleName} is not exist`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (role.type !== ItemTypes.Role) {
|
|
167
|
+
throw new Error(`${roleName} is not a role`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const names = await this.db.find({}, { name: 1 });
|
|
171
|
+
const namesMap = names.reduce((o, { name }) => {
|
|
172
|
+
o[name] = true;
|
|
173
|
+
return o;
|
|
174
|
+
}, {});
|
|
175
|
+
|
|
176
|
+
grantNames.forEach((grantName) => {
|
|
177
|
+
if (!namesMap[grantName]) {
|
|
178
|
+
throw new Error(`${grantName} is not exist`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (roleName === grantName) {
|
|
182
|
+
throw new Error(`You can grant yourself ${roleName}`);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await this.db.update({ name: roleName }, { grants: grantNames });
|
|
187
|
+
return this.get(roleName);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// private
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {Object} item
|
|
194
|
+
* name: string
|
|
195
|
+
* type: ItemTypes
|
|
196
|
+
* grants: string[]
|
|
197
|
+
*/
|
|
198
|
+
resolveRole(item) {
|
|
199
|
+
const { name, title, description, extra } = item;
|
|
200
|
+
const role = new Role(this.rbac, name, { title, description, extra });
|
|
201
|
+
role.grants = item.grants;
|
|
202
|
+
return role;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @param {Object} item
|
|
207
|
+
* name: string
|
|
208
|
+
* type: ItemTypes
|
|
209
|
+
*/
|
|
210
|
+
resolvePermission(item) {
|
|
211
|
+
const { name, title, description, extra } = item;
|
|
212
|
+
const { action, resource } = Permission.decodeName(name, this.rbac.options.delimiter);
|
|
213
|
+
const permission = new Permission(this.rbac, action, resource, { title, description, extra });
|
|
214
|
+
return permission;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = SequelizeStorage;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.8-
|
|
6
|
+
"version": "1.16.8-next-d1e52353",
|
|
7
7
|
"description": "Simple lib to manage access controls in ABT Node",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"author": "linchen1987 <linchen.1987@foxmail.com> (http://github.com/linchen1987)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/db": "1.16.8-
|
|
22
|
+
"@abtnode/db": "1.16.8-next-d1e52353",
|
|
23
23
|
"fs-extra": "^10.1.0",
|
|
24
24
|
"lodash": "^4.17.21"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"jest": "^27.5.1"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "d357376aa3df9ef789befc7f548629deecb04d96"
|
|
30
30
|
}
|