Schema
CREATE TABLE USERTABLE
(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY,
USER_NAME VARCHAR(255) NOT NULL,
PASSWORD VARCHAR(255),
PRIMARY KEY (ID)
);
CREATE TABLE PasswordHistory(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY,
USERID BIGINT NOT NULL,
PASSWORD VARCHAR(255),
ENTRYTIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (ID),
FOREIGN KEY (USERID) REFERENCES USERTABLE(ID)
);
User Table
@DynamicInsert(value = true)
@Entity
@Table(name = "usertable")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@JsonProperty
@NotBlank(message = "User name must be provided")
@Column(name= "USERNAME", nullable = false, unique = true)
@ApiModelProperty(name = "username", value = "Name of user (must be unique)", required = true, example = "user_name", position = 1)
private String username;
@JsonProperty
@NotBlank(message = "Password must be provided")
@Column(nullable = false)
@ApiModelProperty(name = "password", value = "Password of the user", required = true, example = "password", position = 2)
private String password;
@JsonProperty
@Column(columnDefinition = "VARCHAR(255) DEFAULT 'CUSTOMER'")
private String role;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "USERID", referencedColumnName = "id")
List<PasswordHistory> passwordHistories;
//getters, setters
}
Password log table:
@Entity
@Table(name = "passwordhistory")
public class PasswordHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@JsonProperty
@Column(name = "USERID")
private Long userId;
@JsonProperty
@Column(name = "PASSWORD")
@ApiModelProperty(name = "password", value = "Password of the user", required = true, example = "password", position = 2)
private String password;
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonProperty
private Timestamp entrytime;
public PasswordHistory() {
}
//GetterSetter
}
In user Service
public class UserPrincipalService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordHistoryRepository passwordHistoryRepository;
@Autowired
public UserPrincipalService(UserRepository userRepository, PasswordHistoryRepository passwordHistoryRepository) {
this.userRepository = userRepository;
this.passwordHistoryRepository = passwordHistoryRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User savedUser = findUserByUsername(username);
return new UserPrincipal(savedUser);
}
public User findUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
public void changePassword(String username, String newpassword, String oldpassword) throws PasswordMismatchException, OldThreePasswordMatchException {
User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
if (!oldpassword.equals(user.getPassword())) throw new PasswordMismatchException("Old password incorrect");
List<PasswordHistory> passwordHistories = passwordHistoryRepository.findTop3ByUserIdOrderByEntrytimeDesc(user.getId());
for (PasswordHistory passwordHistory : passwordHistories)
if (oldpassword.equals(passwordHistory.getPassword())) {
throw new OldThreePasswordMatchException("Password should not match with last three passwords");
}
user.setPassword(newpassword);
userRepository.save(user);
}
In controller:
@PostMapping(value = "/user/password")
@ApiOperation(value = "Change password")
@ResponseStatus(code = HttpStatus.OK)
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Password has been changed"),
@ApiResponse(code = 404, message = "Record not found", response = ErrorResponse.class),
@ApiResponse(code = 400, message = "Server cannot process request due to client error", response = ErrorResponse.class),
@ApiResponse(code = 500, message = "Something failed in the server", response = ErrorResponse.class)
})
public String changePassword(Principal principal,
@RequestParam("newpassword") String newpassword,
@RequestParam("oldpassword") String oldpassword) throws PasswordMismatchException, OldThreePasswordMatchException {
String username = principal.getName();
userPrincipalService.changePassword(username, newpassword, oldpassword);
return "Password has been updated successfully";
}
I have done one to many mapping between user and password log table. Password log is basically for auditing the passwords and validating.
Password Log table should be updated automatically when any change or new password entry is done in user table. But it is not getting reflected in the database
I am using JPArepository
CodePudding user response:
Looks like you missed the most important step : create a new entry in the password history when setting a new password.
Something like :
public void setPassword(String password) {
this.password = password;
history.add(new PasswordHistory(password);
}
CodePudding user response:
Maybe you just need to anottate the method changePassword
@Transactional
public void changePassword(String username, String newpassword, String oldpassword) throws PasswordMismatchException, OldThreePasswordMatchException {
User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
if (!oldpassword.equals(user.getPassword())) throw new PasswordMismatchException("Old password incorrect");
List<PasswordHistory> passwordHistories = passwordHistoryRepository.findTop3ByUserIdOrderByEntrytimeDesc(user.getId());
for (PasswordHistory passwordHistory : passwordHistories)
if (oldpassword.equals(passwordHistory.getPassword())) {
throw new OldThreePasswordMatchException("Password should not match with last three passwords");
}
user.setPassword(newpassword);
userRepository.save(user);
}