The proposed access control model concept is constructed of two dimensions: Permissions (roles/capabilities) and Scope (tenancy).

Permissions (roles/capabilities)

Permissions are API-centric, therefore a user's role and the associated capabilities define the API endpoints that are accessible to the user.

Concept:

  • a user has one role (i.e. admin, operations, etc)

  • a role has one or more capabilities (i.e. ds-read, ds-write, etc)
  • a capability is mapped to one or more API endpoints (i.e. ds-read is mapped to GET /api/ds and GET /api/ds/:id)

Therefore, a user's capabilities (as defined by their role) determine whether a user has access to an API endpoint or not.

Roles - Proposed

  • Admin
  • Operations
  • Content Provider (formerly known as Portal)
  • Federation
  • Cache (formerly known as ORT)
  • Monitor (new)
  • Router (new)
  • Stats (new)
  • Read-Only
  • Disallowed

Capabilities

API endpoints are grouped into capabilities. An API endpoint can only belong to one capability.

Example: api_capability table

API                                                         | Capability

-----------------------------+-----------------------------
GET  /ds                 | ds-read
GET  /ds/:id                 | ds-read
POST /ds                     | ds-write
PUT /ds/:id           | ds-write
DELETE /ds/:id | ds-write
.... + more mapping API endpoint to capability mappings

Capabilities are then assigned to roles. Capabilities can be assigned to multiple roles.

Example: role_capability table

Role                  | Capability
----------------------+-----------------------------------
operations   | server-read
operations | server-write
operations            | ds-read
operations            | ds-write
.... + more operations role to capability mappings
content-provider      | ds-read
content-provider | ds-write
.... + more content-provider role to capability mappings

Permissions are enforced for each API request using the role/capabilities of the user. A user Joe with the content-provider role has the ds-read and ds-write capabilities and therefore, is allowed access to the following APIs:  GET /ds, GET /ds/:id, POST /ds, PUT /ds/:id and DELETE /ds/:id

Advantages:

  • The point of having two levels of hierarchy in this model (api_capability and role_capability tables) is to separate the capability/api associations from the role/capability associations. While both tables (api_capability and role_capability) can be expanded, the idea is to allow for little flexibility in the api-capability mapping, and a lot of flexibility in the role-capability mapping.
  • An administrator can define roles according to the organization's needs, then assign these roles to users without the need to duplicate a long set of capabilities. Managing user privileges becomes easy this way. Also, when a new API is added, it can usually be mapped to an existing capability, and transparently become available to all the users that have this capability, not forcing the administrator to take any further action. Proprietary (extension) APIs can also be made available by mapping them to capabilities.
  • By mapping API endpoints to capabilities and capabilities to a role, it's easy to see what level of API access each role provides. For example, easy to see the difference between the Admin and Operations role. That is not possible with the existing permission system that leverages privilege levels.
  • Roles are not "additive" as they are today with privilege level. Unrelated, unique roles can be created. For example, the Federation role and Content Provider role can provide access to completely different API endpoints. Currently, those 2 roles provide the exact same level of access because they have the same privilege level.
  • Tightly defined roles with specific capabilities provides better security. I.e. you don't have to give a user an admin role so they can access only two sensitive API endpoints.

Challenges:

  • Given the scenario where a user has the user-create capability, how do we ensure that the users created don't have more permissions than the creator? Example, the creator has the A, B and C capability in addition to the user-create capability. This user cannot create a user with a role that contains the A, B, C and D capability because the creator doesn't have the D capability. For user create, a capability subset check will need to be performed.
  • How do we provide field level permissions? For example, currently GET /api/parameters returns asterisks for the parameter value if user.role != admin. A proposed solution to this problem is creating 2 capabilities that map to GET /api/parameters (params-read and params-read-secure). The API handler then checks for the existence of a certain capability (params-read-secure in this case) to remove the asterisks from secure parameter values.

Scope (tenancy)

Tenancy is used to control a user's scope as it relates to 3 resource types (currently):

  • Delivery Services
  • Users
  • Tenants

Tenants are hierarchical and typically used to define organizations. The top of the hierarchy is always the "root" tenant. Users with the root tenant have access to ALL delivery services, users and tenants because "root" is the parent of every tenant.

Example tenant hierarchy:

- root

-- company A

--- company A.A

-- company B

--- company B.B

---- company B.B.B

Delivery services are assigned one tenant like such:

DS table:

