Skip to content

admin 접근 Slack에서 Discord 인터랙션으로 이관 (Ed25519 + allowlist·채널 게이트) - #668

Merged
sevineleven merged 9 commits into
devfrom
refactor/654-admin-discord-migration
Jul 3, 2026
Merged

admin 접근 Slack에서 Discord 인터랙션으로 이관 (Ed25519 + allowlist·채널 게이트)#668
sevineleven merged 9 commits into
devfrom
refactor/654-admin-discord-migration

Conversation

@sevineleven

@sevineleven sevineleven commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Situation

Task

  • 진입 표면(Slack 슬래시커맨드)만 Discord 인터랙션으로 갈아끼운다. grant 링크·IP 캡처·세션·게이트 필터는 그대로 재사용.
  • Discord 는 응답 본문에 grant 링크를 담아 돌려주므로 "누가 받을 수 있나"가 더 중요해진다. allowlist(userId)와 전용 채널 게이트를 새로 둔다.

Action

검증·게이트 (신규)

  • Ed25519 서명 검증(DiscordInteractionVerifier): Discord 가 {timestamp}{body} 를 앱 개인키로 서명해 보낸다. BouncyCastle 로 raw 32B 공개키를 검증(JDK EdEC raw-key 디코딩이 까다로워 BC). 3분 replay 윈도우 - 캡처한 유효 요청을 재전송해 응답으로 grant 링크를 빼가는 걸 막는다.
  • allowlist 게이트: 허용된 Discord userId(DISCORD_ADMIN_USER_IDS)만 링크·관리 명령 사용. 아니면 거부 embed.
  • 채널 게이트: 지정된 admin 채널(DISCORD_ADMIN_CHANNEL_ID)에서만. 봇이 여러 채널에 있어도 admin 인터랙션을 한 채널로 국한. 미설정이면 fail-closed.

컨트롤러 (교체)

  • DiscordAccessController: PING(type1)→PONG(엔드포인트 등록 검증), 커맨드(type2) → 서명·채널·allowlist 게이트 → ephemeral embed 로 grant/list/revoke/allow. 응답이 ephemeral 이라 링크가 채널에 안 샌다.
  • grant 링크 클릭(/admin-access/grant)·IP 캡처·세션은 진입 표면과 무관해 그대로 둔다.

Slack 제거 + 설정 전환

  • SlackAccessController·SlackSignatureVerifier·테스트 삭제, SlackIdentityGrantIdentity.
  • AdminProperties/application.yml: slackSigningSecretdiscordPublicKey·discordAdminUserIds·discordAdminChannelId. grant 링크 수명 5분→3분.
  • deploy.yml 브릭 가드: dev/staging 도메인을 여는 유일 경로라, SLACK_ADMIN_SIGNING_SECRET 하나 체크에서 Discord admin 3종(공개키·allowlist·채널)이 모두 있어야 배포되게. env·docker run 배선도 전환.

채택 안 한 안

이유
OAuth2 로그인 URL 직접접근+로그인 버튼 UX 라 원타임링크 흐름을 버려야 하고, 기존 IP 캡처·도메인 잠금과 안 맞음 → 원타임링크+allowlist 유지

Result

  • 검증: JDK25 컴파일 통과 + DiscordInteractionVerifierTest(BC 키페어로 실제 서명·검증 - 유효/본문변조/다른키/replay 3분 경계/미설정 등 11케이스) 통과.
  • 통합테스트는 두지 않음: admin 빈이 @ConditionalOnAdminEnabled(테스트 컨텍스트 off)라 로드 안 되고, 켜면 컨텍스트 캐시가 깨진다. verifier 단위테스트로 커버(Slack 시절과 동일 결). 근거는 discord-admin-setup.md 에.
  • 후속(별개): 배포 후 슬래시커맨드 등록 + Interactions Endpoint URL 등록(discord-admin-setup.md). 의연 allowlist 추가(의연(디자이너) Discord admin allowlist 추가 (디코 입장 후) #667). Slack 봇·채널 완전 철거(문서·템플릿 Discord 정리 #655).

연관 이슈

Summary by CodeRabbit

  • New Features

    • 관리자 접근이 Slack 기반에서 Discord 기반으로 전환되어, 새 슬래시 커맨드로 승인·차단·허용 목록 관리가 가능해졌습니다.
    • Discord 관리자 설정 값과 접근 가이드가 추가되어 초기 운영 설정이 쉬워졌습니다.
  • Bug Fixes

    • 관리자 게이트 검증이 Discord 설정 누락 시 즉시 실패하도록 강화되었습니다.
    • 서명 검증 방식이 개선되어 재전송 및 위변조 요청을 더 엄격하게 차단합니다.
  • Tests

    • Discord 서명 검증의 성공/실패 케이스를 폭넓게 검증하는 테스트가 추가되었습니다.

- bcprov-jdk18on 1.80 (Spring Boot 4 BOM 미관리, Maven Central 최신 안정)
- raw 32B 공개키를 Ed25519PublicKeyParameters 로 다루려 BC 사용 (JDK EdEC raw-key 디코딩 까다로움)
- 진입 표면만 Slack→Discord 교체, grant 링크·IP 캡처·세션·게이트 필터는 재사용
- DiscordInteractionVerifier: Ed25519 검증 + 3분 replay 윈도우(캡처 재전송 시 링크 유출 방지)
- DiscordAccessController: PING→PONG·서명검증·채널 게이트·allowlist 게이트·ephemeral embed(grant/list/revoke/allow)
- Slack 완전 제거: 컨트롤러·verifier·테스트 삭제, SlackIdentity→GrantIdentity
- AdminProperties/application.yml: slackSigningSecret → discordPublicKey·adminUserIds·adminChannelId, grantTokenTtl 5m→3m
- deploy.yml: 브릭 가드·env·docker run 의 SLACK_ADMIN_SIGNING_SECRET → DISCORD 3종
- discord-admin-setup.md: 슬래시커맨드·endpoint URL 운영 셋업 문서
@sevineleven sevineleven added the refactor 구조 개선, 외부 동작 불변 label Jul 3, 2026
@sevineleven sevineleven linked an issue Jul 3, 2026 that may be closed by this pull request
5 tasks
@sevineleven sevineleven self-assigned this Jul 3, 2026
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sevineleven, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 9c31d6b8-a8dc-4060-9818-4b32045a902d

📥 Commits

Reviewing files that changed from the base of the PR and between b78a8f8 and acbd89c.

📒 Files selected for processing (12)
  • .github/workflows/deploy.yml
  • build.gradle.kts
  • src/main/kotlin/com/depromeet/piki/admin/access/AdminAccessFilter.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/AdminAllowlistService.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/AdminSession.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/DiscordAccessController.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/GrantTokenCodec.kt
  • src/main/kotlin/com/depromeet/piki/admin/announcement/AdminAnnouncementController.kt
  • src/main/kotlin/com/depromeet/piki/admin/config/AdminProperties.kt
  • src/main/kotlin/com/depromeet/piki/admin/template/AdminTemplateController.kt
  • src/main/resources/application.yml
  • src/test/kotlin/com/depromeet/piki/admin/access/GrantTokenCodecTest.kt

Walkthrough

Admin 백오피스 접근용 슬래시커맨드 인증을 Slack HMAC-SHA256 방식에서 Discord Ed25519 서명검증으로 전환했다. AdminProperties/application.yml의 시크릿 설정, DiscordInteractionVerifier, DiscordAccessController를 새로 추가하고 기존 Slack 관련 컴포넌트(SlackAccessController, SlackSignatureVerifier 및 테스트)를 제거했으며, 배포 워크플로우 가드와 운영 문서도 함께 갱신했다.

Changes

Slack → Discord Admin 접근 제어 전환

Layer / File(s) Summary
Discord admin 설정 계약
src/main/kotlin/.../AdminProperties.kt, src/main/resources/application.yml
slackSigningSecret을 제거하고 discordPublicKey, discordAdminUserIds, discordAdminChannelId를 추가했으며, grantTokenTtl 기본값을 5분에서 3분으로 변경했다.
Ed25519 서명 검증기 및 테스트
build.gradle.kts, .../DiscordInteractionVerifier.kt, .../DiscordInteractionVerifierTest.kt
BouncyCastle 의존성을 추가하고 timestamp+rawBody 기반 Ed25519 서명 검증(180초 재전송 방지 윈도우 포함)을 구현하고 단위테스트로 검증했다.
DiscordAccessController 및 allowlist 신원 변경
.../DiscordAccessController.kt, .../AdminAllowlistService.kt
/admin-access/discord, /admin-access/grant 엔드포인트를 신설해 PING 응답, 채널/유저 allowlist 검증, list/revoke/allow 서브커맨드 처리, 원타임 grant 링크 발급 및 세션 수립을 구현했고, SlackIdentityGrantIdentity로 변경했다.
배포 워크플로우 secret 가드 전환
.github/workflows/deploy.yml
env gate 필수 시크릿 검증, 컨테이너 환경변수 주입, SSH fail-fast 조건, 실패 알림 문구를 Discord admin 시크릿 3종 기반으로 변경했다.
운영 셋업 문서
discord-admin-setup.md
슬래시커맨드 등록 절차, 필요 시크릿, interactions URL 등록, 동작 확인 시나리오, 테스트 커버리지 메모를 문서화했다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Discord
  participant DiscordAccessController
  participant DiscordInteractionVerifier
  participant AdminAllowlistService
  participant AdminSession

  Discord->>DiscordAccessController: POST /admin-access/discord
  DiscordAccessController->>DiscordInteractionVerifier: verify(signature, timestamp, rawBody)
  DiscordInteractionVerifier-->>DiscordAccessController: true/false
  alt 서명 실패
    DiscordAccessController-->>Discord: 401
  else PING
    DiscordAccessController-->>Discord: PONG
  else 허용된 채널/유저
    DiscordAccessController->>AdminAllowlistService: issueGrantToken(userId, name)
    AdminAllowlistService-->>DiscordAccessController: token
    DiscordAccessController-->>Discord: grant 링크 embed
    Discord->>DiscordAccessController: GET /admin-access/grant?token=...
    DiscordAccessController->>AdminAllowlistService: consumeGrantToken(token)
    AdminAllowlistService-->>DiscordAccessController: GrantIdentity
    DiscordAccessController->>AdminAllowlistService: allow(ip)
    DiscordAccessController->>AdminSession: establish()
    DiscordAccessController-->>Discord: /admin 리다이렉트
  else 미허용
    DiscordAccessController-->>Discord: 접근 거부 embed
  end
Loading

Assessment against linked issues

Objective Addressed Explanation
Slack SlackSignatureVerifier(HMAC-SHA256) → Discord Ed25519 검증(X-Signature-Ed25519/X-Signature-Timestamp) 구현 (#654)
SlackAccessController(/admin-access/slack) → Discord interactions 엔드포인트 전환 (#654)
AdminProperties.slackSigningSecretdiscordPublicKey 및 관련 설정 교체 (#654)
AdminAllowlistService(slackUserId|slackName) → discord 용어/타입 전환, allowlist grant/revoke 흐름 동일 유지 (#654)
application.yml/deploy.yml secret 교체(SLACK_ADMIN_SIGNING_SECRETDISCORD_PUBLIC_KEY 등) (#654)
로컬 bypass(localBypass) 동작 유지 (#654) 제공된 요약에 localBypass 관련 변경 여부가 언급되지 않아 확인 불가

Out-of-scope changes

Code Change Explanation
AdminAnnouncementController/AdminTemplateControllerslackName 운영자 표기 변경 (이슈 체크리스트 언급) 요약에 해당 파일 변경 내역이 포함되어 있지 않아 이슈에서 요구한 항목이 이번 PR 범위에 반영되었는지 확인 불가

Suggested labels: feature, admin, security

Suggested reviewers: 백엔드 팀 시니어 리뷰어


💬 한마디: Slack 시그니처에서 Ed25519로 갈아탄 솜씨, 서명검증계의 이사(移徙)치고는 꽤 깔끔하네요! 다만 DiscordAccessController가 라우팅·검증·응답 포맷팅까지 다 떠안고 있어서, 서브커맨드 핸들러 분리(전략 패턴이나 커맨드 맵 활용)를 고려해보면 테스트하기도, 나중에 서브커맨드 추가하기도 훨씬 편해질 거예요. Kotlin 공식 문서의 sealed class 기반 커맨드 패턴 참고해보세요!

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/654-admin-discord-migration

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.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다.

@sevineleven

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@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: 1

🧹 Nitpick comments (4)
discord-admin-setup.md (1)

44-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

코드펜스에 언어 지정이 빠졌습니다(MD040).

markdownlint가 잡아준 부분인데, 고치는 데 몇 초 안 걸리니 같이 정리하시죠.

🔧 제안
-```
+```text
 https://<prod API 도메인>/admin-access/discord
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @discord-admin-setup.md around lines 44 - 46, MD040 is triggered because the
fenced block in the Discord admin setup doc has no language tag; update the code
fence around the admin URL example to use an explicit language identifier,
matching the markdownlint suggestion, so the snippet is recognized correctly.


</details>

<!-- cr-comment:v1:d4bb9d2aa6470c92b92126a5 -->

_Source: Linters/SAST tools_

</blockquote></details>
<details>
<summary>src/main/kotlin/com/depromeet/piki/admin/config/AdminProperties.kt (1)</summary><blockquote>

`36-41`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _⚡ Quick win_

**`toString()`에서 `discordAdminChannelId`가 빠졌습니다.**

KDoc(라인 16)에서 `discordAdminChannelId`가 비면 fail-closed로 전부 거부된다고 명시할 만큼 중요한 설정인데, 정작 `toString()` 출력에는 포함되지 않았습니다. 배포 후 "채널 게이트가 왜 항상 막히지?"를 디버깅할 때 로그만 봐서는 이 값이 설정됐는지 확인할 방법이 없습니다. `discordPublicKey`, `discordAdminUserIds`와 같은 패턴(민감하지 않은 수준으로 마스킹)으로 같이 노출해주시죠.

<details>
<summary>🔧 제안</summary>

```diff
     override fun toString(): String =
         "AdminProperties(enabled=$enabled, environmentGate=$environmentGate, " +
             "discordPublicKey=${if (discordPublicKey.isBlank()) "<none>" else "<set>"}, " +
             "discordAdminUserIds=${discordAdminUserIds.size} ids, " +
+            "discordAdminChannelId=${if (discordAdminChannelId.isBlank()) "<none>" else "<set>"}, " +
             "allowlistTtl=$allowlistTtl, grantTokenTtl=$grantTokenTtl, localBypass=$localBypass)"
🤖 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 `@src/main/kotlin/com/depromeet/piki/admin/config/AdminProperties.kt` around
lines 36 - 41, `AdminProperties.toString()`에서 `discordAdminChannelId`가 누락되어 있어
채널 게이트 설정 상태를 로그로 확인할 수 없습니다. `AdminProperties`의 `toString()` 구현에
`discordPublicKey`와 `discordAdminUserIds`처럼 민감도에 맞게 마스킹한 `discordAdminChannelId`
출력 항목을 추가하고, 비어 있으면 `<none>`, 설정됐으면 `<set>` 같은 형태로 표시되도록 수정하세요.
src/main/kotlin/com/depromeet/piki/admin/access/DiscordAccessController.kt (1)

48-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

컨트롤러 라우팅 로직 자체는 테스트가 전혀 없습니다.

discord-admin-setup.md(61번 줄)에 "컨트롤러 통합테스트는 @ConditionalOnAdminEnabled가 테스트 컨텍스트에서 꺼져 있어 안 둔다"고 적혀 있는데, 이건 @SpringBootTest통합 테스트 얘기입니다. 이 컨트롤러는 생성자 주입 구조라 Spring 컨텍스트 없이 DiscordAccessController(mockVerifier, mockAllowlistService, mockAuditService, adminProperties, ObjectMapper())로 직접 인스턴스화하고 MockHttpServletRequest/MockHttpServletResponse(spring-test에 이미 의존성 있음)를 넘겨 .discord()를 호출하는 순수 단위테스트는 컨텍스트 캐시 문제와 무관하게 가능합니다.

PING/채널게이트/allowlist게이트/서브커맨드 라우팅(list·revoke·allow·grant)처럼 분기가 많은 곳이라, verifier만 단위테스트로 커버하고 컨트롤러 라우팅은 완전 무테스트로 두는 건 회귀 위험이 좀 있어 보입니다.

🤖 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 `@src/main/kotlin/com/depromeet/piki/admin/access/DiscordAccessController.kt`
around lines 48 - 94, `DiscordAccessController.discord`의 라우팅 분기에는 단위테스트가 없어서
PING, 채널 게이트, allowlist 게이트, 그리고 `firstOption`/`optionValue` 기반 서브커맨드 분기(list,
revoke, allow, 기본 grant link)를 직접 검증할 수 없습니다. Spring 컨텍스트 없이
`DiscordAccessController`를 생성자 주입으로 직접 인스턴스화하고, `MockHttpServletRequest`와
`MockHttpServletResponse`로 `.discord()`를 호출하는 순수 단위테스트를 추가해 각 분기에서 기대하는 응답과
status, 그리고 `auditService.record` 호출 여부를 검증하세요.
build.gradle.kts (1)

57-60: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

bcprov-jdk18on:1.80에 알려진 CVE 있음 — 최신 1.84로 올리는 걸 권장합니다.

1.80 버전은 Direct vulnerabilities: CVE-2026-5598CVE-2026-0636 대상입니다. CVE-2026-0636은 Affected versions of this package are vulnerable to LDAP Injection via the parseDN handling and the LDAP store helpers in X509LDAPCertStoreSpi and LDAPStoreHelper. CVE-2026-5598은 Non-constant time comparisons risk private key leakage in FrodoKEM.

지금 코드는 Ed25519Signer/Ed25519PublicKeyParameters만 써서 LDAP·FrodoKEM 경로와는 무관하지만, 어차피 Newer Version Available · 1.80→1.84 · (0 changes) 이므로 굳이 취약 버전에 머물 이유가 없습니다. SCA/의존성 스캐너(예: Dependabot, Snyk)에서 계속 알림이 뜰 테니 미리 올려두는 게 마음 편합니다.

🔧 제안
-    implementation("org.bouncycastle:bcprov-jdk18on:1.80")
+    implementation("org.bouncycastle:bcprov-jdk18on:1.84")

버전업 시 Ed25519 관련 API(Ed25519PublicKeyParameters, Ed25519Signer) 시그니처 변경 여부는 확인 부탁드립니다.

🤖 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 `@build.gradle.kts` around lines 57 - 60, The BouncyCastle dependency in
build.gradle.kts is pinned to a vulnerable version, so update the bcprov-jdk18on
artifact from 1.80 to 1.84. Keep the existing Ed25519 usage intact, but verify
that the symbols used here and in the related signature verification code
(`Ed25519Signer`, `Ed25519PublicKeyParameters`) still compile and behave the
same after the version bump.
🤖 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 @.github/workflows/deploy.yml:
- Line 702: The `secret_guard` failure label is inconsistent between the alert
text and the deployment summary, so update the summary block that still says `❌
중단 (Slack signing secret 누락 가드)` to match the renamed Discord admin secret
wording. Use the existing `CAUSE` handling and the `secret_guard` summary logic
in the deploy workflow to replace the stale Slack reference with the same
Discord admin secret message used elsewhere so both reports describe the same
failure consistently.

---

Nitpick comments:
In `@build.gradle.kts`:
- Around line 57-60: The BouncyCastle dependency in build.gradle.kts is pinned
to a vulnerable version, so update the bcprov-jdk18on artifact from 1.80 to
1.84. Keep the existing Ed25519 usage intact, but verify that the symbols used
here and in the related signature verification code (`Ed25519Signer`,
`Ed25519PublicKeyParameters`) still compile and behave the same after the
version bump.

In `@discord-admin-setup.md`:
- Around line 44-46: MD040 is triggered because the fenced block in the Discord
admin setup doc has no language tag; update the code fence around the admin URL
example to use an explicit language identifier, matching the markdownlint
suggestion, so the snippet is recognized correctly.

In `@src/main/kotlin/com/depromeet/piki/admin/access/DiscordAccessController.kt`:
- Around line 48-94: `DiscordAccessController.discord`의 라우팅 분기에는 단위테스트가 없어서
PING, 채널 게이트, allowlist 게이트, 그리고 `firstOption`/`optionValue` 기반 서브커맨드 분기(list,
revoke, allow, 기본 grant link)를 직접 검증할 수 없습니다. Spring 컨텍스트 없이
`DiscordAccessController`를 생성자 주입으로 직접 인스턴스화하고, `MockHttpServletRequest`와
`MockHttpServletResponse`로 `.discord()`를 호출하는 순수 단위테스트를 추가해 각 분기에서 기대하는 응답과
status, 그리고 `auditService.record` 호출 여부를 검증하세요.

In `@src/main/kotlin/com/depromeet/piki/admin/config/AdminProperties.kt`:
- Around line 36-41: `AdminProperties.toString()`에서 `discordAdminChannelId`가
누락되어 있어 채널 게이트 설정 상태를 로그로 확인할 수 없습니다. `AdminProperties`의 `toString()` 구현에
`discordPublicKey`와 `discordAdminUserIds`처럼 민감도에 맞게 마스킹한 `discordAdminChannelId`
출력 항목을 추가하고, 비어 있으면 `<none>`, 설정됐으면 `<set>` 같은 형태로 표시되도록 수정하세요.
🪄 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.yml

Review profile: CHILL

Plan: Pro

Run ID: 185fda0c-c4f5-40a5-a5cb-5c8dcfca239d

📥 Commits

Reviewing files that changed from the base of the PR and between d05ea43 and b78a8f8.

📒 Files selected for processing (12)
  • .github/workflows/deploy.yml
  • build.gradle.kts
  • discord-admin-setup.md
  • src/main/kotlin/com/depromeet/piki/admin/access/AdminAllowlistService.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/DiscordAccessController.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/DiscordInteractionVerifier.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/SlackAccessController.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/SlackSignatureVerifier.kt
  • src/main/kotlin/com/depromeet/piki/admin/config/AdminProperties.kt
  • src/main/resources/application.yml
  • src/test/kotlin/com/depromeet/piki/admin/access/DiscordInteractionVerifierTest.kt
  • src/test/kotlin/com/depromeet/piki/admin/access/SlackSignatureVerifierTest.kt
💤 Files with no reviewable changes (3)
  • src/main/kotlin/com/depromeet/piki/admin/access/SlackSignatureVerifier.kt
  • src/test/kotlin/com/depromeet/piki/admin/access/SlackSignatureVerifierTest.kt
  • src/main/kotlin/com/depromeet/piki/admin/access/SlackAccessController.kt

Comment thread .github/workflows/deploy.yml
@github-actions
github-actions Bot requested a review from m-a-king July 3, 2026 07:11
sevineleven and others added 5 commits July 3, 2026 16:19
- Discord 앱은 인터랙션 엔드포인트가 앱당 1개라 Slack 처럼 커맨드별 URL 로 환경 분리 불가. /piki-admin env:<dev|staging|prod> 로 한 커맨드에서 환경 골라 그 env 링크 발급
- grant 토큰 Redis랜덤(env별) → HMAC 서명 stateless(GrantTokenCodec). 발급 env 와 소비 env 가 달라도 공유키로 검증, env 바인딩 + nonce setIfAbsent 로 one-time
- AdminProperties/application.yml: environment·grantSigningKey·grantHosts
- deploy.yml: ADMIN_ENVIRONMENT + ADMIN_GRANT_SIGNING_KEY(브릭 가드 4번째) 배선
- list/revoke 는 cross-env allowlist 관리라 후속. GrantTokenCodecTest 7케이스
- AdminSession: slackName/slackUserId → actorName/userId. 로그 actor 값은 이미 Discord username 이고 네이밍만 slack 잔재였음
- AdminAccessFilter·AdminAnnouncementController·AdminTemplateController 호출부·주석
- deploy.yml: secret_guard 실패 요약 STATUS 가 여전히 "Slack signing secret 누락 가드" 이던 것 → "Discord admin secret" (CAUSE 와 일치). 놓친 주석 하나도 Discord 로
- AdminProperties.toString: discordAdminChannelId·environment 노출 추가, grantSigningKey 는 크리덴셜이라 set 여부만 마스킹
- bcprov-jdk18on 1.80 → 1.84 (알려진 CVE·최신 안정, canonical maven-metadata 로 확인 — solrsearch 는 stale 이었음)
- discord-admin-setup.md: 코드펜스 언어 지정(text) 추가 (MD040)
@sevineleven

Copy link
Copy Markdown
Collaborator Author

CodeRabbit review body nitpick 4건 처리 (대부분 b79c937):

  • 문서 코드펜스 언어(MD040) — accept. endpoint URL 블록에 text 지정 추가.
  • AdminProperties.toString 에 discordAdminChannelId 누락 — accept. discordAdminChannelId·environment 노출 추가, 신규 크리덴셜 grantSigningKey 는 set 여부만 마스킹.
  • bcprov 1.80 CVE → 1.84 — accept. canonical maven-metadata.xml 로 latest=1.84 확인(solrsearch API 가 stale 하게 1.80 을 주고 있었음) 후 반영. 컴파일·테스트 통과.
  • 컨트롤러 단위테스트 부재declined. 지적대로 생성자 주입이라 mock 으로 인스턴스화는 가능하지만, 이 레포 테스트 컨벤션(CLAUDE.md testing-convention)이 내부 컴포넌트 모킹을 금지합니다. 컨트롤러의 협력자(DiscordInteractionVerifier·AdminAllowlistService·AdminAuditService)는 전부 내부 컴포넌트라 mock 하면 컨벤션 위반입니다. 위험 로직(Ed25519 서명검증·grant 토큰 HMAC)은 DiscordInteractionVerifierTest·GrantTokenCodecTest(pure 단위, 모킹 없음)로 이미 망라했고, 컨트롤러는 얇은 라우팅(parse→gate→delegate)이라 gap 을 감수합니다. (admin 빈이 테스트 컨텍스트에서 off 라 통합테스트도 불가)

- username(핸들, 예 sevineleven) 대신 Discord 서버 별명 → 표시이름 → 핸들 순 fallback 으로 사람이 알아보기 쉬운 이름을 로그·감사에 남긴다
- admin Discord 슬래시커맨드 등록·엔드포인트 URL 설정은 일회성 운영 절차라 리포에 두지 않고 추적 해제. 절차는 이슈 #650 코멘트로 이관해 보존
@sevineleven
sevineleven merged commit bdbc6ff into dev Jul 3, 2026
6 checks passed
@sevineleven
sevineleven deleted the refactor/654-admin-discord-migration branch July 3, 2026 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor 구조 개선, 외부 동작 불변

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin 슬래시커맨드 Discord 이관 (Ed25519 서명검증)

1 participant