> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Otomatik Provisioning

> KeycloakProvisioning config'i, hosted service, seed model'ler ve idempotent davranış

`KeycloakProvisioning`, API açılışta Keycloak'ı (realm, client, rol, kullanıcı, protocol mapper,
identity provider) `appsettings` config'inden otomatik kurar. Ayrı bir Worker Service'e gerek
yoktur; bir `IHostedService` olarak ana uygulamada çalışır.

```csharp theme={null}
// Program.cs
builder.Services.AddKeycloakProvisioning(builder.Configuration);
```

## Hosted service davranışı

`KeycloakProvisioningHostedService`
(`src/BuildingBlocks/BuildingBlocks.Keycloak/Provisioning/Application/Services/`) `StartAsync`'te:

1. `Enabled: false` ise hiçbir şey yapmaz.
2. Keycloak'ın hazır olmasını bekler (`WaitForKeycloakReadyAsync`) — `master` realm'ın OIDC
   discovery endpoint'ini max 120 sn, 5 sn aralıkla poll'lar.
3. Scoped `IProvisioningOrchestrator` ile `ProvisionAllAsync` çağırır.
4. Hata olursa loglar ama **uygulamayı çökertmez**.

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant API as API (Startup)
    participant HS as ProvisioningHostedService
    participant KC as Keycloak Admin REST API
    API->>HS: StartAsync()
    HS->>KC: GET /realms/master/.well-known/... (hazır mı?)
    KC-->>HS: 200
    HS->>KC: master realm'dan admin token (admin-cli)
    loop her realm için
        HS->>KC: realm upsert
        HS->>KC: client + protocol mapper upsert
        HS->>KC: realm roles upsert
        HS->>KC: identity providers upsert
        HS->>KC: users upsert (ProvisionUsers=true ise)
    end
    HS-->>API: ready
```

<Warning>
  Bu metot dönene kadar uygulama HTTP isteklerini kabul etmez — Keycloak hazır olmadan hiçbir
  istek işlenmez. Bu, "API ayakta ama realm henüz yok" yarış durumunu engeller.
</Warning>

## Config: `KeycloakProvisioning`

```json theme={null}
"KeycloakProvisioning": {
  "Enabled": true,
  "RunOnce": true,
  "Admin": {
    "BaseUrl": "http://localhost:8080",
    "AdminRealm": "master",
    "ClientId": "admin-cli",
    "Username": "admin",
    "Password": "admin",
    "TimeoutSeconds": 30
  },
  "Realms": [ /* aşağıda */ ]
}
```

| Alan       | Anlam                                                                                                                 |
| ---------- | --------------------------------------------------------------------------------------------------------------------- |
| `Enabled`  | Provisioning açık mı (prod'da genelde `false`).                                                                       |
| `RunOnce`  | `true`: açılışta bir kez çalışır. `false` (periyodik) bu sürümde desteklenmez — yine bir kez çalışır, uyarı loglanır. |
| `Admin`    | Keycloak Admin REST API erişimi (`admin-cli`, `master` realm).                                                        |
| `Realms[]` | Kurulacak realm tanımları.                                                                                            |

### Realm seed modeli

Her realm `Clients`, `Users`, `IdentityProviders` içerir:

```json theme={null}
{
  "Name": "diyanet-yonetim-dev-realm",
  "DisplayName": "Diyanet Yönetim (Development)",
  "Enabled": true,
  "ProvisionUsers": true,
  "LoginTheme": "keycloak.v2",
  "DefaultLocale": "tr",
  "SupportedLocales": [ "tr", "en" ],
  "Clients": [
    {
      "ClientId": "diyanet-admin",
      "PublicClient": true,
      "StandardFlowEnabled": true,
      "PkceRequired": true,
      "DirectAccessGrantsEnabled": false,
      "AccessTokenLifespanSeconds": 300,
      "RefreshTokenRotationEnabled": true,
      "RefreshTokenLifespanSeconds": 1800,
      "RedirectUris": [ "http://localhost:3001/*", "http://localhost:5005/swagger/oauth2-redirect.html" ],
      "WebOrigins": [ "http://localhost:3001", "http://localhost:5005" ],
      "Roles": [ "SuperAdmin", "Admin", "Staff", "ReadOnly" ],
      "ProtocolMappers": [
        { "Name": "permissions", "UserAttribute": "permissions", "ClaimName": "permissions",
          "JsonType": "String", "Multivalued": true, "AddToAccessToken": true },
        { "Name": "organization_id", "UserAttribute": "organization_id", "ClaimName": "organization_id",
          "JsonType": "String", "Multivalued": false, "AddToAccessToken": true }
      ]
    }
  ],
  "Users": [
    {
      "Id": "a0000001-0000-0000-0000-000000000001",
      "Username": "cihan.delipinar",
      "Email": "cihan.delipinar@diyanet.gov.tr",
      "TemporaryPassword": "123456",
      "Roles": [ { "ClientId": "diyanet-admin", "Role": "SuperAdmin" } ]
    }
  ]
}
```

Vatandaş realm'ında ek olarak `IdentityProviders` (Google) tanımlıdır ve client rolü `Citizen`'dır.

<Note>
  Seed kullanıcı `Id`'leri sabittir (örn. `a0000001-...`) ve DB'deki dev seed ile birebir eşleşir:
  Keycloak user ID = local `UserIdentityProvider.provider_user_id`. Böylece "Keycloak'ta giriş yapan
  kullanıcı = DB'deki kullanıcı" bağı kurulur.
</Note>

## İdempotent davranış

Orchestrator her öğeyi **upsert** eder: varsa günceller, yoksa oluşturur. Bu yüzden her API
açılışında güvenle çalışır — mevcut realm/client'ı kırmaz, yalnızca eksikleri tamamlar ve drift'i
config'e geri çeker.

## Yardımcı script'ler

Provisioning'e ek olarak `scripts/` altında elle çalıştırılabilen PowerShell yardımcıları vardır:

* `scripts/create-keycloak-dev-users.ps1` — DB dev seed'iyle eşleşen `sub` UUID'lerine sahip
  Keycloak kullanıcılarını oluşturur (Keycloak user ID = `UserIdentityProvider.provider_user_id`).
* `scripts/set-keycloak-test-user-attributes.ps1` — bir test kullanıcısına `permissions` ve
  `tenant_id` attribute'larını set eder.

Her ikisi de Admin REST API'ye `admin-cli` ile `master` realm'dan token alıp bağlanır.

```powershell theme={null}
.\scripts\create-keycloak-dev-users.ps1
.\scripts\set-keycloak-test-user-attributes.ps1
```

<Tip>
  Stage/prod'da provisioning'i kapatın (`Enabled: false`) ve realm'i bir kez kurup yönetin —
  otomatik upsert'in canlı realm konfigürasyonunu beklenmedik şekilde değiştirmesini istemezsiniz.
</Tip>

## İlgili

<CardGroup cols={2}>
  <Card title="Client Yapılandırması" href="/keycloak/client-configuration">
    Aynı yapının manuel kurulumu.
  </Card>

  <Card title="Realm Yapısı" href="/keycloak/realm-structure">
    Provision edilen roller ve mapper'lar.
  </Card>

  <Card title="Ortamlar" href="/keycloak/environments">
    Hangi ortamda provisioning açık.
  </Card>

  <Card title="Sorun Giderme" href="/keycloak/troubleshooting">
    Provisioning loglarını okuma.
  </Card>
</CardGroup>