DS-Name        | ... | Tenant
---------------------+--------
cp-a-vod       | ... | company A
cp-a-linear    | ... | company B
cp-b-vod       | ... | company B.B
cp-e-linear    | ... | company B.B.B

Users are assigned a one tenant like such:

Users table:

Username   | ... | Tenant
-----------+-----+--------
joe        | ... | root
jack       | ... | company A
janet      | ... | company B

Tenants are assigned one parent tenant  like such:

Tenant table:

ID  | Tenant-name     | Parent-ID
----+-----------------+-----------
1   | root    | - // the root tenant has no parent. it is the top of the hierarchy and cannot be removed/edited
2   | company A       | 1         // a child of the root tenant
3   | company B   | 1 // a child of the root tenant
4   | company B.B   | 3 // a child of company B
5   | company B.B.B   | 4 // a child of company B.B

 

Based on tenancy, the following is true for Joe:

  • he is assigned the root tenant
  • he can see ALL delivery services because each delivery service is assigned to a tenant that is a descendent of joe's tenant (root) or equal to joe's tenant
  • he can see ALL users because each user is assigned to a tenant that is a descendent of joe's tenant (root) or equal to joe's tenant
  • he can see ALL tenants because a user can see his own tenant plus its child tenants

Based on tenancy, the following is true for Jack:

  • he is assigned the "company A" tenant
  • he can see ONLY the cp-a-vod delivery service because it is the only delivery service that belongs to "company A" or a child of "company A"
  • he can see ONLY one user (himself) because he is the only user that belongs to "company A" or a child of "company A"
  • he can see ONLY one tenant (company A) because a user can see his own tenant plus its child tenants

 

Based on tenancy, the following is true for Janet:

  • she is assigned the "company B" tenant
  • she can see the cp-a-linear, cp-b-vod and cp-e-linear delivery services because they all belong to her tenant (company B) or one of its child tenants
  • she can see ONLY one user (herself) because she is the only user that belongs to "company B" or a child of "company B"
  • she can see the company B, company B.B and company B.B.B tenants because a user can see her own tenant plus its child tenants

 

Access Control - Putting it all together

When trying to access an API resource (aka an API endpoint), 2 things are checked independently:

  1. Does the user have access to the requested API endpoint as determined by their role/capability?
  2. Does the user have access to the request resource (only applicable currently for delivery service, user or tenant) based on their tenancy?

If either of those checks fail, a 403 Forbidden is returned from the API.

 

 

  • No labels

