Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort the authz roles in ListRoles response #3857

Merged
merged 2 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion internal/authz/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ func (a *ClientWrapper) doDelete(ctx context.Context, user string, role string,

// DeleteUser removes all tuples for the given user
func (a *ClientWrapper) DeleteUser(ctx context.Context, user string) error {
for role := range AllRoles {
for role := range AllRolesDescriptions {
listresp, err := a.cli.ListObjects(ctx).Body(fgaclient.ClientListObjectsRequest{
Type: "project",
Relation: role.String(),
Expand Down
12 changes: 6 additions & 6 deletions internal/authz/authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestAllRolesExistInFGAModel(t *testing.T) {

t.Logf("relations: %v", projectTypeDef.Relations)

for r := range authz.AllRoles {
for r := range authz.AllRolesDescriptions {
assert.Contains(t, *projectTypeDef.Relations, r.String(), "role %s not found in authz model", r)
}
}
Expand Down Expand Up @@ -101,7 +101,7 @@ func TestVerifyOneProject(t *testing.T) {

// create a project
prj := uuid.New()
assert.NoError(t, c.Write(ctx, "user-1", authz.AuthzRoleAdmin, prj), "failed to write project")
assert.NoError(t, c.Write(ctx, "user-1", authz.RoleAdmin, prj), "failed to write project")

userJWT := openid.New()
assert.NoError(t, userJWT.Set("sub", "user-1"))
Expand All @@ -123,7 +123,7 @@ func TestVerifyOneProject(t *testing.T) {
assert.Equal(t, "user-1", assignments[0].Subject, "expected user to be assigned to project")

// delete the project
assert.NoError(t, c.Delete(ctx, "user-1", authz.AuthzRoleAdmin, prj), "failed to delete project")
assert.NoError(t, c.Delete(ctx, "user-1", authz.RoleAdmin, prj), "failed to delete project")

// verify the project is gone
assert.Error(t, c.Check(userctx, "get", prj), "expected project to be gone")
Expand Down Expand Up @@ -155,7 +155,7 @@ func TestVerifyMultipleProjects(t *testing.T) {

// create a project
prj1 := uuid.New()
assert.NoError(t, c.Write(ctx, "user-1", authz.AuthzRoleAdmin, prj1), "failed to write project")
assert.NoError(t, c.Write(ctx, "user-1", authz.RoleAdmin, prj1), "failed to write project")

user1JWT := openid.New()
assert.NoError(t, user1JWT.Set("sub", "user-1"))
Expand All @@ -166,14 +166,14 @@ func TestVerifyMultipleProjects(t *testing.T) {

// create another project
prj2 := uuid.New()
assert.NoError(t, c.Write(ctx, "user-1", authz.AuthzRoleViewer, prj2), "failed to write project")
assert.NoError(t, c.Write(ctx, "user-1", authz.RoleViewer, prj2), "failed to write project")

// verify the project
assert.NoError(t, c.Check(userctx, "get", prj2), "failed to check project")

// create an unrelated project
prj3 := uuid.New()
assert.NoError(t, c.Write(ctx, "user-2", authz.AuthzRoleAdmin, prj3), "failed to write project")
assert.NoError(t, c.Write(ctx, "user-2", authz.RoleAdmin, prj3), "failed to write project")

// verify the project
user2JWT := openid.New()
Expand Down
4 changes: 2 additions & 2 deletions internal/authz/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func FuzzAllAuthzApis(f *testing.F) {
}
prj := uuid.New()

c.Write(ctx, str1, authz.AuthzRoleAdmin, prj)
c.Write(ctx, str1, authz.RoleAdmin, prj)

userJWT := openid.New()

Expand All @@ -58,7 +58,7 @@ func FuzzAllAuthzApis(f *testing.F) {

c.ProjectsForUser(userctx, str4)
c.AssignmentsToProject(userctx, prj)
c.Delete(ctx, str5, authz.AuthzRoleAdmin, prj)
c.Delete(ctx, str5, authz.RoleAdmin, prj)
c.Check(userctx, "get", prj)
c.ProjectsForUser(userctx, str6)
c.AssignmentsToProject(userctx, prj)
Expand Down
48 changes: 25 additions & 23 deletions internal/authz/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,39 +32,41 @@ var ErrNotAuthorized = fmt.Errorf("not authorized")
type Role string

const (
// AuthzRoleAdmin is the admin role
AuthzRoleAdmin Role = "admin"
// AuthzRoleEditor is the editor role
AuthzRoleEditor Role = "editor"
// AuthzRoleViewer is the viewer role
AuthzRoleViewer Role = "viewer"
// AuthzRolePolicyWriter is the `policy_writer` role
AuthzRolePolicyWriter Role = "policy_writer"
// AuthzRolePermissionsManager is the `permissions_manager` role
AuthzRolePermissionsManager Role = "permissions_manager"
// RoleAdmin is the admin role
RoleAdmin Role = "admin"
// RoleEditor is the editor role
RoleEditor Role = "editor"
// RoleViewer is the viewer role
RoleViewer Role = "viewer"
// RolePolicyWriter is the `policy_writer` role
RolePolicyWriter Role = "policy_writer"
// RolePermissionsManager is the `permissions_manager` role
RolePermissionsManager Role = "permissions_manager"
)

var (
// AllRoles is a list of all roles
AllRoles = map[Role]string{
AuthzRoleAdmin: "The Admin role allows the user to perform all actions on the project and " +
// AllRolesDescriptions is a list of all roles
AllRolesDescriptions = map[Role]string{
RoleAdmin: "The Admin role allows the user to perform all actions on the project and " +
"sub-projects.",
AuthzRoleEditor: "The Editor role allows for more write and read actions on the project and " +
RoleEditor: "The Editor role allows for more write and read actions on the project and " +
"sub-projects except for project administration.",
AuthzRoleViewer: "The Viewer role allows for read actions on the project and sub-projects.",
AuthzRolePolicyWriter: "The Policy Writer role allows for writing policies (rule types and " +
RoleViewer: "The Viewer role allows for read actions on the project and sub-projects.",
RolePolicyWriter: "The Policy Writer role allows for writing policies (rule types and " +
"profiles) on the project and sub-projects. This is handy for CI jobs.",
AuthzRolePermissionsManager: "The Permissions Manager role allows for managing permissions " +
RolePermissionsManager: "The Permissions Manager role allows for managing permissions " +
"on the project and sub-projects.",
}
// AllRolesDisplayName is a list of all roles with their display names
AllRolesDisplayName = map[Role]string{
AuthzRoleAdmin: "Admin",
AuthzRoleEditor: "Editor",
AuthzRoleViewer: "Viewer",
AuthzRolePolicyWriter: "Policy Writer",
AuthzRolePermissionsManager: "Permissions Manager",
RoleAdmin: "Admin",
RoleEditor: "Editor",
RoleViewer: "Viewer",
RolePolicyWriter: "Policy Writer",
RolePermissionsManager: "Permissions Manager",
}
// AllRolesSorted is a list of all roles sorted
AllRolesSorted = []Role{RoleAdmin, RoleEditor, RoleViewer, RolePolicyWriter, RolePermissionsManager}
)

func (r Role) String() string {
Expand All @@ -77,7 +79,7 @@ func ParseRole(r string) (Role, error) {
return "", fmt.Errorf("role cannot be empty")
}
rr := Role(r)
if _, ok := AllRoles[rr]; !ok {
if _, ok := AllRolesDescriptions[rr]; !ok {
return "", fmt.Errorf("invalid role %s", r)
}

Expand Down
18 changes: 12 additions & 6 deletions internal/controlplane/handlers_authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,22 @@ var _ minder.PermissionsServiceServer = (*Server)(nil)
// ListRoles returns the list of available roles for the minder instance
func (*Server) ListRoles(_ context.Context, _ *minder.ListRolesRequest) (*minder.ListRolesResponse, error) {
resp := minder.ListRolesResponse{
Roles: make([]*minder.Role, 0, len(authz.AllRoles)),
Roles: make([]*minder.Role, 0, len(authz.AllRolesDescriptions)),
}
for role, desc := range authz.AllRoles {
// Iterate over all roles and add them to the response if they have a description. Skip if they don't.
// The roles are sorted by the order in which they are defined in the authz package, i.e. admin, editor, viewer, etc.
for _, role := range authz.AllRolesSorted {
// Skip roles that don't have a description
if authz.AllRolesDescriptions[role] == "" {
continue
}
// Add the role to the response
resp.Roles = append(resp.Roles, &minder.Role{
Name: role.String(),
DisplayName: authz.AllRolesDisplayName[role],
Description: desc,
Description: authz.AllRolesDescriptions[role],
})
}

return &resp, nil
}

Expand Down Expand Up @@ -661,7 +667,7 @@ func (s *Server) removeRole(
}

// Validate in case there's only one admin for the project and the user is trying to remove themselves
if roleToRemove == authz.AuthzRoleAdmin {
if roleToRemove == authz.RoleAdmin {
// Get all role assignments for the project
as, err := s.authzClient.AssignmentsToProject(ctx, targetProject)
if err != nil {
Expand All @@ -670,7 +676,7 @@ func (s *Server) removeRole(
// Count the number of admin roles
adminRolesCnt := 0
for _, existing := range as {
if existing.Role == authz.AuthzRoleAdmin.String() {
if existing.Role == authz.RoleAdmin.String() {
adminRolesCnt++
}
}
Expand Down
40 changes: 20 additions & 20 deletions internal/controlplane/handlers_authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,50 +317,50 @@ func TestRoleManagement(t *testing.T) {
}{{
name: "simple adds",
adds: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
}},
result: &minder.ListRoleAssignmentsResponse{
RoleAssignments: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
DisplayName: "user1",
Project: proto.String(project.String()),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
DisplayName: "user2",
Subject: user2.String(),
Project: proto.String(project.String()),
}},
},
stored: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
Project: proto.String(project.String()),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
Project: proto.String(project.String()),
}},
}, {
name: "add and remove",
adds: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
}},
removes: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
}},
result: &minder.ListRoleAssignmentsResponse{
RoleAssignments: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
DisplayName: "user1",
Subject: user1.String(),
Project: proto.String(project.String()),
Expand All @@ -370,31 +370,31 @@ func TestRoleManagement(t *testing.T) {
name: "IDP resolution",
idpFlag: true,
adds: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: "user1",
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
}},
result: &minder.ListRoleAssignmentsResponse{
RoleAssignments: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
DisplayName: "user1",
Project: proto.String(project.String()),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
DisplayName: "user2",
Project: proto.String(project.String()),
}},
},
stored: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
Project: proto.String(project.String()),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
Project: proto.String(project.String()),
}},
Expand All @@ -403,23 +403,23 @@ func TestRoleManagement(t *testing.T) {
// NOTE: we don't have a way to create invitations yet.
userManagementFlag: true,
adds: []*minder.RoleAssignment{{
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user1.String(),
}, {
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Subject: user2.String(),
}},
invites: []db.ListInvitationsForProjectRow{{
Email: "[email protected]",
Role: authz.AuthzRoleEditor.String(),
Role: authz.RoleEditor.String(),
IdentitySubject: user1.String(),
CreatedAt: time.Time{},
UpdatedAt: time.Time{},
}},
result: &minder.ListRoleAssignmentsResponse{
RoleAssignments: []*minder.RoleAssignment{},
Invitations: []*minder.Invitation{{
Role: authz.AuthzRoleEditor.String(),
Role: authz.RoleEditor.String(),
Email: "[email protected]",
Project: project.String(),
Sponsor: user1.String(),
Expand Down
2 changes: 1 addition & 1 deletion internal/controlplane/handlers_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func (s *Server) getUserDependencies(ctx context.Context, user db.User) ([]*pb.P
projectRole = &pb.Role{
Name: authzRole.String(),
DisplayName: authz.AllRolesDisplayName[authzRole],
Description: authz.AllRoles[authzRole],
Description: authz.AllRolesDescriptions[authzRole],
}
}

Expand Down
10 changes: 5 additions & 5 deletions internal/controlplane/identity_events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestDeleteUserOneProject(t *testing.T) {
p1.ID: {
{
Subject: u1.IdentitySubject,
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Project: &pID,
},
},
Expand Down Expand Up @@ -196,19 +196,19 @@ func TestDeleteUserMultiProjectMembership(t *testing.T) {
p1.ID: {
{
Subject: u1.IdentitySubject,
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Project: &p1ID,
},
},
p2.ID: {
{
Subject: u2.IdentitySubject,
Role: authz.AuthzRoleAdmin.String(),
Role: authz.RoleAdmin.String(),
Project: &p2ID,
},
{
Subject: u1.IdentitySubject,
Role: authz.AuthzRoleViewer.String(),
Role: authz.RoleViewer.String(),
Project: &p2ID,
},
},
Expand Down Expand Up @@ -247,7 +247,7 @@ func TestDeleteUserMultiProjectMembership(t *testing.T) {
assert.NotEmpty(t, assignments, "Assignments are empty")
assert.Len(t, assignments, 1, "Assignments length is not 1")
assert.Equal(t, u2.IdentitySubject, assignments[0].Subject, "User2 is not a member of project2")
assert.Equal(t, authz.AuthzRoleAdmin.String(), assignments[0].Role, "User2 is not an admin of project2")
assert.Equal(t, authz.RoleAdmin.String(), assignments[0].Role, "User2 is not an admin of project2")
assert.Equal(t, p2ID, *assignments[0].Project, "User2 is not a member of project2")
}

Expand Down
4 changes: 2 additions & 2 deletions internal/projects/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,14 @@ func (p *projectCreator) ProvisionSelfEnrolledProject(
projectID := uuid.New()

// Create authorization tuple
if err := p.authzClient.Write(ctx, userSub, authz.AuthzRoleAdmin, projectID); err != nil {
if err := p.authzClient.Write(ctx, userSub, authz.RoleAdmin, projectID); err != nil {
return nil, fmt.Errorf("failed to create authorization tuple: %w", err)
}
defer func() {
// TODO: this can't be part of a transaction, so we should probably find a saga-ish
// way to reverse this operation if the transaction fails.
if outproj == nil && projerr != nil {
if err := p.authzClient.Delete(ctx, userSub, authz.AuthzRoleAdmin, projectID); err != nil {
if err := p.authzClient.Delete(ctx, userSub, authz.RoleAdmin, projectID); err != nil {
log.Ctx(ctx).Error().Err(err).Msg("failed to delete authorization tuple")
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/projects/deleter.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,6 @@ func (p *projectDeleter) DeleteProject(

func hasOtherRoleAssignments(as []*v1.RoleAssignment, subject string) bool {
return slices.ContainsFunc(as, func(a *v1.RoleAssignment) bool {
return a.GetRole() == authz.AuthzRoleAdmin.String() && a.Subject != subject
return a.GetRole() == authz.RoleAdmin.String() && a.Subject != subject
})
}
Loading