Implementing RBAC for Patent Docketing Systems

Role-based access control for a patent docketing system is the mapping from a small set of job roles — paralegal, docketing manager, responsible attorney, external counsel, auditor — to an explicit permission matrix over docketing resources, enforced so that no single role can both edit and unilaterally commit a discretionary change to a statutory deadline. This page specifies that matrix, the least-privilege and separation-of-duties rules behind it, and a focused Python guard that enforces it.

It sits beneath the Security & Access Control Boundaries reference, which layers attribute-based rules and token scopes on top of the role model built here. Where that page governs how a token proves a scope, this one governs what each role may do and why the right to override a date must be split across two people. The audit records it produces feed Malpractice Compliance Reporting, and the reports themselves are generated under the same role controls described in Exporting Compliance Reports (CSV/XML) for Malpractice Insurers.

Roles as the unit of least privilege

RBAC works when roles model actual accountability, not org-chart convenience. Five roles cover a docketing operation, and each maps to a floor of privilege that the matrix then narrows further:

  • Paralegal — the day-to-day operator. Views and edits routine matter data and proposes deadline changes, but never approves or unilaterally overrides a statutory date.
  • Docketing manager — supervises the calendar. Approves routine changes, exports compliance data, and may propose a discretionary date override, but cannot both propose and commit the same one.
  • Responsible attorney — bears the professional duty for the matter. Holds the authority to approve and commit a discretionary date override, scoped to matters they are assigned to.
  • External counsel — outside firm or foreign associate. Read-only visibility, strictly scoped to their own matters, so a shared portal never leaks another client’s portfolio.
  • Auditor — internal risk, compliance, or a malpractice insurer’s reviewer. Read-only across the whole docket plus the audit log, and permission to export, but no edit rights whatsoever.

This design applies least privilege as required by control AC-6 of NIST SP 800-53 Rev. 5: a principal receives only the permissions its role demonstrably needs. The role definitions themselves are the core RBAC construct standardized in the NIST Role-Based Access Control model — permissions attach to roles, and users acquire permissions solely by being assigned a role, never directly.

Operational Action: Assign every user exactly one primary role per matter and grant permissions only through roles. A permission granted directly to a user is invisible to the next access review and is the first thing a malpractice auditor will flag.

The permission matrix

Permissions are the cross-product of actions and resources. The actions are view, edit, approve, override, and export; the resources are the deadline record, matter data, the audit log, and the compliance export. The matrix below states role capability against the actions, with the resource nuance noted inline.

Role View Edit Approve Override Export
Paralegal ✓ routine fields
Docketing manager ✓ routine changes ◐ propose only
Responsible attorney ✓ own matters ✓ own matters ✓ commit
External counsel ◐ own matters
Auditor ✓ + audit log

Two cells carry the whole compliance weight. Override is the discretionary re-setting of a computed statutory date — the single most dangerous operation in a docketing system, because a wrong override manufactures a missed deadline that looks intentional after the fact. It is split: a docketing manager may propose an override (◐), but only a responsible attorney may commit it (✓), and the two must be different people. Export is confined to roles with a defensible need — manager, attorney, and auditor — because a compliance export carries privileged prosecution data outside the working system.

Operational Action: Treat the matrix as version-pinned configuration in the rule registry, not as code branches. A change to any cell ships as a reviewed pull request with legal sign-off, exactly like a deadline rule, so the access posture at any past date is reconstructable.

Role to permission matrix grid for patent docketing A grid with five role rows and five action columns. Rows are paralegal, docketing manager, responsible attorney, external counsel and auditor; columns are View, Edit, Approve, Override and Export. Each cell shows one of three states: a filled teal cell marked grant means the role holds the permission, a filled gold cell marked conditional means the permission is limited to the role's own matters or is propose-only under separation of duties, and a pale cell marked with a dash means no permission. The paralegal may view and edit routine fields only. The docketing manager may view, edit, approve routine changes, export, and only propose an override. The responsible attorney may view, edit and approve on their own matters, commit an override, and export. External counsel may only view their own matters. The auditor may view including the audit log and export, but cannot edit, approve or override. The Override column highlights that proposing and committing a date override are held by different roles. ROLE → PERMISSION MATRIX ROLE ↓ / action → View Edit Approve Override Export Paralegal Docketing manager Responsible attorney External counsel Auditor grant routine fields grant grant routine changes propose only grant own matters own matters grant commit grant own matters grant + audit log grant grant — permission held conditional — own matters / propose-only (SoD) — no permission Override: manager proposes, a different attorney commits.

