{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://oidc.pub/schemas/service-account-policy.v1.json",
  "title": "oidc.pub Service Account Policy",
  "description": "Authorization policy attached to a Service Account on oidc.pub. The document is consumed verbatim by POST /api/service-accounts as `policy`.",
  "type": "object",
  "required": [
    "kind"
  ],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string"
    },
    "kind": {
      "enum": [
        "static",
        "oidc"
      ]
    },
    "services": {
      "$ref": "#/$defs/ServicesPatterns"
    },
    "rules": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/OidcRule"
      }
    },
    "ip_allowlist": {
      "$ref": "#/$defs/IpAllowlist"
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "kind": {
            "const": "static"
          }
        },
        "required": [
          "kind"
        ]
      },
      "then": {
        "required": [
          "services"
        ],
        "not": {
          "required": [
            "rules"
          ]
        }
      }
    },
    {
      "if": {
        "properties": {
          "kind": {
            "const": "oidc"
          }
        },
        "required": [
          "kind"
        ]
      },
      "then": {
        "required": [
          "rules"
        ],
        "not": {
          "required": [
            "services"
          ]
        }
      }
    }
  ],
  "$defs": {
    "ServicesPatterns": {
      "type": "array",
      "minItems": 1,
      "uniqueItems": true,
      "description": "Service subdomains the policy applies to. Use `\"*\"` to match every service you own. Partial globs (e.g. `acme-*`) are supported. Entries are validated against the services you own at save time.",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "IpAllowlist": {
      "type": "array",
      "description": "Optional IPv4 / IPv6 addresses or CIDR ranges. Omit or use an empty array to allow all source IPs. Maximum 50 entries.",
      "maxItems": 50,
      "uniqueItems": true,
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "ExactString": {
      "type": "string",
      "minLength": 1,
      "description": "Exact-match string. Globs are not allowed here.",
      "not": {
        "pattern": "[*?]"
      }
    },
    "ClaimPattern": {
      "type": "string",
      "minLength": 1,
      "description": "Exact value or glob (e.g. `repo:myorg/*`)."
    },
    "OidcRule": {
      "type": "object",
      "title": "OIDC authorization rule",
      "description": "A token is accepted if its claims satisfy this rule and the requested service subdomain matches one of `services`. Claim values support `*` globs except for `iss`, which must be an exact issuer URL.",
      "required": [
        "services",
        "claims"
      ],
      "additionalProperties": false,
      "properties": {
        "services": {
          "$ref": "#/$defs/ServicesPatterns"
        },
        "claims": {
          "type": "object",
          "required": [
            "iss",
            "aud",
            "sub"
          ],
          "properties": {
            "iss": {
              "$ref": "#/$defs/ExactString"
            },
            "aud": {
              "$ref": "#/$defs/ClaimPattern"
            },
            "sub": {
              "$ref": "#/$defs/ClaimPattern"
            }
          },
          "additionalProperties": {
            "$ref": "#/$defs/ClaimPattern"
          }
        }
      }
    }
  }
}
