feat: implement user credential management with new UserCredential entity and related components

This commit is contained in:
siujamo
2026-03-12 13:56:06 +08:00
parent 0562c8548b
commit 7ce241cc16
16 changed files with 229 additions and 137 deletions
+7 -7
View File
@@ -34,12 +34,12 @@
--- Type Definitions ---
DROP TYPE IF EXISTS USER_STATUS CASCADE;
DROP TYPE IF EXISTS STATUS CASCADE;
DROP TYPE IF EXISTS IDENTITY_PROVIDER CASCADE;
DROP TYPE IF EXISTS CREDENTIAL_PROVIDER CASCADE;
DROP TYPE IF EXISTS SETTING_TYPE CASCADE;
CREATE TYPE USER_STATUS AS ENUM ('ACTIVE', 'INACTIVE', 'LOCKED');
CREATE TYPE STATUS AS ENUM ('ACTIVE', 'INACTIVE');
CREATE TYPE IDENTITY_PROVIDER AS ENUM ('LOCAL', 'OIDC', 'MICROSOFT_ENTRA_ID', 'GOOGLE_OIDC', 'SAML');
CREATE TYPE CREDENTIAL_PROVIDER AS ENUM ('LOCAL', 'OIDC', 'MICROSOFT_ENTRA_ID', 'GOOGLE_OIDC', 'SAML');
CREATE TYPE SETTING_TYPE AS ENUM ('STRING', 'BOOLEAN', 'INT');
--- Departments Table ---
@@ -186,15 +186,15 @@ SELECT 'johndoe',
CURRENT_TIMESTAMP;
--- User Identities Table ---
DROP TABLE IF EXISTS user_identities CASCADE;
CREATE TABLE user_identities
DROP TABLE IF EXISTS user_credentials CASCADE;
CREATE TABLE user_credentials
(
user_id BIGINT NOT NULL REFERENCES users (id),
provider IDENTITY_PROVIDER NOT NULL,
external_id VARCHAR(255) NOT NULL,
provider CREDENTIAL_PROVIDER NOT NULL,
credential VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, provider, external_id)
PRIMARY KEY (user_id, provider, credential)
);
--- Roles Table ---
@@ -48,12 +48,6 @@ public class User {
@Column(nullable = false, length = 64)
private String username;
/**
* The encrypted password for user authentication.
*/
@Column
private String password;
/**
* The user's complete full name.
*/
@@ -124,10 +118,9 @@ public class User {
public User() {
}
public User(Long id, String username, String password, String fullName, String email, String regionAbbreviation, String phoneNumber, String avatarUrl, UserStatus status, Long departmentId, Long positionId, LocalDateTime createdAt, LocalDateTime updatedAt) {
public User(Long id, String username, String fullName, String email, String regionAbbreviation, String phoneNumber, String avatarUrl, UserStatus status, Long departmentId, Long positionId, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.username = username;
this.password = password;
this.fullName = fullName;
this.email = email;
this.regionAbbreviation = regionAbbreviation;
@@ -176,24 +169,6 @@ public class User {
this.username = username;
}
/**
* Gets the encrypted password.
*
* @return the encrypted password
*/
public String getPassword() {
return password;
}
/**
* Sets the encrypted password.
*
* @param password the encrypted password (never plain text)
*/
public void setPassword(String password) {
this.password = password;
}
public String getFullName() {
return fullName;
}
@@ -337,7 +312,6 @@ public class User {
public static class UserBuilder {
private Long id;
private String username;
private String password;
private String fullName;
private String email;
private String regionAbbreviation;
@@ -362,11 +336,6 @@ public class User {
return this;
}
public UserBuilder password(String password) {
this.password = password;
return this;
}
public UserBuilder fullName(String fullName) {
this.fullName = fullName;
return this;
@@ -423,7 +392,7 @@ public class User {
* @return a new User instance
*/
public User build() {
return new User(id, username, password, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
return new User(id, username, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
}
}
@@ -1,7 +1,7 @@
package com.onixbyte.helix.domain.entity;
import com.onixbyte.helix.enumeration.IdentityProvider;
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
import com.onixbyte.helix.enumeration.CredentialProvider;
import com.onixbyte.helix.domain.entity.embeddable.UserCredentialId;
import jakarta.persistence.*; // 导入 Jakarta Persistence API
import java.time.LocalDateTime;
import java.util.Objects;
@@ -19,14 +19,14 @@ import java.util.Objects;
* @since 1.0
*/
@Entity
@Table(name = "user_identities")
public class UserIdentity {
@Table(name = "user_credentials")
public class UserCredential {
/**
* The composite primary key for the entity, composed of userId, provider, and externalId.
*/
@EmbeddedId
private UserIdentityId id;
private UserCredentialId id;
/**
* The timestamp when this identity mapping was created.
@@ -53,7 +53,7 @@ public class UserIdentity {
* @param userId the user ID
*/
public void setUserId(Long userId) {
if (this.id == null) this.id = new UserIdentityId();
if (this.id == null) this.id = new UserCredentialId();
this.id.setUserId(userId);
}
@@ -61,7 +61,7 @@ public class UserIdentity {
* Gets the external identity provider from the composite primary key.
* @return the provider
*/
public IdentityProvider getProvider() {
public CredentialProvider getProvider() {
return this.id != null ? this.id.getProvider() : null;
}
@@ -69,8 +69,8 @@ public class UserIdentity {
* Sets the external identity provider within the composite primary key.
* @param provider the provider
*/
public void setProvider(IdentityProvider provider) {
if (this.id == null) this.id = new UserIdentityId();
public void setProvider(CredentialProvider provider) {
if (this.id == null) this.id = new UserCredentialId();
this.id.setProvider(provider);
}
@@ -78,17 +78,17 @@ public class UserIdentity {
* Gets the unique identifier from the external provider from the composite primary key.
* @return the external ID
*/
public String getExternalId() {
return this.id != null ? this.id.getExternalId() : null;
public String getCredential() {
return this.id != null ? this.id.getCredential() : null;
}
/**
* Sets the unique identifier from the external provider within the composite primary key.
* @param externalId the external ID
* @param credential the external ID
*/
public void setExternalId(String externalId) {
if (this.id == null) this.id = new UserIdentityId();
this.id.setExternalId(externalId);
public void setCredential(String credential) {
if (this.id == null) this.id = new UserCredentialId();
this.id.setCredential(credential);
}
public LocalDateTime getCreatedAt() {
@@ -109,12 +109,12 @@ public class UserIdentity {
// --- Constructors (Adjusted for EmbeddedId) ---
public UserIdentity() {
this.id = new UserIdentityId(); // Initialize ID object for safety
public UserCredential() {
this.id = new UserCredentialId(); // Initialize ID object for safety
}
public UserIdentity(Long userId, IdentityProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = new UserIdentityId(userId, provider, externalId);
public UserCredential(Long userId, CredentialProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = new UserCredentialId(userId, provider, externalId);
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
@@ -125,7 +125,7 @@ public class UserIdentity {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserIdentity that = (UserIdentity) o;
UserCredential that = (UserCredential) o;
return Objects.equals(id, that.id); // Entity equality based on primary key
}
@@ -139,7 +139,7 @@ public class UserIdentity {
return "UserIdentity{" +
"userId=" + getUserId() +
", provider=" + getProvider() +
", externalId='" + getExternalId() + '\'' +
", externalId='" + getCredential() + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
@@ -162,7 +162,7 @@ public class UserIdentity {
*/
public static class UserIdentityBuilder {
private Long userId;
private IdentityProvider provider;
private CredentialProvider provider;
private String externalId;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -175,7 +175,7 @@ public class UserIdentity {
return this;
}
public UserIdentityBuilder provider(IdentityProvider provider) {
public UserIdentityBuilder provider(CredentialProvider provider) {
this.provider = provider;
return this;
}
@@ -200,8 +200,8 @@ public class UserIdentity {
*
* @return a new UserIdentity instance
*/
public UserIdentity build() {
return new UserIdentity(userId, provider, externalId, createdAt, updatedAt);
public UserCredential build() {
return new UserCredential(userId, provider, externalId, createdAt, updatedAt);
}
}
@@ -1,6 +1,6 @@
package com.onixbyte.helix.domain.entity.embeddable;
import com.onixbyte.helix.enumeration.IdentityProvider;
import com.onixbyte.helix.enumeration.CredentialProvider;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Enumerated;
@@ -18,7 +18,7 @@ import java.util.Objects;
* from that provider.
*/
@Embeddable
public class UserIdentityId implements Serializable {
public class UserCredentialId implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -37,23 +37,23 @@ public class UserIdentityId implements Serializable {
@Column(nullable = false)
@Enumerated
@JdbcType(PostgreSQLEnumJdbcType.class)
private IdentityProvider provider;
private CredentialProvider provider;
/**
* The unique identifier from the external provider, corresponding to the 'external_id' column.
*/
@Column(nullable = false)
private String externalId;
private String credential;
// --- Constructors ---
public UserIdentityId() {
public UserCredentialId() {
}
public UserIdentityId(Long userId, IdentityProvider provider, String externalId) {
public UserCredentialId(Long userId, CredentialProvider provider, String credential) {
this.userId = userId;
this.provider = provider;
this.externalId = externalId;
this.credential = credential;
}
// --- Getters and Setters (Omitted for brevity, but should exist) ---
@@ -65,32 +65,32 @@ public class UserIdentityId implements Serializable {
this.userId = userId;
}
public IdentityProvider getProvider() {
public CredentialProvider getProvider() {
return provider;
}
public void setProvider(IdentityProvider provider) {
public void setProvider(CredentialProvider provider) {
this.provider = provider;
}
public String getExternalId() {
return externalId;
public String getCredential() {
return credential;
}
public void setExternalId(String externalId) {
this.externalId = externalId;
public void setCredential(String credential) {
this.credential = credential;
}
// --- equals and hashCode (REQUIRED for composite keys) ---
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
UserIdentityId that = (UserIdentityId) o;
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(externalId, that.externalId);
UserCredentialId that = (UserCredentialId) o;
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(credential, that.credential);
}
@Override
public int hashCode() {
return Objects.hash(userId, provider, externalId);
return Objects.hash(userId, provider, credential);
}
}
@@ -1,7 +1,7 @@
package com.onixbyte.helix.domain.view;
import com.onixbyte.helix.enumeration.IdentityProvider;
import com.onixbyte.helix.domain.entity.UserIdentity;
import com.onixbyte.helix.enumeration.CredentialProvider;
import com.onixbyte.helix.domain.entity.UserCredential;
import java.time.LocalDateTime;
import java.util.Objects;
@@ -26,7 +26,7 @@ public class UserIdentityView {
/**
* The external identity provider.
*/
private IdentityProvider provider;
private CredentialProvider provider;
/**
* The unique identifier from the external provider.
@@ -52,7 +52,7 @@ public class UserIdentityView {
/**
* Constructor with all fields.
*/
public UserIdentityView(Long userId, IdentityProvider provider, String externalId,
public UserIdentityView(Long userId, CredentialProvider provider, String externalId,
LocalDateTime createdAt, LocalDateTime updatedAt) {
this.userId = userId;
this.provider = provider;
@@ -64,19 +64,19 @@ public class UserIdentityView {
/**
* Creates a UserIdentityView from a UserIdentity entity.
*
* @param userIdentity the UserIdentity entity
* @param userCredential the UserIdentity entity
* @return the UserIdentityView object
*/
public static UserIdentityView fromEntity(UserIdentity userIdentity) {
if (userIdentity == null) {
public static UserIdentityView fromEntity(UserCredential userCredential) {
if (userCredential == null) {
return null;
}
return new UserIdentityView(
userIdentity.getUserId(),
userIdentity.getProvider(),
userIdentity.getExternalId(),
userIdentity.getCreatedAt(),
userIdentity.getUpdatedAt()
userCredential.getUserId(),
userCredential.getProvider(),
userCredential.getCredential(),
userCredential.getCreatedAt(),
userCredential.getUpdatedAt()
);
}
@@ -88,11 +88,11 @@ public class UserIdentityView {
this.userId = userId;
}
public IdentityProvider getProvider() {
public CredentialProvider getProvider() {
return provider;
}
public void setProvider(IdentityProvider provider) {
public void setProvider(CredentialProvider provider) {
this.provider = provider;
}
@@ -162,7 +162,7 @@ public class UserIdentityView {
*/
public static class UserIdentityViewBuilder {
private Long userId;
private IdentityProvider provider;
private CredentialProvider provider;
private String externalId;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -175,7 +175,7 @@ public class UserIdentityView {
return this;
}
public UserIdentityViewBuilder provider(IdentityProvider provider) {
public UserIdentityViewBuilder provider(CredentialProvider provider) {
this.provider = provider;
return this;
}
@@ -6,8 +6,4 @@ public record LoginSuccessResponse(
String accessToken,
User user
) {
public LoginSuccessResponse {
user.setPassword(null);
}
}
@@ -1,3 +1,25 @@
/*
* Copyright (c) 2024-2026 OnixByte
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.onixbyte.helix.enumeration;
import com.onixbyte.helix.config.AuthenticationConfig;
@@ -18,7 +40,7 @@ import com.onixbyte.helix.config.AuthenticationConfig;
* @since 1.0.0
* @see AuthenticationConfig
*/
public enum IdentityProvider {
public enum CredentialProvider {
/**
* Local identity provider using the application's internal user database.
@@ -67,8 +67,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
.map((authority) -> (GrantedAuthority) authority::getCode)
.toList();
user.setPassword(null);
var authentication = UsernamePasswordAuthentication.authenticated(user, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
@@ -1,6 +1,8 @@
package com.onixbyte.helix.manager;
import com.onixbyte.helix.common.regex.Patterns;
import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
import com.onixbyte.helix.mapper.UserCredentialMapper;
import com.onixbyte.helix.shared.CacheName;
import com.onixbyte.helix.domain.database.query.wrapper.QueryUserWrapper;
import com.onixbyte.helix.domain.entity.User;
@@ -30,16 +32,19 @@ public class UserManager {
private final UserMapper userMapper;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserCredentialMapper userCredentialMapper;
@Autowired
public UserManager(
UserMapper userMapper,
UserRepository userRepository,
PasswordEncoder passwordEncoder
PasswordEncoder passwordEncoder,
UserCredentialMapper userCredentialMapper
) {
this.userMapper = userMapper;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userCredentialMapper = userCredentialMapper;
}
/**
@@ -87,11 +92,6 @@ public class UserManager {
var userToUpdate = userRepository.findById(user.getId())
.orElseThrow(() -> new BizException(HttpStatus.BAD_REQUEST, "找不到 ID 为" + user.getId() + "的用户信息"));
Optional.ofNullable(user.getPassword())
.filter(StringUtils::isNotBlank)
.map(passwordEncoder::encode)
.ifPresent(userToUpdate::setPassword);
Optional.ofNullable(user.getFullName())
.filter(StringUtils::isNotBlank)
.ifPresent(userToUpdate::setFullName);
@@ -126,4 +126,12 @@ public class UserManager {
return userToUpdate;
}
@Transactional(rollbackFor = Throwable.class)
public void updateUserPassword(ResetPasswordRequest request) {
userCredentialMapper.updateUserCredential(
request.id(),
passwordEncoder.encode(request.password())
);
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2024-2026 OnixByte
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.onixbyte.helix.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface UserCredentialMapper {
int updateUserCredential(
@Param("userId") Long userId,
@Param("encodedPassword") String encodedPassword
);
}
@@ -0,0 +1,10 @@
package com.onixbyte.helix.repository;
import com.onixbyte.helix.domain.entity.UserCredential;
import com.onixbyte.helix.domain.entity.embeddable.UserCredentialId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserCredentialRepository extends JpaRepository<UserCredential, UserCredentialId> {
}
@@ -1,10 +0,0 @@
package com.onixbyte.helix.repository;
import com.onixbyte.helix.domain.entity.UserIdentity;
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserIdentityRepository extends JpaRepository<UserIdentity, UserIdentityId> {
}
@@ -1,13 +1,17 @@
package com.onixbyte.helix.security.provider;
import com.onixbyte.helix.domain.entity.Authority;
import com.onixbyte.helix.domain.entity.UserCredential;
import com.onixbyte.helix.enumeration.CredentialProvider;
import com.onixbyte.helix.exception.BizException;
import com.onixbyte.helix.manager.AuthorityManager;
import com.onixbyte.helix.manager.UserManager;
import com.onixbyte.helix.repository.UserCredentialRepository;
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
@@ -24,16 +28,19 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro
private final UserManager userManager;
private final PasswordEncoder passwordEncoder;
private final AuthorityManager authorityManager;
private final UserCredentialRepository userCredentialRepository;
@Autowired
public UsernamePasswordAuthenticationProvider(
UserManager userManager,
PasswordEncoder passwordEncoder,
AuthorityManager authorityManager
AuthorityManager authorityManager,
UserCredentialRepository userCredentialRepository
) {
this.userManager = userManager;
this.passwordEncoder = passwordEncoder;
this.authorityManager = authorityManager;
this.userCredentialRepository = userCredentialRepository;
}
@Override
@@ -49,14 +56,20 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro
throw new BizException(HttpStatus.UNAUTHORIZED, "用户名或密码错误。");
}
// get user credentials from database
var userCredentials = userCredentialRepository.findOne(Example.of(UserCredential.builder()
.provider(CredentialProvider.LOCAL)
.userId(user.getId())
.build()))
.orElseThrow(() -> new BizException(HttpStatus.UNAUTHORIZED, "您还没有配置密码,请使用第三方账号登录"));
// validate password
if (!passwordEncoder.matches(usernamePasswordAuthentication.getCredentials(), user.getPassword())) {
if (!passwordEncoder.matches(usernamePasswordAuthentication.getCredentials(), userCredentials.getCredential())) {
log.error("User {} is trying to authenticate but password is incorrect.", usernamePasswordAuthentication.getPrincipal());
throw new BizException(HttpStatus.UNAUTHORIZED, "用户名或密码错误。");
}
// erase credentials
user.setPassword(null);
usernamePasswordAuthentication.eraseCredentials();
// get authorities
@@ -3,7 +3,9 @@ package com.onixbyte.helix.service;
import com.onixbyte.helix.domain.entity.Setting;
import com.onixbyte.helix.domain.entity.User;
import com.onixbyte.helix.domain.web.request.LoginRequest;
import com.onixbyte.helix.enumeration.ApplicationMode;
import com.onixbyte.helix.exception.BizException;
import com.onixbyte.helix.manager.ApplicationManager;
import com.onixbyte.helix.manager.CaptchaManager;
import com.onixbyte.helix.manager.SecurityManager;
import com.onixbyte.helix.manager.SettingManager;
@@ -30,18 +32,20 @@ public class AuthService {
private final AuthenticationManager authenticationManager;
private final SettingManager settingManager;
private final SecurityManager securityManager;
private final ApplicationManager applicationManager;
@Autowired
public AuthService(
CaptchaManager captchaManager,
AuthenticationManager authenticationManager,
SettingManager settingManager,
SecurityManager securityManager
) {
SecurityManager securityManager,
ApplicationManager applicationManager) {
this.captchaManager = captchaManager;
this.authenticationManager = authenticationManager;
this.settingManager = settingManager;
this.securityManager = securityManager;
this.applicationManager = applicationManager;
}
/**
@@ -94,10 +98,22 @@ public class AuthService {
public ResponseCookie buildCookie(String cookieName, String token) {
var cookieBuilder = ResponseCookie.from(cookieName, token)
.httpOnly(true)
.maxAge(securityManager.getTokenValidDuration())
.secure(true)
.path("/");
var applicationMode = applicationManager.getApplicationMode();
switch (applicationMode) {
case PRODUCTION -> {
cookieBuilder.httpOnly(true);
}
case DEVELOPMENT -> {
cookieBuilder.sameSite("NONE");
}
case null, default -> {
}
}
return cookieBuilder.build();
}
@@ -12,6 +12,8 @@ import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
import com.onixbyte.helix.domain.web.request.EditUserRequest;
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
import com.onixbyte.helix.manager.*;
import com.onixbyte.helix.mapper.UserCredentialMapper;
import com.onixbyte.helix.repository.UserCredentialRepository;
import com.onixbyte.identitygenerator.IdentityGenerator;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -35,6 +37,8 @@ public class UserService {
private final ApplicationManager applicationManager;
private final DepartmentManager departmentManager;
private final PositionManager positionManager;
private final UserCredentialRepository userCredentialRepository;
private final UserCredentialMapper userCredentialMapper;
@Autowired
public UserService(
@@ -45,8 +49,8 @@ public class UserService {
PasswordEncoder passwordEncoder,
ApplicationManager applicationManager,
DepartmentManager departmentManager,
PositionManager positionManager
) {
PositionManager positionManager,
UserCredentialRepository userCredentialRepository, UserCredentialMapper userCredentialMapper) {
this.userManager = userManager;
this.userIdentityGenerator = userIdentityGenerator;
this.roleManager = roleManager;
@@ -55,6 +59,8 @@ public class UserService {
this.applicationManager = applicationManager;
this.departmentManager = departmentManager;
this.positionManager = positionManager;
this.userCredentialRepository = userCredentialRepository;
this.userCredentialMapper = userCredentialMapper;
}
public Page<UserDetailResponse> queryUserDetailsPage(Pageable pageable, QueryUserRequest request) {
@@ -98,7 +104,6 @@ public class UserService {
var user = userManager.save(User.builder()
.id(userIdentityGenerator.nextId())
.username(request.username())
.password(passwordEncoder.encode(request.password()))
.fullName(request.fullName())
.email(request.email())
.regionAbbreviation(request.regionAbbreviation())
@@ -168,10 +173,7 @@ public class UserService {
@Transactional(rollbackFor = Throwable.class)
public void resetPassword(ResetPasswordRequest request) {
userManager.updateUser(User.builder()
.id(request.id())
.password(request.password())
.build());
userManager.updateUserPassword(request);
}
@Transactional(rollbackFor = Throwable.class)
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright (c) 2024-2026 OnixByte
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.onixbyte.helix.mapper.UserCredentialMapper">
<update id="updateUserCredential">
UPDATE user_credentials
SET credential = #{encodedPassword}
WHERE user_id = #{userId}
AND provider = 'LOCAL'::credential_provider
</update>
</mapper>