Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ plugins {
id 'io.spring.dependency-management' version '1.1.7'
id "org.sonarqube" version "6.2.0.5505"
id 'jacoco'
id 'org.flywaydb.flyway' version '11.10.0'

}

group = 'org.Runimo'
Expand Down Expand Up @@ -44,6 +46,8 @@ dependencies {

runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'


testImplementation 'io.rest-assured:rest-assured:5.4.0'
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/runimo/runimo/auth/domain/SignupToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.runimo.runimo.auth.exceptions.SignUpException;
import org.runimo.runimo.common.CreatedAuditEntity;
import org.runimo.runimo.user.domain.SocialProvider;
import org.runimo.runimo.user.enums.UserHttpResponseCode;

@Table(name = "signup_token")
@Entity
Expand All @@ -32,6 +35,13 @@ public class SignupToken extends CreatedAuditEntity {
@Enumerated(EnumType.STRING)
private SocialProvider socialProvider;

@Version
@Column(name = "version")
private Long version;

@Column(name = "used", nullable = false)
private Boolean used = false;

@Builder
public SignupToken(String token, String providerId, String refreshToken,
SocialProvider socialProvider) {
Expand All @@ -40,4 +50,11 @@ public SignupToken(String token, String providerId, String refreshToken,
this.refreshToken = refreshToken;
this.socialProvider = socialProvider;
}

public void markAsUsed() {
if (this.used) {
throw new SignUpException(UserHttpResponseCode.SIGNIN_FAIL_ALREADY_EXIST);
}
this.used = true;
}
Comment on lines +54 to +59
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix the error code for token reuse scenario.

The markAsUsed() method throws SIGNIN_FAIL_ALREADY_EXIST which seems inappropriate for a token reuse scenario. Consider using a more specific error code like TOKEN_ALREADY_USED or TOKEN_INVALID.

 public void markAsUsed() {
     if (this.used) {
-        throw new SignUpException(UserHttpResponseCode.SIGNIN_FAIL_ALREADY_EXIST);
+        throw new SignUpException(UserHttpResponseCode.TOKEN_INVALID);
     }
     this.used = 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
public void markAsUsed() {
if (this.used) {
throw new SignUpException(UserHttpResponseCode.SIGNIN_FAIL_ALREADY_EXIST);
}
this.used = true;
}
public void markAsUsed() {
if (this.used) {
throw new SignUpException(UserHttpResponseCode.TOKEN_INVALID);
}
this.used = true;
}
🤖 Prompt for AI Agents
In src/main/java/org/runimo/runimo/auth/domain/SignupToken.java lines 54 to 59,
the markAsUsed() method throws an incorrect error code SIGNIN_FAIL_ALREADY_EXIST
when the token is already used. Replace this error code with a more appropriate
one such as TOKEN_ALREADY_USED or TOKEN_INVALID to accurately reflect the token
reuse scenario.

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
public interface SignupTokenRepository extends JpaRepository<SignupToken, String> {

@Query(
"SELECT st FROM SignupToken st WHERE st.token = :token AND st.createdAt > :createdAtAfter"
"SELECT st FROM SignupToken st WHERE st.token = :token "
+ "AND st.createdAt > :createdAtAfter "
+ "AND st.used = false"
)
Optional<SignupToken> findByIdAndCreatedAtAfter(String token, LocalDateTime createdAtAfter);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,11 @@ public SignupUserResponse register(UserSignupCommand command) {
Egg grantedEgg = eggGrantService.grantGreetingEggToUser(savedUser);
EggType eggType = grantedEgg.getEggType();

removeSignupToken(payload.token());
signupToken.markAsUsed();
return new SignupUserResponse(savedUser, jwtTokenFactory.generateTokenPair(savedUser),
grantedEgg, eggType.getCode());
}

private void removeSignupToken(String token) {
signupTokenRepository.deleteByToken(token);
}

private SignupToken findUnExpiredSignupToken(String token) {
LocalDateTime cutOffTime = LocalDateTime.now().minusMinutes(REGISTER_CUTOFF_MIN);
return signupTokenRepository.
Expand Down
27 changes: 27 additions & 0 deletions src/main/resources/db/migration/V1__Create_baseline.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- V1__Create_baseline.sql
-- Production baseline - existing schema as of 2025-06-24
-- This migration represents the current state of the production database
-- All existing tables are already present, this is just a baseline marker

-- Flyway baseline marker for existing production database
-- No actual schema changes in this file as tables already exist

-- Existing tables:
-- - users
-- - user_token
-- - user_love_point
-- - oauth_account
-- - apple_user_token
-- - signup_token
-- - running_record
-- - item
-- - egg_type
-- - item_activity
-- - user_item
-- - incubating_egg
-- - runimo_definition
-- - runimo
-- - user_refresh_token

-- This baseline allows us to start versioned migrations from V2 onwards
SELECT 'Baseline migration - existing schema marked as V1' as message;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ALTER TABLE `signup_token`
ADD COLUMN `version` BIGINT NOT NULL DEFAULT 0;

ALTER TABLE `signup_token`
ADD COLUMN `used` BOOLEAN NOT NULL DEFAULT FALSE;

-- 기존 토큰들은 사용되지 않은 것으로 처리
UPDATE `signup_token`
SET `used` = FALSE,
`version` = 0;
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ void tearDown() {
}

@Test
void 중복_유저_회원가입_409응답() throws JsonProcessingException {
@DisplayName("중복된 유저를 회원가입 시도하면 401응답 - 동일한 토큰")
void 중복_유저_회원가입_401응답() throws JsonProcessingException {
AuthSignupRequest request = new AuthSignupRequest(token, "username", Gender.UNKNOWN);

given()
Expand All @@ -176,6 +177,44 @@ void tearDown() {
);
token = jwtTokenFactory.generateSignupTemporalToken("provider-id", SocialProvider.KAKAO,
"valid-token");
request = new AuthSignupRequest(token, "username", Gender.UNKNOWN);

given()
.contentType(ContentType.MULTIPART)
.multiPart("request", objectMapper.writeValueAsString(request))
.when()
.post("/api/v1/auth/signup")
.then()
.statusCode(HttpStatus.UNAUTHORIZED.value());

signupTokenRepository.delete(signupToken);
}

@Test
@DisplayName("중복된 유저를 회원가입 시도하면 409응답 - 동일한 토큰")
void 중복_유저_회원가입_409응답_다른토큰() throws JsonProcessingException {
AuthSignupRequest request = new AuthSignupRequest(token, "username", Gender.UNKNOWN);

given()
.contentType(ContentType.MULTIPART)
.multiPart("request", objectMapper.writeValueAsString(request))
.when()
.post("/api/v1/auth/signup")
.then()
.statusCode(HttpStatus.CREATED.value())
.log().all()
.body("payload.nickname", equalTo("username"))
.body("payload.token_pair.access_token", notNullValue())
.body("payload.token_pair.refresh_token", notNullValue());

SignupToken signupToken = new SignupToken(
"valid-token-2",
"provider-id",
"refresh-token",
SocialProvider.KAKAO
);
token = jwtTokenFactory.generateSignupTemporalToken("provider-id", SocialProvider.KAKAO,
"valid-token-2");
signupTokenRepository.save(signupToken);
request = new AuthSignupRequest(token, "username", Gender.UNKNOWN);

Expand Down
12 changes: 10 additions & 2 deletions src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,26 @@ spring:
url: jdbc:h2:mem:testdb;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

flyway:
enabled: true
locations: classpath:db/migration/h2
baseline-on-migrate: true
clean-disabled: false

jpa:
hibernate:
ddl-auto: none
open-in-view: false
show-sql: true
defer-datasource-initialization: true
defer-datasource-initialization: false

jackson:
property-naming-strategy: SNAKE_CASE

sql:
init:
mode: always
mode: never
schema-locations: classpath*:sql/schema.sql
data-locations:

Expand Down
Loading