Skip to content

MOSU-30 feat: 비밀번호 변경#91

Merged
wlgns12370 merged 5 commits intodevelopfrom
feature/mosu-30
Jul 13, 2025
Merged

MOSU-30 feat: 비밀번호 변경#91
wlgns12370 merged 5 commits intodevelopfrom
feature/mosu-30

Conversation

@wlgns12370
Copy link
Contributor

@wlgns12370 wlgns12370 commented Jul 13, 2025

✨ 구현한 기능

  • 비밀번호 찾기 기능
  • 비밀번호 변경 기능

📢 논의하고 싶은 내용

  • x

🎸 기타

  • 내일 인증 서비스 문서를 받아서 전반적인 api 초안으로 만들었습니다.

Summary by CodeRabbit

  • New Features

    • Added endpoints for changing and recovering user passwords with enhanced request and response validation.
    • Introduced new request and response formats for password change and recovery operations.
    • Implemented custom validation annotations for login IDs and phone numbers to enforce input format rules.
  • Documentation

    • Enhanced API documentation with detailed descriptions and examples for authentication and user management fields.
  • Bug Fixes

    • Strengthened validation for phone number and password fields to prevent blank entries.
  • Chores

    • Added a new error code for invalid user information to improve error handling and messaging.

@coderabbitai
Copy link

coderabbitai bot commented Jul 13, 2025

## Walkthrough

This update introduces new user password management features, including password change and recovery, by adding service, controller, and DTO classes. It also enhances input validation with new and updated annotation interfaces, augments API documentation, and expands error handling with a new error code. Swagger and validation annotations are applied to relevant request DTOs.

## Changes

| File(s)                                                                                                   | Change Summary                                                                                                            |
|-----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| .../application/user/MyUserService.java                                                                   | Added new service class for password change and recovery with transactional methods and custom exception handling.        |
| .../presentation/user/MyUserController.java<br>.../presentation/user/MyUserControllerDocs.java            | Added new REST controller and Swagger documentation interface for password-related endpoints.                             |
| .../presentation/user/dto/ChangePasswordRequest.java<br>.../ChangePasswordResponse.java<br>.../FindPasswordRequest.java | Introduced new DTO records for password change and recovery requests/responses, with validation annotations.              |
| .../domain/user/UserJpaEntity.java                                                                        | Added public method to update the user's password field.                                                                  |
| .../global/annotation/LoginIdPattern.java                                                                 | Introduced new annotation for validating login ID format and non-blank constraint.                                        |
| .../global/annotation/PhoneNumberPattern.java                                                             | Updated annotation to add `@NotBlank` for phone number validation.                                                        |
| .../global/annotation/PasswordPattern.java                                                                | Updated annotation to add `@NotBlank` for password validation.                                                            |
| .../global/exception/ErrorCode.java                                                                       | Added new error code `USER_INFO_INVALID` for invalid user information scenarios.                                          |
| .../presentation/auth/dto/LoginRequest.java                                                               | Enhanced login request DTO with Swagger and validation annotations for input fields.                                      |
| .../presentation/auth/dto/SignUpRequest.java                                                              | Added login ID validation annotation and updated Swagger description for sign-up request DTO.                             |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant MyUserController
    participant MyUserService
    participant UserJpaEntity
    participant Repository

    Client->>MyUserController: POST /user/me/password (userId, ChangePasswordRequest)
    MyUserController->>MyUserService: changePassword(userId, request)
    MyUserService->>Repository: findById(userId)
    Repository-->>MyUserService: UserJpaEntity
    MyUserService->>UserJpaEntity: changePassword(encodedNewPassword)
    MyUserService-->>MyUserController: ChangePasswordResponse
    MyUserController-->>Client: ApiResponseWrapper<ChangePasswordResponse>

    Client->>MyUserController: POST /user/me/find-password (userId, FindPasswordRequest)
    MyUserController->>MyUserService: findPassword(userId, request)
    MyUserService->>Repository: findById(userId)
    Repository-->>MyUserService: UserJpaEntity
    MyUserService->>MyUserService: validate name/loginId/phone
    MyUserService-->>MyUserController: Void
    MyUserController-->>Client: ApiResponseWrapper<Void>

Suggested labels

우선순위 중

Suggested reviewers

  • jbh010204

Poem

