@spinajs/rbac-http-token 2.0.507
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/README.md +152 -0
- package/lib/cjs/actions.d.ts +119 -0
- package/lib/cjs/actions.d.ts.map +1 -0
- package/lib/cjs/actions.js +257 -0
- package/lib/cjs/actions.js.map +1 -0
- package/lib/cjs/cli/CreateToken.d.ts +13 -0
- package/lib/cjs/cli/CreateToken.d.ts.map +1 -0
- package/lib/cjs/cli/CreateToken.js +56 -0
- package/lib/cjs/cli/CreateToken.js.map +1 -0
- package/lib/cjs/cli/DeleteExpiredTokens.d.ts +11 -0
- package/lib/cjs/cli/DeleteExpiredTokens.d.ts.map +1 -0
- package/lib/cjs/cli/DeleteExpiredTokens.js +39 -0
- package/lib/cjs/cli/DeleteExpiredTokens.js.map +1 -0
- package/lib/cjs/cli/DeleteToken.d.ts +7 -0
- package/lib/cjs/cli/DeleteToken.d.ts.map +1 -0
- package/lib/cjs/cli/DeleteToken.js +36 -0
- package/lib/cjs/cli/DeleteToken.js.map +1 -0
- package/lib/cjs/cli/GrantTokenRole.d.ts +7 -0
- package/lib/cjs/cli/GrantTokenRole.d.ts.map +1 -0
- package/lib/cjs/cli/GrantTokenRole.js +37 -0
- package/lib/cjs/cli/GrantTokenRole.js.map +1 -0
- package/lib/cjs/cli/RevokeTokenRole.d.ts +12 -0
- package/lib/cjs/cli/RevokeTokenRole.d.ts.map +1 -0
- package/lib/cjs/cli/RevokeTokenRole.js +42 -0
- package/lib/cjs/cli/RevokeTokenRole.js.map +1 -0
- package/lib/cjs/config/rbac-http-token.d.ts +71 -0
- package/lib/cjs/config/rbac-http-token.d.ts.map +1 -0
- package/lib/cjs/config/rbac-http-token.js +98 -0
- package/lib/cjs/config/rbac-http-token.js.map +1 -0
- package/lib/cjs/controllers/AccessTokenController.d.ts +139 -0
- package/lib/cjs/controllers/AccessTokenController.d.ts.map +1 -0
- package/lib/cjs/controllers/AccessTokenController.js +264 -0
- package/lib/cjs/controllers/AccessTokenController.js.map +1 -0
- package/lib/cjs/dto/create-token-dto.d.ts +34 -0
- package/lib/cjs/dto/create-token-dto.d.ts.map +1 -0
- package/lib/cjs/dto/create-token-dto.js +35 -0
- package/lib/cjs/dto/create-token-dto.js.map +1 -0
- package/lib/cjs/events/AccessTokenCreated.d.ts +4 -0
- package/lib/cjs/events/AccessTokenCreated.d.ts.map +1 -0
- package/lib/cjs/events/AccessTokenCreated.js +18 -0
- package/lib/cjs/events/AccessTokenCreated.js.map +1 -0
- package/lib/cjs/events/AccessTokenDeleted.d.ts +4 -0
- package/lib/cjs/events/AccessTokenDeleted.d.ts.map +1 -0
- package/lib/cjs/events/AccessTokenDeleted.js +18 -0
- package/lib/cjs/events/AccessTokenDeleted.js.map +1 -0
- package/lib/cjs/events/AccessTokenEvent.d.ts +10 -0
- package/lib/cjs/events/AccessTokenEvent.d.ts.map +1 -0
- package/lib/cjs/events/AccessTokenEvent.js +26 -0
- package/lib/cjs/events/AccessTokenEvent.js.map +1 -0
- package/lib/cjs/events/AccessTokenRoleGranted.d.ts +7 -0
- package/lib/cjs/events/AccessTokenRoleGranted.d.ts.map +1 -0
- package/lib/cjs/events/AccessTokenRoleGranted.js +27 -0
- package/lib/cjs/events/AccessTokenRoleGranted.js.map +1 -0
- package/lib/cjs/events/AccessTokenRoleRevoked.d.ts +7 -0
- package/lib/cjs/events/AccessTokenRoleRevoked.d.ts.map +1 -0
- package/lib/cjs/events/AccessTokenRoleRevoked.js +27 -0
- package/lib/cjs/events/AccessTokenRoleRevoked.js.map +1 -0
- package/lib/cjs/events/index.d.ts +6 -0
- package/lib/cjs/events/index.d.ts.map +1 -0
- package/lib/cjs/events/index.js +22 -0
- package/lib/cjs/events/index.js.map +1 -0
- package/lib/cjs/generator.d.ts +12 -0
- package/lib/cjs/generator.d.ts.map +1 -0
- package/lib/cjs/generator.js +42 -0
- package/lib/cjs/generator.js.map +1 -0
- package/lib/cjs/index.d.ts +20 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +39 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/interfaces.d.ts +57 -0
- package/lib/cjs/interfaces.d.ts.map +1 -0
- package/lib/cjs/interfaces.js +11 -0
- package/lib/cjs/interfaces.js.map +1 -0
- package/lib/cjs/middlewares.d.ts +76 -0
- package/lib/cjs/middlewares.d.ts.map +1 -0
- package/lib/cjs/middlewares.js +181 -0
- package/lib/cjs/middlewares.js.map +1 -0
- package/lib/cjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.d.ts +6 -0
- package/lib/cjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.js +38 -0
- package/lib/cjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.js.map +1 -0
- package/lib/cjs/models/AccessToken.d.ts +55 -0
- package/lib/cjs/models/AccessToken.d.ts.map +1 -0
- package/lib/cjs/models/AccessToken.js +73 -0
- package/lib/cjs/models/AccessToken.js.map +1 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/policies/NoImpersonationPolicy.d.ts +28 -0
- package/lib/cjs/policies/NoImpersonationPolicy.d.ts.map +1 -0
- package/lib/cjs/policies/NoImpersonationPolicy.js +45 -0
- package/lib/cjs/policies/NoImpersonationPolicy.js.map +1 -0
- package/lib/cjs/policies/NoTokenAuthPolicy.d.ts +12 -0
- package/lib/cjs/policies/NoTokenAuthPolicy.d.ts.map +1 -0
- package/lib/cjs/policies/NoTokenAuthPolicy.js +24 -0
- package/lib/cjs/policies/NoTokenAuthPolicy.js.map +1 -0
- package/lib/cjs/policies/TokenPolicy.d.ts +49 -0
- package/lib/cjs/policies/TokenPolicy.d.ts.map +1 -0
- package/lib/cjs/policies/TokenPolicy.js +79 -0
- package/lib/cjs/policies/TokenPolicy.js.map +1 -0
- package/lib/mjs/actions.d.ts +119 -0
- package/lib/mjs/actions.d.ts.map +1 -0
- package/lib/mjs/actions.js +242 -0
- package/lib/mjs/actions.js.map +1 -0
- package/lib/mjs/cli/CreateToken.d.ts +13 -0
- package/lib/mjs/cli/CreateToken.d.ts.map +1 -0
- package/lib/mjs/cli/CreateToken.js +53 -0
- package/lib/mjs/cli/CreateToken.js.map +1 -0
- package/lib/mjs/cli/DeleteExpiredTokens.d.ts +11 -0
- package/lib/mjs/cli/DeleteExpiredTokens.d.ts.map +1 -0
- package/lib/mjs/cli/DeleteExpiredTokens.js +36 -0
- package/lib/mjs/cli/DeleteExpiredTokens.js.map +1 -0
- package/lib/mjs/cli/DeleteToken.d.ts +7 -0
- package/lib/mjs/cli/DeleteToken.d.ts.map +1 -0
- package/lib/mjs/cli/DeleteToken.js +33 -0
- package/lib/mjs/cli/DeleteToken.js.map +1 -0
- package/lib/mjs/cli/GrantTokenRole.d.ts +7 -0
- package/lib/mjs/cli/GrantTokenRole.d.ts.map +1 -0
- package/lib/mjs/cli/GrantTokenRole.js +34 -0
- package/lib/mjs/cli/GrantTokenRole.js.map +1 -0
- package/lib/mjs/cli/RevokeTokenRole.d.ts +12 -0
- package/lib/mjs/cli/RevokeTokenRole.d.ts.map +1 -0
- package/lib/mjs/cli/RevokeTokenRole.js +39 -0
- package/lib/mjs/cli/RevokeTokenRole.js.map +1 -0
- package/lib/mjs/config/rbac-http-token.d.ts +71 -0
- package/lib/mjs/config/rbac-http-token.d.ts.map +1 -0
- package/lib/mjs/config/rbac-http-token.js +96 -0
- package/lib/mjs/config/rbac-http-token.js.map +1 -0
- package/lib/mjs/controllers/AccessTokenController.d.ts +139 -0
- package/lib/mjs/controllers/AccessTokenController.d.ts.map +1 -0
- package/lib/mjs/controllers/AccessTokenController.js +261 -0
- package/lib/mjs/controllers/AccessTokenController.js.map +1 -0
- package/lib/mjs/dto/create-token-dto.d.ts +34 -0
- package/lib/mjs/dto/create-token-dto.d.ts.map +1 -0
- package/lib/mjs/dto/create-token-dto.js +32 -0
- package/lib/mjs/dto/create-token-dto.js.map +1 -0
- package/lib/mjs/events/AccessTokenCreated.d.ts +4 -0
- package/lib/mjs/events/AccessTokenCreated.d.ts.map +1 -0
- package/lib/mjs/events/AccessTokenCreated.js +15 -0
- package/lib/mjs/events/AccessTokenCreated.js.map +1 -0
- package/lib/mjs/events/AccessTokenDeleted.d.ts +4 -0
- package/lib/mjs/events/AccessTokenDeleted.d.ts.map +1 -0
- package/lib/mjs/events/AccessTokenDeleted.js +15 -0
- package/lib/mjs/events/AccessTokenDeleted.js.map +1 -0
- package/lib/mjs/events/AccessTokenEvent.d.ts +10 -0
- package/lib/mjs/events/AccessTokenEvent.d.ts.map +1 -0
- package/lib/mjs/events/AccessTokenEvent.js +23 -0
- package/lib/mjs/events/AccessTokenEvent.js.map +1 -0
- package/lib/mjs/events/AccessTokenRoleGranted.d.ts +7 -0
- package/lib/mjs/events/AccessTokenRoleGranted.d.ts.map +1 -0
- package/lib/mjs/events/AccessTokenRoleGranted.js +24 -0
- package/lib/mjs/events/AccessTokenRoleGranted.js.map +1 -0
- package/lib/mjs/events/AccessTokenRoleRevoked.d.ts +7 -0
- package/lib/mjs/events/AccessTokenRoleRevoked.d.ts.map +1 -0
- package/lib/mjs/events/AccessTokenRoleRevoked.js +24 -0
- package/lib/mjs/events/AccessTokenRoleRevoked.js.map +1 -0
- package/lib/mjs/events/index.d.ts +6 -0
- package/lib/mjs/events/index.d.ts.map +1 -0
- package/lib/mjs/events/index.js +6 -0
- package/lib/mjs/events/index.js.map +1 -0
- package/lib/mjs/generator.d.ts +12 -0
- package/lib/mjs/generator.d.ts.map +1 -0
- package/lib/mjs/generator.js +39 -0
- package/lib/mjs/generator.js.map +1 -0
- package/lib/mjs/index.d.ts +20 -0
- package/lib/mjs/index.d.ts.map +1 -0
- package/lib/mjs/index.js +23 -0
- package/lib/mjs/index.js.map +1 -0
- package/lib/mjs/interfaces.d.ts +57 -0
- package/lib/mjs/interfaces.d.ts.map +1 -0
- package/lib/mjs/interfaces.js +7 -0
- package/lib/mjs/interfaces.js.map +1 -0
- package/lib/mjs/middlewares.d.ts +76 -0
- package/lib/mjs/middlewares.d.ts.map +1 -0
- package/lib/mjs/middlewares.js +178 -0
- package/lib/mjs/middlewares.js.map +1 -0
- package/lib/mjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.d.ts +6 -0
- package/lib/mjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.js +35 -0
- package/lib/mjs/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.js.map +1 -0
- package/lib/mjs/models/AccessToken.d.ts +55 -0
- package/lib/mjs/models/AccessToken.d.ts.map +1 -0
- package/lib/mjs/models/AccessToken.js +70 -0
- package/lib/mjs/models/AccessToken.js.map +1 -0
- package/lib/mjs/package.json +1 -0
- package/lib/mjs/policies/NoImpersonationPolicy.d.ts +28 -0
- package/lib/mjs/policies/NoImpersonationPolicy.d.ts.map +1 -0
- package/lib/mjs/policies/NoImpersonationPolicy.js +41 -0
- package/lib/mjs/policies/NoImpersonationPolicy.js.map +1 -0
- package/lib/mjs/policies/NoTokenAuthPolicy.d.ts +12 -0
- package/lib/mjs/policies/NoTokenAuthPolicy.d.ts.map +1 -0
- package/lib/mjs/policies/NoTokenAuthPolicy.js +20 -0
- package/lib/mjs/policies/NoTokenAuthPolicy.js.map +1 -0
- package/lib/mjs/policies/TokenPolicy.d.ts +49 -0
- package/lib/mjs/policies/TokenPolicy.d.ts.map +1 -0
- package/lib/mjs/policies/TokenPolicy.js +75 -0
- package/lib/mjs/policies/TokenPolicy.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -0
- package/lib/tsconfig.mjs.tsbuildinfo +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
8
|
+
import { OrmMigration, Migration } from '@spinajs/orm';
|
|
9
|
+
let RbacHttpTokenInitial_2026_08_11_01_00_00 = class RbacHttpTokenInitial_2026_08_11_01_00_00 extends OrmMigration {
|
|
10
|
+
async up(connection) {
|
|
11
|
+
await connection.schema().createTable('rbac_access_tokens', (table) => {
|
|
12
|
+
table.int('Id').primaryKey().autoIncrement();
|
|
13
|
+
table.string('Uuid', 36).notNull();
|
|
14
|
+
table.string('Name', 128).notNull();
|
|
15
|
+
table.string('Token', 64).notNull();
|
|
16
|
+
table.string('Roles', 512).notNull();
|
|
17
|
+
table.dateTime('ExpiresAt');
|
|
18
|
+
table.dateTime('CreatedAt').notNull().default().dateTime();
|
|
19
|
+
table.dateTime('LastUsedAt');
|
|
20
|
+
table.int('user_id').notNull();
|
|
21
|
+
table.foreignKey('user_id').references('users', 'Id').cascade();
|
|
22
|
+
});
|
|
23
|
+
await connection.index().unique().table('rbac_access_tokens').name('access_token_hash_idx').columns(['Token']);
|
|
24
|
+
await connection.index().unique().table('rbac_access_tokens').name('access_token_uuid_idx').columns(['Uuid']);
|
|
25
|
+
await connection.index().table('rbac_access_tokens').name('access_token_user_idx').columns(['user_id']);
|
|
26
|
+
await connection.index().table('rbac_access_tokens').name('access_token_expires_idx').columns(['ExpiresAt']);
|
|
27
|
+
}
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
29
|
+
async down(_connection) { }
|
|
30
|
+
};
|
|
31
|
+
RbacHttpTokenInitial_2026_08_11_01_00_00 = __decorate([
|
|
32
|
+
Migration('default')
|
|
33
|
+
], RbacHttpTokenInitial_2026_08_11_01_00_00);
|
|
34
|
+
export { RbacHttpTokenInitial_2026_08_11_01_00_00 };
|
|
35
|
+
//# sourceMappingURL=RbacHttpTokenInitial_2026_08_11_01_00_00.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RbacHttpTokenInitial_2026_08_11_01_00_00.js","sourceRoot":"","sources":["../../../src/migrations/RbacHttpTokenInitial_2026_08_11_01_00_00.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAsD;AACtD,OAAO,EAAE,YAAY,EAAa,SAAS,EAAE,MAAM,cAAc,CAAC;AAG3D,IAAM,wCAAwC,GAA9C,MAAM,wCAAyC,SAAQ,YAAY;IACjE,KAAK,CAAC,EAAE,CAAC,UAAqB;QACnC,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;YACpE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE,CAAC;YAC7C,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YACnC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACpC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YACpC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACrC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAC5B,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC;YAC3D,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC7B,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;YAC/B,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/G,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9G,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QACxG,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;IAC/G,CAAC;IAED,gEAAgE;IACzD,KAAK,CAAC,IAAI,CAAC,WAAsB,IAAkB,CAAC;CAC5D,CAAA;AAvBY,wCAAwC;IADpD,SAAS,CAAC,SAAS,CAAC;GACR,wCAAwC,CAuBpD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ModelBase, SingleRelation } from '@spinajs/orm';
|
|
2
|
+
import { User } from '@spinajs/rbac';
|
|
3
|
+
import { DateTime } from 'luxon';
|
|
4
|
+
/**
|
|
5
|
+
* Personal access token. Only the SHA-256 hash of the token is stored;
|
|
6
|
+
* the plaintext is returned once at creation and cannot be recovered.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AccessToken extends ModelBase<AccessToken> {
|
|
9
|
+
constructor(data?: Partial<AccessToken>);
|
|
10
|
+
/**
|
|
11
|
+
* Internal row id, never leaves the process. Tokens are addressed by Uuid.
|
|
12
|
+
*/
|
|
13
|
+
Id: number;
|
|
14
|
+
/**
|
|
15
|
+
* Public identifier used by the API and CLI.
|
|
16
|
+
*/
|
|
17
|
+
Uuid: string;
|
|
18
|
+
/**
|
|
19
|
+
* Human readable label ("ci deploy key").
|
|
20
|
+
*/
|
|
21
|
+
Name: string;
|
|
22
|
+
/**
|
|
23
|
+
* SHA-256 hex digest of the plaintext token. Hidden: even the hash is
|
|
24
|
+
* internal - leaking it invites offline correlation.
|
|
25
|
+
*/
|
|
26
|
+
Token: string;
|
|
27
|
+
/**
|
|
28
|
+
* Roles allowed on this token. Effective roles at request time are the
|
|
29
|
+
* intersection of this list with the owner's current roles.
|
|
30
|
+
*/
|
|
31
|
+
Roles: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Absolute expiration. Null/absent = never expires.
|
|
34
|
+
*
|
|
35
|
+
* NOTE: declared optional rather than as a `DateTime | null` union - the
|
|
36
|
+
* `@DateTime()` decorator reads `design:type`, and a union is emitted as
|
|
37
|
+
* `Object`, which it rejects at decoration time. An optional property still
|
|
38
|
+
* emits `design:type = DateTime` while keeping the absence in the type.
|
|
39
|
+
*/
|
|
40
|
+
ExpiresAt?: DateTime;
|
|
41
|
+
CreatedAt: DateTime;
|
|
42
|
+
/**
|
|
43
|
+
* Last successful authentication with this token. Updated throttled.
|
|
44
|
+
* Null/absent until the token is used for the first time - see the note on
|
|
45
|
+
* `ExpiresAt` for why this is optional rather than a nullable union.
|
|
46
|
+
*/
|
|
47
|
+
LastUsedAt?: DateTime;
|
|
48
|
+
User: SingleRelation<User>;
|
|
49
|
+
user_id: number;
|
|
50
|
+
/**
|
|
51
|
+
* True when the token carries an expiration in the past.
|
|
52
|
+
*/
|
|
53
|
+
get IsExpired(): boolean;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=AccessToken.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessToken.d.ts","sourceRoot":"","sources":["../../../src/models/AccessToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmE,SAAS,EAAgB,cAAc,EAAE,MAAM,cAAc,CAAC;AACxI,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAIjC;;;GAGG;AACH,qBAEa,WAAY,SAAQ,SAAS,CAAC,WAAW,CAAC;IACrD,YAAmB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,EAG7C;IAED;;OAEG;IAGI,EAAE,EAAG,MAAM,CAAC;IAEnB;;OAEG;IACI,IAAI,EAAG,MAAM,CAAC;IAErB;;OAEG;IACI,IAAI,EAAG,MAAM,CAAC;IAErB;;;OAGG;IAEI,KAAK,EAAG,MAAM,CAAC;IAEtB;;;OAGG;IAEI,KAAK,EAAG,MAAM,EAAE,CAAC;IAExB;;;;;;;OAOG;IAEI,SAAS,CAAC,EAAE,QAAQ,CAAC;IAGrB,SAAS,EAAG,QAAQ,CAAC;IAE5B;;;;OAIG;IAEI,UAAU,CAAC,EAAE,QAAQ,CAAC;IAItB,IAAI,EAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAG5B,OAAO,EAAG,MAAM,CAAC;IAExB;;OAEG;IACH,IAAW,SAAS,IAAI,OAAO,CAE9B;CACF"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { BelongsTo, Connection, CreatedAt, DateTime as DT, Hidden, Model, ModelBase, Primary, Set, SingleRelation } from '@spinajs/orm';
|
|
11
|
+
import { DateTime } from 'luxon';
|
|
12
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
13
|
+
import { _check_arg, _default } from '@spinajs/util';
|
|
14
|
+
/**
|
|
15
|
+
* Personal access token. Only the SHA-256 hash of the token is stored;
|
|
16
|
+
* the plaintext is returned once at creation and cannot be recovered.
|
|
17
|
+
*/
|
|
18
|
+
let AccessToken = class AccessToken extends ModelBase {
|
|
19
|
+
constructor(data) {
|
|
20
|
+
super(data);
|
|
21
|
+
this.Uuid = _check_arg(_default(uuidv4()))(this.Uuid, 'uuid');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* True when the token carries an expiration in the past.
|
|
25
|
+
*/
|
|
26
|
+
get IsExpired() {
|
|
27
|
+
return !!this.ExpiresAt && this.ExpiresAt <= DateTime.now();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
__decorate([
|
|
31
|
+
Primary(),
|
|
32
|
+
Hidden(),
|
|
33
|
+
__metadata("design:type", Number)
|
|
34
|
+
], AccessToken.prototype, "Id", void 0);
|
|
35
|
+
__decorate([
|
|
36
|
+
Hidden(),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], AccessToken.prototype, "Token", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
Set(),
|
|
41
|
+
__metadata("design:type", Array)
|
|
42
|
+
], AccessToken.prototype, "Roles", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
DT(),
|
|
45
|
+
__metadata("design:type", DateTime)
|
|
46
|
+
], AccessToken.prototype, "ExpiresAt", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
CreatedAt(),
|
|
49
|
+
__metadata("design:type", DateTime)
|
|
50
|
+
], AccessToken.prototype, "CreatedAt", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
DT(),
|
|
53
|
+
__metadata("design:type", DateTime)
|
|
54
|
+
], AccessToken.prototype, "LastUsedAt", void 0);
|
|
55
|
+
__decorate([
|
|
56
|
+
Hidden(),
|
|
57
|
+
BelongsTo('User'),
|
|
58
|
+
__metadata("design:type", SingleRelation)
|
|
59
|
+
], AccessToken.prototype, "User", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
Hidden(),
|
|
62
|
+
__metadata("design:type", Number)
|
|
63
|
+
], AccessToken.prototype, "user_id", void 0);
|
|
64
|
+
AccessToken = __decorate([
|
|
65
|
+
Connection('default'),
|
|
66
|
+
Model('rbac_access_tokens'),
|
|
67
|
+
__metadata("design:paramtypes", [Object])
|
|
68
|
+
], AccessToken);
|
|
69
|
+
export { AccessToken };
|
|
70
|
+
//# sourceMappingURL=AccessToken.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessToken.js","sourceRoot":"","sources":["../../../src/models/AccessToken.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAExI,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD;;;GAGG;AAGI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,SAAsB;IACrD,YAAmB,IAA2B;QAC5C,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IA8DD;;OAEG;IACH,IAAW,SAAS;QAClB,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;IAC9D,CAAC;CACF,CAAA;AA7DQ;IAFN,OAAO,EAAE;IACT,MAAM,EAAE;;uCACU;AAiBZ;IADN,MAAM,EAAE;;0CACa;AAOf;IADN,GAAG,EAAE;;0CACkB;AAWjB;IADN,EAAE,EAAE;8BACc,QAAQ;8CAAC;AAGrB;IADN,SAAS,EAAE;8BACO,QAAQ;8CAAC;AAQrB;IADN,EAAE,EAAE;8BACe,QAAQ;+CAAC;AAItB;IAFN,MAAM,EAAE;IACR,SAAS,CAAC,MAAM,CAAC;8BACJ,cAAc;yCAAO;AAG5B;IADN,MAAM,EAAE;;4CACe;AAhEb,WAAW;IAFvB,UAAU,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,oBAAoB,CAAC;;GACf,WAAW,CAwEvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BasePolicy, IController, IRoute, Request as sRequest } from '@spinajs/http';
|
|
2
|
+
import '../interfaces.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rejects requests made from an impersonated session.
|
|
5
|
+
*
|
|
6
|
+
* Impersonation is a *supervised, revocable* act: an administrator borrows a
|
|
7
|
+
* user's view of the application and the borrowing ends when the impersonation
|
|
8
|
+
* does. An access token is the opposite - a bearer credential that outlives the
|
|
9
|
+
* session that minted it, carries the victim's roles, and is invisible in the
|
|
10
|
+
* impersonator's own audit trail. Letting a credential-issuing route run while
|
|
11
|
+
* impersonating therefore turns a temporary, reversible grant into a permanent
|
|
12
|
+
* one that the account owner never consented to and cannot see.
|
|
13
|
+
*
|
|
14
|
+
* `RbacMiddleware` puts the impersonation TARGET in `req.storage.User` and the
|
|
15
|
+
* original administrator in `req.storage.Impersonator`
|
|
16
|
+
* ( `rbac-http/src/middlewares.ts` ), so every ownership and permission check
|
|
17
|
+
* downstream already reads as the victim - this policy is the only thing that
|
|
18
|
+
* can tell the two situations apart.
|
|
19
|
+
*
|
|
20
|
+
* Same reasoning as a password change demanding the CURRENT password: an
|
|
21
|
+
* operation that hands out lasting credentials has to be performed by the
|
|
22
|
+
* account owner, not on their behalf.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NoImpersonationPolicy extends BasePolicy {
|
|
25
|
+
isEnabled(_action: IRoute, _instance: IController): boolean;
|
|
26
|
+
execute(req: sRequest, _action: IRoute, _instance: IController): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=NoImpersonationPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoImpersonationPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/NoImpersonationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAQrF,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;IAC5C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAEjE;IAEY,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1F;CACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BasePolicy } from '@spinajs/http';
|
|
2
|
+
import { Forbidden } from '@spinajs/exceptions';
|
|
3
|
+
// Same side-effect import both siblings carry. `req.storage.Impersonator` comes
|
|
4
|
+
// from `@spinajs/rbac-http`'s augmentation of `IActionLocalStoregeContext`,
|
|
5
|
+
// which this package's `interfaces.ts` pulls into the program ( type-only ).
|
|
6
|
+
// Without it the field only type-checks when something ELSE in the compilation
|
|
7
|
+
// happened to drag that augmentation in - fine today, silently broken the day
|
|
8
|
+
// this file is compiled on its own.
|
|
9
|
+
import '../interfaces.js';
|
|
10
|
+
/**
|
|
11
|
+
* Rejects requests made from an impersonated session.
|
|
12
|
+
*
|
|
13
|
+
* Impersonation is a *supervised, revocable* act: an administrator borrows a
|
|
14
|
+
* user's view of the application and the borrowing ends when the impersonation
|
|
15
|
+
* does. An access token is the opposite - a bearer credential that outlives the
|
|
16
|
+
* session that minted it, carries the victim's roles, and is invisible in the
|
|
17
|
+
* impersonator's own audit trail. Letting a credential-issuing route run while
|
|
18
|
+
* impersonating therefore turns a temporary, reversible grant into a permanent
|
|
19
|
+
* one that the account owner never consented to and cannot see.
|
|
20
|
+
*
|
|
21
|
+
* `RbacMiddleware` puts the impersonation TARGET in `req.storage.User` and the
|
|
22
|
+
* original administrator in `req.storage.Impersonator`
|
|
23
|
+
* ( `rbac-http/src/middlewares.ts` ), so every ownership and permission check
|
|
24
|
+
* downstream already reads as the victim - this policy is the only thing that
|
|
25
|
+
* can tell the two situations apart.
|
|
26
|
+
*
|
|
27
|
+
* Same reasoning as a password change demanding the CURRENT password: an
|
|
28
|
+
* operation that hands out lasting credentials has to be performed by the
|
|
29
|
+
* account owner, not on their behalf.
|
|
30
|
+
*/
|
|
31
|
+
export class NoImpersonationPolicy extends BasePolicy {
|
|
32
|
+
isEnabled(_action, _instance) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
async execute(req, _action, _instance) {
|
|
36
|
+
if (req.storage?.Impersonator) {
|
|
37
|
+
throw new Forbidden('this route cannot be used while impersonating another user');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=NoImpersonationPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoImpersonationPolicy.js","sourceRoot":"","sources":["../../../src/policies/NoImpersonationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4C,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,gFAAgF;AAChF,4EAA4E;AAC5E,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,oCAAoC;AACpC,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,qBAAsB,SAAQ,UAAU;IAC5C,SAAS,CAAC,OAAe,EAAE,SAAsB;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,GAAa,EAAE,OAAe,EAAE,SAAsB;QACzE,IAAI,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YAC9B,MAAM,IAAI,SAAS,CAAC,4DAA4D,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BasePolicy, IController, IRoute, Request as sRequest } from '@spinajs/http';
|
|
2
|
+
import '../interfaces.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rejects requests authenticated with an access token. Applied to the token
|
|
5
|
+
* management API so a token can never be used to mint or manage tokens
|
|
6
|
+
* ( no self-replication ). Session-authenticated requests pass through.
|
|
7
|
+
*/
|
|
8
|
+
export declare class NoTokenAuthPolicy extends BasePolicy {
|
|
9
|
+
isEnabled(_action: IRoute, _instance: IController): boolean;
|
|
10
|
+
execute(req: sRequest, _action: IRoute, _instance: IController): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=NoTokenAuthPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoTokenAuthPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/NoTokenAuthPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGrF,OAAO,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IACxC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAEjE;IAEY,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1F;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BasePolicy } from '@spinajs/http';
|
|
2
|
+
import { Forbidden } from '@spinajs/exceptions';
|
|
3
|
+
// Brings the `req.storage.TokenAuth` module augmentation into the program.
|
|
4
|
+
import '../interfaces.js';
|
|
5
|
+
/**
|
|
6
|
+
* Rejects requests authenticated with an access token. Applied to the token
|
|
7
|
+
* management API so a token can never be used to mint or manage tokens
|
|
8
|
+
* ( no self-replication ). Session-authenticated requests pass through.
|
|
9
|
+
*/
|
|
10
|
+
export class NoTokenAuthPolicy extends BasePolicy {
|
|
11
|
+
isEnabled(_action, _instance) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
async execute(req, _action, _instance) {
|
|
15
|
+
if (req.storage?.TokenAuth) {
|
|
16
|
+
throw new Forbidden('access tokens cannot be used on this route');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=NoTokenAuthPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoTokenAuthPolicy.js","sourceRoot":"","sources":["../../../src/policies/NoTokenAuthPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4C,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,2EAA2E;AAC3E,OAAO,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,UAAU;IACxC,SAAS,CAAC,OAAe,EAAE,SAAsB;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,GAAa,EAAE,OAAe,EAAE,SAAsB;QACzE,IAAI,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { BasePolicy, IController, IRoute, Request as sRequest } from '@spinajs/http';
|
|
2
|
+
import '../interfaces.js';
|
|
3
|
+
/**
|
|
4
|
+
* Route requires authentication with an access token AND the token's
|
|
5
|
+
* effective roles must satisfy the route's @Resource/@Permission grants.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors RbacPolicy's grant enforcement, but for token-authenticated
|
|
8
|
+
* requests ( which carry no session ).
|
|
9
|
+
*
|
|
10
|
+
* WIRING - do NOT pass this policy to @Permission's `also` parameter:
|
|
11
|
+
*
|
|
12
|
+
* @Permission(['readOwn'], TokenPolicy) // BROKEN - always 401
|
|
13
|
+
*
|
|
14
|
+
* `@Permission` unconditionally bundles RbacPolicy together with every `also`
|
|
15
|
+
* policy into the SAME policy group ( rbac-http/src/decorators.ts ), and
|
|
16
|
+
* policies inside one group are combined with AND. RbacPolicy demands an
|
|
17
|
+
* authorized session, which a token-authenticated request never has, so the
|
|
18
|
+
* pair can never both pass and the route is permanently unreachable by token.
|
|
19
|
+
*
|
|
20
|
+
* Declare it as its OWN group at the ROUTE scope instead - groups of ONE scope
|
|
21
|
+
* are ORed, so a token request satisfies the TokenPolicy group while a session
|
|
22
|
+
* request satisfies the RbacPolicy group:
|
|
23
|
+
*
|
|
24
|
+
* @Get('data')
|
|
25
|
+
* @Permission(['readOwn']) // the permission metadata + RbacPolicy group
|
|
26
|
+
* @Policy(TokenPolicy) // METHOD level - a second route scope group
|
|
27
|
+
*
|
|
28
|
+
* The placement is not free-standing: a CLASS level `@Policy(TokenPolicy)` goes
|
|
29
|
+
* to the CONTROLLER scope, and the two scopes are ANDed - it would then have to
|
|
30
|
+
* hold TOGETHER with the route's RbacPolicy group, which is exactly the broken
|
|
31
|
+
* combination above. Only a method level declaration lands in the same scope as
|
|
32
|
+
* `@Permission` and gets ORed with it.
|
|
33
|
+
*
|
|
34
|
+
* Writing `@Policy(TokenPolicy)` BELOW `@Permission` is deliberate too.
|
|
35
|
+
* Decorators apply bottom up, so this one is pushed first, and when no group
|
|
36
|
+
* holds `createPolicyGate` reports the first rejection in declaration order: a
|
|
37
|
+
* token that authenticated but lacks the grant then gets this policy's 403
|
|
38
|
+
* instead of RbacPolicy's misleading "user not logged" 401.
|
|
39
|
+
*
|
|
40
|
+
* The `@Permission` line is still required even with `@Policy(TokenPolicy)`:
|
|
41
|
+
* this policy reads the same route permission metadata, and a route without it
|
|
42
|
+
* silently inherits the controller-level default `['readOwn']` rather than
|
|
43
|
+
* failing loudly.
|
|
44
|
+
*/
|
|
45
|
+
export declare class TokenPolicy extends BasePolicy {
|
|
46
|
+
isEnabled(_action: IRoute, _instance: IController): boolean;
|
|
47
|
+
execute(req: sRequest, action: IRoute, instance: IController): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=TokenPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/TokenPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAe,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAMlG,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,qBAAa,WAAY,SAAQ,UAAU;IAClC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAEjE;IAEY,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBxF;CACF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BasePolicy, ServerError } from '@spinajs/http';
|
|
2
|
+
import { AuthenticationFailed, Forbidden } from '@spinajs/exceptions';
|
|
3
|
+
import { ACL_CONTROLLER_DESCRIPTOR, checkRoutePermission } from '@spinajs/rbac-http';
|
|
4
|
+
// Brings the `req.storage.TokenAuth` module augmentation into the program.
|
|
5
|
+
// Same side-effect import the middleware uses; without it this file compiles
|
|
6
|
+
// on its own against an un-augmented IActionLocalStoregeContext.
|
|
7
|
+
import '../interfaces.js';
|
|
8
|
+
/**
|
|
9
|
+
* Route requires authentication with an access token AND the token's
|
|
10
|
+
* effective roles must satisfy the route's @Resource/@Permission grants.
|
|
11
|
+
*
|
|
12
|
+
* Mirrors RbacPolicy's grant enforcement, but for token-authenticated
|
|
13
|
+
* requests ( which carry no session ).
|
|
14
|
+
*
|
|
15
|
+
* WIRING - do NOT pass this policy to @Permission's `also` parameter:
|
|
16
|
+
*
|
|
17
|
+
* @Permission(['readOwn'], TokenPolicy) // BROKEN - always 401
|
|
18
|
+
*
|
|
19
|
+
* `@Permission` unconditionally bundles RbacPolicy together with every `also`
|
|
20
|
+
* policy into the SAME policy group ( rbac-http/src/decorators.ts ), and
|
|
21
|
+
* policies inside one group are combined with AND. RbacPolicy demands an
|
|
22
|
+
* authorized session, which a token-authenticated request never has, so the
|
|
23
|
+
* pair can never both pass and the route is permanently unreachable by token.
|
|
24
|
+
*
|
|
25
|
+
* Declare it as its OWN group at the ROUTE scope instead - groups of ONE scope
|
|
26
|
+
* are ORed, so a token request satisfies the TokenPolicy group while a session
|
|
27
|
+
* request satisfies the RbacPolicy group:
|
|
28
|
+
*
|
|
29
|
+
* @Get('data')
|
|
30
|
+
* @Permission(['readOwn']) // the permission metadata + RbacPolicy group
|
|
31
|
+
* @Policy(TokenPolicy) // METHOD level - a second route scope group
|
|
32
|
+
*
|
|
33
|
+
* The placement is not free-standing: a CLASS level `@Policy(TokenPolicy)` goes
|
|
34
|
+
* to the CONTROLLER scope, and the two scopes are ANDed - it would then have to
|
|
35
|
+
* hold TOGETHER with the route's RbacPolicy group, which is exactly the broken
|
|
36
|
+
* combination above. Only a method level declaration lands in the same scope as
|
|
37
|
+
* `@Permission` and gets ORed with it.
|
|
38
|
+
*
|
|
39
|
+
* Writing `@Policy(TokenPolicy)` BELOW `@Permission` is deliberate too.
|
|
40
|
+
* Decorators apply bottom up, so this one is pushed first, and when no group
|
|
41
|
+
* holds `createPolicyGate` reports the first rejection in declaration order: a
|
|
42
|
+
* token that authenticated but lacks the grant then gets this policy's 403
|
|
43
|
+
* instead of RbacPolicy's misleading "user not logged" 401.
|
|
44
|
+
*
|
|
45
|
+
* The `@Permission` line is still required even with `@Policy(TokenPolicy)`:
|
|
46
|
+
* this policy reads the same route permission metadata, and a route without it
|
|
47
|
+
* silently inherits the controller-level default `['readOwn']` rather than
|
|
48
|
+
* failing loudly.
|
|
49
|
+
*/
|
|
50
|
+
export class TokenPolicy extends BasePolicy {
|
|
51
|
+
isEnabled(_action, _instance) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
async execute(req, action, instance) {
|
|
55
|
+
if (!req.storage || !req.storage.TokenAuth || !req.storage.User) {
|
|
56
|
+
throw new AuthenticationFailed('access token required');
|
|
57
|
+
}
|
|
58
|
+
const descriptor = Reflect.getMetadata(ACL_CONTROLLER_DESCRIPTOR, instance);
|
|
59
|
+
// Guard BEFORE dereferencing `.Routes`: a route decorated with this policy
|
|
60
|
+
// but no @Resource/@Permission must produce the descriptive server error,
|
|
61
|
+
// not a raw TypeError 500.
|
|
62
|
+
if (!descriptor || !descriptor.Permission || descriptor.Permission.length === 0) {
|
|
63
|
+
throw new ServerError('no route permission or resources assigned');
|
|
64
|
+
}
|
|
65
|
+
let permission = descriptor.Permission ?? [];
|
|
66
|
+
if (descriptor.Routes.has(String(action.Method))) {
|
|
67
|
+
permission = descriptor.Routes.get(String(action.Method)).Permission ?? [];
|
|
68
|
+
}
|
|
69
|
+
if (!permission.some((p) => checkRoutePermission(req, descriptor.Resource, p)?.granted)) {
|
|
70
|
+
const effective = req.storage.ActiveRole ?? req.storage.User.Role;
|
|
71
|
+
throw new Forbidden(`token role(s) ${effective} do not have permission ${permission} for resource ${descriptor.Resource}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=TokenPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenPolicy.js","sourceRoot":"","sources":["../../../src/policies/TokenPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,WAAW,EAAuB,MAAM,eAAe,CAAC;AAClG,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAmB,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACtG,2EAA2E;AAC3E,6EAA6E;AAC7E,iEAAiE;AACjE,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,OAAO,WAAY,SAAQ,UAAU;IAClC,SAAS,CAAC,OAAe,EAAE,SAAsB;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,GAAa,EAAE,MAAc,EAAE,QAAqB;QACvE,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAChE,MAAM,IAAI,oBAAoB,CAAC,uBAAuB,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,UAAU,GAAoB,OAAO,CAAC,WAAW,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;QAE7F,2EAA2E;QAC3E,0EAA0E;QAC1E,2BAA2B;QAC3B,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChF,MAAM,IAAI,WAAW,CAAC,2CAA2C,CAAC,CAAC;QACrE,CAAC;QAED,IAAI,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,EAAE,CAAC;QAC7C,IAAI,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACjD,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAE,CAAC,UAAU,IAAI,EAAE,CAAC;QAC9E,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;YACxF,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAClE,MAAM,IAAI,SAAS,CAAC,iBAAiB,SAAS,2BAA2B,UAAU,iBAAiB,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7H,CAAC;IACH,CAAC;CACF"}
|