@solidstarters/solid-core 1.2.169 → 1.2.171

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 (97) hide show
  1. package/dist/config/iam.config.d.ts +2 -0
  2. package/dist/config/iam.config.d.ts.map +1 -1
  3. package/dist/config/iam.config.js +2 -1
  4. package/dist/config/iam.config.js.map +1 -1
  5. package/dist/controllers/authentication.controller.d.ts.map +1 -1
  6. package/dist/controllers/authentication.controller.js +0 -8
  7. package/dist/controllers/authentication.controller.js.map +1 -1
  8. package/dist/controllers/email-template.controller.d.ts +1 -1
  9. package/dist/controllers/email-template.controller.d.ts.map +1 -1
  10. package/dist/controllers/email-template.controller.js +3 -6
  11. package/dist/controllers/email-template.controller.js.map +1 -1
  12. package/dist/controllers/google-authentication.controller.d.ts +3 -3
  13. package/dist/controllers/google-authentication.controller.d.ts.map +1 -1
  14. package/dist/controllers/google-authentication.controller.js +5 -8
  15. package/dist/controllers/google-authentication.controller.js.map +1 -1
  16. package/dist/controllers/media.controller.d.ts +1 -1
  17. package/dist/controllers/media.controller.d.ts.map +1 -1
  18. package/dist/controllers/media.controller.js +1 -5
  19. package/dist/controllers/media.controller.js.map +1 -1
  20. package/dist/controllers/model-metadata.controller.d.ts.map +1 -1
  21. package/dist/controllers/model-metadata.controller.js +0 -5
  22. package/dist/controllers/model-metadata.controller.js.map +1 -1
  23. package/dist/controllers/otp-authentication.controller.js +0 -3
  24. package/dist/controllers/otp-authentication.controller.js.map +1 -1
  25. package/dist/controllers/service.controller.d.ts +2 -2
  26. package/dist/controllers/service.controller.d.ts.map +1 -1
  27. package/dist/controllers/service.controller.js +3 -7
  28. package/dist/controllers/service.controller.js.map +1 -1
  29. package/dist/controllers/sms-template.controller.d.ts.map +1 -1
  30. package/dist/controllers/sms-template.controller.js +0 -3
  31. package/dist/controllers/sms-template.controller.js.map +1 -1
  32. package/dist/entities/user.entity.d.ts +3 -0
  33. package/dist/entities/user.entity.d.ts.map +1 -1
  34. package/dist/entities/user.entity.js +13 -1
  35. package/dist/entities/user.entity.js.map +1 -1
  36. package/dist/filters/http-exception.filter.js +1 -1
  37. package/dist/filters/http-exception.filter.js.map +1 -1
  38. package/dist/helpers/error-mapper.service.d.ts.map +1 -1
  39. package/dist/helpers/error-mapper.service.js +1 -1
  40. package/dist/helpers/error-mapper.service.js.map +1 -1
  41. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.d.ts +2 -1
  42. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.d.ts.map +1 -1
  43. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.js +3 -3
  44. package/dist/helpers/field-crud-managers/PasswordFieldCrudManager.js.map +1 -1
  45. package/dist/helpers/model-metadata-helper.service.js.map +1 -1
  46. package/dist/passport-strategies/local.strategy.js +1 -1
  47. package/dist/passport-strategies/local.strategy.js.map +1 -1
  48. package/dist/seeders/seed-data/solid-core-metadata.json +43 -0
  49. package/dist/services/authentication.service.d.ts +2 -1
  50. package/dist/services/authentication.service.d.ts.map +1 -1
  51. package/dist/services/authentication.service.js +28 -5
  52. package/dist/services/authentication.service.js.map +1 -1
  53. package/dist/services/bcrypt.service.d.ts +11 -1
  54. package/dist/services/bcrypt.service.d.ts.map +1 -1
  55. package/dist/services/bcrypt.service.js +35 -5
  56. package/dist/services/bcrypt.service.js.map +1 -1
  57. package/dist/services/crud.service.d.ts.map +1 -1
  58. package/dist/services/crud.service.js +2 -1
  59. package/dist/services/crud.service.js.map +1 -1
  60. package/dist/services/export-transaction.service.d.ts +3 -1
  61. package/dist/services/export-transaction.service.d.ts.map +1 -1
  62. package/dist/services/export-transaction.service.js +70 -16
  63. package/dist/services/export-transaction.service.js.map +1 -1
  64. package/dist/services/hashing.service.d.ts +4 -1
  65. package/dist/services/hashing.service.d.ts.map +1 -1
  66. package/dist/services/hashing.service.js.map +1 -1
  67. package/dist/solid-core.module.d.ts.map +1 -1
  68. package/dist/solid-core.module.js +46 -61
  69. package/dist/solid-core.module.js.map +1 -1
  70. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  71. package/dist/subscribers/audit.subscriber.js +1 -1
  72. package/dist/subscribers/audit.subscriber.js.map +1 -1
  73. package/dist/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +1 -1
  75. package/src/config/iam.config.ts +2 -1
  76. package/src/controllers/authentication.controller.ts +8 -10
  77. package/src/controllers/email-template.controller.ts +7 -10
  78. package/src/controllers/google-authentication.controller.ts +9 -10
  79. package/src/controllers/media.controller.ts +5 -6
  80. package/src/controllers/model-metadata.controller.ts +5 -6
  81. package/src/controllers/otp-authentication.controller.ts +2 -2
  82. package/src/controllers/service.controller.ts +8 -9
  83. package/src/controllers/sms-template.controller.ts +3 -4
  84. package/src/entities/user.entity.ts +9 -0
  85. package/src/filters/http-exception.filter.ts +1 -1
  86. package/src/helpers/error-mapper.service.ts +1 -35
  87. package/src/helpers/field-crud-managers/PasswordFieldCrudManager.ts +4 -3
  88. package/src/helpers/model-metadata-helper.service.ts +1 -1
  89. package/src/passport-strategies/local.strategy.ts +1 -1
  90. package/src/seeders/seed-data/solid-core-metadata.json +43 -0
  91. package/src/services/authentication.service.ts +32 -3
  92. package/src/services/bcrypt.service.ts +45 -7
  93. package/src/services/crud.service.ts +2 -1
  94. package/src/services/export-transaction.service.ts +118 -55
  95. package/src/services/hashing.service.ts +5 -2
  96. package/src/solid-core.module.ts +59 -61
  97. package/src/subscribers/audit.subscriber.ts +6 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.169",
