diff --git a/coderd/rbac/builtin.go b/coderd/rbac/builtin.go index 26ec0a1afa1cc..6a85cfe3256a2 100644 --- a/coderd/rbac/builtin.go +++ b/coderd/rbac/builtin.go @@ -51,7 +51,8 @@ var ( // admin grants all actions to all resources. admin: func(_ string) Role { return Role{ - Name: admin, + Name: admin, + DisplayName: "Admin", Site: permissions(map[Object][]Action{ ResourceWildcard: {WildcardSymbol}, }), @@ -61,7 +62,8 @@ var ( // member grants all actions to all resources owned by the user member: func(_ string) Role { return Role{ - Name: member, + Name: member, + DisplayName: "Member", User: permissions(map[Object][]Action{ ResourceWildcard: {WildcardSymbol}, }), @@ -73,7 +75,8 @@ var ( // TODO: Finish the auditor as we add resources. auditor: func(_ string) Role { return Role{ - Name: "auditor", + Name: "auditor", + DisplayName: "Auditor", Site: permissions(map[Object][]Action{ // Should be able to read all template details, even in orgs they // are not in. @@ -86,7 +89,8 @@ var ( // organization scope. orgAdmin: func(organizationID string) Role { return Role{ - Name: roleName(orgAdmin, organizationID), + Name: roleName(orgAdmin, organizationID), + DisplayName: "Organization Admin", Org: map[string][]Permission{ organizationID: { { @@ -104,7 +108,8 @@ var ( // in an organization. orgMember: func(organizationID string) Role { return Role{ - Name: roleName(orgMember, organizationID), + Name: roleName(orgMember, organizationID), + DisplayName: "Organization Member", Org: map[string][]Permission{ organizationID: {}, }, @@ -151,11 +156,11 @@ func IsOrgRole(roleName string) (string, bool) { // // This should be a list in a database, but until then we build // the list from the builtins. -func OrganizationRoles(organizationID uuid.UUID) []string { - var roles []string +func OrganizationRoles(organizationID uuid.UUID) []Role { + var roles []Role for _, roleF := range builtInRoles { - role := roleF(organizationID.String()).Name - _, scope, err := roleSplit(role) + role := roleF(organizationID.String()) + _, scope, err := roleSplit(role.Name) if err != nil { // This should never happen continue @@ -172,8 +177,8 @@ func OrganizationRoles(organizationID uuid.UUID) []string { // // This should be a list in a database, but until then we build // the list from the builtins. -func SiteRoles() []string { - var roles []string +func SiteRoles() []Role { + var roles []Role for _, roleF := range builtInRoles { role := roleF("random") _, scope, err := roleSplit(role.Name) @@ -182,7 +187,7 @@ func SiteRoles() []string { continue } if scope == "" { - roles = append(roles, role.Name) + roles = append(roles, role) } } return roles diff --git a/coderd/rbac/builtin_test.go b/coderd/rbac/builtin_test.go index d8b937f78ac53..72cde1eea40a6 100644 --- a/coderd/rbac/builtin_test.go +++ b/coderd/rbac/builtin_test.go @@ -65,6 +65,12 @@ func TestIsOrgRole(t *testing.T) { func TestListRoles(t *testing.T) { t.Parallel() + siteRoles := rbac.SiteRoles() + siteRoleNames := make([]string, 0, len(siteRoles)) + for _, role := range siteRoles { + siteRoleNames = append(siteRoleNames, role.Name) + } + // If this test is ever failing, just update the list to the roles // expected from the builtin set. require.ElementsMatch(t, []string{ @@ -72,12 +78,18 @@ func TestListRoles(t *testing.T) { "member", "auditor", }, - rbac.SiteRoles()) + siteRoleNames) orgID := uuid.New() + orgRoles := rbac.OrganizationRoles(orgID) + orgRoleNames := make([]string, 0, len(orgRoles)) + for _, role := range orgRoles { + orgRoleNames = append(orgRoleNames, role.Name) + } + require.ElementsMatch(t, []string{ fmt.Sprintf("organization-admin:%s", orgID.String()), fmt.Sprintf("organization-member:%s", orgID.String()), }, - rbac.OrganizationRoles(orgID)) + orgRoleNames) } diff --git a/coderd/rbac/role.go b/coderd/rbac/role.go index a083b2a11ac80..c236bf4a3cf59 100644 --- a/coderd/rbac/role.go +++ b/coderd/rbac/role.go @@ -17,8 +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"` - Site []Permission `json:"site"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + Site []Permission `json:"site"` // Org is a map of orgid to permissions. We represent orgid as a string. // We scope the organizations in the role so we can easily combine all the // roles. diff --git a/coderd/roles.go b/coderd/roles.go index 64bd7353b4293..470d726252809 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/rbac" @@ -14,7 +15,7 @@ func (*api) assignableSiteRoles(rw http.ResponseWriter, _ *http.Request) { // TODO: @emyrk in the future, allow granular subsets of roles to be returned based on the // role of the user. roles := rbac.SiteRoles() - httpapi.Write(rw, http.StatusOK, roles) + httpapi.Write(rw, http.StatusOK, codersdk.ConvertRoles(roles)) } // assignableSiteRoles returns all site wide roles that can be assigned. @@ -23,5 +24,5 @@ func (*api) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) { // role of the user. organization := httpmw.OrganizationParam(r) roles := rbac.OrganizationRoles(organization.ID) - httpapi.Write(rw, http.StatusOK, roles) + httpapi.Write(rw, http.StatusOK, codersdk.ConvertRoles(roles)) } diff --git a/coderd/roles_test.go b/coderd/roles_test.go index 523439883e33d..6254ffa73d1d1 100644 --- a/coderd/roles_test.go +++ b/coderd/roles_test.go @@ -45,13 +45,13 @@ func TestListRoles(t *testing.T) { testCases := []struct { Name string Client *codersdk.Client - APICall func() ([]string, error) - ExpectedRoles []string + APICall func() ([]codersdk.Role, error) + ExpectedRoles []codersdk.Role AuthorizedError string }{ { Name: "MemberListSite", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { x, err := member.ListSiteRoles(ctx) return x, err }, @@ -59,14 +59,14 @@ func TestListRoles(t *testing.T) { }, { Name: "OrgMemberListOrg", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return member.ListOrganizationRoles(ctx, admin.OrganizationID) }, AuthorizedError: unauth, }, { Name: "NonOrgMemberListOrg", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return member.ListOrganizationRoles(ctx, otherOrg.ID) }, AuthorizedError: notMember, @@ -74,21 +74,21 @@ func TestListRoles(t *testing.T) { // Org admin { Name: "OrgAdminListSite", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return orgAdmin.ListSiteRoles(ctx) }, AuthorizedError: unauth, }, { Name: "OrgAdminListOrg", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return orgAdmin.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: rbac.OrganizationRoles(admin.OrganizationID), + ExpectedRoles: codersdk.ConvertRoles(rbac.OrganizationRoles(admin.OrganizationID)), }, { Name: "OrgAdminListOtherOrg", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return orgAdmin.ListOrganizationRoles(ctx, otherOrg.ID) }, AuthorizedError: notMember, @@ -96,17 +96,17 @@ func TestListRoles(t *testing.T) { // Admin { Name: "AdminListSite", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return client.ListSiteRoles(ctx) }, - ExpectedRoles: rbac.SiteRoles(), + ExpectedRoles: codersdk.ConvertRoles(rbac.SiteRoles()), }, { Name: "AdminListOrg", - APICall: func() ([]string, error) { + APICall: func() ([]codersdk.Role, error) { return client.ListOrganizationRoles(ctx, admin.OrganizationID) }, - ExpectedRoles: rbac.OrganizationRoles(admin.OrganizationID), + ExpectedRoles: codersdk.ConvertRoles(rbac.OrganizationRoles(admin.OrganizationID)), }, } diff --git a/coderd/users.go b/coderd/users.go index 108ad0813cb49..7841198558f5b 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -361,10 +361,10 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) { } func (api *api) putUserPassword(rw http.ResponseWriter, r *http.Request) { - var ( - user = httpmw.UserParam(r) - params codersdk.UpdateUserPasswordRequest - ) + var ( + user = httpmw.UserParam(r) + params codersdk.UpdateUserPasswordRequest + ) if !httpapi.Read(rw, r, ¶ms) { return } diff --git a/codersdk/roles.go b/codersdk/roles.go index d7d6d0fe2b8bc..cc2234505fa2d 100644 --- a/codersdk/roles.go +++ b/codersdk/roles.go @@ -6,12 +6,18 @@ import ( "fmt" "net/http" + "github.com/coder/coder/coderd/rbac" "github.com/google/uuid" ) +type Role struct { + Name string `json:"name"` + DisplayName string `json:"display_name"` +} + // ListSiteRoles lists all available site wide roles. // This is not user specific. -func (c *Client) ListSiteRoles(ctx context.Context) ([]string, error) { +func (c *Client) ListSiteRoles(ctx context.Context) ([]Role, error) { res, err := c.request(ctx, http.MethodGet, "/api/v2/users/roles", nil) if err != nil { return nil, err @@ -20,13 +26,13 @@ func (c *Client) ListSiteRoles(ctx context.Context) ([]string, error) { if res.StatusCode != http.StatusOK { return nil, readBodyAsError(res) } - var roles []string + var roles []Role return roles, json.NewDecoder(res.Body).Decode(&roles) } // ListOrganizationRoles lists all available roles for a given organization. // This is not user specific. -func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]string, error) { +func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]Role, error) { res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/members/roles/", org.String()), nil) if err != nil { return nil, err @@ -35,6 +41,17 @@ func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]st if res.StatusCode != http.StatusOK { return nil, readBodyAsError(res) } - var roles []string + var roles []Role return roles, json.NewDecoder(res.Body).Decode(&roles) } + +func ConvertRoles(roles []rbac.Role) []Role { + converted := make([]Role, 0, len(roles)) + for _, role := range roles { + converted = append(converted, Role{ + DisplayName: role.DisplayName, + Name: role.Name, + }) + } + return converted +}
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: