Flow
Moving to one access table
1Find every copy
List each place that decides who can enter an area, including one-off checks in the menu.
2Name the areas
Give each area a fixed name so a typo fails before it ships.
3Keep the rules small
Only the rule shapes you have: any of these permissions, or admin.
4Wrap the old helpers
Old per-area functions call the table, so nothing breaks mid-move.
5Point guards and menu at it
Both call the same check; an empty permission list must mean no entry.
The Users link in my ordering system's sidebar checked your permissions with two typed-out strings. Most other links asked a shared helper. This one did its own check, right in the menu code. It matched the real rule, so it worked. But it was a copy, and copies drift.
I spotted it while reading a big cleanup change. Most of that change just moved code around. The access part was small, but it's the part I care about most, because when it's wrong you just get a link that kicks you out, or a page with no link to it.
Three places, one rule
The question "can this person get into this area?" was answered in three places. A helper file had one small function per area, each with its own list of permissions. The page guards on the server called those functions and sent you back to the dashboard on a no. The menu called most of them, except for Users. And two guards wrote out their own lists instead of asking the helpers.
As far as I could tell, they all agreed. But they only agreed because people kept them in sync by hand. Change the guard and forget the menu, and people see a link that bounces them.
Moving the rules into a table
Now each area gets one row in one table: a name and a rule. A rule is either "has any of these permissions" or "is an admin". The menu, the guards and the old helper functions all read that table. If you want this in your own app:
- Give each area a fixed name, so a typo fails before it ships.
- Keep only the rule shapes you actually have.
- Let the guards and the menu both call the same check.
- Turn the old helpers into one-line wrappers so nothing breaks while you move callers over.
An area with no list
The admin-only area has no permission list, so asking for its list returns nothing. That could read as "nothing required". My read is that the guard only lets you in if at least one check passes, and with no checks, none would. So an empty list should mean no entry, though I couldn't see the guard's body to confirm it. A test now locks that in, along with tests that area names are unique and that the Users link follows the right permission.
A repeated pair and hand-built links
One broad rule still repeats two permissions from a narrower one. Some menu links are still built by hand. And I suspect the menu can run behind the server right after someone's permissions change. Of the three, the repeated pair looks like the cheapest fix: build the broad rule out of the narrow one.