3
+ "version": "1.2.171",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,8 @@ export const iamConfig = registerAs('iam', () => {
22
22
  callbackURL: process.env.IAM_GOOGLE_OAUTH_CALLBACK_URL,
23
23
  redirectURL: process.env.IAM_GOOGLE_OAUTH_REDIRECT_URL,
24
24
  },
25
- iamAutoGeneratedPassword:process.env.IAM_AUTOGENERATED_PASSWORD || true
25
+ iamAutoGeneratedPassword:process.env.IAM_AUTOGENERATED_PASSWORD || true,
26
+ passwordPepper: process.env.IAM_PASSWORD_PEPPER || '', // Adding a pepper to the password hashing process for extra security
26
27
  };
27
28
  })
28
29
 
@@ -1,6 +1,5 @@
1
- import { Body, Controller, Get, HttpCode, HttpStatus, Logger, Post, Res, UseGuards } from '@nestjs/common';
1
+ import { Body, Controller, Get, HttpCode, HttpStatus, Logger, Post, Res } from '@nestjs/common';
2
2
  import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
3
- import { SkipThrottle, ThrottlerGuard } from '@nestjs/throttler';
4
3
  import { Response } from 'express';
5
4
  import { ActiveUser } from "../decorators/active-user.decorator";
6
5
  import { Public } from '../decorators/public.decorator';
@@ -17,15 +16,15 @@ import { AuthenticationService } from '../services/authentication.service';
17
16
  // @Auth(AuthType.None)
18
17
  @Controller('iam')
19
18
  @ApiTags("Iam")
