Skip to content

Feature/auth password reset with OCI Email SDK#139

Merged
snu-BuMinSeong merged 9 commits into
developfrom
feature/auth-password-reset
May 27, 2026
Merged

Feature/auth password reset with OCI Email SDK#139
snu-BuMinSeong merged 9 commits into
developfrom
feature/auth-password-reset

Conversation

@snu-BuMinSeong

@snu-BuMinSeong snu-BuMinSeong commented May 17, 2026

Copy link
Copy Markdown
Contributor

변경 사항

  • 비밀번호 찾기/재설정 API 3종 추가
    • POST /api/v3/auth/password-reset/email
    • POST /api/v3/auth/password-reset/verify
    • POST /api/v3/auth/password-reset
  • 비밀번호 재설정 인증번호 저장용 password_reset_verification 테이블 추가
  • OCI Email Delivery SDK 기반 메일 전송 서비스 추가
  • 인증번호 생성, 해시 저장, 만료 처리, 시도 횟수 제한, 사용 완료 처리 로직 추가
  • 비밀번호 재설정 서비스 테스트 및 메일 SDK payload 테스트 추가

구현 상세

  • 인증번호 원문은 DB에 저장하지 않고 PasswordService로 해시하여 저장합니다.
  • 인증번호 유효 시간은 10분입니다.
  • 인증번호 검증 실패는 최대 5회까지 허용합니다.
  • 존재하지 않는 이메일로 인증번호 전송 요청이 들어와도 계정 존재 여부가 노출되지 않도록 정상 응답합니다.
  • 메일 발송은 SMTP credential 없이 oci.email_data_plane.EmailDPClient.submit_email()을 사용합니다.
  • Instance Principal signer 생성이 실패할 때만 로컬 OCI config(oci.config.from_file())로 fallback합니다.
  • 비밀번호 재설정 인증번호는 DB에 먼저 저장된 뒤 메일로 발송되어, 저장 실패 시 검증 불가능한 코드가 발송되지 않습니다.

필요한 운영 설정

OCI Email Delivery를 설정한 뒤 아래 secret/env 값을 등록해야 합니다.

email_compartment_id
email_from_email
email_from_name        # optional
email_reply_to         # optional
email_service_endpoint # optional

mjy926 and others added 4 commits March 18, 2026 16:44
Co-authored-by: Minseong Bu <80934034+snu-BuMinSeong@users.noreply.github.com>
@snu-BuMinSeong snu-BuMinSeong requested a review from a team as a code owner May 17, 2026 11:53
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@snu-BuMinSeong, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 1 review of capacity. Refill in 46 minutes and 39 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 50e902b0-1d32-4c20-b65b-6fc36c166eef

📥 Commits

Reviewing files that changed from the base of the PR and between 7d459f4 and c003097.

📒 Files selected for processing (3)
  • wacruit/src/apps/mail/exceptions.py
  • wacruit/src/apps/mail/services.py
  • wacruit/src/tests/mail/test_mail_service.py
📝 Walkthrough

워크스루

이 PR은 비밀번호 재설정 기능을 구현합니다. 이메일 기반 검증 코드를 통해 사용자가 비밀번호를 재설정할 수 있도록 인증 시스템을 확장하며, 메일 서비스 인프라, 데이터베이스 모델, 리포지토리 메서드, 비즈니스 로직, API 엔드포인트 및 테스트를 추가합니다.

변경 사항

비밀번호 재설정 플로우

