Memra

Read-only and read-write, per role, per column group

◈ 4 cards

Turning a schema and a list of job functions into a defensible access design: split the data by column group first, then assign, then justify against least privilege and separation of duty.

The method, which is what is actually being graded

A question that hands you a schema and three job titles and asks for read-only and read-write access is not testing whether you can fill in a grid. It is testing whether you can reason from job function to permission, and the marks are in the justification and the granularity, not in the cells.

There is one move that decides most of the outcome, and it comes first: do not assign per table. Assigning "read-write on the inventory table" to anyone means you have assigned by convenience, and it hands that role the ability to edit fields that have nothing to do with their job. Split the data into column groups — sets of columns that belong to a single business purpose — and assign against those.

For a purchasing department's parts-and-vendors inventory, the natural groups are:

  • Part identity — name, model number, description, colour, size. Reference data. Almost nobody should be writing it.
  • Stock level — quantity in stock, location. Changes constantly, and only when goods physically move.
  • Vendor identity — name, address, contact details. Reference data owned by purchasing.
  • Pending purchase orders — what has been ordered and not yet received.
  • Closed purchase orders — what has been received and is being, or has been, paid.

Now assign each role against each group, and write down the reason every time.

Worked example — three roles over the purchasing inventory

Accounts payable clerk. Their job is to settle invoices against what was actually ordered and received. So:

  • Part identity: read. They must be able to see what a line item refers to, and they never author part data.
  • Stock level: no access. Nothing in settling an invoice requires knowing how many are on the shelf, and least privilege says an unnecessary right is a liability.
  • Vendor identity: read. They need the payee's name and address; they must not be able to change it, because a clerk who can edit a vendor's bank address and then approve payment to it is a fraud waiting to happen.
  • Pending purchase orders: read. They must see what was committed; they are not the party who commits it.
  • Closed purchase orders: read-write. This is their own workspace — recording invoice receipt, payment status, disputes.

Receiving clerk. Their job is to book goods in as they arrive at the dock. So:

  • Part identity: read, to identify what has turned up.
  • Stock level: read-write. This is the one field their job exists to change.
  • Vendor identity: no access. Recording a delivery does not require the vendor's financial details.
  • Pending purchase orders: read-write, because marking a pending order as received is their job.
  • Closed purchase orders: no access. Payment is not their function, and this denial is the separation-of-duty boundary.

Installation supervisor. Their job is to plan and dispatch work in the field. So:

  • Part identity: read, to specify what a job needs.
  • Stock level: read, to know whether it is available.
  • Vendor identity, pending orders, closed orders: no access at all. None of the purchasing or financial side of the department is their function. A design that gives the supervisor read on everything "because it is only read" has not applied least privilege — read includes copying and printing, and a supervisor walking out with the vendor list is a real loss.

The two principles the marks hang on

Least privilege is the rule that each role receives the minimum set of rights its function requires, and it applies to reads as much as to writes. Every one of the denials above should be argued, not merely left blank: the supervisor is denied vendor identity because field dispatch does not need supplier commercial data, not because the grid ran out of room.

Separation of duty is the rule that no single role may hold both halves of a transaction that could be abused. The live conflict here is explicit: the person who books goods in must not also be the person who approves payment for them. If one role held both, that person could record a fictitious delivery from a vendor they control and then settle the invoice for it. In RBAC terms, you express this as a mutually exclusive roles constraint — Receiving Clerk and Accounts Payable Clerk may not be held by the same user — which is RBAC₂, and it upgrades fraud from a solo act to a conspiracy.

If the department is small enough that one person must cover both, the constraint does not disappear; it becomes a compensating control — a second signature above a threshold, or an exception report reviewed by someone who holds neither role.

The same question with different roles

Expect this problem with the role names changed — a warehouse supervisor instead of an installation supervisor, a purchasing agent instead of an accounts payable clerk. The method is invariant, and that is the point: derive the permissions from what the job does, never from a table you memorised. Take each new role, ask what transaction it is responsible for, give it read-write on the data that transaction changes, read on the data it needs to see, and an argued denial everywhere else.

When roles stop being enough

RBAC assigns rights to job functions. It breaks down when the decision depends on things that are not job functions — the time of day, the requester's location, a property of the record rather than of the table. Suppose the supervisor may see stock levels only for the depot they are assigned to. In RBAC that is one role per depot, and if you then add a second distinction — say, only during a shift — the role count becomes the product of the two ranges, not the sum. This is role explosion, and it is exponential in the number of attributes.

ABAC writes the rule over attributes instead: can_access(s, o, e) as a Boolean function of subject, object and environment attributes, where a role is simply one of the subject attributes. The depot condition is one conjunct and the shift condition is a second. ABAC can express DAC, RBAC and MAC — it is the general case — and its cost is real: it evaluates predicates on both sides of every single access, which is only tolerable where per-access cost is already high. There is a governance cost too. With an ACL the root of trust is the object's owner; with ABAC it is whoever asserts the attributes.

RolePartsStockVendorsPending POClosed POA/P clerkRRRRWReceivingclerkRRWRWInstall.supervisorRRBooking goods in and approving payment never share a role.
Assign against column groups, never against whole tables. Each read-write cell is the data that role’s own transaction changes; every dash is a denial you should be able to justify out loud.
RBACABACThe rule is written overthe roles a user holdssubject, object andenvironment attributesOne more distinction costsa product of the rangesone extra conjunctCost per accessa role lookupevaluating predicates onboth sidesRoot of trust isthe object owner oradministratorwhoever asserts theattributesA role is just one subject attribute — ABAC is the general case.
Role explosion is the quantitative case for ABAC: a new distinction multiplies the role count but only adds a conjunct to an attribute rule.
NORMAL ~/memra/learn/comp-400/designing-rbac-roles-over-an-inventory-database utf-8 LF