@fiado/type-kit 3.148.0 → 3.149.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.
@@ -0,0 +1,64 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import { PrepareChallengeRequest } from '../../../../src/platformRbac/auth/PrepareChallengeRequest';
5
+ import { ChallengeNameEnum } from '../../../../src/platformRbac/enums/ChallengeNameEnum';
6
+
7
+ /**
8
+ * DEC-RBAC-094: `email` es opcional y robusto a string vacío. Un usuario phone-primario
9
+ * (sin email) hace login MFA (SMS_OTP / TOTP) sin email — el trigger CreateAuthChallenge
10
+ * ya no manda `''`. `@ValidateIf` saltea undefined/null/'' (`@IsOptional` NO saltea `''`).
11
+ * La rama EMAIL_OTP del preparer sigue necesitando email en runtime — eso NO lo valida el DTO.
12
+ */
13
+ describe('PrepareChallengeRequest', () => {
14
+ const base = {
15
+ cognitoSub: 'f4586478-20c1-700c-c40a-2396db1affe3',
16
+ challengeName: ChallengeNameEnum.SMS_OTP,
17
+ phone: '+524422194128',
18
+ };
19
+
20
+ it('valida SMS_OTP sin email (undefined)', async () => {
21
+ const dto = plainToInstance(PrepareChallengeRequest, { ...base });
22
+ const errors = await validate(dto);
23
+ expect(errors).toEqual([]);
24
+ });
25
+
26
+ it('valida SMS_OTP con email = "" (string vacío del trigger legacy)', async () => {
27
+ const dto = plainToInstance(PrepareChallengeRequest, { ...base, email: '' });
28
+ const errors = await validate(dto);
29
+ expect(errors).toEqual([]);
30
+ });
31
+
32
+ it('valida SMS_OTP con email = null', async () => {
33
+ const dto = plainToInstance(PrepareChallengeRequest, { ...base, email: null });
34
+ const errors = await validate(dto);
35
+ expect(errors).toEqual([]);
36
+ });
37
+
38
+ it('valida cuando email SÍ viene y es un email válido', async () => {
39
+ const dto = plainToInstance(PrepareChallengeRequest, {
40
+ ...base,
41
+ challengeName: ChallengeNameEnum.EMAIL_OTP,
42
+ email: 'user@dominio.com',
43
+ });
44
+ const errors = await validate(dto);
45
+ expect(errors).toEqual([]);
46
+ });
47
+
48
+ it('falla cuando email viene pero NO es un email válido', async () => {
49
+ const dto = plainToInstance(PrepareChallengeRequest, { ...base, email: 'no-es-email' });
50
+ const errors = await validate(dto);
51
+ expect(errors.length).toBeGreaterThan(0);
52
+ expect(errors[0].property).toBe('email');
53
+ });
54
+
55
+ it('falla si challengeName no es un valor del enum', async () => {
56
+ const dto = plainToInstance(PrepareChallengeRequest, {
57
+ ...base,
58
+ challengeName: 'BOGUS' as ChallengeNameEnum,
59
+ });
60
+ const errors = await validate(dto);
61
+ expect(errors.length).toBeGreaterThan(0);
62
+ expect(errors[0].property).toBe('challengeName');
63
+ });
64
+ });
@@ -2,6 +2,6 @@ import { ChallengeNameEnum } from '../enums/ChallengeNameEnum';
2
2
  export declare class PrepareChallengeRequest {
3
3
  cognitoSub: string;
4
4
  challengeName: ChallengeNameEnum;
5
- email: string;
5
+ email?: string;
6
6
  phone?: string;
7
7
  }
@@ -24,6 +24,7 @@ __decorate([
24
24
  __metadata("design:type", String)
25
25
  ], PrepareChallengeRequest.prototype, "challengeName", void 0);
26
26
  __decorate([
27
+ (0, class_validator_1.ValidateIf)((o) => o.email !== undefined && o.email !== null && o.email !== ''),
27
28
  (0, class_validator_1.IsEmail)(),
28
29
  __metadata("design:type", String)
29
30
  ], PrepareChallengeRequest.prototype, "email", void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.148.0",
3
+ "version": "3.149.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -1,4 +1,4 @@
1
- import { IsEmail, IsEnum, IsOptional, IsString } from 'class-validator';
1
+ import { IsEmail, IsEnum, IsOptional, IsString, ValidateIf } from 'class-validator';
2
2
  import { ChallengeNameEnum } from '../enums/ChallengeNameEnum';
3
3
 
4
4
  export class PrepareChallengeRequest {
@@ -8,8 +8,14 @@ export class PrepareChallengeRequest {
8
8
  @IsEnum(ChallengeNameEnum)
9
9
  challengeName!: ChallengeNameEnum;
10
10
 
11
+ // DEC-RBAC-094: email OPCIONAL y robusto a string vacío. Un usuario phone-primario (sin email)
12
+ // hace login MFA (SMS_OTP/TOTP) sin email; el trigger CreateAuthChallenge ya no manda ''. Se usa
13
+ // @ValidateIf (no @IsOptional) porque @IsOptional NO saltea '' — acá necesitamos que undefined,
14
+ // null y '' NO rompan la validación. Cuando email SÍ trae valor no vacío, debe ser un email válido.
15
+ // La rama EMAIL_OTP del preparer sigue exigiendo email en runtime (no se fuerza en el DTO). Ver DECISIONES.md.
16
+ @ValidateIf((o) => o.email !== undefined && o.email !== null && o.email !== '')
11
17
  @IsEmail()
12
- email!: string;
18
+ email?: string;
13
19
 
14
20
  // DEC-RBAC-083: teléfono destino del challenge SMS_OTP. Lo popula el trigger CreateAuthChallenge
15
21
  // (JWTAuth-cognito) desde el atributo phone_number de Cognito, igual que email. Opcional: ausente