feat(target): Implement new WithPermissions option
Also changes the Target RepositoryFactory to allow for passing options at construction time.
This commit is contained in:
1 parent
224f8d2d4e
commit
63196ee4ca
6 files changed
+100
-16
No files matched your search
@@ -333,8 +333,8 @@ func New(ctx context.Context, conf *Config) (*Controller, error) {
|
||||
c.PasswordAuthRepoFn = func() (*password.Repository, error) {
|
||||
return password.NewRepository(dbase, dbase, c.kms)
|
||||
}
|
||||
c.TargetRepoFn = func() (*target.Repository, error) {
|
||||
return target.NewRepository(dbase, dbase, c.kms)
|
||||
c.TargetRepoFn = func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(dbase, dbase, c.kms, o...)
|
||||
}
|
||||
c.SessionRepoFn = func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, dbase, dbase, c.kms, opt...)
|
||||
|
||||
@@ -76,8 +76,8 @@ var testAuthorizedActions = []string{
|
||||
func testService(t *testing.T, ctx context.Context, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper) (targets.Service, error) {
|
||||
rw := db.New(conn)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms, o...)
|
||||
}
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iam.TestRepo(t, conn, wrapper), nil
|
||||
@@ -710,7 +710,7 @@ func TestUpdate(t *testing.T) {
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
}
|
||||
repo, err := repoFn()
|
||||
@@ -1134,7 +1134,7 @@ func TestUpdate_BadVersion(t *testing.T) {
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
}
|
||||
repo, err := repoFn()
|
||||
@@ -2739,7 +2739,7 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
}
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
@@ -2999,7 +2999,7 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
}
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
@@ -3578,7 +3578,7 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
}
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
@@ -3,6 +3,7 @@ package target
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
)
|
||||
|
||||
@@ -34,6 +35,7 @@ type options struct {
|
||||
WithStaticCredentials []*StaticCredential
|
||||
WithSessionMaxSeconds uint32
|
||||
WithSessionConnectionLimit int32
|
||||
WithPermissions []perms.Permission
|
||||
WithPublicId string
|
||||
WithWorkerFilter string
|
||||
WithTargetIds []string
|
||||
@@ -55,6 +57,7 @@ func getDefaultOptions() options {
|
||||
WithStaticCredentials: nil,
|
||||
WithSessionMaxSeconds: uint32((8 * time.Hour).Seconds()),
|
||||
WithSessionConnectionLimit: -1,
|
||||
WithPermissions: nil,
|
||||
WithPublicId: "",
|
||||
WithWorkerFilter: "",
|
||||
}
|
||||
@@ -178,3 +181,11 @@ func WithTargetIds(with []string) Option {
|
||||
o.WithTargetIds = with
|
||||
}
|
||||
}
|
||||
|
||||
// WithPermissions is used by this repo to restrict a list
|
||||
// request's results based on the given set of permissions.
|
||||
func WithPermissions(perms []perms.Permission) Option {
|
||||
return func(o *options) {
|
||||
o.WithPermissions = perms
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/credential"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/target/store"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -108,6 +109,13 @@ func Test_GetOpts(t *testing.T) {
|
||||
testOpts.WithWorkerFilter = `"/foo" == "bar"`
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
t.Run("WithPermissions", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts := GetOpts(WithPermissions([]perms.Permission{{ScopeId: "test1"}, {ScopeId: "test2"}}))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.WithPermissions = []perms.Permission{{ScopeId: "test1"}, {ScopeId: "test2"}}
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
t.Run("WithCredentialLibraries", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts := GetOpts(WithCredentialLibraries([]*CredentialLibrary{
|
||||
|
||||
@@ -11,13 +11,15 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/hashicorp/go-dbw"
|
||||
)
|
||||
|
||||
// RepositoryFactory enables `target.Repository` object instantiation,
|
||||
// and is used by the various service packages/controller object to do so.
|
||||
type RepositoryFactory func() (*Repository, error)
|
||||
type RepositoryFactory func(...Option) (*Repository, error)
|
||||
|
||||
// Cloneable provides a cloning interface
|
||||
type Cloneable interface {
|
||||
@@ -32,10 +34,19 @@ type Repository struct {
|
||||
|
||||
// defaultLimit provides a default for limiting the number of results returned from the repo
|
||||
defaultLimit int
|
||||
|
||||
// permissions provides a set of user permissions - these directly correlate to what the user
|
||||
// has access to in terms of actions and resources and we use it to build queries.
|
||||
// These are passed in on the repository constructor using `WithPermissions`, meaning the
|
||||
// `Repository` object is contextualized to whatever the request context is.
|
||||
permissions []perms.Permission
|
||||
}
|
||||
|
||||
// NewRepository creates a new target Repository. Supports the options: WithLimit
|
||||
// which sets a default limit on results returned by repo operations.
|
||||
// NewRepository creates a new target Repository.
|
||||
// Supports the following options:
|
||||
// - WithLimit: sets a limit on the number of results returned by various repo operations.
|
||||
// - WithPermissions: defines the permissions the user has to perform different
|
||||
// actions and access resources within the created repo object.
|
||||
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
const op = "target.NewRepository"
|
||||
if r == nil {
|
||||
@@ -47,16 +58,25 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repo
|
||||
if kms == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil kms")
|
||||
}
|
||||
|
||||
opts := GetOpts(opt...)
|
||||
if opts.WithLimit == 0 {
|
||||
// zero signals the boundary defaults should be used.
|
||||
opts.WithLimit = db.DefaultLimit
|
||||
}
|
||||
|
||||
for _, p := range opts.WithPermissions {
|
||||
if p.Resource != resource.Target {
|
||||
return nil, errors.New(context.Background(), errors.InvalidParameter, op, "permission for incorrect resource found")
|
||||
}
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
reader: r,
|
||||
writer: w,
|
||||
kms: kms,
|
||||
defaultLimit: opts.WithLimit,
|
||||
permissions: opts.WithPermissions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -16,9 +18,10 @@ func TestNewRepository(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
type args struct {
|
||||
r db.Reader
|
||||
w db.Writer
|
||||
kms *kms.Kms
|
||||
r db.Reader
|
||||
w db.Writer
|
||||
kms *kms.Kms
|
||||
opts []Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -75,11 +78,53 @@ func TestNewRepository(t *testing.T) {
|
||||
wantErr: true,
|
||||
wantErrString: "target.NewRepository: nil reader: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "WithPermissions sets object to `permissions`",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
kms: testKms,
|
||||
opts: []Option{
|
||||
WithPermissions([]perms.Permission{
|
||||
{ScopeId: "test1", Resource: resource.Target},
|
||||
{ScopeId: "test2", Resource: resource.Target},
|
||||
}),
|
||||
},
|
||||
},
|
||||
want: &Repository{
|
||||
reader: rw,
|
||||
writer: rw,
|
||||
kms: testKms,
|
||||
defaultLimit: db.DefaultLimit,
|
||||
permissions: []perms.Permission{
|
||||
{ScopeId: "test1", Resource: resource.Target},
|
||||
{ScopeId: "test2", Resource: resource.Target},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Don't accept permissions that aren't for the Target resource",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
kms: testKms,
|
||||
opts: []Option{
|
||||
WithPermissions([]perms.Permission{
|
||||
{ScopeId: "test1", Resource: resource.Target},
|
||||
{ScopeId: "test2", Resource: resource.Host},
|
||||
}),
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
wantErrString: "target.NewRepository: permission for incorrect resource found: parameter violation: error #100",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms)
|
||||
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Equal(tt.wantErrString, err.Error())
|
||||
|
||||
Reference in new issue
Block a user