Skip to content

Access Control

access_control

Access Control Simulator — RBAC/ABAC policy modelling for AI agents.

Define roles, permissions, and attribute-based rules, then test access decisions and detect privilege-escalation paths.

CLI usage::

# List built-in policies
python -m replication access-control --list

# Evaluate a request against the default policy
python -m replication access-control --policy strict \
    --agent worker --action replicate --resource cluster_a

# Detect privilege escalation paths
python -m replication access-control --policy permissive --escalation

# Full audit — evaluate every agent×action×resource combination
python -m replication access-control --policy strict --audit

# Export policy as JSON
python -m replication access-control --policy strict --export json

# Generate interactive HTML dashboard
python -m replication access-control --policy strict -o dashboard.html

Permission dataclass

A single permission: action on resource.

Source code in src/replication/access_control.py
@dataclass
class Permission:
    """A single permission: action on resource."""
    action: str
    resource: str = "*"
    conditions: Dict[str, Any] = field(default_factory=dict)

    def matches(self, action: str, resource: str, attrs: Dict[str, Any]) -> bool:
        if self.action != "*" and self.action != action:
            return False
        if self.resource != "*" and self.resource != resource:
            return False
        for key, expected in self.conditions.items():
            if attrs.get(key) != expected:
                return False
        return True

Role dataclass

Named role with a set of permissions and optional parent roles.

Source code in src/replication/access_control.py
@dataclass
class Role:
    """Named role with a set of permissions and optional parent roles."""
    name: str
    permissions: List[Permission] = field(default_factory=list)
    inherits: List[str] = field(default_factory=list)

Agent dataclass

An agent with roles and attributes (for ABAC).

Source code in src/replication/access_control.py
@dataclass
class Agent:
    """An agent with roles and attributes (for ABAC)."""
    name: str
    roles: List[str] = field(default_factory=list)
    attributes: Dict[str, Any] = field(default_factory=dict)

AccessPolicy

RBAC + ABAC policy engine.

