rl-core-api 0.18.10 → 0.19.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.
Files changed (54) hide show
  1. package/dist/core/auth/decorators/allowPendingPassword.decorator.d.ts +3 -0
  2. package/dist/core/auth/decorators/allowPendingPassword.decorator.js +7 -0
  3. package/dist/core/auth/guards/passwordPolicy.guard.d.ts +8 -0
  4. package/dist/core/auth/guards/passwordPolicy.guard.js +39 -0
  5. package/dist/core/config/env.schema.d.ts +2 -2
  6. package/dist/core/config/env.schema.js +33 -3
  7. package/dist/core/context/requestLog.context.d.ts +1 -0
  8. package/dist/core/context/requestLog.context.js +7 -1
  9. package/dist/core/database/migrations/1789603200000-AuthTokenClient.d.ts +6 -0
  10. package/dist/core/database/migrations/1789603200000-AuthTokenClient.js +16 -0
  11. package/dist/core/database/migrations/index.js +2 -0
  12. package/dist/core/export/export.controller.js +0 -2
  13. package/dist/core/filters/appErrorCode.enum.d.ts +2 -1
  14. package/dist/core/filters/appErrorCode.enum.js +1 -0
  15. package/dist/core/images/imageFetch.service.js +79 -15
  16. package/dist/core/images/images.controller.js +0 -2
  17. package/dist/core/images/images.service.d.ts +1 -0
  18. package/dist/core/images/images.service.js +13 -3
  19. package/dist/core/queue/queueAdmin.controller.js +0 -2
  20. package/dist/core/utils/codedError.util.d.ts +2 -1
  21. package/dist/core/utils/codedError.util.js +8 -0
  22. package/dist/features/audit/audit.module.js +5 -1
  23. package/dist/features/audit/domain/audit.service.d.ts +5 -2
  24. package/dist/features/audit/domain/audit.service.js +8 -4
  25. package/dist/features/audit/infra/repositories/requestLog.repository.d.ts +1 -1
  26. package/dist/features/audit/infra/repositories/requestLog.repository.js +11 -2
  27. package/dist/features/audit/presentation/audit.controller.d.ts +2 -1
  28. package/dist/features/audit/presentation/audit.controller.js +8 -7
  29. package/dist/features/audit/presentation/audit.interceptor.js +2 -2
  30. package/dist/features/auth/auth.module.js +4 -0
  31. package/dist/features/auth/domain/auth.service.d.ts +1 -2
  32. package/dist/features/auth/domain/auth.service.js +2 -2
  33. package/dist/features/auth/domain/enums/tokenClient.enum.d.ts +7 -0
  34. package/dist/features/auth/domain/enums/tokenClient.enum.js +13 -0
  35. package/dist/features/auth/domain/token.service.d.ts +1 -1
  36. package/dist/features/auth/domain/token.service.js +7 -1
  37. package/dist/features/auth/infra/schema/authToken.schema.d.ts +2 -0
  38. package/dist/features/auth/infra/schema/authToken.schema.js +4 -0
  39. package/dist/features/auth/presentation/auth.controller.d.ts +2 -2
  40. package/dist/features/auth/presentation/auth.controller.js +10 -7
  41. package/dist/features/auth/presentation/commands/password.command.d.ts +3 -0
  42. package/dist/features/auth/presentation/commands/password.command.js +9 -1
  43. package/dist/features/notifications/presentation/notifications.controller.js +0 -2
  44. package/dist/features/rbac/domain/rbac.catalog.js +5 -1
  45. package/dist/features/rbac/domain/rbac.service.d.ts +3 -1
  46. package/dist/features/rbac/domain/rbac.service.js +30 -2
  47. package/dist/features/rbac/presentation/rbac.controller.d.ts +1 -1
  48. package/dist/features/rbac/presentation/rbac.controller.js +4 -5
  49. package/dist/features/users/domain/users.service.d.ts +3 -1
  50. package/dist/features/users/domain/users.service.js +5 -1
  51. package/dist/features/users/presentation/users.controller.js +7 -17
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +8 -4
  54. package/package.json +1 -1
