A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
13 KiB
description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
| description | i18n_source_hash | i18n_provenance | i18n_output_hash | i18n_hash_version |
|---|---|---|---|---|
| SnapOtter에서 사용자, 기본 및 커스텀 역할, 권한, API 키, 팀, 세션, 감사 로그를 관리합니다. | bea8955f3aff | human | 387e5820c217 | 2 |
사용자, 역할 및 권한
SnapOtter는 기본 역할 3개, 세분화된 권한 17개, 그리고 선택적 도구별 접근 제어가 가능한 커스텀 역할을 제공합니다. 이 페이지는 전체 인가 모델, API 키 스코프, 팀 관리, 감사 로깅을 다룹니다.
::: tip 관련 페이지 OIDC / SSO | SAML SSO | SCIM 프로비저닝 | 보안 및 하드닝 :::
사용자
사용자 생성
관리자는 관리자 패널이나 POST /api/auth/register 엔드포인트를 통해 사용자를 생성할 수 있습니다. 각 사용자는 사용자 이름, 역할, 팀 배정, 그리고 선택적 이메일 주소를 가집니다.
기본 관리자
첫 시작 시 SnapOtter는 기본 관리자 계정을 생성합니다. 자격 증명은 환경 변수에서 옵니다:
| Variable | Default | Description |
|---|---|---|
DEFAULT_USERNAME |
admin |
초기 관리자 계정의 사용자 이름 |
DEFAULT_PASSWORD |
admin |
초기 관리자 계정의 비밀번호 |
기본 관리자는 첫 로그인 시 비밀번호를 변경해야 합니다.
인증 제공자
사용자는 여러 방법으로 인증할 수 있습니다:
- 로컬 - SnapOtter 데이터베이스에 저장된 사용자 이름과 비밀번호
- OIDC - 모든 OpenID Connect 제공자(OIDC / SSO 참고)
- SAML - SAML 2.0 ID 제공자(SAML SSO 참고)
- SCIM - ID 제공자로부터의 자동 프로비저닝(SCIM 프로비저닝 참고)
인증 비활성화
인증을 완전히 비활성화하려면 AUTH_ENABLED=false을 설정하세요. 이 모드에서는 admin 역할을 가진 가상의 익명 사용자가 모든 요청에 사용됩니다. 로그인이 필요하지 않습니다.
::: warning 인증을 비활성화하면 인스턴스에 접근할 수 있는 누구에게나 전체 관리자 권한이 부여됩니다. 신뢰할 수 있는 환경에서만 사용하세요. :::
기본 역할
SnapOtter에는 기본 역할 3개가 포함됩니다. 이들은 수정하거나 삭제할 수 없습니다.
Admin
17개 권한 전부. 인스턴스에 대한 완전한 제어.
tools:use files:own files:all apikeys:own apikeys:all pipelines:own pipelines:all settings:read settings:write users:manage teams:manage features:manage system:health audit:read compliance:manage webhooks:manage security:manage
Editor
7개 권한. 모든 도구를 사용하고 모든 파일과 파이프라인을 관리할 수 있으나 관리자 기능에는 접근할 수 없습니다.
tools:use files:own files:all apikeys:own pipelines:own pipelines:all settings:read
User
5개 권한. 도구를 사용하고 자신의 리소스를 관리할 수 있습니다.
tools:use files:own apikeys:own pipelines:own settings:read
권한 참조
| Permission | Description |
|---|---|
tools:use |
모든 처리 도구 사용 |
files:own |
자신의 파일 조회 및 관리 |
files:all |
모든 사용자의 파일 조회 및 관리 |
apikeys:own |
자신의 API 키 생성 및 관리 |
apikeys:all |
모든 사용자의 API 키 조회 |
pipelines:own |
자신의 파이프라인 생성 및 관리 |
pipelines:all |
모든 사용자의 파이프라인 조회 및 관리 |
settings:read |
인스턴스 설정 조회 |
settings:write |
인스턴스 설정 수정 |
users:manage |
행위자의 권한 범위 내에서 사용자 계정 생성 및 관리 |
teams:manage |
팀 생성, 업데이트, 삭제 |
features:manage |
AI 기능 번들 설치 및 관리 |
system:health |
상태 및 준비 엔드포인트 접근 |
audit:read |
감사 로그 조회 및 역할 목록 조회 |
compliance:manage |
GDPR 수명주기 및 규정 준수 기능을 관리합니다. 파괴적인 사용자 작업은 여전히 권한 제한을 받습니다. |
webhooks:manage |
아웃바운드 웹훅 구성 |
security:manage |
보안 설정 관리(IP 허용 목록, SSO 강제) |
커스텀 역할
security:manage 권한을 가진 관리자는 관리자 패널이나 역할 API를 통해 커스텀 역할을 생성할 수 있습니다. 역할 목록 조회에는 audit:read이 필요합니다.
커스텀 역할 생성
curl -X POST http://localhost:1349/api/v1/roles \
-H "Authorization: Bearer si_..." \
-H "Content-Type: application/json" \
-d '{
"name": "reviewer",
"description": "Can use tools and view all files",
"permissions": ["tools:use", "files:own", "files:all", "settings:read"]
}'
역할 이름은 2~30자여야 하며, 하이픈과 밑줄을 포함한 소문자 영숫자여야 합니다.
위임된 관리 경계
17개 권한 모두 사용자 정의 역할을 통해 위임할 수 있지만 관리 권한이 해당 역할을 기본 제공 admin 역할과 동일하게 만들지는 않습니다. users:manage에 의해 승인된 사용자 변이, compliance:manage에 의해 승인된 파괴적인 작업, security:manage에 의해 승인된 사용자 지정 역할 관리는 행위자의 현재 권한에 의해 제한됩니다.
- 기본 제공 역할은
admin>editor>user를 따릅니다. 사용자 정의 역할은 기본 제공 역할 아래에 있습니다. - 대상의 권한은 행위자의 유효 권한에 포함되어야 합니다. 따라서 범위가 지정된 API 키는 해당 범위에서 생략된 권한을 행사할 수 없습니다.
- 대상 역할의 도구 액세스는 행위자 자신의 도구 액세스에 포함되어야 합니다.
- 비활성화된 계정은 해당 역할이
disabled:<original-role>로 기록될 때 원래 역할과 비교하여 확인됩니다. - 사용자 정의 역할을 삭제하려면 내장된
user대체를 할당할 권한도 필요합니다. 비활성화된 멤버는disabled:user로 비활성화된 상태로 유지됩니다.
전역 자격 증명 및 구성이 더 엄격합니다. SCIM 토큰을 발급 또는 취소하고 인스턴스 구성을 가져오려면 완전한 유효 관리 권한이 있는 기본 제공 admin 역할이 필요합니다.
도구 수준 권한
커스텀 역할은 선택적으로 사용자가 접근할 수 있는 도구를 제한할 수 있습니다. 두 가지 모드를 사용할 수 있습니다:
| Mode | Behavior | License requirement |
|---|---|---|
category |
모달리티(image, video, audio, document, file)별 제한 | 없음(무료) |
tool |
개별 도구 ID별 제한 | per_tool_permissions 엔터프라이즈 기능 필요 |
tool 모드가 설정되었으나 엔터프라이즈 기능을 사용할 수 없는 경우, SnapOtter는 우아하게 저하되어 모든 도구에 대한 접근을 허용합니다.
{
"name": "image-only",
"permissions": ["tools:use", "files:own"],
"toolPermissions": {
"mode": "category",
"allowed": ["image"]
}
}
커스텀 역할 삭제
커스텀 역할이 삭제되면, 그 역할에 배정된 모든 사용자가 자동으로 user 역할로 재배정됩니다.
팀
팀은 스토리지 및 보존 관리를 위해 사용자를 그룹화합니다. 첫 시작 시 Default 팀이 생성됩니다.
| Field | Type | Description |
|---|---|---|
name |
string | 고유한 팀 이름(1~50자) |
storageQuota |
number | 팀별 스토리지 한도(바이트 단위, 엔터프라이즈 없이 작동) |
retentionHours |
number | 이 시간(시간 단위) 이후 출력 자동 삭제(team_retention_overrides 필요, 엔터프라이즈) |
legalHold |
boolean | 팀 구성원 파일의 자동 삭제 방지(legal_hold 필요, 엔터프라이즈) |
::: info
Default 팀은 삭제할 수 없습니다. 여전히 구성원이 있는 팀은 삭제할 수 없습니다. 먼저 구성원을 재배정하세요.
:::
API 키
사용자는 프로그래매틱 접근을 위해 API 키를 생성할 수 있습니다. 각 키는 si_ 접두사를 사용하며 생성 시 단 한 번만 표시됩니다.
스코프 권한
API 키는 선택적으로 permissions 배열을 가질 수 있습니다. 설정되면, 요청에 대한 유효 권한은 사용자 역할 권한과 키의 스코프 권한의 교집합입니다. 즉, API 키는 사용자 자신의 권한을 넘어 상승할 수 없습니다.
curl -X POST http://localhost:1349/api/v1/api-keys \
-H "Authorization: Bearer si_..." \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline key",
"permissions": ["tools:use", "files:own"],
"expiresAt": "2027-01-01T00:00:00Z"
}'
만료
키는 선택적 expiresAt 타임스탬프를 받습니다. 만료된 키는 인증 시점에 거부됩니다.
감사 로그
SnapOtter는 보안 관련 이벤트를 audit_log 데이터베이스 테이블에 저장된 구조화된 감사 로그에 기록합니다.
감사 로그 조회
GET /api/v1/audit-log?page=1&limit=50&action=LOGIN_FAILED&from=2026-01-01T00:00:00Z&to=2026-12-31T23:59:59Z
audit:read 권한이 필요합니다. 페이지네이션(page, limit)과 필터(action, ip, from, to)를 지원합니다.
도구 작업 감사
::: warning
TOOL_EXECUTED 이벤트는 기본적으로 로깅되지 않습니다. 두 경로 중 하나를 통해 옵트인됩니다:
auditToolOperations관리자 설정을true으로 설정합니다.audit_export기능(팀 및 엔터프라이즈 플랜 모두에서 사용 가능)이 포함된 활성 라이선스를 보유합니다.
이 중 하나가 없으면 개별 도구 실행은 감사 로그에 기록되지 않습니다. :::
내보내기
GET /api/v1/enterprise/audit/export?format=csv&from=2026-01-01T00:00:00Z
audit:read 권한과 audit_export 엔터프라이즈 기능(팀 및 엔터프라이즈 플랜 모두에서 사용 가능)이 필요합니다. CSV 및 JSON 형식을 지원하며, action, actorId, targetType, targetId, from, to로 필터링합니다.
변조 방지 서명
활성화되면 각 감사 로그 항목은 DATA_ENCRYPTION_KEY에서 파생된 HMAC로 서명됩니다. 이를 위해서는:
- 환경에
DATA_ENCRYPTION_KEY을 설정합니다. tamperResistantAudit관리자 설정을 활성화합니다.tamper_resistant_audit기능이 포함된 엔터프라이즈 라이선스가 필요합니다.
보존
오래된 항목을 자동으로 정리하려면 AUDIT_RETENTION_DAYS을 설정하세요. 기본값은 0이며, 이는 항목이 무기한 보관됨을 의미합니다.
이벤트 참조
| Event | Category |
|---|---|
LOGIN_SUCCESS, LOGIN_FAILED |
Authentication |
OIDC_LOGIN_SUCCESS, OIDC_LOGIN_FAILED |
Authentication |
SAML_LOGIN_SUCCESS, SAML_LOGIN_FAILED |
Authentication |
LOGOUT |
Authentication |
USER_CREATED, USER_UPDATED, USER_DELETED |
User management |
PASSWORD_CHANGED, PASSWORD_RESET |
User management |
MFA_ENROLLED, MFA_DISABLED, MFA_VERIFIED, MFA_VERIFY_FAILED |
MFA |
MFA_CHALLENGE_ISSUED, MFA_RECOVERY_USED, MFA_RESET |
MFA |
ROLE_CREATED, ROLE_UPDATED, ROLE_DELETED |
Roles |
API_KEY_CREATED, API_KEY_DELETED |
API keys |
SETTINGS_UPDATED, IP_ALLOWLIST_UPDATED |
Settings |
FILE_UPLOADED, FILE_DELETED |
Files |
TOOL_EXECUTED |
Tools (opt-in) |
SCIM_USER_PROVISIONED, SCIM_USER_UPDATED, SCIM_USER_DEPROVISIONED |
SCIM |
SCIM_GROUP_SYNCED |
SCIM |
LEGAL_HOLD_APPLIED, LEGAL_HOLD_RELEASED |
Compliance |
GDPR_EXPORT_INITIATED, GDPR_USER_PURGED, GDPR_TEAM_PURGED |
Compliance |
CONFIG_EXPORTED, CONFIG_IMPORTED |
Configuration |
세션 관리
세션은 쿠키 기반이며 SESSION_DURATION_HOURS(기본값: 168시간 / 7일)으로 제어됩니다.
역할 변경은 세션을 무효화합니다
관리자가 사용자의 역할을 변경하면, 그 사용자의 모든 활성 세션이 삭제됩니다. 사용자는 새 권한을 적용받으려면 다시 로그인해야 합니다.
안전장치
- 마지막 관리자 보호: 마지막으로 남은 관리자는 더 낮은 역할로 강등될 수 없습니다. 시도하면 API가 오류를 반환합니다.
- 자기 삭제 방지: 관리자는 API를 통해 자신의 계정을 삭제할 수 없습니다.