Separation of duties for date overrides

A discretionary override changes a computed statutory date to a value a human asserts is correct — for example, when an office corrects a mailing date after the fact. Because the override can move a deadline earlier or later, it must never be a single-actor operation. Separation of duties, control AC-5 of NIST SP 800-53 Rev. 5, requires that the proposer and the committer be distinct principals. In practice this is a maker-checker (four-eyes) flow: a docketing manager or paralegal proposes the new date with a written justification, and a responsible attorney assigned to the matter reviews and commits it. The system rejects any attempt by one person to do both.

This also reflects the supervising-attorney’s professional duty. Under ABA Model Rule 5.3, a lawyer with managerial authority must ensure nonlawyer assistants’ work is compatible with the lawyer’s professional obligations, so the attorney’s commit is not a rubber stamp — it is the point where professional responsibility attaches to the changed date.

A Python RBAC guard

The enforcement layer is a pure, testable permission check plus a decorator that guards any callable. Permissions are looked up from the role, evaluated against the resource action, and the override path additionally verifies that the committing principal differs from the proposer. The check fails closed: an unknown role or action denies.

from dataclasses import dataclass, field
from enum import Enum
from functools import wraps
from typing import Callable, TypeVar

class Action(str, Enum):
    VIEW = "view"
    EDIT = "edit"
    APPROVE = "approve"
    OVERRIDE = "override"   # commit a discretionary date change
    EXPORT = "export"

class Role(str, Enum):
    PARALEGAL = "paralegal"
    DOCKETING_MANAGER = "docketing_manager"
    RESPONSIBLE_ATTORNEY = "responsible_attorney"
    EXTERNAL_COUNSEL = "external_counsel"
    AUDITOR = "auditor"

# Baseline role → permitted actions. "own-matter" scoping and the override
# split are enforced separately below, never encoded as a broad grant.
ROLE_PERMISSIONS: dict[Role, frozenset[Action]] = {
    Role.PARALEGAL: frozenset({Action.VIEW, Action.EDIT}),
    Role.DOCKETING_MANAGER: frozenset(
        {Action.VIEW, Action.EDIT, Action.APPROVE, Action.EXPORT}
    ),
    Role.RESPONSIBLE_ATTORNEY: frozenset(
        {Action.VIEW, Action.EDIT, Action.APPROVE, Action.OVERRIDE, Action.EXPORT}
    ),
    Role.EXTERNAL_COUNSEL: frozenset({Action.VIEW}),
    Role.AUDITOR: frozenset({Action.VIEW, Action.EXPORT}),
}

# Roles whose VIEW/EDIT are confined to matters they are assigned to.
OWN_MATTER_ONLY: frozenset[Role] = frozenset(
    {Role.EXTERNAL_COUNSEL, Role.RESPONSIBLE_ATTORNEY}
)

@dataclass(frozen=True)
class Principal:
    user_id: str
    role: Role
    assigned_matters: frozenset[str] = field(default_factory=frozenset)

class AccessDenied(PermissionError):
    """Raised on any failed check — the guard always fails closed."""

def can(principal: Principal, action: Action, matter_id: str,
        *, proposer_id: str | None = None) -> bool:
    """Return True only if the role grants the action for this matter.

    Override additionally enforces separation of duties: the committing
    principal must differ from whoever proposed the change.
    """
    permitted = ROLE_PERMISSIONS.get(principal.role, frozenset())
    if action not in permitted:
        return False
    # Least privilege: own-matter roles cannot reach unassigned matters.
    if principal.role in OWN_MATTER_ONLY and matter_id not in principal.assigned_matters:
        return False
    # Separation of duties: the override committer cannot be the proposer.
    if action is Action.OVERRIDE and proposer_id == principal.user_id:
        return False
    return True

F = TypeVar("F", bound=Callable[..., object])

