Passport
Attribute-Based Conditions
Design ABAC conditions based on user attributes (department, clearance, employment type), resource attributes (owner, classification), and environment (time, location, MFA status).
Attribute Sources
| Attribute Category | Source | Context Property |
|---|---|---|
| User identity | JWT claims + IDInfo | context.User.UserId, context.User.Email, context.User.Roles |
| User profile | Custom user attributes (external system) | Loaded by resolver from IUserProfileService |
| Resource metadata | Resource registry or database | context.Resource.OwnerId, context.Resource.Classification, context.Resource.Department |
| Environment | HTTP request context | context.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.