> ## 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.

# Authorization

> Dinamik permission policy, permission/scope/active-account handler'ları, roller ve RolePermission seed

Authorization tamamen uygulama katmanına aittir. Keycloak rol ve permission claim'lerini token'a
koyar; "bu endpoint bu izni gerektiriyor" kararını .NET tarafı verir. İki seviyede çalışır:
**HTTP authorization** (ASP.NET policy/handler) ve **MediatR pipeline** (defense-in-depth).

## DynamicPermissionPolicyProvider

`[Authorize(Policy = "...")]` için her permission'a tek tek named policy tanımlamak yerine,
policy adı bir konvansiyonla çözülür (`SeedWork/Authorization/DynamicPermissionPolicyProvider.cs`):

* `Permission:{code}` → `PermissionAuthorizationRequirement(code)`
* `Scope:{name}` → `ScopeAuthorizationRequirement(name)`

```csharp theme={null}
public async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
    if (policyName.StartsWith(RequirePermissionAttribute.PolicyPrefix, ...)) // "Permission:"
    {
        var permission = policyName[RequirePermissionAttribute.PolicyPrefix.Length..];
        return new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddRequirements(new PermissionAuthorizationRequirement(permission))
            .AddRequirements(new ActiveAccountRequirement())
            .Build();
    }
    // "Scope:" → ScopeAuthorizationRequirement
    // bilinmeyen policy → default provider'a düşer (ama ActiveAccountRequirement eklenir)
}
```

<Note>
  Provider **her** policy'yi (named, fallback ve `Permission:`/`Scope:` dinamik policy'leri)
  `ActiveAccountRequirement` ile augment eder. Böylece `[Authorize]` taşıyan hiçbir endpoint'e
  `Pending/Banned/Suspended` hesap erişemez. Augment idempotenttir (zaten varsa tekrar eklemez).
</Note>

### `[RequirePermission("...")]`

`RequirePermissionAttribute` aslında bir `AuthorizeAttribute`'tur; policy adını
`Permission:` + kod olarak üretir:

```csharp theme={null}
[RequirePermission("users:read")]   // → Policy = "Permission:users:read"
public async Task<IActionResult> List(...) => ...;
```

## Handler'lar

`Program.cs` → RBAC bölümünde üç handler kayıtlıdır:

```csharp theme={null}
builder.Services.AddSingleton<IAuthorizationPolicyProvider, DynamicPermissionPolicyProvider>();
builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
builder.Services.AddScoped<IAuthorizationHandler, ScopeAuthorizationHandler>();
builder.Services.AddScoped<IAuthorizationHandler, ActiveAccountAuthorizationHandler>();
```

### PermissionAuthorizationHandler

`permissions` claim'lerini kontrol eder. `*:*` wildcard her izni geçirir (SuperAdmin):

```csharp theme={null}
var permissions = context.User.Claims
    .Where(c => c.Type == DiyanetCleanArchitectureClaimTypes.Permissions)
    .Select(c => c.Value).ToHashSet();

bool granted = permissions.Contains("*:*") ||
               permissions.Contains(requirement.Permission);
if (granted) context.Succeed(requirement);
```

### ScopeAuthorizationHandler

`scope` claim'ini kontrol eder; `"shared"` scope her iki uygulama için geçerlidir, `*:*` yine
geçer:

```csharp theme={null}
bool granted = scope == requirement.Scope ||
               scope == "shared" ||
               context.User.Claims.Any(c => c.Type == Permissions && c.Value == "*:*");
```

### ActiveAccountAuthorizationHandler

`account_status` claim'inin `Active` olmasını gerektirir; endpoint `[AllowPendingAccount]`
taşıyorsa atlanır:

```csharp theme={null}
var endpoint = httpContext.GetEndpoint();
if (endpoint?.Metadata.GetMetadata<AllowPendingAccountAttribute>() is not null)
{
    context.Succeed(requirement);   // Pending/Banned/Suspended bypass
    return Task.CompletedTask;
}
var status = context.User.FindFirstValue(DiyanetCleanArchitectureClaimTypes.AccountStatus);
if (string.Equals(status, "Active", StringComparison.OrdinalIgnoreCase))
    context.Succeed(requirement);
```

`account_status` her istekte `UserContextClaimsTransformation` / `CitizenContextClaimsTransformation`
tarafından DB'den güncel olarak yazılır. Olası değerler: `Active`, `Pending`, `Banned`,
`Suspended`, `Draft`, `Unknown`.

<Tip>
  `/me/status`, logout, profil okuma gibi kendi durumunu öğrenmesi gereken endpoint'ler
  `[AllowPendingAccount]` ile işaretlenir — aksi halde yeni kayıt olmuş (Pending) kullanıcı
  durumunu hiç öğrenemez.
</Tip>

## AuthPolicies — named scheme policy'leri

