Skip to content

feat: 근로자 보안 링크(Worker Link) 발급, 안내, 문서제출, 응답제출 구현 - #76

Merged
chaeliki merged 41 commits into
mainfrom
feat/7-worker-link
Aug 4, 2026
Merged

feat: 근로자 보안 링크(Worker Link) 발급, 안내, 문서제출, 응답제출 구현#76
chaeliki merged 41 commits into
mainfrom
feat/7-worker-link

Conversation

@chaeliki

@chaeliki chaeliki commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

왜 필요한가요?

Closes #7

#7(근로자 링크)의 API 4개를 구현했습니다.

완료된 범위

API 4개

  • POST /tasks/{taskId}/worker-link 근로자 링크 발급/재발급
  • GET /public/worker-links/{token} 근로자 공개 안내 조회
  • POST /public/worker-links/{token}/documents 근로자 문서 제출
  • POST /public/worker-links/{token}/responses 근로자 응답 제출

보안 설계

  • 토큰: SecureRandom(32바이트) + Base64 URL-safe + SHA-256 해싱
  • Tenant bootstrap: WorkerLinkTenantBootstrap (AuthTenantBootstrap 패턴, JPA + PostgreSQL SECURITY DEFINER 함수 구현 완료, RLS 정책 적용)
  • 승인된 현재 Task version에서만 발급 (ApprovalRequest.isValidFor() 재사용)
  • #13StoredFile/FileStorage 재사용, verified 필드로 AVAILABLE 판단
  • Cache-Control: no-store
  • 감사 로그 연동 (ActorType.WORKER_LINK)
  • 존재하지 않는/만료된/폐기된 링크를 410으로 통일 (존재 여부 비노출)
  • 근로자 QUESTION/NOT_UNDERSTOOD 응답 시 WorkerLink.conversationStatusNEEDS_FOLLOWUP으로 전환
  • Idempotency-Key(헤더 방식, Stripe 패턴 참고): 같은 (taskId, idempotency_key) 조합 재시도 시 기존 발급 결과 그대로 반환
  • GET(공개 조회) 접근 시에도 감사 로그 기록 (AuditAction.WORKER_LINK_ACCESSED)
  • Idempotency-Key 재시도 응답: worker_url=null, already_issued=true 반환
  • 문서 업로드(POST .../documents)도 clientRequestId 기반 멱등성 실제 구현
    (worker_document_upload_idempotency 테이블, 재시도 시 같은 결과 반환)
  • 공개 API 경로 /public/worker-links/**로 통일

발견하여 수정한 버그

  • SecurityConfig에 공개 API 경로 permitAll() 등록 누락 (실제로는 401로 막히던 문제)

통합 테스트

  • WorkerLinkSecurityIntegrationTest 4개 (전체 흐름, 미승인 Task 거부, 타 사업장 격리, 존재하지 않는 토큰)

결정 사항

  • AuditAction.WORKER_LINK_RESPONSE_SUBMITTED 추가, AuditTargetType은 기존 TASK 재사용 (ApprovalService 패턴 확인 후 결정)
  • Rate Limit/replay detection은 관련 인프라 자체가 없어, 후속이슈로
  • RLS 정책은 #34와 동일한 패턴worker_link/worker_response/worker_response_upload에 적용

아직 확인 필요 — 후속 조치

  • workerUrl 형식: /worker-portal 라우트만 확인됨, 정확한 파라미터 전달 방식은 미확정. 현재는 원문 토큰만 반환
  • document-request-draft 연동 #61 완료 후 처리 예정

검증

  • ./gradlew clean test 전체 통과
  • CORS 허용 헤더에 Idempotency-Key 추가, OPTIONS preflight 검증 및
    실제 서버 E2E 확인 완료
  • StoredFile.verify() 추가, 업로드 시 자동 검증 완료 처리

chaeliki added 28 commits August 4, 2026 09:42
@chaeliki
chaeliki force-pushed the feat/7-worker-link branch from 67a494c to 2ff71ff Compare August 4, 2026 00:45
@chaeliki
chaeliki requested a review from hywznn August 4, 2026 01:51
@chaeliki chaeliki added area:server Spring Boot API·도메인·DB·tenant·Task Workflow 영역; Prompt·모델·Provider 구현 제외 priority:P0 MVP 진행을 막는 최우선 핵심 작업 security:privacy 개인정보·접근권한·토큰·보안 영향이 있는 작업 type:feature 사용자 또는 Agent가 사용하는 기능 개발 labels Aug 4, 2026
@chaeliki
chaeliki requested a review from krestar August 4, 2026 02:01

@hywznn hywznn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

병합 전 아래 3가지를 맞춰 주세요. 모두 실제 Client 연동 또는 Accepted ADR과 직접 관련된 항목입니다.

  1. 공개 API canonical path: ADR-0002는 /public/worker-links/**를 정의하지만 현재 Controller·SecurityConfig는 /api/v1/public/worker-links/**입니다. ADR 기준으로 Controller, SecurityConfig, OpenAPI, 통합 테스트 경로를 함께 맞춰 주세요.
  2. 문서 업로드 멱등성: WorkerLinkDocumentControllerIdempotency-Key를 받지만 Command·Service에서 사용하지 않고, Service에도 clientRequestId 중복 방지가 TODO로 남아 있습니다. 현재 설명과 달리 같은 요청 재시도 시 파일이 중복 저장됩니다. 한 키 계약으로 실제 중복 저장을 막고 재시도 테스트를 추가해 주세요.
  3. 브라우저 CORS: CorsConfig 허용 헤더에 Idempotency-Key가 없어 Client preflight가 차단됩니다. 실제 브라우저 E2E에서 재현했습니다. 허용 헤더와 OPTIONS 통합 테스트를 추가해 주세요.

이 세 항목 반영 후 다시 확인하겠습니다.

Comment thread src/main/java/com/fowoco/server/audit/domain/AuditAction.java
Comment thread src/test/java/com/fowoco/server/workerlink/WorkerLinkSecurityIntegrationTest.java Outdated

@krestar krestar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

추가 테이블 관련 RLS 테스트는 추후에 제가 보강하겠습니다

@chaeliki

chaeliki commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

3가지 모두 반영했습니다!

  1. 공개 API 경로를 /public/worker-links/**로 변경
    (Controller, SecurityConfig, 통합 테스트 전부 수정)
  2. 문서 업로드 clientRequestId 멱등성 실제 구현
    • 새 테이블(worker_document_upload_idempotency)로 (worker_link_id,
      client_request_id) 조합 중복 방지
    • 재시도 시 같은 upload_id 반환하는 통합 테스트 추가
  3. CorsConfig 허용 헤더에 Idempotency-Key 추가
    • OPTIONS preflight 통합 테스트 추가
    • 실제 서버 띄워서 curl로도 확인함 (Access-Control-Allow-Headers:
      Idempotency-Key 정상 응답 확인)

다시 확인 부탁드립니다!

@chaeliki
chaeliki requested a review from hywznn August 4, 2026 06:50
Comment thread src/main/java/com/fowoco/server/common/config/SecurityConfig.java
Comment thread src/main/resources/db/migration/V11__create_worker_link.sql
@chaeliki
chaeliki merged commit 9f528a1 into main Aug 4, 2026
4 checks passed
@chaeliki
chaeliki deleted the feat/7-worker-link branch August 4, 2026 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:server Spring Boot API·도메인·DB·tenant·Task Workflow 영역; Prompt·모델·Provider 구현 제외 priority:P0 MVP 진행을 막는 최우선 핵심 작업 security:privacy 개인정보·접근권한·토큰·보안 영향이 있는 작업 type:feature 사용자 또는 Agent가 사용하는 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Worker Link] 로그인 없는 근로자 보안 링크와 응답 구현

3 participants