Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: Implied 'member' roles for site and organization#1917

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Emyrk merged 9 commits intomainfromstevenmasley/implied_member
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletionscoderd/authorize.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,12 +13,12 @@ import (
)

func AuthorizeFilter[O rbac.Objecter](api *API, r *http.Request, action rbac.Action, objects []O) []O {
roles := httpmw.UserRoles(r)
roles := httpmw.AuthorizationUserRoles(r)
return rbac.Filter(r.Context(), api.Authorizer, roles.ID.String(), roles.Roles, action, objects)
}

func (api *API) Authorize(rw http.ResponseWriter, r *http.Request, action rbac.Action, object rbac.Objecter) bool {
roles := httpmw.UserRoles(r)
roles := httpmw.AuthorizationUserRoles(r)
err := api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, action, object.RBACObject())
if err != nil {
httpapi.Write(rw, http.StatusForbidden, httpapi.Response{
Expand Down
2 changes: 1 addition & 1 deletioncoderd/coderdtest/coderdtest.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -281,7 +281,7 @@ func CreateAnotherUser(t *testing.T, client *codersdk.Client, organizationID uui
organizationID, err := uuid.Parse(orgID)
require.NoError(t, err, fmt.Sprintf("parse org id %q", orgID))
_, err = client.UpdateOrganizationMemberRoles(context.Background(), organizationID, user.ID.String(),
codersdk.UpdateRoles{Roles:append(roles, rbac.RoleOrgMember(organizationID))})
codersdk.UpdateRoles{Roles: roles})
require.NoError(t, err, "update org membership roles")
}
}
Expand Down
8 changes: 5 additions & 3 deletionscoderd/database/databasefake/databasefake.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -276,7 +276,7 @@ func (q *fakeQuerier) GetUsersByIDs(_ context.Context, ids []uuid.UUID) ([]datab
return users, nil
}

func (q *fakeQuerier)GetAllUserRoles(_ context.Context, userID uuid.UUID) (database.GetAllUserRolesRow, error) {
func (q *fakeQuerier)GetAuthorizationUserRoles(_ context.Context, userID uuid.UUID) (database.GetAuthorizationUserRolesRow, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

Expand All@@ -286,6 +286,7 @@ func (q *fakeQuerier) GetAllUserRoles(_ context.Context, userID uuid.UUID) (data
if u.ID == userID {
u := u
roles = append(roles, u.RBACRoles...)
roles = append(roles, "member")
user = &u
break
}
Expand All@@ -294,14 +295,15 @@ func (q *fakeQuerier) GetAllUserRoles(_ context.Context, userID uuid.UUID) (data
for _, mem := range q.organizationMembers {
if mem.UserID == userID {
roles = append(roles, mem.Roles...)
roles = append(roles, "organization-member:"+mem.OrganizationID.String())
}
}

if user == nil {
return database.GetAllUserRolesRow{}, sql.ErrNoRows
return database.GetAuthorizationUserRolesRow{}, sql.ErrNoRows
}

return database.GetAllUserRolesRow{
return database.GetAuthorizationUserRolesRow{
ID: userID,
Username: user.Username,
Status: user.Status,
Expand Down
11 changes: 11 additions & 0 deletionscoderd/database/migrations/000016_drop_member_roles.down.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
--- Remove the now implied 'member' role.
UPDATE
users
SET
rbac_roles = array_append(rbac_roles, 'member');

--- Remove the now implied 'organization-member' role.
UPDATE
organization_members
SET
roles = array_append(roles, 'organization-member:'||organization_id::text);
11 changes: 11 additions & 0 deletionscoderd/database/migrations/000016_drop_member_roles.up.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
--- Remove the now implied 'member' role.
UPDATE
users
SET
rbac_roles = array_remove(rbac_roles, 'member');

--- Remove the now implied 'organization-member' role.
UPDATE
organization_members
SET
roles = array_remove(roles, 'organization-member:'||organization_id::text);
4 changes: 3 additions & 1 deletioncoderd/database/querier.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

26 changes: 17 additions & 9 deletionscoderd/database/queries.sql.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

18 changes: 13 additions & 5 deletionscoderd/database/queries/users.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -134,12 +134,20 @@ WHERE
id = $1 RETURNING *;


-- name: GetAllUserRoles :one
-- name: GetAuthorizationUserRoles :one
-- This function returns roles for authorization purposes. Implied member roles
-- are included.
SELECT
-- username is returned just to help for logging purposes
-- status is used to enforce 'suspended' users, as all roles are ignored
--when suspended.
id, username, status, array_cat(users.rbac_roles, organization_members.roles) :: text[] AS roles
-- username is returned just to help for logging purposes
-- status is used to enforce 'suspended' users, as all roles are ignored
--when suspended.
id, username, status,
array_cat(
-- All users are members
array_append(users.rbac_roles, 'member'),
-- All org_members get the org-member role for their orgs
array_append(organization_members.roles, 'organization-member:'||organization_members.organization_id::text)) :: text[]
AS roles
FROM
users
LEFT JOIN organization_members
Expand Down
15 changes: 14 additions & 1 deletioncoderd/httpmw/apikey.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,6 +31,19 @@ func APIKey(r *http.Request) database.APIKey {
return apiKey
}

// User roles are the 'subject' field of Authorize()
type userRolesKey struct{}

// AuthorizationUserRoles returns the roles used for authorization.
// Comes from the ExtractAPIKey handler.
func AuthorizationUserRoles(r *http.Request) database.GetAuthorizationUserRolesRow {
apiKey, ok := r.Context().Value(userRolesKey{}).(database.GetAuthorizationUserRolesRow)
if !ok {
panic("developer error: user roles middleware not provided")
}
return apiKey
}

// OAuth2Configs is a collection of configurations for OAuth-based authentication.
// This should be extended to support other authentication types in the future.
type OAuth2Configs struct {
Expand DownExpand Up@@ -178,7 +191,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// If the key is valid, we also fetch the user roles and status.
// The roles are used for RBAC authorize checks, and the status
// is to block 'suspended' users from accessing the platform.
roles, err := db.GetAllUserRoles(r.Context(), key.UserID)
roles, err := db.GetAuthorizationUserRoles(r.Context(), key.UserID)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "roles not found",
Expand Down
40 changes: 0 additions & 40 deletionscoderd/httpmw/authorize.go
View file
Open in desktop

This file was deleted.

16 changes: 8 additions & 8 deletionscoderd/httpmw/authorize_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,23 +31,23 @@ func TestExtractUserRoles(t *testing.T) {
{
Name: "Member",
AddUser: func(db database.Store) (database.User, []string, string) {
roles := []string{rbac.RoleMember()}
roles := []string{}
user, token := addUser(t, db, roles...)
return user, roles, token
return user,append(roles, rbac.RoleMember()), token
},
},
{
Name: "Admin",
AddUser: func(db database.Store) (database.User, []string, string) {
roles := []string{rbac.RoleMember(), rbac.RoleAdmin()}
roles := []string{rbac.RoleAdmin()}
user, token := addUser(t, db, roles...)
return user, roles, token
return user,append(roles, rbac.RoleMember()), token
},
},
{
Name: "OrgMember",
AddUser: func(db database.Store) (database.User, []string, string) {
roles := []string{rbac.RoleMember()}
roles := []string{}
user, token := addUser(t, db, roles...)
org, err := db.InsertOrganization(context.Background(), database.InsertOrganizationParams{
ID: uuid.New(),
Expand All@@ -58,7 +58,7 @@ func TestExtractUserRoles(t *testing.T) {
})
require.NoError(t, err)

orgRoles := []string{rbac.RoleOrgMember(org.ID)}
orgRoles := []string{}
_, err = db.InsertOrganizationMember(context.Background(), database.InsertOrganizationMemberParams{
OrganizationID: org.ID,
UserID: user.ID,
Expand All@@ -67,7 +67,7 @@ func TestExtractUserRoles(t *testing.T) {
Roles: orgRoles,
})
require.NoError(t, err)
return user, append(roles, orgRoles...), token
return user, append(roles,append(orgRoles, rbac.RoleMember(), rbac.RoleOrgMember(org.ID))...), token
},
},
}
Expand All@@ -86,7 +86,7 @@ func TestExtractUserRoles(t *testing.T) {
httpmw.ExtractAPIKey(db, &httpmw.OAuth2Configs{}),
)
rtr.Get("/", func(_ http.ResponseWriter, r *http.Request) {
roles := httpmw.UserRoles(r)
roles := httpmw.AuthorizationUserRoles(r)
require.ElementsMatch(t, user.ID, roles.ID)
require.ElementsMatch(t, expRoles, roles.Roles)
})
Expand Down
4 changes: 3 additions & 1 deletioncoderd/members.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,9 @@ func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) {
return
}

added, removed := rbac.ChangeRoleSet(member.Roles, params.Roles)
// The org-member role is always implied.
impliedTypes := append(params.Roles, rbac.RoleOrgMember(organization.ID))
added, removed := rbac.ChangeRoleSet(member.Roles, impliedTypes)
for _, roleName := range added {
// Assigning a role requires the create permission.
if !api.Authorize(rw, r, rbac.ActionCreate, rbac.ResourceOrgRoleAssignment.WithID(roleName).InOrg(organization.ID)) {
Expand Down
4 changes: 1 addition & 3 deletionscoderd/organizations.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -73,13 +73,11 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Roles: []string{
// Also assign member role incase they get demoted from admin
rbac.RoleOrgMember(organization.ID),
rbac.RoleOrgAdmin(organization.ID),
},
})
if err != nil {
return xerrors.Errorf("create organizationmember: %w", err)
return xerrors.Errorf("create organizationadmin: %w", err)
}
return nil
})
Expand Down
4 changes: 2 additions & 2 deletionscoderd/rbac/builtin.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,7 +63,7 @@ var (
member: func(_ string) Role {
return Role{
Name: member,
DisplayName: "Member",
DisplayName: "",
Site: permissions(map[Object][]Action{
// All users can read all other users and know they exist.
ResourceUser: {ActionRead},
Expand DownExpand Up@@ -116,7 +116,7 @@ var (
orgMember: func(organizationID string) Role {
return Role{
Name: roleName(orgMember, organizationID),
DisplayName: "Organization Member",
DisplayName: "",
Org: map[string][]Permission{
organizationID: {
{
Expand Down
4 changes: 3 additions & 1 deletioncoderd/rbac/role.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,9 @@ type Permission struct {
// Users of this package should instead **only** use the role names, and
// this package will expand the role names into their json payloads.
type Role struct {
Name string `json:"name"`
Name string `json:"name"`
// DisplayName is used for UI purposes. If the role has no display name,
// that means the UI should never display it.
DisplayName string `json:"display_name"`
Comment on lines +20 to 23
Copy link
Member

Choose a reason for hiding this comment

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

Can we just not send this role to the UI at all? I think the wholeorg-member: thing is just the Rego leaking out a bit.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I don't send it to the UI. I just use theDisplayName to indicate that.

Pretty much in the current form all role lists are compiled from thebuiltin const. So that list needs to be filtered for the UI. I thought it was easy to just use this field, since it has no purpose for the member roles now

Copy link
Member

Choose a reason for hiding this comment

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

Ah gotcha.

Site []Permission `json:"site"`
// Org is a map of orgid to permissions. We represent orgid as a string.
Expand Down
6 changes: 5 additions & 1 deletioncoderd/roles.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,7 +45,7 @@ func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) {
}

// use the roles of the user specified, not the person making the request.
roles, err := api.Database.GetAllUserRoles(r.Context(), user.ID)
roles, err := api.Database.GetAuthorizationUserRoles(r.Context(), user.ID)
if err != nil {
httpapi.Forbidden(rw)
return
Expand DownExpand Up@@ -91,6 +91,10 @@ func convertRole(role rbac.Role) codersdk.Role {
func convertRoles(roles []rbac.Role) []codersdk.Role {
converted := make([]codersdk.Role, 0, len(roles))
for _, role := range roles {
// Roles without display names should never be shown to the ui.
if role.DisplayName == "" {
continue
}
converted = append(converted, convertRole(role))
}
return converted
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp