@solidstarters/solid-core 1.2.153 → 1.2.155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/iam.config.d.ts +6 -0
- package/dist/config/iam.config.d.ts.map +1 -1
- package/dist/config/iam.config.js +3 -0
- package/dist/config/iam.config.js.map +1 -1
- package/dist/constants/error-messages.js +1 -1
- package/dist/constants/error-messages.js.map +1 -1
- package/dist/controllers/ai-interaction.controller.d.ts +2 -1
- package/dist/controllers/ai-interaction.controller.d.ts.map +1 -1
- package/dist/controllers/ai-interaction.controller.js +5 -3
- package/dist/controllers/ai-interaction.controller.js.map +1 -1
- package/dist/controllers/authentication.controller.d.ts +6 -6
- package/dist/controllers/authentication.controller.d.ts.map +1 -1
- package/dist/controllers/authentication.controller.js +15 -7
- package/dist/controllers/authentication.controller.js.map +1 -1
- package/dist/controllers/email-template.controller.d.ts.map +1 -1
- package/dist/controllers/email-template.controller.js +3 -0
- package/dist/controllers/email-template.controller.js.map +1 -1
- package/dist/controllers/google-authentication.controller.d.ts.map +1 -1
- package/dist/controllers/google-authentication.controller.js +3 -0
- package/dist/controllers/google-authentication.controller.js.map +1 -1
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +4 -0
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/controllers/model-metadata.controller.d.ts.map +1 -1
- package/dist/controllers/model-metadata.controller.js +5 -0
- package/dist/controllers/model-metadata.controller.js.map +1 -1
- package/dist/controllers/otp-authentication.controller.d.ts.map +1 -1
- package/dist/controllers/otp-authentication.controller.js +3 -0
- package/dist/controllers/otp-authentication.controller.js.map +1 -1
- package/dist/controllers/service.controller.d.ts.map +1 -1
- package/dist/controllers/service.controller.js +4 -0
- package/dist/controllers/service.controller.js.map +1 -1
- package/dist/controllers/sms-template.controller.d.ts +1 -1
- package/dist/controllers/sms-template.controller.d.ts.map +1 -1
- package/dist/controllers/sms-template.controller.js +6 -3
- package/dist/controllers/sms-template.controller.js.map +1 -1
- package/dist/decorators/solid-password.decorator.d.ts +12 -0
- package/dist/decorators/solid-password.decorator.d.ts.map +1 -0
- package/dist/decorators/solid-password.decorator.js +43 -0
- package/dist/decorators/solid-password.decorator.js.map +1 -0
- package/dist/dtos/change-password.dto.d.ts.map +1 -1
- package/dist/dtos/change-password.dto.js +2 -0
- package/dist/dtos/change-password.dto.js.map +1 -1
- package/dist/dtos/confirm-forgot-password.dto.d.ts.map +1 -1
- package/dist/dtos/confirm-forgot-password.dto.js +2 -0
- package/dist/dtos/confirm-forgot-password.dto.js.map +1 -1
- package/dist/helpers/cors.helper.d.ts +4 -0
- package/dist/helpers/cors.helper.d.ts.map +1 -0
- package/dist/helpers/cors.helper.js +33 -0
- package/dist/helpers/cors.helper.js.map +1 -0
- package/dist/helpers/security.helper.d.ts +9 -0
- package/dist/helpers/security.helper.d.ts.map +1 -0
- package/dist/helpers/security.helper.js +46 -0
- package/dist/helpers/security.helper.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/repository/chatter-message-details.repository.d.ts +16 -0
- package/dist/repository/chatter-message-details.repository.d.ts.map +1 -0
- package/dist/repository/chatter-message-details.repository.js +62 -0
- package/dist/repository/chatter-message-details.repository.js.map +1 -0
- package/dist/repository/chatter-message.repository.d.ts +16 -0
- package/dist/repository/chatter-message.repository.d.ts.map +1 -0
- package/dist/repository/chatter-message.repository.js +61 -0
- package/dist/repository/chatter-message.repository.js.map +1 -0
- package/dist/repository/security-rule.repository.d.ts +1 -1
- package/dist/repository/security-rule.repository.d.ts.map +1 -1
- package/dist/repository/security-rule.repository.js +2 -2
- package/dist/repository/security-rule.repository.js.map +1 -1
- package/dist/repository/solid-base.repository.d.ts +6 -1
- package/dist/repository/solid-base.repository.d.ts.map +1 -1
- package/dist/repository/solid-base.repository.js +35 -0
- package/dist/repository/solid-base.repository.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +9 -0
- package/dist/services/ai-interaction.service.d.ts +2 -4
- package/dist/services/ai-interaction.service.d.ts.map +1 -1
- package/dist/services/ai-interaction.service.js +4 -8
- package/dist/services/ai-interaction.service.js.map +1 -1
- package/dist/services/authentication.service.d.ts +6 -2
- package/dist/services/authentication.service.d.ts.map +1 -1
- package/dist/services/authentication.service.js +94 -43
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message-details.service.d.ts +4 -3
- package/dist/services/chatter-message-details.service.d.ts.map +1 -1
- package/dist/services/chatter-message-details.service.js +2 -3
- package/dist/services/chatter-message-details.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts +3 -2
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +2 -2
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js +4 -1
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +4 -1
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/request-context.service.d.ts +3 -0
- package/dist/services/request-context.service.d.ts.map +1 -1
- package/dist/services/request-context.service.js +6 -0
- package/dist/services/request-context.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +23 -5
- package/dist/solid-core.module.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/src/config/iam.config.ts +3 -0
- package/src/constants/error-messages.ts +1 -1
- package/src/controllers/ai-interaction.controller.ts +4 -2
- package/src/controllers/authentication.controller.ts +16 -10
- package/src/controllers/email-template.controller.ts +4 -1
- package/src/controllers/google-authentication.controller.ts +4 -0
- package/src/controllers/media.controller.ts +5 -1
- package/src/controllers/model-metadata.controller.ts +7 -2
- package/src/controllers/otp-authentication.controller.ts +4 -1
- package/src/controllers/service.controller.ts +5 -1
- package/src/controllers/sms-template.controller.ts +8 -7
- package/src/decorators/solid-password.decorator.ts +51 -0
- package/src/dtos/change-password.dto.ts +2 -0
- package/src/dtos/confirm-forgot-password.dto.ts +2 -0
- package/src/helpers/cors.helper.ts +34 -0
- package/src/helpers/security.helper.ts +53 -0
- package/src/index.ts +3 -0
- package/src/repository/chatter-message-details.repository.ts +109 -0
- package/src/repository/chatter-message.repository.ts +68 -0
- package/src/repository/security-rule.repository.ts +2 -2
- package/src/repository/solid-base.repository.ts +66 -0
- package/src/seeders/seed-data/email-templates/password-changed.handlebars.html +158 -0
- package/src/seeders/seed-data/solid-core-metadata.json +9 -0
- package/src/services/ai-interaction.service.ts +5 -5
- package/src/services/authentication.service.ts +181 -56
- package/src/services/chatter-message-details.service.ts +3 -2
- package/src/services/chatter-message.service.ts +3 -2
- package/src/services/crud.service.ts +7 -2
- package/src/services/model-metadata.service.ts +15 -1
- package/src/services/request-context.service.ts +9 -0
- package/src/solid-core.module.ts +29 -5
|
@@ -12,12 +12,12 @@ import {
|
|
|
12
12
|
import { ConfigType } from '@nestjs/config';
|
|
13
13
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
14
14
|
import { JwtService } from '@nestjs/jwt';
|
|
15
|
-
import { InjectRepository } from '@nestjs/typeorm';
|
|
15
|
+
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
|
16
16
|
import { isEmpty, isNotEmpty } from 'class-validator';
|
|
17
17
|
import { randomInt, randomUUID } from 'crypto';
|
|
18
18
|
import { SMTPEMailService } from 'src/services/mail/smtp-email.service';
|
|
19
19
|
import { Msg91OTPService } from 'src/services/sms/Msg91OTPService';
|
|
20
|
-
import { Repository } from 'typeorm';
|
|
20
|
+
import { DataSource, Repository } from 'typeorm';
|
|
21
21
|
import { iamConfig, jwtConfig } from '../config/iam.config';
|
|
22
22
|
import { ChangePasswordDto } from "../dtos/change-password.dto";
|
|
23
23
|
import { ConfirmForgotPasswordDto } from '../dtos/confirm-forgot-password.dto';
|
|
@@ -49,7 +49,7 @@ import { RequestContextService } from './request-context.service';
|
|
|
49
49
|
import { ERROR_MESSAGES } from 'src/constants/error-messages';
|
|
50
50
|
import { SUCCESS_MESSAGES } from 'src/constants/success-messages';
|
|
51
51
|
import { MailFactory } from 'src/factories/mail.factory';
|
|
52
|
-
|
|
52
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
53
53
|
|
|
54
54
|
enum LoginProvider {
|
|
55
55
|
LOCAL = 'local',
|
|
@@ -88,9 +88,11 @@ export class AuthenticationService {
|
|
|
88
88
|
private readonly commonConfiguration: ConfigType<typeof commonConfig>,
|
|
89
89
|
private readonly userActivityHistoryService: UserActivityHistoryService,
|
|
90
90
|
private readonly requestContextService: RequestContextService,
|
|
91
|
+
@InjectDataSource()
|
|
92
|
+
private readonly dataSource: DataSource,
|
|
91
93
|
) {
|
|
92
94
|
// this.mailService = this.mailServiceFactory.getMailService();
|
|
93
|
-
|
|
95
|
+
}
|
|
94
96
|
|
|
95
97
|
private async getConfig(key: string): Promise<any> {
|
|
96
98
|
return this.settingService.getConfigValue(key);
|
|
@@ -112,6 +114,13 @@ export class AuthenticationService {
|
|
|
112
114
|
});
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
async resolveUserByVerificationToken(token: string) {
|
|
118
|
+
return await this.userRepository.findOne({
|
|
119
|
+
where: { verificationTokenOnForgotPassword: token },
|
|
120
|
+
relations: { roles: true }
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
115
124
|
async validateUser(signInDto: SignInDto) {
|
|
116
125
|
|
|
117
126
|
const user = await this.resolveUser(signInDto.username, signInDto.email);
|
|
@@ -729,6 +738,19 @@ export class AuthenticationService {
|
|
|
729
738
|
return true;
|
|
730
739
|
}
|
|
731
740
|
|
|
741
|
+
// generate uuid token for forgot password
|
|
742
|
+
private generateForgotPasswordToken() {
|
|
743
|
+
const expiryTime = new Date();
|
|
744
|
+
expiryTime.setMinutes(expiryTime.getMinutes() + this.iamConfiguration.forgotPasswordVerificationTokenExpiry);
|
|
745
|
+
|
|
746
|
+
return {
|
|
747
|
+
token: this.iamConfiguration.dummyOtp
|
|
748
|
+
? this.iamConfiguration.dummyOtp
|
|
749
|
+
: uuidv4(), // UUID instead of numeric OTP
|
|
750
|
+
expiresAt: expiryTime,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
732
754
|
async initiateForgotPassword(initiateForgotPasswordDto: InitiateForgotPasswordDto) {
|
|
733
755
|
// Steps / Algorithm:
|
|
734
756
|
// 1. Identify the user using the specified "username", if not found exit.
|
|
@@ -738,20 +760,20 @@ export class AuthenticationService {
|
|
|
738
760
|
const user = await this.resolveUser(initiateForgotPasswordDto.username, initiateForgotPasswordDto.email);
|
|
739
761
|
|
|
740
762
|
if (!user) {
|
|
741
|
-
throw new NotFoundException(ERROR_MESSAGES.
|
|
763
|
+
throw new NotFoundException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
742
764
|
}
|
|
743
765
|
if (!user.active) {
|
|
744
|
-
throw new UnauthorizedException(ERROR_MESSAGES.
|
|
766
|
+
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
745
767
|
}
|
|
746
768
|
|
|
747
769
|
// 2. Validate if user has used a provider which is "local", only then it makes sense for us to initiate the forgot password routine.
|
|
748
770
|
if (user.lastLoginProvider !== 'local') {
|
|
749
|
-
throw new BadRequestException(ERROR_MESSAGES.
|
|
771
|
+
throw new BadRequestException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
750
772
|
}
|
|
751
773
|
|
|
752
774
|
// 3. Generate a 6 digit validation token, we send this token to the user over their email & mobile number (controlled using configuration).
|
|
753
775
|
// 4. Save this validation token in new fields on the user record.
|
|
754
|
-
const { token, expiresAt } = this.
|
|
776
|
+
const { token, expiresAt } = this.generateForgotPasswordToken();
|
|
755
777
|
user.verificationTokenOnForgotPassword = token;
|
|
756
778
|
user.verificationTokenOnForgotPasswordExpiresAt = expiresAt;
|
|
757
779
|
await this.userRepository.save(user);
|
|
@@ -789,7 +811,7 @@ export class AuthenticationService {
|
|
|
789
811
|
firstName: user.username,
|
|
790
812
|
fullName: user.fullName,
|
|
791
813
|
// TODO: Need to prefix this with the page url where the forgot password page will open up.
|
|
792
|
-
passwordResetLink: `${process.env.IAM_FRONTEND_APP_FORGOT_PASSWORD_PAGE_URL}?token=${user.verificationTokenOnForgotPassword}
|
|
814
|
+
passwordResetLink: `${process.env.IAM_FRONTEND_APP_FORGOT_PASSWORD_PAGE_URL}?token=${user.verificationTokenOnForgotPassword}`,
|
|
793
815
|
companyLogoUrl: companyLogo
|
|
794
816
|
},
|
|
795
817
|
this.commonConfiguration.shouldQueueEmails,
|
|
@@ -816,63 +838,166 @@ export class AuthenticationService {
|
|
|
816
838
|
}
|
|
817
839
|
|
|
818
840
|
async confirmForgotPassword(confirmForgotPasswordDto: ConfirmForgotPasswordDto) {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
841
|
+
return this.dataSource.transaction(async (m) => {
|
|
842
|
+
// Resolve the user id first (by username/email), but DON'T check the token in JS.
|
|
843
|
+
const user = await this.resolveUserByVerificationToken(confirmForgotPasswordDto.verificationToken);
|
|
844
|
+
if (!user) throw new NotFoundException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
845
|
+
if (user.lastLoginProvider !== 'local') throw new BadRequestException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
846
|
+
if (!user.active) throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
847
|
+
|
|
848
|
+
// 1) Atomically consume the token (only one request can succeed)
|
|
849
|
+
const { affected } = await m
|
|
850
|
+
.createQueryBuilder()
|
|
851
|
+
.update(User)
|
|
852
|
+
.set({
|
|
853
|
+
forgotPasswordConfirmedAt: () => 'NOW()',
|
|
854
|
+
verificationTokenOnForgotPassword: () => 'NULL',
|
|
855
|
+
verificationTokenOnForgotPasswordExpiresAt: () => 'NULL',
|
|
856
|
+
})
|
|
857
|
+
.where('id = :id', { id: user.id })
|
|
858
|
+
.andWhere('verificationTokenOnForgotPassword = :token', { token: confirmForgotPasswordDto.verificationToken })
|
|
859
|
+
.andWhere('verificationTokenOnForgotPasswordExpiresAt > NOW()')
|
|
860
|
+
.execute();
|
|
861
|
+
|
|
862
|
+
if (affected !== 1) {
|
|
863
|
+
// Token invalid/expired/already used (or a parallel call already consumed it)
|
|
864
|
+
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
|
|
865
|
+
}
|
|
825
866
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
}
|
|
867
|
+
// 2) Now update the password & history (still inside the same transaction)
|
|
868
|
+
const pwdHash = await this.hashingService.hash(confirmForgotPasswordDto.password);
|
|
829
869
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
throw new BadRequestException(ERROR_MESSAGES.NON_LOCAL_PROVIDER);
|
|
833
|
-
}
|
|
834
|
-
if (!user.active) {
|
|
835
|
-
throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
|
|
836
|
-
}
|
|
870
|
+
// Avoid ever assigning plaintext:
|
|
871
|
+
// user.password = dto.password <-- remove this line in your original code
|
|
837
872
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
throw new UnauthorizedException(ERROR_MESSAGES.INVALID_VERIFICATION_TOKEN);
|
|
844
|
-
}
|
|
845
|
-
user.forgotPasswordConfirmedAt = new Date();
|
|
846
|
-
user.verificationTokenOnForgotPassword = null;
|
|
847
|
-
user.verificationTokenOnForgotPasswordExpiresAt = null;
|
|
873
|
+
// Check reuse with your existing method (ensure it looks at hashes).
|
|
874
|
+
const tempUser = { ...user, password: pwdHash } as User; // if your helper expects it
|
|
875
|
+
if (await this.isPasswordDuplicate(tempUser)) {
|
|
876
|
+
throw new BadRequestException(ERROR_MESSAGES.PASSWORD_REUSED);
|
|
877
|
+
}
|
|
848
878
|
|
|
849
|
-
|
|
850
|
-
const pwd = await this.hashingService.hash(confirmForgotPasswordDto.password);
|
|
851
|
-
user.password = confirmForgotPasswordDto.password
|
|
879
|
+
await this.deleteOldPasswords(user);
|
|
852
880
|
|
|
853
|
-
|
|
854
|
-
throw new BadRequestException(ERROR_MESSAGES.PASSWORD_REUSED);
|
|
855
|
-
}
|
|
856
|
-
await this.deleteOldPasswords(user);
|
|
881
|
+
await m.getRepository(User).update({ id: user.id }, { password: pwdHash });
|
|
857
882
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
883
|
+
const history = m.getRepository(UserPasswordHistory).create({
|
|
884
|
+
user: { id: user.id } as any,
|
|
885
|
+
passwordHash: pwdHash,
|
|
886
|
+
});
|
|
887
|
+
await m.getRepository(UserPasswordHistory).save(history);
|
|
888
|
+
this.notifyUserOnPasswordChanged(user);
|
|
862
889
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
890
|
+
return {
|
|
891
|
+
status: 'success',
|
|
892
|
+
message: SUCCESS_MESSAGES.FORGOT_PASSWORD_CONFIRMED,
|
|
893
|
+
error: '',
|
|
894
|
+
errorCode: '',
|
|
895
|
+
data: {},
|
|
896
|
+
};
|
|
897
|
+
});
|
|
898
|
+
}
|
|
866
899
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
900
|
+
private async notifyUserOnPasswordChanged(user: User) {
|
|
901
|
+
const companyLogo = await this.getCompanyLogo();
|
|
902
|
+
|
|
903
|
+
const forgotPasswordSendVerificationTokenOn = this.iamConfiguration.forgotPasswordSendVerificationTokenOn;
|
|
904
|
+
|
|
905
|
+
if (forgotPasswordSendVerificationTokenOn == ForgotPasswordSendVerificationTokenOn.EMAIL) {
|
|
906
|
+
const mailService = this.mailServiceFactory.getMailService();
|
|
907
|
+
mailService.sendEmailUsingTemplate(
|
|
908
|
+
user.email,
|
|
909
|
+
'password-changed',
|
|
910
|
+
{
|
|
911
|
+
solidAppName: process.env.SOLID_APP_NAME,
|
|
912
|
+
solidAppWebsiteUrl: process.env.SOLID_APP_WEBSITE_URL,
|
|
913
|
+
email: user.email,
|
|
914
|
+
firstName: user.username,
|
|
915
|
+
fullName: user.fullName,
|
|
916
|
+
// TODO: Need to prefix this with the page url where the forgot password page will open up.
|
|
917
|
+
passwordResetLink: `${process.env.IAM_FRONTEND_APP_FORGOT_PASSWORD_PAGE_URL}?token=${user.verificationTokenOnForgotPassword}`,
|
|
918
|
+
companyLogoUrl: companyLogo
|
|
919
|
+
},
|
|
920
|
+
this.commonConfiguration.shouldQueueEmails,
|
|
921
|
+
null,
|
|
922
|
+
null,
|
|
923
|
+
'user',
|
|
924
|
+
user.id
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
// Assuming all users do not have mobile as mandatory.
|
|
928
|
+
if (forgotPasswordSendVerificationTokenOn == ForgotPasswordSendVerificationTokenOn.MOBILE && user.mobile) {
|
|
929
|
+
this.smsService.sendSMSUsingTemplate(
|
|
930
|
+
user.mobile,
|
|
931
|
+
'forgot-password',
|
|
932
|
+
{
|
|
933
|
+
solidAppName: process.env.SOLID_APP_NAME,
|
|
934
|
+
otp: user.verificationTokenOnForgotPassword,
|
|
935
|
+
verificationTokenOnForgotPassword: user.verificationTokenOnForgotPassword,
|
|
936
|
+
firstName: user.username,
|
|
937
|
+
companyLogoUrl: companyLogo
|
|
938
|
+
}
|
|
939
|
+
);
|
|
873
940
|
}
|
|
874
941
|
}
|
|
875
942
|
|
|
943
|
+
// async confirmForgotPassword(confirmForgotPasswordDto: ConfirmForgotPasswordDto) {
|
|
944
|
+
// // Steps / Algorithm:
|
|
945
|
+
// // 1. Identify the user using the specified "username", if not found exit.
|
|
946
|
+
// // const user = await this.userRepository.findOne({
|
|
947
|
+
// // where: { username: confirmForgotPasswordDto.username, }
|
|
948
|
+
// // });
|
|
949
|
+
// const user = await this.resolveUserByVerificationToken(confirmForgotPasswordDto.verificationToken);
|
|
950
|
+
|
|
951
|
+
// if (!user) {
|
|
952
|
+
// throw new NotFoundException(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
953
|
+
// }
|
|
954
|
+
|
|
955
|
+
// // 2. Validate if user has used a provider which is "local", only then it makes sense for us to initiate the forgot password routine.
|
|
956
|
+
// if (user.lastLoginProvider !== 'local') {
|
|
957
|
+
// throw new BadRequestException(ERROR_MESSAGES.NON_LOCAL_PROVIDER);
|
|
958
|
+
// }
|
|
959
|
+
// if (!user.active) {
|
|
960
|
+
// throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
|
|
961
|
+
// }
|
|
962
|
+
|
|
963
|
+
// // 3. Validate the verification token is proper & update the user record.
|
|
964
|
+
// if (user.verificationTokenOnForgotPassword !== confirmForgotPasswordDto.verificationToken) {
|
|
965
|
+
// throw new UnauthorizedException(ERROR_MESSAGES.INVALID_VERIFICATION_TOKEN);
|
|
966
|
+
// }
|
|
967
|
+
// if (user.verificationTokenOnForgotPasswordExpiresAt < new Date()) {
|
|
968
|
+
// throw new UnauthorizedException(ERROR_MESSAGES.INVALID_VERIFICATION_TOKEN);
|
|
969
|
+
// }
|
|
970
|
+
// user.forgotPasswordConfirmedAt = new Date();
|
|
971
|
+
// user.verificationTokenOnForgotPassword = null;
|
|
972
|
+
// user.verificationTokenOnForgotPasswordExpiresAt = null;
|
|
973
|
+
|
|
974
|
+
// // 4. Update the users password while encrypting it.
|
|
975
|
+
// const pwd = await this.hashingService.hash(confirmForgotPasswordDto.password);
|
|
976
|
+
// user.password = confirmForgotPasswordDto.password
|
|
977
|
+
|
|
978
|
+
// if (await this.isPasswordDuplicate(user)) {
|
|
979
|
+
// throw new BadRequestException(ERROR_MESSAGES.PASSWORD_REUSED);
|
|
980
|
+
// }
|
|
981
|
+
// await this.deleteOldPasswords(user);
|
|
982
|
+
|
|
983
|
+
// user.password = pwd;
|
|
984
|
+
// const userPasswordHistory = new UserPasswordHistory();
|
|
985
|
+
// userPasswordHistory.passwordHash = pwd;
|
|
986
|
+
// userPasswordHistory.user = user;
|
|
987
|
+
|
|
988
|
+
// await this.userRepository.save(user);
|
|
989
|
+
// //FIXME: Do this check conditionally, basis a configuration parameter i.e if IAM_ALLOW_PREVIOUS_PASSWORDS=false, default true
|
|
990
|
+
// await this.userPasswordHistoryRepository.save(userPasswordHistory);
|
|
991
|
+
|
|
992
|
+
// return {
|
|
993
|
+
// status: 'success',
|
|
994
|
+
// message: SUCCESS_MESSAGES.FORGOT_PASSWORD_CONFIRMED,
|
|
995
|
+
// error: '',
|
|
996
|
+
// errorCode: '',
|
|
997
|
+
// data: {}
|
|
998
|
+
// }
|
|
999
|
+
// }
|
|
1000
|
+
|
|
876
1001
|
//FIXME: Do this check conditionally, basis a configuration parameter i.e if IAM_ALLOW_PREVIOUS_PASSWORDS=true, return immediately without processing, i.e false.
|
|
877
1002
|
private async isPasswordDuplicate(user: User) {
|
|
878
1003
|
const userPwdHistoryEntityArray = await this.userPasswordHistoryRepository.findBy(
|
|
@@ -1088,7 +1213,7 @@ export class AuthenticationService {
|
|
|
1088
1213
|
await this.userActivityHistoryService.logEvent('logout', user);
|
|
1089
1214
|
|
|
1090
1215
|
|
|
1091
|
-
return { message: SUCCESS_MESSAGES.LOGOUT_SUCCESS};
|
|
1216
|
+
return { message: SUCCESS_MESSAGES.LOGOUT_SUCCESS };
|
|
1092
1217
|
} catch (err) {
|
|
1093
1218
|
throw err instanceof UnauthorizedException || err instanceof InternalServerErrorException
|
|
1094
1219
|
? err
|
|
@@ -11,6 +11,7 @@ import { FileService } from 'src/services/file.service';
|
|
|
11
11
|
import { CrudHelperService } from 'src/services/crud-helper.service';
|
|
12
12
|
|
|
13
13
|
import { ChatterMessageDetails } from '../entities/chatter-message-details.entity';
|
|
14
|
+
import { ChatterMessageDetailsRepository } from 'src/repository/chatter-message-details.repository';
|
|
14
15
|
|
|
15
16
|
@Injectable()
|
|
16
17
|
export class ChatterMessageDetailsService extends CRUDService<ChatterMessageDetails>{
|
|
@@ -23,8 +24,8 @@ export class ChatterMessageDetailsService extends CRUDService<ChatterMessageDeta
|
|
|
23
24
|
readonly crudHelperService: CrudHelperService,
|
|
24
25
|
@InjectEntityManager()
|
|
25
26
|
readonly entityManager: EntityManager,
|
|
26
|
-
@InjectRepository(ChatterMessageDetails, 'default')
|
|
27
|
-
readonly repo:
|
|
27
|
+
// @InjectRepository(ChatterMessageDetails, 'default')
|
|
28
|
+
readonly repo: ChatterMessageDetailsRepository,
|
|
28
29
|
readonly moduleRef: ModuleRef,
|
|
29
30
|
) {
|
|
30
31
|
super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'chatterMessageDetails', 'solid-core', moduleRef);
|
|
@@ -17,6 +17,7 @@ import { MediaStorageProviderType } from '../dtos/create-media-storage-provider-
|
|
|
17
17
|
import { ChatterMessageDetails } from '../entities/chatter-message-details.entity';
|
|
18
18
|
import { ModelMetadata } from 'src/entities/model-metadata.entity';
|
|
19
19
|
import { RequestContextService } from './request-context.service';
|
|
20
|
+
import { ChatterMessageRepository } from 'src/repository/chatter-message.repository';
|
|
20
21
|
@Injectable()
|
|
21
22
|
export class ChatterMessageService extends CRUDService<ChatterMessage>{
|
|
22
23
|
constructor(
|
|
@@ -28,8 +29,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage>{
|
|
|
28
29
|
readonly crudHelperService: CrudHelperService,
|
|
29
30
|
@InjectEntityManager()
|
|
30
31
|
readonly entityManager: EntityManager,
|
|
31
|
-
@InjectRepository(ChatterMessage, 'default')
|
|
32
|
-
readonly repo:
|
|
32
|
+
// @InjectRepository(ChatterMessage, 'default')
|
|
33
|
+
readonly repo: ChatterMessageRepository,
|
|
33
34
|
@InjectRepository(ChatterMessageDetails, 'default')
|
|
34
35
|
readonly chatterMessageDetailsRepo: Repository<ChatterMessageDetails>,
|
|
35
36
|
readonly moduleRef: ModuleRef,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestException, Inject, Optional } from "@nestjs/common";
|
|
1
|
+
import { BadRequestException, Inject, NotFoundException, Optional } from "@nestjs/common";
|
|
2
2
|
import { ConfigService, ConfigType } from "@nestjs/config";
|
|
3
3
|
import { DiscoveryService, ModuleRef } from "@nestjs/core";
|
|
4
4
|
import { EntityManager, In, IsNull, Not, QueryFailedError, SelectQueryBuilder } from "typeorm";
|
|
@@ -37,6 +37,7 @@ import { ModuleMetadataService } from "./module-metadata.service";
|
|
|
37
37
|
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
|
+
import { RequestContextService } from "./request-context.service";
|
|
40
41
|
|
|
41
42
|
export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDto, so we get the proper types in our service
|
|
42
43
|
|
|
@@ -437,6 +438,10 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
437
438
|
}
|
|
438
439
|
}
|
|
439
440
|
|
|
441
|
+
// Set the request filter in the request context service
|
|
442
|
+
const requestContextService = this.moduleRef.get(RequestContextService, { strict: false });
|
|
443
|
+
requestContextService.setRequestFilter(basicFilterDto);
|
|
444
|
+
|
|
440
445
|
// Create above query on pincode table using query builder
|
|
441
446
|
var qb: SelectQueryBuilder<T> = this.repo.createQueryBuilder(alias)
|
|
442
447
|
qb = this.crudHelperService.buildFilterQuery(qb, basicFilterDto, alias);
|
|
@@ -639,7 +644,7 @@ export class CRUDService<T> { // Add two generic value i.e Person,CreatePersonDt
|
|
|
639
644
|
select: fields,
|
|
640
645
|
});
|
|
641
646
|
if (!entity) {
|
|
642
|
-
throw new
|
|
647
|
+
throw new NotFoundException(`Entity [${this.moduleName}.${this.modelName}] with id ${id} not found`);
|
|
643
648
|
}
|
|
644
649
|
// Populate the entity with the media
|
|
645
650
|
if (normalizedPopulateMedia.length > 0) {
|
|
@@ -660,9 +660,23 @@ export class ModelMetadataService {
|
|
|
660
660
|
const existingModelIndex = metaData.moduleMetadata.models.findIndex(
|
|
661
661
|
(existingModel: any) => existingModel.singularName === modelEntity.singularName
|
|
662
662
|
);
|
|
663
|
+
|
|
664
|
+
// Remove the model to be deleted from the metadata
|
|
663
665
|
if (existingModelIndex !== -1) {
|
|
664
666
|
metaData.moduleMetadata.models.splice(existingModelIndex, 1);
|
|
665
667
|
}
|
|
668
|
+
|
|
669
|
+
// Remove references to this model in the menu, action & view sections.
|
|
670
|
+
metaData.moduleMetadata.menus = metaData.moduleMetadata.menus.filter(
|
|
671
|
+
(menu: any) => menu.modelUserKey !== modelEntity.singularName
|
|
672
|
+
);
|
|
673
|
+
metaData.moduleMetadata.actions = metaData.moduleMetadata.actions.filter(
|
|
674
|
+
(action: any) => action.modelUserKey !== modelEntity.singularName
|
|
675
|
+
);
|
|
676
|
+
metaData.moduleMetadata.views = metaData.moduleMetadata.views.filter(
|
|
677
|
+
(view: any) => view.modelUserKey !== modelEntity.singularName
|
|
678
|
+
);
|
|
679
|
+
|
|
666
680
|
const updatedContent = JSON.stringify(metaData, null, 2);
|
|
667
681
|
await fs.writeFile(filePath, updatedContent);
|
|
668
682
|
|
|
@@ -1034,7 +1048,7 @@ export class ModelMetadataService {
|
|
|
1034
1048
|
type: "solid",
|
|
1035
1049
|
domain: "" as any,
|
|
1036
1050
|
context: "" as any,
|
|
1037
|
-
customComponent:
|
|
1051
|
+
customComponent: "",
|
|
1038
1052
|
customIsModal: true,
|
|
1039
1053
|
serverEndpoint: "",
|
|
1040
1054
|
view: view,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Injectable } from "@nestjs/common";
|
|
2
2
|
import { ClsService } from "nestjs-cls";
|
|
3
3
|
import { REQUEST_USER_KEY } from "src/constants";
|
|
4
|
+
import { BasicFilterDto } from "src/dtos/basic-filters.dto";
|
|
4
5
|
|
|
5
6
|
@Injectable()
|
|
6
7
|
export class RequestContextService {
|
|
@@ -20,4 +21,12 @@ export class RequestContextService {
|
|
|
20
21
|
return this.cls.get('userAgent');
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
setRequestFilter(filter: BasicFilterDto) {
|
|
25
|
+
this.cls.set('filter', filter);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getRequestFilter(): BasicFilterDto | undefined {
|
|
29
|
+
return this.cls.get('filter');
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
}
|
package/src/solid-core.module.ts
CHANGED
|
@@ -270,6 +270,9 @@ import { MailFactory } from './factories/mail.factory';
|
|
|
270
270
|
import { TwilioSMSService } from './services/sms/TwilioSMSService';
|
|
271
271
|
import { PollerService } from './services/poller.service';
|
|
272
272
|
import { TextractService } from './services/textract.service';
|
|
273
|
+
import { seconds, ThrottlerModule } from '@nestjs/throttler';
|
|
274
|
+
import { ChatterMessageRepository } from './repository/chatter-message.repository';
|
|
275
|
+
import { ChatterMessageDetailsRepository } from './repository/chatter-message-details.repository';
|
|
273
276
|
|
|
274
277
|
|
|
275
278
|
@Global()
|
|
@@ -309,6 +312,10 @@ import { TextractService } from './services/textract.service';
|
|
|
309
312
|
ImportTransactionErrorLog,
|
|
310
313
|
UserActivityHistory,
|
|
311
314
|
AiInteraction,
|
|
315
|
+
Dashboard,
|
|
316
|
+
DashboardVariable,
|
|
317
|
+
DashboardQuestion,
|
|
318
|
+
DashboardQuestionSqlDatasetConfig,
|
|
312
319
|
]),
|
|
313
320
|
ConfigModule.forFeature(appBuilderConfig),
|
|
314
321
|
ConfigModule.forFeature(commonConfig),
|
|
@@ -319,6 +326,17 @@ import { TextractService } from './services/textract.service';
|
|
|
319
326
|
ServeStaticModule.forRoot({
|
|
320
327
|
rootPath: join(process.cwd(), 'media-files-storage'),
|
|
321
328
|
serveRoot: '/media-files-storage',
|
|
329
|
+
serveStaticOptions: {
|
|
330
|
+
setHeaders: (res /*, path, stat*/) => {
|
|
331
|
+
// Allow use of these files from a different origin (e.g., :3000 UI)
|
|
332
|
+
// Use 'same-site' if both origins are on the same site (localhost:* counts as same-site)
|
|
333
|
+
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin'); // or 'same-site'
|
|
334
|
+
|
|
335
|
+
// If you need to load into <canvas> without tainting or fetch images via XHR,
|
|
336
|
+
// you can also expose CORS here (not needed for simple <img>):
|
|
337
|
+
// res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
|
|
338
|
+
},
|
|
339
|
+
}
|
|
322
340
|
}),
|
|
323
341
|
MulterModule.registerAsync({
|
|
324
342
|
imports: [ConfigModule],
|
|
@@ -330,11 +348,14 @@ import { TextractService } from './services/textract.service';
|
|
|
330
348
|
HttpModule,
|
|
331
349
|
ConfigModule,
|
|
332
350
|
ClsModule,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
351
|
+
ThrottlerModule.forRoot({
|
|
352
|
+
throttlers: [
|
|
353
|
+
{ name: 'short', ttl: seconds(10), limit: 10 },
|
|
354
|
+
{ name: 'login', ttl: seconds(10), limit: 5 },
|
|
355
|
+
{ name: 'burst', ttl: seconds(1), limit: 100 },
|
|
356
|
+
{ name: 'sustained', ttl: seconds(300), limit: 500 },
|
|
357
|
+
],
|
|
358
|
+
}),
|
|
338
359
|
],
|
|
339
360
|
controllers: [
|
|
340
361
|
ModuleMetadataController,
|
|
@@ -567,6 +588,8 @@ import { TextractService } from './services/textract.service';
|
|
|
567
588
|
ScheduledJobSubscriber,
|
|
568
589
|
AlphaNumExternalIdComputationProvider,
|
|
569
590
|
MailFactory,
|
|
591
|
+
ChatterMessageRepository,
|
|
592
|
+
ChatterMessageDetailsRepository,
|
|
570
593
|
],
|
|
571
594
|
exports: [
|
|
572
595
|
ModuleMetadataService,
|
|
@@ -613,6 +636,7 @@ import { TextractService } from './services/textract.service';
|
|
|
613
636
|
MailFactory,
|
|
614
637
|
PollerService,
|
|
615
638
|
AiInteractionService,
|
|
639
|
+
ThrottlerModule,
|
|
616
640
|
],
|
|
617
641
|
})
|
|
618
642
|
export class SolidCoreModule { }
|