20
- @UseGuards(ThrottlerGuard)
21
- @SkipThrottle({login: true, short: true, burst: true, sustained: true}) // disable all sets by default for this controller
19
+ // @UseGuards(ThrottlerGuard)
20
+ // @SkipThrottle({login: true, short: true, burst: true, sustained: true}) // disable all sets by default for this controller
22
21
  export class AuthenticationController {
23
22
  private readonly logger = new Logger(AuthenticationController.name);
24
23
 
25
24
  constructor(private readonly authService: AuthenticationService) { }
26
25
 
27
26
  @Public()
28
- @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
27
+ // @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
29
28
  @Post('register')
30
29
  signUp(@Body() signUpDto: SignUpDto) {
31
30
  return this.authService.signUp(signUpDto);
@@ -38,8 +37,7 @@ export class AuthenticationController {
38
37
  }
39
38
 
40
39
  @Public()
41
- // @UseGuards(LocalAuthGuard)
42
- @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
40
+ // @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
43
41
  @HttpCode(HttpStatus.OK) // by default @Post does 201, we wanted 200 - hence using @HttpCode(HttpStatus.OK)
44
42
  @Post('authenticate')
45
43
  async signIn(
@@ -62,7 +60,7 @@ export class AuthenticationController {
62
60
  }
63
61
 
64
62
  @Public()
65
- @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
63
+ // @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
66
64
  @HttpCode(HttpStatus.OK) // changed since the default is 201
67
65
  @Post('refresh-tokens')
68
66
  refreshTokens(@Body() refreshTokenDto: RefreshTokenDto) {
@@ -70,14 +68,14 @@ export class AuthenticationController {
70
68
  }
71
69
 
72
70
  @Public()
73
- @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
71
+ // @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
74
72
  @Post('initiate/forgot-password')
75
73
  initiateForgotPassword(@Body() initiateForgotPasswordDto: InitiateForgotPasswordDto) {
76
74
  return this.authService.initiateForgotPassword(initiateForgotPasswordDto);
77
75
  }
78
76
 
79
77
  @Public()
80
- @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
78
+ // @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
81
79
  @Post('confirm/forgot-password')
82
80
  confirmForgotPassword(@Body() confirmForgotPasswordDto: ConfirmForgotPasswordDto) {
83
81
  return this.authService.confirmForgotPassword(confirmForgotPasswordDto);
@@ -1,25 +1,22 @@
1
- import { Body, Controller, Delete, Get, Header, Param, Patch, Post, Put, Query, UploadedFiles, UseGuards, UseInterceptors } from '@nestjs/common';
2
- import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
3
- import { PaginationQueryDto } from 'src/dtos/pagination-query.dto';
4
- import { Roles } from 'src/decorators/roles.decorator';
5
- import { EmailTemplateService } from '../services/email-template.service';
1
+ import { Body, Controller, Delete, Get, Header, Param, Post, Put, Query, UploadedFiles, UseInterceptors } from '@nestjs/common';
2
+ import { AnyFilesInterceptor } from '@nestjs/platform-express';
3
+ import { ApiQuery, ApiTags } from '@nestjs/swagger';
4
+ import { Public } from 'src/decorators/public.decorator';
6
5
  import { CreateEmailTemplateDto } from '../dtos/create-email-template.dto';
7
6
  import { UpdateEmailTemplateDto } from '../dtos/update-email-template.dto';
8
- import { Public } from 'src/decorators/public.decorator';
7
+ import { EmailTemplateService } from '../services/email-template.service';
9
8
 
10
9
 
11
10
 
12
11
  // TODO: esInterop not working somehow, defaulted to using the require syntax to import Mailgen. Figure a better way to do this.
13
12
  // import { Mailgen } from 'mailgen';
14
13
  import Mailgen = require('mailgen');
15
- import { AnyFilesInterceptor } from '@nestjs/platform-express';
16
- import { ThrottlerGuard, SkipThrottle } from '@nestjs/throttler';
17
14
 
18
15
 
19
16
  @Controller('email-template')
20
17
  @ApiTags("Common")
21
- @UseGuards(ThrottlerGuard)
22
- @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
18
+ // @UseGuards(ThrottlerGuard)
19
+ // @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
23
20
  export class EmailTemplateController {
24
21
  constructor(private readonly service: EmailTemplateService) { }
25
22
 
@@ -1,24 +1,23 @@
1
1
  import { Controller, Get, Inject, InternalServerErrorException, Query, Req, Res, UseGuards } from '@nestjs/common';
2
- import { AuthenticationService } from '../services/authentication.service';
2
+ import { ConfigType } from '@nestjs/config';
3
+ import { ApiQuery, ApiTags } from '@nestjs/swagger';
4
+ import { Request, Response } from 'express';
5
+ import { isGoogleOAuthConfigured } from 'src/helpers/google-oauth.helper';
6
+ import { iamConfig } from '../config/iam.config';
3
7
  import { Auth } from '../decorators/auth.decorator';
8
+ import { Public } from '../decorators/public.decorator';
4
9
  import { AuthType } from '../enums/auth-type.enum';
5
- import { ApiQuery, ApiTags } from '@nestjs/swagger';
6
10
  import { GoogleOauthGuard } from '../passport-strategies/google-oauth.strategy';
7
- import { Request, Response } from 'express';
8
- import { ConfigType } from '@nestjs/config';
11
+ import { AuthenticationService } from '../services/authentication.service';
9
12
  import { UserService } from '../services/user.service';
10
- import { Public } from '../decorators/public.decorator';
11
- import { iamConfig } from '../config/iam.config';
12
- import { isGoogleOAuthConfigured } from 'src/helpers/google-oauth.helper';
13
- import { ThrottlerGuard, SkipThrottle } from '@nestjs/throttler';
14
13
 
15
14
 
16
15
 
17
16
  @Auth(AuthType.None)
18
17
  @Controller('iam/google')
19
18
  @ApiTags("Iam")
20
- @UseGuards(ThrottlerGuard)
21
- @SkipThrottle({ login: false, short: false, burst: true, sustained: true }) //Enable the login throttle only
19
+ // @UseGuards(ThrottlerGuard)
20
+ // @SkipThrottle({ login: false, short: false, burst: true, sustained: true }) //Enable the login throttle only
22
21
  export class GoogleAuthenticationController {
23
22
  constructor(
24
23
  @Inject(iamConfig.KEY) private iamConfiguration: ConfigType<typeof iamConfig>,
@@ -1,11 +1,10 @@
1
- import { Controller, Post, Body, Param, UploadedFiles, UseInterceptors, Put, Get, Query, Delete, Patch, UseGuards } from '@nestjs/common';
1
+ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, UploadedFiles, UseInterceptors } from '@nestjs/common';
2
2
  import { AnyFilesInterceptor } from "@nestjs/platform-express";
3
3
  import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
4
4
  import { Public } from 'src/decorators/public.decorator';
5
- import { MediaService } from 'src/services/media.service';
6
5
  import { CreateMediaDto } from 'src/dtos/create-media.dto';
7
6
  import { UpdateMediaDto } from 'src/dtos/update-media.dto';
8
- import { ThrottlerGuard, SkipThrottle } from '@nestjs/throttler';
7
+ import { MediaService } from 'src/services/media.service';
9
8
 
10
9
  enum ShowSoftDeleted {
11
10
  INCLUSIVE = "inclusive",
@@ -14,8 +13,8 @@ enum ShowSoftDeleted {
14
13
 
15
14
  @ApiTags('Solid Core')
16
15
  @Controller('media')
17
- @UseGuards(ThrottlerGuard)
18
- @SkipThrottle({ short: true, login: true, burst: true, sustained: true }) //Skip all
16
+ // @UseGuards(ThrottlerGuard)
17
+ // @SkipThrottle({ short: true, login: true, burst: true, sustained: true }) //Skip all
19
18
  export class MediaController {
20
19
  constructor(private readonly service: MediaService) {}
21
20
 
@@ -49,7 +48,7 @@ export class MediaController {
49
48
  }
50
49
 
51
50
  @Public()
52
- @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
51
+ // @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
53
52
  @ApiBearerAuth("jwt")
54
53
  @Post('/upload')
55
54
  @UseInterceptors(AnyFilesInterceptor())
@@ -1,16 +1,15 @@
1
- import { Body, Controller, Delete, Get, Logger, Param, ParseIntPipe, Post, Put, Query, UseGuards } from '@nestjs/common';
1
+ import { Body, Controller, Delete, Get, Logger, Param, ParseIntPipe, Post, Put, Query } from '@nestjs/common';
2
2
  import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
3
3
  import { Public } from 'src/decorators/public.decorator';
4
4
  import { BasicFilterDto } from '../dtos/basic-filters.dto';
5
5
  import { CreateModelMetadataDto } from '../dtos/create-model-metadata.dto';
6
6
  import { UpdateModelMetaDataDto } from '../dtos/update-model-metadata.dto';
7
7
  import { ModelMetadataService } from '../services/model-metadata.service';
8
- import { ThrottlerGuard, SkipThrottle } from '@nestjs/throttler';
9
8
 
10
9
  @Controller('model-metadata')
11
10
  @ApiTags("App Builder")
12
- @UseGuards(ThrottlerGuard)
13
- @SkipThrottle({ short: true, login: true, burst: true, sustained: true }) //Skip all
11
+ // @UseGuards(ThrottlerGuard)
12
+ // @SkipThrottle({ short: true, login: true, burst: true, sustained: true }) //Skip all
14
13
  export class ModelMetadataController {
15
14
  private logger = new Logger('ModelMetadataController');
16
15
 
@@ -35,7 +34,7 @@ export class ModelMetadataController {
35
34
  }
36
35
 
37
36
  @Public()
38
- @SkipThrottle({ burst: false, short: true, login: true, sustained: true }) //Enable burst only
37
+ // @SkipThrottle({ burst: false, short: true, login: true, sustained: true }) //Enable burst only
39
38
  @Get('public')
40
39
  async findManyPublic() {
41
40
  const basicFilterDto: BasicFilterDto = {
@@ -65,7 +64,7 @@ export class ModelMetadataController {
65
64
  }
66
65
 
67
66
  @Public()
68
- @SkipThrottle({ short: false, burst: true, login: true, sustained: true }) //Enable short only
67
+ // @SkipThrottle({ short: false, burst: true, login: true, sustained: true }) //Enable short only
69
68
  @Post('/update-user-key')
70
69
  updateUserKey(@Body() data: any) {
71
70
  return this.modelMetadataService.updateUserKey(data);
@@ -14,8 +14,8 @@ import { ThrottlerGuard, SkipThrottle } from '@nestjs/throttler';
14
14
  @Auth(AuthType.None)
15
15
  @Controller('iam/otp')
16
16
  @ApiTags("Iam")
17
- @UseGuards(ThrottlerGuard)
18
- @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
17
+ // @UseGuards(ThrottlerGuard)
18
+ // @SkipThrottle({ login: false, short: true, burst: true, sustained: true }) //Enable the login throttle only
19
19
  export class OTPAuthenticationController {
20
20
  constructor(private readonly authService: AuthenticationService) { }
21
21
 
@@ -1,19 +1,18 @@
1
- import { Body, Controller, Get, Logger, Post, UseGuards } from '@nestjs/common';
2
- import { Public } from 'src/decorators/public.decorator';
3
- import { SolidRegistry } from '../helpers/solid-registry';
1
+ import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
4
2
  import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
5
- import { ThrottlerGuard, SkipThrottle } from '@nestjs/throttler';
6
- import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
7
3
  import { ActiveUser } from 'src/decorators/active-user.decorator';
4
+ import { Public } from 'src/decorators/public.decorator';
5
+ import { ErrorMapperService } from 'src/helpers/error-mapper.service';
6
+ import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
8
7
  import { AiInteractionService } from 'src/services/ai-interaction.service';
9
8
  import { MqMessageService } from 'src/services/mq-message.service';
10
- import { ErrorMapperService } from 'src/helpers/error-mapper.service';
9
+ import { SolidRegistry } from '../helpers/solid-registry';
11
10
 
12
11
 
13
12
  @Controller('')
14
13
  @ApiTags("Common")
15
- @UseGuards(ThrottlerGuard)
16
- @SkipThrottle({ short: true, login: true, burst: true, sustained: true }) // Skip all
14
+ // @UseGuards(ThrottlerGuard)
15
+ // @SkipThrottle({ short: true, login: true, burst: true, sustained: true }) // Skip all
17
16
  export class ServiceController {
18
17
  private readonly logger = new Logger(ServiceController.name);
19
18
 
@@ -87,7 +86,7 @@ export class ServiceController {
87
86
  }
88
87
 
89
88
  @Public()
90
- @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
89
+ // @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
91
90
  @Post('seed')
92
91
  async seedData(@Body() seedData: any) {
93
92
  const seeder = this.solidRegistry
@@ -1,7 +1,6 @@
1
- import { Body, Controller, Delete, Get, Param, Post, Put, Query, UploadedFiles, UseGuards, UseInterceptors } from '@nestjs/common';
1
+ import { Body, Controller, Delete, Get, Param, Post, Put, Query, UploadedFiles, UseInterceptors } from '@nestjs/common';
2
2
  import { AnyFilesInterceptor } from '@nestjs/platform-express';
3
3
  import { ApiQuery, ApiTags } from '@nestjs/swagger';
4
- import { SkipThrottle, ThrottlerGuard } from '@nestjs/throttler';
5
4
  import { Public } from 'src/decorators/public.decorator';
6
5
  import { CreateSmsTemplateDto } from '../dtos/create-sms-template.dto';
7
6
  import { UpdateSmsTemplateDto } from '../dtos/update-sms-template.dto';
@@ -10,8 +9,8 @@ import { SmsTemplateService } from '../services/sms-template.service';
10
9
 
11
10
  @Controller('sms-template')
12
11
  @ApiTags("Common")
13
- @UseGuards(ThrottlerGuard)
14
- @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
12
+ // @UseGuards(ThrottlerGuard)
13
+ // @SkipThrottle({ short: false, login: true, burst: true, sustained: true }) //Enable the short throttle only
15
14
  export class SmsTemplateController {
16
15
  constructor(private readonly service: SmsTemplateService) { }
17
16
 
@@ -102,4 +102,13 @@ export class User extends CommonEntity {
102
102
  @OneToMany(() => UserViewMetadata, userViewMetadata => userViewMetadata.user, { cascade: true })
103
103
  // don't send to client
104
104
  userViewMetadata: UserViewMetadata[];
105
+ // dont send to client
106
+ @Column({ type: "varchar", default: "bcrypt" })
107
+ passwordScheme: string;
108
+ // dont send to client
109
+ @Column({ type: "int", default: 1 })
110
+ passwordSchemeVersion: number;
111
+ // dont send to client
112
+ @Column({ type: "timestamp", nullable: true })
113
+ rehashedAt: Date;
105
114
  }
@@ -33,7 +33,7 @@ export class HttpExceptionFilter implements ExceptionFilter {
33
33
  // Canonical code + static message
34
34
  const code: ErrorCode = this.errorMapper.mapException(exception);
35
35
  const defaultStatus = this.errorMapper.getHttpStatus(code);
36
- const message = code === 'unknown-error' ? `${exception?.message}` : this.errorMapper.getMessage(code);
36
+ const message = code === 'solidx-unknown-error' ? `${exception?.message}` : this.errorMapper.getMessage(code);
37
37
 
38
38
  const status = explicitStatus ?? defaultStatus ?? 500;
39
39
 
@@ -1,40 +1,6 @@
1
1
  import { Injectable, Logger } from '@nestjs/common';
2
2
  import { SolidRegistry } from 'src/helpers/solid-registry';
3
3
  import { ErrorCode, ErrorMeta, ErrorRule, IErrorCodeProvider } from 'src/interfaces';
4
-
5
- // export const ERROR_CODES = [
6
- // 'db-duplicate-key',
7
- // 'db-foreign-key-error',
8
- // 'solidx-mcp-server-unavailable',
9
- // 'unknown-error',
10
- // ] as const;
11
-
12
- // export type ErrorCode = typeof ERROR_CODES[number];
13
-
14
- // type ErrorMeta = {
15
- // message: string;
16
- // httpStatus?: number;
17
- // };
18
-
19
- // const ERROR_MESSAGES: Record<ErrorCode, ErrorMeta> = {
20
- // 'db-duplicate-key': {
21
- // message: 'Duplicate key violation. A record with these values already exists.',
22
- // httpStatus: 409,
23
- // },
24
- // 'db-foreign-key-error': {
25
- // message: 'Foreign key constraint prevents this operation due to related records.',
26
- // httpStatus: 409,
27
- // },
28
- // 'solidx-mcp-server-unavailable': {
29
- // message: 'SolidX MCP server is unreachable. Please verify the MCP endpoint.',
30
- // httpStatus: 503,
31
- // },
32
- // 'unknown-error': {
33
- // message: 'An unexpected error occurred.',
34
- // httpStatus: 500,
35
- // },
36
- // };
37
-
38
4
  @Injectable()
39
5
  export class ErrorMapperService {
40
6
  private readonly logger = new Logger(ErrorMapperService.name);
@@ -76,7 +42,7 @@ export class ErrorMapperService {
76
42
  this.logger.warn(`Error rule threw in match(): code=${rule.code} provider? — ${e}`);
77
43
  }
78
44
  }
79
- return 'unknown-error';
45
+ return 'solidx-unknown-error';
80
46
  }
81
47
 
82
48
  private lookupMeta(code: ErrorCode): ErrorMeta | undefined {
@@ -10,11 +10,10 @@ export interface PasswordFieldOptions {
10
10
  regexPattern: string | undefined | null;
11
11
  fieldName: string | undefined | null;
12
12
  isUpdate: Boolean;
13
+ hashingService: HashingService | undefined | null;
13
14
  }
14
15
 
15
16
  export class PasswordFieldCrudManager implements FieldCrudManager {
16
- private hashingService: HashingService = new BcryptService(); //FIXME: The bcrypt service injected here probably can be optimized to be a singleton
17
-
18
17
  constructor(private readonly options: PasswordFieldOptions) {
19
18
  }
20
19
 
@@ -47,7 +46,9 @@ export class PasswordFieldCrudManager implements FieldCrudManager {
47
46
 
48
47
  async transformForCreate(dto: any): Promise<any> {
49
48
  if(dto[this.options.fieldName]){
50
- dto[this.options.fieldName] = await this.hashingService.hash(dto[this.options.fieldName]);
49
+ dto[this.options.fieldName] = await this.options.hashingService.hash(dto[this.options.fieldName]);
50
+ dto['passwordScheme'] = this.options.hashingService.name(); //Don't want to expose this in dto // e.g., 'bcrypt'
51
+ dto['passwordHashVersion'] = this.options.hashingService.currentVersion(); //Don't want to expose this in dto // e.g., 1, 2, ...
51
52
  }
52
53
  return dto;
53
54
  }
@@ -122,7 +122,7 @@ export class ModelMetadataHelperService {
122
122
  }
123
123
  }
124
124
  });
125
- const fields = [];
125
+ const fields: any[] = [];
126
126
  if (model) {
127
127
  // Add the fields of the current model
128
128
  fields.push(...model.fields);
@@ -15,7 +15,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) {
15
15
  }
16
16
 
17
17
  async validate(username: string, password: string): Promise<any> {
18
- const user = await this.authService.validateUser({ username, password, email: null });
18
+ const user = await this.authService.validateUserAndRehashPasswordIfRequired({ username, password, email: null });
19
19
  if (!user) {
20
20
  throw new UnauthorizedException();
21
21
  }
@@ -2115,6 +2115,49 @@
2115
2115
  "relationCreateInverse": true,
2116
2116
  "relationModelModuleName": "solid-core",
2117
2117
  "isSystem": true
2118
+ },
2119
+ {
2120
+ "name": "passwordScheme",
2121
+ "displayName": "Password Scheme",
2122
+ "type": "selectionStatic",
2123
+ "ormType": "varchar",
2124
+ "length": 256,
2125
+ "required": true,
2126
+ "index": false,
2127
+ "isSystem": true,
2128
+ "columnName": "password_scheme",
2129
+ "defaultValue": "bcrypt",
2130
+ "selectionValueType": "string",
2131
+ "selectionStaticValues": [
2132
+ "bcrypt:bcrypt"
2133
+ ]
2134
+ },
2135
+ {
2136
+ "name": "passwordSchemeVersion",
2137
+ "displayName": "Password Scheme Version",
2138
+ "type": "int",
2139
+ "ormType": "integer",
2140
+ "length": 512,
2141
+ "required": true,
2142
+ "defaultValue": 1,
2143
+ "unique": false,
2144
+ "index": false,
2145
+ "private": false,
2146
+ "encrypt": false,
2147
+ "isSystem": true
2148
+ },
2149
+ {
2150
+ "name": "rehashedAt",
2151
+ "displayName": "Password Rehashed At",
2152
+ "type": "datetime",
2153
+ "ormType": "timestamp",
2154
+ "length": 512,
2155
+ "required": false,
2156
+ "unique": false,
2157
+ "index": false,
2158
+ "private": false,
2159
+ "encrypt": false,
2160
+ "isSystem": true
2118
2161
  }
2119
2162
  ]
2120
2163
  },
@@ -112,6 +112,20 @@ export class AuthenticationService {
112
112
  });
113
113
  }
114
114
 
115
+ async updatePasswordDetails(user: User, newPassword: string) {
116
+ user.password = await this.hashingService.hash(newPassword);
117
+ user.passwordScheme = this.hashingService.name();
118
+ user.passwordSchemeVersion = this.hashingService.currentVersion();
119
+ user.rehashedAt = new Date();
120
+ await this.userRepository.update(user.id, {
121
+ password: user.password,
122
+ passwordScheme: user.passwordScheme,
123
+ passwordSchemeVersion: user.passwordSchemeVersion,
124
+ rehashedAt: user.rehashedAt
125
+ });
126
+ return user;
127
+ }
128
+
115
129
  async resolveUserByVerificationToken(token: string) {
116
130
  return await this.userRepository.findOne({
117
131
  where: { verificationTokenOnForgotPassword: token },
@@ -119,7 +133,7 @@ export class AuthenticationService {
119
133
  });
120
134
  }
121
135
 
122
- async validateUser(signInDto: SignInDto) {
136
+ async validateUserAndRehashPasswordIfRequired(signInDto: SignInDto) {
123
137
 
124
138
  const user = await this.resolveUser(signInDto.username, signInDto.email);
125
139
 
@@ -132,11 +146,19 @@ export class AuthenticationService {
132
146
  const isEqual = await this.hashingService.compare(
133
147
  signInDto.password,
134
148
  user.password,
149
+ user.passwordSchemeVersion
135
150
  );
136
151
  if (!isEqual) {
137
152
  throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
138
153
  }
139
154
 
155
+ // If we reach here means that the user has been validated successfully.
156
+ // Now we check if the password needs to be rehashed based on the current hashing scheme and version.
157
+ if (this.hashingService.needsRehash(user.password, user.passwordSchemeVersion)) {
158
+ const rehashedUser = await this.updatePasswordDetails(user, signInDto.password);
159
+ return rehashedUser;
160
+ }
161
+
140
162
  return user;
141
163
  }
142
164
 
@@ -241,6 +263,8 @@ export class AuthenticationService {
241
263
  }
242
264
 
243
265
  user.password = pwd;
266
+ user.passwordScheme = this.hashingService.name(); // e.g. bcrypt
267
+ user.passwordSchemeVersion = this.hashingService.currentVersion(); // e.g. 1, 2, 3 ...
244
268
  user.active = isUserActive;
245
269
  return { user, pwd, autoGeneratedPwd };
246
270
  }
@@ -520,7 +544,7 @@ export class AuthenticationService {
520
544
  }
521
545
 
522
546
  async signIn(signInDto: SignInDto) {
523
- const user = await this.validateUser(signInDto);
547
+ const user = await this.validateUserAndRehashPasswordIfRequired(signInDto);
524
548
 
525
549
  // TODO: Unset the password etc...
526
550
  const tokens = await this.generateTokens(user);
@@ -720,6 +744,7 @@ export class AuthenticationService {
720
744
  const isEqual = await this.hashingService.compare(
721
745
  changePasswordDto.currentPassword,
722
746
  user.password,
747
+ user.passwordSchemeVersion
723
748
  );
724
749
  if (!isEqual) {
725
750
  throw new UnauthorizedException(ERROR_MESSAGES.INCORRECT_CURRENT_PASSWORD);
@@ -728,6 +753,8 @@ export class AuthenticationService {
728
753
  // Update Password
729
754
  const newPwd = await this.hashingService.hash(changePasswordDto.newPassword);
730
755
  user.password = changePasswordDto.newPassword;
756
+ user.passwordScheme = this.hashingService.name(); // e.g. bcrypt
757
+ user.passwordSchemeVersion = this.hashingService.currentVersion(); // e.g. 1, 2, 3 ...
731
758
 
732
759
  // Everytime the user changes the password we reset the forcePasswordChange flag back to false.
733
760
  user.forcePasswordChange = false;
@@ -872,9 +899,11 @@ export class AuthenticationService {
872
899
 
873
900
  // 2) Now update the password & history (still inside the same transaction)
874
901
  const pwdHash = await this.hashingService.hash(confirmForgotPasswordDto.password);
902
+ const pwdScheme = this.hashingService.name(); // e.g. bcrypt
903
+ const pwdSchemeVersion = this.hashingService.currentVersion(); // e.g. 1, 2, 3 ...
875
904
 
876
905
  // Check reuse with your existing method (ensure it looks at hashes).
877
- await m.getRepository(User).update({ id: user.id }, { password: pwdHash });
906
+ await m.getRepository(User).update({ id: user.id }, { password: pwdHash, passwordScheme: pwdScheme, passwordSchemeVersion: pwdSchemeVersion});
878
907
  this.notifyUserOnPasswordChanged(user);
879
908
 
880
909
  return {
@@ -1,15 +1,53 @@
1
- import { Injectable } from '@nestjs/common';
1
+ import { Inject, Injectable } from '@nestjs/common';
2
+ import { ConfigType } from '@nestjs/config';
3
+ import { compare as bcryptCompare, genSalt, hash as bcryptHash } from 'bcrypt';
4
+ import { iamConfig } from 'src/config/iam.config';
2
5
  import { HashingService } from './hashing.service';
3
- import { genSalt, hash, compare } from 'bcrypt';
4
6
 
5
7
  @Injectable()
6
8
  export class BcryptService implements HashingService {
9
+ constructor(
10
+ @Inject(iamConfig.KEY)
11
+ private readonly iamConfiguration: ConfigType<typeof iamConfig>,
12
+ ) { }
13
+
14
+ private readonly rounds = 12;
15
+ private readonly pepper = this.iamConfiguration.passwordPepper ?? '';
16
+
7
17
  async hash(data: string | Buffer): Promise<string> {
8
- const salt = await genSalt();
9
- return hash(data, salt);
18
+ const salt = await genSalt(this.rounds);
19
+ const normalized = this.normalize(data, this.currentVersion());
20
+ return bcryptHash(normalized, salt);
21
+ }
22
+
23
+ async compare(
24
+ data: string | Buffer,
25
+ hashed: string,
26
+ hashVersion: number = this.currentVersion()
27
+ ): Promise<boolean> {
28
+ const normalized = this.normalize(data, hashVersion);
29
+ return bcryptCompare(normalized, hashed);
30
+ }
31
+
32
+ needsRehash(_hashed: string, hashVersion: number = this.currentVersion()): boolean {
33
+ return hashVersion < this.currentVersion();
10
34
  }
11
35
 
12
- compare(data: string | Buffer, encrypted: string): Promise<boolean> {
13
- return compare(data, encrypted);
36
+ /** Current hashing policy version (bump when you change rounds/pepper rules) */
37
+ currentVersion(): number {
38
+ return 2;
14
39
  }
15
- }
40
+
41
+ /** Name of the hashing scheme */
42
+ name(): string {
43
+ return 'bcrypt';
44
+ }
45
+
46
+ /** Normalize input based on version & pepper policy */
47
+ private normalize(data: string | Buffer, version: number): string {
48
+ const plain = typeof data === 'string' ? data : data.toString('utf8');
49
+ const usePepper = version >= 2 && this.pepper.length > 0;
50
+ return usePepper ? plain + this.pepper : plain;
51
+ }
52
+
53
+ }
@@ -38,6 +38,7 @@ import { isArray } from "class-validator";
38
38
  import { ERROR_MESSAGES } from "src/constants/error-messages";
39
39
  import { SUCCESS_MESSAGES } from "src/constants/success-messages";
40
40
  import { RequestContextService } from "./request-context.service";
41
+ import { HashingService } from "./hashing.service";
41
42
 
42
43
  export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
43
44
 
@@ -307,7 +308,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
307
308
  return new DateFieldCrudManager(options);
308
309
  }
309
310
  case SolidFieldType.password: {
310
- const options = { ...commonOptions, min: fieldMetadata.min, max: fieldMetadata.max, regexPattern: fieldMetadata.regexPattern };
311
+ const options = { ...commonOptions, min: fieldMetadata.min, max: fieldMetadata.max, regexPattern: fieldMetadata.regexPattern, hashingService: this.moduleRef.get(HashingService, { strict: false }) };
311
312
  return new PasswordFieldCrudManager(options);
312
313
  }
313
314
  case SolidFieldType.mediaSingle: