8000 feat: Implement authorize for each endpoint · coder/coder@62ec87e · GitHub
[go: up one dir, main page]

Skip to content

Commit 62ec87e

Browse files
committed
feat: Implement authorize for each endpoint
1 parent cd2fda7 commit 62ec87e

File tree

3 files changed

+57
-54
lines changed

3 files changed

+57
-54
lines changed

coderd/authorize.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package coderd
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/coder/coder/coderd/httpapi"
7+
"github.com/coder/coder/coderd/httpmw"
8+
"github.com/coder/coder/coderd/rbac"
9+
)
10+
11+
func (api *api) Authorize(rw http.ResponseWriter, r *http.Request, action rbac.Action, object rbac.Object) bool {
12+
roles := httpmw.UserRoles(r)
13+
err := api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, action, object)
14+
if err != nil {
15+
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
16+
Message: err.Error(),
17+
})
18+
return false
19+
}
20+
return true
21+
}

coderd/coderd.go

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ func New(options *Options) (http.Handler, func()) {
8383
// TODO: @emyrk we should just move this into 'ExtractAPIKey'.
8484
authRolesMiddleware := httpmw.ExtractUserRoles(options.Database)
8585

86-
authorize := func(f http.HandlerFunc, actions ...rbac.Action) http.HandlerFunc {
87-
return httpmw.Authorize(api.Logger, api.Authorizer, actions...)(f).ServeHTTP
86+
authorize := func(f http.HandlerFunc, actions rbac.Action) http.HandlerFunc {
87+
return httpmw.Authorize(api.Logger, api.Authorizer, actions)(f).ServeHTTP
8888
}
8989

9090
r := chi.NewRouter()
@@ -127,25 +127,20 @@ func New(options *Options) (http.Handler, func()) {
127127
r.Route("/files", func(r chi.Router) {
128128
r.Use(
129129
apiKeyMiddleware,
130-
authRolesMiddleware,
131130
// This number is arbitrary, but reading/writing
132131
// file content is expensive so it should be small.
133132
httpmw.RateLimitPerMinute(12),
134-
// TODO: @emyrk (rbac) Currently files are owned by the site?
135-
// Should files be org scoped? User scoped?
136-
httpmw.WithRBACObject(rbac.ResourceFile),
137133
)
138134
r.Get("/{hash}", api.fileByHash)
139135
r.Post("/", api.postFile)
140136
})
141137
r.Route("/organizations/{organization}", func(r chi.Router) {
142138
r.Use(
143139
apiKeyMiddleware,
144-
authRolesMiddleware,
145140
httpmw.ExtractOrganizationParam(options.Database),
141+
authRolesMiddleware,
146142
)
147-
r.With(httpmw.WithRBACObject(rbac.ResourceOrganization)).
148-
Get("/", authorize(api.organization, rbac.ActionRead))
143+
r.Get("/", api.organization)
149144
r.Get("/provisionerdaemons", api.provisionerDaemonsByOrganization)
150145
r.Post("/templateversions", api.postTemplateVersionsByOrganization)
151146
r.Route("/templates", func(r chi.Router) {
@@ -154,17 +149,12 @@ func New(options *Options) (http.Handler, func()) {
154149
r.Get("/{templatename}", api.templateByOrganizationAndName)
155150
})
156151
r.Route("/workspaces", func(r chi.Router) {
157-
r.Use(httpmw.WithRBACObject(rbac.ResourceWorkspace))
158-
// Posting a workspace is inherently owned by the api key creating it.
159-
r.With(httpmw.WithAPIKeyAsOwner()).
160-
Post("/", authorize(api.postWorkspacesByOrganization, rbac.ActionCreate))
161-
r.Get("/", authorize(api.workspacesByOrganization, rbac.ActionRead))
162152
r.Post("/", api.postWorkspacesByOrganization)
153+
r.Get("/", api.workspacesByOrganization)
163154
r.Route("/{user}", func(r chi.Router) {
164155
r.Use(httpmw.ExtractUserParam(options.Database))
165-
// TODO: @emyrk add the resource id to this authorize.
166-
r.Get("/{workspace}", authorize(api.workspaceByOwnerAndName, rbac.ActionRead))
167-
r.Get("/", authorize(api.workspacesByOwner, rbac.ActionRead))
156+
r.Get("/{workspace}", api.workspaceByOwnerAndName)
157+
r.Get("/", api.workspacesByOwner)
168158
})
169159
})
170160
r.Route("/members", func(r chi.Router) {
@@ -238,58 +228,38 @@ func New(options *Options) (http.Handler, func()) {
238228
apiKeyMiddleware,
239229
authRolesMiddleware,
240230
)
241-
r.Group(func(r chi.Router) {
242-
// Site wide, all users
243-
r.Use(httpmw.WithRBACObject(rbac.ResourceUser))
244-
r.Post("/", authorize(api.postUser, rbac.ActionCreate))
245-
r.Get("/", authorize(api.users, rbac.ActionRead))
246-
})
231+
r.Post("/", api.postUser)
232+
r.Get("/", api.users)
247233
// These routes query information about site wide roles.
248234
r.Route("/roles", func(r chi.Router) {
249235
r.Use(httpmw.WithRBACObject(rbac.ResourceUserRole))
250236
r.Get("/", authorize(api.assignableSiteRoles, rbac.ActionRead))
251237
})
252238
r.Route("/{user}", func(r chi.Router) {
253239
r.Use(httpmw.ExtractUserParam(options.Database))
254-
r.Group(func(r chi.Router) {
255-
r.Use(httpmw.WithRBACObject(rbac.ResourceUser))
256-
r.Get("/", authorize(api.userByName, rbac.ActionRead))
257-
r.Put("/profile", authorize(api.putUserProfile, rbac.ActionUpdate))
258-
// suspension is deleting for a user
259-
r.Put("/suspend", authorize(api.putUserSuspend, rbac.ActionDelete))
260-
r.Route("/password", func(r chi.Router) {
261-
r.Put("/", authorize(api.putUserPassword, rbac.ActionUpdate))
262-
})
263-
// This route technically also fetches the organization member struct, but only
264-
// returns the roles.
265-
r.Get("/roles", authorize(api.userRoles, rbac.ActionRead))
266-
267-
// This has 2 authorize calls. The second is explicitly called
268-
// in the handler.
269-
r.Put("/roles", authorize(api.putUserRoles, rbac.ActionUpdate))
270-
271-
// For now, just use the "user" role for their ssh keys.
272-
// We can always split this out to it's own resource if we need to.
273-
r.Get("/gitsshkey", authorize(api.gitSSHKey, rbac.ActionRead))
274-
r.Put("/gitsshkey", authorize(api.regenerateGitSSHKey, rbac.ActionUpdate))
275-
276-
r.Post("/authorization", authorize(api.checkPermissions, rbac.ActionRead))
240+
r.Get("/", api.userByName)
241+
r.Put("/profile", api.putUserProfile)
242+
r.Put("/suspend", api.putUserSuspend)
243+
r.Route("/password", func(r chi.Router) {
244+
r.Put("/", authorize(api.putUserPassword, rbac.ActionUpdate))
277245
})
246+
r.Get("/organizations", api.organizationsByUser)
247+
r.Post("/organizations", api.postOrganizationsByUser)
248+
// These roles apply to the site wide permissions.
249+
r.Put("/roles", api.putUserRoles)
250+
r.Get("/roles", api.userRoles)
278251

279-
r.With(httpmw.WithRBACObject(rbac.ResourceAPIKey)).Post("/keys", authorize(api.postAPIKey, rbac.ActionCreate))
280-
r.Get("/workspaces", api.workspacesByUser)
252+
r.Post("/authorization", api.checkPermissions)
281253

254+
r.Post("/keys", api.postAPIKey)
282255
r.Route("/organizations", func(r chi.Router) {
283-
// TODO: @emyrk This creates an organization, so why is it nested under {user}?
284-
// Shouldn't this be outside the {user} param subpath? Maybe in the organizations/
285-
// path?
286256
r.Post("/", api.postOrganizationsByUser)
287-
288257
r.Get("/", api.organizationsByUser)
289-
290-
// TODO: @emyrk why is this nested under {user} when the user param is not used?
291258
r.Get("/{organizationname}", api.organizationByUserAndName)
292259
})
260+
r.Get("/gitsshkey", api.gitSSHKey)
261+
r.Put("/gitsshkey", api.regenerateGitSSHKey)
262+
r.Get("/workspaces", api.workspacesByUser)
293263
})
294264
})
295265
})

coderd/users.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,18 @@ func (api *api) postUser(rw http.ResponseWriter, r *http.Request) {
163163
if !httpapi.Read(rw, r, &createUser) {
164164
return
165165
}
166+
167+
// Create the user on the site
168+
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceUser) {
169+
return
170+
}
171+
172+
// Create the organization member in the org.
173+
if !api.Authorize(rw, r, rbac.ActionCreate,
174+
rbac.ResourceOrganizationMember.InOrg(createUser.OrganizationID)) {
175+
return
176+
}
177+
166178
_, err := api.Database.GetUserByEmailOrUsername(r.Context(), database.GetUserByEmailOrUsernameParams{
167179
Username: createUser.Username,
168180
Email: createUser.Email,

0 commit comments

Comments
 (0)
0