def require(action: Action) -> Callable[[F], F]:
    """Decorator guard: the wrapped function's first two arguments must be
    the acting Principal and the matter_id; override also takes proposer_id."""
    def decorator(func: F) -> F:
        @wraps(func)
        def wrapper(principal: Principal, matter_id: str, *args, **kwargs):
            proposer_id = kwargs.get("proposer_id")
            if not can(principal, action, matter_id, proposer_id=proposer_id):
                raise AccessDenied(
                    f"{principal.role.value} may not {action.value} matter {matter_id}"
                )
            return func(principal, matter_id, *args, **kwargs)
        return wrapper  # type: ignore[return-value]
    return decorator

@require(Action.OVERRIDE)
def commit_deadline_override(principal: Principal, matter_id: str,
                             new_date: str, *, proposer_id: str) -> dict[str, str]:
    # Reaches here only for an assigned attorney who is NOT the proposer.
    return {"matter_id": matter_id, "deadline": new_date, "committed_by": principal.user_id}

# Verify: an attorney cannot commit their own proposed override (fails closed).
# attorney = Principal("at-1", Role.RESPONSIBLE_ATTORNEY, frozenset({"M-42"}))
# commit_deadline_override(attorney, "M-42", "2026-09-01", proposer_id="at-1")  -> AccessDenied
# commit_deadline_override(attorney, "M-42", "2026-09-01", proposer_id="pl-9")  -> ok

Operational Action: Enforce the guard at the service boundary, not only in the UI. A UI that hides the override button but leaves the endpoint ungated is exactly the gap a compromised paralegal token exploits — the can() check must run server-side on every mutating call.

Least privilege in practice

Least privilege is easy to state and easy to erode. Three disciplines keep it intact. First, run periodic access recertification: on a fixed cadence, the docketing manager and a supervising attorney re-attest every role assignment, and any user with no docketing activity in the window is downgraded. Second, forbid direct user permissions entirely — if a one-off need arises, create or extend a role, review it, and assign it, so the grant is visible to the next audit. Third, scope external counsel to their own matters with an explicit assignment list, never a firm-wide read role, so a shared associate portal cannot enumerate other clients’ portfolios.

The auditor role is deliberately read-plus-export with zero write. A malpractice insurer’s reviewer must be able to reconstruct the docket and export the compliance record without any ability to alter it — the same tamper-evidence goal the parent Security & Access Control Boundaries model enforces with its append-only, hash-chained ledger.

Known pitfalls

  • Role explosion. Adding a bespoke role per attorney defeats RBAC. Keep the role set small and use own-matter scoping for per-person restriction.
  • Approve and override conflated. Approving a routine change and committing a discretionary date override are different rights. Fold them together and you lose the separation of duties that makes an override defensible.
  • UI-only enforcement. Hiding a control is not access control. Every check must run server-side and fail closed.
  • Auditor with write access. An auditor who can edit anything cannot certify the record they reviewed. Keep the role strictly read-plus-export.

Frequently Asked Questions

Why split proposing and committing a deadline override across two roles?
Because a discretionary override moves a statutory date, and a single-actor change to a deadline is the classic manufactured-malpractice risk. Separation of duties, control AC-5 of NIST SP 800-53, requires the proposer and committer to be different principals: a manager or paralegal proposes the new date with justification, and an assigned responsible attorney reviews and commits it. The guard rejects any attempt by one person to do both.
Should external counsel get a read-only role across the whole docket?
No. External counsel is scoped to their own assigned matters through an explicit assignment list, never a firm-wide read grant. A shared associate portal with broad read access would let one outside firm enumerate another client's portfolio, so the own-matter check runs on every view and denies access to any matter not on the principal's list.
Is RBAC alone enough, or do I still need attribute-based rules?
RBAC sets the baseline: it decides what a role may do at all. Attribute-based rules add context the role cannot express — jurisdiction, deadline criticality, and client data residency — and are layered on top in the Security and Access Control Boundaries model. In a docketing system you need both: RBAC narrows the action space, and ABAC constrains which specific resource a valid role may touch right now.
What access should a malpractice insurer's auditor be given?
Read access across the full docket including the audit log, plus permission to export the compliance report, and no edit, approve, or override rights at all. The auditor must be able to reconstruct and export the record without the ability to alter it, which is what lets them certify that what they reviewed matches what the system holds.

↑ Back to Security & Access Control Boundaries