AccountApiController.java
package com.taxonomy.security.controller;
import com.taxonomy.security.service.PasswordChangeService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Profile;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/** Self-service account operations for local API clients. */
@RestController
@RequestMapping("/api/account")
@Profile("!keycloak")
@ConditionalOnProperty(name = "taxonomy.security.change-password-enabled",
havingValue = "true", matchIfMissing = true)
public class AccountApiController {
private final PasswordChangeService passwordChangeService;
public AccountApiController(PasswordChangeService passwordChangeService) {
this.passwordChangeService = passwordChangeService;
}
@PostMapping("/change-password")
public ResponseEntity<Map<String, String>> changePassword(
Authentication authentication,
@RequestBody Map<String, String> body) {
if (authentication == null) {
return ResponseEntity.status(401).body(Map.of(
"error", "AUTHENTICATION_REQUIRED"));
}
PasswordChangeService.Result result = passwordChangeService.changePassword(
authentication.getName(),
body.get("currentPassword"),
body.get("newPassword"),
body.get("confirmPassword"));
if (result == PasswordChangeService.Result.CHANGED) {
return ResponseEntity.ok(Map.of(
"status", "PASSWORD_CHANGED",
"message", "Password changed successfully"));
}
return ResponseEntity.badRequest().body(Map.of(
"error", result.name(),
"message", messageFor(result)));
}
private static String messageFor(PasswordChangeService.Result result) {
return switch (result) {
case USER_NOT_FOUND -> "User not found";
case CURRENT_PASSWORD_INCORRECT -> "Current password is incorrect";
case TOO_SHORT -> "New password must be at least "
+ PasswordChangeService.MINIMUM_PASSWORD_LENGTH + " characters";
case CONFIRMATION_MISMATCH -> "New passwords do not match";
case SAME_AS_CURRENT -> "New password must differ from the current password";
case CHANGED -> "Password changed successfully";
};
}
}