9 ms·
I've fought with several auth systems a long while ago. Eventually came up with a pattern that worked well for all my needs. Here is what I did. Big picture:
by gambler 5y ago
I've fought with several auth systems a long while ago. Eventually came up with a pattern that worked well for all my needs. Here is what I did.
Big picture:
user <-> something <-> ... <-> something <-> action
You give the permission system the starting point and the ending point, asking it whether this user can perform this action. The system loads or generates the graph in the middle and tries to find a path from one side to the other.
...
More details follow.
API for checking permissions was implemented as a boolean function of the following form:
CanDo(user, action)
Action was represented by a string of a particular format, e.g. "articles.edit.987". This is semi-arbitrary, as long as you stick with your convention.
The function expanded user into a list of appropriate "roles".
Roles were handled in such a way that they could represent real users (user.12434) or groups (group.admins) or virtual groups (virtual.loggedIn).
The database stored information about what various roles could do in a table that had two essential columns: role id and permission pattern. This can be done in a variety of ways, depending on what storage mechanisms you use.
The resolution was as follows.
1. Find all rules for all the roles the user is in.
2. Take each pattern and match it against action supplied to canDo() function.
3. If there is a match, return true. If you exhaust the list, return false.
This setup is surprisingly flexible, as long as you understand how to use it. The trick is that you can add more entities between "user" and "action" and that will not change the API, just the resolution process. Moreover, since you effectively searching for a path in an acyclic graph between user and actions, you can search in any order and in any direction. And you can store permissions in all kinds of formats, again, without changing the application API.
All the business logic unrelated to authentication is handled in user code. E.g., if you want to allow someone to post articles on Tuesdays only, you write something like the following code:
if (TodayIsTuesday() && CanDo(currentUser, "articles.editOnTuesdays")){
//do stuff
} else {
//error
}
Technically, "pattern" for permissions could be anything. You could use a regex, but I would advice against it. If you use a simple hierarchy, you could expand it and run the match in a single DB query.
- neandrake 5y agoDo you know where there is more information about this type of design or what it might be called, in blog or book format? I ended up designing a very simplified (and modified) version of this same thing a decade ago for a web application: Data is owned by some account (user or group). Users have membership/roles associated with an account (either a share or a membership) which enumerate all the operations that user is allowed to perform on data owned by that account. During a request a graph is built mapping the current user to the data's owning account via user->account shares/memberships or group->account shares/memberships. The graph is traversed ensuring that the action being taken exists on every role through the path (e.g. a user can share data with another user but can only grant a maximum of the same permissions that user has). The big challenge I had was getting others to expand on this system as the product grew. Particularly as new features were added there was reluctance or ignorance that the actions introduced by features had to be enumerated in the roles/permissions. Though, when adding new actions it is difficult to know how to insert those into existing defined roles (as our roles are entirely customizable and not some constant set). That required account managers to go in and update their role definitions to specify where new actions should be allowed. The desire to avoid doing that led others to "just do an admin check" that would proliferate through the codebase as a means to simplify the logic, defeating the purpose of this flexible system. Early on I had eliminated the isAdmin check by ensuring all accounts had an un-editable Admin role whose list of permitted actions always contained everything. However an isAdmin check was added later which looked for the user having the role with all the permissions, instead of doing a check for that specific action. Edit: Apparently this type of design is called Role-Based Access Control and information about it can be found by searching this term. And oso (the product made by the people who wrote the parent article) is a library for implementing an RBAC. https://en.wikipedia.org/wiki/Role-based_access_control https://en.wikipedia.org/wiki/Role-based_access_control