diff --git a/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserService.java b/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserService.java index 37918f46..484ac2db 100644 --- a/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserService.java +++ b/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserService.java @@ -8,8 +8,8 @@ import com.openblocks.domain.user.model.AuthorizedUser; import com.openblocks.domain.user.model.Connection; -import com.openblocks.domain.user.model.UserDetail; import com.openblocks.domain.user.model.User; +import com.openblocks.domain.user.model.UserDetail; import com.openblocks.infra.annotation.NonEmptyMono; import reactor.core.publisher.Mono; @@ -45,6 +45,8 @@ public interface UserService { Mono updatePassword(String userId, String oldPassword, String newPassword); + Mono resetPassword(String userId); + Mono setPassword(String userId, String password); Mono buildUserDetail(User user, boolean withoutDynamicGroups); diff --git a/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserServiceImpl.java b/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserServiceImpl.java index 9279df44..25aee17c 100644 --- a/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserServiceImpl.java +++ b/server/api-service/openblocks-domain/src/main/java/com/openblocks/domain/user/service/UserServiceImpl.java @@ -7,6 +7,7 @@ import static com.openblocks.sdk.util.ExceptionUtils.ofError; import static com.openblocks.sdk.util.ExceptionUtils.ofException; +import java.security.SecureRandom; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -17,9 +18,11 @@ import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.codec.multipart.Part; @@ -37,9 +40,9 @@ import com.openblocks.domain.organization.service.OrgMemberService; import com.openblocks.domain.user.model.AuthorizedUser; import com.openblocks.domain.user.model.Connection; -import com.openblocks.domain.user.model.UserDetail; import com.openblocks.domain.user.model.User; import com.openblocks.domain.user.model.User.TransformedUserInfo; +import com.openblocks.domain.user.model.UserDetail; import com.openblocks.domain.user.model.UserState; import com.openblocks.domain.user.repository.UserRepository; import com.openblocks.infra.mongo.MongoUpsertHelper; @@ -245,6 +248,31 @@ public Mono updatePassword(String userId, String oldPassword, String ne .thenReturn(true); } + @Override + public Mono resetPassword(String userId) { + return findById(userId) + .flatMap(user -> { + String password = user.getPassword(); + if (StringUtils.isBlank(password)) { + return ofError(BizError.INVALID_PASSWORD, "PASSWORD_NOT_SET_YET"); + } + + String randomStr = generateNewRandomPwd(); + user.setPassword(encryptionService.encryptPassword(randomStr)); + return repository.save(user) + .thenReturn(randomStr); + }); + } + + @SuppressWarnings("SpellCheckingInspection") + @Nonnull + private static String generateNewRandomPwd() { + char[] possibleCharacters = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}<>?") + .toCharArray(); + return RandomStringUtils.random(12, 0, possibleCharacters.length - 1, + false, false, possibleCharacters, new SecureRandom()); + } + @Override public Mono setPassword(String userId, String password) { return findById(userId) diff --git a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/config/CommonConfig.java b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/config/CommonConfig.java index 2a081aa3..aa5f8399 100644 --- a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/config/CommonConfig.java +++ b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/config/CommonConfig.java @@ -39,6 +39,10 @@ public boolean isSelfHost() { return !isCloud(); } + public boolean isEnterpriseMode() { + return workspace.getMode() == WorkspaceMode.ENTERPRISE; + } + @Data public static class Domain { private String defaultValue; diff --git a/server/api-service/openblocks-sdk/src/main/resources/locale_en.properties b/server/api-service/openblocks-sdk/src/main/resources/locale_en.properties index 1054c1d7..aacb3d75 100644 --- a/server/api-service/openblocks-sdk/src/main/resources/locale_en.properties +++ b/server/api-service/openblocks-sdk/src/main/resources/locale_en.properties @@ -43,7 +43,8 @@ USER_NOT_SIGNED_IN=Unknown user, you have to log in first. FAIL_TO_GET_OIDC_INFO=Failed to get OIDC information, error message: {0}. LOG_IN_SOURCE_NOT_SUPPORTED=Sorry, this log in channel is not supported. USER_LOGIN_ID_EXIST=Current email already used by another user. -INVALID_PASSWORD=Sorry, passwords do not match, please retype. +INVALID_PASSWORD=Sorry, passwords do not match. +PASSWORD_NOT_SET_YET=This user hasn't set password yet and cannot be reset. INVALID_EMAIL_OR_PASSWORD=Invalid email or password. ALREADY_BIND=Sorry, {0} has been bound by user {1}. NEED_BIND_THIRD_PARTY_CONNECTION=Sorry, it needs to bind the current workspace login channel. diff --git a/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserApiService.java b/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserApiService.java index 31c09bf5..0147084b 100644 --- a/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserApiService.java +++ b/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserApiService.java @@ -28,13 +28,13 @@ public class UserApiService { private UserService userService; public Mono getUserDetailById(String userId) { - return checkPermission(userId) + return checkAdminPermissionAndUserBelongsToCurrentOrg(userId) .then(userService.findById(userId) .flatMap(user -> userService.buildUserDetail(user, false))); } - private Mono checkPermission(String userId) { - return sessionUserService.getVisitorOrgMember() + private Mono checkAdminPermissionAndUserBelongsToCurrentOrg(String userId) { + return sessionUserService.getVisitorOrgMemberCache() .flatMap(orgMember -> { if (!orgMember.isAdmin()) { return ofError(UNSUPPORTED_OPERATION, "BAD_REQUEST"); @@ -50,4 +50,8 @@ private Mono checkPermission(String userId) { }); } + public Mono resetPassword(String userId) { + return checkAdminPermissionAndUserBelongsToCurrentOrg(userId) + .then(userService.resetPassword(userId)); + } } diff --git a/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserController.java b/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserController.java index e4e45d2b..d33a9f62 100644 --- a/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserController.java +++ b/server/api-service/openblocks-server/src/main/java/com/openblocks/api/usermanagement/UserController.java @@ -32,6 +32,7 @@ import com.openblocks.domain.user.service.UserStatusService; import com.openblocks.infra.constant.NewUrl; import com.openblocks.infra.constant.Url; +import com.openblocks.sdk.config.CommonConfig; import com.openblocks.sdk.exception.BizError; import com.openblocks.sdk.util.UriUtils; @@ -61,6 +62,9 @@ public class UserController { @Autowired private UserApiService userApiService; + @Autowired + private CommonConfig commonConfig; + @GetMapping("/me") public Mono> getUserProfile(ServerWebExchange exchange) { String domain = UriUtils.getRefererDomain(exchange); @@ -133,11 +137,23 @@ public Mono getProfilePhoto(ServerWebExchange exchange, @PathVariable Stri @PutMapping("/password") public Mono> updatePassword(@RequestBody UpdatePasswordRequest request) { - if (StringUtils.isBlank(request.oldPassword) || StringUtils.isBlank(request.newPassword)) { + if (StringUtils.isBlank(request.oldPassword()) || StringUtils.isBlank(request.newPassword())) { return ofError(BizError.INVALID_PARAMETER, "PASSWORD_EMPTY"); } return sessionUserService.getVisitorId() - .flatMap(user -> userService.updatePassword(user, request.oldPassword, request.newPassword)) + .flatMap(user -> userService.updatePassword(user, request.oldPassword(), request.newPassword())) + .map(ResponseView::success); + } + + @PostMapping("/reset-password") + public Mono> resetPassword(@RequestBody ResetPasswordRequest request) { + if (!commonConfig.isEnterpriseMode()) { + return ofError(BizError.UNSUPPORTED_OPERATION, "BAD_REQUEST"); + } + if (StringUtils.isBlank(request.userId())) { + return ofError(BizError.INVALID_PARAMETER, "INVALID_USER_ID"); + } + return userApiService.resetPassword(request.userId()) .map(ResponseView::success); } @@ -165,6 +181,9 @@ public Mono> getUserDetail(@PathVariable("id") String userId) { .map(ResponseView::success); } + public record ResetPasswordRequest(String userId) { + } + public record UpdatePasswordRequest(String oldPassword, String newPassword) { }