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

# Application Yetkilendirme

> IRequirePermissions, IRequireTenantAccess, IRequirePermission marker'ları ve context soyutlamaları

## Genel bakış

Application katmanı yetkilendirmeyi **marker arayüzleriyle** taşır. Bir Command/Query yalnızca bir işaretleyici arayüz uygular; gerçek kontrolü pipeline behavior'lar yapar (bkz. [Pipeline Behaviors](/application/pipeline-behaviors)). Bu sayede yetki niyeti use-case'in kendi tipinde, deklaratif biçimde durur.

| Marker                        | Kontrol eden behavior           | Hata                 | HTTP |
| ----------------------------- | ------------------------------- | -------------------- | ---- |
| `IRequirePermissions` (çoğul) | `AuthorizationPipelineBehavior` | `ForbiddenException` | 403  |
| `IRequireTenantAccess`        | `AuthorizationPipelineBehavior` | `ForbiddenException` | 403  |
| `IRequirePermission` (tekil)  | `PermissionPipelineBehavior`    | `DomainException`    | 400  |

## IRequirePermissions

Birden fazla izin gerektiren command/query'ler için. Tüm izinler **AND** mantığıyla karşılanmalıdır.

```csharp theme={null}
public interface IRequirePermissions
{
    /// Handler çalışmadan önce kullanıcının sahip olması gereken izinler (AND).
    string[] RequiredPermissions { get; }
}
```

Kullanım — command bir izin dizisi bildirir:

```csharp theme={null}
public class UpdateStaffCommand : IRequest<Result>, IRequirePermissions
{
    public string[] RequiredPermissions =>
        [StaffPermission.ManageStaff.Name];
}
```

`AuthorizationPipelineBehavior` eksik izinleri toplar ve hepsini içeren bir `ForbiddenException` fırlatır:

```csharp theme={null}
var missing = permRequest.RequiredPermissions
    .Where(p => !_currentUser.HasPermission(p))
    .ToList();

if (missing.Count > 0)
    throw new ForbiddenException(missing);
```

## IRequireTenantAccess

Çok-kiracılı (multi-tenant) izolasyon için. Kullanıcının token'ındaki tenant ile request'teki tenant örtüşmeli; aksi halde başka bir organizasyonun kaynağına erişim engellenir.

```csharp theme={null}
public interface IRequireTenantAccess
{
    Guid TenantId { get; }
}
```

Kullanım — route/body'den gelen organizasyon ID'si:

```csharp theme={null}
public class GetBranchesQuery : IRequest<Result>, IRequireTenantAccess
{
    public Guid TenantId { get; init; }
}
```

Behavior kontrolü:

```csharp theme={null}
if (request is IRequireTenantAccess tenantRequest)
{
    if (!_currentUser.IsInTenant(tenantRequest.TenantId))
        throw new ForbiddenException("Farklı bir organizasyona ait kaynağa erişim engellendi.");
}
```

<Note>
  Bir request hem `IRequirePermissions` hem `IRequireTenantAccess` uygulayabilir. `AuthorizationPipelineBehavior` önce kimlik doğrulamayı, sonra izinleri (AND), en son tenant örtüşmesini kontrol eder.
</Note>

## IRequirePermission

Tekil izin gerektiren, JWT `permissions` claim'ini doğrudan okuyan daha hafif marker. `PermissionPipelineBehavior` `HttpContext` üzerinden kontrol eder ve `"*:*"` wildcard'ını süper yetki olarak kabul eder.

```csharp theme={null}
public interface IRequirePermission
{
    /// Gerekli izin kodu. Örn: "users:read", "reports:export"
    string RequiredPermission { get; }
}
```

<Warning>
  `IRequirePermission` (tekil) ile `IRequirePermissions` (çoğul) karıştırılmamalıdır. Çoğul olan `ICurrentUserService` soyutlaması üzerinden çalışır ve 403 üretir; tekil olan ham `HttpContext` claim'lerini okur ve 400 (`DomainException`) üretir. Yeni admin use-case'lerinde çoğul `IRequirePermissions` tercih edilir.
</Warning>

## ForbiddenException