A hop, a skip, a password flip,
With patterns checked and docs equipped.
New flows for help when passwords hide,
And errors caught with gentle guide.
The bunnies cheer, the users smile—
Secure and swift, in CodeRabbit style! 🐇✨


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=mosu-dev/mosu-server&utm_content=91):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@wlgns12370 wlgns12370 linked an issue Jul 13, 2025 that may be closed by this pull request
@wlgns12370 wlgns12370 added this to the 모수 MVP API 구현 milestone Jul 13, 2025
@wlgns12370 wlgns12370 self-assigned this Jul 13, 2025
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @wlgns12370, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements core functionalities for user password management, enabling users to change their passwords and initiate a password recovery process. It also introduces stricter input validation for user IDs and phone numbers across relevant DTOs, enhancing data integrity and security.

Highlights

  • Password Management API: Introduced new API endpoints and corresponding service logic for changing and finding user passwords. This includes /user/me/password for password updates and /user/me/find-password for password retrieval.
  • Input Validation Enhancements: A new custom validation annotation, @LoginIdPattern, has been added to enforce specific format rules for user login IDs. Additionally, the existing @PhoneNumberPattern annotation was enhanced to ensure phone number fields are not blank.
  • Error Handling: A new error code, USER_INFO_INVALID, was added to the ErrorCode enum to specifically handle scenarios where provided user information is incorrect during password retrieval attempts.
  • Domain Model Update: The UserJpaEntity domain model now includes a changePassword method, centralizing the logic for updating a user's password within the entity itself.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds password change and password finding functionalities. The changes are well-structured, introducing new services, controllers, and DTOs. My review has identified critical security vulnerabilities (IDOR) in the new controller endpoints that must be addressed, as well as a critical bug related to incorrect string comparison. Additionally, I've provided several suggestions to improve code quality and maintainability by removing redundant code and adhering to Java naming conventions.

UserJpaEntity user = userJpaRepository.findById(userId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.USER_NOT_FOUND));

