mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add API key scopes and application_connect scope (#4067)
This commit is contained in:
+30
-10
@@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
type Authorizer interface {
|
||||
ByRoleName(ctx context.Context, subjectID string, roleNames []string, action Action, object Object) error
|
||||
PrepareByRoleName(ctx context.Context, subjectID string, roleNames []string, action Action, objectType string) (PreparedAuthorized, error)
|
||||
ByRoleName(ctx context.Context, subjectID string, roleNames []string, scope Scope, action Action, object Object) error
|
||||
PrepareByRoleName(ctx context.Context, subjectID string, roleNames []string, scope Scope, action Action, objectType string) (PreparedAuthorized, error)
|
||||
}
|
||||
|
||||
type PreparedAuthorized interface {
|
||||
@@ -24,7 +24,7 @@ type PreparedAuthorized interface {
|
||||
// Filter takes in a list of objects, and will filter the list removing all
|
||||
// the elements the subject does not have permission for. All objects must be
|
||||
// of the same type.
|
||||
func Filter[O Objecter](ctx context.Context, auth Authorizer, subjID string, subjRoles []string, action Action, objects []O) ([]O, error) {
|
||||
func Filter[O Objecter](ctx context.Context, auth Authorizer, subjID string, subjRoles []string, scope Scope, action Action, objects []O) ([]O, error) {
|
||||
ctx, span := tracing.StartSpan(ctx, trace.WithAttributes(
|
||||
attribute.String("subject_id", subjID),
|
||||
attribute.StringSlice("subject_roles", subjRoles),
|
||||
@@ -39,7 +39,7 @@ func Filter[O Objecter](ctx context.Context, auth Authorizer, subjID string, sub
|
||||
objectType := objects[0].RBACObject().Type
|
||||
|
||||
filtered := make([]O, 0)
|
||||
prepared, err := auth.PrepareByRoleName(ctx, subjID, subjRoles, action, objectType)
|
||||
prepared, err := auth.PrepareByRoleName(ctx, subjID, subjRoles, scope, action, objectType)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("prepare: %w", err)
|
||||
}
|
||||
@@ -63,6 +63,8 @@ type RegoAuthorizer struct {
|
||||
query rego.PreparedEvalQuery
|
||||
}
|
||||
|
||||
var _ Authorizer = (*RegoAuthorizer)(nil)
|
||||
|
||||
// Load the policy from policy.rego in this directory.
|
||||
//
|
||||
//go:embed policy.rego
|
||||
@@ -91,13 +93,31 @@ type authSubject struct {
|
||||
// ByRoleName will expand all roleNames into roles before calling Authorize().
|
||||
// This is the function intended to be used outside this package.
|
||||
// The role is fetched from the builtin map located in memory.
|
||||
func (a RegoAuthorizer) ByRoleName(ctx context.Context, subjectID string, roleNames []string, action Action, object Object) error {
|
||||
func (a RegoAuthorizer) ByRoleName(ctx context.Context, subjectID string, roleNames []string, scope Scope, action Action, object Object) error {
|
||||
roles, err := RolesByNames(roleNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.Authorize(ctx, subjectID, roles, action, object)
|
||||
err = a.Authorize(ctx, subjectID, roles, action, object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the scope isn't "any", we need to check with the scope's role as well.
|
||||
if scope != ScopeAll {
|
||||
scopeRole, err := ScopeRole(scope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = a.Authorize(ctx, subjectID, []Role{scopeRole}, action, object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Authorize allows passing in custom Roles.
|
||||
@@ -129,11 +149,11 @@ func (a RegoAuthorizer) Authorize(ctx context.Context, subjectID string, roles [
|
||||
|
||||
// Prepare will partially execute the rego policy leaving the object fields unknown (except for the type).
|
||||
// This will vastly speed up performance if batch authorization on the same type of objects is needed.
|
||||
func (RegoAuthorizer) Prepare(ctx context.Context, subjectID string, roles []Role, action Action, objectType string) (*PartialAuthorizer, error) {
|
||||
func (RegoAuthorizer) Prepare(ctx context.Context, subjectID string, roles []Role, scope Scope, action Action, objectType string) (*PartialAuthorizer, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
auth, err := newPartialAuthorizer(ctx, subjectID, roles, action, objectType)
|
||||
auth, err := newPartialAuthorizer(ctx, subjectID, roles, scope, action, objectType)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("new partial authorizer: %w", err)
|
||||
}
|
||||
@@ -141,7 +161,7 @@ func (RegoAuthorizer) Prepare(ctx context.Context, subjectID string, roles []Rol
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func (a RegoAuthorizer) PrepareByRoleName(ctx context.Context, subjectID string, roleNames []string, action Action, objectType string) (PreparedAuthorized, error) {
|
||||
func (a RegoAuthorizer) PrepareByRoleName(ctx context.Context, subjectID string, roleNames []string, scope Scope, action Action, objectType string) (PreparedAuthorized, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
@@ -150,5 +170,5 @@ func (a RegoAuthorizer) PrepareByRoleName(ctx context.Context, subjectID string,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.Prepare(ctx, subjectID, roles, action, objectType)
|
||||
return a.Prepare(ctx, subjectID, roles, scope, action, objectType)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rbac
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
// subject is required because rego needs
|
||||
type subject struct {
|
||||
UserID string `json:"id"`
|
||||
// For the unit test we want to pass in the roles directly, instead of just
|
||||
@@ -42,7 +42,7 @@ func TestFilterError(t *testing.T) {
|
||||
auth, err := NewAuthorizer()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = Filter(context.Background(), auth, uuid.NewString(), []string{}, ActionRead, []Object{ResourceUser, ResourceWorkspace})
|
||||
_, err = Filter(context.Background(), auth, uuid.NewString(), []string{}, ScopeAll, ActionRead, []Object{ResourceUser, ResourceWorkspace})
|
||||
require.ErrorContains(t, err, "object types must be uniform")
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ func TestFilter(t *testing.T) {
|
||||
SubjectID string
|
||||
Roles []string
|
||||
Action Action
|
||||
Scope Scope
|
||||
ObjectType string
|
||||
}{
|
||||
{
|
||||
@@ -139,6 +140,13 @@ func TestFilter(t *testing.T) {
|
||||
ObjectType: ResourceOrganization.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
{
|
||||
Name: "ScopeApplicationConnect",
|
||||
SubjectID: userIDs[0].String(),
|
||||
Roles: []string{RoleOrgMember(orgIDs[0]), "auditor", RoleOwner(), RoleMember()},
|
||||
ObjectType: ResourceWorkspace.Type,
|
||||
Action: ActionRead,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -154,11 +162,16 @@ func TestFilter(t *testing.T) {
|
||||
auth, err := NewAuthorizer()
|
||||
require.NoError(t, err, "new auth")
|
||||
|
||||
scope := ScopeAll
|
||||
if tc.Scope != "" {
|
||||
scope = tc.Scope
|
||||
}
|
||||
|
||||
// Run auth 1 by 1
|
||||
var allowedCount int
|
||||
for i, obj := range localObjects {
|
||||
obj.Type = tc.ObjectType
|
||||
err := auth.ByRoleName(ctx, tc.SubjectID, tc.Roles, ActionRead, obj.RBACObject())
|
||||
err := auth.ByRoleName(ctx, tc.SubjectID, tc.Roles, scope, ActionRead, obj.RBACObject())
|
||||
obj.Allowed = err == nil
|
||||
if err == nil {
|
||||
allowedCount++
|
||||
@@ -167,7 +180,7 @@ func TestFilter(t *testing.T) {
|
||||
}
|
||||
|
||||
// Run by filter
|
||||
list, err := Filter(ctx, auth, tc.SubjectID, tc.Roles, tc.Action, localObjects)
|
||||
list, err := Filter(ctx, auth, tc.SubjectID, tc.Roles, scope, tc.Action, localObjects)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, allowedCount, len(list), "expected number of allowed")
|
||||
for _, obj := range list {
|
||||
@@ -614,6 +627,36 @@ func TestAuthorizeLevels(t *testing.T) {
|
||||
}))
|
||||
}
|
||||
|
||||
func TestAuthorizeScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
defOrg := uuid.New()
|
||||
unusedID := uuid.New()
|
||||
user := subject{
|
||||
UserID: "me",
|
||||
Roles: []Role{},
|
||||
}
|
||||
|
||||
user.Roles = []Role{must(ScopeRole(ScopeApplicationConnect))}
|
||||
testAuthorize(t, "Admin_ScopeApplicationConnect", user, []authTestCase{
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.All(), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner(user.UserID), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID).WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.InOrg(unusedID), actions: allActions(), allow: false},
|
||||
{resource: ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false},
|
||||
|
||||
// Allowed by scope:
|
||||
{resource: ResourceWorkspaceApplicationConnect.InOrg(defOrg).WithOwner("not-me"), actions: []Action{ActionCreate}, allow: true},
|
||||
{resource: ResourceWorkspaceApplicationConnect.InOrg(defOrg).WithOwner(user.UserID), actions: []Action{ActionCreate}, allow: true},
|
||||
})
|
||||
}
|
||||
|
||||
// cases applies a given function to all test cases. This makes generalities easier to create.
|
||||
func cases(opt func(c authTestCase) authTestCase, cases []authTestCase) []authTestCase {
|
||||
if opt == nil {
|
||||
@@ -636,13 +679,20 @@ func testAuthorize(t *testing.T, name string, subject subject, sets ...[]authTes
|
||||
authorizer, err := NewAuthorizer()
|
||||
require.NoError(t, err)
|
||||
for _, cases := range sets {
|
||||
for _, c := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
for i, c := range cases {
|
||||
c := c
|
||||
if c.resource.Type != "application_connect" {
|
||||
continue
|
||||
}
|
||||
caseName := fmt.Sprintf("%s/%d", name, i)
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, a := range c.actions {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
authError := authorizer.Authorize(ctx, subject.UserID, subject.Roles, a, c.resource)
|
||||
|
||||
// Logging only
|
||||
if authError != nil {
|
||||
var uerr *UnauthorizedError
|
||||
@@ -666,23 +716,35 @@ func testAuthorize(t *testing.T, name string, subject subject, sets ...[]authTes
|
||||
assert.Error(t, authError, "expected unauthorized")
|
||||
}
|
||||
|
||||
partialAuthz, err := authorizer.Prepare(ctx, subject.UserID, subject.Roles, a, c.resource.Type)
|
||||
partialAuthz, err := authorizer.Prepare(ctx, subject.UserID, subject.Roles, ScopeAll, a, c.resource.Type)
|
||||
require.NoError(t, err, "make prepared authorizer")
|
||||
|
||||
// Also check the rego policy can form a valid partial query result.
|
||||
// This ensures we can convert the queries into SQL WHERE clauses in the future.
|
||||
// If this function returns 'Support' sections, then we cannot convert the query into SQL.
|
||||
if len(partialAuthz.partialQueries.Support) > 0 {
|
||||
d, _ := json.Marshal(partialAuthz.input)
|
||||
if len(partialAuthz.mainAuthorizer.partialQueries.Support) > 0 {
|
||||
d, _ := json.Marshal(partialAuthz.mainAuthorizer.input)
|
||||
t.Logf("input: %s", string(d))
|
||||
for _, q := range partialAuthz.partialQueries.Queries {
|
||||
for _, q := range partialAuthz.mainAuthorizer.partialQueries.Queries {
|
||||
t.Logf("query: %+v", q.String())
|
||||
}
|
||||
for _, s := range partialAuthz.partialQueries.Support {
|
||||
for _, s := range partialAuthz.mainAuthorizer.partialQueries.Support {
|
||||
t.Logf("support: %+v", s.String())
|
||||
}
|
||||
}
|
||||
require.Equal(t, 0, len(partialAuthz.partialQueries.Support), "expected 0 support rules")
|
||||
if partialAuthz.scopeAuthorizer != nil {
|
||||
if len(partialAuthz.scopeAuthorizer.partialQueries.Support) > 0 {
|
||||
d, _ := json.Marshal(partialAuthz.scopeAuthorizer.input)
|
||||
t.Logf("scope input: %s", string(d))
|
||||
for _, q := range partialAuthz.scopeAuthorizer.partialQueries.Queries {
|
||||
t.Logf("scope query: %+v", q.String())
|
||||
}
|
||||
for _, s := range partialAuthz.scopeAuthorizer.partialQueries.Support {
|
||||
t.Logf("scope support: %+v", s.String())
|
||||
}
|
||||
}
|
||||
require.Equal(t, 0, len(partialAuthz.scopeAuthorizer.partialQueries.Support), "expected 0 support rules in scope authorizer")
|
||||
}
|
||||
|
||||
partialErr := partialAuthz.Authorize(ctx, c.resource)
|
||||
if authError != nil {
|
||||
|
||||
@@ -33,28 +33,33 @@ func BenchmarkRBACFilter(b *testing.B) {
|
||||
Name string
|
||||
Roles []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",
|
||||
@@ -66,6 +71,14 @@ func BenchmarkRBACFilter(b *testing.B) {
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -77,7 +90,7 @@ func BenchmarkRBACFilter(b *testing.B) {
|
||||
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, rbac.ActionRead, objects)
|
||||
allowed, err := rbac.Filter(context.Background(), authorizer, c.UserID.String(), c.Roles, c.Scope, rbac.ActionRead, objects)
|
||||
require.NoError(b, err)
|
||||
var _ = allowed
|
||||
})
|
||||
@@ -184,6 +197,16 @@ func TestRolePermissions(t *testing.T) {
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "MyWorkspaceInOrgAppConnect",
|
||||
// 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.ResourceWorkspaceApplicationConnect.InOrg(orgID).WithOwner(currentUser.String()),
|
||||
AuthorizeMap: map[bool][]authSubject{
|
||||
true: {owner, orgAdmin, orgMemberMe},
|
||||
false: {memberMe, otherOrgAdmin, otherOrgMember, templateAdmin, userAdmin},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Templates",
|
||||
Actions: []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete},
|
||||
@@ -335,7 +358,8 @@ func TestRolePermissions(t *testing.T) {
|
||||
for _, subj := range subjs {
|
||||
delete(remainingSubjs, subj.Name)
|
||||
msg := fmt.Sprintf("%s as %q doing %q on %q", c.Name, subj.Name, action, c.Resource.Type)
|
||||
err := auth.ByRoleName(context.Background(), subj.UserID, subj.Roles, action, c.Resource)
|
||||
// TODO: scopey
|
||||
err := auth.ByRoleName(context.Background(), subj.UserID, subj.Roles, rbac.ScopeAll, action, c.Resource)
|
||||
if result {
|
||||
assert.NoError(t, err, fmt.Sprintf("Should pass: %s", msg))
|
||||
} else {
|
||||
|
||||
@@ -31,6 +31,15 @@ var (
|
||||
Type: "workspace_execution",
|
||||
}
|
||||
|
||||
// ResourceWorkspaceApplicationConnect CRUD. Org + User owner
|
||||
// create = connect to an application
|
||||
// read = ?
|
||||
// update = ?
|
||||
// delete = ?
|
||||
ResourceWorkspaceApplicationConnect = Object{
|
||||
Type: "application_connect",
|
||||
}
|
||||
|
||||
// ResourceAuditLog
|
||||
// read = access audit log
|
||||
ResourceAuditLog = Object{
|
||||
|
||||
+56
-3
@@ -11,6 +11,59 @@ import (
|
||||
)
|
||||
|
||||
type PartialAuthorizer struct {
|
||||
// mainAuthorizer is used for the user's roles. It is always not-nil.
|
||||
mainAuthorizer *subPartialAuthorizer
|
||||
// scopeAuthorizer is used for the API key scope. It may be nil.
|
||||
scopeAuthorizer *subPartialAuthorizer
|
||||
}
|
||||
|
||||
var _ PreparedAuthorized = (*PartialAuthorizer)(nil)
|
||||
|
||||
func (pa *PartialAuthorizer) Authorize(ctx context.Context, object Object) error {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
err := pa.mainAuthorizer.Authorize(ctx, object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pa.scopeAuthorizer != nil {
|
||||
return pa.scopeAuthorizer.Authorize(ctx, object)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPartialAuthorizer(ctx context.Context, subjectID string, roles []Role, scope Scope, action Action, objectType string) (*PartialAuthorizer, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
pAuth, err := newSubPartialAuthorizer(ctx, subjectID, roles, action, objectType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var scopeAuth *subPartialAuthorizer
|
||||
if scope != ScopeAll {
|
||||
scopeRole, err := ScopeRole(scope)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unknown scope %q", scope)
|
||||
}
|
||||
|
||||
scopeAuth, err = newSubPartialAuthorizer(ctx, subjectID, []Role{scopeRole}, action, objectType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &PartialAuthorizer{
|
||||
mainAuthorizer: pAuth,
|
||||
scopeAuthorizer: scopeAuth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type subPartialAuthorizer struct {
|
||||
// partialQueries is mainly used for unit testing to assert our rego policy
|
||||
// can always be compressed into a set of queries.
|
||||
partialQueries *rego.PartialQueries
|
||||
@@ -25,7 +78,7 @@ type PartialAuthorizer struct {
|
||||
alwaysTrue bool
|
||||
}
|
||||
|
||||
func newPartialAuthorizer(ctx context.Context, subjectID string, roles []Role, action Action, objectType string) (*PartialAuthorizer, error) {
|
||||
func newSubPartialAuthorizer(ctx context.Context, subjectID string, roles []Role, action Action, objectType string) (*subPartialAuthorizer, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
@@ -55,7 +108,7 @@ func newPartialAuthorizer(ctx context.Context, subjectID string, roles []Role, a
|
||||
return nil, xerrors.Errorf("prepare: %w", err)
|
||||
}
|
||||
|
||||
pAuth := &PartialAuthorizer{
|
||||
pAuth := &subPartialAuthorizer{
|
||||
partialQueries: partialQueries,
|
||||
preparedQueries: []rego.PreparedEvalQuery{},
|
||||
input: input,
|
||||
@@ -87,7 +140,7 @@ func newPartialAuthorizer(ctx context.Context, subjectID string, roles []Role, a
|
||||
}
|
||||
|
||||
// Authorize authorizes a single object using the partially prepared queries.
|
||||
func (a PartialAuthorizer) Authorize(ctx context.Context, object Object) error {
|
||||
func (a subPartialAuthorizer) Authorize(ctx context.Context, object Object) error {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Scope string
|
||||
|
||||
const (
|
||||
ScopeAll Scope = "all"
|
||||
ScopeApplicationConnect Scope = "application_connect"
|
||||
)
|
||||
|
||||
var builtinScopes map[Scope]Role = map[Scope]Role{
|
||||
// ScopeAll is a special scope that allows access to all resources. During
|
||||
// authorize checks it is usually not used directly and skips scope checks.
|
||||
ScopeAll: {
|
||||
Name: fmt.Sprintf("Scope_%s", ScopeAll),
|
||||
DisplayName: "All operations",
|
||||
Site: permissions(map[Object][]Action{
|
||||
ResourceWildcard: {WildcardSymbol},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
},
|
||||
|
||||
ScopeApplicationConnect: {
|
||||
Name: fmt.Sprintf("Scope_%s", ScopeApplicationConnect),
|
||||
DisplayName: "Ability to connect to applications",
|
||||
Site: permissions(map[Object][]Action{
|
||||
ResourceWorkspaceApplicationConnect: {ActionCreate},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: []Permission{},
|
||||
},
|
||||
}
|
||||
|
||||
func ScopeRole(scope Scope) (Role, error) {
|
||||
role, ok := builtinScopes[scope]
|
||||
if !ok {
|
||||
return Role{}, xerrors.Errorf("no scope named %q", scope)
|
||||
}
|
||||
return role, nil
|
||||
}
|
||||
Reference in New Issue
Block a user