Rights management is a core piece of many applications. And it certainly seems like one of those things that has been done before. Or not?
The world is not the same, but user and permissions management has little changed over the years. Most software either does a custom implementation or they resort to LDAP or ActiveDirectory.
These traditional services are database driven monstrosities and are not very handy for newer web based applications. Furthermore, it is very difficult to do anything ‘modern’ with them. Modern things:
- Monitoring in real time
- Audit trail
- Behavior tracking
- Hooks for integrations
- Simple management of Organizations, Teams, Users, and Projects (Resources)
This may sound strange because most permission management systems work in a sort of ‘god’ mode. Changes are made, you press refresh and see the changes.
But User management is inherently a collaborative effort. Why not take the logical step and make it a true collaborative application? By using and tracking a single application state using a command-event architecture you know the exact state of the entire system at any change point.
Most collaborative applications fail because they are implemented with traditional technologies, such as a typical Java Enterprise or Rails project.
The trick here is to always have a live cache, backed by a persistent database. Reads don’t hit the database. Writes do. Everything change is serialized. See the last section (Implementation) to see how we can do this with Elixir/Phoenix.
Develop an integratable user and permissions management service. One that supports a dual-mode interface: both HTTP and notifications over web-sockets.
When integrating the software can use a simple REST API and simply get the results. However, an application interested in tracking the changes can plug into the websockets channels and see things as they happen in real time.
Using a command-event architecture would guarantee serialization of changes and yield a very responsive system. Characteristics of these systems are that there might be 100 reads for every one write. So using a cached backed process would give high performance.
A command-event architecture would also allow playback and undo. The aggregate state being the application of the events in the order they were received.
Audit trails naturally come directly from the command-event stream.
Monitoring in real time is facilitated by the dual mode API. The advantage being that many people could be monitoring in real time the changes made to the system without adversely affecting performance.
Behavior hooks such as miss-use could be tracked by observing the command-event queue and applying analytics to the usage.
Integration hooks could be implemented - need to look at how this could be done. The idea is that any actual authentication is done externally to the service via hooks.
The Minimum Viable Product would manage simple structure:
- Resource (typically Project)
There’s a lot of joins here, but this is how I’ve modelled it:
Users: Can be member of Team. Can be member of Project. Can be member of Organization. Can be an admin member of Organization.
Team: Can be member of Project. Is a member of an Organization.
Organization: just a name really.
Project/Resource: Can be owned by User or Organization.
If a User is a member of an Organization, then the User can create a Project.
An Admin User of an Organization can create Teams and add Users to Teams. A User doesn’t have to be a member of an Organization, but if they are not, then they can’t create Projects.
If you are interested in this project send me an email, and we can talk about the architecture. I have all the drawings and am working on an Elixir implementation.
Elixir/Phoenix provides some great tools to implement a command-event and process backed cache, as well as web sockets. I’ve been researching and developing collaborative applications for years and understand the challenges of creating scalable and robust applications with guaranteed consistent views for all users.
- User creates a projects.
- A user can be a member of a project.
- A user can be a member of a team.
- A user can be an admin member of an organization.
- A team can be a member of a project.
- A team is owned by an organization.
An organization can own a project.
- A user can directly add any other user to be a member of a project.
- An organization owning a project overrides the user’s ownership (but creator still exists).
- A user with organization membership means that the user can create a project owned by the organization and can be a member of any team owned by the organization.
- A project that is owned by an organization means any organization user can be invited.
- A project can only have teams that are owned by the same organization.
- A project that is not owned by an organization means that teams cannot be added.
A user with admin member of an organization can add members to the organization.
- A user’s dashboard shows any project they are a member of.
- A user can be both directly a member of a project, or indirectly via a team.
- Separate admin dashboard shows all projects owned by organization, all user who are members, all teams owned by organization.
It is possible to have any team from any organization belong to a project but it is usually not the intention.
A non-organization user could be added to an organization owned team, but it is usually not the intention.
If a user is removed from organization, they should be removed from all organization’s teams also.