feat(target): Implement new WithPermissions option

Also changes the Target RepositoryFactory to allow for passing options
at construction time.
This commit is contained in:
Hugo Vieira authored and Timothy Messier committed 2022-09-07 17:53:40 +00:00
1 parent 224f8d2d4e
commit 63196ee4ca
6 files changed
+100 -16

No files matched your search

+2 -2
View File
@@ -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)
+11
View File
@@ -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
}
}
+8
View File
@@ -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{
+23 -3
View File
@@ -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
}
+49 -4
View File
@@ -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())