@@ -0,0 +1,3 @@
1
+ import { CustomDecorator } from "@nestjs/common";
2
+ export declare const ALLOW_PENDING_PASSWORD_KEY = "allowPendingPassword";
3
+ export declare const AllowPendingPassword: () => CustomDecorator<string>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AllowPendingPassword = exports.ALLOW_PENDING_PASSWORD_KEY = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.ALLOW_PENDING_PASSWORD_KEY = "allowPendingPassword";
6
+ const AllowPendingPassword = () => (0, common_1.SetMetadata)(exports.ALLOW_PENDING_PASSWORD_KEY, true);
7
+ exports.AllowPendingPassword = AllowPendingPassword;
@@ -0,0 +1,8 @@
1
+ import { CanActivate, ExecutionContext } from "@nestjs/common";
2
+ import { Reflector } from "@nestjs/core";
3
+ export declare const PASSWORD_CHANGE_REQUIRED_MESSAGE = "Troque sua senha para continuar";
4
+ export declare class PasswordPolicyGuard implements CanActivate {
5
+ private readonly reflector;
6
+ constructor(reflector: Reflector);
7
+ canActivate(context: ExecutionContext): boolean;
8
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PasswordPolicyGuard = exports.PASSWORD_CHANGE_REQUIRED_MESSAGE = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@nestjs/core");
15
+ const allowPendingPassword_decorator_1 = require("../decorators/allowPendingPassword.decorator");
16
+ const appErrorCode_enum_1 = require("../../filters/appErrorCode.enum");
17
+ const codedError_util_1 = require("../../utils/codedError.util");
18
+ exports.PASSWORD_CHANGE_REQUIRED_MESSAGE = "Troque sua senha para continuar";
19
+ let PasswordPolicyGuard = class PasswordPolicyGuard {
20
+ constructor(reflector) {
21
+ this.reflector = reflector;
22
+ }
23
+ canActivate(context) {
24
+ const user = context.switchToHttp().getRequest().user;
25
+ if (!user || (!user.mustChangePassword && !user.passwordExpired)) {
26
+ return true;
27
+ }
28
+ const allowed = this.reflector.getAllAndOverride(allowPendingPassword_decorator_1.ALLOW_PENDING_PASSWORD_KEY, [context.getHandler(), context.getClass()]);
29
+ if (allowed) {
30
+ return true;
31
+ }
32
+ throw (0, codedError_util_1.codedForbidden)(exports.PASSWORD_CHANGE_REQUIRED_MESSAGE, appErrorCode_enum_1.AppErrorCode.PASSWORD_CHANGE_REQUIRED);
33
+ }
34
+ };
35
+ exports.PasswordPolicyGuard = PasswordPolicyGuard;
36
+ exports.PasswordPolicyGuard = PasswordPolicyGuard = __decorate([
37
+ (0, common_1.Injectable)(),
38
+ __metadata("design:paramtypes", [core_1.Reflector])
39
+ ], PasswordPolicyGuard);
@@ -81,8 +81,8 @@ export declare const envSchema: z.ZodObject<{
81
81
  AUDIT_IP_SALT: z.ZodOptional<z.ZodString>;
82
82
  NOTIFICATION_RETENTION_DAYS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
83
83
  SEED_ADMIN_NAME: z.ZodDefault<z.ZodString>;
84
- SEED_ADMIN_EMAIL: z.ZodDefault<z.ZodString>;
85
- SEED_ADMIN_PASSWORD: z.ZodDefault<z.ZodString>;
84
+ SEED_ADMIN_EMAIL: z.ZodOptional<z.ZodString>;
85
+ SEED_ADMIN_PASSWORD: z.ZodOptional<z.ZodString>;
86
86
  }, z.core.$strip>;
87
87
  export type Env = z.infer<typeof envSchema>;
88
88
  export declare function validateEnv(config: Record<string, unknown>): Env;
@@ -6,6 +6,19 @@ const zod_1 = require("zod");
6
6
  const booleanFromString = zod_1.z
7
7
  .enum(["true", "false"])
8
8
  .transform((v) => v === "true");
9
+ const PLACEHOLDER_SECRETS = new Set([
10
+ "changeme",
11
+ "change-me",
12
+ "admin",
13
+ "admin@123",
14
+ "admin123",
15
+ "password",
16
+ "senha",
17
+ "secret",
18
+ "redis",
19
+ "root",
20
+ "123456",
21
+ ]);
9
22
  exports.envSchema = zod_1.z
10
23
  .object({
11
24
  NODE_ENV: zod_1.z
@@ -98,8 +111,8 @@ exports.envSchema = zod_1.z
98
111
  .positive()
99
112
  .default(90),
100
113
  SEED_ADMIN_NAME: zod_1.z.string().default("Administrador"),
101
- SEED_ADMIN_EMAIL: zod_1.z.string().email().default("admin@admin.com"),
102
- SEED_ADMIN_PASSWORD: zod_1.z.string().min(1).default("admin@123"),
114
+ SEED_ADMIN_EMAIL: zod_1.z.string().email().optional(),
115
+ SEED_ADMIN_PASSWORD: zod_1.z.string().min(1).optional(),
103
116
  })
104
117
  .superRefine((env, ctx) => {
105
118
  if (env.NODE_ENV === "production" && !env.COOKIE_SECURE) {
@@ -109,9 +122,26 @@ exports.envSchema = zod_1.z
109
122
  message: "COOKIE_SECURE deve ser true quando NODE_ENV=production",
110
123
  });
111
124
  }
125
+ if (env.NODE_ENV === "production") {
126
+ for (const [key, value] of Object.entries({
127
+ DB_PASSWORD: env.DB_PASSWORD,
128
+ REDIS_PASSWORD: env.REDIS_PASSWORD,
129
+ SEED_ADMIN_PASSWORD: env.SEED_ADMIN_PASSWORD,
130
+ EMAIL_PASSWORD: env.EMAIL_PASSWORD,
131
+ })) {
132
+ if (value && PLACEHOLDER_SECRETS.has(value.toLowerCase())) {
133
+ ctx.addIssue({
134
+ code: zod_1.z.ZodIssueCode.custom,
135
+ path: [key],
136
+ message: `${key} está com valor de exemplo — defina um segredo real em produção`,
137
+ });
138
+ }
139
+ }
140
+ }
112
141
  });
