Permission Configuration Guide
coomia-dip uses a three-layer permission model: RBAC (Role-Based) controls feature access, ABAC (Attribute-Based) controls data access policies, and Row-Level Security (RLS) controls record-level visibility. This guide walks through configuring and managing all three layers with complete examples, including role definitions, policy writing, permission inheritance, and audit logging.
“Series: S12 Developer Tutorials · Article 11 | Level: Intermediate | Reading Time: 15 min
Permission Configuration Guide
#TL;DR
coomia-dip uses a three-layer permission model: RBAC (Role-Based) controls feature access, ABAC (Attribute-Based) controls data access policies, and Row-Level Security (RLS) controls record-level visibility. This guide walks through configuring and managing all three layers with complete examples, including role definitions, policy writing, permission inheritance, and audit logging.
#1. Permission Model Overview
#1.1 Three-Layer Permission Architecture
┌──────────────────────────────────────┐
│ Layer 1: RBAC │
│ (Role → Feature/API Access) │
├──────────────────────────────────────┤
│ Layer 2: ABAC │
│ (Attribute Conditions → Policy) │
├──────────────────────────────────────┤
│ Layer 3: Row-Level Security │
│ (User Context → Row Filtering) │
└──────────────────────────────────────┘
#1.2 Permission Check Flow
User Request
│
▼
① RBAC Check: Does the user's role permit this operation?
│ Pass
▼
② ABAC Check: Do user attributes satisfy the access policy?
│ Pass
▼
③ RLS Filter: Automatically inject row-level filter conditions
│
▼
Return filtered data
#2. RBAC Configuration
#2.1 Defining Roles
# permissions/roles.yaml
roles:
- name: platform_admin
display_name: Platform Administrator
description: Full access to all features
permissions:
- "*"
- name: data_admin
display_name: Data Administrator
permissions:
- "schema:*"
- "object:read"
- "object:create"
- "object:update"
- "object:delete"
- "relation:*"
- "pipeline:*"
- "metric:*"
- "dashboard:read"
- "dashboard:create"
- name: data_analyst
display_name: Data Analyst
permissions:
- "object:read"
- "relation:read"
- "metric:read"
- "metric:query"
- "dashboard:read"
- "dashboard:create"
- "oql:execute"
- "function:invoke"
- name: business_user
display_name: Business User
permissions:
- "object:read"
- "action:execute"
- "dashboard:read"
- "metric:read"
- name: viewer
display_name: Read-Only User
permissions:
- "object:read"
- "dashboard:read"
- "metric:read"
#2.2 Python API Role Management
from ontology_sdk import OntoPlatform
from ontology_sdk.permissions import Role, Permission
platform = OntoPlatform(control_plane_url="localhost:50051")
pm = platform.permissions
# Create role
pm.create_role(
Role(
name="project_manager",
display_name="Project Manager",
permissions=[
Permission("object:read"),
Permission("object:create", resource_type="Project"),
Permission("object:update", resource_type="Project"),
Permission("action:execute"),
Permission("dashboard:read"),
Permission("dashboard:create"),
Permission("metric:read"),
Permission("metric:query"),
]
)
)
# Role inheritance
pm.create_role(
Role(
name="senior_analyst",
display_name="Senior Analyst",
inherits=["data_analyst"],
additional_permissions=[
Permission("pipeline:create"),
Permission("pipeline:execute"),
Permission("function:create"),
]
)
)
# Assign roles to users
pm.assign_role("user-001", "project_manager")
pm.assign_role("user-002", ["data_analyst", "viewer"])
# View user permissions
user_perms = pm.get_user_permissions("user-001")
print(f"Permissions: {[p.name for p in user_perms.permissions]}")
print(f"Roles: {user_perms.roles}")
#2.3 Fine-Grained Resource Permissions
pm.create_role(
Role(
name="hr_manager",
display_name="HR Manager",
permissions=[
Permission("object:read", resource_type="Employee"),
Permission("object:create", resource_type="Employee"),
Permission("object:update", resource_type="Employee"),
Permission("object:delete", resource_type="Employee"),
Permission("object:read", resource_type="Department"),
Permission("object:update", resource_type="Department"),
Permission("action:execute", resource_pattern="hr_*"),
Permission("dashboard:read", resource_pattern="hr_*"),
]
)
)
#3. ABAC Policy Configuration
#3.1 ABAC Policy Structure
# permissions/policies/data_access.yaml
policies:
- name: department_data_isolation
display_name: Department Data Isolation
description: Users can only access data from their own department
effect: allow
target:
object_types: [Employee, Task, Project]
conditions:
all:
- subject.department == resource.department
- subject.status == "active"
priority: 100
- name: sensitive_salary_access
display_name: Salary Data Restriction
description: Only HR and management can view salary fields
effect: deny
target:
object_types: [Employee]
properties: [salary, bonus, stock_options]
conditions:
none:
- subject.role IN ["hr_manager", "platform_admin", "cfo"]
priority: 200
- name: customer_tier_access
display_name: Customer Tier Access Control
description: Regular users can only access Silver tier and below
effect: deny
target:
object_types: [Customer]
conditions:
all:
- resource.tier IN ["platinum", "gold"]
- subject.role NOT IN ["account_manager", "sales_director", "platform_admin"]
priority: 150
#3.2 Python API Policy Management
from ontology_sdk.permissions import Policy, Condition
# Create ABAC policy
pm.create_policy(
Policy(
name="department_data_isolation",
display_name="Department Data Isolation",
effect="allow",
target_object_types=["Employee", "Task", "Project"],
conditions=[
Condition.equals("subject.department", "resource.department"),
Condition.equals("subject.status", "active"),
],
priority=100,
)
)
# Property-level policy (field masking)
pm.create_policy(
Policy(
name="sensitive_field_masking",
display_name="Sensitive Field Masking",
effect="mask",
target_object_types=["Employee"],
target_properties=["phone", "ssn", "bank_account"],
conditions=[
Condition.not_in("subject.role", ["hr_manager", "platform_admin"]),
],
masking_rules={
"phone": "partial", # 138****1234
"ssn": "partial", # ***-**-1234
"bank_account": "full", # ****************
},
priority=200,
)
)
# Time-based policy
pm.create_policy(
Policy(
name="after_hours_readonly",
display_name="After-Hours Read-Only",
effect="deny",
target_actions=["object:create", "object:update", "object:delete"],
conditions=[
Condition.time_outside("08:00", "20:00", timezone="UTC"),
Condition.not_in("subject.role", ["platform_admin"]),
],
priority=50,
)
)
#3.3 Policy Evaluation
# Check access (without executing)
result = pm.check_access(
subject={"user_id": "user-001", "role": "data_analyst", "department": "Engineering"},
action="object:read",
resource={"type": "Employee", "department": "Engineering", "properties": ["name", "salary"]}
)
print(f"Access allowed: {result.allowed}")
print(f"Applied policies: {[p.name for p in result.applied_policies]}")
print(f"Masked properties: {result.masked_properties}")
# Batch permission check
checks = [
{"action": "object:read", "resource": {"type": "Project"}},
{"action": "object:update", "resource": {"type": "Project"}},
{"action": "object:delete", "resource": {"type": "Project"}},
{"action": "pipeline:execute", "resource": {"name": "employee_import"}},
]
batch_results = pm.check_access_batch(
subject={"user_id": "user-001", "role": "business_user"},
checks=checks
)
for check, result in zip(checks, batch_results):
status = "ALLOW" if result.allowed else "DENY"
print(f" {check['action']}: {status}")
#4. Row-Level Security (RLS)
#4.1 RLS Rule Definition
# permissions/rls/department_isolation.yaml
rls_rules:
- name: dept_employee_filter
display_name: Department Employee Visibility
object_type: Employee
filter:
department: $user.department
apply_to:
roles: [business_user, data_analyst]
exclude:
roles: [platform_admin, hr_manager]
- name: region_customer_filter
display_name: Regional Customer Visibility
object_type: Customer
filter:
region: $user.region
apply_to:
roles: [sales_rep, regional_manager]
- name: project_participant_filter
display_name: Project Participant Visibility
object_type: Project
filter_query: |
FIND Project
TRAVERSE participates_in <- Employee
WHERE Employee.rid = $user.employee_rid
apply_to:
roles: [project_member]
#4.2 Python API RLS Management
from ontology_sdk.permissions import RLSRule, RLSFilter
# Create RLS rule
pm.create_rls_rule(
RLSRule(
name="dept_employee_filter",
display_name="Department Employee Visibility",
object_type="Employee",
filter=RLSFilter.equals("department", "$user.department"),
apply_to_roles=["business_user", "data_analyst"],
exclude_roles=["platform_admin", "hr_manager"],
)
)
# Complex RLS (multiple conditions)
pm.create_rls_rule(
RLSRule(
name="multi_condition_filter",
display_name="Multi-Condition Data Filter",
object_type="Order",
filter=RLSFilter.all_of([
RLSFilter.equals("region", "$user.region"),
RLSFilter.greater_than("total_amount", 0),
RLSFilter.in_list("status", ["confirmed", "shipped", "delivered"]),
]),
apply_to_roles=["sales_rep"],
)
)
# Relation-based RLS
pm.create_rls_rule(
RLSRule(
name="manager_team_visibility",
display_name="Manager Team Visibility",
object_type="Employee",
filter_query="""
FIND Employee
TRAVERSE reports_to -> Employee AS manager
WHERE manager.rid = $user.employee_rid
UNION
FIND Employee WHERE rid = $user.employee_rid
""",
apply_to_roles=["team_manager"],
)
)
#4.3 Automatic RLS Injection
RLS rules are automatically injected into all queries:
# User A (sales_rep, region=East) executes query
# Original OQL:
results = platform.oql.execute("FIND Customer SELECT name, tier, revenue")
# Actually executed OQL (RLS auto-injected):
# FIND Customer WHERE region = 'East' SELECT name, tier, revenue
# User B (platform_admin) executes the same query
# Original OQL = Actual OQL (admin is exempt from RLS)
#5. Permission Groups and Inheritance
#5.1 Organizational Permission Structure
# Create permission groups
pm.create_group(
name="engineering_dept",
display_name="Engineering Department",
members=["user-001", "user-002", "user-003"],
roles=["data_analyst"],
attributes={"department": "Engineering", "region": "Shanghai"}
)
pm.create_group(
name="engineering_leads",
display_name="Engineering Tech Leads",
parent_group="engineering_dept",
members=["user-001"],
additional_roles=["project_manager"],
attributes={"level": "lead"}
)
# Permissions are automatically inherited
# engineering_leads has: data_analyst + project_manager roles
#5.2 Multi-Tenant Permission Isolation
pm.create_tenant_policy(
tenant_id="tenant-001",
name="tenant_isolation",
rules=[
RLSRule(
object_type="*",
filter=RLSFilter.equals("tenant_id", "$user.tenant_id"),
apply_to_roles=["*"],
exclude_roles=["super_admin"],
),
Policy(
name="tenant_user_management",
effect="allow",
target_actions=["user:*"],
conditions=[
Condition.equals("resource.tenant_id", "subject.tenant_id"),
],
),
]
)
#6. Audit Logging
#6.1 Viewing Audit Logs
# Query permission-related audit logs
logs = pm.audit_log.query(
time_range=("2025-01-01", "2025-03-31"),
user_id="user-001",
actions=["object:read", "object:update"],
result="denied",
limit=50
)
for log in logs:
print(f"[{log.timestamp}] {log.user_id} -> {log.action}")
print(f" Resource: {log.resource_type}:{log.resource_id}")
print(f" Result: {log.result}")
print(f" Reason: {log.reason}")
print(f" Policy: {log.applied_policy}")
print("---")
# Permission change audit
changes = pm.audit_log.query_changes(
time_range=("2025-01-01", "2025-03-31"),
change_types=["role_assigned", "role_revoked", "policy_created", "policy_updated"]
)
for change in changes:
print(f"[{change.timestamp}] {change.operator} {change.change_type}")
print(f" Target: {change.target}")
print(f" Details: {change.details}")
#6.2 Permission Reports
# Generate permission matrix report
matrix = pm.generate_permission_matrix(
roles=["data_analyst", "project_manager", "business_user"],
resource_types=["Project", "Employee", "Customer", "Order"]
)
print("Permission Matrix:")
header = f"{'Role':<20} {'Project':<15} {'Employee':<15} {'Customer':<15} {'Order':<15}"
print(header)
for role_row in matrix.rows:
perms = [','.join(role_row.permissions.get(rt, ['-'])) for rt in matrix.resource_types]
print(f"{role_row.role:<20} {perms[0]:<15} {perms[1]:<15} {perms[2]:<15} {perms[3]:<15}")
# User permission detail report
user_report = pm.generate_user_report("user-001")
print(f"\nUser: {user_report.user_name}")
print(f"Roles: {user_report.roles}")
print(f"Groups: {user_report.groups}")
print(f"Effective permissions: {len(user_report.effective_permissions)}")
print(f"RLS rules: {len(user_report.rls_rules)}")
print(f"ABAC policies: {len(user_report.applicable_policies)}")
#7. Complete Example: Enterprise Permission Setup
from ontology_sdk import OntoPlatform
from ontology_sdk.permissions import Role, Policy, RLSRule, Condition, RLSFilter, Permission
platform = OntoPlatform(control_plane_url="localhost:50051")
pm = platform.permissions
# === Step 1: Define Role Hierarchy ===
roles = [
Role(name="admin", display_name="Administrator", permissions=[Permission("*")]),
Role(name="dept_manager", display_name="Department Manager", permissions=[
Permission("object:read"), Permission("object:update"),
Permission("action:execute"), Permission("dashboard:*"),
Permission("metric:*"), Permission("oql:execute"),
]),
Role(name="employee", display_name="Employee", permissions=[
Permission("object:read"), Permission("action:execute"),
Permission("dashboard:read"), Permission("metric:read"),
]),
]
for role in roles:
pm.create_role(role)
# === Step 2: ABAC Policies ===
policies = [
Policy(
name="dept_isolation",
effect="allow",
target_object_types=["Employee", "Task"],
conditions=[Condition.equals("subject.department", "resource.department")],
priority=100,
),
Policy(
name="salary_protection",
effect="mask",
target_object_types=["Employee"],
target_properties=["salary", "bonus"],
conditions=[Condition.not_in("subject.role", ["admin", "hr_manager"])],
masking_rules={"salary": "full", "bonus": "full"},
priority=200,
),
]
for policy in policies:
pm.create_policy(policy)
# === Step 3: RLS Rules ===
rls_rules = [
RLSRule(
name="dept_filter",
object_type="Employee",
filter=RLSFilter.equals("department", "$user.department"),
apply_to_roles=["employee", "dept_manager"],
exclude_roles=["admin"],
),
RLSRule(
name="project_access",
object_type="Project",
filter_query="""
FIND Project TRAVERSE participates_in <- Employee
WHERE Employee.rid = $user.employee_rid
""",
apply_to_roles=["employee"],
),
]
for rule in rls_rules:
pm.create_rls_rule(rule)
# === Step 4: Assign Roles ===
pm.assign_role("user-admin", "admin")
pm.assign_role("user-mgr-001", "dept_manager")
pm.assign_role("user-emp-001", "employee")
# === Verify ===
result = pm.check_access(
subject={"user_id": "user-emp-001", "role": "employee", "department": "Engineering"},
action="object:read",
resource={"type": "Employee", "department": "Engineering"}
)
print(f"Employee access own department: {result.allowed}") # True
result2 = pm.check_access(
subject={"user_id": "user-emp-001", "role": "employee", "department": "Engineering"},
action="object:read",
resource={"type": "Employee", "department": "Finance"}
)
print(f"Employee access other department: {result2.allowed}") # False
#Key Takeaways
- Three-layer defense: RBAC for feature access, ABAC for data policies, RLS for record visibility
- Declarative policies: YAML-defined policies, Python API management, hot-update support
- Automatic injection: RLS rules auto-inject into all queries -- developers never filter manually
- Field masking: ABAC supports property-level masking policies
- Complete audit trail: All permission operations and changes are traceable
- Multi-tenant support: Tenant-level isolation policies work out of the box
#Next Article
Next: S12-12 Data Import Guide — Learn how to connect various data sources to the coomia-dip platform.
Tags: Permissions RBAC ABAC RLS Security Data Isolation coomia-dip