if (user.getName() != request.name() || user.getLoginId() != request.loginId()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

String objects are being compared using !=. In Java, == and != compare object references, not the actual string content. This can lead to incorrect behavior where two strings with the same content are considered different because they are different objects in memory. You should use the .equals() method for string comparison. Additionally, since user.getName() and request.name() could potentially be null, using java.util.Objects.equals() is a safer approach to prevent NullPointerExceptions.

Suggested change
if (user.getName() != request.name() || user.getLoginId() != request.loginId()) {
if (!java.util.Objects.equals(user.getName(), request.name()) || !java.util.Objects.equals(user.getLoginId(), request.loginId())) {

Comment on lines +26 to +29
public ResponseEntity<ApiResponseWrapper<ChangePasswordResponse>> changePassword(
@RequestParam Long userId,
@RequestBody @Valid ChangePasswordRequest request
) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The endpoint /user/me/password suggests that the password change should apply to the currently authenticated user. However, the userId is taken from a @RequestParam. This is a serious security vulnerability known as Insecure Direct Object Reference (IDOR), as it allows any authenticated user to attempt to change the password of any other user by simply providing their userId. The user's ID should be retrieved from the security context instead of being passed as a request parameter.

Comment on lines +36 to +39
public ResponseEntity<ApiResponseWrapper<Void>> findPassword(
@RequestParam Long userId,
@RequestBody @Valid FindPasswordRequest request
) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Similar to the changePassword endpoint, /user/me/find-password takes userId as a @RequestParam, which is a critical Insecure Direct Object Reference (IDOR) vulnerability. The user's identity should be confirmed via the security context, not a request parameter that can be manipulated by a malicious user.

@NotNull String id,
@PasswordPattern @NotNull String password
@Schema(description = "아이디는 6~12자의 영문, 숫자, 특수문자(-, _)만 사용 가능합니다.", example = "user123", required = true)
@LoginIdPattern @NotNull String id,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The custom annotation @LoginIdPattern is already meta-annotated with @NotBlank. The @NotBlank annotation handles null checks, so the @NotNull annotation here is redundant and can be removed for better code clarity.

Suggested change
@LoginIdPattern @NotNull String id,
@LoginIdPattern String id,

Comment on lines 19 to 20
@LoginIdPattern
@NotNull String id,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The custom annotation @LoginIdPattern is already meta-annotated with @NotBlank. The @NotBlank annotation handles null checks, so the @NotNull annotation on the next line is redundant and can be removed for better code clarity.

Suggested change
@LoginIdPattern
@NotNull String id,
@LoginIdPattern String id,

public interface MyUserControllerDocs {

@Operation(summary = "비밀번호 변경", description = "현재 로그인한 사용자의 비밀번호를 변경합니다. 새 비밀번호와 새 비밀번호 확인이 일치해야 합니다.(입력값으로 valificationCode 필드 추가 예정)")
public ResponseEntity<ApiResponseWrapper<ChangePasswordResponse>> changePassword(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In a Java interface, methods are public by default. The public access modifier is redundant here and can be removed to improve code clarity and adhere to common conventions.

Suggested change
public ResponseEntity<ApiResponseWrapper<ChangePasswordResponse>> changePassword(
ResponseEntity<ApiResponseWrapper<ChangePasswordResponse>> changePassword(

);

@Operation(summary = "비밀번호 찾기", description = "사용자의 비밀번호를 찾기 위한 API입니다. (반환값으로 전화번호 본인인증 후 valificationCode 필드 추가 예정)")
public ResponseEntity<ApiResponseWrapper<Void>> findPassword(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In a Java interface, methods are public by default. The public access modifier is redundant here and can be removed to improve code clarity and adhere to common conventions.

Suggested change
public ResponseEntity<ApiResponseWrapper<Void>> findPassword(
ResponseEntity<ApiResponseWrapper<Void>> findPassword(

@LoginIdPattern
String loginId,
@PhoneNumberPattern
String PhoneNumber

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to Java naming conventions, field names (and record components) should be in camelCase. The component PhoneNumber should be renamed to phoneNumber.

Suggested change
String PhoneNumber
String phoneNumber

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
src/main/java/life/mosu/mosuserver/presentation/user/dto/ChangePasswordResponse.java (1)

3-9: Consider enhancing response with more detailed information.

The implementation is clean and follows good patterns. However, consider if a simple success boolean provides sufficient feedback for password change operations. You might want to include additional context like timestamp or operation details for better user experience.

src/main/java/life/mosu/mosuserver/presentation/user/MyUserControllerDocs.java (2)

15-15: Consider removing implementation details from API documentation.

The description mentions future implementation details ("입력값으로 valificationCode 필드 추가 예정") which may confuse current API consumers and should be tracked in development documentation instead.

Consider simplifying the description:

-@Operation(summary = "비밀번호 변경", description = "현재 로그인한 사용자의 비밀번호를 변경합니다. 새 비밀번호와 새 비밀번호 확인이 일치해야 합니다.(입력값으로 valificationCode 필드 추가 예정)")
+@Operation(summary = "비밀번호 변경", description = "현재 로그인한 사용자의 비밀번호를 변경합니다. 새 비밀번호와 새 비밀번호 확인이 일치해야 합니다.")

21-21: Consider removing implementation details from API documentation.

Similar to the password change endpoint, the description contains future implementation details that should be in development documentation.

Consider simplifying the description:

-@Operation(summary = "비밀번호 찾기", description = "사용자의 비밀번호를 찾기 위한 API입니다. (반환값으로 전화번호 본인인증 후 valificationCode 필드 추가 예정)")
+@Operation(summary = "비밀번호 찾기", description = "사용자의 비밀번호를 찾기 위한 API입니다.")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8682ea and fe2863e.

📒 Files selected for processing (12)
  • src/main/java/life/mosu/mosuserver/application/user/MyUserService.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/user/UserJpaEntity.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/annotation/LoginIdPattern.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/auth/dto/LoginRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/auth/dto/SignUpRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/MyUserController.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/MyUserControllerDocs.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/ChangePasswordRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/ChangePasswordResponse.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/FindPasswordRequest.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/life/mosu/mosuserver/application/user/MyUserService.java (1)
src/main/java/life/mosu/mosuserver/global/util/EncodeUtil.java (1)
  • EncodeUtil (5-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
🔇 Additional comments (9)
src/main/java/life/mosu/mosuserver/global/annotation/PhoneNumberPattern.java (1)

5-5: LGTM! Good enhancement to phone number validation.

Adding @NotBlank alongside the existing @Pattern constraint creates a comprehensive validation that ensures phone numbers are both non-empty and properly formatted. This follows Spring validation best practices for composite annotations.

Also applies to: 16-16

src/main/java/life/mosu/mosuserver/presentation/user/dto/ChangePasswordRequest.java (1)

6-14: LGTM! Clean and well-validated DTO implementation.

The record structure is concise and appropriate for this use case. The validation annotations (@NotBlank and @PasswordPattern) ensure proper input validation, and the Korean error message provides clear user feedback.

src/main/java/life/mosu/mosuserver/global/annotation/LoginIdPattern.java (1)

12-24: LGTM! Well-structured validation annotation.

The annotation correctly combines @Pattern and @NotBlank constraints with appropriate regex for login ID validation. The 6-12 character length restriction and allowed character set (alphanumeric, hyphen, underscore) provide good security boundaries while maintaining usability. Korean error messages are consistent with the application's localization.

src/main/java/life/mosu/mosuserver/domain/user/UserJpaEntity.java (1)

92-94: LGTM! Appropriately simple domain entity method.

The changePassword method correctly focuses on state management without including validation or encoding logic, which should be handled at the service layer. This follows good domain-driven design principles by keeping the entity focused on its core responsibility.

src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java (1)

31-31: LGTM! Appropriate error code addition.

The USER_INFO_INVALID error code follows established patterns with correct HTTP status (BAD_REQUEST) and clear Korean error message. It's properly placed in the user-related error section and will be useful for password recovery validation scenarios.

src/main/java/life/mosu/mosuserver/presentation/auth/dto/SignUpRequest.java (1)

10-10: LGTM! Validation standardization improves consistency.

The addition of @LoginIdPattern annotation and updated schema description enhances input validation consistency across the application.

Also applies to: 16-16, 19-19

src/main/java/life/mosu/mosuserver/presentation/auth/dto/LoginRequest.java (1)

3-3: Excellent enhancement of API documentation and validation.

The addition of Swagger annotations and @LoginIdPattern validation improves both API documentation and input validation consistency. The schema descriptions provide clear guidance for API consumers.

Also applies to: 5-5, 9-12

src/main/java/life/mosu/mosuserver/application/user/MyUserService.java (1)

1-23: LGTM: Clean service structure and dependencies.

The service class is well-structured with proper dependency injection, appropriate annotations, and clear separation of concerns.

src/main/java/life/mosu/mosuserver/presentation/user/MyUserControllerDocs.java (1)

12-26: LGTM: Well-structured API documentation interface.

The interface properly uses Swagger annotations and provides clear API documentation. The method signatures are consistent with the service layer implementation.

import life.mosu.mosuserver.global.annotation.PhoneNumberPattern;

public record FindPasswordRequest(
String name,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation annotation for the name field.

The name field lacks validation which could lead to security issues or data quality problems. Consider adding appropriate validation such as @NotBlank and potentially a pattern constraint.

-        String name,
+        @NotBlank String name,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String name,
@NotBlank String name,
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/presentation/user/dto/FindPasswordRequest.java
at line 7, the name field lacks validation annotations. Add @NotBlank to ensure
the field is not empty or null, and consider adding a @Pattern annotation with a
suitable regex to enforce allowed characters or format for the name. Import the
necessary validation annotations from javax.validation.constraints.

Comment on lines +18 to +21
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class MyUserController implements MyUserControllerDocs {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add security annotations and implement authentication extraction.

The controller lacks security annotations and proper authentication handling for sensitive operations.

+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;

+@PreAuthorize("hasRole('USER')")
 @RestController
 @RequestMapping("/user")
 @RequiredArgsConstructor
 public class MyUserController implements MyUserControllerDocs {

Add a helper method to extract authenticated user ID:

private Long getCurrentAuthenticatedUserId() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    // Extract user ID from authentication principal
    // Implementation depends on your authentication setup
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/user/MyUserController.java
around lines 18 to 21, the controller is missing security annotations and a
method to extract the authenticated user's ID. Add appropriate security
annotations such as @PreAuthorize or @Secured to protect sensitive endpoints.
Implement a private helper method getCurrentAuthenticatedUserId() that retrieves
the Authentication object from SecurityContextHolder and extracts the user ID
from the principal according to your authentication setup.

Comment on lines +25 to +33
@PostMapping("/me/password")
public ResponseEntity<ApiResponseWrapper<ChangePasswordResponse>> changePassword(
@RequestParam Long userId,
@RequestBody @Valid ChangePasswordRequest request
) {
ChangePasswordResponse response = myUserService.changePassword(userId, request);

return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "비밀번호 변경 성공", response));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical security concern: Implement proper authentication and authorization.

The password change endpoint accepts userId as a request parameter, which is a serious security vulnerability. Users could potentially change other users' passwords. The userId should be extracted from the authenticated user's context, not from request parameters.

 @PostMapping("/me/password")
 public ResponseEntity<ApiResponseWrapper<ChangePasswordResponse>> changePassword(
-        @RequestParam Long userId,
         @RequestBody @Valid ChangePasswordRequest request
 ) {
+    // Extract userId from SecurityContext/Authentication
+    Long userId = getCurrentAuthenticatedUserId();
     ChangePasswordResponse response = myUserService.changePassword(userId, request);

Also consider adding:

  • Rate limiting to prevent brute force attacks
  • Audit logging for password change events
  • Additional security headers

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/user/MyUserController.java
around lines 25 to 33, remove the userId parameter from the method signature and
instead retrieve the authenticated user's ID from the security context or
authentication principal. This ensures only the logged-in user can change their
own password. Additionally, implement rate limiting on this endpoint, add audit
logging for password changes, and configure appropriate security headers in the
response to enhance protection.

Comment on lines +35 to +43
@PostMapping("/me/find-password")
public ResponseEntity<ApiResponseWrapper<Void>> findPassword(
@RequestParam Long userId,
@RequestBody @Valid FindPasswordRequest request
) {
myUserService.findPassword(userId, request);

return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "비밀번호 찾기 요청 성공"));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security vulnerability: Protect password recovery endpoint.

Similar to the password change endpoint, accepting userId as a parameter for password recovery is unsafe. This endpoint should also extract the user ID from the authentication context.

 @PostMapping("/me/find-password")
 public ResponseEntity<ApiResponseWrapper<Void>> findPassword(
-        @RequestParam Long userId,
         @RequestBody @Valid FindPasswordRequest request
 ) {
+    // Extract userId from SecurityContext or validate against authenticated user
+    Long userId = getCurrentAuthenticatedUserId();
     myUserService.findPassword(userId, request);

Additional security measures needed:

  • Rate limiting for password recovery attempts
  • CAPTCHA or similar anti-automation measures
  • Audit logging for security events

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/user/MyUserController.java
lines 35 to 43, the findPassword endpoint currently accepts userId as a request
parameter, which is a security risk. Modify the method to extract the user ID
from the authenticated user's security context instead of accepting it as a
parameter. Additionally, implement rate limiting, CAPTCHA or similar
anti-automation mechanisms, and audit logging for password recovery attempts to
enhance security.

Comment on lines +39 to +41
if (user.getName() != request.name() || user.getLoginId() != request.loginId()) {
throw new CustomRuntimeException(ErrorCode.USER_INFO_INVALID);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add null safety checks.

The current implementation could throw NullPointerException if user.getName(), user.getLoginId(), request.name(), or request.loginId() return null.

Consider using Objects.equals() for null-safe comparison:

+import java.util.Objects;

-if (!user.getName().equals(request.name()) || !user.getLoginId().equals(request.loginId())) {
+if (!Objects.equals(user.getName(), request.name()) || !Objects.equals(user.getLoginId(), request.loginId())) {
     throw new CustomRuntimeException(ErrorCode.USER_INFO_INVALID);
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/user/MyUserService.java around
lines 39 to 41, the current equality checks between user.getName() and
request.name(), as well as user.getLoginId() and request.loginId(), can throw
NullPointerException if any of these values are null. Replace the '!='
comparisons with Objects.equals() method calls to safely compare these values
while handling nulls without exceptions.

Comment on lines +24 to +32
@Transactional
public ChangePasswordResponse changePassword(Long userId, ChangePasswordRequest request) {
var user = userJpaRepository.findById(userId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.USER_NOT_FOUND));

user.changePassword(passwordEncode(encoder, request.newPassword()));

return ChangePasswordResponse.from(Boolean.TRUE);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add current password verification for security.

The changePassword method directly changes the password without verifying the user's current password. This poses a security risk if the user session is compromised.

Consider adding current password verification:

 @Transactional
-public ChangePasswordResponse changePassword(Long userId, ChangePasswordRequest request) {
+public ChangePasswordResponse changePassword(Long userId, ChangePasswordRequest request) {
     var user = userJpaRepository.findById(userId)
             .orElseThrow(() -> new CustomRuntimeException(ErrorCode.USER_NOT_FOUND));
 
+    if (!encoder.matches(request.currentPassword(), user.getPassword())) {
+        throw new CustomRuntimeException(ErrorCode.INVALID_CURRENT_PASSWORD);
+    }
+
     user.changePassword(passwordEncode(encoder, request.newPassword()));
 
     return ChangePasswordResponse.from(Boolean.TRUE);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Transactional
public ChangePasswordResponse changePassword(Long userId, ChangePasswordRequest request) {
var user = userJpaRepository.findById(userId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.USER_NOT_FOUND));
user.changePassword(passwordEncode(encoder, request.newPassword()));
return ChangePasswordResponse.from(Boolean.TRUE);
}
@Transactional
public ChangePasswordResponse changePassword(Long userId, ChangePasswordRequest request) {
var user = userJpaRepository.findById(userId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.USER_NOT_FOUND));
if (!encoder.matches(request.currentPassword(), user.getPassword())) {
throw new CustomRuntimeException(ErrorCode.INVALID_CURRENT_PASSWORD);
}
user.changePassword(passwordEncode(encoder, request.newPassword()));
return ChangePasswordResponse.from(Boolean.TRUE);
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/user/MyUserService.java
between lines 24 and 32, the changePassword method lacks verification of the
user's current password before allowing a password change. To fix this, add a
check that compares the provided current password from the request with the
stored password using the encoder before proceeding. If the current password
does not match, throw an appropriate exception to prevent unauthorized password
changes.

UserJpaEntity user = userJpaRepository.findById(userId)
.orElseThrow(() -> new CustomRuntimeException(ErrorCode.USER_NOT_FOUND));

if (user.getName() != request.name() || user.getLoginId() != request.loginId()) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix string comparison logic bug.

Using != for string comparison will compare object references, not content. This will cause the validation to fail even when the strings are equal.

Apply this fix to use proper string comparison:

-if (user.getName() != request.name() || user.getLoginId() != request.loginId()) {
+if (!user.getName().equals(request.name()) || !user.getLoginId().equals(request.loginId())) {
     throw new CustomRuntimeException(ErrorCode.USER_INFO_INVALID);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (user.getName() != request.name() || user.getLoginId() != request.loginId()) {
if (!user.getName().equals(request.name()) || !user.getLoginId().equals(request.loginId())) {
throw new CustomRuntimeException(ErrorCode.USER_INFO_INVALID);
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/user/MyUserService.java at
line 39, the code uses '!=' to compare strings, which compares references
instead of content. Replace the '!=' comparisons with calls to the .equals()
method for proper string content comparison, ensuring the validation logic
correctly compares the string values.

@wlgns12370 wlgns12370 merged commit fa6623c into develop Jul 13, 2025
1 of 2 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java (1)

12-12: Consider expanding the special character set for better user experience.

The current regex limits special characters to [@$!%*?&], which might be too restrictive for users. Consider including commonly used special characters like -, _, +, =, ., , to improve user experience while maintaining security.

-@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,20}$", message = "비밀번호 형식이 올바르지 않습니다.")
+@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&_\\-+=.,])[A-Za-z\\d@$!%*?&_\\-+=.,]{8,20}$", message = "비밀번호 형식이 올바르지 않습니다.")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe2863e and e8e145f.

📒 Files selected for processing (5)
  • src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/auth/dto/LoginRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/auth/dto/SignUpRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/ChangePasswordRequest.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/FindPasswordRequest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/FindPasswordRequest.java
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/ChangePasswordRequest.java
  • src/main/java/life/mosu/mosuserver/presentation/auth/dto/LoginRequest.java
  • src/main/java/life/mosu/mosuserver/presentation/auth/dto/SignUpRequest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-and-deploy
🔇 Additional comments (2)
src/main/java/life/mosu/mosuserver/global/annotation/PasswordPattern.java (2)

5-5: Excellent addition of @notblank constraint!

Adding @NotBlank alongside @Pattern is a best practice that ensures password fields cannot be null, empty, or contain only whitespace characters, which @Pattern alone might not catch in all scenarios.

Also applies to: 13-13


12-12: Password regex validated – no changes required

All existing test cases passed as expected. The pattern correctly enforces at least one lowercase, one uppercase, one digit, and one of the allowed special characters, within 8–20 characters.

Optional improvement:

  • Add boundary tests for exact lengths:
    • Exactly 8 characters with minimal requirements (e.g., "aA1@aaaa") → should pass
    • Exactly 20 characters with minimal requirements (e.g., "Ab1@Ab1@Ab1@Ab1@Ab1@") → should pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[🛠 기능 구현] 비밀번호 변경

1 participant