@vsaas/loopback 10.0.0
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/LICENSE +25 -0
- package/README.md +91 -0
- package/common/models/README.md +109 -0
- package/common/models/access-token.json +37 -0
- package/common/models/acl.json +17 -0
- package/common/models/application.json +130 -0
- package/common/models/change.json +25 -0
- package/common/models/checkpoint.json +14 -0
- package/common/models/email.json +11 -0
- package/common/models/key-value-model.json +4 -0
- package/common/models/role-mapping.json +26 -0
- package/common/models/role.json +30 -0
- package/common/models/scope.json +14 -0
- package/common/models/user.json +118 -0
- package/dist/_virtual/_rolldown/runtime.cjs +32 -0
- package/dist/common/models/access-token.cjs +144 -0
- package/dist/common/models/access-token2.cjs +43 -0
- package/dist/common/models/acl.cjs +428 -0
- package/dist/common/models/acl2.cjs +27 -0
- package/dist/common/models/application.cjs +100 -0
- package/dist/common/models/application2.cjs +118 -0
- package/dist/common/models/change.cjs +404 -0
- package/dist/common/models/change2.cjs +25 -0
- package/dist/common/models/checkpoint.cjs +43 -0
- package/dist/common/models/checkpoint2.cjs +18 -0
- package/dist/common/models/email.cjs +18 -0
- package/dist/common/models/email2.cjs +30 -0
- package/dist/common/models/key-value-model.cjs +140 -0
- package/dist/common/models/key-value-model2.cjs +14 -0
- package/dist/common/models/role-mapping.cjs +57 -0
- package/dist/common/models/role-mapping2.cjs +34 -0
- package/dist/common/models/role.cjs +396 -0
- package/dist/common/models/role2.cjs +38 -0
- package/dist/common/models/scope.cjs +30 -0
- package/dist/common/models/scope2.cjs +21 -0
- package/dist/common/models/user.cjs +810 -0
- package/dist/common/models/user2.cjs +118 -0
- package/dist/index.cjs +16 -0
- package/dist/lib/access-context.cjs +228 -0
- package/dist/lib/application.cjs +450 -0
- package/dist/lib/builtin-models.cjs +60 -0
- package/dist/lib/configure-shared-methods.cjs +41 -0
- package/dist/lib/connectors/base-connector.cjs +23 -0
- package/dist/lib/connectors/mail-direct-transport.cjs +375 -0
- package/dist/lib/connectors/mail-stub-transport.cjs +86 -0
- package/dist/lib/connectors/mail.cjs +128 -0
- package/dist/lib/connectors/memory.cjs +19 -0
- package/dist/lib/current-context.cjs +22 -0
- package/dist/lib/globalize.cjs +29 -0
- package/dist/lib/loopback.cjs +313 -0
- package/dist/lib/model.cjs +1009 -0
- package/dist/lib/persisted-model.cjs +1835 -0
- package/dist/lib/registry.cjs +291 -0
- package/dist/lib/runtime.cjs +25 -0
- package/dist/lib/server-app.cjs +231 -0
- package/dist/lib/utils.cjs +154 -0
- package/dist/package.cjs +124 -0
- package/dist/server/middleware/context.cjs +7 -0
- package/dist/server/middleware/error-handler.cjs +6 -0
- package/dist/server/middleware/favicon.cjs +13 -0
- package/dist/server/middleware/rest.cjs +44 -0
- package/dist/server/middleware/static.cjs +14 -0
- package/dist/server/middleware/status.cjs +28 -0
- package/dist/server/middleware/token.cjs +66 -0
- package/dist/server/middleware/url-not-found.cjs +20 -0
- package/favicon.ico +0 -0
- package/package.json +121 -0
- package/templates/reset-form.ejs +3 -0
- package/templates/verify.ejs +9 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
7
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
8
|
+
var __exportAll = (all, no_symbols) => {
|
|
9
|
+
let target = {};
|
|
10
|
+
for (var name in all) __defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true
|
|
13
|
+
});
|
|
14
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
15
|
+
return target;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
19
|
+
key = keys[i];
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
21
|
+
get: ((k) => from[k]).bind(null, key),
|
|
22
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
//#endregion
|
|
29
|
+
exports.__commonJSMin = __commonJSMin;
|
|
30
|
+
exports.__esmMin = __esmMin;
|
|
31
|
+
exports.__exportAll = __exportAll;
|
|
32
|
+
exports.__toCommonJS = __toCommonJS;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_lib_globalize = require("../../lib/globalize.cjs");
|
|
4
|
+
const require_lib_utils = require("../../lib/utils.cjs");
|
|
5
|
+
//#region src/common/models/access-token.ts
|
|
6
|
+
var require_access_token = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
|
|
7
|
+
const g = require_lib_globalize;
|
|
8
|
+
const utils = require_lib_utils;
|
|
9
|
+
const assert = require("assert");
|
|
10
|
+
const crypto = require("crypto");
|
|
11
|
+
const DEFAULT_TOKEN_LEN = 64;
|
|
12
|
+
function createRandomToken(length, callback) {
|
|
13
|
+
let token = "";
|
|
14
|
+
function appendChunk() {
|
|
15
|
+
const remainingLength = length - token.length;
|
|
16
|
+
if (remainingLength <= 0) {
|
|
17
|
+
callback(null, token);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const byteLength = Math.max(1, Math.ceil(remainingLength * 3 / 4));
|
|
21
|
+
crypto.randomBytes(byteLength, function(err, buffer) {
|
|
22
|
+
if (err) {
|
|
23
|
+
callback(err);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
token += buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
27
|
+
if (token.length >= length) {
|
|
28
|
+
callback(null, token.slice(0, length));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
appendChunk();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
appendChunk();
|
|
35
|
+
}
|
|
36
|
+
function configureAccessToken(AccessToken) {
|
|
37
|
+
AccessToken.ANONYMOUS = new AccessToken({ id: "$anonymous" });
|
|
38
|
+
AccessToken.createAccessTokenId = function(fn) {
|
|
39
|
+
createRandomToken(this.settings.accessTokenIdLength || DEFAULT_TOKEN_LEN, fn);
|
|
40
|
+
};
|
|
41
|
+
AccessToken.observe("before save", function(ctx, next) {
|
|
42
|
+
if (!ctx.instance || ctx.instance.id) return next();
|
|
43
|
+
AccessToken.createAccessTokenId(function(err, id) {
|
|
44
|
+
if (err) return next(err);
|
|
45
|
+
ctx.instance.id = id;
|
|
46
|
+
next();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
AccessToken.getIdForRequest = function(req, options) {
|
|
50
|
+
options = options || {};
|
|
51
|
+
let params = options.params || [];
|
|
52
|
+
let headers = options.headers || [];
|
|
53
|
+
let cookies = options.cookies || [];
|
|
54
|
+
let i = 0;
|
|
55
|
+
let length;
|
|
56
|
+
let id;
|
|
57
|
+
if (options.searchDefaultTokenKeys !== false) {
|
|
58
|
+
params = params.concat(["access_token"]);
|
|
59
|
+
headers = headers.concat(["X-Access-Token", "authorization"]);
|
|
60
|
+
cookies = cookies.concat(["access_token", "authorization"]);
|
|
61
|
+
}
|
|
62
|
+
for (length = params.length; i < length; i++) {
|
|
63
|
+
const param = params[i];
|
|
64
|
+
id = req.params && req.params[param] !== void 0 ? req.params[param] : req.body && req.body[param] !== void 0 ? req.body[param] : req.query && req.query[param] !== void 0 ? req.query[param] : void 0;
|
|
65
|
+
if (typeof id === "string") return id;
|
|
66
|
+
}
|
|
67
|
+
for (i = 0, length = headers.length; i < length; i++) {
|
|
68
|
+
id = req.header(headers[i]);
|
|
69
|
+
if (typeof id === "string") {
|
|
70
|
+
if (id === "") continue;
|
|
71
|
+
if (id.indexOf("Bearer ") === 0) {
|
|
72
|
+
id = id.substring(7);
|
|
73
|
+
if (options.bearerTokenBase64Encoded) id = Buffer.from(id, "base64").toString("utf8");
|
|
74
|
+
} else if (/^Basic /i.test(id)) {
|
|
75
|
+
id = id.substring(6);
|
|
76
|
+
id = Buffer.from(id, "base64").toString("utf8");
|
|
77
|
+
const parts = /^([^:]*):(.*)$/.exec(id);
|
|
78
|
+
if (parts) id = parts[2].length > parts[1].length ? parts[2] : parts[1];
|
|
79
|
+
}
|
|
80
|
+
return id;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (req.signedCookies) for (i = 0, length = cookies.length; i < length; i++) {
|
|
84
|
+
id = req.signedCookies[cookies[i]];
|
|
85
|
+
if (typeof id === "string") return id;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
AccessToken.resolve = function(id, cb) {
|
|
90
|
+
return promiseOrCallback(this.findById(id).then(function(token) {
|
|
91
|
+
if (!token) return null;
|
|
92
|
+
return token.validate().then(function(isValid) {
|
|
93
|
+
if (isValid) return token;
|
|
94
|
+
const e = new Error(g.f("Invalid Access Token"));
|
|
95
|
+
e.status = e.statusCode = 401;
|
|
96
|
+
e.code = "INVALID_TOKEN";
|
|
97
|
+
throw e;
|
|
98
|
+
});
|
|
99
|
+
}), cb);
|
|
100
|
+
};
|
|
101
|
+
AccessToken.findForRequest = function(req, options, cb) {
|
|
102
|
+
if (cb === void 0 && typeof options === "function") {
|
|
103
|
+
cb = options;
|
|
104
|
+
options = {};
|
|
105
|
+
}
|
|
106
|
+
const id = this.getIdForRequest(req, options);
|
|
107
|
+
return promiseOrCallback(id ? utils.invokeWithCallback(this.resolve, this, [id]) : Promise.resolve(null), cb);
|
|
108
|
+
};
|
|
109
|
+
AccessToken.prototype.validate = function(cb) {
|
|
110
|
+
const token = this;
|
|
111
|
+
return promiseOrCallback((async function() {
|
|
112
|
+
assert(token.created && typeof token.created.getTime === "function", "token.created must be a valid Date");
|
|
113
|
+
assert(token.ttl !== 0, "token.ttl must be not be 0");
|
|
114
|
+
assert(token.ttl, "token.ttl must exist");
|
|
115
|
+
assert(token.ttl >= -1, "token.ttl must be >= -1");
|
|
116
|
+
const AccessToken = token.constructor;
|
|
117
|
+
const userRelation = AccessToken.relations.user;
|
|
118
|
+
let User = userRelation && userRelation.modelTo;
|
|
119
|
+
if (token.principalType) {
|
|
120
|
+
User = AccessToken.registry.findModel(token.principalType);
|
|
121
|
+
if (!User) return false;
|
|
122
|
+
}
|
|
123
|
+
const elapsedSeconds = (Date.now() - token.created.getTime()) / 1e3;
|
|
124
|
+
const secondsToLive = token.ttl;
|
|
125
|
+
const eternalTokensAllowed = !!(User && User.settings.allowEternalTokens);
|
|
126
|
+
if (secondsToLive === -1 ? eternalTokensAllowed : elapsedSeconds < secondsToLive) return true;
|
|
127
|
+
await token.destroy();
|
|
128
|
+
return false;
|
|
129
|
+
})(), cb);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function promiseOrCallback(promise, cb) {
|
|
133
|
+
if (typeof cb === "function") {
|
|
134
|
+
promise.then(function(result) {
|
|
135
|
+
cb(null, result);
|
|
136
|
+
}, cb);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
return promise;
|
|
140
|
+
}
|
|
141
|
+
module.exports = configureAccessToken;
|
|
142
|
+
}));
|
|
143
|
+
//#endregion
|
|
144
|
+
module.exports = require_access_token();
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region common/models/access-token.json
|
|
2
|
+
var require_access_token = /* @__PURE__ */ require("../../_virtual/_rolldown/runtime.cjs").__commonJSMin(((exports, module) => {
|
|
3
|
+
module.exports = {
|
|
4
|
+
"name": "AccessToken",
|
|
5
|
+
"properties": {
|
|
6
|
+
"id": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"id": true
|
|
9
|
+
},
|
|
10
|
+
"ttl": {
|
|
11
|
+
"type": "number",
|
|
12
|
+
"ttl": true,
|
|
13
|
+
"default": 1209600,
|
|
14
|
+
"description": "time to live in seconds (2 weeks by default)"
|
|
15
|
+
},
|
|
16
|
+
"scopes": {
|
|
17
|
+
"type": ["string"],
|
|
18
|
+
"description": "Array of scopes granted to this access token."
|
|
19
|
+
},
|
|
20
|
+
"created": {
|
|
21
|
+
"type": "Date",
|
|
22
|
+
"defaultFn": "now"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"relations": { "user": {
|
|
26
|
+
"type": "belongsTo",
|
|
27
|
+
"model": "User",
|
|
28
|
+
"foreignKey": "userId"
|
|
29
|
+
} },
|
|
30
|
+
"acls": [{
|
|
31
|
+
"principalType": "ROLE",
|
|
32
|
+
"principalId": "$everyone",
|
|
33
|
+
"permission": "DENY"
|
|
34
|
+
}]
|
|
35
|
+
};
|
|
36
|
+
}));
|
|
37
|
+
//#endregion
|
|
38
|
+
Object.defineProperty(exports, "default", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
get: function() {
|
|
41
|
+
return require_access_token();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const require_runtime$1 = require("../../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_lib_globalize = require("../../lib/globalize.cjs");
|
|
4
|
+
const require_lib_runtime = require("../../lib/runtime.cjs");
|
|
5
|
+
const require_lib_utils = require("../../lib/utils.cjs");
|
|
6
|
+
const require_lib_access_context = require("../../lib/access-context.cjs");
|
|
7
|
+
//#region src/common/models/acl.ts
|
|
8
|
+
var require_acl = /* @__PURE__ */ require_runtime$1.__commonJSMin(((exports, module) => {
|
|
9
|
+
const g = require_lib_globalize;
|
|
10
|
+
const runtime = require_lib_runtime;
|
|
11
|
+
const utils = require_lib_utils;
|
|
12
|
+
const assert = require("assert");
|
|
13
|
+
const debug = require("debug")("loopback:security:acl");
|
|
14
|
+
const ctx = (require_lib_access_context.init_access_context(), require_runtime$1.__toCommonJS(require_lib_access_context.access_context_exports));
|
|
15
|
+
const AccessContext = ctx.AccessContext;
|
|
16
|
+
const Principal = ctx.Principal;
|
|
17
|
+
const AccessRequest = ctx.AccessRequest;
|
|
18
|
+
module.exports = function(ACL) {
|
|
19
|
+
const Role = runtime.loopback.Role;
|
|
20
|
+
assert(Role, "Role model must be defined before ACL model");
|
|
21
|
+
ACL.ALL = AccessContext.ALL;
|
|
22
|
+
ACL.DEFAULT = AccessContext.DEFAULT;
|
|
23
|
+
ACL.ALLOW = AccessContext.ALLOW;
|
|
24
|
+
ACL.ALARM = AccessContext.ALARM;
|
|
25
|
+
ACL.AUDIT = AccessContext.AUDIT;
|
|
26
|
+
ACL.DENY = AccessContext.DENY;
|
|
27
|
+
ACL.READ = AccessContext.READ;
|
|
28
|
+
ACL.REPLICATE = AccessContext.REPLICATE;
|
|
29
|
+
ACL.WRITE = AccessContext.WRITE;
|
|
30
|
+
ACL.EXECUTE = AccessContext.EXECUTE;
|
|
31
|
+
ACL.USER = Principal.USER;
|
|
32
|
+
ACL.APP = ACL.APPLICATION = Principal.APPLICATION;
|
|
33
|
+
ACL.ROLE = Principal.ROLE;
|
|
34
|
+
ACL.SCOPE = Principal.SCOPE;
|
|
35
|
+
ACL.DEFAULT_SCOPE = ctx.DEFAULT_SCOPES[0];
|
|
36
|
+
ACL.getMatchingScore = function getMatchingScore(rule, req) {
|
|
37
|
+
const props = [
|
|
38
|
+
"model",
|
|
39
|
+
"property",
|
|
40
|
+
"accessType"
|
|
41
|
+
];
|
|
42
|
+
let score = 0;
|
|
43
|
+
for (let i = 0; i < props.length; i++) {
|
|
44
|
+
score = score * 4;
|
|
45
|
+
const ruleValue = rule[props[i]] || ACL.ALL;
|
|
46
|
+
const requestedValue = req[props[i]] || ACL.ALL;
|
|
47
|
+
const isMatchingMethodName = props[i] === "property" && req.methodNames.indexOf(ruleValue) !== -1;
|
|
48
|
+
let isMatchingAccessType = ruleValue === requestedValue;
|
|
49
|
+
if (props[i] === "accessType" && !isMatchingAccessType) switch (ruleValue) {
|
|
50
|
+
case ACL.EXECUTE:
|
|
51
|
+
isMatchingAccessType = true;
|
|
52
|
+
break;
|
|
53
|
+
case ACL.WRITE:
|
|
54
|
+
isMatchingAccessType = requestedValue === ACL.REPLICATE;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (isMatchingMethodName || isMatchingAccessType) score += 3;
|
|
58
|
+
else if (ruleValue === ACL.ALL) score += 2;
|
|
59
|
+
else if (requestedValue === ACL.ALL) score += 1;
|
|
60
|
+
else return -1;
|
|
61
|
+
}
|
|
62
|
+
score = score * 4;
|
|
63
|
+
switch (rule.principalType) {
|
|
64
|
+
case ACL.USER:
|
|
65
|
+
score += 4;
|
|
66
|
+
break;
|
|
67
|
+
case ACL.APP:
|
|
68
|
+
score += 3;
|
|
69
|
+
break;
|
|
70
|
+
case ACL.ROLE:
|
|
71
|
+
score += 2;
|
|
72
|
+
break;
|
|
73
|
+
default: score += 1;
|
|
74
|
+
}
|
|
75
|
+
score = score * 8;
|
|
76
|
+
if (rule.principalType === ACL.ROLE) switch (rule.principalId) {
|
|
77
|
+
case Role.OWNER:
|
|
78
|
+
score += 4;
|
|
79
|
+
break;
|
|
80
|
+
case Role.RELATED:
|
|
81
|
+
score += 3;
|
|
82
|
+
break;
|
|
83
|
+
case Role.AUTHENTICATED:
|
|
84
|
+
case Role.UNAUTHENTICATED:
|
|
85
|
+
score += 2;
|
|
86
|
+
break;
|
|
87
|
+
case Role.EVERYONE:
|
|
88
|
+
score += 1;
|
|
89
|
+
break;
|
|
90
|
+
default: score += 5;
|
|
91
|
+
}
|
|
92
|
+
score = score * 4;
|
|
93
|
+
score += AccessContext.permissionOrder[rule.permission || ACL.ALLOW] - 1;
|
|
94
|
+
return score;
|
|
95
|
+
};
|
|
96
|
+
ACL.prototype.score = function(req) {
|
|
97
|
+
return this.constructor.getMatchingScore(this, req);
|
|
98
|
+
};
|
|
99
|
+
ACL.resolvePermission = function resolvePermission(acls, req) {
|
|
100
|
+
if (!(req instanceof AccessRequest)) {
|
|
101
|
+
req.registry = this.registry;
|
|
102
|
+
req = new AccessRequest(req);
|
|
103
|
+
}
|
|
104
|
+
const scoredACLs = acls.map(function(acl, index) {
|
|
105
|
+
return {
|
|
106
|
+
acl,
|
|
107
|
+
index,
|
|
108
|
+
score: ACL.getMatchingScore(acl, req)
|
|
109
|
+
};
|
|
110
|
+
}).sort(function(rule1, rule2) {
|
|
111
|
+
return rule2.score - rule1.score || rule1.index - rule2.index;
|
|
112
|
+
});
|
|
113
|
+
let permission = ACL.DEFAULT;
|
|
114
|
+
for (let i = 0; i < scoredACLs.length; i++) {
|
|
115
|
+
const candidate = scoredACLs[i].acl;
|
|
116
|
+
if (scoredACLs[i].score < 0) break;
|
|
117
|
+
if (!req.isWildcard()) {
|
|
118
|
+
permission = candidate.permission;
|
|
119
|
+
break;
|
|
120
|
+
} else {
|
|
121
|
+
if (req.exactlyMatches(candidate)) {
|
|
122
|
+
permission = candidate.permission;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
if (AccessContext.permissionOrder[candidate.permission] > AccessContext.permissionOrder[permission]) {
|
|
126
|
+
permission = candidate.permission;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (debug.enabled) {
|
|
132
|
+
debug("The following ACLs were searched: ");
|
|
133
|
+
scoredACLs.forEach(function(result) {
|
|
134
|
+
result.acl.debug();
|
|
135
|
+
debug("with score:", result.score);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const res = new AccessRequest({
|
|
139
|
+
model: req.model,
|
|
140
|
+
property: req.property,
|
|
141
|
+
accessType: req.accessType,
|
|
142
|
+
permission: permission || ACL.DEFAULT,
|
|
143
|
+
registry: this.registry
|
|
144
|
+
});
|
|
145
|
+
res.settleDefaultPermission();
|
|
146
|
+
return res;
|
|
147
|
+
};
|
|
148
|
+
ACL.getStaticACLs = function getStaticACLs(model, property) {
|
|
149
|
+
const modelClass = this.registry.findModel(model);
|
|
150
|
+
const staticACLs = [];
|
|
151
|
+
if (modelClass && modelClass.settings.acls) modelClass.settings.acls.forEach(function(acl) {
|
|
152
|
+
let prop = acl.property;
|
|
153
|
+
if (Array.isArray(prop) && prop.indexOf(property) >= 0) prop = property;
|
|
154
|
+
if (!prop || prop === ACL.ALL || property === prop) staticACLs.push(new ACL({
|
|
155
|
+
model,
|
|
156
|
+
property: prop || ACL.ALL,
|
|
157
|
+
principalType: acl.principalType,
|
|
158
|
+
principalId: acl.principalId,
|
|
159
|
+
accessType: acl.accessType || ACL.ALL,
|
|
160
|
+
permission: acl.permission
|
|
161
|
+
}));
|
|
162
|
+
});
|
|
163
|
+
const prop = modelClass && (modelClass.definition.properties[property] || modelClass._scopeMeta && modelClass._scopeMeta[property] || modelClass[property] || modelClass.prototype[property]);
|
|
164
|
+
if (prop && prop.acls) prop.acls.forEach(function(acl) {
|
|
165
|
+
staticACLs.push(new ACL({
|
|
166
|
+
model: modelClass.modelName,
|
|
167
|
+
property,
|
|
168
|
+
principalType: acl.principalType,
|
|
169
|
+
principalId: acl.principalId,
|
|
170
|
+
accessType: acl.accessType,
|
|
171
|
+
permission: acl.permission
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
return staticACLs;
|
|
175
|
+
};
|
|
176
|
+
ACL.checkPermission = function checkPermission(principalType, principalId, model, property, accessType, callback) {
|
|
177
|
+
if (!callback) callback = utils.createPromiseCallback();
|
|
178
|
+
if (principalId !== null && principalId !== void 0 && typeof principalId !== "string") principalId = principalId.toString();
|
|
179
|
+
property = property || ACL.ALL;
|
|
180
|
+
const propertyQuery = property === ACL.ALL ? void 0 : { inq: [property, ACL.ALL] };
|
|
181
|
+
accessType = accessType || ACL.ALL;
|
|
182
|
+
const accessTypeQuery = accessType === ACL.ALL ? void 0 : { inq: [
|
|
183
|
+
accessType,
|
|
184
|
+
ACL.ALL,
|
|
185
|
+
ACL.EXECUTE
|
|
186
|
+
] };
|
|
187
|
+
const req = new AccessRequest({
|
|
188
|
+
model,
|
|
189
|
+
property,
|
|
190
|
+
accessType,
|
|
191
|
+
registry: this.registry
|
|
192
|
+
});
|
|
193
|
+
let acls = this.getStaticACLs(model, property);
|
|
194
|
+
let resolved = this.resolvePermission(acls, req);
|
|
195
|
+
if (resolved && resolved.permission === ACL.DENY) {
|
|
196
|
+
debug("Permission denied by statically resolved permission");
|
|
197
|
+
debug(" Resolved Permission: %j", resolved);
|
|
198
|
+
process.nextTick(function() {
|
|
199
|
+
callback(null, resolved);
|
|
200
|
+
});
|
|
201
|
+
return callback.promise;
|
|
202
|
+
}
|
|
203
|
+
this.find({ where: {
|
|
204
|
+
principalType,
|
|
205
|
+
principalId,
|
|
206
|
+
model,
|
|
207
|
+
property: propertyQuery,
|
|
208
|
+
accessType: accessTypeQuery
|
|
209
|
+
} }, (err, dynACLs) => {
|
|
210
|
+
if (err) return callback(err);
|
|
211
|
+
acls = acls.concat(dynACLs);
|
|
212
|
+
resolved = this.resolvePermission(acls, req);
|
|
213
|
+
return callback(null, resolved);
|
|
214
|
+
});
|
|
215
|
+
return callback.promise;
|
|
216
|
+
};
|
|
217
|
+
ACL.prototype.debug = function() {
|
|
218
|
+
if (debug.enabled) {
|
|
219
|
+
debug("---ACL---");
|
|
220
|
+
debug("model %s", this.model);
|
|
221
|
+
debug("property %s", this.property);
|
|
222
|
+
debug("principalType %s", this.principalType);
|
|
223
|
+
debug("principalId %s", this.principalId);
|
|
224
|
+
debug("accessType %s", this.accessType);
|
|
225
|
+
debug("permission %s", this.permission);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
ACL.isAllowed = function(permission, defaultPermission) {
|
|
229
|
+
if (permission === ACL.DEFAULT) permission = defaultPermission || ACL.ALLOW;
|
|
230
|
+
return permission !== ACL.DENY;
|
|
231
|
+
};
|
|
232
|
+
ACL.prototype.isAllowed = function(defaultPermission) {
|
|
233
|
+
return this.constructor.isAllowed(this.permission, defaultPermission);
|
|
234
|
+
};
|
|
235
|
+
ACL.checkAccessForContext = function(context, callback) {
|
|
236
|
+
if (!callback) callback = utils.createPromiseCallback();
|
|
237
|
+
this.resolveRelatedModels();
|
|
238
|
+
const roleModel = this.roleModel;
|
|
239
|
+
if (!(context instanceof AccessContext)) {
|
|
240
|
+
context.registry = this.registry;
|
|
241
|
+
context = new AccessContext(context);
|
|
242
|
+
}
|
|
243
|
+
let authorizedRoles = {};
|
|
244
|
+
const remotingContext = context.remotingContext;
|
|
245
|
+
const model = context.model;
|
|
246
|
+
const modelDefaultPermission = model && model.settings.defaultPermission;
|
|
247
|
+
const property = context.property;
|
|
248
|
+
const accessType = context.accessType;
|
|
249
|
+
const modelName = context.modelName;
|
|
250
|
+
const methodNames = context.methodNames;
|
|
251
|
+
const propertyQuery = property === ACL.ALL ? void 0 : { inq: methodNames.concat([ACL.ALL]) };
|
|
252
|
+
const accessTypeQuery = accessType === ACL.ALL ? void 0 : accessType === ACL.REPLICATE ? { inq: [
|
|
253
|
+
ACL.REPLICATE,
|
|
254
|
+
ACL.WRITE,
|
|
255
|
+
ACL.ALL
|
|
256
|
+
] } : { inq: [accessType, ACL.ALL] };
|
|
257
|
+
const req = new AccessRequest({
|
|
258
|
+
model: modelName,
|
|
259
|
+
property,
|
|
260
|
+
accessType,
|
|
261
|
+
permission: ACL.DEFAULT,
|
|
262
|
+
methodNames,
|
|
263
|
+
registry: this.registry
|
|
264
|
+
});
|
|
265
|
+
if (!context.isScopeAllowed()) {
|
|
266
|
+
req.permission = ACL.DENY;
|
|
267
|
+
debug("--Denied by scope config--");
|
|
268
|
+
debug("Scopes allowed:", context.accessToken.scopes || ctx.DEFAULT_SCOPES);
|
|
269
|
+
debug("Scope required:", context.getScopes());
|
|
270
|
+
context.debug();
|
|
271
|
+
callback(null, req);
|
|
272
|
+
return callback.promise;
|
|
273
|
+
}
|
|
274
|
+
const effectiveACLs = [];
|
|
275
|
+
const staticACLs = this.getStaticACLs(model.modelName, property);
|
|
276
|
+
const principalLookup = buildPrincipalLookup(context.principals);
|
|
277
|
+
const query = { where: {
|
|
278
|
+
model: { inq: [model.modelName, ACL.ALL] },
|
|
279
|
+
property: propertyQuery,
|
|
280
|
+
accessType: accessTypeQuery
|
|
281
|
+
} };
|
|
282
|
+
this.find(query, (err, acls) => {
|
|
283
|
+
if (err) return callback(err);
|
|
284
|
+
const roleCheckResults = Object.create(null);
|
|
285
|
+
const roleChecks = Object.create(null);
|
|
286
|
+
const inRoleChecks = [];
|
|
287
|
+
const pendingRoleACLs = [];
|
|
288
|
+
for (let i = 0; i < staticACLs.length; i++) acls.push(staticACLs[i]);
|
|
289
|
+
for (let i = 0; i < acls.length; i++) {
|
|
290
|
+
const acl = acls[i];
|
|
291
|
+
if (hasExactPrincipalMatch(principalLookup, acl)) {
|
|
292
|
+
effectiveACLs.push(acl);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (acl.principalType !== ACL.ROLE) continue;
|
|
296
|
+
pendingRoleACLs.push(acl);
|
|
297
|
+
if (!roleChecks[acl.principalId]) {
|
|
298
|
+
const principalId = acl.principalId;
|
|
299
|
+
roleChecks[principalId] = utils.invokeWithCallback(roleModel.isInRole, roleModel, [principalId, context]).then(function(inRole) {
|
|
300
|
+
roleCheckResults[principalId] = inRole;
|
|
301
|
+
return inRole;
|
|
302
|
+
});
|
|
303
|
+
inRoleChecks.push(roleChecks[principalId]);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
Promise.all(inRoleChecks).then(() => {
|
|
307
|
+
for (let i = 0; i < pendingRoleACLs.length; i++) {
|
|
308
|
+
const acl = pendingRoleACLs[i];
|
|
309
|
+
if (!roleCheckResults[acl.principalId]) continue;
|
|
310
|
+
effectiveACLs.push(acl);
|
|
311
|
+
if (acl.isAllowed(modelDefaultPermission)) authorizedRoles[acl.principalId] = true;
|
|
312
|
+
}
|
|
313
|
+
const resolved = this.resolvePermission(effectiveACLs, req);
|
|
314
|
+
debug("---Resolved---");
|
|
315
|
+
resolved.debug();
|
|
316
|
+
authorizedRoles = resolved.isAllowed() ? authorizedRoles : {};
|
|
317
|
+
saveAuthorizedRolesToRemotingContext(remotingContext, authorizedRoles);
|
|
318
|
+
return callback(null, resolved);
|
|
319
|
+
}).catch(function(error) {
|
|
320
|
+
return callback(error, null);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
return callback.promise;
|
|
324
|
+
};
|
|
325
|
+
function buildPrincipalLookup(principals) {
|
|
326
|
+
const lookup = Object.create(null);
|
|
327
|
+
for (let i = 0; i < principals.length; i++) {
|
|
328
|
+
const principal = principals[i];
|
|
329
|
+
lookup[getPrincipalLookupKey(principal.type, principal.id)] = true;
|
|
330
|
+
}
|
|
331
|
+
return lookup;
|
|
332
|
+
}
|
|
333
|
+
function hasExactPrincipalMatch(principalLookup, acl) {
|
|
334
|
+
return principalLookup[getPrincipalLookupKey(acl.principalType, acl.principalId)] === true;
|
|
335
|
+
}
|
|
336
|
+
function getPrincipalLookupKey(type, id) {
|
|
337
|
+
return String(type) + ":" + String(id);
|
|
338
|
+
}
|
|
339
|
+
function saveAuthorizedRolesToRemotingContext(remotingContext, authorizedRoles) {
|
|
340
|
+
const options = remotingContext && remotingContext.args && remotingContext.args.options;
|
|
341
|
+
if (options && typeof options === "object") options.authorizedRoles = authorizedRoles;
|
|
342
|
+
}
|
|
343
|
+
ACL.checkAccessForToken = function(token, model, modelId, method, callback) {
|
|
344
|
+
assert(token, "Access token is required");
|
|
345
|
+
if (!callback) callback = utils.createPromiseCallback();
|
|
346
|
+
const context = new AccessContext({
|
|
347
|
+
registry: this.registry,
|
|
348
|
+
accessToken: token,
|
|
349
|
+
model,
|
|
350
|
+
property: method,
|
|
351
|
+
method,
|
|
352
|
+
modelId
|
|
353
|
+
});
|
|
354
|
+
this.checkAccessForContext(context, function(err, accessRequest) {
|
|
355
|
+
if (err) callback(err);
|
|
356
|
+
else callback(null, accessRequest.isAllowed());
|
|
357
|
+
});
|
|
358
|
+
return callback.promise;
|
|
359
|
+
};
|
|
360
|
+
ACL.resolveRelatedModels = function() {
|
|
361
|
+
if (!this.roleModel) {
|
|
362
|
+
const reg = this.registry;
|
|
363
|
+
this.roleModel = reg.getModelByType("Role");
|
|
364
|
+
this.roleMappingModel = reg.getModelByType("RoleMapping");
|
|
365
|
+
this.userModel = reg.getModelByType("User");
|
|
366
|
+
this.applicationModel = reg.getModelByType("Application");
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
ACL.resolvePrincipal = function(type, id, cb) {
|
|
370
|
+
cb = cb || utils.createPromiseCallback();
|
|
371
|
+
type = type || ACL.ROLE;
|
|
372
|
+
this.resolveRelatedModels();
|
|
373
|
+
switch (type) {
|
|
374
|
+
case ACL.ROLE:
|
|
375
|
+
this.roleModel.findOne({ where: { or: [{ name: id }, { id }] } }, cb);
|
|
376
|
+
break;
|
|
377
|
+
case ACL.USER:
|
|
378
|
+
this.userModel.findOne({ where: { or: [
|
|
379
|
+
{ username: id },
|
|
380
|
+
{ email: id },
|
|
381
|
+
{ id }
|
|
382
|
+
] } }, cb);
|
|
383
|
+
break;
|
|
384
|
+
case ACL.APP:
|
|
385
|
+
this.applicationModel.findOne({ where: { or: [
|
|
386
|
+
{ name: id },
|
|
387
|
+
{ email: id },
|
|
388
|
+
{ id }
|
|
389
|
+
] } }, cb);
|
|
390
|
+
break;
|
|
391
|
+
default:
|
|
392
|
+
const userModel = this.registry.findModel(type);
|
|
393
|
+
if (userModel) userModel.findOne({ where: { or: [
|
|
394
|
+
{ username: id },
|
|
395
|
+
{ email: id },
|
|
396
|
+
{ id }
|
|
397
|
+
] } }, cb);
|
|
398
|
+
else process.nextTick(function() {
|
|
399
|
+
const err = new Error(g.f("Invalid principal type: %s", type));
|
|
400
|
+
err.statusCode = 400;
|
|
401
|
+
err.code = "INVALID_PRINCIPAL_TYPE";
|
|
402
|
+
cb(err);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return cb.promise;
|
|
406
|
+
};
|
|
407
|
+
ACL.isMappedToRole = function(principalType, principalId, role, cb) {
|
|
408
|
+
cb = cb || utils.createPromiseCallback();
|
|
409
|
+
(async () => {
|
|
410
|
+
const principal = await utils.invokeWithCallback(this.resolvePrincipal, this, [principalType, principalId]);
|
|
411
|
+
if (principal != null) principalId = principal.id;
|
|
412
|
+
principalType = principalType || "ROLE";
|
|
413
|
+
const resolvedRole = await utils.invokeWithCallback(this.resolvePrincipal, this, ["ROLE", role]);
|
|
414
|
+
if (!resolvedRole) return null;
|
|
415
|
+
return utils.invokeWithCallback(this.roleMappingModel.findOne, this.roleMappingModel, [{ where: {
|
|
416
|
+
roleId: resolvedRole.id,
|
|
417
|
+
principalType,
|
|
418
|
+
principalId: String(principalId)
|
|
419
|
+
} }]);
|
|
420
|
+
})().then(function(result) {
|
|
421
|
+
cb(null, !!result);
|
|
422
|
+
}, cb);
|
|
423
|
+
return cb.promise;
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
}));
|
|
427
|
+
//#endregion
|
|
428
|
+
module.exports = require_acl();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region common/models/acl.json
|
|
2
|
+
var require_acl = /* @__PURE__ */ require("../../_virtual/_rolldown/runtime.cjs").__commonJSMin(((exports, module) => {
|
|
3
|
+
module.exports = {
|
|
4
|
+
"name": "ACL",
|
|
5
|
+
"properties": {
|
|
6
|
+
"model": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The name of the model"
|
|
9
|
+
},
|
|
10
|
+
"property": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "The name of the property, method, scope, or relation"
|
|
13
|
+
},
|
|
14
|
+
"accessType": "string",
|
|
15
|
+
"permission": "string",
|
|
16
|
+
"principalType": "string",
|
|
17
|
+
"principalId": "string"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}));
|
|
21
|
+
//#endregion
|
|
22
|
+
Object.defineProperty(exports, "default", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
get: function() {
|
|
25
|
+
return require_acl();
|
|
26
|
+
}
|
|
27
|
+
});
|