113
142
  function validateEnv(config) {
114
- const parsed = exports.envSchema.safeParse(config);
143
+ const declared = Object.fromEntries(Object.entries(config).filter(([, value]) => value !== ""));
144
+ const parsed = exports.envSchema.safeParse(declared);
115
145
  if (!parsed.success) {
116
146
  const issues = parsed.error.issues
117
147
  .map((i) => ` - ${i.path.join(".")}: ${i.message}`)
@@ -5,6 +5,7 @@ export interface RequestLogContext {
5
5
  userId: string | null;
6
6
  }
7
7
  export declare const routePathOf: (request: Request) => string | null;
8
+ export declare const redactPath: (path: string) => string;
8
9
  export declare const setRequestLogContext: (request: Request, context: RequestLogContext) => void;
9
10
  export declare const getRequestLogContext: (request: Request) => RequestLogContext | null;
10
11
  export declare const setRequestLogUser: (request: Request, userId: string | null) => void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setRequestLogUser = exports.getRequestLogContext = exports.setRequestLogContext = exports.routePathOf = void 0;
3
+ exports.setRequestLogUser = exports.getRequestLogContext = exports.setRequestLogContext = exports.redactPath = exports.routePathOf = void 0;
4
4
  const KEY = "__requestLogContext";
5
5
  const routePathOf = (request) => {
6
6
  const route = request.route;
@@ -11,6 +11,12 @@ const routePathOf = (request) => {
11
11
  return null;
12
12
  };
13
13
  exports.routePathOf = routePathOf;
14
+ const SECRET_SEGMENT = /^(?:[a-f0-9]{40,}|[\w-]+\.[\w-]+\.[\w-]+)$/i;
15
+ const redactPath = (path) => path
16
+ .split("/")
17
+ .map((segment) => (SECRET_SEGMENT.test(segment) ? "[redacted]" : segment))
18
+ .join("/");
19
+ exports.redactPath = redactPath;
14
20
  const setRequestLogContext = (request, context) => {
15
21
  request[KEY] = context;
16
22
  };
@@ -0,0 +1,6 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+ export declare class AuthTokenClient1789603200000 implements MigrationInterface {
3
+ name: string;
4
+ up(queryRunner: QueryRunner): Promise<void>;
5
+ down(queryRunner: QueryRunner): Promise<void>;
6
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthTokenClient1789603200000 = void 0;
4
+ class AuthTokenClient1789603200000 {
5
+ constructor() {
6
+ this.name = "AuthTokenClient1789603200000";
7
+ }
8
+ async up(queryRunner) {
9
+ await queryRunner.query(`ALTER TABLE auth_tokens
10
+ ADD COLUMN client tinyint unsigned NULL COMMENT '0=web, 1=mobile' AFTER family`);
11
+ }
12
+ async down(queryRunner) {
13
+ await queryRunner.query(`ALTER TABLE auth_tokens DROP COLUMN client`);
14
+ }
15
+ }
16
+ exports.AuthTokenClient1789603200000 = AuthTokenClient1789603200000;
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.coreMigrations = void 0;
4
4
  const _1789171200000_NotificationDismissal_1 = require("./1789171200000-NotificationDismissal");
5
+ const _1789603200000_AuthTokenClient_1 = require("./1789603200000-AuthTokenClient");
5
6
  const _1710000000000_InitialSchema_1 = require("./1710000000000-InitialSchema");
6
7
  exports.coreMigrations = [
7
8
  _1710000000000_InitialSchema_1.InitialSchema1710000000000,
8
9
  _1789171200000_NotificationDismissal_1.NotificationDismissal1789171200000,
10
+ _1789603200000_AuthTokenClient_1.AuthTokenClient1789603200000,
9
11
  ];
@@ -17,7 +17,6 @@ const common_1 = require("@nestjs/common");
17
17
  const swagger_1 = require("@nestjs/swagger");
18
18
  const fs_1 = require("fs");
19
19
  const currentUser_decorator_1 = require("../auth/decorators/currentUser.decorator");
20
- const permissions_guard_1 = require("../auth/guards/permissions.guard");
21
20
  const export_service_1 = require("./export.service");
22
21
  const exportAccepted_response_1 = require("./exportAccepted.response");
23
22
  const exportExtension_enum_1 = require("./exportExtension.enum");
@@ -101,7 +100,6 @@ __decorate([
101
100
  exports.ExportController = ExportController = __decorate([
102
101
  (0, swagger_1.ApiTags)("Exports"),
103
102
  (0, swagger_1.ApiBearerAuth)(),
104
- (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
105
103
  (0, common_1.Controller)({ path: "exports", version: "1" }),
106
104
  __metadata("design:paramtypes", [export_service_1.ExportService,
107
105
  exportStorage_service_1.ExportStorageService])
@@ -7,5 +7,6 @@ export declare enum AppErrorCode {
7
7
  PASSWORD_REUSED = "PASSWORD_REUSED",
8
8
  TOO_MANY_REQUESTS = "TOO_MANY_REQUESTS",
9
9
  INVALID_FILTER = "INVALID_FILTER",
10
- GROUP_REQUIRED = "GROUP_REQUIRED"
10
+ GROUP_REQUIRED = "GROUP_REQUIRED",
11
+ PASSWORD_CHANGE_REQUIRED = "PASSWORD_CHANGE_REQUIRED"
11
12
  }
@@ -12,4 +12,5 @@ var AppErrorCode;
12
12
  AppErrorCode["TOO_MANY_REQUESTS"] = "TOO_MANY_REQUESTS";
13
13
  AppErrorCode["INVALID_FILTER"] = "INVALID_FILTER";
14
14
  AppErrorCode["GROUP_REQUIRED"] = "GROUP_REQUIRED";
15
+ AppErrorCode["PASSWORD_CHANGE_REQUIRED"] = "PASSWORD_CHANGE_REQUIRED";
15
16
  })(AppErrorCode || (exports.AppErrorCode = AppErrorCode = {}));
@@ -29,23 +29,87 @@ const isBlockedIPv4 = (address) => {
29
29
  (a === 198 && (b === 18 || b === 19)) ||
30
30
  a >= 224);
31
31
  };
32
+ const expandIPv6 = (address) => {
33
+ let value = address.toLowerCase().replace(/^\[|\]$/g, "");
34
+ const zone = value.indexOf("%");
35
+ if (zone >= 0) {
36
+ value = value.slice(0, zone);
37
+ }
38
+ const dotted = /(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(value);
39
+ if (dotted) {
40
+ const octets = dotted.slice(1, 5).map(Number);
41
+ if (octets.some((octet) => octet > 255)) {
42
+ return null;
43
+ }
44
+ const high = ((octets[0] << 8) | octets[1]).toString(16);
45
+ const low = ((octets[2] << 8) | octets[3]).toString(16);
46
+ value = `${value.slice(0, dotted.index)}${high}:${low}`;
47
+ }
48
+ const halves = value.split("::");
49
+ if (halves.length > 2) {
50
+ return null;
51
+ }
52
+ const parse = (part) => part === "" ? [] : part.split(":").map((group) => parseInt(group, 16));
53
+ const head = parse(halves[0]);
54
+ const tail = halves.length === 2 ? parse(halves[1]) : [];
55
+ const missing = 8 - head.length - tail.length;
56
+ if (missing < 0 || (halves.length === 1 && missing !== 0)) {
57
+ return null;
58
+ }
59
+ const groups = [...head, ...new Array(missing).fill(0), ...tail];
60
+ if (groups.some((group) => Number.isNaN(group) || group < 0 || group > 0xffff)) {
61
+ return null;
62
+ }
63
+ return groups;
64
+ };
65
+ const embeddedIPv4 = (groups, offset) => [
66
+ groups[offset] >> 8,
67
+ groups[offset] & 0xff,
68
+ groups[offset + 1] >> 8,
69
+ groups[offset + 1] & 0xff,
70
+ ].join(".");
32
71
  const isBlockedIPv6 = (address) => {
33
- const value = address.toLowerCase().replace(/^\[|\]$/g, "");
34
- const mapped = /^::ffff:(?<v4>\d+\.\d+\.\d+\.\d+)$/.exec(value);
35
- if (mapped?.groups) {
36
- return isBlockedIPv4(mapped.groups.v4);
37
- }
38
- return (value === "::" ||
39
- value === "::1" ||
40
- value.startsWith("fe8") ||
41
- value.startsWith("fe9") ||
42
- value.startsWith("fea") ||
43
- value.startsWith("feb") ||
44
- value.startsWith("fc") ||
45
- value.startsWith("fd") ||
46
- value.startsWith("ff"));
72
+ const groups = expandIPv6(address);
73
+ if (!groups) {
74
+ return true;
75
+ }
76
+ const [g0, g1, g2, g3, g4, g5] = groups;
77
+ const prefixZero = g0 === 0 && g1 === 0 && g2 === 0 && g3 === 0 && g4 === 0;
78
+ if (prefixZero && (g5 === 0xffff || g5 === 0)) {
79
+ return groups[6] === 0 && groups[7] <= 1
80
+ ? true
81
+ : isBlockedIPv4(embeddedIPv4(groups, 6));
82
+ }
83
+ if (g0 === 0 && g1 === 0 && g2 === 0 && g3 === 0 && g4 === 0xffff && g5 === 0) {
84
+ return isBlockedIPv4(embeddedIPv4(groups, 6));
85
+ }
86
+ if (g0 === 0x64 && g1 === 0xff9b && g2 === 0 && g3 === 0 && g4 === 0 && g5 === 0) {
87
+ return isBlockedIPv4(embeddedIPv4(groups, 6));
88
+ }
89
+ if (g0 === 0x64 && g1 === 0xff9b && g2 === 1) {
90
+ return true;
91
+ }
92
+ if (g0 === 0x2002) {
93
+ return isBlockedIPv4(embeddedIPv4(groups, 1));
94
+ }
95
+ return ((g0 & 0xffc0) === 0xfe80 ||
96
+ (g0 & 0xffc0) === 0xfec0 ||
97
+ (g0 & 0xfe00) === 0xfc00 ||
98
+ (g0 & 0xff00) === 0xff00 ||
99
+ (g0 === 0x2001 && g1 === 0xdb8) ||
100
+ (g0 === 0x2001 && g1 === 0));
101
+ };
102
+ const isBlockedAddress = (address) => {
103
+ const literal = address.replace(/^\[|\]$/g, "");
104
+ const version = (0, net_1.isIP)(literal.replace(/%.*$/, ""));
105
+ if (version === 4) {
106
+ return isBlockedIPv4(literal);
107
+ }
108
+ if (version === 6) {
109
+ return isBlockedIPv6(literal);
110
+ }
111
+ return true;
47
112
  };
48
- const isBlockedAddress = (address) => (0, net_1.isIP)(address) === 6 ? isBlockedIPv6(address) : isBlockedIPv4(address);
49
113
  exports.isBlockedAddress = isBlockedAddress;
50
114
  const lookupAll = (hostname) => new Promise((resolve, reject) => {
51
115
  (0, dns_1.lookup)(hostname, { all: true }, (error, addresses) => {
@@ -18,7 +18,6 @@ const platform_express_1 = require("@nestjs/platform-express");
18
18
  const swagger_1 = require("@nestjs/swagger");
19
19
  const multer_1 = require("multer");
20
20
  const currentUser_decorator_1 = require("../auth/decorators/currentUser.decorator");
21
- const permissions_guard_1 = require("../auth/guards/permissions.guard");
22
21
  const images_service_1 = require("./images.service");
23
22
  const imageAccepted_response_1 = require("./responses/imageAccepted.response");
24
23
  const imageProfile_response_1 = require("./responses/imageProfile.response");
@@ -127,7 +126,6 @@ __decorate([
127
126
  exports.ImagesController = ImagesController = __decorate([
128
127
  (0, swagger_1.ApiTags)("Images"),
129
128
  (0, swagger_1.ApiBearerAuth)(),
130
- (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
131
129
  (0, common_1.Controller)({ path: "images", version: "1" }),
132
130
  __metadata("design:paramtypes", [images_service_1.ImagesService])
133
131
  ], ImagesController);
@@ -27,6 +27,7 @@ export declare class ImagesService {
27
27
  discard(path: string | null | undefined): Promise<void>;
28
28
  promote(path: string | null | undefined): Promise<string | null>;
29
29
  private authorize;
30
+ private authorizeRoute;
30
31
  private enqueue;
31
32
  private processNow;
32
33
  private tooLarge;
@@ -33,7 +33,7 @@ let ImagesService = class ImagesService {
33
33
  return this.registry.require(name);
34
34
  }
35
35
  async fromFile(user, profileName, file) {
36
- const profile = this.authorize(user, profileName);
36
+ const profile = this.authorizeRoute(user, profileName);
37
37
  if (!file?.buffer?.length) {
38
38
  throw new common_1.BadRequestException("Nenhum arquivo recebido");
39
39
  }
@@ -66,7 +66,7 @@ let ImagesService = class ImagesService {
66
66
  return path ?? summary.path;
67
67
  }
68
68
  async fromUrl(user, profileName, url) {
69
- const profile = this.authorize(user, profileName);
69
+ const profile = this.authorizeRoute(user, profileName);
70
70
  await this.fetcher.assertAllowed(url);
71
71
  if (!this.producer.isAvailable(queue_constants_1.IMAGES_QUEUE)) {
72
72
  const downloaded = await this.fetcher.download(url, profile.maxBytes);
@@ -83,11 +83,14 @@ let ImagesService = class ImagesService {
83
83
  return await this.producer.status(jobId, ownerId, queue_constants_1.IMAGES_QUEUE);
84
84
  }
85
85
  async remove(user, profileName, path) {
86
- const profile = this.authorize(user, profileName);
86
+ const profile = this.authorizeRoute(user, profileName);
87
87
  const ref = this.storage.parsePublicPath(path);
88
88
  if (!ref || ref.folder !== profile.folder) {
89
89
  throw new common_1.BadRequestException("Caminho de imagem inválido");
90
90
  }
91
+ if (!ref.pending) {
92
+ throw new common_1.BadRequestException("Só uma imagem ainda não confirmada pode ser apagada por aqui — a definitiva sai junto com o registro");
93
+ }
91
94
  await this.storage.remove(path);
92
95
  }
93
96
  async discard(path) {
@@ -103,6 +106,13 @@ let ImagesService = class ImagesService {
103
106
  }
104
107
  return profile;
105
108
  }
109
+ authorizeRoute(user, profileName) {
110
+ const profile = this.authorize(user, profileName);
111
+ if (!profile.permission) {
112
+ throw new common_1.ForbiddenException("Este perfil de imagem só é acessível pela rota da própria funcionalidade");
113
+ }
114
+ return profile;
115
+ }
106
116
  async enqueue(ownerId, profile, payload) {
107
117
  const ref = await this.producer.enqueue(profile.name, payload, {
108
118
  ownerId,
@@ -16,7 +16,6 @@ exports.QueueAdminController = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const swagger_1 = require("@nestjs/swagger");
18
18
  const requirePermission_decorator_1 = require("../auth/decorators/requirePermission.decorator");
19
- const permissions_guard_1 = require("../auth/guards/permissions.guard");
20
19
  const queue_constants_1 = require("./queue.constants");
21
20
  const queueAdmin_response_1 = require("./queueAdmin.response");
22
21
  const queueAdmin_service_1 = require("./queueAdmin.service");
@@ -93,7 +92,6 @@ __decorate([
93
92
  exports.QueueAdminController = QueueAdminController = __decorate([
94
93
  (0, swagger_1.ApiTags)("Queues"),
95
94
  (0, swagger_1.ApiBearerAuth)(),
96
- (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
97
95
  (0, common_1.Controller)({ path: "queues", version: "1" }),
98
96
  __metadata("design:paramtypes", [queueAdmin_service_1.QueueAdminService])
99
97
  ], QueueAdminController);
@@ -1,5 +1,6 @@
1
- import { BadRequestException, HttpException, UnauthorizedException } from "@nestjs/common";
1
+ import { BadRequestException, ForbiddenException, HttpException, UnauthorizedException } from "@nestjs/common";
2
2
  import { AppErrorCode } from "../filters/appErrorCode.enum";
3
3
  export declare function codedUnauthorized(message: string, errorCode: AppErrorCode): UnauthorizedException;
4
4
  export declare function codedBadRequest(message: string, errorCode: AppErrorCode): BadRequestException;
5
+ export declare function codedForbidden(message: string, errorCode: AppErrorCode): ForbiddenException;
5
6
  export declare function codedTooManyRequests(message: string, errorCode: AppErrorCode): HttpException;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codedUnauthorized = codedUnauthorized;
4
4
  exports.codedBadRequest = codedBadRequest;
5
+ exports.codedForbidden = codedForbidden;
5
6
  exports.codedTooManyRequests = codedTooManyRequests;
6
7
  const common_1 = require("@nestjs/common");
7
8
  function codedUnauthorized(message, errorCode) {
@@ -18,6 +19,13 @@ function codedBadRequest(message, errorCode) {
18
19
  errorCode,
19
20
  });
20
21
  }
22
+ function codedForbidden(message, errorCode) {
23
+ return new common_1.ForbiddenException({
24
+ message,
25
+ error: "Forbidden",
26
+ errorCode,
27
+ });
28
+ }
21
29
  function codedTooManyRequests(message, errorCode) {
22
30
  return new common_1.HttpException({ message, error: "Too Many Requests", errorCode }, common_1.HttpStatus.TOO_MANY_REQUESTS);
23
31
  }
@@ -13,6 +13,7 @@ const typeorm_1 = require("@nestjs/typeorm");
13
13
  const domain_1 = require("./domain");
14
14
  const infra_1 = require("./infra");
15
15
  const presentation_1 = require("./presentation");
16
+ const rbac_module_1 = require("../rbac/rbac.module");
16
17
  const user_repository_1 = require("../users/infra/repositories/user.repository");
17
18
  const user_schema_1 = require("../users/infra/schema/user.schema");
18
19
  let AuditModule = class AuditModule {
@@ -20,7 +21,10 @@ let AuditModule = class AuditModule {
20
21
  exports.AuditModule = AuditModule;
21
22
  exports.AuditModule = AuditModule = __decorate([
22
23
  (0, common_1.Module)({
23
- imports: [typeorm_1.TypeOrmModule.forFeature([infra_1.RequestLog, infra_1.AuditDataChange, user_schema_1.User])],
24
+ imports: [
25
+ typeorm_1.TypeOrmModule.forFeature([infra_1.RequestLog, infra_1.AuditDataChange, user_schema_1.User]),
26
+ rbac_module_1.RbacModule,
27
+ ],
24
28
  controllers: [presentation_1.AuditController],
25
29
  providers: [
26
30
  infra_1.RequestLogRepository,
@@ -1,8 +1,10 @@
1
+ import { Actor } from "../../../core/auth/actor.interface";
1
2
  import { Paginated, PaginationQuery } from "../../../core/utils/pagination.util";
2
3
  import { AuditDataChangeRepository } from "../infra/repositories/auditDataChange.repository";
3
4
  import { RequestLogRepository } from "../infra/repositories/requestLog.repository";
4
5
  import { AuditDataAction, AuditDataChange } from "../infra/schema/auditDataChange.schema";
5
6
  import { RequestLog } from "../infra/schema/requestLog.schema";
7
+ import { TeamScopeService } from "../../rbac/domain/teamScope.service";
6
8
  import { UserRepository } from "../../users/infra/repositories/user.repository";
7
9
  export type WithUserRef<T> = T & {
8
10
  user?: {
@@ -16,8 +18,9 @@ export declare class AuditService {
16
18
  private readonly requestLogRepo;
17
19
  private readonly dataChangeRepo;
18
20
  private readonly userRepo;
21
+ private readonly teamScope;
19
22
  private readonly logger;
20
- constructor(requestLogRepo: RequestLogRepository, dataChangeRepo: AuditDataChangeRepository, userRepo: UserRepository);
23
+ constructor(requestLogRepo: RequestLogRepository, dataChangeRepo: AuditDataChangeRepository, userRepo: UserRepository, teamScope: TeamScopeService);
21
24
  recordRequest(data: Partial<RequestLog>): Promise<void>;
22
25
  recordChange(data: {
23
26
  entity: string;
@@ -26,7 +29,7 @@ export declare class AuditService {
26
29
  oldData: Record<string, unknown> | null;
27
30
  newData: Record<string, unknown> | null;
28
31
  }): Promise<void>;
29
- listRequests(query: PaginationQuery): Promise<Paginated<WithUserRef<RequestLog>>>;
32
+ listRequests(query: PaginationQuery, actor: Actor): Promise<Paginated<WithUserRef<RequestLog>>>;
30
33
  listDataChanges(query: PaginationQuery): Promise<Paginated<AuditDataChangeWithUser>>;
31
34
  private enrichWithUser;
32
35
  }
@@ -17,12 +17,14 @@ const auditContext_1 = require("../../../core/context/auditContext");
17
17
  const pagination_util_1 = require("../../../core/utils/pagination.util");
18
18
  const auditDataChange_repository_1 = require("../infra/repositories/auditDataChange.repository");
19
19
  const requestLog_repository_1 = require("../infra/repositories/requestLog.repository");
20
+ const teamScope_service_1 = require("../../rbac/domain/teamScope.service");
20
21
  const user_repository_1 = require("../../users/infra/repositories/user.repository");
21
22
  let AuditService = AuditService_1 = class AuditService {
22
- constructor(requestLogRepo, dataChangeRepo, userRepo) {
23
+ constructor(requestLogRepo, dataChangeRepo, userRepo, teamScope) {
23
24
  this.requestLogRepo = requestLogRepo;
24
25
  this.dataChangeRepo = dataChangeRepo;
25
26
  this.userRepo = userRepo;
27
+ this.teamScope = teamScope;
26
28
  this.logger = new common_1.Logger(AuditService_1.name);
27
29
  }
28
30
  async recordRequest(data) {
@@ -46,8 +48,9 @@ let AuditService = AuditService_1 = class AuditService {
46
48
  this.logger.warn(`Falha ao gravar AuditDataChange (${data.entity}): ${err.message}`);
47
49
  }
48
50
  }
49
- async listRequests(query) {
50
- const [items, total] = await this.requestLogRepo.findPaginated(query);
51
+ async listRequests(query, actor) {
52
+ const reachable = await this.teamScope.reachableUserIds(actor, "audit:read");
53
+ const [items, total] = await this.requestLogRepo.findPaginated(query, reachable);
51
54
  return new pagination_util_1.Paginated(await this.enrichWithUser(items), total, query.page, query.limit);
52
55
  }
53
56
  async listDataChanges(query) {
@@ -83,5 +86,6 @@ exports.AuditService = AuditService = AuditService_1 = __decorate([
83
86
  (0, common_1.Injectable)(),
84
87
  __metadata("design:paramtypes", [requestLog_repository_1.RequestLogRepository,
85
88
  auditDataChange_repository_1.AuditDataChangeRepository,
86
- user_repository_1.UserRepository])
89
+ user_repository_1.UserRepository,
90
+ teamScope_service_1.TeamScopeService])
87
91
  ], AuditService);
@@ -3,5 +3,5 @@ import { PaginationQuery } from "../../../../core/utils/pagination.util";
3
3
  import { RequestLog } from "../schema/requestLog.schema";
4
4
  export declare class RequestLogRepository extends Repository<RequestLog> {
5
5
  constructor(dataSource: DataSource);
6
- findPaginated(query: PaginationQuery): Promise<[RequestLog[], number]>;
6
+ findPaginated(query: PaginationQuery, reachableIds?: string[] | null): Promise<[RequestLog[], number]>;
7
7
  }
@@ -19,8 +19,17 @@ let RequestLogRepository = class RequestLogRepository extends typeorm_1.Reposito
19
19
  constructor(dataSource) {
20
20
  super(requestLog_schema_1.RequestLog, dataSource.createEntityManager());
21
21
  }
22
- findPaginated(query) {
23
- return (0, paginateWithFilter_util_1.paginateWithFilter)(this.createQueryBuilder("requestLog").leftJoin("users", "logUser", "logUser.id = requestLog.user_id"), {
22
+ findPaginated(query, reachableIds) {
23
+ const qb = this.createQueryBuilder("requestLog").leftJoin("users", "logUser", "logUser.id = requestLog.user_id");
24
+ if (reachableIds) {
25
+ if (reachableIds.length === 0) {
26
+ qb.andWhere("1 = 0");
27
+ }
28
+ else {
29
+ qb.andWhere("requestLog.user_id IN (:...reachableIds)", { reachableIds });
30
+ }
31
+ }
32
+ return (0, paginateWithFilter_util_1.paginateWithFilter)(qb, {
24
33
  query,
25
34
  catalog: audit_filters_1.REQUEST_LOGS_FILTER_CATALOG,
26
35
  defaultSort: "createdAt",
@@ -1,3 +1,4 @@
1
+ import { AuthenticatedUser } from "../../../core/auth/authenticatedUser.interface";
1
2
  import { FilterFieldResponse } from "../../../core/query/filterSchema.response";
2
3
  import { Paginated, PaginationQuery } from "../../../core/utils/pagination.util";
3
4
  import { AuditDataChangeWithUser, AuditService, WithUserRef } from "../domain/audit.service";
@@ -5,7 +6,7 @@ import { RequestLog } from "../infra/schema/requestLog.schema";
5
6
  export declare class AuditController {
6
7
  private readonly audit;
7
8
  constructor(audit: AuditService);
8
- requests(query: PaginationQuery): Promise<Paginated<WithUserRef<RequestLog>>>;
9
+ requests(query: PaginationQuery, actor: AuthenticatedUser): Promise<Paginated<WithUserRef<RequestLog>>>;
9
10
  requestsFilterSchema(): FilterFieldResponse[];
10
11
  dataChanges(query: PaginationQuery): Promise<Paginated<AuditDataChangeWithUser>>;
11
12
  dataChangesFilterSchema(): FilterFieldResponse[];