Source code in src/replication/access_control.py
class AccessPolicy:
    """RBAC + ABAC policy engine."""

    def __init__(self, name: str = "custom", default: Decision = Decision.DENY):
        self.name = name
        self.default = default
        self.roles: Dict[str, Role] = {}
        self.agents: Dict[str, Agent] = {}
        self.deny_rules: List[Permission] = []  # explicit deny always wins

    def add_role(self, role: Role) -> None:
        self.roles[role.name] = role

    def add_agent(self, agent: Agent) -> None:
        self.agents[agent.name] = agent

    def add_deny_rule(self, perm: Permission) -> None:
        self.deny_rules.append(perm)

    def _resolve_permissions(self, role_name: str, seen: Optional[Set[str]] = None) -> List[Tuple[str, Permission]]:
        """Recursively collect permissions from a role and its parents."""
        if seen is None:
            seen = set()
        if role_name in seen or role_name not in self.roles:
            return []
        seen.add(role_name)
        role = self.roles[role_name]
        result: List[Tuple[str, Permission]] = [(role_name, p) for p in role.permissions]
        for parent in role.inherits:
            result.extend(self._resolve_permissions(parent, seen))
        return result

    def evaluate(self, req: AccessRequest) -> AccessResult:
        agent = self.agents.get(req.agent)
        if agent is None:
            return AccessResult(req, Decision.DENY, reason=f"Unknown agent: {req.agent}")

        attrs = {**agent.attributes, **req.context}

        # Explicit deny rules win
        for d in self.deny_rules:
            if d.matches(req.action, req.resource, attrs):
                return AccessResult(req, Decision.DENY, matched_permission=d,
                                    reason=f"Explicit deny rule: {d.action}@{d.resource}")

        # Check RBAC + ABAC
        for role_name in agent.roles:
            for src_role, perm in self._resolve_permissions(role_name):
                if perm.matches(req.action, req.resource, attrs):
                    return AccessResult(req, Decision.ALLOW, matched_role=src_role,
                                        matched_permission=perm,
                                        reason=f"Granted via role '{src_role}'")

        return AccessResult(req, self.default, reason="No matching permission (default policy)")

    def audit(self) -> List[AccessResult]:
        """Evaluate every agent × action × resource combination."""
        actions: Set[str] = set()
        resources: Set[str] = set()
        for role in self.roles.values():
            for p in role.permissions:
                if p.action != "*":
                    actions.add(p.action)
                if p.resource != "*":
                    resources.add(p.resource)
        for d in self.deny_rules:
            if d.action != "*":
                actions.add(d.action)
            if d.resource != "*":
                resources.add(d.resource)
        if not actions:
            actions = {"read", "write", "replicate", "execute"}
        if not resources:
            resources = {"cluster_a", "cluster_b", "model_weights", "config"}

        results = []
        for agent_name in sorted(self.agents):
            for action in sorted(actions):
                for resource in sorted(resources):
                    req = AccessRequest(agent_name, action, resource)
                    results.append(self.evaluate(req))
        return results

    def find_escalation_paths(self) -> List[Dict[str, Any]]:
        """Detect privilege escalation risks via role inheritance chains."""
        paths = []

        # 1. Find circular inheritance
        for role_name in self.roles:
            visited: Set[str] = set()
            stack = [role_name]
            while stack:
                current = stack.pop()
                if current in visited:
                    paths.append({
                        "type": "circular_inheritance",
                        "severity": "HIGH",
                        "role": role_name,
                        "detail": f"Role '{role_name}' has circular inheritance path"
                    })
                    break
                visited.add(current)
                if current in self.roles:
                    stack.extend(self.roles[current].inherits)

        # 2. Wildcard permissions
        for role_name, role in self.roles.items():
            for perm in role.permissions:
                if perm.action == "*" and perm.resource == "*":
                    paths.append({
                        "type": "wildcard_permission",
                        "severity": "CRITICAL",
                        "role": role_name,
                        "detail": f"Role '{role_name}' has */* (god mode) permission"
                    })
                elif perm.action == "*":
                    paths.append({
                        "type": "wildcard_action",
                        "severity": "HIGH",
                        "role": role_name,
                        "detail": f"Role '{role_name}' allows all actions on '{perm.resource}'"
                    })

        # 3. Agents with multiple powerful roles
        powerful = set()
        for role_name, role in self.roles.items():
            total = len(list(self._resolve_permissions(role_name)))
            if total >= 5:
                powerful.add(role_name)

        for agent_name, agent in self.agents.items():
            overlap = set(agent.roles) & powerful
            if len(overlap) >= 2:
                paths.append({
                    "type": "role_accumulation",
                    "severity": "MEDIUM",
                    "agent": agent_name,
                    "roles": sorted(overlap),
                    "detail": f"Agent '{agent_name}' holds multiple powerful roles: {sorted(overlap)}"
                })

        # 4. Inheriting from admin/root roles
        admin_roles = {n for n in self.roles if any(k in n.lower() for k in ("admin", "root", "super"))}
        for role_name, role in self.roles.items():
            if role_name not in admin_roles:
                inherited = set()
                self._collect_parents(role_name, inherited)
                escalation = inherited & admin_roles
                if escalation:
                    paths.append({
                        "type": "admin_inheritance",
                        "severity": "HIGH",
                        "role": role_name,
                        "inherits_from": sorted(escalation),
                        "detail": f"Role '{role_name}' inherits from admin role(s): {sorted(escalation)}"
                    })

        return paths

    def _collect_parents(self, role_name: str, collected: Set[str], seen: Optional[Set[str]] = None) -> None:
        if seen is None:
            seen = set()
        if role_name in seen or role_name not in self.roles:
            return
        seen.add(role_name)
        for parent in self.roles[role_name].inherits:
            collected.add(parent)
            self._collect_parents(parent, collected, seen)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "name": self.name,
            "default": self.default.value,
            "roles": {
                name: {
                    "permissions": [{"action": p.action, "resource": p.resource, "conditions": p.conditions} for p in role.permissions],
                    "inherits": role.inherits,
                }
                for name, role in self.roles.items()
            },
            "agents": {
                name: {"roles": a.roles, "attributes": a.attributes}
                for name, a in self.agents.items()
            },
            "deny_rules": [{"action": d.action, "resource": d.resource, "conditions": d.conditions} for d in self.deny_rules],
        }