Layer / File(s) 설명
데이터 모델 및 계약 정의
wacruit/src/apps/auth/schemas.py, wacruit/src/apps/auth/exceptions.py, wacruit/src/apps/auth/models.py, wacruit/src/settings.py
비밀번호 재설정 요청 스키마 3종(PasswordResetEmailRequest, PasswordResetVerifyRequest, PasswordResetRequest), 도메인 예외 3개(InvalidPasswordResetCodeException, ExpiredPasswordResetCodeException, PasswordResetCodeNotVerifiedException), ORM 모델 PasswordResetVerification(이메일, 코드 해시, 만료/검증/사용 시각, 시도 횟수, 생성 시각 포함)을 정의합니다.
메일 서비스 인프라
wacruit/src/apps/mail/config.py, wacruit/src/apps/mail/exceptions.py, wacruit/src/apps/mail/services.py
MailConfig로 환경변수/시크릿 매니저에서 설정을 로드하고, OCI Email Data Plane을 사용해 이메일을 제출하는 EmailService를 구현합니다. 메일 관련 예외(MailConfigException, MailSendFailedException)를 추가합니다.
리포지토리 데이터 접근 메서드
wacruit/src/apps/auth/repositories.py
AuthRepository에 사용자/검증 레코드 관리 메서드를 추가합니다: update_user, create_password_reset_verification, replace_active_password_reset_for_email, get_latest_password_reset_verification, update_password_reset_verification, consume_password_reset_verification, expire_unused_password_reset_verifications.
서비스 비즈니스 로직
wacruit/src/apps/auth/services.py
AuthService에 비밀번호 재설정 관련 메서드를 추가합니다: send_password_reset_email, verify_password_reset_code, reset_password, _generate_password_reset_code, _get_valid_password_reset_verification. 시도 횟수 제한·만료 검사·해시 비교 로직이 포함됩니다.
API 엔드포인트 및 라우팅
wacruit/src/apps/auth/views.py
비밀번호 재설정 API 엔드포인트 3개를 추가합니다: /v3/auth/password-reset/email, /v3/auth/password-reset/verify, /v3/auth/password-reset. 각 엔드포인트는 대응하는 서비스 메서드를 호출하고 예외 응답 매핑을 지정합니다.
데이터베이스 마이그레이션
wacruit/src/database/migrations/versions/2026_05_17_0000-2f9c8e0a1b34_add_password_reset_verification.py
password_reset_verification 테이블을 생성하고 이메일 인덱스를 추가합니다.
테스트 및 검증
wacruit/src/tests/auth/conftest.py, wacruit/src/tests/auth/test_auth_password_reset_service.py, wacruit/src/tests/mail/test_mail_service.py
테스트용 더미 FakeAuthRepository, FakeEmailService, 픽스처 및 AuthService의 비밀번호 재설정 플로우(전송/검증/재설정) 단위 테스트와 이메일 페이로드 제출 검증 테스트를 추가합니다.
모듈 내보내기 및 초기화
wacruit/src/apps/auth/__init__.py, wacruit/src/database/__init__.py
router__all__로 공개하고, 새 모델을 데이터베이스 모델 레지스트리에 포함시키기 위한 임포트를 추가합니다.

🎯 평가

🎯 3 (중간) | ⏱️ ~25분

관련된 가능성이 있는 PR

제안 검토자

  • deveroskp
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 변경사항의 핵심인 비밀번호 재설정 기능 추가와 OCI Email SDK 통합을 명확히 반영합니다.
Description check ✅ Passed PR 설명은 추가된 API 엔드포인트, 테이블, 메일 서비스 구현, 상세 동작 로직 및 필요한 운영 설정을 포괄적으로 다룹니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/auth-password-reset

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
wacruit/src/tests/auth/test_auth_password_reset_service.py (1)

66-82: ⚡ Quick win

시도 횟수 상한(5회) 경계 테스트를 추가해 주세요.

