Skip to main content

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). Bu sayede yetki niyeti use-case’in kendi tipinde, deklaratif biçimde durur.
MarkerKontrol eden behaviorHataHTTP
IRequirePermissions (çoğul)AuthorizationPipelineBehaviorForbiddenException403
IRequireTenantAccessAuthorizationPipelineBehaviorForbiddenException403
IRequirePermission (tekil)PermissionPipelineBehaviorDomainException400

IRequirePermissions

Birden fazla izin gerektiren command/query’ler için. Tüm izinler AND mantığıyla karşılanmalıdır.
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:
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:
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.
public interface IRequireTenantAccess
{
    Guid TenantId { get; }
}
Kullanım — route/body’den gelen organizasyon ID’si:
public class GetBranchesQuery : IRequest<Result>, IRequireTenantAccess
{
    public Guid TenantId { get; init; }
}
Behavior kontrolü:
if (request is IRequireTenantAccess tenantRequest)
{
    if (!_currentUser.IsInTenant(tenantRequest.TenantId))
        throw new ForbiddenException("Farklı bir organizasyona ait kaynağa erişim engellendi.");
}
Bir request hem IRequirePermissions hem IRequireTenantAccess uygulayabilir. AuthorizationPipelineBehavior önce kimlik doğrulamayı, sonra izinleri (AND), en son tenant örtüşmesini kontrol eder.

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.
public interface IRequirePermission
{
    /// Gerekli izin kodu. Örn: "users:read", "reports:export"
    string RequiredPermission { get; }
}
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.

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).
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.
public interface ICurrentUser
{
    Guid? UserId { get; }
    Guid? TenantId { get; }
    bool IsAuthenticated { get; }
    IReadOnlyList<string> Roles { get; }
    IReadOnlyList<string> Permissions { get; }
}
public interface ICurrentTenant
{
    /// JWT tenant_id claim. Null = global/superadmin.
    Guid? TenantId { get; }
    bool HasTenant => TenantId.HasValue;
}
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.
public interface ICurrentActor
{
    Guid ActorId { get; }
}

Katmanlı yetki (defense in depth)

Aynı yetki, iki ayrı katmanda bağımsız doğrulanır:
  • 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.
Rol/izin modelinin tam dökümü (RolePermission aggregate, Keycloak permissions multivalued claim, RBAC token invalidation, TokenVersion) için bkz. Güvenlik › Yetkilendirme.

Sonraki adımlar

Güvenlik › Yetkilendirme

Detaylı RBAC, RolePermission ve Keycloak claim dökümü.

Pipeline Behaviors

Authorization ve Permission behavior’larının tam implementasyonu.

Command / Query deseni

Marker arayüzlerinin command/query üzerinde kullanımı.

Keycloak

Çift realm, claim üretimi ve provisioning.