chore: Drop resource_id support in rbac system (#3426)

This commit is contained in:
Steven Masley
2022-08-09 18:16:53 +00:00
committed by GitHub
parent ccf6f4e7ed
commit db665e7261
17 changed files with 459 additions and 470 deletions
+59 -209
View File
@@ -3,8 +3,6 @@ package rbac_test
import (
"context"
"encoding/json"
"fmt"
"strconv"
"testing"
"github.com/google/uuid"
@@ -30,9 +28,8 @@ func TestFilter(t *testing.T) {
workspaceList := make([]rbac.Object, 0)
fileList := make([]rbac.Object, 0)
for i := 0; i < 10; i++ {
idxStr := strconv.Itoa(i)
workspace := rbac.ResourceWorkspace.WithID(idxStr).WithOwner("me")
file := rbac.ResourceFile.WithID(idxStr).WithOwner("me")
workspace := rbac.ResourceWorkspace.WithOwner("me")
file := rbac.ResourceFile.WithOwner("me")
workspaceList = append(workspaceList, workspace)
fileList = append(fileList, file)
@@ -116,7 +113,6 @@ func TestAuthorizeDomain(t *testing.T) {
t.Parallel()
defOrg := uuid.New()
unuseID := uuid.New()
wrkID := "1234"
user := subject{
UserID: "me",
@@ -127,42 +123,28 @@ func TestAuthorizeDomain(t *testing.T) {
}
testAuthorize(t, "Member", user, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
// Other org + other us
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false},
})
user = subject{
@@ -174,7 +156,6 @@ func TestAuthorizeDomain(t *testing.T) {
{
Negate: true,
ResourceType: rbac.WildcardSymbol,
ResourceID: rbac.WildcardSymbol,
Action: rbac.WildcardSymbol,
},
},
@@ -182,42 +163,28 @@ func TestAuthorizeDomain(t *testing.T) {
}
testAuthorize(t, "DeletedMember", user, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false},
})
user = subject{
@@ -229,42 +196,28 @@ func TestAuthorizeDomain(t *testing.T) {
}
testAuthorize(t, "OrgAdmin", user, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false},
})
user = subject{
@@ -276,57 +229,44 @@ func TestAuthorizeDomain(t *testing.T) {
}
testAuthorize(t, "SiteAdmin", user, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: true},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: true},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: true},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: true},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: true},
{resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: true},
})
// In practice this is a token scope on a regular subject
// In practice this is a token scope on a regular subject.
// So this unit test does not represent a practical role. It is just
// testing the capabilities of the RBAC system.
user = subject{
UserID: "me",
Roles: []rbac.Role{
{
Name: fmt.Sprintf("agent-%s", wrkID),
Name: "WorkspaceToken",
// This is at the site level to prevent the token from losing access if the user
// is kicked from the org
Site: []rbac.Permission{
{
Negate: false,
ResourceType: rbac.ResourceWorkspace.Type,
ResourceID: wrkID,
Action: rbac.ActionRead,
},
},
@@ -334,48 +274,34 @@ func TestAuthorizeDomain(t *testing.T) {
},
}
testAuthorize(t, "WorkspaceAgentToken", user,
testAuthorize(t, "WorkspaceToken", user,
// Read Actions
cases(func(c authTestCase) authTestCase {
c.actions = []rbac.Action{rbac.ActionRead}
return c
}, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: false},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: true},
{resource: rbac.ResourceWorkspace.WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.All(), allow: true},
{resource: rbac.ResourceWorkspace.All(), allow: false},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: true},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: true},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: true},
}),
// Not read actions
cases(func(c authTestCase) authTestCase {
@@ -383,42 +309,28 @@ func TestAuthorizeDomain(t *testing.T) {
c.allow = false
return c
}, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.InOrg(defOrg)},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.WithID(wrkID)},
{resource: rbac.ResourceWorkspace.All()},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID)},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)},
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id")},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id")},
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")},
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.WithID("not-id")},
}),
)
@@ -433,7 +345,6 @@ func TestAuthorizeDomain(t *testing.T) {
defOrg.String(): {{
Negate: false,
ResourceType: "*",
ResourceID: "*",
Action: rbac.ActionRead,
}},
},
@@ -441,7 +352,6 @@ func TestAuthorizeDomain(t *testing.T) {
{
Negate: false,
ResourceType: "*",
ResourceID: "*",
Action: rbac.ActionRead,
},
},
@@ -455,42 +365,28 @@ func TestAuthorizeDomain(t *testing.T) {
return c
}, []authTestCase{
// Read
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: true},
{resource: rbac.ResourceWorkspace.WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.All(), allow: false},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID), allow: false},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id"), allow: false},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unuseID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false},
}),
// Pass non-read actions
@@ -500,42 +396,28 @@ func TestAuthorizeDomain(t *testing.T) {
return c
}, []authTestCase{
// Read
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.InOrg(defOrg)},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.WithID(wrkID)},
{resource: rbac.ResourceWorkspace.All()},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID).WithID(wrkID)},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)},
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me").WithID("not-id")},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.InOrg(unuseID).WithID("not-id")},
{resource: rbac.ResourceWorkspace.InOrg(unuseID)},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")},
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.WithID("not-id")},
}))
}
@@ -544,7 +426,6 @@ func TestAuthorizeDomain(t *testing.T) {
func TestAuthorizeLevels(t *testing.T) {
defOrg := uuid.New()
unusedID := uuid.New()
wrkID := "1234"
user := subject{
UserID: "me",
@@ -557,7 +438,6 @@ func TestAuthorizeLevels(t *testing.T) {
{
Negate: true,
ResourceType: "*",
ResourceID: "*",
Action: "*",
},
},
@@ -570,7 +450,6 @@ func TestAuthorizeLevels(t *testing.T) {
{
Negate: true,
ResourceType: rbac.WildcardSymbol,
ResourceID: rbac.WildcardSymbol,
Action: rbac.WildcardSymbol,
},
},
@@ -584,42 +463,28 @@ func TestAuthorizeLevels(t *testing.T) {
c.allow = true
return c
}, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.InOrg(defOrg)},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.WithID(wrkID)},
{resource: rbac.ResourceWorkspace.All()},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID).WithID(wrkID)},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID)},
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID(wrkID)},
{resource: rbac.ResourceWorkspace.InOrg(unusedID)},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)},
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me").WithID("not-id")},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID("not-id")},
{resource: rbac.ResourceWorkspace.InOrg(unusedID)},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")},
{resource: rbac.ResourceWorkspace.WithOwner("not-me")},
{resource: rbac.ResourceWorkspace.WithID("not-id")},
}))
user = subject{
@@ -631,7 +496,6 @@ func TestAuthorizeLevels(t *testing.T) {
{
Negate: true,
ResourceType: "random",
ResourceID: rbac.WildcardSymbol,
Action: rbac.WildcardSymbol,
},
},
@@ -644,7 +508,6 @@ func TestAuthorizeLevels(t *testing.T) {
{
Negate: true,
ResourceType: rbac.WildcardSymbol,
ResourceID: rbac.WildcardSymbol,
Action: rbac.WildcardSymbol,
},
},
@@ -657,42 +520,28 @@ func TestAuthorizeLevels(t *testing.T) {
c.actions = allActions()
return c
}, []authTestCase{
// Org + me + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true},
// Org + me
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true},
{resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: false},
{resource: rbac.ResourceWorkspace.WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.All(), allow: false},
// Other org + me + id
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID).WithID(wrkID), allow: false},
// Other org + me
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unusedID), allow: false},
// Other org + other user + id
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true},
// Other org + other user
{resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
// Other org + other use + other id
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me").WithID("not-id"), allow: false},
// Other org + other use
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unusedID).WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.InOrg(unusedID), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false},
{resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false},
{resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false},
}))
}
@@ -714,6 +563,7 @@ type authTestCase struct {
}
func testAuthorize(t *testing.T, name string, subject subject, sets ...[]authTestCase) {
t.Helper()
authorizer, err := rbac.NewAuthorizer()
require.NoError(t, err)
for _, cases := range sets {
+53 -7
View File
@@ -82,7 +82,7 @@ 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
@@ -103,7 +103,6 @@ var (
{
Negate: false,
ResourceType: "*",
ResourceID: "*",
Action: "*",
},
},
@@ -123,24 +122,20 @@ var (
// All org members can read the other members in their org.
ResourceType: ResourceOrganizationMember.Type,
Action: ActionRead,
ResourceID: "*",
},
{
// All org members can read the organization
ResourceType: ResourceOrganization.Type,
Action: ActionRead,
ResourceID: "*",
},
{
// All org members can read templates in the org
ResourceType: ResourceTemplate.Type,
Action: ActionRead,
ResourceID: "*",
},
{
// Can read available roles.
ResourceType: ResourceOrgRoleAssignment.Type,
ResourceID: "*",
Action: ActionRead,
},
},
@@ -150,6 +145,58 @@ var (
}
)
var (
// assignRoles is a map of roles that can be assigned if a user has a given
// role.
// The first key is the actor role, the second is the roles they can assign.
// map[actor_role][assign_role]<can_assign>
assignRoles = map[string]map[string]bool{
admin: {
admin: true,
auditor: true,
member: true,
orgAdmin: true,
orgMember: true,
},
orgAdmin: {
orgAdmin: true,
orgMember: true,
},
}
)
// CanAssignRole is a helper function that returns true if the user can assign
// the specified role. This also can be used for removing a role.
// This is a simple implementation for now.
func CanAssignRole(roles []string, assignedRole string) bool {
assigned, assignedOrg, err := roleSplit(assignedRole)
if err != nil {
return false
}
for _, longRole := range roles {
role, orgID, err := roleSplit(longRole)
if err != nil {
continue
}
if orgID != "" && orgID != assignedOrg {
// Org roles only apply to the org they are assigned to.
continue
}
allowed, ok := assignRoles[role]
if !ok {
continue
}
if allowed[assigned] {
return true
}
}
return false
}
// RoleByName returns the permissions associated with a given role name.
// This allows just the role names to be stored and expanded when required.
func RoleByName(name string) (Role, error) {
@@ -292,7 +339,6 @@ func permissions(perms map[Object][]Action) []Permission {
list = append(list, Permission{
Negate: false,
ResourceType: k.Type,
ResourceID: WildcardSymbol,
Action: act,
})
}
+92 -11
View File
@@ -12,6 +12,87 @@ import (
"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
UserID uuid.UUID
}{
{
Name: "NoRoles",
Roles: []string{},
UserID: users[0],
},
{
Name: "Admin",
// Give some extra roles that an admin might have
Roles: []string{rbac.RoleOrgMember(orgs[0]), "auditor", rbac.RoleAdmin(), rbac.RoleMember()},
UserID: users[0],
},
{
Name: "OrgAdmin",
Roles: []string{rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgAdmin(orgs[0]), rbac.RoleMember()},
UserID: users[0],
},
{
Name: "OrgMember",
// Member of 2 orgs
Roles: []string{rbac.RoleOrgMember(orgs[0]), rbac.RoleOrgMember(orgs[1]), rbac.RoleMember()},
UserID: users[0],
},
{
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],
},
}
authorizer, err := rbac.NewAuthorizer()
if err != nil {
require.NoError(b, err)
}
for _, c := range benchCases {
b.Run(c.Name, func(b *testing.B) {
objects := benchmarkSetup(orgs, users, b.N)
b.ResetTimer()
allowed := rbac.Filter(context.Background(), authorizer, c.UserID.String(), c.Roles, rbac.ActionRead, objects)
var _ = allowed
})
}
}
func benchmarkSetup(orgs []uuid.UUID, users []uuid.UUID, size int) []rbac.Object {
// Create a "random" but deterministic set of objects.
objectList := make([]rbac.Object, size)
for i := range objectList {
objectList[i] = rbac.ResourceWorkspace.
InOrg(orgs[i%len(orgs)]).
WithOwner(users[i%len(users)].String())
}
return objectList
}
type authSubject struct {
// Name is helpful for test assertions
Name string
@@ -61,7 +142,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "MyUser",
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceUser.WithID(currentUser.String()),
Resource: rbac.ResourceUser,
AuthorizeMap: map[bool][]authSubject{
true: {admin, memberMe, orgMemberMe, orgAdmin, otherOrgMember, otherOrgAdmin},
false: {},
@@ -80,7 +161,7 @@ func TestRolePermissions(t *testing.T) {
Name: "MyWorkspaceInOrg",
// When creating the WithID won't be set, but it does not change the result.
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()).WithID(uuid.NewString()),
Resource: rbac.ResourceWorkspace.InOrg(orgID).WithOwner(currentUser.String()),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgMemberMe, orgAdmin},
false: {memberMe, otherOrgAdmin, otherOrgMember},
@@ -89,7 +170,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "Templates",
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceTemplate.InOrg(orgID).WithID(uuid.NewString()),
Resource: rbac.ResourceTemplate.InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgAdmin},
false: {memberMe, orgMemberMe, otherOrgAdmin, otherOrgMember},
@@ -98,7 +179,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "ReadTemplates",
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceTemplate.InOrg(orgID).WithID(uuid.NewString()),
Resource: rbac.ResourceTemplate.InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgMemberMe, orgAdmin},
false: {memberMe, otherOrgAdmin, otherOrgMember},
@@ -116,7 +197,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "MyFile",
Actions: []rbac.Action{rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceFile.WithID(uuid.NewString()).WithOwner(currentUser.String()),
Resource: rbac.ResourceFile.WithOwner(currentUser.String()),
AuthorizeMap: map[bool][]authSubject{
true: {admin, memberMe, orgMemberMe},
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
@@ -134,7 +215,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "Organizations",
Actions: []rbac.Action{rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceOrganization.InOrg(orgID).WithID(orgID.String()),
Resource: rbac.ResourceOrganization.InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgAdmin},
false: {otherOrgAdmin, otherOrgMember, memberMe, orgMemberMe},
@@ -143,7 +224,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "ReadOrganizations",
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceOrganization.InOrg(orgID).WithID(orgID.String()),
Resource: rbac.ResourceOrganization.InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgAdmin, orgMemberMe},
false: {otherOrgAdmin, otherOrgMember, memberMe},
@@ -188,7 +269,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "APIKey",
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceAPIKey.WithOwner(currentUser.String()).WithID(uuid.NewString()),
Resource: rbac.ResourceAPIKey.WithOwner(currentUser.String()),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgMemberMe, memberMe},
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
@@ -197,7 +278,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "UserData",
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceUserData.WithOwner(currentUser.String()).WithID(currentUser.String()),
Resource: rbac.ResourceUserData.WithOwner(currentUser.String()),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgMemberMe, memberMe},
false: {orgAdmin, otherOrgAdmin, otherOrgMember},
@@ -206,7 +287,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "ManageOrgMember",
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
Resource: rbac.ResourceOrganizationMember.InOrg(orgID).WithID(uuid.NewString()),
Resource: rbac.ResourceOrganizationMember.InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgAdmin},
false: {orgMemberMe, memberMe, otherOrgAdmin, otherOrgMember},
@@ -215,7 +296,7 @@ func TestRolePermissions(t *testing.T) {
{
Name: "ReadOrgMember",
Actions: []rbac.Action{rbac.ActionRead},
Resource: rbac.ResourceOrganizationMember.InOrg(orgID).WithID(uuid.NewString()),
Resource: rbac.ResourceOrganizationMember.InOrg(orgID),
AuthorizeMap: map[bool][]authSubject{
true: {admin, orgAdmin, orgMemberMe},
false: {memberMe, otherOrgAdmin, otherOrgMember},
-3
View File
@@ -50,9 +50,6 @@ func TestExample(t *testing.T) {
// Note 'database.Workspace' could fulfill the object interface and be passed in directly
err := authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg(defaultOrg).WithOwner(user.UserID))
require.NoError(t, err, "this user can their workspace")
err = authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg(defaultOrg).WithOwner(user.UserID).WithID("1234"))
require.NoError(t, err, "this user can read workspace '1234'")
})
}
+11 -25
View File
@@ -88,7 +88,7 @@ var (
}
// ResourceOrganizationMember is a user's membership in an organization.
// Has ONLY an organization owner. The resource ID is the user's ID
// Has ONLY an organization owner.
// create/delete = Create/delete member from org.
// update = Update organization member
// read = View member
@@ -108,8 +108,7 @@ var (
// that represents the set of workspaces you are trying to get access too.
// Do not export this type, as it can be created from a resource type constant.
type Object struct {
ResourceID string `json:"id"`
Owner string `json:"owner"`
Owner string `json:"owner"`
// OrgID specifies which org the object is a part of.
OrgID string `json:"org_owner"`
@@ -125,39 +124,26 @@ func (z Object) RBACObject() Object {
// All returns an object matching all resources of the same type.
func (z Object) All() Object {
return Object{
ResourceID: "",
Owner: "",
OrgID: "",
Type: z.Type,
Owner: "",
OrgID: "",
Type: z.Type,
}
}
// InOrg adds an org OwnerID to the resource
func (z Object) InOrg(orgID uuid.UUID) Object {
return Object{
ResourceID: z.ResourceID,
Owner: z.Owner,
OrgID: orgID.String(),
Type: z.Type,
Owner: z.Owner,
OrgID: orgID.String(),
Type: z.Type,
}
}
// WithOwner adds an OwnerID to the resource
func (z Object) WithOwner(ownerID string) Object {
return Object{
ResourceID: z.ResourceID,
Owner: ownerID,
OrgID: z.OrgID,
Type: z.Type,
}
}
// WithID adds a ResourceID to the resource
func (z Object) WithID(resourceID string) Object {
return Object{
ResourceID: resourceID,
Owner: z.Owner,
OrgID: z.OrgID,
Type: z.Type,
Owner: ownerID,
OrgID: z.OrgID,
Type: z.Type,
}
}
+3 -4
View File
@@ -22,17 +22,16 @@ bool_flip(b) = flipped {
# perms_grant returns a set of boolean values {true, false}.
# True means a positive permission in the set, false is a negative permission.
# It will only return `bool_flip(perm.negate)` for permissions that affect a given
# resource_type, resource_id, and action.
# resource_type, and action.
# The empty set is returned if no relevant permissions are found.
perms_grant(permissions) = grants {
# If there are no permissions, this value is the empty set {}.
grants := { x |
# All permissions ...
perm := permissions[_]
# Such that the permission action, type, and resource_id matches
# Such that the permission action, and type matches
perm.action in [input.action, "*"]
perm.resource_type in [input.object.type, "*"]
perm.resource_id in [input.object.id, "*"]
x := bool_flip(perm.negate)
}
}
@@ -137,4 +136,4 @@ allow {
not false in user
# And all permissions are positive
user[_]
}
}
-1
View File
@@ -5,7 +5,6 @@ type Permission struct {
// Negate makes this a negative permission
Negate bool `json:"negate"`
ResourceType string `json:"resource_type"`
ResourceID string `json:"resource_id"`
Action Action `json:"action"`
}