현재는 오답 1회 증가만 검증해서, “최대 5회” 규칙의 경계(5회/6회) 회귀를 잡기 어렵습니다. 이 케이스를 추가하면 보안 규칙 검증 신뢰도가 크게 올라갑니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacruit/src/tests/auth/test_auth_password_reset_service.py` around lines 66 -
82, Add a boundary test that simulates repeated wrong password-reset codes to
exercise the 5-attempt limit: call auth_service._generate_password_reset_code =
lambda: "123456" and
auth_service.send_password_reset_email(EmailStr(user.email)) once, then call
auth_service.verify_password_reset_code(EmailStr(user.email), "000000") five
times asserting InvalidPasswordResetCodeException each time and that
auth_repository.get_latest_password_reset_verification(EmailStr(user.email)).attempt_count
increments up to 5, then call it a sixth time and assert the expected
locked/blocked behavior (e.g., still raises InvalidPasswordResetCodeException or
a specific lock exception and that attempt_count == 6 or a locked flag is set)
to cover the 5/6 boundary for verify_password_reset_code and related repository
state.
wacruit/src/database/migrations/versions/2026_05_17_0000-2f9c8e0a1b34_add_password_reset_verification.py (1)

18-49: 운영 관점에서 만료 레코드 정리 정책을 함께 두는 것을 권장합니다.

password_reset_verification는 이메일(PII)을 저장하므로, 만료/사용 완료 레코드를 주기적으로 정리하는 배치(또는 TTL성 정책)와 관련 모니터링을 같이 정의해두면 장기 운영 리스크를 줄일 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@wacruit/src/database/migrations/versions/2026_05_17_0000-2f9c8e0a1b34_add_password_reset_verification.py`
around lines 18 - 49, Add an operational cleanup policy for the
password_reset_verification table: in upgrade() create an index on expires_at
(and optionally on verified_at/used_at) to make TTL deletions efficient, and add
a migration comment or companion SQL to create a DB-side TTL/partitioning rule
or background job that periodically deletes rows where expires_at < now() OR
used_at IS NOT NULL OR verified_at IS NOT NULL; also add/update
monitoring/alerting hooks (e.g., a metric name or housekeeping job entry) so the
batch run success/failure and retention counts for password_reset_verification
are tracked. Ensure references to the table and columns use the exact names
password_reset_verification, expires_at, used_at, verified_at, and the migration
functions upgrade()/downgrade() so reviewers can locate the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wacruit/src/apps/auth/schemas.py`:
- Line 32: 스키마의 new_password Field(new_password: str = Field(...,
max_length=50))에 최소 길이 검증이 빠져있으니 Field에 min_length를 추가해 재설정 계약에 맞는 최소 길이(예:
min_length=8 또는 프로젝트 규정값)를 명시하세요; 해당 변경은 wacruit/src/apps/auth/schemas.py의
new_password 선언을 수정하여 적용하면 됩니다.

In `@wacruit/src/apps/auth/services.py`:
- Around line 114-122: The current flow in send_password_reset_code path uses
AuthService methods expire_unused_password_reset_verifications and
create_password_reset_verification separately which can race and invalidate the
just-sent code; introduce a new repository method (e.g.,
auth_repository.replace_active_password_reset_for_email) that, inside a DB
transaction, atomically expires any existing active PasswordResetVerification
rows for the email and inserts the new PasswordResetVerification (with code_hash
from PasswordService.hash_password and expires_at), then update the service to
call that single transactional method instead of calling
expire_unused_password_reset_verifications and
create_password_reset_verification separately after
self.email_service.send_password_reset_code.
- Around line 129-141: The reset_password flow can race because verification is
checked and consumed in separate steps; change reset_password to perform a
single atomic DB transaction or acquire a row lock when consuming the
verification so the check-and-mark-used and the user password update happen
together. Specifically, in reset_password replace the current pattern of calling
_get_valid_password_reset_verification then later setting verification.used_at
and calling auth_repository.update_password_reset_verification /
auth_repository.update_user with a repository method or transaction block that:
re-queries the verification FOR UPDATE (or equivalent), verifies it is unused,
sets used_at, updates the user's hashed password
(PasswordService.hash_password), and commits both updates in one transaction so
a concurrent request cannot pass the check.

In `@wacruit/src/apps/mail/services.py`:
- Around line 35-40: The SMTP client can hang because no socket timeout is set;
update the smtplib.SMTP call to pass a timeout (e.g., timeout =
getattr(mail_config, "timeout", 10)) so the connection and subsequent calls
(smtplib.SMTP(...), smtp.starttls(), smtp.login(...), smtp.send_message(...))
will fail fast; ensure mail_config exposes a configurable timeout or use a
sensible default and propagate that value into the smtplib.SMTP(...) constructor
call.

---

Nitpick comments:
In
`@wacruit/src/database/migrations/versions/2026_05_17_0000-2f9c8e0a1b34_add_password_reset_verification.py`:
- Around line 18-49: Add an operational cleanup policy for the
password_reset_verification table: in upgrade() create an index on expires_at
(and optionally on verified_at/used_at) to make TTL deletions efficient, and add
a migration comment or companion SQL to create a DB-side TTL/partitioning rule
or background job that periodically deletes rows where expires_at < now() OR
used_at IS NOT NULL OR verified_at IS NOT NULL; also add/update
monitoring/alerting hooks (e.g., a metric name or housekeeping job entry) so the
batch run success/failure and retention counts for password_reset_verification
are tracked. Ensure references to the table and columns use the exact names
password_reset_verification, expires_at, used_at, verified_at, and the migration
functions upgrade()/downgrade() so reviewers can locate the changes.

In `@wacruit/src/tests/auth/test_auth_password_reset_service.py`:
- Around line 66-82: Add a boundary test that simulates repeated wrong
password-reset codes to exercise the 5-attempt limit: call
auth_service._generate_password_reset_code = lambda: "123456" and
auth_service.send_password_reset_email(EmailStr(user.email)) once, then call
auth_service.verify_password_reset_code(EmailStr(user.email), "000000") five
times asserting InvalidPasswordResetCodeException each time and that
auth_repository.get_latest_password_reset_verification(EmailStr(user.email)).attempt_count
increments up to 5, then call it a sixth time and assert the expected
locked/blocked behavior (e.g., still raises InvalidPasswordResetCodeException or
a specific lock exception and that attempt_count == 6 or a locked flag is set)
to cover the 5/6 boundary for verify_password_reset_code and related repository
state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a681001-909b-4a46-b313-fbe7e387bf17

📥 Commits

Reviewing files that changed from the base of the PR and between a6c3b1e and 343cbb0.

📒 Files selected for processing (15)
  • wacruit/src/apps/auth/__init__.py
  • wacruit/src/apps/auth/exceptions.py
  • wacruit/src/apps/auth/models.py
  • wacruit/src/apps/auth/repositories.py
  • wacruit/src/apps/auth/schemas.py
  • wacruit/src/apps/auth/services.py
  • wacruit/src/apps/auth/views.py
  • wacruit/src/apps/mail/__init__.py
  • wacruit/src/apps/mail/config.py
  • wacruit/src/apps/mail/exceptions.py
  • wacruit/src/apps/mail/services.py
  • wacruit/src/database/__init__.py
  • wacruit/src/database/migrations/versions/2026_05_17_0000-2f9c8e0a1b34_add_password_reset_verification.py
  • wacruit/src/tests/auth/conftest.py
  • wacruit/src/tests/auth/test_auth_password_reset_service.py

Comment thread wacruit/src/apps/auth/schemas.py Outdated
Comment thread wacruit/src/apps/auth/services.py Outdated
Comment thread wacruit/src/apps/mail/services.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
wacruit/src/apps/auth/repositories.py (1)

42-61: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

동시 재발급이 아직 직렬화되지 않습니다.

이 메서드는 현재 unused row만 UPDATE한 뒤 새 row를 INSERT합니다. 같은 이메일로 두 요청이 동시에 들어오면 서로의 신규 row를 보지 못한 채 둘 다 active row를 남길 수 있고, 이후 검증은 최신 1건만 보므로 사용자에게 방금 도착한 코드가 즉시 무효화될 수 있습니다. 이메일 단위로 User row를 FOR UPDATE로 잠그거나 advisory lock을 잡아서 expire+insert를 직렬화해 주세요.

🔧 한 가지 가능한 방향
     ) -> PasswordResetVerification:
         with self.transaction:
+            (
+                self.session.query(User)
+                .where(User.email == email)
+                .with_for_update()
+                .first()
+            )
             (
                 self.session.query(PasswordResetVerification)
                 .where(
                     PasswordResetVerification.email == email,
                     PasswordResetVerification.used_at.is_(None),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacruit/src/apps/auth/repositories.py` around lines 42 - 61, The current