Bazı endpoint'ler için "sadece doğru realm'den authenticated olsun yeter" gerekir.
`Program.cs` üç named policy tanımlar (`SeedWork/Authorization/AuthPolicies.cs`):

```csharp theme={null}
options.AddPolicy(AuthPolicies.PersonelAuthenticated, p => p
    .AddAuthenticationSchemes(KeycloakSchemeNames.Personel).RequireAuthenticatedUser());
options.AddPolicy(AuthPolicies.VatandasAuthenticated, p => p
    .AddAuthenticationSchemes(KeycloakSchemeNames.Vatandas).RequireAuthenticatedUser());
options.AddPolicy(AuthPolicies.AnyKeycloakAuthenticated, p => p
    .AddAuthenticationSchemes(KeycloakSchemeNames.Vatandas, KeycloakSchemeNames.Personel)
    .RequireAuthenticatedUser());
```

<Warning>
  Bare `[Authorize(AuthenticationSchemes = X)]` policy'ye hiçbir requirement eklemez; boş-requirement
  policy bazı senaryolarda yanlışlıkla 403 dönebilir. Named policy `RequireAuthenticatedUser()`
  çağrısıyla `DenyAnonymousAuthorizationRequirement` ekler → policy düzgün değerlendirilir.
</Warning>

```csharp theme={null}
[Authorize(Policy = AuthPolicies.PersonelAuthenticated)]
public async Task<IActionResult> Me(...) => ...;
```

## Pipeline-level defense-in-depth (MediatR)

HTTP authorization ilk savunma hattıdır. İkinci hat, MediatR `AuthorizationPipelineBehavior`'dır
(`Application/SeedWork/PipelineBehaviors/AuthorizationPipelineBehavior.cs`). Command/query
şu marker'ları implement ederse handler çalışmadan önce kontrol edilir:

* `IRequirePermissions` → `RequiredPermissions` dizisindeki **tüm** izinler olmalı (AND).
* `IRequireTenantAccess` → token tenant'ı ile command'daki `TenantId` örtüşmeli.

```csharp theme={null}
public class UpdateStaffCommand : IRequest<Result>, IRequirePermissions
{
    public string[] RequiredPermissions => [ "users:write" ];
}
```

Eksik izin / tenant uyuşmazlığı → `ForbiddenException` → `ExceptionPipelineBehavior` bunu
HTTP 403'e çevirir. Bu sayede bir endpoint controller attribute'unu unutsa bile use-case
seviyesinde korunur.

## Roller ve RolePermission seed

Sabit dört sistem rolü vardır (`Domain/AggregatesModel/RoleAggregate/Role.cs`, `Enumeration`):

| Id | Rol          | Açıklama                                  |
| -- | ------------ | ----------------------------------------- |
| 1  | `SuperAdmin` | Sistem genelinde tam yetki                |
| 2  | `Admin`      | Kurum/tenant yöneticisi                   |
| 3  | `Staff`      | Standart personel — okuma + sınırlı işlem |
| 4  | `ReadOnly`   | Salt okunur (raporlama, denetim)          |

Rol → izin haritası DB'de `role_permission` tablosunda seed edilir
(`RolePermissionEntityTypeConfiguration`). Varsayılan matrisin bir kesiti:

| İzin                  | SuperAdmin | Admin | Staff | ReadOnly |
| --------------------- | :--------: | :---: | :---: | :------: |
| `users:read`          |      ✓     |   ✓   |   —   |     ✓    |
| `users:write`         |      ✓     |   ✓   |   —   |     —    |
| `users:delete`        |      ✓     |   —   |   —   |     —    |
| `roles:manage`        |      ✓     |   —   |   —   |     —    |
| `tenants:switch`      |      ✓     |   ✓   |   ✓   |     —    |
| `reports:read`        |      ✓     |   ✓   |   ✓   |     ✓    |
| `faqs:write`          |      ✓     |   ✓   |   ✓   |     —    |
| `bagisBasvuru:onayla` |      ✓     |   ✓   |   —   |     —    |

Seed ID'leri deterministiktir (`a{roleId}-...-{permissionId}`), böylece migration tekrarlarında
duplicate oluşmaz. Aktif `(role_id, permission_id)` çifti partial unique index ile korunur
(`deleted_at IS NULL`), soft-delete sonrası yeniden atama mümkündür.

## İlgili

<CardGroup cols={2}>
  <Card title="Authentication" href="/security/authentication">
    Token doğrulama ve claim üretimi.
  </Card>

  <Card title="Multi-Tenancy" href="/security/multi-tenancy">
    `IRequireTenantAccess` ve tenant izolasyonu.
  </Card>

  <Card title="Realm Yapısı" href="/keycloak/realm-structure">
    Rollerin Keycloak'taki karşılığı, permissions mapper.
  </Card>

  <Card title="Application Authorization" href="/application/authorization">
    MediatR pipeline ve marker arayüzleri.
  </Card>
</CardGroup>
