chore: More complete tracing for RBAC functions (#5690)

* chore: More complete tracing for RBAC functions
* Add input.json as example rbac input for rego cli

The input.json is required to play with the rego cli and debug
the policy without golang. It is good to have an example to run
the commands in the readme.md

* Add span events to capture authorize and prepared results
* chore: Add prometheus metrics to rbac authorizer
This commit is contained in:
Steven Masley
2023-01-13 16:07:15 -06:00
committed by GitHub
parent e821b98918
commit eb48341696
12 changed files with 425 additions and 147 deletions

View File

@ -6,113 +6,13 @@ import (
"testing"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/rbac"
)
// BenchmarkRBACFilter benchmarks the rbac.Filter method.
//
// go test -bench BenchmarkRBACFilter -benchmem -memprofile memprofile.out -cpuprofile profile.out
func BenchmarkRBACFilter(b *testing.B) {
orgs := []uuid.UUID{
uuid.MustParse("bf7b72bd-a2b1-4ef2-962c-1d698e0483f6"),
uuid.MustParse("e4660c6f-b9de-422d-9578-cd888983a795"),
uuid.MustParse("fb13d477-06f4-42d9-b957-f6b89bd63515"),
}
users := []uuid.UUID{
uuid.MustParse("10d03e62-7703-4df5-a358-4f76577d4e2f"),
uuid.MustParse("4ca78b1d-f2d2-4168-9d76-cd93b51c6c1e"),
uuid.MustParse("0632b012-49e0-4d70-a5b3-f4398f1dcd52"),
uuid.MustParse("70dbaa7a-ea9c-4f68-a781-97b08af8461d"),
}
benchCases := []struct {
Name string
Roles []string
Groups []string
UserID uuid.UUID
Scope rbac.Scope
}{
{
Name: "NoRoles",
Roles: []string{},
UserID: users[0],
Scope: rbac.ScopeAll,
},
{
Name: "Admin",
// Give some extra roles that an admin might have
Roles: []string{rbac.RoleOrgMember(orgs[0]), "auditor", rbac.RoleOwner(), rbac.RoleMember()},
UserID: users[0],
Scope: rbac.ScopeAll,
},
{
Name: "OrgAdmin",
Roles: []string{rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgAdmin(orgs[0]), rbac.RoleMember()},
UserID: users[0],
Scope: rbac.ScopeAll,
},
{
Name: "OrgMember",
// Member of 2 orgs
Roles: []string{rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgMember(orgs[1]), rbac.RoleMember()},
UserID: users[0],
Scope: rbac.ScopeAll,
},
{
Name: "ManyRoles",
// Admin of many orgs
Roles: []string{
rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgAdmin(orgs[0]),
rbac.RoleOrgMember(orgs[1]), rbac.RoleOrgAdmin(orgs[1]),
rbac.RoleOrgMember(orgs[2]), rbac.RoleOrgAdmin(orgs[2]),
rbac.RoleMember(),
},
UserID: users[0],
Scope: rbac.ScopeAll,
},
{
Name: "AdminWithScope",
// Give some extra roles that an admin might have
Roles: []string{rbac.RoleOrgMember(orgs[0]), "auditor", rbac.RoleOwner(), rbac.RoleMember()},
UserID: users[0],
Scope: rbac.ScopeApplicationConnect,
},
}
authorizer := rbac.NewAuthorizer()
for _, c := range benchCases {
b.Run(c.Name, func(b *testing.B) {
objects := benchmarkSetup(orgs, users, b.N)
b.ResetTimer()
allowed, err := rbac.Filter(context.Background(), authorizer, c.UserID.String(), c.Roles, c.Scope, c.Groups, rbac.ActionRead, objects)
require.NoError(b, err)
var _ = allowed
})
}
}
func benchmarkSetup(orgs []uuid.UUID, users []uuid.UUID, size int) []rbac.Object {
// Create a "random" but deterministic set of objects.
aclList := map[string][]rbac.Action{
uuid.NewString(): {rbac.ActionRead, rbac.ActionUpdate},
uuid.NewString(): {rbac.ActionCreate},
}
objectList := make([]rbac.Object, size)
for i := range objectList {
objectList[i] = rbac.ResourceWorkspace.
InOrg(orgs[i%len(orgs)]).
WithOwner(users[i%len(users)].String()).
WithACLUserList(aclList).
WithGroupACL(aclList)
}
return objectList
}
type authSubject struct {
// Name is helpful for test assertions
Name string
@ -124,7 +24,7 @@ type authSubject struct {
func TestRolePermissions(t *testing.T) {
t.Parallel()
auth := rbac.NewAuthorizer()
auth := rbac.NewAuthorizer(prometheus.NewRegistry())
// currentUser is anything that references "me", "mine", or "my".
currentUser := uuid.New()