BE Teck Notes Back to be-teck.com

5 min read

Who is allowed to decide what

Most permission systems answer "what may this person see". The harder question is what they may decide, and organisations usually cannot state their own answer.

Ask an organisation who is allowed to approve a payment and you will get an answer. Ask who is allowed to cancel a goods receipt, revise an order total, release retention, export the vendor list, or write off a balance, and the answers get vaguer, then contradictory, then absent.

That vagueness is not a documentation problem. It is a design problem, and it surfaces as software behaviour that nobody intended.

Seeing is not deciding#

The commonest structural mistake is to treat visibility and authority as one scale, so that anybody senior enough to see something is by construction allowed to change it.

They are different axes. A site engineer may need to see every payment on their project and should not be able to authorise one. An accountant may need to adjust stock valuations without being able to see who is on leave. A super administrator may see everything and still should not be able to move money alone, for reasons in why one person should never move the company's money.

Collapsing the two produces a system where the only way to give somebody information is to give them power, and the practical result is that people are either over-privileged or blind.

The permission checks that are actually wrong#

Three specific faults we found in our own system, each of which is common enough to be worth checking for in yours.

A check that asks the wrong question. The function that reversed a stock entry asked only whether the caller was an administrator — and, separately, allowed anybody whose name appeared on a row to cancel that row. Neither is the rule anybody would have written down if asked. Both were the rule for as long as nobody asked.

A check that reads the wrong field. We introduced a category of account that must be refused certain things, and implemented the test as a comparison against the account's primary role. That comparison misses any account carrying a second role, because the role field holds only the primary hat. The correct implementation was a property of the account in its own right, not a value of a field that happens to usually contain it.

That is a general trap. Any check written as role equals X is fragile the moment a person can hold two roles, and people always end up holding two roles.

A check that leaks through a second door. Our stock reversal could reach through the store's interface and cancel an entry that the gate had written — leaving the gate's own register saying the lorry arrived and the entry impossible to restore. The permission on the store door was correct. There was a second door.

The lesson from the third is procedural rather than technical: enumerate the doors. Every way into a piece of data, including the ones added later for a different purpose, and check each one against the same rule. A capability reachable by two routes needs every control that applies to the first route.

Errors that answer questions#

A subtle one, worth its own paragraph because it is easy to build by accident.

If a system responds differently to that record does not exist and that record exists but is not yours, then anybody can determine which records exist by guessing at identifiers and reading the difference in the responses. We had exactly this: a card could be filed onto any task in the company, signed with the filer's name, on a task they could not see — and its two different errors answered does task X exist? for anything anybody cared to guess.

The same refusal, either way. It feels unhelpful and it is the correct behaviour.

Grant, do not assume#

The most useful change we made in this area was to stop treating capabilities as implied by seniority and start treating them as granted keys.

Exporting data and importing data are the clearest examples. Both are powerful — an export takes information out of the building and an import writes over things — and both had historically been available to whoever happened to be senior enough to find the screen.

They are now explicit grants: held implicitly by the few people at the top, and given to anybody else person by person, on a panel where you can see the whole list and take one back.

Two properties make that work. The list must be visible in one place, so somebody can look at it and be surprised. And a grant must be revocable in one action, because a permission you cannot easily withdraw is one people are reluctant to give, and reluctance produces workarounds.

Delegation is a permission with a clock#

People travel. Work must continue. So authority gets handed over, and the handover is where most permission designs quietly fail.

The version that holds:

  • Bounded in time, with an end date.
  • Revocable by either party at any point.
  • Never granted to somebody who already holds the counterpart authority, enforced by the system rather than by the good sense of whoever sets it up.
  • Visible on the record, so the audit trail says on behalf of rather than silently recording the stand-in as the decider.
  • Excluding the exceptional powers. An emergency override stays with its original holder; a stand-in gets the ordinary authority.

Without the last two you have a system where, after the fact, nobody can tell who actually decided something — which defeats the purpose of recording decisions at all. What that record has to contain is set out in what an audit trail is for.

The question to ask about every action#

For each thing a person can do in your system, four questions:

  1. Who may do this? Stated as a rule, not as a list of names.
  2. Is it reachable by more than one route? If so, does every route check?
  3. What does it leave behind? An action that changes something and records nothing cannot be governed at all.
  4. Can it be undone, and by whom? Frequently the undo is more powerful than the action, and is guarded less.

The fourth is the one people miss. Cancelling a receipt is a bigger capability than creating one, because creating a wrong receipt is visible and reversing a correct one is not. Put to somebody else's system rather than your own, the same enquiry takes the shape of approval chains worth asking about.

The short version#

Visibility and authority are different axes, and merging them forces you to choose between over-privileged people and blind ones.

Check the right property rather than the primary role. Enumerate every door into a capability, not just the one it was built behind. Give the same refusal whether a record is missing or forbidden. Make powerful capabilities explicit grants that one screen can show and one action can withdraw.

And when authority is delegated, bound it, record it as delegated, and never let one person end up holding both halves of a rule that exists to require two.

Have a gap worth closing?

If something in your daily work is broken in a way everybody has stopped complaining about, that is exactly what we want to hear.

Write to hello@be-teck.com

More notes