@scryan7371/sdr-security 0.1.4 → 0.1.6
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/dist/api/migrations/1700000000001-add-refresh-tokens.js +0 -2
- package/dist/api/migrations/1739490000000-create-app-user.d.ts +9 -0
- package/dist/api/migrations/1739490000000-create-app-user.js +34 -0
- package/dist/api/migrations/1739500000000-create-security-identity.js +0 -2
- package/dist/api/migrations/1739510000000-create-security-roles.js +1 -2
- package/dist/api/migrations/1739515000000-create-security-user-roles.js +0 -3
- package/dist/api/migrations/1739520000000-create-password-reset-tokens.js +0 -2
- package/dist/api/migrations/1739530000000-create-security-user.js +0 -1
- package/dist/api/migrations/index.d.ts +4 -2
- package/dist/api/migrations/index.js +6 -2
- package/dist/api/migrations/migrations.test.js +21 -0
- package/dist/integration/database.integration.test.js +2 -9
- package/dist/nest/security-workflows.controller.d.ts +4 -4
- package/dist/nest/security-workflows.controller.js +36 -16
- package/package.json +12 -2
- package/src/api/migrations/1700000000001-add-refresh-tokens.ts +0 -2
- package/src/api/migrations/1739490000000-create-app-user.ts +39 -0
- package/src/api/migrations/1739500000000-create-security-identity.ts +0 -2
- package/src/api/migrations/1739510000000-create-security-roles.ts +1 -2
- package/src/api/migrations/1739515000000-create-security-user-roles.ts +0 -3
- package/src/api/migrations/1739520000000-create-password-reset-tokens.ts +0 -2
- package/src/api/migrations/1739530000000-create-security-user.ts +0 -1
- package/src/api/migrations/index.ts +8 -1
- package/src/api/migrations/migrations.test.ts +30 -1
- package/src/integration/database.integration.test.ts +3 -11
- package/src/nest/security-workflows.controller.ts +42 -13
|
@@ -13,8 +13,6 @@ class AddRefreshTokens1700000000001 {
|
|
|
13
13
|
"revoked_at" timestamptz,
|
|
14
14
|
"userId" uuid,
|
|
15
15
|
"created_at" timestamptz NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
|
16
|
-
CONSTRAINT "CHK_refresh_token_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
17
|
-
CONSTRAINT "CHK_refresh_token_userId_uuidv7" CHECK ("userId" IS NULL OR "userId"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
16
|
CONSTRAINT "FK_refresh_token_user" FOREIGN KEY ("userId") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
|
19
17
|
)
|
|
20
18
|
`);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreateAppUser1739490000000 = void 0;
|
|
4
|
+
class CreateAppUser1739490000000 {
|
|
5
|
+
name = "CreateAppUser1739490000000";
|
|
6
|
+
async up(queryRunner) {
|
|
7
|
+
const userTableRef = getUserTableReference();
|
|
8
|
+
await queryRunner.query(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS ${userTableRef} (
|
|
10
|
+
"id" uuid PRIMARY KEY NOT NULL,
|
|
11
|
+
"email" varchar NOT NULL
|
|
12
|
+
)
|
|
13
|
+
`);
|
|
14
|
+
}
|
|
15
|
+
async down(queryRunner) {
|
|
16
|
+
const userTableRef = getUserTableReference();
|
|
17
|
+
await queryRunner.query(`DROP TABLE IF EXISTS ${userTableRef}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CreateAppUser1739490000000 = CreateAppUser1739490000000;
|
|
21
|
+
const getSafeIdentifier = (value, fallback) => {
|
|
22
|
+
const resolved = value?.trim() || fallback;
|
|
23
|
+
if (!resolved || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(resolved)) {
|
|
24
|
+
throw new Error(`Invalid SQL identifier: ${resolved}`);
|
|
25
|
+
}
|
|
26
|
+
return resolved;
|
|
27
|
+
};
|
|
28
|
+
const getUserTableReference = () => {
|
|
29
|
+
const table = getSafeIdentifier(process.env.USER_TABLE, "app_user");
|
|
30
|
+
const schema = process.env.USER_TABLE_SCHEMA
|
|
31
|
+
? getSafeIdentifier(process.env.USER_TABLE_SCHEMA, "public")
|
|
32
|
+
: "public";
|
|
33
|
+
return `"${schema}"."${table}"`;
|
|
34
|
+
};
|
|
@@ -13,8 +13,6 @@ class CreateSecurityIdentity1739500000000 {
|
|
|
13
13
|
"provider_subject" varchar NOT NULL,
|
|
14
14
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
15
15
|
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
16
|
-
CONSTRAINT "CHK_security_identity_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
17
|
-
CONSTRAINT "CHK_security_identity_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
16
|
CONSTRAINT "FK_security_identity_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
19
17
|
)
|
|
20
18
|
`);
|
|
@@ -11,8 +11,7 @@ class CreateSecurityRoles1739510000000 {
|
|
|
11
11
|
"description" text,
|
|
12
12
|
"is_system" boolean NOT NULL DEFAULT false,
|
|
13
13
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
14
|
-
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
15
|
-
CONSTRAINT "CHK_security_role_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$')
|
|
14
|
+
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
16
15
|
)
|
|
17
16
|
`);
|
|
18
17
|
await queryRunner.query(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_security_role_key" ON "security_role" ("role_key")`);
|
|
@@ -11,9 +11,6 @@ class CreateSecurityUserRoles1739515000000 {
|
|
|
11
11
|
"user_id" uuid NOT NULL,
|
|
12
12
|
"role_id" uuid NOT NULL,
|
|
13
13
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
14
|
-
CONSTRAINT "CHK_security_user_role_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
15
|
-
CONSTRAINT "CHK_security_user_role_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
16
|
-
CONSTRAINT "CHK_security_user_role_role_id_uuidv7" CHECK ("role_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
17
14
|
CONSTRAINT "FK_security_user_role_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE,
|
|
18
15
|
CONSTRAINT "FK_security_user_role_role_id" FOREIGN KEY ("role_id") REFERENCES "security_role" ("id") ON DELETE CASCADE
|
|
19
16
|
)
|
|
@@ -13,8 +13,6 @@ class CreatePasswordResetTokens1739520000000 {
|
|
|
13
13
|
"expires_at" timestamptz NOT NULL,
|
|
14
14
|
"used_at" timestamptz,
|
|
15
15
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
16
|
-
CONSTRAINT "CHK_security_password_reset_token_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
17
|
-
CONSTRAINT "CHK_security_password_reset_token_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
16
|
CONSTRAINT "FK_security_password_reset_token_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
19
17
|
)
|
|
20
18
|
`);
|
|
@@ -14,7 +14,6 @@ class CreateSecurityUser1739530000000 {
|
|
|
14
14
|
"admin_approved_at" timestamptz,
|
|
15
15
|
"is_active" boolean NOT NULL DEFAULT true,
|
|
16
16
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
17
|
-
CONSTRAINT "CHK_security_user_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
17
|
CONSTRAINT "FK_security_user_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
19
18
|
)
|
|
20
19
|
`);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { CreateAppUser1739490000000 } from "./1739490000000-create-app-user";
|
|
1
2
|
import { AddRefreshTokens1700000000001 } from "./1700000000001-add-refresh-tokens";
|
|
2
3
|
import { CreateSecurityIdentity1739500000000 } from "./1739500000000-create-security-identity";
|
|
3
4
|
import { CreateSecurityRoles1739510000000 } from "./1739510000000-create-security-roles";
|
|
4
5
|
import { CreateSecurityUserRoles1739515000000 } from "./1739515000000-create-security-user-roles";
|
|
5
6
|
import { CreatePasswordResetTokens1739520000000 } from "./1739520000000-create-password-reset-tokens";
|
|
6
7
|
import { CreateSecurityUser1739530000000 } from "./1739530000000-create-security-user";
|
|
7
|
-
export declare const
|
|
8
|
-
export
|
|
8
|
+
export declare const allMigrations: (typeof CreateAppUser1739490000000)[];
|
|
9
|
+
export declare const securityMigrations: (typeof CreateAppUser1739490000000)[];
|
|
10
|
+
export { CreateAppUser1739490000000, AddRefreshTokens1700000000001, CreateSecurityIdentity1739500000000, CreateSecurityRoles1739510000000, CreateSecurityUserRoles1739515000000, CreatePasswordResetTokens1739520000000, CreateSecurityUser1739530000000, };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CreateSecurityUser1739530000000 = exports.CreatePasswordResetTokens1739520000000 = exports.CreateSecurityUserRoles1739515000000 = exports.CreateSecurityRoles1739510000000 = exports.CreateSecurityIdentity1739500000000 = exports.AddRefreshTokens1700000000001 = exports.securityMigrations = void 0;
|
|
3
|
+
exports.CreateSecurityUser1739530000000 = exports.CreatePasswordResetTokens1739520000000 = exports.CreateSecurityUserRoles1739515000000 = exports.CreateSecurityRoles1739510000000 = exports.CreateSecurityIdentity1739500000000 = exports.AddRefreshTokens1700000000001 = exports.CreateAppUser1739490000000 = exports.securityMigrations = exports.allMigrations = void 0;
|
|
4
|
+
const _1739490000000_create_app_user_1 = require("./1739490000000-create-app-user");
|
|
5
|
+
Object.defineProperty(exports, "CreateAppUser1739490000000", { enumerable: true, get: function () { return _1739490000000_create_app_user_1.CreateAppUser1739490000000; } });
|
|
4
6
|
const _1700000000001_add_refresh_tokens_1 = require("./1700000000001-add-refresh-tokens");
|
|
5
7
|
Object.defineProperty(exports, "AddRefreshTokens1700000000001", { enumerable: true, get: function () { return _1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001; } });
|
|
6
8
|
const _1739500000000_create_security_identity_1 = require("./1739500000000-create-security-identity");
|
|
@@ -13,7 +15,8 @@ const _1739520000000_create_password_reset_tokens_1 = require("./1739520000000-c
|
|
|
13
15
|
Object.defineProperty(exports, "CreatePasswordResetTokens1739520000000", { enumerable: true, get: function () { return _1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000; } });
|
|
14
16
|
const _1739530000000_create_security_user_1 = require("./1739530000000-create-security-user");
|
|
15
17
|
Object.defineProperty(exports, "CreateSecurityUser1739530000000", { enumerable: true, get: function () { return _1739530000000_create_security_user_1.CreateSecurityUser1739530000000; } });
|
|
16
|
-
exports.
|
|
18
|
+
exports.allMigrations = [
|
|
19
|
+
_1739490000000_create_app_user_1.CreateAppUser1739490000000,
|
|
17
20
|
_1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001,
|
|
18
21
|
_1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000,
|
|
19
22
|
_1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000,
|
|
@@ -21,3 +24,4 @@ exports.securityMigrations = [
|
|
|
21
24
|
_1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000,
|
|
22
25
|
_1739530000000_create_security_user_1.CreateSecurityUser1739530000000,
|
|
23
26
|
];
|
|
27
|
+
exports.securityMigrations = exports.allMigrations.filter((migration) => migration !== _1739490000000_create_app_user_1.CreateAppUser1739490000000);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const vitest_1 = require("vitest");
|
|
4
|
+
const _1739490000000_create_app_user_1 = require("./1739490000000-create-app-user");
|
|
4
5
|
const _1700000000001_add_refresh_tokens_1 = require("./1700000000001-add-refresh-tokens");
|
|
5
6
|
const _1739500000000_create_security_identity_1 = require("./1739500000000-create-security-identity");
|
|
6
7
|
const _1739510000000_create_security_roles_1 = require("./1739510000000-create-security-roles");
|
|
@@ -14,6 +15,18 @@ const originalEnv = { ...process.env };
|
|
|
14
15
|
vitest_1.vi.restoreAllMocks();
|
|
15
16
|
});
|
|
16
17
|
(0, vitest_1.describe)("security migrations", () => {
|
|
18
|
+
(0, vitest_1.it)("exports all migration list for test/dev", () => {
|
|
19
|
+
(0, vitest_1.expect)(index_1.allMigrations.length).toBe(7);
|
|
20
|
+
(0, vitest_1.expect)(index_1.allMigrations).toEqual([
|
|
21
|
+
_1739490000000_create_app_user_1.CreateAppUser1739490000000,
|
|
22
|
+
_1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001,
|
|
23
|
+
_1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000,
|
|
24
|
+
_1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000,
|
|
25
|
+
_1739515000000_create_security_user_roles_1.CreateSecurityUserRoles1739515000000,
|
|
26
|
+
_1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000,
|
|
27
|
+
_1739530000000_create_security_user_1.CreateSecurityUser1739530000000,
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
17
30
|
(0, vitest_1.it)("exports migration list", () => {
|
|
18
31
|
(0, vitest_1.expect)(index_1.securityMigrations.length).toBe(6);
|
|
19
32
|
(0, vitest_1.expect)(index_1.securityMigrations).toEqual([
|
|
@@ -25,6 +38,14 @@ const originalEnv = { ...process.env };
|
|
|
25
38
|
_1739530000000_create_security_user_1.CreateSecurityUser1739530000000,
|
|
26
39
|
]);
|
|
27
40
|
});
|
|
41
|
+
(0, vitest_1.it)("runs app user migration up/down", async () => {
|
|
42
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
43
|
+
const migration = new _1739490000000_create_app_user_1.CreateAppUser1739490000000();
|
|
44
|
+
await migration.up({ query });
|
|
45
|
+
await migration.down({ query });
|
|
46
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CREATE TABLE IF NOT EXISTS "public"."app_user"'));
|
|
47
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP TABLE IF EXISTS "public"."app_user"'));
|
|
48
|
+
});
|
|
28
49
|
(0, vitest_1.it)("runs refresh token migration up/down", async () => {
|
|
29
50
|
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
30
51
|
const migration = new _1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001();
|
|
@@ -95,15 +95,9 @@ const resetSchemaBeforeRun = process.env.SECURITY_TEST_RESET_SCHEMA !== "false";
|
|
|
95
95
|
}
|
|
96
96
|
await client.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`);
|
|
97
97
|
await client.query(`SET search_path TO "${schema}", public`);
|
|
98
|
-
await client.query(`
|
|
99
|
-
CREATE TABLE IF NOT EXISTS "${schema}"."app_user" (
|
|
100
|
-
"id" uuid PRIMARY KEY NOT NULL,
|
|
101
|
-
"email" varchar NOT NULL
|
|
102
|
-
)
|
|
103
|
-
`);
|
|
104
98
|
process.env.USER_TABLE = "app_user";
|
|
105
99
|
process.env.USER_TABLE_SCHEMA = schema;
|
|
106
|
-
for (const Migration of migrations_1.
|
|
100
|
+
for (const Migration of migrations_1.allMigrations) {
|
|
107
101
|
await new Migration().up(runner);
|
|
108
102
|
}
|
|
109
103
|
});
|
|
@@ -112,10 +106,9 @@ const resetSchemaBeforeRun = process.env.SECURITY_TEST_RESET_SCHEMA !== "false";
|
|
|
112
106
|
return;
|
|
113
107
|
}
|
|
114
108
|
if (!keepSchemaForDebug) {
|
|
115
|
-
for (const Migration of [...migrations_1.
|
|
109
|
+
for (const Migration of [...migrations_1.allMigrations].reverse()) {
|
|
116
110
|
await new Migration().down(runner);
|
|
117
111
|
}
|
|
118
|
-
await client.query(`DROP TABLE IF EXISTS "${schema}"."app_user"`);
|
|
119
112
|
await client.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
|
|
120
113
|
}
|
|
121
114
|
await client.end();
|
|
@@ -49,23 +49,23 @@ export declare class SecurityWorkflowsController {
|
|
|
49
49
|
} | {
|
|
50
50
|
success: true;
|
|
51
51
|
}>;
|
|
52
|
-
getUserRoles(
|
|
52
|
+
getUserRoles(userId: string): Promise<{
|
|
53
53
|
userId: string;
|
|
54
54
|
roles: string[];
|
|
55
55
|
}>;
|
|
56
|
-
setUserRoles(
|
|
56
|
+
setUserRoles(userId: string, body: {
|
|
57
57
|
roles?: string[];
|
|
58
58
|
}): Promise<{
|
|
59
59
|
userId: string;
|
|
60
60
|
roles: string[];
|
|
61
61
|
}>;
|
|
62
|
-
assignUserRole(
|
|
62
|
+
assignUserRole(userId: string, body: {
|
|
63
63
|
role?: string;
|
|
64
64
|
}): Promise<{
|
|
65
65
|
userId: string;
|
|
66
66
|
roles: string[];
|
|
67
67
|
}>;
|
|
68
|
-
removeUserRole(
|
|
68
|
+
removeUserRole(userId: string, role: string): Promise<{
|
|
69
69
|
userId: string;
|
|
70
70
|
roles: string[];
|
|
71
71
|
}>;
|
|
@@ -53,23 +53,23 @@ let SecurityWorkflowsController = class SecurityWorkflowsController {
|
|
|
53
53
|
async removeRole(role) {
|
|
54
54
|
return this.workflowsService.removeRole(role);
|
|
55
55
|
}
|
|
56
|
-
async getUserRoles(
|
|
57
|
-
return this.workflowsService.getUserRoles(
|
|
56
|
+
async getUserRoles(userId) {
|
|
57
|
+
return this.workflowsService.getUserRoles(userId);
|
|
58
58
|
}
|
|
59
|
-
async setUserRoles(
|
|
59
|
+
async setUserRoles(userId, body) {
|
|
60
60
|
if (!Array.isArray(body.roles)) {
|
|
61
61
|
throw new common_1.BadRequestException("roles must be an array");
|
|
62
62
|
}
|
|
63
|
-
return this.workflowsService.setUserRoles(
|
|
63
|
+
return this.workflowsService.setUserRoles(userId, body.roles);
|
|
64
64
|
}
|
|
65
|
-
async assignUserRole(
|
|
65
|
+
async assignUserRole(userId, body) {
|
|
66
66
|
if (!body.role || !body.role.trim()) {
|
|
67
67
|
throw new common_1.BadRequestException("role is required");
|
|
68
68
|
}
|
|
69
|
-
return this.workflowsService.assignRoleToUser(
|
|
69
|
+
return this.workflowsService.assignRoleToUser(userId, body.role);
|
|
70
70
|
}
|
|
71
|
-
async removeUserRole(
|
|
72
|
-
return this.workflowsService.removeRoleFromUser(
|
|
71
|
+
async removeUserRole(userId, role) {
|
|
72
|
+
return this.workflowsService.removeRoleFromUser(userId, role);
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
exports.SecurityWorkflowsController = SecurityWorkflowsController;
|
|
@@ -136,45 +136,65 @@ __decorate([
|
|
|
136
136
|
__metadata("design:returntype", Promise)
|
|
137
137
|
], SecurityWorkflowsController.prototype, "removeRole", null);
|
|
138
138
|
__decorate([
|
|
139
|
-
(0, common_1.Get)("users/:
|
|
139
|
+
(0, common_1.Get)("users/:userId/roles"),
|
|
140
140
|
(0, common_1.UseGuards)(security_jwt_guard_1.SecurityJwtGuard, security_admin_guard_1.SecurityAdminGuard),
|
|
141
141
|
(0, swagger_1.ApiOperation)({ summary: "Get assigned roles for a user" }),
|
|
142
|
+
(0, swagger_1.ApiParam)({
|
|
143
|
+
name: "userId",
|
|
144
|
+
description: "User id from app_user.id",
|
|
145
|
+
type: String,
|
|
146
|
+
}),
|
|
142
147
|
(0, swagger_1.ApiBearerAuth)(),
|
|
143
|
-
__param(0, (0, common_1.Param)("
|
|
148
|
+
__param(0, (0, common_1.Param)("userId")),
|
|
144
149
|
__metadata("design:type", Function),
|
|
145
150
|
__metadata("design:paramtypes", [String]),
|
|
146
151
|
__metadata("design:returntype", Promise)
|
|
147
152
|
], SecurityWorkflowsController.prototype, "getUserRoles", null);
|
|
148
153
|
__decorate([
|
|
149
|
-
(0, common_1.Put)("users/:
|
|
154
|
+
(0, common_1.Put)("users/:userId/roles"),
|
|
150
155
|
(0, common_1.UseGuards)(security_jwt_guard_1.SecurityJwtGuard, security_admin_guard_1.SecurityAdminGuard),
|
|
151
156
|
(0, swagger_1.ApiOperation)({ summary: "Replace user roles" }),
|
|
157
|
+
(0, swagger_1.ApiParam)({
|
|
158
|
+
name: "userId",
|
|
159
|
+
description: "User id from app_user.id",
|
|
160
|
+
type: String,
|
|
161
|
+
}),
|
|
152
162
|
(0, swagger_1.ApiBearerAuth)(),
|
|
153
163
|
(0, swagger_1.ApiBody)({ type: workflows_dto_1.SetUserRolesDto }),
|
|
154
|
-
__param(0, (0, common_1.Param)("
|
|
164
|
+
__param(0, (0, common_1.Param)("userId")),
|
|
155
165
|
__param(1, (0, common_1.Body)()),
|
|
156
166
|
__metadata("design:type", Function),
|
|
157
167
|
__metadata("design:paramtypes", [String, Object]),
|
|
158
168
|
__metadata("design:returntype", Promise)
|
|
159
169
|
], SecurityWorkflowsController.prototype, "setUserRoles", null);
|
|
160
170
|
__decorate([
|
|
161
|
-
(0, common_1.Post)("users/:
|
|
171
|
+
(0, common_1.Post)("users/:userId/roles"),
|
|
162
172
|
(0, common_1.UseGuards)(security_jwt_guard_1.SecurityJwtGuard, security_admin_guard_1.SecurityAdminGuard),
|
|
163
173
|
(0, swagger_1.ApiOperation)({ summary: "Assign one role to a user" }),
|
|
174
|
+
(0, swagger_1.ApiParam)({
|
|
175
|
+
name: "userId",
|
|
176
|
+
description: "User id from app_user.id",
|
|
177
|
+
type: String,
|
|
178
|
+
}),
|
|
164
179
|
(0, swagger_1.ApiBearerAuth)(),
|
|
165
180
|
(0, swagger_1.ApiBody)({ type: workflows_dto_1.AssignRoleDto }),
|
|
166
|
-
__param(0, (0, common_1.Param)("
|
|
181
|
+
__param(0, (0, common_1.Param)("userId")),
|
|
167
182
|
__param(1, (0, common_1.Body)()),
|
|
168
183
|
__metadata("design:type", Function),
|
|
169
184
|
__metadata("design:paramtypes", [String, Object]),
|
|
170
185
|
__metadata("design:returntype", Promise)
|
|
171
186
|
], SecurityWorkflowsController.prototype, "assignUserRole", null);
|
|
172
187
|
__decorate([
|
|
173
|
-
(0, common_1.Delete)("users/:
|
|
188
|
+
(0, common_1.Delete)("users/:userId/roles/:role"),
|
|
174
189
|
(0, common_1.UseGuards)(security_jwt_guard_1.SecurityJwtGuard, security_admin_guard_1.SecurityAdminGuard),
|
|
175
190
|
(0, swagger_1.ApiOperation)({ summary: "Remove one role from a user" }),
|
|
191
|
+
(0, swagger_1.ApiParam)({
|
|
192
|
+
name: "userId",
|
|
193
|
+
description: "User id from app_user.id",
|
|
194
|
+
type: String,
|
|
195
|
+
}),
|
|
176
196
|
(0, swagger_1.ApiBearerAuth)(),
|
|
177
|
-
__param(0, (0, common_1.Param)("
|
|
197
|
+
__param(0, (0, common_1.Param)("userId")),
|
|
178
198
|
__param(1, (0, common_1.Param)("role")),
|
|
179
199
|
__metadata("design:type", Function),
|
|
180
200
|
__metadata("design:paramtypes", [String, String]),
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scryan7371/sdr-security",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Reusable auth/security capability for API and app clients.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./dist/index.js",
|
|
8
|
-
"./nest":
|
|
8
|
+
"./nest": {
|
|
9
|
+
"types": "./dist/nest/index.d.ts",
|
|
10
|
+
"default": "./dist/nest/index.js"
|
|
11
|
+
}
|
|
9
12
|
},
|
|
10
13
|
"types": "dist/index.d.ts",
|
|
11
14
|
"publishConfig": {
|
|
@@ -72,5 +75,12 @@
|
|
|
72
75
|
"typescript": "5.9.3",
|
|
73
76
|
"typescript-eslint": "8.56.0",
|
|
74
77
|
"vitest": "4.0.18"
|
|
78
|
+
},
|
|
79
|
+
"typesVersions": {
|
|
80
|
+
"*": {
|
|
81
|
+
"nest": [
|
|
82
|
+
"dist/nest/index.d.ts"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
75
85
|
}
|
|
76
86
|
}
|
|
@@ -14,8 +14,6 @@ export class AddRefreshTokens1700000000001 {
|
|
|
14
14
|
"revoked_at" timestamptz,
|
|
15
15
|
"userId" uuid,
|
|
16
16
|
"created_at" timestamptz NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
|
17
|
-
CONSTRAINT "CHK_refresh_token_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
|
-
CONSTRAINT "CHK_refresh_token_userId_uuidv7" CHECK ("userId" IS NULL OR "userId"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
19
17
|
CONSTRAINT "FK_refresh_token_user" FOREIGN KEY ("userId") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
|
20
18
|
)
|
|
21
19
|
`);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export class CreateAppUser1739490000000 {
|
|
2
|
+
name = "CreateAppUser1739490000000";
|
|
3
|
+
|
|
4
|
+
async up(queryRunner: {
|
|
5
|
+
query: (sql: string) => Promise<unknown>;
|
|
6
|
+
}): Promise<void> {
|
|
7
|
+
const userTableRef = getUserTableReference();
|
|
8
|
+
|
|
9
|
+
await queryRunner.query(`
|
|
10
|
+
CREATE TABLE IF NOT EXISTS ${userTableRef} (
|
|
11
|
+
"id" uuid PRIMARY KEY NOT NULL,
|
|
12
|
+
"email" varchar NOT NULL
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async down(queryRunner: {
|
|
18
|
+
query: (sql: string) => Promise<unknown>;
|
|
19
|
+
}): Promise<void> {
|
|
20
|
+
const userTableRef = getUserTableReference();
|
|
21
|
+
await queryRunner.query(`DROP TABLE IF EXISTS ${userTableRef}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const getSafeIdentifier = (value: string | undefined, fallback: string) => {
|
|
26
|
+
const resolved = value?.trim() || fallback;
|
|
27
|
+
if (!resolved || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(resolved)) {
|
|
28
|
+
throw new Error(`Invalid SQL identifier: ${resolved}`);
|
|
29
|
+
}
|
|
30
|
+
return resolved;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getUserTableReference = () => {
|
|
34
|
+
const table = getSafeIdentifier(process.env.USER_TABLE, "app_user");
|
|
35
|
+
const schema = process.env.USER_TABLE_SCHEMA
|
|
36
|
+
? getSafeIdentifier(process.env.USER_TABLE_SCHEMA, "public")
|
|
37
|
+
: "public";
|
|
38
|
+
return `"${schema}"."${table}"`;
|
|
39
|
+
};
|
|
@@ -14,8 +14,6 @@ export class CreateSecurityIdentity1739500000000 {
|
|
|
14
14
|
"provider_subject" varchar NOT NULL,
|
|
15
15
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
16
16
|
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
17
|
-
CONSTRAINT "CHK_security_identity_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
|
-
CONSTRAINT "CHK_security_identity_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
19
17
|
CONSTRAINT "FK_security_identity_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
20
18
|
)
|
|
21
19
|
`);
|
|
@@ -11,8 +11,7 @@ export class CreateSecurityRoles1739510000000 {
|
|
|
11
11
|
"description" text,
|
|
12
12
|
"is_system" boolean NOT NULL DEFAULT false,
|
|
13
13
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
14
|
-
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
15
|
-
CONSTRAINT "CHK_security_role_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$')
|
|
14
|
+
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
16
15
|
)
|
|
17
16
|
`);
|
|
18
17
|
|
|
@@ -12,9 +12,6 @@ export class CreateSecurityUserRoles1739515000000 {
|
|
|
12
12
|
"user_id" uuid NOT NULL,
|
|
13
13
|
"role_id" uuid NOT NULL,
|
|
14
14
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
15
|
-
CONSTRAINT "CHK_security_user_role_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
16
|
-
CONSTRAINT "CHK_security_user_role_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
17
|
-
CONSTRAINT "CHK_security_user_role_role_id_uuidv7" CHECK ("role_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
15
|
CONSTRAINT "FK_security_user_role_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE,
|
|
19
16
|
CONSTRAINT "FK_security_user_role_role_id" FOREIGN KEY ("role_id") REFERENCES "security_role" ("id") ON DELETE CASCADE
|
|
20
17
|
)
|
|
@@ -14,8 +14,6 @@ export class CreatePasswordResetTokens1739520000000 {
|
|
|
14
14
|
"expires_at" timestamptz NOT NULL,
|
|
15
15
|
"used_at" timestamptz,
|
|
16
16
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
17
|
-
CONSTRAINT "CHK_security_password_reset_token_id_uuidv7" CHECK ("id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
18
|
-
CONSTRAINT "CHK_security_password_reset_token_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
19
17
|
CONSTRAINT "FK_security_password_reset_token_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
20
18
|
)
|
|
21
19
|
`);
|
|
@@ -15,7 +15,6 @@ export class CreateSecurityUser1739530000000 {
|
|
|
15
15
|
"admin_approved_at" timestamptz,
|
|
16
16
|
"is_active" boolean NOT NULL DEFAULT true,
|
|
17
17
|
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
18
|
-
CONSTRAINT "CHK_security_user_user_id_uuidv7" CHECK ("user_id"::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'),
|
|
19
18
|
CONSTRAINT "FK_security_user_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
20
19
|
)
|
|
21
20
|
`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CreateAppUser1739490000000 } from "./1739490000000-create-app-user";
|
|
1
2
|
import { AddRefreshTokens1700000000001 } from "./1700000000001-add-refresh-tokens";
|
|
2
3
|
import { CreateSecurityIdentity1739500000000 } from "./1739500000000-create-security-identity";
|
|
3
4
|
import { CreateSecurityRoles1739510000000 } from "./1739510000000-create-security-roles";
|
|
@@ -5,7 +6,8 @@ import { CreateSecurityUserRoles1739515000000 } from "./1739515000000-create-sec
|
|
|
5
6
|
import { CreatePasswordResetTokens1739520000000 } from "./1739520000000-create-password-reset-tokens";
|
|
6
7
|
import { CreateSecurityUser1739530000000 } from "./1739530000000-create-security-user";
|
|
7
8
|
|
|
8
|
-
export const
|
|
9
|
+
export const allMigrations = [
|
|
10
|
+
CreateAppUser1739490000000,
|
|
9
11
|
AddRefreshTokens1700000000001,
|
|
10
12
|
CreateSecurityIdentity1739500000000,
|
|
11
13
|
CreateSecurityRoles1739510000000,
|
|
@@ -14,7 +16,12 @@ export const securityMigrations = [
|
|
|
14
16
|
CreateSecurityUser1739530000000,
|
|
15
17
|
];
|
|
16
18
|
|
|
19
|
+
export const securityMigrations = allMigrations.filter(
|
|
20
|
+
(migration) => migration !== CreateAppUser1739490000000,
|
|
21
|
+
);
|
|
22
|
+
|
|
17
23
|
export {
|
|
24
|
+
CreateAppUser1739490000000,
|
|
18
25
|
AddRefreshTokens1700000000001,
|
|
19
26
|
CreateSecurityIdentity1739500000000,
|
|
20
27
|
CreateSecurityRoles1739510000000,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { CreateAppUser1739490000000 } from "./1739490000000-create-app-user";
|
|
2
3
|
import { AddRefreshTokens1700000000001 } from "./1700000000001-add-refresh-tokens";
|
|
3
4
|
import { CreateSecurityIdentity1739500000000 } from "./1739500000000-create-security-identity";
|
|
4
5
|
import { CreateSecurityRoles1739510000000 } from "./1739510000000-create-security-roles";
|
|
5
6
|
import { CreateSecurityUserRoles1739515000000 } from "./1739515000000-create-security-user-roles";
|
|
6
7
|
import { CreatePasswordResetTokens1739520000000 } from "./1739520000000-create-password-reset-tokens";
|
|
7
8
|
import { CreateSecurityUser1739530000000 } from "./1739530000000-create-security-user";
|
|
8
|
-
import { securityMigrations } from "./index";
|
|
9
|
+
import { allMigrations, securityMigrations } from "./index";
|
|
9
10
|
|
|
10
11
|
const originalEnv = { ...process.env };
|
|
11
12
|
|
|
@@ -15,6 +16,19 @@ afterEach(() => {
|
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
describe("security migrations", () => {
|
|
19
|
+
it("exports all migration list for test/dev", () => {
|
|
20
|
+
expect(allMigrations.length).toBe(7);
|
|
21
|
+
expect(allMigrations).toEqual([
|
|
22
|
+
CreateAppUser1739490000000,
|
|
23
|
+
AddRefreshTokens1700000000001,
|
|
24
|
+
CreateSecurityIdentity1739500000000,
|
|
25
|
+
CreateSecurityRoles1739510000000,
|
|
26
|
+
CreateSecurityUserRoles1739515000000,
|
|
27
|
+
CreatePasswordResetTokens1739520000000,
|
|
28
|
+
CreateSecurityUser1739530000000,
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
|
|
18
32
|
it("exports migration list", () => {
|
|
19
33
|
expect(securityMigrations.length).toBe(6);
|
|
20
34
|
expect(securityMigrations).toEqual([
|
|
@@ -27,6 +41,21 @@ describe("security migrations", () => {
|
|
|
27
41
|
]);
|
|
28
42
|
});
|
|
29
43
|
|
|
44
|
+
it("runs app user migration up/down", async () => {
|
|
45
|
+
const query = vi.fn().mockResolvedValue(undefined);
|
|
46
|
+
const migration = new CreateAppUser1739490000000();
|
|
47
|
+
|
|
48
|
+
await migration.up({ query });
|
|
49
|
+
await migration.down({ query });
|
|
50
|
+
|
|
51
|
+
expect(query).toHaveBeenCalledWith(
|
|
52
|
+
expect.stringContaining('CREATE TABLE IF NOT EXISTS "public"."app_user"'),
|
|
53
|
+
);
|
|
54
|
+
expect(query).toHaveBeenCalledWith(
|
|
55
|
+
expect.stringContaining('DROP TABLE IF EXISTS "public"."app_user"'),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
30
59
|
it("runs refresh token migration up/down", async () => {
|
|
31
60
|
const query = vi.fn().mockResolvedValue(undefined);
|
|
32
61
|
const migration = new AddRefreshTokens1700000000001();
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
4
|
import { Client, ClientConfig } from "pg";
|
|
5
|
-
import {
|
|
5
|
+
import { allMigrations } from "../api/migrations";
|
|
6
6
|
|
|
7
7
|
type QueryRunnerLike = {
|
|
8
8
|
query: (sql: string, params?: unknown[]) => Promise<unknown>;
|
|
@@ -119,17 +119,10 @@ describe("database integration", () => {
|
|
|
119
119
|
}
|
|
120
120
|
await client.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`);
|
|
121
121
|
await client.query(`SET search_path TO "${schema}", public`);
|
|
122
|
-
await client.query(`
|
|
123
|
-
CREATE TABLE IF NOT EXISTS "${schema}"."app_user" (
|
|
124
|
-
"id" uuid PRIMARY KEY NOT NULL,
|
|
125
|
-
"email" varchar NOT NULL
|
|
126
|
-
)
|
|
127
|
-
`);
|
|
128
|
-
|
|
129
122
|
process.env.USER_TABLE = "app_user";
|
|
130
123
|
process.env.USER_TABLE_SCHEMA = schema;
|
|
131
124
|
|
|
132
|
-
for (const Migration of
|
|
125
|
+
for (const Migration of allMigrations) {
|
|
133
126
|
await new Migration().up(runner as never);
|
|
134
127
|
}
|
|
135
128
|
});
|
|
@@ -140,10 +133,9 @@ describe("database integration", () => {
|
|
|
140
133
|
}
|
|
141
134
|
|
|
142
135
|
if (!keepSchemaForDebug) {
|
|
143
|
-
for (const Migration of [...
|
|
136
|
+
for (const Migration of [...allMigrations].reverse()) {
|
|
144
137
|
await new Migration().down(runner as never);
|
|
145
138
|
}
|
|
146
|
-
await client.query(`DROP TABLE IF EXISTS "${schema}"."app_user"`);
|
|
147
139
|
await client.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
|
|
148
140
|
}
|
|
149
141
|
await client.end();
|
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
Put,
|
|
11
11
|
UseGuards,
|
|
12
12
|
} from "@nestjs/common";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ApiBearerAuth,
|
|
15
|
+
ApiBody,
|
|
16
|
+
ApiOperation,
|
|
17
|
+
ApiParam,
|
|
18
|
+
ApiTags,
|
|
19
|
+
} from "@nestjs/swagger";
|
|
14
20
|
import { SecurityAdminGuard } from "./security-admin.guard";
|
|
15
21
|
import { SecurityJwtGuard } from "./security-jwt.guard";
|
|
16
22
|
import { SecurityWorkflowsService } from "./security-workflows.service";
|
|
@@ -101,49 +107,72 @@ export class SecurityWorkflowsController {
|
|
|
101
107
|
return this.workflowsService.removeRole(role);
|
|
102
108
|
}
|
|
103
109
|
|
|
104
|
-
@Get("users/:
|
|
110
|
+
@Get("users/:userId/roles")
|
|
105
111
|
@UseGuards(SecurityJwtGuard, SecurityAdminGuard)
|
|
106
112
|
@ApiOperation({ summary: "Get assigned roles for a user" })
|
|
113
|
+
@ApiParam({
|
|
114
|
+
name: "userId",
|
|
115
|
+
description: "User id from app_user.id",
|
|
116
|
+
type: String,
|
|
117
|
+
})
|
|
107
118
|
@ApiBearerAuth()
|
|
108
|
-
async getUserRoles(@Param("
|
|
109
|
-
return this.workflowsService.getUserRoles(
|
|
119
|
+
async getUserRoles(@Param("userId") userId: string) {
|
|
120
|
+
return this.workflowsService.getUserRoles(userId);
|
|
110
121
|
}
|
|
111
122
|
|
|
112
|
-
@Put("users/:
|
|
123
|
+
@Put("users/:userId/roles")
|
|
113
124
|
@UseGuards(SecurityJwtGuard, SecurityAdminGuard)
|
|
114
125
|
@ApiOperation({ summary: "Replace user roles" })
|
|
126
|
+
@ApiParam({
|
|
127
|
+
name: "userId",
|
|
128
|
+
description: "User id from app_user.id",
|
|
129
|
+
type: String,
|
|
130
|
+
})
|
|
115
131
|
@ApiBearerAuth()
|
|
116
132
|
@ApiBody({ type: SetUserRolesDto })
|
|
117
133
|
async setUserRoles(
|
|
118
|
-
@Param("
|
|
134
|
+
@Param("userId") userId: string,
|
|
119
135
|
@Body() body: { roles?: string[] },
|
|
120
136
|
) {
|
|
121
137
|
if (!Array.isArray(body.roles)) {
|
|
122
138
|
throw new BadRequestException("roles must be an array");
|
|
123
139
|
}
|
|
124
|
-
return this.workflowsService.setUserRoles(
|
|
140
|
+
return this.workflowsService.setUserRoles(userId, body.roles);
|
|
125
141
|
}
|
|
126
142
|
|
|
127
|
-
@Post("users/:
|
|
143
|
+
@Post("users/:userId/roles")
|
|
128
144
|
@UseGuards(SecurityJwtGuard, SecurityAdminGuard)
|
|
129
145
|
@ApiOperation({ summary: "Assign one role to a user" })
|
|
146
|
+
@ApiParam({
|
|
147
|
+
name: "userId",
|
|
148
|
+
description: "User id from app_user.id",
|
|
149
|
+
type: String,
|
|
150
|
+
})
|
|
130
151
|
@ApiBearerAuth()
|
|
131
152
|
@ApiBody({ type: AssignRoleDto })
|
|
132
153
|
async assignUserRole(
|
|
133
|
-
@Param("
|
|
154
|
+
@Param("userId") userId: string,
|
|
134
155
|
@Body() body: { role?: string },
|
|
135
156
|
) {
|
|
136
157
|
if (!body.role || !body.role.trim()) {
|
|
137
158
|
throw new BadRequestException("role is required");
|
|
138
159
|
}
|
|
139
|
-
return this.workflowsService.assignRoleToUser(
|
|
160
|
+
return this.workflowsService.assignRoleToUser(userId, body.role);
|
|
140
161
|
}
|
|
141
162
|
|
|
142
|
-
@Delete("users/:
|
|
163
|
+
@Delete("users/:userId/roles/:role")
|
|
143
164
|
@UseGuards(SecurityJwtGuard, SecurityAdminGuard)
|
|
144
165
|
@ApiOperation({ summary: "Remove one role from a user" })
|
|
166
|
+
@ApiParam({
|
|
167
|
+
name: "userId",
|
|
168
|
+
description: "User id from app_user.id",
|
|
169
|
+
type: String,
|
|
170
|
+
})
|
|
145
171
|
@ApiBearerAuth()
|
|
146
|
-
async removeUserRole(
|
|
147
|
-
|
|
172
|
+
async removeUserRole(
|
|
173
|
+
@Param("userId") userId: string,
|
|
174
|
+
@Param("role") role: string,
|
|
175
|
+
) {
|
|
176
|
+
return this.workflowsService.removeRoleFromUser(userId, role);
|
|
148
177
|
}
|
|
149
178
|
}
|