rl-core-api 0.3.0 → 0.5.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 (133) hide show
  1. package/README.md +1 -1
  2. package/dist/core/auth/actor.interface.d.ts +4 -0
  3. package/dist/core/auth/actor.interface.js +2 -0
  4. package/dist/core/auth/permissionScope.util.d.ts +3 -0
  5. package/dist/core/auth/permissionScope.util.js +10 -0
  6. package/dist/core/config/env.schema.d.ts +3 -3
  7. package/dist/core/config/env.schema.js +3 -3
  8. package/dist/core/config/env.service.d.ts +1 -0
  9. package/dist/core/config/env.service.js +3 -0
  10. package/dist/core/context/auditContext.d.ts +1 -0
  11. package/dist/core/context/auditContext.js +8 -1
  12. package/dist/core/context/requestLog.context.d.ts +10 -0
  13. package/dist/core/context/requestLog.context.js +26 -0
  14. package/dist/core/database/migrations/1710000000000-InitialSchema.js +63 -36
  15. package/dist/core/database/migrations/{1786233600000-Notifications.d.ts → 1787270400000-GroupManager.d.ts} +1 -1
  16. package/dist/core/database/migrations/1787270400000-GroupManager.js +26 -0
  17. package/dist/core/database/migrations/index.js +2 -6
  18. package/dist/core/events/appEvent.enum.d.ts +19 -1
  19. package/dist/core/events/appEvent.enum.js +1 -0
  20. package/dist/core/filters/allExceptions.filter.d.ts +6 -0
  21. package/dist/core/filters/allExceptions.filter.js +47 -2
  22. package/dist/core/filters/appErrorCode.enum.d.ts +3 -1
  23. package/dist/core/filters/appErrorCode.enum.js +2 -0
  24. package/dist/core/query/parseFilter.util.js +6 -4
  25. package/dist/core/utils/ip.util.d.ts +1 -0
  26. package/dist/core/utils/ip.util.js +8 -0
  27. package/dist/core/websocket/baseGateway.d.ts +1 -0
  28. package/dist/core/websocket/baseGateway.js +3 -0
  29. package/dist/coreEntities.d.ts +2 -3
  30. package/dist/coreEntities.js +2 -4
  31. package/dist/features/audit/audit.module.js +5 -7
  32. package/dist/features/audit/domain/audit.service.d.ts +14 -17
  33. package/dist/features/audit/domain/audit.service.js +22 -35
  34. package/dist/features/audit/domain/classifyError.util.d.ts +1 -1
  35. package/dist/features/audit/domain/classifyError.util.js +8 -8
  36. package/dist/features/audit/domain/enums/errorType.enum.d.ts +9 -0
  37. package/dist/features/audit/domain/enums/errorType.enum.js +13 -0
  38. package/dist/features/audit/domain/enums/requestOutcome.enum.d.ts +5 -0
  39. package/dist/features/audit/domain/enums/requestOutcome.enum.js +9 -0
  40. package/dist/features/audit/domain/index.d.ts +2 -0
  41. package/dist/features/audit/domain/index.js +2 -0
  42. package/dist/features/audit/infra/auditData.subscriber.js +21 -12
  43. package/dist/features/audit/infra/index.d.ts +2 -4
  44. package/dist/features/audit/infra/index.js +2 -4
  45. package/dist/features/audit/infra/repositories/requestLog.repository.d.ts +7 -0
  46. package/dist/features/audit/infra/repositories/{auditChange.repository.js → requestLog.repository.js} +10 -10
  47. package/dist/features/audit/infra/schema/auditDataChange.schema.d.ts +3 -3
  48. package/dist/features/audit/infra/schema/auditDataChange.schema.js +8 -7
  49. package/dist/features/audit/infra/schema/requestLog.schema.d.ts +20 -0
  50. package/dist/features/audit/infra/schema/requestLog.schema.js +92 -0
  51. package/dist/features/audit/presentation/audit.controller.d.ts +4 -8
  52. package/dist/features/audit/presentation/audit.controller.js +8 -31
  53. package/dist/features/audit/presentation/audit.interceptor.d.ts +5 -1
  54. package/dist/features/audit/presentation/audit.interceptor.js +53 -28
  55. package/dist/features/audit/presentation/decorators/auditable.decorator.d.ts +10 -0
  56. package/dist/features/audit/presentation/decorators/auditable.decorator.js +10 -0
  57. package/dist/features/audit/presentation/filters/audit.filters.d.ts +1 -2
  58. package/dist/features/audit/presentation/filters/audit.filters.js +65 -49
  59. package/dist/features/audit/presentation/httpError.listener.d.ts +8 -0
  60. package/dist/features/audit/presentation/httpError.listener.js +61 -0
  61. package/dist/features/audit/presentation/index.d.ts +3 -0
  62. package/dist/features/audit/presentation/index.js +3 -0
  63. package/dist/features/audit/presentation/responses/auditDataChange.response.js +1 -1
  64. package/dist/features/audit/presentation/responses/log.response.d.ts +12 -27
  65. package/dist/features/audit/presentation/responses/log.response.js +66 -88
  66. package/dist/features/audit/presentation/userRolesChanged.listener.d.ts +7 -0
  67. package/dist/features/audit/presentation/userRolesChanged.listener.js +43 -0
  68. package/dist/features/auth/domain/auth.service.d.ts +7 -4
  69. package/dist/features/auth/domain/auth.service.js +22 -4
  70. package/dist/features/auth/domain/twoFactor.service.d.ts +2 -0
  71. package/dist/features/auth/domain/twoFactor.service.js +7 -2
  72. package/dist/features/auth/infra/schema/authToken.schema.d.ts +5 -5
  73. package/dist/features/auth/infra/schema/authToken.schema.js +7 -7
  74. package/dist/features/auth/presentation/auth.controller.d.ts +2 -5
  75. package/dist/features/auth/presentation/auth.controller.js +11 -0
  76. package/dist/features/maintenance/domain/cleanup.service.d.ts +1 -4
  77. package/dist/features/maintenance/domain/cleanup.service.js +1 -26
  78. package/dist/features/maintenance/maintenance.module.js +1 -2
  79. package/dist/features/notifications/domain/enums/channelType.enum.d.ts +5 -5
  80. package/dist/features/notifications/domain/enums/channelType.enum.js +5 -5
  81. package/dist/features/notifications/domain/enums/deliveryStatus.enum.d.ts +6 -6
  82. package/dist/features/notifications/domain/enums/deliveryStatus.enum.js +6 -6
  83. package/dist/features/notifications/domain/enums/notificationType.enum.d.ts +4 -4
  84. package/dist/features/notifications/domain/enums/notificationType.enum.js +4 -4
  85. package/dist/features/notifications/domain/enums/scopeType.enum.d.ts +3 -3
  86. package/dist/features/notifications/domain/enums/scopeType.enum.js +3 -3
  87. package/dist/features/notifications/infra/schema/notification.schema.js +2 -2
  88. package/dist/features/notifications/infra/schema/notificationScope.schema.js +2 -2
  89. package/dist/features/notifications/presentation/commands/createNotification.command.js +2 -2
  90. package/dist/features/notifications/presentation/domainEvents.listener.d.ts +5 -2
  91. package/dist/features/notifications/presentation/domainEvents.listener.js +24 -2
  92. package/dist/features/notifications/presentation/responses/notification.response.js +1 -1
  93. package/dist/features/rbac/domain/rbac.catalog.js +31 -2
  94. package/dist/features/rbac/domain/rbac.service.d.ts +1 -0
  95. package/dist/features/rbac/domain/rbac.service.js +14 -0
  96. package/dist/features/rbac/domain/teamScope.service.d.ts +15 -0
  97. package/dist/features/rbac/domain/teamScope.service.js +74 -0
  98. package/dist/features/rbac/infra/repositories/group.repository.d.ts +2 -0
  99. package/dist/features/rbac/infra/repositories/group.repository.js +26 -0
  100. package/dist/features/rbac/infra/schema/group.schema.d.ts +1 -0
  101. package/dist/features/rbac/infra/schema/group.schema.js +5 -0
  102. package/dist/features/rbac/presentation/commands/createGroup.command.d.ts +1 -0
  103. package/dist/features/rbac/presentation/commands/createGroup.command.js +7 -0
  104. package/dist/features/rbac/presentation/rbac.controller.d.ts +1 -0
  105. package/dist/features/rbac/presentation/rbac.controller.js +16 -0
  106. package/dist/features/rbac/presentation/responses/rbac.response.d.ts +1 -0
  107. package/dist/features/rbac/presentation/responses/rbac.response.js +4 -0
  108. package/dist/features/rbac/rbac.module.js +3 -0
  109. package/dist/features/users/domain/users.service.d.ts +16 -8
  110. package/dist/features/users/domain/users.service.js +72 -14
  111. package/dist/features/users/infra/repositories/user.repository.d.ts +1 -1
  112. package/dist/features/users/infra/repositories/user.repository.js +9 -1
  113. package/dist/features/users/presentation/commands/createUser.command.d.ts +1 -0
  114. package/dist/features/users/presentation/commands/createUser.command.js +8 -0
  115. package/dist/features/users/presentation/filters/users.filters.js +8 -0
  116. package/dist/features/users/presentation/users.controller.d.ts +5 -5
  117. package/dist/features/users/presentation/users.controller.js +38 -27
  118. package/dist/features/users/users.module.js +2 -0
  119. package/dist/index.d.ts +7 -3
  120. package/dist/index.js +14 -6
  121. package/package.json +1 -1
  122. package/dist/core/database/migrations/1786060800000-FirstAccessTokenType.d.ts +0 -6
  123. package/dist/core/database/migrations/1786060800000-FirstAccessTokenType.js +0 -19
  124. package/dist/core/database/migrations/1786147200000-PermanentAccountLock.d.ts +0 -6
  125. package/dist/core/database/migrations/1786147200000-PermanentAccountLock.js +0 -15
  126. package/dist/core/database/migrations/1786233600000-Notifications.js +0 -59
  127. package/dist/features/audit/infra/repositories/auditChange.repository.d.ts +0 -7
  128. package/dist/features/audit/infra/repositories/errorLog.repository.d.ts +0 -7
  129. package/dist/features/audit/infra/repositories/errorLog.repository.js +0 -35
  130. package/dist/features/audit/infra/schema/auditChange.schema.d.ts +0 -14
  131. package/dist/features/audit/infra/schema/auditChange.schema.js +0 -68
  132. package/dist/features/audit/infra/schema/errorLog.schema.d.ts +0 -21
  133. package/dist/features/audit/infra/schema/errorLog.schema.js +0 -69
