ABAC Implementation: Attribute-Based Fine-Grained Access Control
ABAC (Attribute-Based Access Control) is the second layer in coomia-dip's three-layer permission model, responsible for fine-grained access control based on combinations of subject attributes, resource attributes, and environment attributes. This article details the ABAC attribute model, policy expression engine, implementation of 8 operators, performance optimization of condition evaluation, and coordination mechanisms with the RBAC and ReBAC layers.
“Series: S6 Platform Engineering · Article 3 | Level: Advanced | Reading Time: 18 min
ABAC Implementation: Attribute-Based Fine-Grained Access Control
#TL;DR
ABAC (Attribute-Based Access Control) is the second layer in coomia-dip's three-layer permission model, responsible for fine-grained access control based on combinations of subject attributes, resource attributes, and environment attributes. This article details the ABAC attribute model, policy expression engine, implementation of 8 operators, performance optimization of condition evaluation, and coordination mechanisms with the RBAC and ReBAC layers.
#1. Core Concepts of ABAC
#1.1 Evolution from Roles to Attributes
RBAC provides indirect authorization through roles, but roles are static. ABAC achieves context-aware authorization through dynamic attributes:
+-------------------+ +-------------------+ +-------------------+
| Subject Attrs | | Resource Attrs | | Environment Attrs|
+-------------------+ +-------------------+ +-------------------+
| - department | | - classification | | - time |
| - clearance_level | | - owner | | - ip_address |
| - location | | - sensitivity | | - device_type |
| - job_title | | - data_region | | - mfa_status |
| - certifications | | - retention_days | | - network_zone |
+-------------------+ +-------------------+ +-------------------+
\ | /
+----------------------+----------------------+
|
+----------------------------+
| ABAC Policy Engine |
| 8 Operators for Eval |
+----------------------------+
|
ALLOW / DENY
#1.2 ABAC Advantages
| Dimension | RBAC | ABAC |
|---|---|---|
| Granularity | Role-level | Attribute-level |
| Dynamism | Static role assignment | Real-time attribute evaluation |
| Context awareness | None | Time, IP, device, etc. |
| Data awareness | None | Classification level, sensitivity |
| Policy flexibility | Role combinations | Arbitrary attribute expressions |
| Management complexity | Low | Medium-High |
#2. Attribute Model Design
#2.1 Attribute Classification System
coomia-dip categorizes attributes into four major groups with predefined and custom attributes:
Attribute System
+-- Subject Attributes
| +-- Identity: user_id, username, email
| +-- Organization: department, team, division
| +-- Security: clearance_level, mfa_enabled
| +-- Role: roles[], groups[]
| +-- Custom: certifications, specialties
|
+-- Resource Attributes
| +-- Classification: classification (A1-D)
| +-- Ownership: owner, project, tenant
| +-- Metadata: created_at, sensitivity
| +-- Geography: data_region, storage_location
| +-- Custom: retention_policy, audit_required
|
+-- Action Attributes
| +-- Type: action_type (READ/WRITE/DELETE)
| +-- Bulk: is_bulk_operation
| +-- Export: is_export
|
+-- Environment Attributes
+-- Time: current_time, day_of_week
+-- Network: source_ip, network_zone
+-- Device: device_type, os, browser
+-- Auth: auth_method, mfa_status
+-- Session: session_duration, last_activity
#2.2 Attribute Data Model
message AttributeDefinition {
string name = 1; // Attribute name: "department"
string display_name = 2; // Display name
AttributeType type = 3; // STRING, INT, BOOL, LIST, MAP
AttributeCategory category = 4; // SUBJECT, RESOURCE, ENV
bool is_required = 5;
string default_value = 6;
repeated string allowed_values = 7;
string validation_regex = 8;
}
enum AttributeType {
STRING = 0;
INTEGER = 1;
BOOLEAN = 2;
DOUBLE = 3;
TIMESTAMP = 4;
STRING_LIST = 5;
STRING_MAP = 6;
}
message AttributeValue {
string name = 1;
oneof value {
string string_value = 2;
int64 int_value = 3;
bool bool_value = 4;
double double_value = 5;
google.protobuf.Timestamp timestamp_value = 6;
StringList list_value = 7;
StringMap map_value = 8;
}
}
#3. Policy Expression Engine
#3.1 Policy Structure
ABAC policies consist of condition expression trees supporting AND/OR/NOT logical combinations:
PolicyRule
+-- effect: ALLOW | DENY
+-- target:
| +-- subjects: {attribute match conditions}
| +-- resources: {attribute match conditions}
| +-- actions: [READ, WRITE]
+-- conditions:
+-- LogicalExpression (AND/OR/NOT)
+-- Condition (attribute, operator, value)
+-- Condition (attribute, operator, value)
+-- LogicalExpression (nested)
#3.2 Policy YAML Definition
policy:
id: "abac-dataset-access-001"
name: "Dataset Access Control Policy"
effect: ALLOW
target:
resource_types: [DATASET, OBJECT]
actions: [READ, EXPORT]
conditions:
operator: AND
children:
- attribute: "subject.clearance_level"
op: "gte"
value_ref: "resource.classification_level"
- operator: OR
children:
- attribute: "environment.network_zone"
op: "eq"
value: "INTERNAL"
- attribute: "subject.mfa_enabled"
op: "eq"
value: true
- attribute: "environment.current_time"
op: "between"
value: ["08:00", "20:00"]
obligations:
- type: MASK
condition:
attribute: "resource.sensitivity"
op: "gte"
value: "HIGH"
params:
mode: PARTIAL_MASK
fields: ["phone", "email", "ssn"]
- type: AUDIT
params:
level: DETAILED
#3.3 Expression Evaluation Engine
public class AbacExpressionEvaluator {
private final Map<String, OperatorEvaluator> operators;
public AbacExpressionEvaluator() {
operators = Map.of(
"eq", new EqualityOperator(),
"ne", new InequalityOperator(),
"gt", new GreaterThanOperator(),
"lt", new LessThanOperator(),
"gte", new GreaterOrEqualOperator(),
"lte", new LessOrEqualOperator(),
"in", new InSetOperator(),
"contains", new ContainsOperator(),
"matches", new RegexMatchOperator(),
"between", new BetweenOperator()
);
}
public boolean evaluate(LogicalExpression expr,
EvaluationContext ctx) {
if (expr.isLeaf()) {
return evaluateCondition(
expr.getCondition(), ctx);
}
switch (expr.getLogicalOperator()) {
case AND:
return expr.getChildren().stream()
.allMatch(c -> evaluate(c, ctx));
case OR:
return expr.getChildren().stream()
.anyMatch(c -> evaluate(c, ctx));
case NOT:
return !evaluate(
expr.getChildren().get(0), ctx);
default:
throw new IllegalArgumentException(
"Unknown: " + expr.getLogicalOperator());
}
}
private boolean evaluateCondition(
Condition cond, EvaluationContext ctx) {
Object actual = ctx.resolveAttribute(
cond.getAttribute());
Object expected = cond.isValueRef()
? ctx.resolveAttribute(cond.getValueRef())
: cond.getValue();
OperatorEvaluator op = operators.get(
cond.getOperator());
return op.evaluate(actual, expected);
}
}
#4. Eight Operators in Detail
#4.1 Operator Implementations
// Equality operator
public class EqualityOperator implements OperatorEvaluator {
@Override
public boolean evaluate(Object actual, Object expected) {
if (actual == null) return expected == null;
return actual.equals(
coerce(expected, actual.getClass()));
}
}
// Set membership operator
public class InSetOperator implements OperatorEvaluator {
@Override
public boolean evaluate(Object actual, Object expected) {
if (expected instanceof Collection<?> set) {
return set.contains(actual);
}
throw new PolicyEvaluationException(
"IN operator requires collection value");
}
}
// Regex match operator
public class RegexMatchOperator implements OperatorEvaluator {
private final Cache<String, Pattern> patternCache =
Caffeine.newBuilder().maximumSize(1000).build();
@Override
public boolean evaluate(Object actual, Object expected) {
Pattern compiled = patternCache.get(
expected.toString(), Pattern::compile);
return compiled.matcher(
actual.toString()).matches();
}
}
// Range operator
public class BetweenOperator implements OperatorEvaluator {
@Override
public boolean evaluate(Object actual, Object expected) {
if (expected instanceof List<?> range
&& range.size() == 2) {
Comparable comp = (Comparable) actual;
Comparable min = (Comparable) coerce(
range.get(0), actual.getClass());
Comparable max = (Comparable) coerce(
range.get(1), actual.getClass());
return comp.compareTo(min) >= 0
&& comp.compareTo(max) <= 0;
}
throw new PolicyEvaluationException(
"BETWEEN requires [min, max] array");
}
}
#4.2 Operator Compatibility Matrix
+----------+--------+-----+------+------+-----------+--------+
| Operator | String | Int | Bool | Time | List<Str> | Double |
+----------+--------+-----+------+------+-----------+--------+
| eq | Y | Y | Y | Y | Y | Y |
| ne | Y | Y | Y | Y | Y | Y |
| gt | Y | Y | - | Y | - | Y |
| lt | Y | Y | - | Y | - | Y |
| in | Y | Y | - | - | - | Y |
| contains | Y | - | - | - | Y | - |
| matches | Y | - | - | - | - | - |
| between | Y | Y | - | Y | - | Y |
+----------+--------+-----+------+------+-----------+--------+
#5. Attribute Resolution and Collection
#5.1 Attribute Collection Architecture
+-------------------+
| Request Arrives |
+-------------------+
|
+----v----+
| Attr | Collect attributes from multiple sources
| Collector|
+----+----+
|
+----+----+----+----+
| | | | |
v v v v v
JWT LDAP DB Env Custom
Attrs Query Query Aware Providers
|
+----v----+
| Attr | Merge, validate, cache
| Context |
+----+----+
|
+----v----+
| ABAC | Policy evaluation
| Evaluator|
+---------+
#5.2 Attribute Collector Implementation
@Component
public class AttributeCollector {
private final List<AttributeProvider> providers;
private final Cache<String, Map<String, Object>> cache;
public EvaluationContext collectAttributes(
AuthenticationContext auth,
ResourceRef resource,
HttpServletRequest request) {
// Subject attributes
Map<String, Object> subjectAttrs = new HashMap<>();
subjectAttrs.put("user_id", auth.getUserId());
subjectAttrs.put("roles", auth.getRoles());
subjectAttrs.put("department",
auth.getClaim("department"));
subjectAttrs.put("clearance_level",
auth.getClaim("clearance_level"));
subjectAttrs.put("mfa_enabled",
auth.isMfaAuthenticated());
// Resource attributes (cached)
Map<String, Object> resourceAttrs = cache.get(
resource.getId(),
() -> loadResourceAttributes(resource));
// Environment attributes
Map<String, Object> envAttrs = new HashMap<>();
envAttrs.put("current_time",
LocalTime.now().toString());
envAttrs.put("day_of_week",
LocalDate.now().getDayOfWeek().name());
envAttrs.put("source_ip",
request.getRemoteAddr());
envAttrs.put("network_zone",
classifyNetworkZone(
request.getRemoteAddr()));
// Custom attribute providers
for (AttributeProvider p : providers) {
p.enhance(subjectAttrs, resourceAttrs,
envAttrs);
}
return new EvaluationContext(
subjectAttrs, resourceAttrs, envAttrs);
}
private String classifyNetworkZone(String ip) {
if (ip.startsWith("10.")
|| ip.startsWith("192.168."))
return "INTERNAL";
if (ip.startsWith("172."))
return "DMZ";
return "EXTERNAL";
}
}
#6. Performance Optimization
#6.1 Short-Circuit Evaluation
// AND expression short-circuit evaluation
public boolean evaluateAndExpression(
List<LogicalExpression> children,
EvaluationContext ctx) {
for (LogicalExpression child : children) {
if (!evaluate(child, ctx)) {
return false; // Short-circuit
}
}
return true;
}
// OR expression short-circuit evaluation
public boolean evaluateOrExpression(
List<LogicalExpression> children,
EvaluationContext ctx) {
for (LogicalExpression child : children) {
if (evaluate(child, ctx)) {
return true; // Short-circuit
}
}
return false;
}
#6.2 Policy Compilation Optimization
public class CompiledPolicy {
private final Map<String, Pattern> compiledPatterns;
private final List<Condition> sortedConditions;
public static CompiledPolicy compile(PolicyRule rule) {
CompiledPolicy compiled = new CompiledPolicy();
// Extract and compile all regex patterns
for (Condition c : rule.getAllConditions()) {
if ("matches".equals(c.getOperator())) {
compiled.compiledPatterns.put(
c.getValue().toString(),
Pattern.compile(
c.getValue().toString()));
}
}
// Sort conditions by evaluation cost
compiled.sortedConditions = rule.getAllConditions()
.stream()
.sorted(Comparator.comparingInt(
CompiledPolicy::estimateCost))
.collect(Collectors.toList());
return compiled;
}
private static int estimateCost(Condition c) {
return switch (c.getOperator()) {
case "eq", "ne" -> 1;
case "gt", "lt", "gte", "lte" -> 2;
case "in" -> 3;
case "between" -> 4;
case "contains" -> 5;
case "matches" -> 10; // Regex is most expensive
default -> 5;
};
}
}
#6.3 Performance Benchmarks
| Scenario | Avg Latency | P99 Latency |
|---|---|---|
| Simple eq condition (1-2) | 0.1ms | 0.5ms |
| Compound AND (3-5) | 0.3ms | 1.2ms |
| Nested AND/OR (5-10) | 0.8ms | 3.5ms |
| With regex match | 1.5ms | 6.0ms |
| With attribute value ref | 1.2ms | 5.0ms |
#7. Practical Policy Examples
#7.1 Work Hours Restriction Policy
policy:
id: "abac-time-restriction"
name: "Work Hours Data Access Restriction"
effect: DENY
conditions:
operator: AND
children:
- attribute: "resource.classification"
op: "gte"
value: "B2"
- operator: OR
children:
- attribute: "environment.current_time"
op: "lt"
value: "08:00"
- attribute: "environment.current_time"
op: "gt"
value: "20:00"
#7.2 Geo-Fencing Policy
policy:
id: "abac-geo-fence"
name: "China Data Restricted to Domestic Access"
effect: DENY
conditions:
operator: AND
children:
- attribute: "resource.data_region"
op: "eq"
value: "CN"
- attribute: "environment.source_country"
op: "ne"
value: "CN"
#7.3 Security Clearance Matching Policy
policy:
id: "abac-clearance-match"
name: "Security Clearance Matching"
effect: ALLOW
conditions:
operator: AND
children:
- attribute: "subject.clearance_level"
op: "gte"
value_ref: "resource.classification_level"
- attribute: "subject.department"
op: "in"
value_ref: "resource.allowed_departments"
#8. Coordination with RBAC and ReBAC
#8.1 Evaluation Order
Request -> [RBAC Fast Path] -> [ABAC Refinement] -> [ReBAC Check]
| | |
Fast allow/deny Condition eval Graph traversal
| | |
+----> DENY-overrides merge <--------------+
#8.2 ABAC Augmenting RBAC Pattern
Scenario: DATA_ANALYST has READ permission (RBAC ALLOW)
but needs ABAC further constraints:
RBAC: DATA_ANALYST -> READ -> DATASET (ALLOW)
ABAC: + classification < C1 (Condition 1)
+ network_zone = INTERNAL (Condition 2)
+ time between 08:00-20:00 (Condition 3)
Final: Allow only when all ABAC conditions are satisfied
#9. Policy Management
#9.1 Policy Version Control
Policy change workflow:
1. Write policy YAML
2. Policy validation (syntax, attribute refs, type checks)
3. Impact analysis (simulate against existing requests)
4. Approval workflow
5. Canary release (verify in test environment first)
6. Full release
7. Monitoring and alerting
#9.2 Policy Simulation
from ontology_sdk import OntoPlatform
platform = OntoPlatform(endpoint="grpc://control-Layer:9090")
simulation = await platform.policy.simulate(
policy_id="abac-dataset-access-001",
test_cases=[
{
"subject": {"clearance_level": "B3",
"department": "engineering"},
"resource": {"classification": "B2",
"type": "DATASET"},
"environment": {"time": "10:00",
"network_zone": "INTERNAL"},
"expected": "ALLOW"
},
{
"subject": {"clearance_level": "A2",
"department": "marketing"},
"resource": {"classification": "C1",
"type": "DATASET"},
"environment": {"time": "22:00",
"network_zone": "EXTERNAL"},
"expected": "DENY"
}
]
)
for result in simulation.results:
print(f"Case: {result.case_id}, "
f"Decision: {result.decision}, "
f"Match: {result.matches_expected}")
#10. Monitoring and Alerting
#10.1 ABAC Evaluation Metrics
| Metric | Description | Alert Threshold |
|---|---|---|
| abac_evaluation_duration_ms | Evaluation time | P99 > 10ms |
| abac_evaluation_total | Total evaluations | - |
| abac_evaluation_deny_rate | Deny rate | > 40% |
| abac_attribute_resolution_errors | Attr resolution errors | > 0 |
| abac_policy_compilation_errors | Policy compilation errors | > 0 |
| abac_cache_hit_ratio | Attribute cache hit rate | < 60% |
#Key Takeaways
- Four attribute categories: Subject, resource, action, and environment attributes provide unlimited flexibility for authorization expressions
- 8 operators cover equality comparison, range checks, set membership, regex matching, and all enterprise-grade requirements
- Expression trees support AND/OR/NOT nesting to express arbitrarily complex policy logic
- Short-circuit evaluation and policy compilation optimization ensure sub-millisecond evaluation performance
- Attribute value references allow conditions to reference other attributes (e.g., subject.clearance >= resource.classification)
- Policy simulation capability makes policy changes safe and controllable
#Next Article
The next article S6-04: ReBAC Implementation: Relationship-Based Access Control (Like Zanzibar) will explain how coomia-dip draws from Google's Zanzibar paper to implement relationship-based access control through relationship tuples and graph traversal.
Tags: #ABAC #AttributeAccessControl #PolicyEngine #Operators #FineGrainedPermissions #ExpressionEngine #coomia-dip #PlatformEngineering #Security #Palantir