상품·주문·수동 결제 승인·권리·청구·축소·해지·AI Wallet 표시·사용량 배정 화면·경고 엔진 (§6·§13·§27)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
c90ac27596
commit
c397452acd
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* portal/commerce/opsauth.mjs — 운영자 콘솔(HAL)에서 들어오는 내부 호출(/internal/ops/*) 인증.
|
||||
*
|
||||
* 운영자 계정·세션은 HAL 에 있고(§25), HAL 이 포털의 내부 API 를 서비스 키로 호출한다(lib/serviceauth).
|
||||
* 비밀은 포털 보관소의 'service:<keyId>' 에 있다. 허용 키 id 는 'hal' 뿐이다.
|
||||
* 호출 주체 운영자 식별(감사 기록용)은 HAL 이 X-Ops-Actor 헤더로 전달한다(이름만, 비밀 없음).
|
||||
* 테스트에서는 deps.opsAuthBypass=true 로 서명 없이 부를 수 있다(운영 코드 경로에서는 절대 켜지 않는다).
|
||||
*/
|
||||
import { HttpError } from '../../lib/http.mjs';
|
||||
import { verifyCtx } from '../../lib/serviceauth.mjs';
|
||||
|
||||
const ALLOWED_KEYS = new Set(['hal']);
|
||||
/** 검증 후 { keyId, actor } 를 돌려준다. 실패는 401. */
|
||||
export async function requireOps(ctx, deps) {
|
||||
const actor = String(ctx.headers['x-ops-actor'] || 'operator').slice(0, 80);
|
||||
if (deps.opsAuthBypass) return { keyId: 'test', actor };
|
||||
const r = await verifyCtx(ctx, (id) => (ALLOWED_KEYS.has(id) ? deps.vault.get('service:' + id) : null));
|
||||
if (!r.ok) throw new HttpError(401, 'service_auth_failed', `운영자 콘솔 호출 인증 실패(${r.reason})`);
|
||||
return { keyId: r.keyId, actor };
|
||||
}
|
||||
Reference in New Issue
Block a user