
# 조직 정책 미리 보기

`POST /v1/organizations/{org_id}/policies/{policy_kind}:preview` · 권한 `organizations:write`

저장하지 않고 설정 객체 후보를 평가합니다. 본문은 [정책 생성 또는 교체](/docs/api/management/organization-policies/put)와 동일하며, 서버는 같은 규칙과 같은 플랜 게이트로 검사하므로 `PUT`가 거부할 후보는 여기서도 같은 문제 코드로 거부됩니다. 아무것도 기록되지 않습니다. 정책, 감사 항목, 이벤트가 모두 생성되지 않습니다. 이 요청에는 `If-Match`와 `Idempotency-Key`를 지정하지 않으며, 응답에도 `ETag`가 포함되지 않습니다.

응답은 후보가 적용되는 사용자 수(`basis` `users_in_scope`)를 집계하며, 차단될 사용자 수를 집계하지는 않습니다. 다만 `phishing_resistant`는 예외입니다(아래 참조). 로그인 시점에만 알 수 있는 조건이나 정책 서비스가 보유하지 않은 조건은 필터링되지 않으며, `not_evaluated`에 나열되어 집계 값이 실제보다 많다고 읽히지 않도록 합니다.

| 종류 | 집계 사용자 | `not_evaluated` |
|---|---|---|
| `authentication_methods` | 조직의 활성 구성원 | `credentials` |
| `mfa` (`required_for` `admins` 또는 `all`) | 조직의 활성 구성원 | `mfa_enrollment`; `admins`에 대해서는 `admin_scope`도 집계 |
| `phishing_resistant` (`required_for` `admins` 또는 `all`) | 승인된 WebAuthn 자격 증명이 없는 조직의 활성 구성원 (`basis` `users_without_webauthn`, 아래 참조) | `credential_policy`; `admins`에 대해서는 `admin_scope`도 집계 |
| `sso_enforcement` `org_context` | 조직의 활성 구성원, `bypass_user_ids` 제외 | `federated_sign_in` |
| `sso_enforcement` `verified_domains` | 활성 구성원 + 환경의 활성 사용자 중 이 조직의 검증된 도메인에 이메일 주소가 있는 사용자, `bypass_user_ids` 제외 | `federated_sign_in` |
| `session` | 조직의 활성 구성원 | 없음 |
| `ip_allowlist` | 조직의 활성 구성원 | `client_ip`; `admins`에 대해서는 `admin_scope`도 집계 |

`phishing_resistant`의 경우 집계 수와 샘플은 범위 내 사용자 중 현재 피싱 저항 방식으로 로그인할 수 없는 사용자입니다. 즉, 활성 WebAuthn 자격 증명이 없는 사용자(패스키, 또는 1차 인증과 함께 사용하는 보안 키)로서 AAGUID 허용 목록이 수락하지 않는 사용자입니다. 허용 목록은 후보의 것이며, 후보가 비어 있으면 환경의 것을 사용합니다. 허용 목록이 있으면 자격 증명은 해당 모델이 목록에 있고 인증(attestation)이 검증된 경우에만 집계됩니다. 대기 중, 취소됨, 복제 의심 자격 증명은 집계되지 않습니다. `credential_policy`는 환경의 인증 요구 사항, 기기 바인딩 패스키 규칙, 인증 기기 모델 상태가 적용되지 않는다는 뜻이므로, 집계 수가 실제로 차단될 사용자 수보다 적을 수 있습니다. 범위 내 사용자가 10,000명을 초과하면 응답은 `users_in_scope`로 대체되며 `passkey_enrollment`가 포함됩니다. 자격 증명 데이터를 읽을 수 없으면 요청은 `503`로 실패하며 집계 값이 반환되지 않습니다.

`required_for` `none`와 `mode` `off`은 아무도 집계하지 않습니다. 활성 구성원은 구성원 자격이 활성 상태이고 사용자가 차단되지 않은 구성원입니다. 각 사용자는 한 번만 집계됩니다. `admin_scope`는 `admins`를 모든 구성원으로 집계한다는 뜻이며, 로그인이 현재 이를 적용하는 방식입니다. 다른 조직이나 다른 환경의 사용자는 절대 집계되지 않습니다.

## 요청

| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
| `org_id` | path | 예 | 조직 ID (`org_…`). |
| `policy_kind` | path | 예 | 정책 종류: `authentication_methods`, `mfa`, `phishing_resistant`, `sso_enforcement`, `session` 또는 `ip_allowlist`. 다른 값은 `404`을 반환합니다. |
| `settings` | object | 예 | 해당 종류의 설정 객체 후보이며, 필드는 `PUT`와 같습니다. |

요청 본문:

```json
{
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  }
}
```

## 응답

`200 OK`. `sample`에는 집계 대상 집합 중 최대 20명의 사용자가 ID 순으로 담기며, 사용자 ID만 포함합니다.

| 필드 | 유형 | 설명 |
|---|---|---|
| `organization_id` | string | 조직 ID (`org_…`). |
| `policy_kind` | string | 미리 본 정책 종류. |
| `basis` | string | `affected_count`가 집계하는 대상: `users_in_scope`, 또는 `phishing_resistant`의 경우 `users_without_webauthn`. 새 값이 추가될 수 있습니다. |
| `affected_count` | integer | `basis`가 집계하는 사용자 수. |
| `sample` | array | 최대 20개의 객체, `user_id` (`usr_…`). |
| `not_evaluated` | array | 집계에서 필터링되지 않는 조건: `admin_scope`, `credentials`, `mfa_enrollment`, `passkey_enrollment`, `federated_sign_in`, `client_ip`, `credential_policy`. 새 값이 추가될 수 있습니다. |

```json
{
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "policy_kind": "mfa",
  "basis": "users_in_scope",
  "affected_count": 2,
  "sample": [
    {
      "user_id": "usr_01j9xk3m7q8r2s4t6v8w0y2z4b"
    },
    {
      "user_id": "usr_01j9xk3m7q8r2s4t6v8w0y2z4c"
    }
  ],
  "not_evaluated": [
    "mfa_enrollment"
  ]
}
```

## 오류

| HTTP | 코드 | 발생 시점 |
|---|---|---|
| 400 | `unknown_field` | 본문에 스키마에 없는 필드가 있습니다. |
| 403 | `plan_feature_unavailable` | 해당 종류 또는 AAGUID 허용 목록에 더 높은 플랜이 필요합니다. 문제 응답에는 `feature`와 `required_plan`가 포함됩니다. |
| 404 | `not_found` | 조직이 없거나 종류를 알 수 없습니다. |
| 409 | `domain_not_verified` | `mode` `verified_domains`을 사용하는 `sso_enforcement`에는 조직에 검증된 도메인이 필요합니다. |
| 409 | `connection_test_required` | `sso_enforcement`를 켜려면(`off`에서 또는 정책이 없는 상태에서) 지난 24시간 동안 `connection_ids` 중 하나 이상에 대해 현재 구성의 테스트 로그인이 성공해야 합니다. 연결 테스트(`POST /v1/connections/{connection_id}:test`)를 실행한 후 다시 시도하십시오. |
| 409 | `organization_pending_deletion` | 조직이 셀프 서비스 삭제를 기다리고 있습니다. |
| 422 | `validation_failed` | 설정 필드를 알 수 없거나 잘못되었습니다. `errors[]`는 `/settings/…`를 가리킵니다. |
| 422 | `policy_weaker_than_parent` | 설정이 환경의 설정보다 약합니다. `errors[]`가 약한 필드를 가리킵니다. |
| 422 | `at_least_one_method` | `authentication_methods`로 인해 로그인 방법이 남지 않습니다. |
| 401 | `unauthenticated` | 자격 증명이 없거나, 잘못되었거나, 취소되었습니다. |
| 403 | `insufficient_permissions` | 자격 증명에 필요한 권한이 없습니다. |
| 429 | `rate_limited` | 요청 한도를 초과했습니다. `Retry-After` 기간 후에 다시 시도하십시오. |
| 503 | `service_unavailable` | 종속 서비스를 일시적으로 사용할 수 없습니다(`phishing_resistant`의 경우 자격 증명 데이터를 읽을 수 없을 때도 포함). `Retry-After` 기간 후에 다시 시도하십시오. |

## 예제

```bash
curl -X POST https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/policies/mfa:preview \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'
```

```bash
keenauth organizations policies preview org_01j9xk3m7q8r2s4t6v8w0y2z4a mfa \
  --data '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'
```