Yetki reddi için tek exception tipi. Bir `DomainException` alt sınıfıdır; bu yüzden `ExceptionPipelineBehavior` tarafından "bilinen exception" sayılır ve sarılmadan ProblemDetails katmanına ulaşır (HTTP 403).

```csharp theme={null}
public sealed class ForbiddenException : DomainException
{
    public ForbiddenException()
        : base("Bu işlem için yetkiniz bulunmamaktadır.") { }

    public ForbiddenException(string permission)
        : base($"Bu işlem için '{permission}' iznine sahip olmanız gerekmektedir.") { }

    public ForbiddenException(IEnumerable<string> missingPermissions)
        : base($"Bu işlem için şu izinlere sahip olmanız gerekmektedir: {string.Join(", ", missingPermissions)}") { }
}
```

## Context soyutlamaları

`SeedWork/Security/` altında, mevcut isteğin aktörünü temsil eden arayüzler bulunur. Implementasyonları API katmanında `HttpContext` claim'lerinden beslenir.

<AccordionGroup>
  <Accordion title="ICurrentUser — kimliği doğrulanmış kullanıcı">
    ```csharp theme={null}
    public interface ICurrentUser
    {
        Guid? UserId { get; }
        Guid? TenantId { get; }
        bool IsAuthenticated { get; }
        IReadOnlyList<string> Roles { get; }
        IReadOnlyList<string> Permissions { get; }
    }
    ```
  </Accordion>

  <Accordion title="ICurrentTenant — aktif tenant bağlamı">
    ```csharp theme={null}
    public interface ICurrentTenant
    {
        /// JWT tenant_id claim. Null = global/superadmin.
        Guid? TenantId { get; }
        bool HasTenant => TenantId.HasValue;
    }
    ```
  </Accordion>

  <Accordion title="ICurrentActor — audit aktörü (asla null değil)">
    Audit pipeline'ının tek girdisi. Aktörü çözme sırası: `user_id` claim → `citizen_id` claim → `SystemActor.Id` (background job / anonymous). Bu sayede `CreatedBy/UpdatedBy/DeletedBy` her zaman dolu yazılır.

    ```csharp theme={null}
    public interface ICurrentActor
    {
        Guid ActorId { get; }
    }
    ```
  </Accordion>
</AccordionGroup>

## Katmanlı yetki (defense in depth)

Aynı yetki, iki ayrı katmanda bağımsız doğrulanır:

```mermaid theme={null}
flowchart TD
    REQ[HTTP Request] --> API["API: [RequirePermission] attribute<br/>(controller seviyesi)"]
    API --> MED[MediatR Send]
    MED --> BEH["AuthorizationPipelineBehavior /<br/>PermissionPipelineBehavior<br/>(application seviyesi)"]
    BEH --> H[Handler]
```

* **API katmanı** — controller'a `[RequirePermission]` attribute'u ile koyulur; HTTP request'i daha endpoint'e girerken reddeder.
* **Application katmanı** — pipeline behavior, command/query'nin marker'ını okur ve handler çalışmadan tekrar doğrular.

Bu çift kontrol özellikle **Hangfire job'ları veya event handler'ları** gibi HTTP request'i olmadan command tetikleyen yollarda değerlidir: API attribute'u devreye girmez ama application behavior yine korur.

<Info>
  Rol/izin modelinin tam dökümü (RolePermission aggregate, Keycloak `permissions` multivalued claim, RBAC token invalidation, `TokenVersion`) için bkz. [Güvenlik › Yetkilendirme](/security/authorization).
</Info>

## Sonraki adımlar

<CardGroup cols={2}>
  <Card title="Güvenlik › Yetkilendirme" icon="shield-halved" href="/security/authorization">
    Detaylı RBAC, RolePermission ve Keycloak claim dökümü.
  </Card>

  <Card title="Pipeline Behaviors" icon="layer-group" href="/application/pipeline-behaviors">
    Authorization ve Permission behavior'larının tam implementasyonu.
  </Card>

  <Card title="Command / Query deseni" icon="code" href="/application/commands-queries">
    Marker arayüzlerinin command/query üzerinde kullanımı.
  </Card>

  <Card title="Keycloak" icon="key" href="/keycloak/overview">
    Çift realm, claim üretimi ve provisioning.
  </Card>
</CardGroup>