replace_active_password_reset_for_email function can race because it UPDATEs
unused PasswordResetVerification rows then INSERTs a new one without
serialization; wrap the update+insert in a transaction-scoped lock by either
selecting the owning User row FOR UPDATE (e.g., query User where
User.email==email and .with_for_update()) or acquiring a DB advisory lock keyed
by the email (pg_advisory_xact_lock using a hash of email) at the start of
replace_active_password_reset_for_email, then perform the existing .update(...)
and self.session.add(verification) while the lock/transaction is held so
expire+insert are serialized for that email (ensure to keep use of
self.transaction and rollback behavior unchanged).
wacruit/src/apps/auth/services.py (1)

139-146: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

reset 단계에서도 유효성 재검증을 락 안으로 넣어 주세요.

지금은 _get_valid_password_reset_verification() 결과를 신뢰한 뒤 별도 consume 호출로 넘어가서, 그 사이에 재발급 요청이 기존 row의 expires_at를 당기면 이미 무효화된 코드도 비밀번호 변경에 성공할 수 있습니다. consume_password_reset_verification()used_at뿐 아니라 verified_at is not nullexpires_at >= now까지 함께 확인하도록 옮기는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacruit/src/apps/auth/services.py` around lines 139 - 146, 현재 서비스에서
_get_valid_password_reset_verification() 결과를 신뢰하고 별도 consume 호출로 넘어가면 race
condition이 발생할 수 있으니, 검증 로직을 consume_password_reset_verification() 안으로 옮겨 DB
락(또는 트랜잭션 내 FOR UPDATE 같은 방식)으로 처리하세요:
consume_password_reset_verification(verification_id, email, hashed_pw, now) 내부에서
used_at IS NULL, verified_at IS NOT NULL, expires_at >= now 조건을 함께 확인하고
업데이트(used_at/hashed password 저장)를 시도해 성공 여부를 불리언으로 반환하도록 변경하고, 서비스 코드는 이전의
_get_valid... 결과를 최종 결정으로 사용하지 않고 consume의 반환값만으로 성공/실패를 판단하도록 수정하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wacruit/src/apps/auth/services.py`:
- Around line 114-123: Currently the code sends the reset email before
persisting the DB state, which can lead to an un-verifyable code if
replace_active_password_reset_for_email fails; update the sequence in the method
that calls send_password_reset_code and replace_active_password_reset_for_email
so you first create and persist PasswordResetVerification (using
PasswordService.hash_password(code) and
replace_active_password_reset_for_email(email, PasswordResetVerification(...),
now)) and only after that call
email_service.send_password_reset_code(str(email), code); ensure you still
compute expires_at and code beforehand and handle/report errors from
replace_active_password_reset_for_email before attempting to send the email.

