# 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 existing `RegisterRequest` pattern - Package: `com.hiveops.auth.dto.request` ### 2. AuthService — `resetUserPassword(String email, String newPassword)` - Lookup user with `userRepository.findByEmail(email)` → throw `UsernameNotFoundException` (Spring Security) if absent (already handled by `GlobalExceptionHandler` returning 404-ish; check and use an appropriate existing exception or plain `RuntimeException`) - Validate password strength via `PasswordValidator.validatePassword(newPassword)` — throw `IllegalArgumentException` with 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 — sets `revoked=true`, preserving audit history) - `userRepository.save(user)` - `authAuditService.logPasswordChanged(user)` — already implemented, just unused ### 3. Controller — `UserAdminController.java` - Pattern mirrors `RateLimitAdminController` exactly: - `@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": "", "email": "..." } ``` - Log at INFO level: `"Admin password reset for user: {}"` (email only, never the password) ## Key Reused Components - `PasswordEncoder` bean (BCrypt strength 12) — injected into `AuthService` - `PasswordValidator.validatePassword()` — `util/PasswordValidator.java` - `authAuditService.logPasswordChanged(user)` — `service/AuthAuditService.java` - `userRepository.findByEmail()` — `repository/UserRepository.java` - `refreshTokenRepository.revokeAllByUser(user)` — `repository/RefreshTokenRepository.java` - `user.resetFailedLoginAttempts()` — `entity/User.java` - `@PreAuthorize("hasRole('ADMIN')")` pattern — `RateLimitAdminController.java` ## Verification 1. Build: `./mvnw compile` — must be clean 2. Start service (dev profile, H2 in-memory) 3. Register a test user, obtain an admin JWT 4. `POST /auth/api/admin/users/{email}/reset-password` with valid new password → 200 5. `POST /auth/api/login` with old password → 401 6. `POST /auth/api/login` with new password → 200 7. Attempt same endpoint without ADMIN role → 403 8. Attempt with unknown email → 404 9. Attempt with weak password (< 12 chars) → 400 10. Check audit log table for `PASSWORD_CHANGED` event