26 Comments

  1. "Each user belongs to one or more tenants" - Can we simplify things by having a user just be "in" one tenant? 

     

    1. For phase 1, a user will have 1 tenant only per Nir B. Sopher .

  2. At first phase, we can probably have this simplification, but in the long term, I think it is required.

    Think of a parent company A which have child companies B, C & D.
    A's admin wants to allow user X to access B & C data, but not D.
    Is there a way to express this with a single tenant per user?

    1. Our wholesale group has communicated the use case where a user is assigned to 2 of 3 child tenants, hence, the need to assign multiple tenants to a user.

    2. Your example implies only one tenant per user.

       

      Username   | ... | Tenant
      -----------+-----+--------
      Joe        | ... | 1

       

      in my opinion, id change that and honestly shoot for supporting multiple tenants per user in phase one rather than trying to do it later. just my opinion.

  3. Right now a user has one and only ONE role as you can see in the tm_user table. This will have to change, right?

    Also, in the example above the roles are very granular (ds-read, ds-write, etc) which would mean if you wanted to create an admin user, you'd have to apply every granular role to that user....or....is there the thought of creating a "role group" (admin would be an example of a role group) which is a grouping of roles....OR....are roles dynamic and can be created as need. I.e. I can create a Super Foo role and assign all API's to it...

    1. You're taking the discussion to its next step (smile)

       

      First of all, I'm beginning to think I used a wrong terminology before. Let's try to define the terminology again, and if agreed, I will edit the page accordingly:

      I think what I referred to as "roles" should actually be termed "capabilities". A role is actually a higher level of hierarchy.

       

      I think that the capabilities should be very granular, in order to keep the flexibility, and agree that this creates an operational overhead when managing users.

      There are several approaches to decrease this overhead. I like the "role groups" you suggested the most, because it is very structured:

      The capabilities represent very granular sets of APIs, grouped by subjects. These sets are still to be defined, but let's assume the following sets exist:

      asn-r, asn-w, physical-location-r, physical-location-w, cache-group-r, cache-group-w, ds-r, ds-w, regex-r, regex-w, extension-r, extension-w.

      These are seeded capabilities, others can be added, to support API extensions.

      The next level in the hierarchy is a "role", which represents a role in the organization, like network-administrator or content-administrator.

      The TC operator can define the roles relevant for the organization and add capabilities to each role.

      E.g.:

      Role           | Capability
      ---------------+----------------------
      net-admin      | asn-r
      net-admin      | asn-w
      net-admin      | physical-location-r
      net-admin      | physical-location-w
      net-admin      | cache-group-r
      net-admin      | cache-group-w
      net-viewer     | asn-r
      net-viewer     | physical-location-r
      net-viewer     | cache-group-r
      content-admin  | ds-r
      content-admin  | ds-w
      content-admin  | regex-r
      content-admin  | regex-w
      content-admin  | extension-r
      content-admin  | extension-w

      A user is assigned multiple roles. So one could be both a net-admin and a content-admin, if that's required.

      1. So if we stick with the 3 levels -  a capability is a group of api endpoints and a role is a group of capabilities (smile)

        we end up with something like this, right?

        Role           | Capability           | Endpoint
        ---------------+----------------------+----------------------
        net-admin  | asn-r                   | GET /api/:version/asns
        net-admin  | asn-r                   | GET /api/:version/asns/:id
        net-admin  | asn-w                  | PUT /api/:version/asns/:id
        net-admin  | asn-w                  | POST /api/:version/asns

        which i'm just worried would cause an extra layer of management...

        for example, if i wanted to create a Foo role with just GET /api/:version/asns, i'd have to create a new "capability" because there is no capability with just GET /api/:version/asns in it...

        so i was just thinking get rid of the middle level so you have

        Role           | Endpoint
        ---------------+-------------------------------------
        net-admin  | GET /api/:version/asns
        net-admin  | GET /api/:version/asns/:id
        net-admin  | PUT /api/:version/asns/:id
        net-admin  | POST /api/:version/asns

         

         

        1. The problem with the lighter model (only two levels) is that it complicates the average admin's work.

          Consider the guy who manages the privileges. He has to assign 10 people from the network departement 10 different capabilities, instead of assigning them all just one role.

          I think the use-case of needing a new role is quite rare, while assigning users to roles is an everyday task.

          Of course, the better we define the pre-defined roles, it will make this need even more rare.

          1. Ok, just so I understand correctly. If 3 levels, would they be named like this potentially?

            Role --> Capabilities --> Endpoints

            User has N roles
            Role has N capabilities
            Capability has N endpoints?

            Role:
            - name
            - []capabilities

            Capability:
            - name
            - []endpoints

            Endpoint
            - name
            - method (GET, PUT, POST, PATCH, DELETE)
            - path (api/:version/...)

            And all three tables would be seeded and not exposed via an API?

             

            1. I think "API" is cleared than "endpoint".

              I'm not sure there should be a table that lists all existing APIs. What do you think?

              I think the Capabilitie table should be seeded and not exposed via an API.

              The Role table should be exposed via an API. We want to allow sys-admins define new roles with whichever granularity they would like.
              Still, I think this table can be pre-populated with some common roles, like network-admin, content-admin, sys-admin, etc.

      2. Just like a user will have ONE tenant for phase 1, can we make it where a user has ONE role for phase 1? I'd rather ease into this...it will make the transition much simpler....

  4. "Note: There will be a special "root" user that will be allowed to access all resources."

    Rather than a "root user" how about a "root tenant" which is the parent of every tenant....and if your user is assigned the root tenant, you are scoped to nothing...

    1. I agree.

      The term "root user" was used for simplification.
      I think there should be a seeded "root" tenant which is the parent of all other tenants, and therefor has access to all the resources.

  5. Also, "capabilities" or APIs are not static because of TO Extensions. For example, someone can leverage TO Extensions and create GET /api/foo/bar/baz. Therefore, you'd need the ability to add additional apis into the system in addition to the core apis...

    Maybe this is just done with a manual sql insert into the capabilities / api table...just something to think about...

    1. I agree with both statements.

      The capabilities list should be extendible, and it could be done through direct sql access.

  6. I suggest moving this page under the "Traffic Ops" parent page.  I would also suggest a title change since the page has evolved to cover both access control and tenancy.  New title suggested "Access Control and Tenancy Model".

  7. A wiki page on Tenancy is created under Traffic ops to separate access control and tenancy so need to start moving "tenancy" details from this page to the tenancy page. 

  8. What is the benefit/use case of having access control defined with so much granularity? Is there ever going to be a situation where the level of granularity described in this doc would be advantageous? 

  9. From my perspective, I originally went into this thinking in terms of a much simpler model including a few basic roles like Admin, Operations, and Read Only.  However, the second we create a simple system  a use case will come up requiring additional granularity. In that vein, one thing that came up in discussing self-service APIs was the ability to separate out validated self-service APIs with guardrails from newly built self-service APIs with limited validation.  I can already see where granular control will come in handy.  I couch that statement with the idea that we can create some very basic default role templates to keep 95% of the assignments very simple.

  10. Ashish and I are trying to diagram out the architecture and we are getting a bit stuck with User/Roles/Capabilities/APIs around whether a User would have multiple Roles.  If a User is restricted to a single Role, then I think the Capability grouping layer makes perfect sense.  However, if the intent is to allow users to have multiple Roles, then there is no need to have a Capabilities grouping layer since the Role can act as a grouping layer for APIs. Jeremy has touched on this above and I think that Matt M. also identified this complexity.  I want to get feedback from  the group and Ashish and I will diagram out some examples above to make easier to visualize.

    I posted a proposed architecture diagram as well as some base User Roles I think we need at a minimum to help drive thoughts around how everything roles up.  I think we are headed in the right direction, but my concern is that some of the examples above for Capabilities and User Roles seem to have a very narrow focus.  This may just be me reading into it too deeply.  In the examples above it looks like a capability is fairly narrowly defined and may only house 1-2 end points.  I have the same concern  with the User Role examples like "Inventory-Viewer".  This is a very narrow scope for a User Role and implies that Users would need multiple roles. 

    1. I think the level of roles granularity is deployment dependant. I believe some users may want to keep the roles model very strict, and assign a single role per user, while others may want to keep a loose model, where each role groups only a few capabilities and a user is assigned several roles.

      As I see it, the strength of the two levels is that it decouples the specific API details from the roles. It gives an intermediate level, which is handled by the application, and allows the sys-admin, who grants user permissions, to focus on a higher level. When the API changes, the capabilities also change automatically, not requiring any active user's involvement.

      The sys-admin should not need to be aware of the bits & bytes of every single API. She needs to be able to grant permissions based on the understanding of the type of operations a user will need to perform.

      A role can be thought of as a "template" for user permissions. It groups capabilities together to represent a specific organizational role, like Admin or Operations, or a more specific role like Network-Admin, Content-Admin or Security.

      What do you think?

       

      1. I see what you mean.  It does provide a lot of flexibility and obscure lower level complexity from the sys admins.  Ashish and I will update the diagram models to reflect that a user can have multiple user roles.

  11. Do you envision any of the tiers requiring exclusivity?  For example could the same API appear in multiple Capabilities?  Could the same Capability appear in multiple User Roles?

  12. I like this model of a user has a role which has capabilities which map to API endpoints, however, there seems to be one flaw or at least one unaccounted for use case.

    Let's look at the roles listed above:

    • CDN-Admin
    • CDN-Ops
    • CDN-Viewer
    • Tenant-Admin
    • Tenant-Ops
    • Tenant-Viewer

    Jeremy is a CDN-Admin which has the user-create capability (among others) so he creates Bob, a Tenant-Admin. Being a Tenant-Admin, Bob also has user-create so he creates Sally and he can give her ANY role so he decides to give Sally the CDN-Admin role....whoops, we don't want that...

    Bob should be limited to creating users with role=Tenant-Admin (his role), Tenant-Ops or Tenant-Viewer...but how do we correlate one role with another? Currently, we have "privilege level" attached to a role. So I guess we could use that like so:

    • CDN-Admin (100)
    • CDN-Ops (50)
    • CDN-Viewer (40)
    • Tenant-Admin (30)
    • Tenant-Ops (20)
    • Tenant-Viewer (10)

    Now, being a Tenant-Admin with the user-create capability, Bob can only create users where role.priv_level is 30 or below. I feel like this might be the easiest solution.

    Or...you could make roles hierarchical the way that tenants are hierarchical....

    -CDN-Admin

    --CDN-Ops

    --CDN-Viewer

    --Tenant-Admin

    ---Tenant-Ops

    ---Tenant-Viewer

    And in this scenario, if you have the user-create capability you can create users with your role or a child of your role...

    Thoughts?