audit() -> List[AccessResult]

Evaluate every agent × action × resource combination.

Source code in src/replication/access_control.py
def audit(self) -> List[AccessResult]:
    """Evaluate every agent × action × resource combination."""
    actions: Set[str] = set()
    resources: Set[str] = set()
    for role in self.roles.values():
        for p in role.permissions:
            if p.action != "*":
                actions.add(p.action)
            if p.resource != "*":
                resources.add(p.resource)
    for d in self.deny_rules:
        if d.action != "*":
            actions.add(d.action)
        if d.resource != "*":
            resources.add(d.resource)
    if not actions:
        actions = {"read", "write", "replicate", "execute"}
    if not resources:
        resources = {"cluster_a", "cluster_b", "model_weights", "config"}

    results = []
    for agent_name in sorted(self.agents):
        for action in sorted(actions):
            for resource in sorted(resources):
                req = AccessRequest(agent_name, action, resource)
                results.append(self.evaluate(req))
    return results

find_escalation_paths() -> List[Dict[str, Any]]

Detect privilege escalation risks via role inheritance chains.

Source code in src/replication/access_control.py
def find_escalation_paths(self) -> List[Dict[str, Any]]:
    """Detect privilege escalation risks via role inheritance chains."""
    paths = []

    # 1. Find circular inheritance
    for role_name in self.roles:
        visited: Set[str] = set()
        stack = [role_name]
        while stack:
            current = stack.pop()
            if current in visited:
                paths.append({
                    "type": "circular_inheritance",
                    "severity": "HIGH",
                    "role": role_name,
                    "detail": f"Role '{role_name}' has circular inheritance path"
                })
                break
            visited.add(current)
            if current in self.roles:
                stack.extend(self.roles[current].inherits)

    # 2. Wildcard permissions
    for role_name, role in self.roles.items():
        for perm in role.permissions:
            if perm.action == "*" and perm.resource == "*":
                paths.append({
                    "type": "wildcard_permission",
                    "severity": "CRITICAL",
                    "role": role_name,
                    "detail": f"Role '{role_name}' has */* (god mode) permission"
                })
            elif perm.action == "*":
                paths.append({
                    "type": "wildcard_action",
                    "severity": "HIGH",
                    "role": role_name,
                    "detail": f"Role '{role_name}' allows all actions on '{perm.resource}'"
                })

    # 3. Agents with multiple powerful roles
    powerful = set()
    for role_name, role in self.roles.items():
        total = len(list(self._resolve_permissions(role_name)))
        if total >= 5:
            powerful.add(role_name)

    for agent_name, agent in self.agents.items():
        overlap = set(agent.roles) & powerful
        if len(overlap) >= 2:
            paths.append({
                "type": "role_accumulation",
                "severity": "MEDIUM",
                "agent": agent_name,
                "roles": sorted(overlap),
                "detail": f"Agent '{agent_name}' holds multiple powerful roles: {sorted(overlap)}"
            })

    # 4. Inheriting from admin/root roles
    admin_roles = {n for n in self.roles if any(k in n.lower() for k in ("admin", "root", "super"))}
    for role_name, role in self.roles.items():
        if role_name not in admin_roles:
            inherited = set()
            self._collect_parents(role_name, inherited)
            escalation = inherited & admin_roles
            if escalation:
                paths.append({
                    "type": "admin_inheritance",
                    "severity": "HIGH",
                    "role": role_name,
                    "inherits_from": sorted(escalation),
                    "detail": f"Role '{role_name}' inherits from admin role(s): {sorted(escalation)}"
                })

    return paths