In `@wacruit/src/apps/mail/services.py`:
- Around line 76-85: The try/except currently wraps both signer creation and
EmailDPClient initialization so any client creation errors (e.g. bad
settings.oci_region/endpoint) silently fall back to oci.config.from_file();
change it so only oci.auth.signers.InstancePrincipalsSecurityTokenSigner() is
inside the try and its Exception triggers the fallback to
oci.config.from_file(), while EmailDPClient(...) construction runs outside that
except block and is allowed to raise (fail-fast) if it errors; reference
InstancePrincipalsSecurityTokenSigner, EmailDPClient, settings.oci_region, and
oci.config.from_file when making the change.

---

Duplicate comments:
In `@wacruit/src/apps/auth/repositories.py`:
- Around line 42-61: The current replace_active_password_reset_for_email
function can race because it UPDATEs unused PasswordResetVerification rows then
INSERTs a new one without serialization; wrap the update+insert in a
transaction-scoped lock by either selecting the owning User row FOR UPDATE
(e.g., query User where User.email==email and .with_for_update()) or acquiring a
DB advisory lock keyed by the email (pg_advisory_xact_lock using a hash of
email) at the start of replace_active_password_reset_for_email, then perform the
existing .update(...) and self.session.add(verification) while the
lock/transaction is held so expire+insert are serialized for that email (ensure
to keep use of self.transaction and rollback behavior unchanged).

