@proteinjs/db 1.44.0 → 1.45.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/CHANGELOG.md +11 -0
- package/dist/src/auth/TableAuth.d.ts +11 -1
- package/dist/src/auth/TableAuth.d.ts.map +1 -1
- package/dist/src/auth/TableAuth.js +39 -13
- package/dist/src/auth/TableAuth.js.map +1 -1
- package/dist/test/TableAuthIdentity.test.d.ts +2 -0
- package/dist/test/TableAuthIdentity.test.d.ts.map +1 -0
- package/dist/test/TableAuthIdentity.test.js +110 -0
- package/dist/test/TableAuthIdentity.test.js.map +1 -0
- package/package.json +2 -2
- package/src/auth/TableAuth.ts +45 -20
- package/test/TableAuthIdentity.test.ts +115 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.45.0](https://github.com/proteinjs/db/compare/@proteinjs/db@1.44.0...@proteinjs/db@1.45.0) (2026-09-03)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **db-ui:** declared record-form panels — RecordPanel + the one-loader record page (plans/USAGE_SURFACES.md §B.1, US-1/US-2) ([dc72f63](https://github.com/proteinjs/db/commit/dc72f63ea25296ebaec22f9b06ed10b88c932b11))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [1.44.0](https://github.com/proteinjs/db/compare/@proteinjs/db@1.43.0...@proteinjs/db@1.44.0) (2026-09-02)
|
|
7
18
|
|
|
8
19
|
|
|
@@ -46,10 +46,20 @@ export declare class TableAuth {
|
|
|
46
46
|
* reading UserAuth documents for a missing repo, applied to a capability READ.
|
|
47
47
|
*/
|
|
48
48
|
canPerform(table: Table<any>, operation: 'query' | 'insert' | 'update' | 'delete', api?: 'db' | 'service'): boolean;
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Non-throwing read of ONE declared identity against the CURRENT user — the same grammar the
|
|
51
|
+
* table doors speak (`'public' | 'authenticated' | roles[] | { permission }`), for declared
|
|
52
|
+
* UI affordances that carry their own grant (a record form's declared panels, `Table.auth.ui`).
|
|
53
|
+
* An UNDECLARED identity is the default-deny made explicit: admin only. Fails closed (false)
|
|
54
|
+
* when no user can be resolved, like `canPerform`.
|
|
55
|
+
*/
|
|
56
|
+
identityAllows(identity: Identity | undefined): boolean;
|
|
50
57
|
canQuery(table: Table<any>, api?: 'db' | 'service'): void;
|
|
51
58
|
canInsert(table: Table<any>, api?: 'db' | 'service'): void;
|
|
52
59
|
canUpdate(table: Table<any>, api?: 'db' | 'service'): void;
|
|
53
60
|
canDelete(table: Table<any>, api?: 'db' | 'service'): void;
|
|
61
|
+
private canAccess;
|
|
62
|
+
/** One identity against the current user; undefined (nothing declared) is not a grant. */
|
|
63
|
+
private allows;
|
|
54
64
|
}
|
|
55
65
|
//# sourceMappingURL=TableAuth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableAuth.d.ts","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,EAAE,GAAG,kBAAkB,CAAC;AAKlF;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB;;;;;;;;;OASG;IACH,UAAU,CACR,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,SAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACnD,GAAG,GAAE,IAAI,GAAG,SAAgB,GAC3B,OAAO;IAQV,
|
|
1
|
+
{"version":3,"file":"TableAuth.d.ts","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,EAAE,GAAG,kBAAkB,CAAC;AAKlF;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB;;;;;;;;;OASG;IACH,UAAU,CACR,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,SAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACnD,GAAG,GAAE,IAAI,GAAG,SAAgB,GAC3B,OAAO;IAQV;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO;IAQvD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAM/D,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,GAAE,IAAI,GAAG,SAAgB,GAAG,IAAI;IAMhE,OAAO,CAAC,SAAS;IAgBjB,0FAA0F;IAC1F,OAAO,CAAC,MAAM;CAef"}
|
|
@@ -61,20 +61,20 @@ var TableAuth = /** @class */ (function () {
|
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Non-throwing read of ONE declared identity against the CURRENT user — the same grammar the
|
|
66
|
+
* table doors speak (`'public' | 'authenticated' | roles[] | { permission }`), for declared
|
|
67
|
+
* UI affordances that carry their own grant (a record form's declared panels, `Table.auth.ui`).
|
|
68
|
+
* An UNDECLARED identity is the default-deny made explicit: admin only. Fails closed (false)
|
|
69
|
+
* when no user can be resolved, like `canPerform`.
|
|
70
|
+
*/
|
|
71
|
+
TableAuth.prototype.identityAllows = function (identity) {
|
|
72
|
+
try {
|
|
73
|
+
return identity === undefined ? user_auth_1.UserAuth.hasRole('admin') : this.allows(identity);
|
|
74
|
+
}
|
|
75
|
+
catch (_a) {
|
|
76
|
+
return false;
|
|
68
77
|
}
|
|
69
|
-
return (tableAuth.all === 'public' ||
|
|
70
|
-
tableAuth[operation] === 'public' ||
|
|
71
|
-
(tableAuth.all === 'authenticated' && user_auth_1.UserAuth.isLoggedIn()) ||
|
|
72
|
-
(tableAuth[operation] === 'authenticated' && user_auth_1.UserAuth.isLoggedIn()) ||
|
|
73
|
-
(Array.isArray(tableAuth.all) && user_auth_1.UserAuth.hasRoles(tableAuth.all, 'at least one')) ||
|
|
74
|
-
(Array.isArray(tableAuth[operation]) && user_auth_1.UserAuth.hasRoles(tableAuth[operation], 'at least one')) ||
|
|
75
|
-
(isPermissionIdentity(tableAuth.all) && user_auth_1.UserAuth.hasPermission(tableAuth.all.permission)) ||
|
|
76
|
-
(isPermissionIdentity(tableAuth[operation]) &&
|
|
77
|
-
user_auth_1.UserAuth.hasPermission(tableAuth[operation].permission)));
|
|
78
78
|
};
|
|
79
79
|
TableAuth.prototype.canQuery = function (table, api) {
|
|
80
80
|
if (api === void 0) { api = 'db'; }
|
|
@@ -100,6 +100,32 @@ var TableAuth = /** @class */ (function () {
|
|
|
100
100
|
throw new TableAuthError("User is not authorized to delete records from table: ".concat(table.name));
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
+
TableAuth.prototype.canAccess = function (table, api, operation) {
|
|
104
|
+
var tableAuth = table.auth ? table.auth[api] : undefined;
|
|
105
|
+
if (!tableAuth || Object.keys(tableAuth).length == 0) {
|
|
106
|
+
return user_auth_1.UserAuth.hasRole('admin');
|
|
107
|
+
}
|
|
108
|
+
// Within a declared block an undeclared operation stays closed to everyone (the block is
|
|
109
|
+
// the whole declaration) — `allows` reads undefined as false, so the two doors are exactly
|
|
110
|
+
// the ones the table wrote down.
|
|
111
|
+
return this.allows(tableAuth.all) || this.allows(tableAuth[operation]);
|
|
112
|
+
};
|
|
113
|
+
/** One identity against the current user; undefined (nothing declared) is not a grant. */
|
|
114
|
+
TableAuth.prototype.allows = function (identity) {
|
|
115
|
+
if (identity === 'public') {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (identity === 'authenticated') {
|
|
119
|
+
return user_auth_1.UserAuth.isLoggedIn();
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(identity)) {
|
|
122
|
+
return user_auth_1.UserAuth.hasRoles(identity, 'at least one');
|
|
123
|
+
}
|
|
124
|
+
if (isPermissionIdentity(identity)) {
|
|
125
|
+
return user_auth_1.UserAuth.hasPermission(identity.permission);
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
};
|
|
103
129
|
return TableAuth;
|
|
104
130
|
}());
|
|
105
131
|
exports.TableAuth = TableAuth;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableAuth.js","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAgD;AAehD,IAAM,oBAAoB,GAAG,UAAC,QAA8B;IAC1D,OAAA,CAAC,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,QAAQ,CAAC,UAAU,KAAK,QAAQ;AAAjH,CAAiH,CAAC;AAcpH;;;;;GAKG;AACH;IAAoC,kCAAK;IACvC,wBAAY,OAAe;QAA3B,YACE,kBAAM,OAAO,CAAC,SAEf;QADC,KAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;;IAC/B,CAAC;IACH,qBAAC;AAAD,CAAC,AALD,CAAoC,KAAK,GAKxC;AALY,wCAAc;AAO3B;;GAEG;AACH;IAAA;
|
|
1
|
+
{"version":3,"file":"TableAuth.js","sourceRoot":"","sources":["../../../src/auth/TableAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TableAuthIdentity.test.d.ts","sourceRoot":"","sources":["../../test/TableAuthIdentity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
var user_auth_1 = require("@proteinjs/user-auth");
|
|
19
|
+
var Table_1 = require("../src/Table");
|
|
20
|
+
var Record_1 = require("../src/Record");
|
|
21
|
+
var Columns_1 = require("../src/Columns");
|
|
22
|
+
var TableAuth_1 = require("../src/auth/TableAuth");
|
|
23
|
+
var QueryOnlyTable = /** @class */ (function (_super) {
|
|
24
|
+
__extends(QueryOnlyTable, _super);
|
|
25
|
+
function QueryOnlyTable() {
|
|
26
|
+
var _this = _super !== null && _super.apply(this, arguments) || this;
|
|
27
|
+
_this.name = 'identity_query_only_test';
|
|
28
|
+
_this.auth = {
|
|
29
|
+
db: { query: 'authenticated' },
|
|
30
|
+
};
|
|
31
|
+
_this.columns = (0, Record_1.withRecordColumns)({ title: new Columns_1.StringColumn('title') });
|
|
32
|
+
return _this;
|
|
33
|
+
}
|
|
34
|
+
return QueryOnlyTable;
|
|
35
|
+
}(Table_1.Table));
|
|
36
|
+
var setUser = function (roles, email) {
|
|
37
|
+
if (email === void 0) { email = 'user@test.local'; }
|
|
38
|
+
user_auth_1.UserAuth.userRepo = { getUser: function () { return ({ email: email, roles: roles }); } };
|
|
39
|
+
};
|
|
40
|
+
var setGuest = function () {
|
|
41
|
+
user_auth_1.UserAuth.userRepo = { getUser: function () { return ({ email: 'guest', roles: [] }); } };
|
|
42
|
+
};
|
|
43
|
+
var setMapping = function (mapping) {
|
|
44
|
+
user_auth_1.UserAuth.permissionRolesMapping = { getRoles: function (permission) { return mapping[permission]; } };
|
|
45
|
+
};
|
|
46
|
+
describe('TableAuth.identityAllows', function () {
|
|
47
|
+
beforeEach(function () {
|
|
48
|
+
setMapping({ usage: ['usage'] });
|
|
49
|
+
});
|
|
50
|
+
afterEach(function () {
|
|
51
|
+
user_auth_1.UserAuth.userRepo = undefined;
|
|
52
|
+
user_auth_1.UserAuth.permissionRolesMapping = undefined;
|
|
53
|
+
});
|
|
54
|
+
test("'public' admits anyone, including a guest", function () {
|
|
55
|
+
setGuest();
|
|
56
|
+
expect(new TableAuth_1.TableAuth().identityAllows('public')).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
test("'authenticated' admits a signed-in user and refuses a guest", function () {
|
|
59
|
+
setUser([]);
|
|
60
|
+
expect(new TableAuth_1.TableAuth().identityAllows('authenticated')).toBe(true);
|
|
61
|
+
setGuest();
|
|
62
|
+
expect(new TableAuth_1.TableAuth().identityAllows('authenticated')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
test('a roles list admits a holder of at least one role', function () {
|
|
65
|
+
setUser(['ops']);
|
|
66
|
+
expect(new TableAuth_1.TableAuth().identityAllows(['users', 'ops'])).toBe(true);
|
|
67
|
+
setUser(['dev']);
|
|
68
|
+
expect(new TableAuth_1.TableAuth().identityAllows(['users', 'ops'])).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
test('a permission identity resolves through the consumer mapping; admin is break-glass', function () {
|
|
71
|
+
setUser(['usage']);
|
|
72
|
+
expect(new TableAuth_1.TableAuth().identityAllows({ permission: 'usage' })).toBe(true);
|
|
73
|
+
setUser(['users']);
|
|
74
|
+
expect(new TableAuth_1.TableAuth().identityAllows({ permission: 'usage' })).toBe(false);
|
|
75
|
+
setUser(['admin']);
|
|
76
|
+
expect(new TableAuth_1.TableAuth().identityAllows({ permission: 'usage' })).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
test('an undeclared identity is admin-only, never open', function () {
|
|
79
|
+
setUser(['usage', 'users', 'ops']);
|
|
80
|
+
expect(new TableAuth_1.TableAuth().identityAllows(undefined)).toBe(false);
|
|
81
|
+
setUser(['admin']);
|
|
82
|
+
expect(new TableAuth_1.TableAuth().identityAllows(undefined)).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
test('fails closed when no user can be resolved', function () {
|
|
85
|
+
user_auth_1.UserAuth.userRepo = {
|
|
86
|
+
getUser: function () {
|
|
87
|
+
throw new Error('no session');
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
expect(new TableAuth_1.TableAuth().identityAllows('authenticated')).toBe(false);
|
|
91
|
+
expect(new TableAuth_1.TableAuth().identityAllows({ permission: 'usage' })).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('TableAuth operation doors keep their declared-block semantics', function () {
|
|
95
|
+
afterEach(function () {
|
|
96
|
+
user_auth_1.UserAuth.userRepo = undefined;
|
|
97
|
+
user_auth_1.UserAuth.permissionRolesMapping = undefined;
|
|
98
|
+
});
|
|
99
|
+
test('a declared operation opens to its identity', function () {
|
|
100
|
+
setUser([]);
|
|
101
|
+
expect(new TableAuth_1.TableAuth().canPerform(new QueryOnlyTable(), 'query')).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
test('within a declared block an undeclared operation stays closed — admin included', function () {
|
|
104
|
+
setUser(['admin']);
|
|
105
|
+
expect(new TableAuth_1.TableAuth().canPerform(new QueryOnlyTable(), 'insert')).toBe(false);
|
|
106
|
+
setUser([]);
|
|
107
|
+
expect(new TableAuth_1.TableAuth().canPerform(new QueryOnlyTable(), 'insert')).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=TableAuthIdentity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TableAuthIdentity.test.js","sourceRoot":"","sources":["../../test/TableAuthIdentity.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"main": "./dist/generated/index.js",
|
|
5
5
|
"types": "./dist/generated/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"ts-jest": "29.1.1",
|
|
67
67
|
"typescript": "5.2.2"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "135045a9e4b16a75ea11f610a82fa7ff60751e8d"
|
|
70
70
|
}
|
package/src/auth/TableAuth.ts
CHANGED
|
@@ -67,27 +67,19 @@ export class TableAuth {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Non-throwing read of ONE declared identity against the CURRENT user — the same grammar the
|
|
72
|
+
* table doors speak (`'public' | 'authenticated' | roles[] | { permission }`), for declared
|
|
73
|
+
* UI affordances that carry their own grant (a record form's declared panels, `Table.auth.ui`).
|
|
74
|
+
* An UNDECLARED identity is the default-deny made explicit: admin only. Fails closed (false)
|
|
75
|
+
* when no user can be resolved, like `canPerform`.
|
|
76
|
+
*/
|
|
77
|
+
identityAllows(identity: Identity | undefined): boolean {
|
|
78
|
+
try {
|
|
79
|
+
return identity === undefined ? UserAuth.hasRole('admin') : this.allows(identity);
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
78
82
|
}
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
tableAuth.all === 'public' ||
|
|
82
|
-
tableAuth[operation] === 'public' ||
|
|
83
|
-
(tableAuth.all === 'authenticated' && UserAuth.isLoggedIn()) ||
|
|
84
|
-
(tableAuth[operation] === 'authenticated' && UserAuth.isLoggedIn()) ||
|
|
85
|
-
(Array.isArray(tableAuth.all) && UserAuth.hasRoles(tableAuth.all, 'at least one')) ||
|
|
86
|
-
(Array.isArray(tableAuth[operation]) && UserAuth.hasRoles(tableAuth[operation] as string[], 'at least one')) ||
|
|
87
|
-
(isPermissionIdentity(tableAuth.all) && UserAuth.hasPermission(tableAuth.all.permission)) ||
|
|
88
|
-
(isPermissionIdentity(tableAuth[operation]) &&
|
|
89
|
-
UserAuth.hasPermission((tableAuth[operation] as PermissionIdentity).permission))
|
|
90
|
-
);
|
|
91
83
|
}
|
|
92
84
|
|
|
93
85
|
canQuery(table: Table<any>, api: 'db' | 'service' = 'db'): void {
|
|
@@ -113,4 +105,37 @@ export class TableAuth {
|
|
|
113
105
|
throw new TableAuthError(`User is not authorized to delete records from table: ${table.name}`);
|
|
114
106
|
}
|
|
115
107
|
}
|
|
108
|
+
|
|
109
|
+
private canAccess(
|
|
110
|
+
table: Table<any>,
|
|
111
|
+
api: 'db' | 'service',
|
|
112
|
+
operation: 'query' | 'insert' | 'update' | 'delete'
|
|
113
|
+
): boolean {
|
|
114
|
+
const tableAuth = table.auth ? table.auth[api] : undefined;
|
|
115
|
+
if (!tableAuth || Object.keys(tableAuth).length == 0) {
|
|
116
|
+
return UserAuth.hasRole('admin');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Within a declared block an undeclared operation stays closed to everyone (the block is
|
|
120
|
+
// the whole declaration) — `allows` reads undefined as false, so the two doors are exactly
|
|
121
|
+
// the ones the table wrote down.
|
|
122
|
+
return this.allows(tableAuth.all) || this.allows(tableAuth[operation]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** One identity against the current user; undefined (nothing declared) is not a grant. */
|
|
126
|
+
private allows(identity: Identity | undefined): boolean {
|
|
127
|
+
if (identity === 'public') {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (identity === 'authenticated') {
|
|
131
|
+
return UserAuth.isLoggedIn();
|
|
132
|
+
}
|
|
133
|
+
if (Array.isArray(identity)) {
|
|
134
|
+
return UserAuth.hasRoles(identity, 'at least one');
|
|
135
|
+
}
|
|
136
|
+
if (isPermissionIdentity(identity)) {
|
|
137
|
+
return UserAuth.hasPermission(identity.permission);
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
116
141
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { UserAuth } from '@proteinjs/user-auth';
|
|
2
|
+
import { Table } from '../src/Table';
|
|
3
|
+
import { withRecordColumns, Record } from '../src/Record';
|
|
4
|
+
import { StringColumn } from '../src/Columns';
|
|
5
|
+
import { TableAuth } from '../src/auth/TableAuth';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `TableAuth.identityAllows` — the one identity grammar (`'public' | 'authenticated' | roles[] |
|
|
9
|
+
* { permission }`) read for a DECLARED UI affordance that carries its own grant (a record form's
|
|
10
|
+
* declared panels, `Table.auth.ui`). Outcomes pinned:
|
|
11
|
+
* - each identity form resolves against the current user; 'admin' is break-glass throughout;
|
|
12
|
+
* - an UNDECLARED identity is admin-only (default-deny made explicit), never open;
|
|
13
|
+
* - it fails CLOSED when no user can be resolved;
|
|
14
|
+
* - the operation doors (`canPerform`) are byte-for-byte the pre-refactor semantics: within a
|
|
15
|
+
* declared block an undeclared operation stays closed to everyone, admin included.
|
|
16
|
+
*
|
|
17
|
+
* `UserAuth` reads from a static repo; tests stub it directly per identity — no server needed.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface Doc extends Record {
|
|
21
|
+
title: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class QueryOnlyTable extends Table<Doc> {
|
|
25
|
+
public name = 'identity_query_only_test';
|
|
26
|
+
public auth: Table<Doc>['auth'] = {
|
|
27
|
+
db: { query: 'authenticated' },
|
|
28
|
+
};
|
|
29
|
+
public columns = withRecordColumns<Doc>({ title: new StringColumn('title') });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const setUser = (roles: string[], email = 'user@test.local') => {
|
|
33
|
+
(UserAuth as any).userRepo = { getUser: () => ({ email, roles }) };
|
|
34
|
+
};
|
|
35
|
+
const setGuest = () => {
|
|
36
|
+
(UserAuth as any).userRepo = { getUser: () => ({ email: 'guest', roles: [] }) };
|
|
37
|
+
};
|
|
38
|
+
const setMapping = (mapping: { [permission: string]: string[] }) => {
|
|
39
|
+
(UserAuth as any).permissionRolesMapping = { getRoles: (permission: string) => mapping[permission] };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe('TableAuth.identityAllows', () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
setMapping({ usage: ['usage'] });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
(UserAuth as any).userRepo = undefined;
|
|
49
|
+
(UserAuth as any).permissionRolesMapping = undefined;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("'public' admits anyone, including a guest", () => {
|
|
53
|
+
setGuest();
|
|
54
|
+
expect(new TableAuth().identityAllows('public')).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("'authenticated' admits a signed-in user and refuses a guest", () => {
|
|
58
|
+
setUser([]);
|
|
59
|
+
expect(new TableAuth().identityAllows('authenticated')).toBe(true);
|
|
60
|
+
setGuest();
|
|
61
|
+
expect(new TableAuth().identityAllows('authenticated')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('a roles list admits a holder of at least one role', () => {
|
|
65
|
+
setUser(['ops']);
|
|
66
|
+
expect(new TableAuth().identityAllows(['users', 'ops'])).toBe(true);
|
|
67
|
+
setUser(['dev']);
|
|
68
|
+
expect(new TableAuth().identityAllows(['users', 'ops'])).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('a permission identity resolves through the consumer mapping; admin is break-glass', () => {
|
|
72
|
+
setUser(['usage']);
|
|
73
|
+
expect(new TableAuth().identityAllows({ permission: 'usage' })).toBe(true);
|
|
74
|
+
setUser(['users']);
|
|
75
|
+
expect(new TableAuth().identityAllows({ permission: 'usage' })).toBe(false);
|
|
76
|
+
setUser(['admin']);
|
|
77
|
+
expect(new TableAuth().identityAllows({ permission: 'usage' })).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('an undeclared identity is admin-only, never open', () => {
|
|
81
|
+
setUser(['usage', 'users', 'ops']);
|
|
82
|
+
expect(new TableAuth().identityAllows(undefined)).toBe(false);
|
|
83
|
+
setUser(['admin']);
|
|
84
|
+
expect(new TableAuth().identityAllows(undefined)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('fails closed when no user can be resolved', () => {
|
|
88
|
+
(UserAuth as any).userRepo = {
|
|
89
|
+
getUser: () => {
|
|
90
|
+
throw new Error('no session');
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
expect(new TableAuth().identityAllows('authenticated')).toBe(false);
|
|
94
|
+
expect(new TableAuth().identityAllows({ permission: 'usage' })).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('TableAuth operation doors keep their declared-block semantics', () => {
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
(UserAuth as any).userRepo = undefined;
|
|
101
|
+
(UserAuth as any).permissionRolesMapping = undefined;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('a declared operation opens to its identity', () => {
|
|
105
|
+
setUser([]);
|
|
106
|
+
expect(new TableAuth().canPerform(new QueryOnlyTable(), 'query')).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('within a declared block an undeclared operation stays closed — admin included', () => {
|
|
110
|
+
setUser(['admin']);
|
|
111
|
+
expect(new TableAuth().canPerform(new QueryOnlyTable(), 'insert')).toBe(false);
|
|
112
|
+
setUser([]);
|
|
113
|
+
expect(new TableAuth().canPerform(new QueryOnlyTable(), 'insert')).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
});
|