Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: Check permissions endpoint #1389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 12, 2022
Merged

feat: Check permissions endpoint #1389

merged 10 commits into from
May 12, 2022

Conversation

Emyrk
Copy link
Member

@Emyrk Emyrk commented May 11, 2022

Allows FE to query backend for permission capabilities.
Batch requests supported

Example Request

{
   "read-all-users":{
      "object":{
         "resource_type":"users"
      },
      "action":"read"
   },
   "read-my-workspace":{
      "object":{
         "resource_type":"workspaces",
         "owner_id":"me"
      },
      "action":"read"
   },
   "read-myself":{
      "object":{
         "resource_type":"users",
         "owner_id":"me"
      },
      "action":"read"
   },
   "read-org-workspaces":{
      "object":{
         "resource_type":"workspaces",
         "organization_id":"009ceea0-5c51-4f5b-b61e-f91806425268"
      },
      "action":"read"
   }
}

Response

{
   "read-all-users": false,
   "read-my-workspace": true,
   "read-myself": true,
   "read-org-workspaces": true
}

Emyrk added 3 commits May 11, 2022 09:18
Allows FE to query backend for permission capabilities.
Batch requests supported
@Emyrk Emyrk requested a review from a team as a code owner May 11, 2022 14:34
@codecov
Copy link

codecov bot commented May 11, 2022

Codecov Report

Merging #1389 (9a66d88) into main (9d94f4f) will increase coverage by 0.06%.
The diff coverage is 54.45%.

@@            Coverage Diff             @@
##             main    #1389      +/-   ##
==========================================
+ Coverage   66.93%   66.99%   +0.06%     
==========================================
  Files         288      284       -4     
  Lines       18856    19006     +150     
  Branches      241      241              
==========================================
+ Hits        12622    12734     +112     
- Misses       4944     4959      +15     
- Partials     1290     1313      +23     
Flag Coverage Δ
unittest-go-macos-latest 54.39% <54.45%> (+0.31%) ⬆️
unittest-go-postgres- 65.72% <54.45%> (+0.29%) ⬆️
unittest-go-ubuntu-latest 56.68% <54.45%> (+0.29%) ⬆️
unittest-go-windows-2022 ?
unittest-js 74.19% <ø> (-0.05%) ⬇️
Impacted Files Coverage Δ
codersdk/users.go 63.79% <ø> (ø)
scripts/apitypings/main.go 0.00% <0.00%> (ø)
codersdk/roles.go 63.63% <45.45%> (-9.10%) ⬇️
coderd/roles.go 68.96% <50.00%> (-31.04%) ⬇️
coderd/coderdtest/coderdtest.go 98.27% <90.90%> (-0.59%) ⬇️
coderd/coderd.go 94.59% <100.00%> (+0.04%) ⬆️
provisionersdk/serve.go 35.13% <0.00%> (-8.11%) ⬇️
peerbroker/dial.go 77.04% <0.00%> (-6.56%) ⬇️
cli/configssh.go 62.58% <0.00%> (-6.48%) ⬇️
pty/ptytest/ptytest.go 86.95% <0.00%> (-4.35%) ⬇️
... and 42 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9d94f4f...9a66d88. Read the comment docs.

}

response := make(codersdk.UserPermissionCheckResponse)
for k, v := range params.Checks {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to echo user-specified keys back in the response. Do we require this endpoint to check for N permissions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It's a batch request, so the map key is to return the right true/false for each permission check. So the keys will likely be human friendly names such as read-my-workspace, the response will return "read-my-workspace": true

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, but what is the use-case that attempting to fetch the workspace and checking the status code wouldn't catch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is primarily for deciding which UI options to show.

Currently the admin Manage dropdown would use this to determine if it should show, and what buttons.

This is probably not going to be exactly how it's done, but something like this. The manage dropdown has 4 different options, you can do a permission check for each one and conditionally show the ui elements the user can access. If any return true, you show the Manage button.

{
   "manage-all-organizations": true,
   "manage-all-users": true,
   "read-all-auditlogs": true,
   "modify-deployment-settings: false,
}

In the past, the UI used the user's roles, but that means the UI has to know what an admin can and cannot do. In this world, if we ever support custom roles, that becomes impossible. This is the new approach, where the UI comes up with actions associated with UI elements, and queries the backend to handle the Authorize() check. Keeping it centralized in 1 place where RBAC logic is done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this standard amongst other products with RBAC? Something feels fishy to me which isn't enough to prevent this, but maybe is sign to look for validation with other products.

GitHub appears to display UI options based off role, but I know that diverges from our abstraction a bit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I like most on not being attached to the role itself but the permission is in the future, if we want, we can create custom roles without having to update the front end at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this standard amongst other products with RBAC?

Kubernetes has SubjectAccessReview and SelfSubjectAccessReview API endpoints, for example: https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spikecurtis good example that is exactly what we are doing here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kylecarbs What do you think now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel it's odd to echo the key-value pairs, but I don't think it should block progress! Seems fine to me.

@Emyrk Emyrk requested a review from BrunoQuaresma May 11, 2022 15:12
readonly owner_id?: string
readonly organization_id?: string
readonly resource_id?: string
}
Copy link
Contributor

