mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: Rbac more coderd endpoints, unit test to confirm (#1437)
* feat: Enforce authorize call on all endpoints - Make 'request()' exported for running custom requests * Rbac users endpoints * 401 -> 403
This commit is contained in:
@ -58,12 +58,18 @@ func (api *api) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead,
|
||||
rbac.ResourceWorkspace.InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(rw, http.StatusOK,
|
||||
convertWorkspace(workspace, convertWorkspaceBuild(build, convertProvisionerJob(job)), template, owner))
|
||||
}
|
||||
|
||||
func (api *api) workspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
roles := httpmw.UserRoles(r)
|
||||
workspaces, err := api.Database.GetWorkspacesByOrganizationID(r.Context(), database.GetWorkspacesByOrganizationIDParams{
|
||||
OrganizationID: organization.ID,
|
||||
Deleted: false,
|
||||
@ -77,7 +83,18 @@ func (api *api) workspacesByOrganization(rw http.ResponseWriter, r *http.Request
|
||||
})
|
||||
return
|
||||
}
|
||||
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces)
|
||||
|
||||
allowedWorkspaces := make([]database.Workspace, 0)
|
||||
for _, ws := range workspaces {
|
||||
ws := ws
|
||||
err = api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, rbac.ActionRead,
|
||||
rbac.ResourceWorkspace.InOrg(ws.OrganizationID).WithOwner(ws.OwnerID.String()).WithID(ws.ID.String()))
|
||||
if err == nil {
|
||||
allowedWorkspaces = append(allowedWorkspaces, ws)
|
||||
}
|
||||
}
|
||||
|
||||
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, allowedWorkspaces)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("convert workspaces: %s", err),
|
||||
@ -91,42 +108,7 @@ func (api *api) workspacesByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
roles := httpmw.UserRoles(r)
|
||||
|
||||
organizations, err := api.Database.GetOrganizationsByUserID(r.Context(), user.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get organizations: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
organizationIDs := make([]uuid.UUID, 0)
|
||||
for _, organization := range organizations {
|
||||
err = api.Authorizer.AuthorizeByRoleName(r.Context(), user.ID.String(), roles.Roles, rbac.ActionRead, rbac.ResourceWorkspace.All().InOrg(organization.ID))
|
||||
var apiErr *rbac.UnauthorizedError
|
||||
if xerrors.As(err, &apiErr) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("authorize: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
organizationIDs = append(organizationIDs, organization.ID)
|
||||
}
|
||||
|
||||
workspaceIDs := map[uuid.UUID]struct{}{}
|
||||
allWorkspaces, err := api.Database.GetWorkspacesByOrganizationIDs(r.Context(), database.GetWorkspacesByOrganizationIDsParams{
|
||||
Ids: organizationIDs,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get workspaces for organizations: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
for _, ws := range allWorkspaces {
|
||||
workspaceIDs[ws.ID] = struct{}{}
|
||||
}
|
||||
allWorkspaces := make([]database.Workspace, 0)
|
||||
userWorkspaces, err := api.Database.GetWorkspacesByOwnerID(r.Context(), database.GetWorkspacesByOwnerIDParams{
|
||||
OwnerID: user.ID,
|
||||
})
|
||||
@ -137,11 +119,12 @@ func (api *api) workspacesByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
for _, ws := range userWorkspaces {
|
||||
_, exists := workspaceIDs[ws.ID]
|
||||
if exists {
|
||||
continue
|
||||
ws := ws
|
||||
err = api.Authorizer.ByRoleName(r.Context(), user.ID.String(), roles.Roles, rbac.ActionRead,
|
||||
rbac.ResourceWorkspace.InOrg(ws.OrganizationID).WithOwner(ws.OwnerID.String()).WithID(ws.ID.String()))
|
||||
if err == nil {
|
||||
allWorkspaces = append(allWorkspaces, ws)
|
||||
}
|
||||
allWorkspaces = append(allWorkspaces, ws)
|
||||
}
|
||||
|
||||
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, allWorkspaces)
|
||||
@ -156,6 +139,7 @@ func (api *api) workspacesByUser(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (api *api) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
|
||||
owner := httpmw.UserParam(r)
|
||||
roles := httpmw.UserRoles(r)
|
||||
workspaces, err := api.Database.GetWorkspacesByOwnerID(r.Context(), database.GetWorkspacesByOwnerIDParams{
|
||||
OwnerID: owner.ID,
|
||||
})
|
||||
@ -168,7 +152,18 @@ func (api *api) workspacesByOwner(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
return
|
||||
}
|
||||
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces)
|
||||
|
||||
allowedWorkspaces := make([]database.Workspace, 0)
|
||||
for _, ws := range workspaces {
|
||||
ws := ws
|
||||
err = api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, rbac.ActionRead,
|
||||
rbac.ResourceWorkspace.InOrg(ws.OrganizationID).WithOwner(ws.OwnerID.String()).WithID(ws.ID.String()))
|
||||
if err == nil {
|
||||
allowedWorkspaces = append(allowedWorkspaces, ws)
|
||||
}
|
||||
}
|
||||
|
||||
apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, allowedWorkspaces)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("convert workspaces: %s", err),
|
||||
@ -188,9 +183,8 @@ func (api *api) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
||||
Name: workspaceName,
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
|
||||
Message: fmt.Sprintf("no workspace found by name %q", workspaceName),
|
||||
})
|
||||
// Do not leak information if the workspace exists or not
|
||||
httpapi.Forbidden(rw)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@ -207,6 +201,11 @@ func (api *api) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
if !api.Authorize(rw, r, rbac.ActionRead,
|
||||
rbac.ResourceWorkspace.InOrg(workspace.OrganizationID).WithOwner(workspace.OwnerID.String()).WithID(workspace.ID.String())) {
|
||||
return
|
||||
}
|
||||
|
||||
build, err := api.Database.GetWorkspaceBuildByWorkspaceIDWithoutAfter(r.Context(), workspace.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
|
Reference in New Issue
Block a user