3.8 KiB
3.8 KiB
Plan: Admin API Endpoint — Reset User Password
Context
hiveops-auth has no password reset functionality. The login page has a dead "Forgot Password" link. The admin role and /auth/api/admin/** route prefix already exist. Adding an admin password reset endpoint gives operators a way to reset user passwords without direct DB access. The PASSWORD_CHANGED audit event type and logPasswordChanged() method are already defined but never called.
Files to Create / Modify
| Action | File |
|---|---|
| CREATE | src/main/java/com/hiveops/auth/dto/request/AdminPasswordResetRequest.java |
| MODIFY | src/main/java/com/hiveops/auth/service/AuthService.java |
| CREATE | src/main/java/com/hiveops/auth/controller/UserAdminController.java |
Implementation Steps
1. DTO — AdminPasswordResetRequest.java
- Single field:
newPassword(String) - Jakarta validation:
@NotBlank,@Size(min=12)— mirrors the existingRegisterRequestpattern - Package:
com.hiveops.auth.dto.request
2. AuthService — resetUserPassword(String email, String newPassword)
- Lookup user with
userRepository.findByEmail(email)→ throwUsernameNotFoundException(Spring Security) if absent (already handled byGlobalExceptionHandlerreturning 404-ish; check and use an appropriate existing exception or plainRuntimeException) - Validate password strength via
PasswordValidator.validatePassword(newPassword)— throwIllegalArgumentExceptionwith the error message if invalid - Encode:
passwordEncoder.encode(newPassword)→user.setPasswordHash(...) - Side effects for security:
user.resetFailedLoginAttempts()— clears lock + counter- Revoke all existing refresh tokens via
refreshTokenRepository.revokeAllByUser(user)(already exists — setsrevoked=true, preserving audit history)
userRepository.save(user)authAuditService.logPasswordChanged(user)— already implemented, just unused
3. Controller — UserAdminController.java
- Pattern mirrors
RateLimitAdminControllerexactly:@RestController,@RequestMapping("/auth/api/admin/users"),@RequiredArgsConstructor,@Slf4j@Tag(name = "User Admin", description = "User management (Admin only)")- All endpoints:
@PreAuthorize("hasRole('ADMIN')")
- Single endpoint for now:
POST /auth/api/admin/users/{email}/reset-password Body: { "newPassword": "..." } Response 200: { "message": "Password reset successfully", "email": "..." } Response 404: { "message": "User not found", "email": "..." } Response 400: { "message": "<validation error>", "email": "..." } - Log at INFO level:
"Admin password reset for user: {}"(email only, never the password)
Key Reused Components
PasswordEncoderbean (BCrypt strength 12) — injected intoAuthServicePasswordValidator.validatePassword()—util/PasswordValidator.javaauthAuditService.logPasswordChanged(user)—service/AuthAuditService.javauserRepository.findByEmail()—repository/UserRepository.javarefreshTokenRepository.revokeAllByUser(user)—repository/RefreshTokenRepository.javauser.resetFailedLoginAttempts()—entity/User.java@PreAuthorize("hasRole('ADMIN')")pattern —RateLimitAdminController.java
Verification
- Build:
./mvnw compile— must be clean - Start service (dev profile, H2 in-memory)
- Register a test user, obtain an admin JWT
POST /auth/api/admin/users/{email}/reset-passwordwith valid new password → 200POST /auth/api/loginwith old password → 401POST /auth/api/loginwith new password → 200- Attempt same endpoint without ADMIN role → 403
- Attempt with unknown email → 404
- Attempt with weak password (< 12 chars) → 400
- Check audit log table for
PASSWORD_CHANGEDevent