mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
refactor: Return the display_name and name in the roles endpoint (#1328)
This commit is contained in:
@ -51,7 +51,8 @@ var (
|
|||||||
// admin grants all actions to all resources.
|
// admin grants all actions to all resources.
|
||||||
admin: func(_ string) Role {
|
admin: func(_ string) Role {
|
||||||
return Role{
|
return Role{
|
||||||
Name: admin,
|
Name: admin,
|
||||||
|
DisplayName: "Admin",
|
||||||
Site: permissions(map[Object][]Action{
|
Site: permissions(map[Object][]Action{
|
||||||
ResourceWildcard: {WildcardSymbol},
|
ResourceWildcard: {WildcardSymbol},
|
||||||
}),
|
}),
|
||||||
@ -61,7 +62,8 @@ var (
|
|||||||
// member grants all actions to all resources owned by the user
|
// member grants all actions to all resources owned by the user
|
||||||
member: func(_ string) Role {
|
member: func(_ string) Role {
|
||||||
return Role{
|
return Role{
|
||||||
Name: member,
|
Name: member,
|
||||||
|
DisplayName: "Member",
|
||||||
User: permissions(map[Object][]Action{
|
User: permissions(map[Object][]Action{
|
||||||
ResourceWildcard: {WildcardSymbol},
|
ResourceWildcard: {WildcardSymbol},
|
||||||
}),
|
}),
|
||||||
@ -73,7 +75,8 @@ var (
|
|||||||
// TODO: Finish the auditor as we add resources.
|
// TODO: Finish the auditor as we add resources.
|
||||||
auditor: func(_ string) Role {
|
auditor: func(_ string) Role {
|
||||||
return Role{
|
return Role{
|
||||||
Name: "auditor",
|
Name: "auditor",
|
||||||
|
DisplayName: "Auditor",
|
||||||
Site: permissions(map[Object][]Action{
|
Site: permissions(map[Object][]Action{
|
||||||
// Should be able to read all template details, even in orgs they
|
// Should be able to read all template details, even in orgs they
|
||||||
// are not in.
|
// are not in.
|
||||||
@ -86,7 +89,8 @@ var (
|
|||||||
// organization scope.
|
// organization scope.
|
||||||
orgAdmin: func(organizationID string) Role {
|
orgAdmin: func(organizationID string) Role {
|
||||||
return Role{
|
return Role{
|
||||||
Name: roleName(orgAdmin, organizationID),
|
Name: roleName(orgAdmin, organizationID),
|
||||||
|
DisplayName: "Organization Admin",
|
||||||
Org: map[string][]Permission{
|
Org: map[string][]Permission{
|
||||||
organizationID: {
|
organizationID: {
|
||||||
{
|
{
|
||||||
@ -104,7 +108,8 @@ var (
|
|||||||
// in an organization.
|
// in an organization.
|
||||||
orgMember: func(organizationID string) Role {
|
orgMember: func(organizationID string) Role {
|
||||||
return Role{
|
return Role{
|
||||||
Name: roleName(orgMember, organizationID),
|
Name: roleName(orgMember, organizationID),
|
||||||
|
DisplayName: "Organization Member",
|
||||||
Org: map[string][]Permission{
|
Org: map[string][]Permission{
|
||||||
organizationID: {},
|
organizationID: {},
|
||||||
},
|
},
|
||||||
@ -151,11 +156,11 @@ func IsOrgRole(roleName string) (string, bool) {
|
|||||||
//
|
//
|
||||||
// This should be a list in a database, but until then we build
|
// This should be a list in a database, but until then we build
|
||||||
// the list from the builtins.
|
// the list from the builtins.
|
||||||
func OrganizationRoles(organizationID uuid.UUID) []string {
|
func OrganizationRoles(organizationID uuid.UUID) []Role {
|
||||||
var roles []string
|
var roles []Role
|
||||||
for _, roleF := range builtInRoles {
|
for _, roleF := range builtInRoles {
|
||||||
role := roleF(organizationID.String()).Name
|
role := roleF(organizationID.String())
|
||||||
_, scope, err := roleSplit(role)
|
_, scope, err := roleSplit(role.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This should never happen
|
// This should never happen
|
||||||
continue
|
continue
|
||||||
@ -172,8 +177,8 @@ func OrganizationRoles(organizationID uuid.UUID) []string {
|
|||||||
//
|
//
|
||||||
// This should be a list in a database, but until then we build
|
// This should be a list in a database, but until then we build
|
||||||
// the list from the builtins.
|
// the list from the builtins.
|
||||||
func SiteRoles() []string {
|
func SiteRoles() []Role {
|
||||||
var roles []string
|
var roles []Role
|
||||||
for _, roleF := range builtInRoles {
|
for _, roleF := range builtInRoles {
|
||||||
role := roleF("random")
|
role := roleF("random")
|
||||||
_, scope, err := roleSplit(role.Name)
|
_, scope, err := roleSplit(role.Name)
|
||||||
@ -182,7 +187,7 @@ func SiteRoles() []string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if scope == "" {
|
if scope == "" {
|
||||||
roles = append(roles, role.Name)
|
roles = append(roles, role)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return roles
|
return roles
|
||||||
|
@ -65,6 +65,12 @@ func TestIsOrgRole(t *testing.T) {
|
|||||||
func TestListRoles(t *testing.T) {
|
func TestListRoles(t *testing.T) {
|
||||||
t.Parallel()
|
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
|
// If this test is ever failing, just update the list to the roles
|
||||||
// expected from the builtin set.
|
// expected from the builtin set.
|
||||||
require.ElementsMatch(t, []string{
|
require.ElementsMatch(t, []string{
|
||||||
@ -72,12 +78,18 @@ func TestListRoles(t *testing.T) {
|
|||||||
"member",
|
"member",
|
||||||
"auditor",
|
"auditor",
|
||||||
},
|
},
|
||||||
rbac.SiteRoles())
|
siteRoleNames)
|
||||||
|
|
||||||
orgID := uuid.New()
|
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{
|
require.ElementsMatch(t, []string{
|
||||||
fmt.Sprintf("organization-admin:%s", orgID.String()),
|
fmt.Sprintf("organization-admin:%s", orgID.String()),
|
||||||
fmt.Sprintf("organization-member:%s", orgID.String()),
|
fmt.Sprintf("organization-member:%s", orgID.String()),
|
||||||
},
|
},
|
||||||
rbac.OrganizationRoles(orgID))
|
orgRoleNames)
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,9 @@ type Permission struct {
|
|||||||
// Users of this package should instead **only** use the role names, and
|
// Users of this package should instead **only** use the role names, and
|
||||||
// this package will expand the role names into their json payloads.
|
// this package will expand the role names into their json payloads.
|
||||||
type Role struct {
|
type Role struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Site []Permission `json:"site"`
|
DisplayName string `json:"display_name"`
|
||||||
|
Site []Permission `json:"site"`
|
||||||
// Org is a map of orgid to permissions. We represent orgid as a string.
|
// 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
|
// We scope the organizations in the role so we can easily combine all the
|
||||||
// roles.
|
// roles.
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/coder/coder/coderd/httpmw"
|
"github.com/coder/coder/coderd/httpmw"
|
||||||
|
"github.com/coder/coder/codersdk"
|
||||||
|
|
||||||
"github.com/coder/coder/coderd/httpapi"
|
"github.com/coder/coder/coderd/httpapi"
|
||||||
"github.com/coder/coder/coderd/rbac"
|
"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
|
// TODO: @emyrk in the future, allow granular subsets of roles to be returned based on the
|
||||||
// role of the user.
|
// role of the user.
|
||||||
roles := rbac.SiteRoles()
|
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.
|
// 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.
|
// role of the user.
|
||||||
organization := httpmw.OrganizationParam(r)
|
organization := httpmw.OrganizationParam(r)
|
||||||
roles := rbac.OrganizationRoles(organization.ID)
|
roles := rbac.OrganizationRoles(organization.ID)
|
||||||
httpapi.Write(rw, http.StatusOK, roles)
|
httpapi.Write(rw, http.StatusOK, codersdk.ConvertRoles(roles))
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,13 @@ func TestListRoles(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Client *codersdk.Client
|
Client *codersdk.Client
|
||||||
APICall func() ([]string, error)
|
APICall func() ([]codersdk.Role, error)
|
||||||
ExpectedRoles []string
|
ExpectedRoles []codersdk.Role
|
||||||
AuthorizedError string
|
AuthorizedError string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Name: "MemberListSite",
|
Name: "MemberListSite",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
x, err := member.ListSiteRoles(ctx)
|
x, err := member.ListSiteRoles(ctx)
|
||||||
return x, err
|
return x, err
|
||||||
},
|
},
|
||||||
@ -59,14 +59,14 @@ func TestListRoles(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "OrgMemberListOrg",
|
Name: "OrgMemberListOrg",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return member.ListOrganizationRoles(ctx, admin.OrganizationID)
|
return member.ListOrganizationRoles(ctx, admin.OrganizationID)
|
||||||
},
|
},
|
||||||
AuthorizedError: unauth,
|
AuthorizedError: unauth,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "NonOrgMemberListOrg",
|
Name: "NonOrgMemberListOrg",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return member.ListOrganizationRoles(ctx, otherOrg.ID)
|
return member.ListOrganizationRoles(ctx, otherOrg.ID)
|
||||||
},
|
},
|
||||||
AuthorizedError: notMember,
|
AuthorizedError: notMember,
|
||||||
@ -74,21 +74,21 @@ func TestListRoles(t *testing.T) {
|
|||||||
// Org admin
|
// Org admin
|
||||||
{
|
{
|
||||||
Name: "OrgAdminListSite",
|
Name: "OrgAdminListSite",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return orgAdmin.ListSiteRoles(ctx)
|
return orgAdmin.ListSiteRoles(ctx)
|
||||||
},
|
},
|
||||||
AuthorizedError: unauth,
|
AuthorizedError: unauth,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "OrgAdminListOrg",
|
Name: "OrgAdminListOrg",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return orgAdmin.ListOrganizationRoles(ctx, admin.OrganizationID)
|
return orgAdmin.ListOrganizationRoles(ctx, admin.OrganizationID)
|
||||||
},
|
},
|
||||||
ExpectedRoles: rbac.OrganizationRoles(admin.OrganizationID),
|
ExpectedRoles: codersdk.ConvertRoles(rbac.OrganizationRoles(admin.OrganizationID)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "OrgAdminListOtherOrg",
|
Name: "OrgAdminListOtherOrg",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return orgAdmin.ListOrganizationRoles(ctx, otherOrg.ID)
|
return orgAdmin.ListOrganizationRoles(ctx, otherOrg.ID)
|
||||||
},
|
},
|
||||||
AuthorizedError: notMember,
|
AuthorizedError: notMember,
|
||||||
@ -96,17 +96,17 @@ func TestListRoles(t *testing.T) {
|
|||||||
// Admin
|
// Admin
|
||||||
{
|
{
|
||||||
Name: "AdminListSite",
|
Name: "AdminListSite",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return client.ListSiteRoles(ctx)
|
return client.ListSiteRoles(ctx)
|
||||||
},
|
},
|
||||||
ExpectedRoles: rbac.SiteRoles(),
|
ExpectedRoles: codersdk.ConvertRoles(rbac.SiteRoles()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "AdminListOrg",
|
Name: "AdminListOrg",
|
||||||
APICall: func() ([]string, error) {
|
APICall: func() ([]codersdk.Role, error) {
|
||||||
return client.ListOrganizationRoles(ctx, admin.OrganizationID)
|
return client.ListOrganizationRoles(ctx, admin.OrganizationID)
|
||||||
},
|
},
|
||||||
ExpectedRoles: rbac.OrganizationRoles(admin.OrganizationID),
|
ExpectedRoles: codersdk.ConvertRoles(rbac.OrganizationRoles(admin.OrganizationID)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/coder/coder/coderd/rbac"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
}
|
||||||
|
|
||||||
// ListSiteRoles lists all available site wide roles.
|
// ListSiteRoles lists all available site wide roles.
|
||||||
// This is not user specific.
|
// 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)
|
res, err := c.request(ctx, http.MethodGet, "/api/v2/users/roles", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -20,13 +26,13 @@ func (c *Client) ListSiteRoles(ctx context.Context) ([]string, error) {
|
|||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusOK {
|
||||||
return nil, readBodyAsError(res)
|
return nil, readBodyAsError(res)
|
||||||
}
|
}
|
||||||
var roles []string
|
var roles []Role
|
||||||
return roles, json.NewDecoder(res.Body).Decode(&roles)
|
return roles, json.NewDecoder(res.Body).Decode(&roles)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListOrganizationRoles lists all available roles for a given organization.
|
// ListOrganizationRoles lists all available roles for a given organization.
|
||||||
// This is not user specific.
|
// 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)
|
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/members/roles/", org.String()), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -35,6 +41,17 @@ func (c *Client) ListOrganizationRoles(ctx context.Context, org uuid.UUID) ([]st
|
|||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusOK {
|
||||||
return nil, readBodyAsError(res)
|
return nil, readBodyAsError(res)
|
||||||
}
|
}
|
||||||
var roles []string
|
var roles []Role
|
||||||
return roles, json.NewDecoder(res.Body).Decode(&roles)
|
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
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user