hasgeek / lastuser

Lastuser has been merged into Funnel. This repository is archived.
https://hasgeek.com/
BSD 2-Clause "Simplified" License
166 stars 30 forks source link

Role Based Access Control #129

Closed shreyas-satish closed 6 years ago

shreyas-satish commented 9 years ago

Abstract

This is an early design proposal for an authorization system for lastuser.

Functionally, the architecture should be

  1. Powerful. Should let us manage users and groups from one location. One use case is being able to assign a role to a user or a group. That way, a new user can simply be added to a group whose permissions are already set. Another use case is, being able remove a user's entire access at the click of a button.
  2. Modular. Should let the client applications use any technology. All the client application needs to do, is maintain a JSON API.
  3. Flexible. Should combine the ease of managing permissions centrally with the flexibility of having customized permissions.

So, the key idea is, we use Lastuser as a 'policy' manager. It allows you to create and manage users, groups, organizations and their permissions. And we store these permissions in a 'policy' document (we will dive deeper into policies, as we analyze scenarios). A policy is simply a JSON document that details who (groups or individual users) can perform what actions on which resources.

A client application on the other hand (eg: Funnel, Jobs) exposes an API, that details which instances are currently running, which resources (eg talks, job_postings) are available and what actions are possible. It regularly polls lastuser and stores the latest policy (on redis). When we have someone (on a client app) trying to access a resource, we run a check on the user's permissions. The way we do that is, we retrieve the policy document, filter it down to a set that is relevant to the 'current user' and assess if the user should be allowed or denied access.

Scenario 1

We want to manage the access rights to the Funnel for Droidcon '14. So, we start by creating an organization - 'HasGeek' and a group - 'Droidcon14', on lastuser. Let's say, the event's funnel is supposed to have a few event admins, a panel, speakers and viewers. A simple use case would be, for the admins on the Droidcon14 group to have access to the DroidCon14 Funnel (but only to Droidcon14).

Let's create permissions for the admins. We do this by creating a role called 'Admin'. Here's how we would set the permisssions (in a simple UI with text boxes and drop-downs):

Role: 'Admin'
Effect: Allow (or Deny)
Action: Manage (or Read)
Application: Funnel // When you select 'Funnel', we hit the 'Funnel' application, get a list of instances.
Instance: Droidcon14
Resource: '*'

Now, let's add some users. We can either search for existing users or add new ones. Let's 'Add' some of them to the group 'Droidcon14' and assign the role 'Admin'. Our policy document for the 'HasGeek' organization would be automatically generated would look like this:


/v1/org/hasgeek
{
  'Groups':[{
    'id': 1
    'name': 'Droidcon14'
    'Application': 'Funnel',
    'Instance': 'Droidcon14',
    'Roles': ['Admin'],
  }],
  'Users': [{id: 1, name: 'Kiran', roles: ['Admin']}, {id: 2, name: 'Zainab', roles: ['Admin']}]
  'Roles': [
    {id: 1, name: 'Admin', policy: "'Effect': 'Allow', 'Action': 'Manage', 'Resource': '*'}
   ]

}

The client application (Funnel in this case) polls lastuser every 30 seconds, and pulls down the latest policy document, and persists the document on its local Redis cluster.

When a user tries to access a resource on the Droidcon14 Funnel, we retrieve the policy from redis, search for the user in the Droidcon14 group and parse their roles. Then, we either allow or deny access to the user.

Scenario 2:

We want to create track-level admins on Droidcon14.

Let's create a role for the 'Wearables' track - 'WearablesAdmin'.

Here's how we set the permissions:

Role: 'WearableAdmin'
Effect: Allow
Action: Manage
Application: Funnel 
Instance: Droidcon14
Resource: 'Tracks'
Filter: 'Wearables'

Now, we assign the role to a bunch of users similar to how we did in Scenario 1. Here's the policy document we end up with:

{
  'Groups':[{
    'id': 1
    'name': 'Droidcon14'
    'Application': 'Funnel',
    'Instance': 'Droidcon14',
    'Roles': ['Admin'],
  }],
  'Users': [{id: 1, name: 'Kiran', roles: ['Admin']}, {id: 2, name: 'Zainab', roles: ['Admin']}, {id: 3, name: 'Shreyas', roles: ['WearableAdmin']}]
  'Roles': [
    {id: 1, name: 'Admin', policy: "'Effect': 'Allow', 'Action': 'Manage', 'Resource': '*'}
    {id: 2, name: 'WearablesAdmin', policy: "'Effect': 'Allow', 'Action': 'Manage', 'Resource': 'Tracks', 'Filter': 'Wearables'}
   ]

}

We then process the request to access a resource, similar to Scenario 1.

References

This spec is heavily influenced by AWS's Identity and Access Management system. AWS uses it to allow users to manage their AWS services.

rudimk commented 9 years ago

For mobile clients, we could look at client apps using an embedded JSON database, like EJDB. In that case, one has two options:

  1. Server clients use Redis to store policy documents, and mobile clients use an embedded JSON store instead;
  2. Both use embedded JSON stores.

The good part about the second option is consistency; also, unlike a Redis instance which should be monitored, an embedded JSON store like EJDB is like SQLite - drop it into your code, and that's the end of it.

All in all, I'm rather stoked about this particular method in implementing role-based AC. It does look like it will do the trick.

shreyas-satish commented 9 years ago

A slightly altered approach would be to send the access request on the client app as an API call to lastuser. Lastuser would then compute if access should be allowed or denied. In that case, the client wouldn't have to maintain the policy document locally.

rudimk commented 9 years ago

Yes, but that would involve multiple HTTP requests, which may/may not be a good thing. But I somehow feel that might be a better approach than storing policies locally. Further, a client app could call Lastuser asynchronously, and thus not lock up due to network issues. What do you and @jace think?

shreyas-satish commented 9 years ago

The network issue problem is more pertinent to a mobile app, in which case, there's always the option of storing a local copy of the policy. Otherwise, letting lastuser compute access rights, seems like a simpler solution ATM.

rudimk commented 9 years ago

Yes, exactly. Wouldn't want different flows for mobile and web clients; like you said, letting Lastuser do the heavy lifting sounds much simpler and more efficient.

jace commented 6 years ago

Replacing this with the proposed use of libmacaroons in hasgeek/coaster#100, although that is itself in question now that libmacaroons development has slowed down.