In `@wacruit/src/apps/auth/services.py`:
- Around line 139-146: 현재 서비스에서 _get_valid_password_reset_verification() 결과를
신뢰하고 별도 consume 호출로 넘어가면 race condition이 발생할 수 있으니, 검증 로직을
consume_password_reset_verification() 안으로 옮겨 DB 락(또는 트랜잭션 내 FOR UPDATE 같은 방식)으로
처리하세요: consume_password_reset_verification(verification_id, email, hashed_pw,
now) 내부에서 used_at IS NULL, verified_at IS NOT NULL, expires_at >= now 조건을 함께
확인하고 업데이트(used_at/hashed password 저장)를 시도해 성공 여부를 불리언으로 반환하도록 변경하고, 서비스 코드는 이전의
_get_valid... 결과를 최종 결정으로 사용하지 않고 consume의 반환값만으로 성공/실패를 판단하도록 수정하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a3d316a3-546d-4176-beef-005affec76f7

📥 Commits

Reviewing files that changed from the base of the PR and between 343cbb0 and daed57b.

📒 Files selected for processing (9)
  • wacruit/src/apps/auth/repositories.py
  • wacruit/src/apps/auth/schemas.py
  • wacruit/src/apps/auth/services.py
  • wacruit/src/apps/mail/config.py
  • wacruit/src/apps/mail/services.py
  • wacruit/src/settings.py
  • wacruit/src/tests/auth/conftest.py
  • wacruit/src/tests/auth/test_auth_password_reset_service.py
  • wacruit/src/tests/mail/test_mail_service.py
✅ Files skipped from review due to trivial changes (1)
  • wacruit/src/settings.py

Comment thread wacruit/src/apps/auth/services.py Outdated
Comment thread wacruit/src/apps/mail/services.py Outdated
@snu-BuMinSeong snu-BuMinSeong changed the title Feature/auth password reset Feature/auth password reset with OCI Email SDK May 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wacruit/src/apps/mail/services.py (1)

34-35: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

from_email 형식 검증을 추가해 설정 오류를 조기에 차단하세요.

Line 34-35는 빈 값만 검사하고 있어, @가 없는 값이 들어오면 Line 90-92에서 잘못된 message_id가 생성되고 502로 래핑될 수 있습니다. 설정 문제는 MailConfigException으로 fail-fast 하는 편이 안전합니다.

🔧 제안 수정
     def send_email(self, to_email: str, subject: str, content: str) -> None:
-        if not mail_config.compartment_id or not mail_config.from_email:
+        if (
+            not mail_config.compartment_id
+            or not mail_config.from_email
+            or "@" not in mail_config.from_email
+            or mail_config.from_email.rsplit("@", 1)[-1] == ""
+        ):
             raise MailConfigException()

Also applies to: 90-92

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wacruit/src/apps/mail/services.py` around lines 34 - 35, The code currently
only checks for presence of mail_config.compartment_id and
mail_config.from_email before raising MailConfigException; add a simple format
validation for mail_config.from_email (e.g., ensure it contains a single '@' and
a domain part) and raise MailConfigException with a clear message if invalid so
configuration errors fail-fast; update the validation near the existing check
referencing mail_config and MailConfigException and ensure this prevents invalid
values reaching the message_id generation code paths around the logic that
builds message_id (referenced in the file around lines handling message_id)
which currently can produce malformed IDs when from_email lacks '@'.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@wacruit/src/apps/mail/services.py`:
- Around line 34-35: The code currently only checks for presence of
mail_config.compartment_id and mail_config.from_email before raising
MailConfigException; add a simple format validation for mail_config.from_email
(e.g., ensure it contains a single '@' and a domain part) and raise
MailConfigException with a clear message if invalid so configuration errors
fail-fast; update the validation near the existing check referencing mail_config
and MailConfigException and ensure this prevents invalid values reaching the
message_id generation code paths around the logic that builds message_id
(referenced in the file around lines handling message_id) which currently can
produce malformed IDs when from_email lacks '@'.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6d0647b7-15b8-45c1-8198-4faaa97b018f

📥 Commits

Reviewing files that changed from the base of the PR and between daed57b and 7d459f4.

📒 Files selected for processing (3)
  • wacruit/src/apps/auth/services.py
  • wacruit/src/apps/mail/services.py
  • wacruit/src/tests/mail/test_mail_service.py

@snu-BuMinSeong snu-BuMinSeong merged commit a6e7e5a into develop May 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants