Who Can Do What? Role-Based Access and Audit Logs for a Growing Team
One shared admin login and a forgotten weekend promotion can cost a retailer thousands. Learn how to design roles, block risky duty pairs, gate refunds by amount and keep an audit log nobody can quietly edit.
Author
Anichur Rahaman
5 days ago11 min read1 views
Picture the owner of a three-outlet retailer on a Monday morning. She opens the refund report and sees that Outlet 2 issued 41 refunds on Sunday, worth $2,870. Thirty-eight of them are between $50 and $300, which is the range that should have been approved by a second person. Every one of them was issued by the same cashier login, and every one was approved by the same login.
Nobody hacked anything. Two years ago, during a rush, that cashier was given the shift-lead role "just for the weekend", and it was never taken back. The system did exactly what it was told. This is an illustrative scenario, but the pattern is common: access is granted quickly, reviewed never, and recorded in a way nobody reads.
This article is about the three ideas that prevent it: roles scoped to what a job needs, two people for anything that moves money or stock, and an audit log that cannot be quietly edited. It includes a role template for a three-outlet retailer and the fields of one refund record.
How access drifts as a team grows
With three people, everybody knows everybody, and one shared admin login feels harmless. At fifteen people across three outlets, the same habits turn into risk. The causes are ordinary:
Permission creep. People change jobs and keep the old access, because adding is a one-minute task and removing is nobody's task.
Copied accounts. A new hire is set up "like Priya", including everything Priya was given for one project in 2024.
Shared logins. A single till login for the whole shift means a log entry says "till" instead of a name.
Orphaned access. A former employee's account, or an API key they created, still works months later.
Most losses here are not dramatic attacks. They are small, repeated, unnoticed actions by someone who was allowed to do them. Of all the ways to lose money, the internal kind is the one a retailer can control best.
Roles, permissions and scope: three different things
Role-based access control sounds abstract until you split it into parts. A permission is one verb on one thing: refund.issue, refund.approve, stock.adjust, customer.export. A role is a named bundle of permissions that matches a job: Cashier, Shift lead, Accountant. A scope says where the role applies: one outlet, one warehouse, or everywhere.
The assignment, not the role, is the unit that matters. "Sana is a Shift lead at Outlet 2" is one row: user, role, scope, start date, optional end date. Move Sana to Outlet 3 and the row changes; she does not silently keep Outlet 2.
Least privilege, in practice
Least privilege means a person holds the smallest set of permissions that lets them do their job today. NIST SP 800-53 states it as control AC-6: the system enforces the most restrictive set of rights needed for the specified tasks. In a retail system that translates into three habits.
Start roles from the job description, not from "what could they possibly need".
Prefer many small permissions over a few broad ones, so "can see sales" and "can export every customer" are different switches.
Give scope by default to one location. Global access is the exception, and it needs a name next to it.
Segregation of duties: pairs that must not meet
Least privilege limits what one person can do. Segregation of duties limits what one person can do alone. NIST AC-5 describes it as dividing functions among different people so that abuse of privilege needs collusion. The method is simple: list the pairs of duties where one person holding both could create or hide a loss, then make the system refuse the combination.
Four pairs are blocked outright. One is allowed only with a review, because stock adjustments can hide refund fraud.
The four blocked pairs cover most of the money in a commerce business. Someone who can create a supplier and pay that supplier can invent a vendor. Someone who can issue and approve a refund, like our Monday cashier, approves their own work. Editing payroll and running it is the same shape. Adjusting stock and approving the count that justifies the adjustment is how shrinkage becomes invisible.
The rule must be enforced by the software, at the moment of the action, and not only by a policy document. A check that says "the approver must be a different user from the requester" costs one line of logic and removes the whole class of problem.
Sensitive actions get a gate, not just a login
A login proves who you are. It does not prove that this particular action is reasonable. For a short list of actions, such as refunds, price overrides, discounts above a limit, stock adjustments, exports of customer data and payroll runs, add a gate that looks at the amount.
The flow below uses illustrative thresholds. Pick yours from the cost of one mistake in your business.
Every branch ends in the same place: a record. Denied and rejected attempts are written down too.
Three details in that flow matter more than they look.
The permission check includes scope. "Has refund.issue" is not enough. The question is "has refund.issue at Outlet 2".
The approver is checked against the requester, not only against the permission. A shift lead cannot approve their own refund.
Refusals are logged. A cashier who tries to refund $400 and is denied four times in a week is information. If you only record successes, you lose it.
For the highest tier, here over $300, require two approvers, for example the outlet manager and someone from finance. Keep the tiers few. Three is easy to explain at a till; seven are bypassed.
A role template for a three-outlet retailer
This table is a starting point, not a standard. "Own" means the permission applies only to the user's assigned outlet. "Request" means the person can start the action but someone else must approve it.
Permission
Cashier
Shift lead
Outlet manager
Stock clerk
Accountant
Owner
Sell at the counter
Own
Own
Own
No
No
All
Issue refund up to $50
Own
Own
Own
No
No
All
Approve refund $50 to $300
No
Own
Own
No
No
All
Approve refund over $300
No
No
Own (first of two)
No
Second approver
All
Override a price
No
Request
Own, within a set percentage
No
No
All
Adjust stock
No
No
No
Own
No
All
Approve stock count
No
No
Own
No
Yes
All
Create supplier
No
No
No
No
No
All
Pay supplier
No
No
No
No
Yes
No
Export customer list
No
No
No
No
No
All
Manage users and roles
No
No
No
No
No
All, with a second owner approving
Look at the gaps on purpose. The accountant can pay suppliers but cannot create them. The owner can create suppliers but is blocked from paying them, which is slightly annoying and deliberately so. The Owner column shows what the role may do, but the conflict check still runs on every transaction, so the owner cannot approve a refund they issued. Payroll roles are left out of this table because they usually belong in a separate HR template with the same rule: whoever edits pay does not run it.
What an audit record must contain
An audit log answers one question after the fact: who did what, to which record, when, why, and with whose permission. A line that says "refund processed" does not. Here is the record for the $184 refund in the flow above.
Field
Example value
Why it is there
Record id and time
R-20931, 09:41:07 UTC
Ordering and lookup. Store UTC, display local.
Actor
cashier-07 (a named user, never "till")
Accountability
Action and target
refund.issue on order 48211
Which record changed
Where
Outlet 2, register 3, device and IP
Scope and unusual-location checks
Amount and reason
$184.00, reason code "damaged"
Pattern analysis by reason
Before and after
Payment captured then refunded; stock +1 to the returns bin
Proof of the effect
Rule applied
"Refund $50 to $300: one approver"
Shows the policy that was in force
Approver and time
lead-02, 09:42:31 UTC
Proves a second person
Result
Completed
Success, denial or rejection
Previous hash and own hash
7f3a…c91e
Tamper evidence
Tamper evidence, retention and review
A log that an administrator can edit proves little. Two inexpensive measures help. First, make the table append-only for the application: no update or delete permission, corrections written as new rows. Second, chain the records. Each row stores a SHA-256 hash of its own content plus the previous row's hash, so changing an old row breaks every hash after it. Copy the log nightly to storage the application cannot write to.
Retention depends on your rules. As one reference point, PCI DSS v4.0.1 requirement 10.5.1 asks for audit log history of at least 12 months, with the most recent three months immediately available for analysis. Check what your tax, payroll and privacy regimes require on top of that, and write the period down.
The last question is who reads it. A log nobody reviews is a diary. Assign one named person outside the outlets, a finance lead or the owner, and give them three saved views: refunds by user, manual stock adjustments and exports. Ten minutes a week is enough to see the Monday cashier before Monday.
Joiners, movers and leavers
Most access problems start at a job change, not at an attack. Treat the three moments as a routine with a fixed order.
Access should follow the job: granted from a template, moved on the same day, removed before the exit conversation.
Joiner: the manager requests a role from the template, the role owner approves, and the person enrols a second sign-in factor before first use. Mover: remove the old role first, then add the new one. This order stops permission creep. Leaver: disable the account (do not delete it, the history must stay), end sessions, rotate shared codes and API keys, and reassign open approvals.
Temporary access deserves its own rule. "Cover for Priya until Friday" becomes a role assignment with an end date, so it expires without anyone remembering. That single feature would have stopped the weekend lead promotion in our Monday scene.
A quarterly access review, step by step
Every role assignment should be confirmed by a human at least periodically. PCI DSS 7.2.4 sets six months as its minimum for in-scope systems; for a retailer with staff turnover, every quarter is a better rhythm. The review takes an hour if you prepare it properly.
Export every assignment: user, role, scope, start date, end date, last sign-in.
Send each outlet manager the list for their location and ask for a yes or no per line.
Remove accounts with no sign-in for 60 days, and anyone no longer employed.
Flag every user holding both halves of a blocked pair from the matrix above.
List every "all outlets" scope and ask the owner to re-approve each by name.
Record who reviewed, when and what changed. The review is itself an audit record.
What to measure
A few numbers show whether the controls work, and none of them needs a dashboard project.
Self-approved sensitive actions: target zero. Anything above zero is a rule that can be bypassed.
Time to disable a leaver: target the same day, ideally before the exit conversation.
Accounts with no sign-in in 60 days: trend towards zero each quarter.
Refund rate by user, compared to peers in the same outlet. An outlier is a question, not a verdict.
Denied attempts per week: a few are normal, a sudden cluster from one person is worth a conversation.
Back to Monday morning
Run the opening scene again with these controls in place. The cashier's role covers sales and refunds up to $50. A $184 refund goes to a shift lead, who is a different person, and the system will not accept the same login twice. The weekend promotion carries an end date and lapses on Monday.
The 41 refunds do not happen as they did. If a few are still suspicious, the owner finds them in a ten-minute weekly review because each carries a user, a rule, an approver and a hash. She reads a report instead of discovering a loss.
Key takeaways
Separate permissions, roles and scope, and manage access as assignments with start and end dates.
Apply least privilege and scope each role to one location by default.
Block the four classic conflict pairs in software: create and pay supplier, issue and approve refund, edit and run payroll, adjust and approve stock.
Gate sensitive actions by amount, check the approver is not the requester, and log denials as well as successes.
Make the audit log append-only and hash-chained, keep it for the period your rules require, and give one named person a weekly review.
Run joiner, mover and leaver steps in a fixed order and review all access every quarter.
Anichur Rahaman is a software architect and the creator of StoreConsole. He designs commerce and ERP systems for growing businesses, with a focus on event-driven architecture, data integrity and self-hosted operations.