@@ -10,6 +10,7 @@ exports.RbacModule = void 0;
10
10
  const common_1 = require("@nestjs/common");
11
11
  const typeorm_1 = require("@nestjs/typeorm");
12
12
  const domain_1 = require("./domain");
13
+ const teamScope_service_1 = require("./domain/teamScope.service");
13
14
  const infra_1 = require("./infra");
14
15
  const presentation_1 = require("./presentation");
15
16
  const user_repository_1 = require("../users/infra/repositories/user.repository");
@@ -40,6 +41,7 @@ exports.RbacModule = RbacModule = __decorate([
40
41
  infra_1.GroupRepository,
41
42
  user_repository_1.UserRepository,
42
43
  domain_1.RbacService,
44
+ teamScope_service_1.TeamScopeService,
43
45
  ],
44
46
  exports: [
45
47
  infra_1.ResourceRepository,
@@ -50,6 +52,7 @@ exports.RbacModule = RbacModule = __decorate([
50
52
  infra_1.GroupRepository,
51
53
  user_repository_1.UserRepository,
52
54
  domain_1.RbacService,
55
+ teamScope_service_1.TeamScopeService,
53
56
  ],
54
57
  })
55
58
  ], RbacModule);
@@ -1,8 +1,11 @@
1
1
  import { EventEmitter2 } from "@nestjs/event-emitter";