@presleyp presleyp May 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write some docs on how to use this? The example request is helpful but for instance, where do we find the resource type and id, and where does the arbitrary name come in (I think it's the string that keys the record below but having this written somewhere will help when I forget that later lol)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup! Random comment on this, I am going to add the docs to the codersdk struct. These comments are not pulled over to the typescript, but they could in theory be copied over. If that is something that would be helpful, we can pull comments to the typescript too.

I'll link to the docs when I commit them 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@presleyp

coder/codersdk/users.go

Lines 81 to 127 in 79c0886

// UserPermissionCheckRequest is a structure instead of a map because
// go-playground/validate can only validate structs. If you attempt to pass
// a map into 'httpapi.Read', you will get an invalid type error.
type UserPermissionCheckRequest struct {
// Checks is a map keyed with an arbitrary string to a permission check.
// The key can be any string that is helpful to the caller, and allows
// multiple permission checks to be run in a single request.
// The key ensures that each permission check has the same key in the
// response.
Checks map[string]UserPermissionCheck `json:"checks"`
}
// UserPermissionCheck is used to check if a user can do a given action
// to a given set of objects.
type UserPermissionCheck struct {
// Object can represent a "set" of objects, such as:
// - All workspaces in an organization
// - All workspaces owned by me
// - All workspaces across the entire product
// When defining an object, use the most specific language when possible to
// produce the smallest set. Meaning to set as many fields on 'Object' as
// you can. Example, if you want to check if you can update all workspaces
// owned by 'me', try to also add an 'OrganizationID' to the settings.
// Omitting the 'OrganizationID' could produce the incorrect value, as
// workspaces have both `user` and `organization` owners.
Object UserPermissionCheckObject `json:"object"`
// Action can be 'create', 'read', 'update', or 'delete'
Action string `json:"action"`
}
type UserPermissionCheckObject struct {
// ResourceType is the name of the resource.
// './coderd/rbac/object.go' has the list of valid resource types.
ResourceType string `json:"resource_type,omitempty"`
// OwnerID (optional) is a user_id. It adds the set constraint to all resources owned
// by a given user.
OwnerID string `json:"owner_id,omitempty"`
// OrganizationID (optional) is an organization_id. It adds the set constraint to
// all resources owned by a given organization.
OrganizationID string `json:"organization_id,omitempty"`
// ResourceID (optional) reduces the set to a singular resource. This assigns
// a resource ID to the resource type, eg: a single workspace.
// The rbac library will not fetch the resource from the database, so if you
// are using this option, you should also set the 'OwnerID' and 'OrganizationID'
// if possible. Be as specific as possible using all the fields relevant.
ResourceID string `json:"resource_id,omitempty"`
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! And how do I know which resource types are possible, or which resource id a given thing has?

Copy link
Collaborator

@BrunoQuaresma BrunoQuaresma May 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it is not exported into the TS types but you can see them right here

var (
ResourceWorkspace = Object{
Type: "workspace",
}
ResourceTemplate = Object{
Type: "template",
}
// ResourceUserRole might be expanded later to allow more granular permissions
// to modifying roles. For now, this covers all possible roles, so having this permission
// allows granting/deleting **ALL** roles.
ResourceUserRole = Object{
Type: "user_role",
}
ResourceUserPasswordRole = Object{
Type: "user_password",
}
// ResourceWildcard represents all resource types
ResourceWildcard = Object{
Type: WildcardSymbol,
}
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So eventually the parts of the admin menu will be in there, right? I can see how the resource id of a workspace would be the workspace id, but I was having trouble figuring out how I'd ask if a user can see the Users page, for instance.

Copy link
Collaborator

@BrunoQuaresma BrunoQuaresma May 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You eventually can do this:

...
"read-all-users":{
  "object":{
      "resource_type":"users"
  },
  "action":"read"
},

Right now that is not possible because we don't have the resource "users" but I will make that during this ticket #884

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so that one won't need a resource id. So when I don't specify an owner/org/resource id, I'm asking for everything, but supplying ids can narrow the scope - is that right? Would be nice to have a note on that, although maybe it's more obvious to most people.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@presleyp correct.

Copy link
Collaborator

@BrunoQuaresma BrunoQuaresma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

for k, v := range params.Checks {
if v.Object.ResourceType == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "'resource_type' must be defined",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is resource type required? It has a ? in the typescript

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is. @Emyrk is that possible to have this reflected in the TS types?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed.Ah

@BrunoQuaresma
Copy link
Collaborator

Closes #984

@Emyrk Emyrk requested a review from kylecarbs May 11, 2022 21:30
@Emyrk Emyrk enabled auto-merge (squash) May 12, 2022 20:41
@Emyrk Emyrk merged commit 64e408c into main May 12, 2022
@Emyrk Emyrk deleted the stevenmasley/list_permissions branch May 12, 2022 20:56
@misskniss misskniss added this to the V2 Beta milestone May 15, 2022
kylecarbs pushed a commit that referenced this pull request Jun 10, 2022
* feat: Check permissions endpoint

Allows FE to query backend for permission capabilities.
Batch requests supported
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants