@proteinjs/db 1.40.0 → 1.41.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/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/generated/test/index.js +1 -1
- package/dist/generated/test/index.js.map +1 -1
- package/dist/src/Table.d.ts +19 -0
- package/dist/src/Table.d.ts.map +1 -1
- package/dist/src/Table.js.map +1 -1
- package/dist/src/auth/TableAuth.d.ts +11 -0
- package/dist/src/auth/TableAuth.d.ts.map +1 -1
- package/dist/src/auth/TableAuth.js +19 -0
- package/dist/src/auth/TableAuth.js.map +1 -1
- package/dist/src/tables/MigrationTable.d.ts +12 -2
- package/dist/src/tables/MigrationTable.d.ts.map +1 -1
- package/dist/src/tables/MigrationTable.js +18 -4
- package/dist/src/tables/MigrationTable.js.map +1 -1
- package/dist/test/MigrationAuth.test.js +30 -0
- package/dist/test/MigrationAuth.test.js.map +1 -1
- package/generated/index.ts +1 -1
- package/generated/test/index.ts +1 -1
- package/package.json +2 -2
- package/src/Table.ts +19 -0
- package/src/auth/TableAuth.ts +22 -0
- package/src/tables/MigrationTable.ts +18 -4
- package/test/MigrationAuth.test.ts +25 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.41.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": "149e538c8a05858b6eb7d3a2dc362e5ff196caf9"
|
|
70
70
|
}
|
package/src/Table.ts
CHANGED
|
@@ -115,6 +115,25 @@ export abstract class Table<T extends Record> implements Loadable, CustomSeriali
|
|
|
115
115
|
public cascadeDeleteReferences: () => { table: string; referenceColumn: string }[] = () => [];
|
|
116
116
|
/** Options for configuring SourceRecords (see {@link SourceRecordOptions}) */
|
|
117
117
|
public sourceRecordOptions: SourceRecordOptions<T> = {};
|
|
118
|
+
/**
|
|
119
|
+
* Presentation the table declares for the GENERIC record surfaces (@proteinjs/db-ui) — the
|
|
120
|
+
* framework renders what tables declare; per-table display choices never hard-code into the
|
|
121
|
+
* generic components. Distinct from `auth.ui`, which gates WHO may see those surfaces.
|
|
122
|
+
*/
|
|
123
|
+
public ui?: {
|
|
124
|
+
recordTable?: {
|
|
125
|
+
/**
|
|
126
|
+
* The columns rendered as the record table's row columns, in this order. `created` and
|
|
127
|
+
* `updated` are appended automatically (the record family's shared face) unless already
|
|
128
|
+
* listed. Undeclared tables get the meaningful-data default pick
|
|
129
|
+
* (db-ui `defaultRecordTableColumns`). Declare when the default pick misses a column a
|
|
130
|
+
* human scans for (e.g. the migration ledger's `duration`) or surfaces one that has no
|
|
131
|
+
* business in a row scan (e.g. an invite's redeemable `token` — row hygiene; the record
|
|
132
|
+
* form still carries the full record).
|
|
133
|
+
*/
|
|
134
|
+
columns?: (keyof T & string)[];
|
|
135
|
+
};
|
|
136
|
+
};
|
|
118
137
|
public auth?: {
|
|
119
138
|
db?: TableOperationsAuth;
|
|
120
139
|
service?: TableOperationsAuth;
|
package/src/auth/TableAuth.ts
CHANGED
|
@@ -45,6 +45,28 @@ export class TableAuthError extends Error {
|
|
|
45
45
|
* Util to check which table operations a user can perform
|
|
46
46
|
*/
|
|
47
47
|
export class TableAuth {
|
|
48
|
+
/**
|
|
49
|
+
* Non-throwing capability read: whether the CURRENT user clears this operation's declared
|
|
50
|
+
* door on this api. The seam UI derivation rides (db-ui's record-table affordances render
|
|
51
|
+
* only the operations the declaration actually opens — a table with no insert door draws no
|
|
52
|
+
* create button); the throwing guards below stay the enforcement path.
|
|
53
|
+
*
|
|
54
|
+
* Answers false — never throws — when the auth machinery itself cannot resolve a user (e.g.
|
|
55
|
+
* a context whose source graph never loaded `AuthenticatedUserRepo`): the same fail-closed
|
|
56
|
+
* reading UserAuth documents for a missing repo, applied to a capability READ.
|
|
57
|
+
*/
|
|
58
|
+
canPerform(
|
|
59
|
+
table: Table<any>,
|
|
60
|
+
operation: 'query' | 'insert' | 'update' | 'delete',
|
|
61
|
+
api: 'db' | 'service' = 'db'
|
|
62
|
+
): boolean {
|
|
63
|
+
try {
|
|
64
|
+
return this.canAccess(table, api, operation);
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
private canAccess(
|
|
49
71
|
table: Table<any>,
|
|
50
72
|
api: 'db' | 'service',
|
|
@@ -58,14 +58,28 @@ export interface Migration extends SourceRecord {
|
|
|
58
58
|
export class MigrationTable extends Table<Migration> {
|
|
59
59
|
public name = 'migration';
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
61
|
+
* The doors ride the 'dev' permission (consumer-mapped; admin passes as break-glass),
|
|
62
62
|
* matching MigrationRunner's serviceMetadata: the Migrations record table reads/edits via
|
|
63
63
|
* the service api, and the runner writes run state via the db api as the calling user.
|
|
64
|
-
*
|
|
64
|
+
* INSERT deliberately has no door — for anyone, break-glass included: ledger rows are born
|
|
65
|
+
* from source declarations only (boot-time loading rides getDbAsSystem, which bypasses
|
|
66
|
+
* doors), so a caller-path insert could only mint a row no source owns. The generic record
|
|
67
|
+
* surfaces derive their affordances from these doors (db-ui renders no create button here).
|
|
65
68
|
*/
|
|
66
69
|
public auth: Table<Migration>['auth'] = {
|
|
67
|
-
db: {
|
|
68
|
-
service: {
|
|
70
|
+
db: { query: { permission: 'dev' }, update: { permission: 'dev' }, delete: { permission: 'dev' } },
|
|
71
|
+
service: { query: { permission: 'dev' }, update: { permission: 'dev' }, delete: { permission: 'dev' } },
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* The row scan the founder actually reads: what ran, how it went, HOW LONG it took
|
|
75
|
+
* (duration — stamped by the runner at completion; the default pick's five-column cap
|
|
76
|
+
* dropped it), then the declaration flags. Failure detail and timestamps stay on the
|
|
77
|
+
* record form.
|
|
78
|
+
*/
|
|
79
|
+
public ui: Table<Migration>['ui'] = {
|
|
80
|
+
recordTable: {
|
|
81
|
+
columns: ['description', 'status', 'duration', 'manual', 'preSchemaSync', 'retired'],
|
|
82
|
+
},
|
|
69
83
|
};
|
|
70
84
|
public columns = withSourceRecordColumns<Migration>({
|
|
71
85
|
description: new StringColumn('description', {}, 4000),
|
|
@@ -84,4 +84,29 @@ describe('migrations ride the dev permission', () => {
|
|
|
84
84
|
expect(() => auth.canUpdate(table, api)).not.toThrow();
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
it('insert has no door — for anyone, break-glass included: ledger rows are born from source declarations only', () => {
|
|
89
|
+
const auth = new TableAuth();
|
|
90
|
+
const table = new MigrationTable();
|
|
91
|
+
for (const roles of [['dev-crew'], ['admin']]) {
|
|
92
|
+
setUser(roles);
|
|
93
|
+
for (const api of ['db', 'service'] as const) {
|
|
94
|
+
expect(() => auth.canInsert(table, api)).toThrow(
|
|
95
|
+
'User is not authorized to insert records into table: migration'
|
|
96
|
+
);
|
|
97
|
+
// The capability read the record surfaces derive affordances from agrees with the gate.
|
|
98
|
+
expect(auth.canPerform(table, 'insert', api)).toBe(false);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('delete keeps its dev door (ledger hygiene stays possible)', () => {
|
|
104
|
+
setUser(['dev-crew']);
|
|
105
|
+
const auth = new TableAuth();
|
|
106
|
+
const table = new MigrationTable();
|
|
107
|
+
for (const api of ['db', 'service'] as const) {
|
|
108
|
+
expect(() => auth.canDelete(table, api)).not.toThrow();
|
|
109
|
+
expect(auth.canPerform(table, 'delete', api)).toBe(true);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
87
112
|
});
|