Portal Community

Attribute Sources

Attribute CategorySourceContext Property
User identityJWT claims + IDInfocontext.User.UserId, context.User.Email, context.User.Roles
User profileCustom user attributes (external system)Loaded by resolver from IUserProfileService
Resource metadataResource registry or databasecontext.Resource.OwnerId, context.Resource.Classification, context.Resource.Department
EnvironmentHTTP request contextcontext.Environment.Now, context.Environment.RequestIp, context.Environment.IsMfaVerified

Condition Patterns

Department Match

// Pattern: access restricted to users in a specific department
public async Task<PermissionResolution> ResolveAsync(PermissionContext ctx, CancellationToken ct)
{
    var profile = await _profiles.GetAsync(ctx.User.UserId, ct);

    // Rule: Finance resources require Finance department membership
    if (ctx.ResourceType == "finance-report")
    {
        return profile?.Department == "Finance"
            ? PermissionResolution.Allow
            : PermissionResolution.Deny;
    }
    return PermissionResolution.Defer;
}

Clearance Level

// Pattern: resource requires minimum clearance level
public async Task<PermissionResolution> ResolveAsync(PermissionContext ctx, CancellationToken ct)
{
    if (ctx.Resource?.Classification is not string classification)
        return PermissionResolution.Defer;

    int requiredLevel = classification switch
    {
        "CONFIDENTIAL"  => 2,
        "SECRET"        => 3,
        "TOP_SECRET"    => 4,
        _               => 0
    };

    if (requiredLevel == 0) return PermissionResolution.Defer;

    var profile = await _profiles.GetAsync(ctx.User.UserId, ct);
    int userLevel = profile?.ClearanceLevel ?? 0;

    return userLevel >= requiredLevel
        ? PermissionResolution.Allow
        : PermissionResolution.Deny;
}

Geographic Restriction

// Pattern: restrict access by country (e.g., data residency requirements)
public Task<PermissionResolution> ResolveAsync(PermissionContext ctx, CancellationToken ct)
{
    // Only apply to regulated-data resources
    if (ctx.ResourceType != "regulated-data")
        return Task.FromResult(PermissionResolution.Defer);

    var country = ctx.Environment.Country;

    // GDPR-regulated data: EU countries only
    var euCountries = new HashSet<string> { "DE", "FR", "NL", "BE", "AT", "SE", "DK", "FI", "IT", "ES" };

    return Task.FromResult(
        country != null && euCountries.Contains(country)
            ? PermissionResolution.Allow
            : PermissionResolution.Deny);
}

Employment Type

// Pattern: contractors cannot access PII or financial data
public async Task<PermissionResolution> ResolveAsync(PermissionContext ctx, CancellationToken ct)
{
    var sensitiveResources = new[] { "payroll", "hr-record", "customer-pii" };
    if (!sensitiveResources.Contains(ctx.ResourceType))
        return PermissionResolution.Defer;

    var profile = await _profiles.GetAsync(ctx.User.UserId, ct);

    return profile?.EmployeeType == "contractor"
        ? PermissionResolution.Deny
        : PermissionResolution.Defer;
}

Combining Multiple Conditions

// Pattern: multiple conditions combined in one resolver
// Finance manager + Finance department + business hours + MFA verified
public async Task<PermissionResolution> ResolveAsync(PermissionContext ctx, CancellationToken ct)
{
    if (ctx.Permission != "workflow.initiate" || ctx.ResourceType != "payroll-workflow")
        return PermissionResolution.Defer;

    // Condition 1: Must have MFA verified
    if (!ctx.Environment.IsMfaVerified)
        return PermissionResolution.Deny;

    // Condition 2: Must be during business hours
    var hour = ctx.Environment.Now.Hour;
    if (hour < 9 || hour >= 17)
        return PermissionResolution.Deny;

    // Condition 3: Must be Finance department employee (not contractor)
    var profile = await _profiles.GetAsync(ctx.User.UserId, ct);
    if (profile?.Department != "Finance" || profile?.EmployeeType == "contractor")
        return PermissionResolution.Deny;

    return PermissionResolution.Allow;
}
Audit Every ABAC Decision

Log the attributes evaluated and the decision outcome for every ABAC rule invocation. Unlike RBAC (where the role assignment log explains every decision), ABAC decisions are context-dependent and may be difficult to reconstruct from audit data alone. Include the relevant attribute values in your log entries.