2
+ import { Actor } from "../../../core/auth/actor.interface";
2
3
  import { EnvService } from "../../../core/config/env.service";
3
4
  import { MailerService } from "../../../core/mailer/mailer.service";
4
5
  import { Paginated, PaginationQuery } from "../../../core/utils/pagination.util";
5
6
  import { TokenService } from "../../auth/domain/token.service";
7
+ import { TeamScopeService } from "../../rbac/domain/teamScope.service";
8
+ import { GroupRepository } from "../../rbac/infra/repositories/group.repository";
6
9
  import { RoleRepository } from "../../rbac/infra/repositories/role.repository";
7
10
  import { PasswordHistoryRepository } from "../infra/repositories/passwordHistory.repository";
8
11
  import { UserRepository } from "../infra/repositories/user.repository";
@@ -14,28 +17,33 @@ export declare class UsersService {
14
17
  private readonly userRepo;
15
18
  private readonly historyRepo;
16
19
  private readonly roleRepo;
20
+ private readonly groupRepo;
21
+ private readonly teamScope;
17
22
  private readonly env;
18
23
  private readonly mailer;
19
24
  private readonly tokens;
20
25
  private readonly events;
21
- constructor(userRepo: UserRepository, historyRepo: PasswordHistoryRepository, roleRepo: RoleRepository, env: EnvService, mailer: MailerService, tokens: TokenService, events: EventEmitter2);
26
+ constructor(userRepo: UserRepository, historyRepo: PasswordHistoryRepository, roleRepo: RoleRepository, groupRepo: GroupRepository, teamScope: TeamScopeService, env: EnvService, mailer: MailerService, tokens: TokenService, events: EventEmitter2);
22
27
  findByEmail(email: string): Promise<User | null>;
23
28
  findById(id: string): Promise<User>;
24
- list(query: PaginationQuery, actorPermissions?: string[]): Promise<Paginated<User>>;
25
- create(dto: CreateUserCommand, actorPermissions?: string[]): Promise<User>;
26
- sendFirstAccessLink(id: string): Promise<void>;
27
- sendPasswordResetLink(id: string): Promise<void>;
29
+ findByIdForActor(id: string, actor: Actor): Promise<User>;
30
+ list(query: PaginationQuery, actor: Actor): Promise<Paginated<User>>;
31
+ create(dto: CreateUserCommand, actor: Actor): Promise<User>;
32
+ private resolveGroupsForCreate;
33
+ sendFirstAccessLink(id: string, actor: Actor): Promise<void>;
34
+ sendPasswordResetLink(id: string, actor: Actor): Promise<void>;
28
35
  private invalidateCurrentPassword;
29
36
  private issueAndSendFirstAccess;
30
37
  private findActiveForLink;
31
38
  private requirePasswordChange;
32
- update(id: string, dto: UpdateUserCommand, actorPermissions?: string[]): Promise<User>;
39
+ update(id: string, dto: UpdateUserCommand, actor: Actor): Promise<User>;
40
+ private emitIfRolesChanged;
33
41
  remove(id: string): Promise<void>;
34
- setActive(id: string, isActive: boolean): Promise<User>;
42
+ setActive(id: string, isActive: boolean, actor: Actor): Promise<User>;
35
43
  updateProfile(userId: string, dto: UpdateProfileCommand): Promise<User>;
36
44
  setAvatar(userId: string, avatarPath: string): Promise<User>;
37
45
  removeAvatar(userId: string): Promise<User>;
38
- adminResetTwoFactor(id: string): Promise<void>;
46
+ adminResetTwoFactor(id: string, actor: Actor): Promise<void>;
39
47
  changePassword(userId: string, currentPassword: string, newPassword: string): Promise<void>;
40
48
  assertPasswordNotReused(userId: string, newPassword: string): Promise<void>;
41
49
  savePasswordToHistory(userId: string, passwordHash: string): Promise<void>;
@@ -13,6 +13,7 @@ exports.UsersService = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
14
  const event_emitter_1 = require("@nestjs/event-emitter");
15
15
  const typeorm_1 = require("typeorm");
16
+ const permissionScope_util_1 = require("../../../core/auth/permissionScope.util");
16
17
  const env_service_1 = require("../../../core/config/env.service");
17
18
  const appEvent_enum_1 = require("../../../core/events/appEvent.enum");
18
19
  const appErrorCode_enum_1 = require("../../../core/filters/appErrorCode.enum");
@@ -21,16 +22,20 @@ const codedError_util_1 = require("../../../core/utils/codedError.util");
21
22
  const hash_util_1 = require("../../../core/utils/hash.util");
22
23
  const pagination_util_1 = require("../../../core/utils/pagination.util");
23
24
  const token_service_1 = require("../../auth/domain/token.service");
25
+ const teamScope_service_1 = require("../../rbac/domain/teamScope.service");
26
+ const group_repository_1 = require("../../rbac/infra/repositories/group.repository");
24
27
  const role_repository_1 = require("../../rbac/infra/repositories/role.repository");
25
28
  const passwordHistory_repository_1 = require("../infra/repositories/passwordHistory.repository");
26
29
  const user_repository_1 = require("../infra/repositories/user.repository");
27
30
  const PASSWORD_HISTORY_LIMIT = 3;
28
31
  const PASSWORD_REUSED_MESSAGE = "Esta senha já foi utilizada. Escolha uma senha que você ainda não usou neste sistema";
29
32
  let UsersService = class UsersService {
30
- constructor(userRepo, historyRepo, roleRepo, env, mailer, tokens, events) {
33
+ constructor(userRepo, historyRepo, roleRepo, groupRepo, teamScope, env, mailer, tokens, events) {
31
34
  this.userRepo = userRepo;
32
35
  this.historyRepo = historyRepo;
33
36
  this.roleRepo = roleRepo;
37
+ this.groupRepo = groupRepo;
38
+ this.teamScope = teamScope;
34
39
  this.env = env;
35
40
  this.mailer = mailer;
36
41
  this.tokens = tokens;
@@ -49,11 +54,16 @@ let UsersService = class UsersService {
49
54
  }
50
55
  return user;
51
56
  }
52
- async list(query, actorPermissions) {
53
- const [items, total] = await this.userRepo.findPaginated(query, actorPermissions);
57
+ async findByIdForActor(id, actor) {
58
+ await this.teamScope.assertCanAct(actor, id, "users:read");
59
+ return this.findById(id);
60
+ }
61
+ async list(query, actor) {
62
+ const reachable = await this.teamScope.reachableUserIds(actor, "users:read");
63
+ const [items, total] = await this.userRepo.findPaginated(query, actor.permissions, reachable);
54
64
  return new pagination_util_1.Paginated(items, total, query.page, query.limit);
55
65
  }
56
- async create(dto, actorPermissions) {
66
+ async create(dto, actor) {
57
67
  const exists = await this.userRepo.findOne({
58
68
  where: { email: dto.email },
59
69
  withDeleted: true,
@@ -61,12 +71,13 @@ let UsersService = class UsersService {
61
71
  if (exists) {
62
72
  throw new common_1.ConflictException("Email já cadastrado");
63
73
  }
64
- if (dto.roleIds?.length && !actorPermissions?.includes("users:promote:any")) {
74
+ if (dto.roleIds?.length && !actor.permissions.includes("users:promote:any")) {
65
75
  throw new common_1.ForbiddenException("Definir papéis na criação de um usuário é restrito a administradores");
66
76
  }
67
77
  const roles = dto.roleIds?.length
68
78
  ? await this.roleRepo.findBy({ id: (0, typeorm_1.In)(dto.roleIds) })
69
79
  : [];
80
+ const groups = await this.resolveGroupsForCreate(dto, actor);
70
81
  const hashed = await (0, hash_util_1.hashPassword)((0, hash_util_1.randomToken)(48));
71
82
  const user = this.userRepo.create({
72
83
  name: dto.name,
@@ -78,18 +89,41 @@ let UsersService = class UsersService {
78
89
  mustChangePassword: true,
79
90
  isActive: dto.isActive ?? true,
80
91
  roles,
92
+ groups,
81
93
  });
82
94
  const saved = await this.userRepo.save(user);
83
95
  await this.savePasswordToHistory(saved.id, hashed);
84
96
  void this.issueAndSendFirstAccess(saved).catch(() => undefined);
85
97
  return this.findById(saved.id);
86
98
  }
87
- async sendFirstAccessLink(id) {
99
+ async resolveGroupsForCreate(dto, actor) {
100
+ const isAdmin = actor.permissions.includes("users:create:any");
101
+ if (dto.groupIds?.length) {
102
+ if (!isAdmin) {
103
+ await this.teamScope.assertOwnsGroups(actor, dto.groupIds);
104
+ }
105
+ return this.groupRepo.findBy({ id: (0, typeorm_1.In)(dto.groupIds) });
106
+ }
107
+ if (isAdmin) {
108
+ return [];
109
+ }
110
+ const managed = await this.teamScope.managedGroupIds(actor.id);
111
+ if (managed.length === 0) {
112
+ throw new common_1.ForbiddenException("Você não gerencia nenhum grupo — peça a um administrador para criar o usuário");
113
+ }
114
+ if (managed.length > 1) {
115
+ throw (0, codedError_util_1.codedBadRequest)("Informe em qual dos seus grupos o usuário entra", appErrorCode_enum_1.AppErrorCode.GROUP_REQUIRED);
116
+ }
117
+ return this.groupRepo.findBy({ id: (0, typeorm_1.In)(managed) });
118
+ }
119
+ async sendFirstAccessLink(id, actor) {
120
+ await this.teamScope.assertCanAct(actor, id, "users:invite");
88
121
  const user = await this.findActiveForLink(id);
89
122
  await this.issueAndSendFirstAccess(user);
90
123
  await this.requirePasswordChange(user);
91
124
  }
92
- async sendPasswordResetLink(id) {
125
+ async sendPasswordResetLink(id, actor) {
126
+ await this.teamScope.assertCanAct(actor, id, "users:reset-password");
93
127
  const user = await this.findActiveForLink(id);
94
128
  const token = await this.tokens.issuePasswordReset(user.id);
95
129
  await this.mailer.sendPasswordReset(user.email, token, user.name);
@@ -123,7 +157,9 @@ let UsersService = class UsersService {
123
157
  user.failedLoginAttempts = 0;
124
158
  await this.userRepo.save(user);
125
159
  }
126
- async update(id, dto, actorPermissions) {
160
+ async update(id, dto, actor) {
161
+ await this.teamScope.assertCanAct(actor, id, "users:update");
162
+ const actorPermissions = actor.permissions;
127
163
  const user = await this.findById(id);
128
164
  if (dto.email && dto.email !== user.email) {
129
165
  if (!actorPermissions?.includes("users:update-email:any")) {
@@ -137,12 +173,13 @@ let UsersService = class UsersService {
137
173
  throw new common_1.ConflictException("Email já cadastrado");
138
174
  }
139
175
  }
176
+ const canDeactivate = (0, permissionScope_util_1.anyOrTeam)("users:deactivate").some((code) => actorPermissions.includes(code));
140
177
  if (dto.isActive !== undefined &&
141
178
  dto.isActive !== user.isActive &&
142
- !actorPermissions?.includes("users:deactivate:any")) {
143
- throw new common_1.ForbiddenException("Ativar/desativar um usuário é restrito a administradores");
179
+ !canDeactivate) {
180
+ throw new common_1.ForbiddenException("Ativar/desativar um usuário exige permissão específica");
144
181
  }
145
- if (dto.roleIds && !actorPermissions?.includes("users:promote:any")) {
182
+ if (dto.roleIds && !actorPermissions.includes("users:promote:any")) {
146
183
  throw new common_1.ForbiddenException("Alterar papéis de um usuário é restrito a administradores");
147
184
  }
148
185
  Object.assign(user, {
@@ -152,17 +189,35 @@ let UsersService = class UsersService {
152
189
  email: dto.email ?? user.email,
153
190
  isActive: dto.isActive ?? user.isActive,
154
191
  });
192
+ const previousRoles = [...(user.roles ?? [])];
155
193
  if (dto.roleIds) {
156
194
  user.roles = await this.roleRepo.findBy({ id: (0, typeorm_1.In)(dto.roleIds) });
157
195
  }
158
196
  await this.userRepo.save(user);
159
- return this.findById(id);
197
+ const updated = await this.findById(id);
198
+ this.emitIfRolesChanged(previousRoles, updated);
199
+ return updated;
200
+ }
201
+ emitIfRolesChanged(previousRoles, user) {
202
+ const roles = user.roles ?? [];
203
+ const previousIds = new Set(previousRoles.map((role) => role.id));
204
+ const unchanged = roles.length === previousIds.size &&
205
+ roles.every((role) => previousIds.has(role.id));
206
+ if (unchanged) {
207
+ return;
208
+ }
209
+ this.events.emit(appEvent_enum_1.AppEvent.USER_ROLES_CHANGED, {
210
+ userId: user.id,
211
+ roleNames: roles.map((role) => role.name),
212
+ previousRoleNames: previousRoles.map((role) => role.name),
213
+ });
160
214
  }
161
215
  async remove(id) {
162
216
  const user = await this.findById(id);
163
217
  await this.userRepo.softRemove(user);
164
218
  }
165
- async setActive(id, isActive) {
219
+ async setActive(id, isActive, actor) {
220
+ await this.teamScope.assertCanAct(actor, id, "users:deactivate");
166
221
  const user = await this.findById(id);
167
222
  user.isActive = isActive;
168
223
  await this.userRepo.save(user);
@@ -190,7 +245,8 @@ let UsersService = class UsersService {
190
245
  await this.userRepo.save(user);
191
246
  return user;
192
247
  }
193
- async adminResetTwoFactor(id) {
248
+ async adminResetTwoFactor(id, actor) {
249
+ await this.teamScope.assertCanAct(actor, id, "users:reset-password");
194
250
  const user = await this.findById(id);
195
251
  user.twoFactorEnabled = false;
196
252
  user.twoFactorSecret = null;
@@ -284,6 +340,8 @@ exports.UsersService = UsersService = __decorate([
284
340
  __metadata("design:paramtypes", [user_repository_1.UserRepository,
285
341
  passwordHistory_repository_1.PasswordHistoryRepository,
286
342
  role_repository_1.RoleRepository,
343
+ group_repository_1.GroupRepository,
344
+ teamScope_service_1.TeamScopeService,
287
345
  env_service_1.EnvService,
288
346
  mailer_service_1.MailerService,
289
347
  token_service_1.TokenService,
@@ -3,6 +3,6 @@ import { PaginationQuery } from "../../../../core/utils/pagination.util";
3
3
  import { User } from "../schema/user.schema";
4
4
  export declare class UserRepository extends Repository<User> {
5
5
  constructor(dataSource: DataSource);
6
- findPaginated(query: PaginationQuery, permissions?: string[]): Promise<[User[], number]>;
6
+ findPaginated(query: PaginationQuery, permissions?: string[], reachableIds?: string[] | null): Promise<[User[], number]>;
7
7
  findWithPasswordExpiringIn(maxAgeDays: number, milestones: number[]): Promise<User[]>;
8
8
  }
@@ -25,8 +25,16 @@ let UserRepository = class UserRepository extends typeorm_1.Repository {
25
25
  constructor(dataSource) {
26
26
  super(user_schema_1.User, dataSource.createEntityManager());
27
27
  }
28
- findPaginated(query, permissions) {
28
+ findPaginated(query, permissions, reachableIds) {
29
29
  const qb = this.createQueryBuilder("user").leftJoinAndSelect("user.roles", "userRoles");
30
+ if (reachableIds) {
31
+ if (reachableIds.length === 0) {
32
+ qb.andWhere("1 = 0");
33
+ }
34
+ else {
35
+ qb.andWhere("user.id IN (:...reachableIds)", { reachableIds });
36
+ }
37
+ }
30
38
  return (0, paginateWithFilter_util_1.paginateWithFilter)(qb, {
31
39
  query,
32
40
  catalog: users_filters_1.USERS_FILTER_CATALOG,
@@ -5,4 +5,5 @@ export declare class CreateUserCommand {
5
5
  email: string;
6
6
  isActive?: boolean;
7
7
  roleIds?: string[];
8
+ groupIds?: string[];
8
9
  }
@@ -54,3 +54,11 @@ __decorate([
54
54
  (0, class_validator_1.IsUUID)("all", { each: true }),
55
55
  __metadata("design:type", Array)
56
56
  ], CreateUserCommand.prototype, "roleIds", void 0);
57
+ __decorate([
58
+ (0, swagger_1.ApiProperty)({ required: false, type: [String], format: "uuid" }),
59
+ (0, class_validator_1.IsOptional)(),
60
+ (0, class_validator_1.IsArray)(),
61
+ (0, class_validator_1.ArrayUnique)(),
62
+ (0, class_validator_1.IsUUID)("all", { each: true }),
63
+ __metadata("design:type", Array)
64
+ ], CreateUserCommand.prototype, "groupIds", void 0);
@@ -50,6 +50,14 @@ exports.USERS_FILTER_CATALOG = [
50
50
  type: filterFieldType_enum_1.FilterFieldType.DateTime,
51
51
  sortable: true,
52
52
  },
53
+ {
54
+ field: "groupName",
55
+ column: "name",
56
+ label: "Grupo",
57
+ type: filterFieldType_enum_1.FilterFieldType.Select,
58
+ optionsUrl: "/rbac/groups",
59
+ relation: { property: "groups", alias: "groups", kind: "toMany" },
60
+ },
53
61
  {
54
62
  field: "roleName",
55
63
  column: "name",
@@ -19,12 +19,12 @@ export declare class UsersController {
19
19
  deleteAvatar(userId: string): Promise<void>;
20
20
  list(query: PaginationQuery, actor: AuthenticatedUser): Promise<Paginated<User>>;
21
21
  filterSchema(actor: AuthenticatedUser): FilterFieldResponse[];
22
- findOne(id: string): Promise<User>;
22
+ findOne(id: string, actor: AuthenticatedUser): Promise<User>;
23
23
  create(dto: CreateUserCommand, actor: AuthenticatedUser): Promise<User>;
24
24
  update(id: string, dto: UpdateUserCommand, actor: AuthenticatedUser): Promise<User>;
25
- setActive(id: string, state: string): Promise<User>;
26
- sendPasswordResetLink(id: string): Promise<void>;
27
- sendFirstAccessLink(id: string): Promise<void>;
28
- adminResetTwoFactor(id: string): Promise<void>;
25
+ setActive(id: string, state: string, actor: AuthenticatedUser): Promise<User>;
26
+ sendPasswordResetLink(id: string, actor: AuthenticatedUser): Promise<void>;
27
+ sendFirstAccessLink(id: string, actor: AuthenticatedUser): Promise<void>;
28
+ adminResetTwoFactor(id: string, actor: AuthenticatedUser): Promise<void>;
29
29
  remove(id: string, current: AuthenticatedUser): Promise<void>;
30
30
  }
@@ -20,8 +20,10 @@ const multer_1 = require("multer");
20
20
  const currentUser_decorator_1 = require("../../../core/auth/decorators/currentUser.decorator");
21
21
  const requirePermission_decorator_1 = require("../../../core/auth/decorators/requirePermission.decorator");
22
22
  const permissions_guard_1 = require("../../../core/auth/guards/permissions.guard");
23
+ const permissionScope_util_1 = require("../../../core/auth/permissionScope.util");
23
24
  const filterSchema_response_1 = require("../../../core/query/filterSchema.response");
24
25
  const pagination_util_1 = require("../../../core/utils/pagination.util");
26
+ const auditable_decorator_1 = require("../../audit/presentation/decorators/auditable.decorator");
25
27
  const avatar_service_1 = require("../domain/avatar.service");
26
28
  const users_service_1 = require("../domain/users.service");
27
29
  const changePassword_command_1 = require("./commands/changePassword.command");
@@ -53,31 +55,31 @@ let UsersController = class UsersController {
53
55
  await this.users.removeAvatar(userId);
54
56
  }
55
57
  list(query, actor) {
56
- return this.users.list(query, actor.permissions);
58
+ return this.users.list(query, actor);
57
59
  }
58
60
  filterSchema(actor) {
59
61
  return (0, filterSchema_response_1.toFilterSchema)(users_filters_1.USERS_FILTER_CATALOG, actor.permissions);
60
62
  }
61
- findOne(id) {
62
- return this.users.findById(id);
63
+ findOne(id, actor) {
64
+ return this.users.findByIdForActor(id, actor);
63
65
  }
64
66
  create(dto, actor) {
65
- return this.users.create(dto, actor.permissions);
67
+ return this.users.create(dto, actor);
66
68
  }
67
69
  update(id, dto, actor) {
68
- return this.users.update(id, dto, actor.permissions);
70
+ return this.users.update(id, dto, actor);
69
71
  }
70
- setActive(id, state) {
71
- return this.users.setActive(id, state === "true");
72
+ setActive(id, state, actor) {
73
+ return this.users.setActive(id, state === "true", actor);
72
74
  }
73
- async sendPasswordResetLink(id) {
74
- await this.users.sendPasswordResetLink(id);
75
+ async sendPasswordResetLink(id, actor) {
76
+ await this.users.sendPasswordResetLink(id, actor);
75
77
  }
76
- async sendFirstAccessLink(id) {
77
- await this.users.sendFirstAccessLink(id);
78
+ async sendFirstAccessLink(id, actor) {
79
+ await this.users.sendFirstAccessLink(id, actor);
78
80
  }
79
- async adminResetTwoFactor(id) {
80
- await this.users.adminResetTwoFactor(id);
81
+ async adminResetTwoFactor(id, actor) {
82
+ await this.users.adminResetTwoFactor(id, actor);
81
83
  }
82
84
  async remove(id, current) {
83
85
  if (current.id === id) {
@@ -105,6 +107,7 @@ __decorate([
105
107
  __metadata("design:returntype", Promise)
106
108
  ], UsersController.prototype, "updateMe", null);
107
109
  __decorate([
110
+ (0, auditable_decorator_1.Auditable)("Troca da própria senha"),
108
111
  (0, common_1.Patch)("me/password"),
109
112
  (0, common_1.HttpCode)(204),
110
113
  __param(0, (0, currentUser_decorator_1.CurrentUser)("id")),
@@ -135,7 +138,7 @@ __decorate([
135
138
  __decorate([
136
139
  (0, common_1.Get)(),
137
140
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
138
- (0, requirePermission_decorator_1.RequirePermission)("users:read:any"),
141
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:read")),
139
142
  (0, swagger_1.ApiOkResponse)({ type: user_response_1.PaginatedUsersResponse }),
140
143
  __param(0, (0, common_1.Query)()),
141
144
  __param(1, (0, currentUser_decorator_1.CurrentUser)()),
@@ -146,7 +149,7 @@ __decorate([
146
149
  __decorate([
147
150
  (0, common_1.Get)("filter-schema"),
148
151
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
149
- (0, requirePermission_decorator_1.RequirePermission)("users:read:any"),
152
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:read")),
150
153
  (0, swagger_1.ApiOkResponse)({ type: filterSchema_response_1.FilterFieldResponse, isArray: true }),
151
154
  __param(0, (0, currentUser_decorator_1.CurrentUser)()),
152
155
  __metadata("design:type", Function),
@@ -156,17 +159,18 @@ __decorate([
156
159
  __decorate([
157
160
  (0, common_1.Get)(":id"),
158
161
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
159
- (0, requirePermission_decorator_1.RequirePermission)("users:read:any"),
162
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:read")),
160
163
  (0, swagger_1.ApiOkResponse)({ type: user_response_1.UserResponse }),
161
164
  __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
165
+ __param(1, (0, currentUser_decorator_1.CurrentUser)()),
162
166
  __metadata("design:type", Function),
163
- __metadata("design:paramtypes", [String]),
167
+ __metadata("design:paramtypes", [String, Object]),
164
168
  __metadata("design:returntype", Promise)
165
169
  ], UsersController.prototype, "findOne", null);
166
170
  __decorate([
167
171
  (0, common_1.Post)(),
168
172
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
169
- (0, requirePermission_decorator_1.RequirePermission)("users:create:any"),
173
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:create")),
170
174
  (0, swagger_1.ApiOkResponse)({ type: user_response_1.UserResponse }),
171
175
  __param(0, (0, common_1.Body)()),
172
176
  __param(1, (0, currentUser_decorator_1.CurrentUser)()),
@@ -177,7 +181,7 @@ __decorate([
177
181
  __decorate([
178
182
  (0, common_1.Patch)(":id"),
179
183
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
180
- (0, requirePermission_decorator_1.RequirePermission)("users:update:any"),
184
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:update")),
181
185
  (0, swagger_1.ApiOkResponse)({ type: user_response_1.UserResponse }),
182
186
  __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
183
187
  __param(1, (0, common_1.Body)()),
@@ -189,42 +193,49 @@ __decorate([
189
193
  __decorate([
190
194
  (0, common_1.Patch)(":id/active/:state"),
191
195
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
192
- (0, requirePermission_decorator_1.RequirePermission)("users:deactivate:any"),
196
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:deactivate")),
193
197
  (0, swagger_1.ApiOkResponse)({ type: user_response_1.UserResponse }),
194
198
  __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
195
199
  __param(1, (0, common_1.Param)("state")),
200
+ __param(2, (0, currentUser_decorator_1.CurrentUser)()),
196
201
  __metadata("design:type", Function),
197
- __metadata("design:paramtypes", [String, String]),
202
+ __metadata("design:paramtypes", [String, String, Object]),
198
203
  __metadata("design:returntype", Promise)
199
204
  ], UsersController.prototype, "setActive", null);
200
205
  __decorate([
206
+ (0, auditable_decorator_1.Auditable)("Redefinição de senha por administrador"),
201
207
  (0, common_1.Post)(":id/reset-password"),
202
208
  (0, common_1.HttpCode)(204),
203
209
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
204
- (0, requirePermission_decorator_1.RequirePermission)("users:reset-password:any"),
210
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:reset-password")),
205
211
  __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
212
+ __param(1, (0, currentUser_decorator_1.CurrentUser)()),
206
213
  __metadata("design:type", Function),
207
- __metadata("design:paramtypes", [String]),
214
+ __metadata("design:paramtypes", [String, Object]),
208
215
  __metadata("design:returntype", Promise)
209
216
  ], UsersController.prototype, "sendPasswordResetLink", null);
210
217
  __decorate([
218
+ (0, auditable_decorator_1.Auditable)("Envio de link de primeiro acesso"),
211
219
  (0, common_1.Post)(":id/first-access-link"),
212
220
  (0, common_1.HttpCode)(204),
213
221
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
214
- (0, requirePermission_decorator_1.RequirePermission)("users:invite:any"),
222
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:invite")),
215
223
  __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
224
+ __param(1, (0, currentUser_decorator_1.CurrentUser)()),
216
225
  __metadata("design:type", Function),
217
- __metadata("design:paramtypes", [String]),
226
+ __metadata("design:paramtypes", [String, Object]),
218
227
  __metadata("design:returntype", Promise)
219
228
  ], UsersController.prototype, "sendFirstAccessLink", null);
220
229
  __decorate([
230
+ (0, auditable_decorator_1.Auditable)("Reset de 2FA por administrador"),
221
231
  (0, common_1.Post)(":id/reset-2fa"),
222
232
  (0, common_1.HttpCode)(204),
223
233
  (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
224
- (0, requirePermission_decorator_1.RequirePermission)("users:update:any"),
234
+ (0, requirePermission_decorator_1.RequireAnyPermission)(...(0, permissionScope_util_1.anyOrTeam)("users:reset-password")),
225
235
  __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
236
+ __param(1, (0, currentUser_decorator_1.CurrentUser)()),
226
237
  __metadata("design:type", Function),
227
- __metadata("design:paramtypes", [String]),
238
+ __metadata("design:paramtypes", [String, Object]),
228
239
  __metadata("design:returntype", Promise)
229
240
  ], UsersController.prototype, "adminResetTwoFactor", null);
230
241
  __decorate([
@@ -12,6 +12,7 @@ const typeorm_1 = require("@nestjs/typeorm");
12
12
  const token_module_1 = require("../auth/token.module");
13
13
  const role_repository_1 = require("../rbac/infra/repositories/role.repository");
14
14
  const role_schema_1 = require("../rbac/infra/schema/role.schema");
15
+ const rbac_module_1 = require("../rbac/rbac.module");
15
16
  const domain_1 = require("./domain");
16
17
  const infra_1 = require("./infra");
17
18
  const presentation_1 = require("./presentation");
@@ -23,6 +24,7 @@ exports.UsersModule = UsersModule = __decorate([
23
24
  imports: [
24
25
  typeorm_1.TypeOrmModule.forFeature([infra_1.User, infra_1.PasswordHistory, role_schema_1.Role]),
25
26
  token_module_1.TokenModule,
27
+ rbac_module_1.RbacModule,
26
28
  ],
27
29
  controllers: [presentation_1.UsersController],
28
30
  providers: [
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { AppBrand } from "./core/config/appBrand.interface";
8
8
  export { ConfigModule, ConfigModuleOptions, EnvValidator, } from "./core/config/config.module";
9
9
  export { Env, envSchema, validateEnv } from "./core/config/env.schema";
10
10
  export { EnvService } from "./core/config/env.service";
11
+ export { Actor } from "./core/auth/actor.interface";
11
12
  export { AuthenticatedUser, RequestWithUser, } from "./core/auth/authenticatedUser.interface";
12
13
  export { ACCESS_COOKIE, clearAuthCookies, REFRESH_COOKIE, setAuthCookies, } from "./core/auth/cookie.util";
13
14
  export { CurrentUser } from "./core/auth/decorators/currentUser.decorator";
@@ -17,6 +18,7 @@ export { AppThrottlerGuard } from "./core/auth/guards/appThrottler.guard";
17
18
  export { CsrfGuard } from "./core/auth/guards/csrf.guard";
18
19
  export { JwtAuthGuard } from "./core/auth/guards/jwtAuth.guard";
19
20
  export { PermissionsGuard } from "./core/auth/guards/permissions.guard";
21
+ export { anyOrTeam, SCOPE_ANY, SCOPE_TEAM, } from "./core/auth/permissionScope.util";
20
22
  export { applyFilter, FilterSql } from "./core/query/applyFilter.util";
21
23
  export { DEFAULT_OPERATORS, FILTER_MAX_DEPTH, FILTER_MAX_IN_ITEMS, FILTER_MAX_LENGTH, FILTER_MAX_NODES, } from "./core/query/filter.constants";
22
24
  export { FilterCatalog, FilterCondition, FilterFieldDefinition, FilterGroup, FilterNode, FilterOption, FilterRelation, } from "./core/query/filterField.interface";
@@ -31,9 +33,8 @@ export { PaginationMetaResponse } from "./core/utils/paginationMeta.response";
31
33
  export { BaseEntity } from "./core/database/base.schema";
32
34
  export { seedAdmin } from "./core/database/seeds/seedAdmin";
33
35
  export { seedPermissions } from "./core/database/seeds/seedPermissions";
34
- export { AuditChange } from "./features/audit/infra/schema/auditChange.schema";
35
36
  export { AuditDataChange } from "./features/audit/infra/schema/auditDataChange.schema";
36
- export { ErrorLog } from "./features/audit/infra/schema/errorLog.schema";
37
+ export { RequestLog } from "./features/audit/infra/schema/requestLog.schema";
37
38
  export { AuthToken } from "./features/auth/infra/schema/authToken.schema";
38
39
  export { Notification } from "./features/notifications/infra/schema/notification.schema";
39
40
  export { NotificationRead } from "./features/notifications/infra/schema/notificationRead.schema";
@@ -47,9 +48,12 @@ export { Scope } from "./features/rbac/infra/schema/scope.schema";
47
48
  export { PasswordHistory } from "./features/users/infra/schema/passwordHistory.schema";
48
49
  export { User } from "./features/users/infra/schema/user.schema";
49
50
  export { auditContext, AuditContextStore } from "./core/context/auditContext";
50
- export { AppEvent, SystemCleanupFailedEvent, UserLockedEvent, UserPasswordExpiringEvent, UserPasswordResetRequestedEvent, UserRolesChangedEvent, UserTwoFactorResetEvent, } from "./core/events/appEvent.enum";
51
+ export { AppEvent, HttpErrorEvent, SystemCleanupFailedEvent, UserLockedEvent, UserPasswordExpiringEvent, UserPasswordResetRequestedEvent, UserRolesChangedEvent, UserTwoFactorResetEvent, } from "./core/events/appEvent.enum";
51
52
  export { AppErrorCode } from "./core/filters/appErrorCode.enum";
52
53
  export { codedBadRequest, codedTooManyRequests, codedUnauthorized, } from "./core/utils/codedError.util";
54
+ export { ErrorType } from "./features/audit/domain/enums/errorType.enum";
55
+ export { RequestOutcome } from "./features/audit/domain/enums/requestOutcome.enum";
56
+ export { Auditable } from "./features/audit/presentation/decorators/auditable.decorator";
53
57
  export { BaseGateway, SocketData } from "./core/websocket/baseGateway";
54
58
  export { NOTIFICATIONS_NAMESPACE, roleRoom, SESSION_REFRESH_EVENT, userRoom, } from "./core/websocket/socket.constants";
55
59
  export { SOCKET_EVENT_VERSION, SocketEvent, wrapSocketEvent, } from "./core/websocket/socketEvent.envelope";