Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit56f3966

Browse files
authored
Merge pull requestlowcoder-org#715 from irfan-ikhwa/lost-password-feature
Lost Password Feature
2 parents617365e +30dcd9c commit56f3966

File tree

15 files changed

+194
-2
lines changed

15 files changed

+194
-2
lines changed

‎server/api-service/lowcoder-domain/pom.xml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
<groupId>org.lowcoder</groupId>
5151
<artifactId>lowcoder-infra</artifactId>
5252
</dependency>
53+
<dependency>
54+
<groupId>org.springframework.boot</groupId>
55+
<artifactId>spring-boot-starter-mail</artifactId>
56+
</dependency>
5357

5458
<dependency>
5559
<groupId>com.github.cloudyrock.mongock</groupId>

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public OrganizationCommonSettings getCommonSettings() {
8686
publicstaticclassOrganizationCommonSettingsextendsHashMap<String,Object> {
8787
publicstaticfinalStringUSER_EXTRA_TRANSFORMER ="userExtraTransformer";
8888
publicstaticfinalStringUSER_EXTRA_TRANSFORMER_UPDATE_TIME ="userExtraTransformer_updateTime";
89-
89+
publicstaticfinalStringPASSWORD_RESET_EMAIL_TEMPLATE ="passwordResetEmailTemplate";
9090
// custom branding configs
9191
publicstaticfinalStringCUSTOM_BRANDING_KEY ="branding";
9292
}

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/service/OrganizationServiceImpl.java‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
packageorg.lowcoder.domain.organization.service;
22

33
importstaticorg.lowcoder.domain.authentication.AuthenticationService.DEFAULT_AUTH_CONFIG;
4+
importstaticorg.lowcoder.domain.organization.model.Organization.OrganizationCommonSettings.PASSWORD_RESET_EMAIL_TEMPLATE;
45
importstaticorg.lowcoder.domain.organization.model.OrganizationState.ACTIVE;
56
importstaticorg.lowcoder.domain.organization.model.OrganizationState.DELETED;
67
importstaticorg.lowcoder.domain.util.QueryDslUtils.fieldName;
@@ -56,6 +57,12 @@ public class OrganizationServiceImpl implements OrganizationService {
5657

5758
privatefinalConf<Integer>logoMaxSizeInKb;
5859

60+
privatestaticfinalStringPASSWORD_RESET_EMAIL_TEMPLATE_DEFAULT ="<p>Hi, %s<br/>" +
61+
"Here is the link to reset your password: %s<br/>" +
62+
"Please note that the link will expire after 12 hours.<br/><br/>" +
63+
"Regards,<br/>" +
64+
"The Lowcoder Team</p>";
65+
5966
@Autowired
6067
privateAssetRepositoryassetRepository;
6168

@@ -151,6 +158,9 @@ public Mono<Organization> create(Organization organization, String creatorId) {
151158
if (organization ==null ||StringUtils.isNotBlank(organization.getId())) {
152159
returnMono.error(newBizException(BizError.INVALID_PARAMETER,"INVALID_PARAMETER",FieldName.ORGANIZATION));
153160
}
161+
organization.setCommonSettings(newOrganizationCommonSettings());
162+
organization.getCommonSettings().put("PASSWORD_RESET_EMAIL_TEMPLATE",
163+
PASSWORD_RESET_EMAIL_TEMPLATE_DEFAULT);
154164
organization.setState(ACTIVE);
155165
returnMono.just(organization);
156166
})

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/model/User.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
importstaticcom.google.common.base.Suppliers.memoize;
44
importstaticorg.lowcoder.infra.util.AssetUtils.toAssetPath;
55

6+
importjava.time.Instant;
67
importjava.util.*;
78
importjava.util.function.Supplier;
89

@@ -52,6 +53,10 @@ public class User extends HasIdAndAuditing implements BeforeMongodbWrite, AfterM
5253
@JsonProperty(access =JsonProperty.Access.WRITE_ONLY)
5354
privateStringpassword;
5455

56+
privateStringpasswordResetToken;
57+
58+
privateInstantpasswordResetTokenExpiry;
59+
5560
@Transient
5661
BooleanisAnonymous =false;
5762

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
packageorg.lowcoder.domain.user.service;
2+
3+
importjakarta.mail.internet.MimeMessage;
4+
importlombok.extern.slf4j.Slf4j;
5+
importorg.lowcoder.sdk.config.CommonConfig;
6+
importorg.springframework.beans.factory.annotation.Autowired;
7+
importorg.springframework.mail.javamail.JavaMailSender;
8+
importorg.springframework.mail.javamail.MimeMessageHelper;
9+
importorg.springframework.stereotype.Service;
10+
11+
@Service
12+
@Slf4j(topic ="EmailCommunicationService")
13+
publicclassEmailCommunicationService {
14+
15+
@Autowired
16+
privateJavaMailSenderjavaMailSender;
17+
18+
@Autowired
19+
privateCommonConfigconfig;
20+
21+
publicbooleansendPasswordResetEmail(Stringto,Stringtoken,Stringmessage) {
22+
try {
23+
Stringsubject ="Reset Your Lost Password";
24+
MimeMessagemimeMessage =javaMailSender.createMimeMessage();
25+
26+
MimeMessageHelpermimeMessageHelper =newMimeMessageHelper(mimeMessage,true);
27+
28+
mimeMessageHelper.setFrom(config.getLostPasswordEmailSender());
29+
mimeMessageHelper.setTo(to);
30+
mimeMessageHelper.setSubject(subject);
31+
32+
// Construct the message with the token link
33+
StringresetLink =config.getLowcoderPublicUrl() +"/lost-password?token=" +token;
34+
StringformattedMessage =String.format(message,to,resetLink);
35+
mimeMessageHelper.setText(formattedMessage,true);// Set HTML to true to allow links
36+
37+
javaMailSender.send(mimeMessage);
38+
39+
returntrue;
40+
41+
}catch (Exceptione) {
42+
log.error("Failed to send mail to: {}, Exception: ",to,e);
43+
returnfalse;
44+
}
45+
46+
47+
}
48+
49+
}

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public interface UserService {
5050

5151
Mono<String>resetPassword(StringuserId);
5252

53+
Mono<Boolean>lostPassword(StringuserEmail);
54+
55+
Mono<Boolean>resetLostPassword(StringuserEmail,Stringtoken,StringnewPassword);
56+
5357
Mono<Boolean>setPassword(StringuserId,Stringpassword);
5458

5559
Mono<UserDetail>buildUserDetail(Useruser,booleanwithoutDynamicGroups);

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserServiceImpl.java‎

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
importorg.lowcoder.domain.group.service.GroupService;
1717
importorg.lowcoder.domain.organization.model.OrgMember;
1818
importorg.lowcoder.domain.organization.service.OrgMemberService;
19+
importorg.lowcoder.domain.organization.service.OrganizationService;
1920
importorg.lowcoder.domain.user.model.*;
2021
importorg.lowcoder.domain.user.model.User.TransformedUserInfo;
2122
importorg.lowcoder.domain.user.repository.UserRepository;
@@ -29,6 +30,7 @@
2930
importorg.lowcoder.sdk.constants.WorkspaceMode;
3031
importorg.lowcoder.sdk.exception.BizError;
3132
importorg.lowcoder.sdk.exception.BizException;
33+
importorg.lowcoder.sdk.util.HashUtils;
3234
importorg.lowcoder.sdk.util.LocaleUtils;
3335
importorg.springframework.beans.factory.annotation.Autowired;
3436
importorg.springframework.dao.DuplicateKeyException;
@@ -40,6 +42,8 @@
4042

4143
importjavax.annotation.Nonnull;
4244
importjava.security.SecureRandom;
45+
importjava.time.Instant;
46+
importjava.time.temporal.ChronoUnit;
4347
importjava.util.*;
4448
importjava.util.function.Function;
4549
importjava.util.stream.Collectors;
@@ -69,12 +73,15 @@ public class UserServiceImpl implements UserService {
6973
@Autowired
7074
privateOrgMemberServiceorgMemberService;
7175
@Autowired
76+
privateOrganizationServiceorganizationService;
77+
@Autowired
7278
privateGroupServicegroupService;
7379
@Autowired
7480
privateCommonConfigcommonConfig;
7581
@Autowired
7682
privateAuthenticationServiceauthenticationService;
77-
83+
@Autowired
84+
privateEmailCommunicationServiceemailCommunicationService;
7885
privateConf<Integer>avatarMaxSizeInKb;
7986

8087
@PostConstruct
@@ -262,6 +269,47 @@ public Mono<String> resetPassword(String userId) {
262269
});
263270
}
264271

272+
@Override
273+
publicMono<Boolean>lostPassword(StringuserEmail) {
274+
returnfindByName(userEmail)
275+
.zipWhen(user ->orgMemberService.getCurrentOrgMember(user.getId())
276+
.flatMap(orgMember ->organizationService.getById(orgMember.getOrgId()))
277+
.map(organization ->organization.getCommonSettings().get("PASSWORD_RESET_EMAIL_TEMPLATE")))
278+
.flatMap(tuple -> {
279+
Useruser =tuple.getT1();
280+
StringemailTemplate = (String)tuple.getT2();
281+
282+
Stringtoken =generateNewRandomPwd();
283+
InstanttokenExpiry =Instant.now().plus(12,ChronoUnit.HOURS);
284+
if (!emailCommunicationService.sendPasswordResetEmail(userEmail,token,emailTemplate)) {
285+
returnofError(BizError.AUTH_ERROR,"SENDING_EMAIL_FAILED");
286+
}
287+
user.setPasswordResetToken(HashUtils.hash(token.getBytes()));
288+
user.setPasswordResetTokenExpiry(tokenExpiry);
289+
returnrepository.save(user).then(Mono.empty());
290+
});
291+
}
292+
293+
@Override
294+
publicMono<Boolean>resetLostPassword(StringuserEmail,Stringtoken,StringnewPassword) {
295+
returnfindByName(userEmail)
296+
.flatMap(user -> {
297+
if (Instant.now().until(user.getPasswordResetTokenExpiry(),ChronoUnit.MINUTES) <=0) {
298+
returnofError(BizError.INVALID_PARAMETER,"TOKEN_EXPIRED");
299+
}
300+
301+
if (!StringUtils.equals(HashUtils.hash(token.getBytes()),user.getPasswordResetToken())) {
302+
returnofError(BizError.INVALID_PARAMETER,"INVALID_TOKEN");
303+
}
304+
305+
user.setPassword(encryptionService.encryptPassword(newPassword));
306+
user.setPasswordResetToken(StringUtils.EMPTY);
307+
user.setPasswordResetTokenExpiry(Instant.now());
308+
returnrepository.save(user)
309+
.thenReturn(true);
310+
});
311+
}
312+
265313
@SuppressWarnings("SpellCheckingInspection")
266314
@Nonnull
267315
privatestaticStringgenerateNewRandomPwd() {

‎server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/config/CommonConfig.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public class CommonConfig {
4545
privateJsExecutorjsExecutor =newJsExecutor();
4646
privateSet<String>disallowedHosts =newHashSet<>();
4747
privateMarketplacemarketplace =newMarketplace();
48+
privateStringlowcoderPublicUrl;
49+
privateStringlostPasswordEmailSender;
4850

4951
publicbooleanisSelfHost() {
5052
return !isCloud();

‎server/api-service/lowcoder-sdk/src/main/resources/locale_en.properties‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ CANNOT_DELETE_SYSTEM_GROUP=System group cannot be deleted.
1717
NEED_DEV_TO_CREATE_RESOURCE=Invalid operation, workspace developers or admin required.
1818
UNABLE_TO_FIND_VALID_ORG=Cannot find a valid workspace for current user.
1919
USER_BANNED=Current account is frozen.
20+
SENDING_EMAIL_FAILED=Email could not be sent. Please check the smtp settings for the org.
21+
TOKEN_EXPIRED=Token to reset the password has expired
22+
INVALID_TOKEN=Invalid token received for password reset request
2023
# invitation
2124
INVALID_INVITATION_CODE=Invitation code not found.
2225
ALREADY_IN_ORGANIZATION=You are already in this workspace.

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
113113

114114
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,USER_URL +"/me"),
115115
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,USER_URL +"/currentUser"),
116+
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,USER_URL +"/lost-password"),
117+
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,USER_URL +"/reset-lost-password"),
116118

117119
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,GROUP_URL +"/list"),// application view
118120
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,QUERY_URL +"/execute"),// application view
@@ -139,6 +141,8 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
139141
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,NewUrl.APPLICATION_URL +"/marketplace-apps"),// marketplace apps
140142
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,NewUrl.USER_URL +"/me"),
141143
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,NewUrl.USER_URL +"/currentUser"),
144+
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,NewUrl.USER_URL +"/lost-password"),
145+
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,NewUrl.USER_URL +"/reset-lost-password"),
142146
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,NewUrl.GROUP_URL +"/list"),
143147
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,NewUrl.QUERY_URL +"/execute"),
144148
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET,NewUrl.MATERIAL_URL +"/**"),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp