Merge branch 'llb-list-performance' pull request #2342 from hashicorp/llb-list-performance
Improve response time for list requests for targets and sessions. This
refactors the behavior between the target and session services and how
they interact with the domain layer. Now the service is able to evaluate
the set of permissions from the requesting user's grants, use these
permissions when constructing the corresponding repository, and retrieve
the list of the resource the user is authorized to list in a single
query.
This reduces the round trips to the database and greatly reduces the
amount of data received from the database.
This impacts commands (in particular when `-recursive` is used):
boundary targets list
boundary sessions list
And the corresponding API endpoints of:
GET /v1/targets
GET /v1/sessions
There is no change to the data returned.
See:
68568828e6
bf263fbd7d
This commit is contained in:
46 files changed
+1835
-925
No files matched your search
@@ -478,7 +478,7 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error)
|
||||
}
|
||||
}()
|
||||
|
||||
targetRepo, err := target.NewRepository(rw, rw, kmsCache)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating target repository: %w", err)
|
||||
}
|
||||
|
||||
@@ -640,7 +640,7 @@ group by session_id, status;
|
||||
cc[id].active = credentialCount
|
||||
}
|
||||
|
||||
sessionRepo, err := session.NewRepository(rw, rw, kms)
|
||||
sessionRepo, err := session.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
|
||||
count, err := sessionRepo.TerminateCompletedSessions(ctx)
|
||||
|
||||
@@ -29,7 +29,7 @@ type workerServiceServer struct {
|
||||
pbs.UnsafeSessionServiceServer
|
||||
|
||||
serversRepoFn common.ServersRepoFactory
|
||||
sessionRepoFn common.SessionRepoFactory
|
||||
sessionRepoFn session.RepositoryFactory
|
||||
connectionRepoFn common.ConnectionRepoFactory
|
||||
updateTimes *sync.Map
|
||||
kms *kms.Kms
|
||||
@@ -42,7 +42,7 @@ var (
|
||||
|
||||
func NewWorkerServiceServer(
|
||||
serversRepoFn common.ServersRepoFactory,
|
||||
sessionRepoFn common.SessionRepoFactory,
|
||||
sessionRepoFn session.RepositoryFactory,
|
||||
connectionRepoFn common.ConnectionRepoFactory,
|
||||
updateTimes *sync.Map,
|
||||
kms *kms.Kms,
|
||||
|
||||
@@ -47,8 +47,8 @@ func TestStatus(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return serverRepo, nil
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
connRepoFn := func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, rw, rw, kms)
|
||||
@@ -233,8 +233,8 @@ func TestStatusSessionClosed(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return serverRepo, nil
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
connRepoFn := func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, rw, rw, kms)
|
||||
@@ -414,8 +414,8 @@ func TestStatusDeadConnection(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return serverRepo, nil
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
connRepoFn := func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, rw, rw, kms, session.WithWorkerStateDelay(0))
|
||||
@@ -554,8 +554,8 @@ func TestStatusWorkerWithKeyId(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return serverRepo, nil
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
connRepoFn := func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, rw, rw, kms)
|
||||
|
||||
@@ -39,8 +39,8 @@ func TestLookupSession(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
connectionRepoFn := func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, rw, rw, kms)
|
||||
@@ -214,8 +214,8 @@ func TestHcpbWorkers(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kmsCache)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kmsCache)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kmsCache, opts...)
|
||||
}
|
||||
connectionRepoFn := func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, rw, rw, kmsCache)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/tokens"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
@@ -640,6 +641,15 @@ func (r *VerifyResults) FetchOutputFields(res perms.Resource, act action.Type) p
|
||||
return r.v.acl.Allowed(res, act, r.UserId).OutputFields
|
||||
}
|
||||
|
||||
// ACL returns the perms.ACL of the verifier.
|
||||
func (r *VerifyResults) ACL() perms.ACL {
|
||||
if r.v == nil {
|
||||
return perms.ACL{}
|
||||
}
|
||||
|
||||
return r.v.acl
|
||||
}
|
||||
|
||||
// GetTokenFromRequest pulls the token from either the Authorization header or
|
||||
// split cookies and parses it. If it cannot be parsed successfully, the issue
|
||||
// is logged and we return blank, so logic will continue as the anonymous user.
|
||||
@@ -694,3 +704,123 @@ func GetTokenFromRequest(ctx context.Context, kmsCache *kms.Kms, req *http.Reque
|
||||
|
||||
return publicId, encryptedToken, uint32(receivedTokenType)
|
||||
}
|
||||
|
||||
// ScopesAuthorizedForList retrieves and returns all scopes where a user is authorized
|
||||
// to perform a *list* action on. It looks recursively from `rootScopeId`.
|
||||
func (r *VerifyResults) ScopesAuthorizedForList(ctx context.Context, rootScopeId string, resourceType resource.Type) (map[string]*scopes.ScopeInfo, error) {
|
||||
const op = "auth.(VerifyResults).ScopesAuthorizedForList"
|
||||
|
||||
// Validation
|
||||
switch {
|
||||
case resourceType == resource.Unknown:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "unknown resource")
|
||||
case r.v.iamRepoFn == nil:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil iam repo")
|
||||
case rootScopeId == "":
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing root scope id")
|
||||
case r.Scope == nil:
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil scope in auth results")
|
||||
}
|
||||
|
||||
repo, err := r.v.iamRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all scopes recursively. Start at global because we need to take into
|
||||
// account permissions in parent scopes even if they want to scale back the
|
||||
// returned information to a child scope and its children.
|
||||
scps, err := repo.ListScopesRecursively(ctx, scope.Global.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var deferredScopes []*iam.Scope
|
||||
var globalHasList bool // Store whether global has list permission
|
||||
scopeResourceMap := make(map[string]*scopes.ScopeInfo) // Scope data per scope id
|
||||
|
||||
// For each scope, see if we have permission to list that resource in that scope
|
||||
for _, scp := range scps {
|
||||
scpId := scp.GetPublicId()
|
||||
aSet := r.FetchActionSetForType(ctx,
|
||||
resource.Unknown, // This is overridden by `WithResource` option.
|
||||
action.ActionSet{action.List},
|
||||
WithResource(&perms.Resource{Type: resourceType, ScopeId: scpId}),
|
||||
)
|
||||
|
||||
// We only expect the action set to be nothing, or list. In case
|
||||
// this is not the case, we bail out.
|
||||
switch len(aSet) {
|
||||
case 0:
|
||||
// Defer until we've read all scopes. We do this because if the
|
||||
// ordering coming back isn't in parent-first ordering our map
|
||||
// lookup might fail.
|
||||
deferredScopes = append(deferredScopes, scp)
|
||||
case 1:
|
||||
if aSet[0] != action.List {
|
||||
return nil, errors.New(ctx, errors.Internal, op, "unexpected action in set")
|
||||
}
|
||||
if scopeResourceMap[scpId] == nil {
|
||||
scopeResourceMap[scpId] = &scopes.ScopeInfo{
|
||||
Id: scp.GetPublicId(),
|
||||
Type: scp.GetType(),
|
||||
Name: scp.GetName(),
|
||||
Description: scp.GetDescription(),
|
||||
ParentScopeId: scp.GetParentId(),
|
||||
}
|
||||
}
|
||||
if scpId == scope.Global.String() {
|
||||
globalHasList = true
|
||||
}
|
||||
default:
|
||||
return nil, errors.New(ctx, errors.Internal, op, "unexpected number of actions back in set")
|
||||
}
|
||||
}
|
||||
|
||||
// Now go through the deferred scopes and see if a parent matches
|
||||
for _, scp := range deferredScopes {
|
||||
// If they had list on global scope anything else is automatically
|
||||
// included; otherwise if they had list on the parent scope, this
|
||||
// scope is included in the map and is sufficient here.
|
||||
if globalHasList || scopeResourceMap[scp.GetParentId()] != nil {
|
||||
scpId := scp.GetPublicId()
|
||||
if scopeResourceMap[scpId] == nil {
|
||||
scopeResourceMap[scpId] = &scopes.ScopeInfo{
|
||||
Id: scp.GetPublicId(),
|
||||
Type: scp.GetType(),
|
||||
Name: scp.GetName(),
|
||||
Description: scp.GetDescription(),
|
||||
ParentScopeId: scp.GetParentId(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elide out any that aren't under the root scope id
|
||||
elideScopes := make([]string, 0, len(scopeResourceMap))
|
||||
for scpId, scp := range scopeResourceMap {
|
||||
switch rootScopeId {
|
||||
// If the root is global, it matches
|
||||
case scope.Global.String():
|
||||
// If the current scope matches the root, it matches
|
||||
case scpId:
|
||||
// Or if the parent of this scope is the root (for orgs that would mean
|
||||
// a root scope ID which is covered in the case above, so this is really
|
||||
// projects matching an org used as the root)
|
||||
case scp.ParentScopeId:
|
||||
default:
|
||||
elideScopes = append(elideScopes, scpId)
|
||||
}
|
||||
}
|
||||
for _, scpId := range elideScopes {
|
||||
delete(scopeResourceMap, scpId)
|
||||
}
|
||||
|
||||
// If we have nothing in scopeInfoMap at this point, we aren't authorized
|
||||
// anywhere so return 403.
|
||||
if len(scopeResourceMap) == 0 {
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
|
||||
return scopeResourceMap, nil
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
hostplugin "github.com/hashicorp/boundary/internal/plugin/host"
|
||||
"github.com/hashicorp/boundary/internal/server"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -25,8 +24,6 @@ type (
|
||||
StaticRepoFactory func() (*static.Repository, error)
|
||||
PluginHostRepoFactory func() (*pluginhost.Repository, error)
|
||||
HostPluginRepoFactory func() (*hostplugin.Repository, error)
|
||||
SessionRepoFactory func() (*session.Repository, error)
|
||||
ConnectionRepoFactory func() (*session.ConnectionRepository, error)
|
||||
TargetRepoFactory func() (*target.Repository, error)
|
||||
WorkerAuthRepoStorageFactory func() (*server.WorkerAuthRepositoryStorage, error)
|
||||
)
|
||||
@@ -44,8 +44,9 @@ func TestListingScopeIds(t *testing.T) {
|
||||
s, err := groups.NewService(iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
sessionsRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
sessionsRepoFn := func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opt...)
|
||||
}
|
||||
sess, err := sessions.NewService(sessionsRepoFn, iamRepoFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -110,12 +110,12 @@ type Controller struct {
|
||||
OidcRepoFn common.OidcAuthRepoFactory
|
||||
PasswordAuthRepoFn common.PasswordAuthRepoFactory
|
||||
ServersRepoFn common.ServersRepoFactory
|
||||
SessionRepoFn common.SessionRepoFactory
|
||||
SessionRepoFn session.RepositoryFactory
|
||||
ConnectionRepoFn common.ConnectionRepoFactory
|
||||
StaticHostRepoFn common.StaticRepoFactory
|
||||
PluginHostRepoFn common.PluginHostRepoFactory
|
||||
HostPluginRepoFn common.HostPluginRepoFactory
|
||||
TargetRepoFn common.TargetRepoFactory
|
||||
TargetRepoFn target.RepositoryFactory
|
||||
WorkerAuthRepoStorageFn common.WorkerAuthRepoStorageFactory
|
||||
|
||||
scheduler *scheduler.Scheduler
|
||||
@@ -333,11 +333,11 @@ 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(ctx, dbase, dbase, c.kms, o...)
|
||||
}
|
||||
c.SessionRepoFn = func() (*session.Repository, error) {
|
||||
return session.NewRepository(dbase, dbase, c.kms)
|
||||
c.SessionRepoFn = func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, dbase, dbase, c.kms, opt...)
|
||||
}
|
||||
c.ConnectionRepoFn = func() (*session.ConnectionRepository, error) {
|
||||
return session.NewConnectionRepository(ctx, dbase, dbase, c.kms)
|
||||
|
||||
@@ -84,9 +84,6 @@ func BenchmarkSessionList(b *testing.B) {
|
||||
iamRepo, err := iam.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
sessRepo, err := session.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
authTokenRepo, err := authtoken.NewRepository(rw, rw, kmsThing)
|
||||
require.NoError(b, err)
|
||||
|
||||
@@ -99,8 +96,8 @@ func BenchmarkSessionList(b *testing.B) {
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sessRepoFn := func() (*session.Repository, error) {
|
||||
return sessRepo, nil
|
||||
sessRepoFn := func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kmsThing, opt...)
|
||||
}
|
||||
authTokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authTokenRepo, nil
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/sessions"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
@@ -45,14 +45,14 @@ var (
|
||||
type Service struct {
|
||||
pbs.UnsafeSessionServiceServer
|
||||
|
||||
repoFn common.SessionRepoFactory
|
||||
repoFn session.RepositoryFactory
|
||||
iamRepoFn common.IamRepoFactory
|
||||
}
|
||||
|
||||
var _ pbs.SessionServiceServer = (*Service)(nil)
|
||||
|
||||
// NewService returns a session service which handles session related requests to boundary.
|
||||
func NewService(repoFn common.SessionRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) {
|
||||
func NewService(repoFn session.RepositoryFactory, iamRepoFn common.IamRepoFactory) (Service, error) {
|
||||
const op = "sessions.NewService"
|
||||
if repoFn == nil {
|
||||
return Service{}, errors.NewDeprecated(errors.InvalidParameter, op, "missing session repository")
|
||||
@@ -139,34 +139,26 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
}
|
||||
}
|
||||
|
||||
repo, err := s.repoFn()
|
||||
var scopeIds map[string]*scopes.ScopeInfo
|
||||
var err error
|
||||
|
||||
if !req.GetRecursive() {
|
||||
scopeIds = map[string]*scopes.ScopeInfo{authResults.Scope.Id: authResults.Scope}
|
||||
} else {
|
||||
scopeIds, err = authResults.ScopesAuthorizedForList(ctx, req.GetScopeId(), resource.Session)
|
||||
}
|
||||
|
||||
listPerms := authResults.ACL().ListPermissions(scopeIds, resource.Session, IdActions)
|
||||
|
||||
repo, err := s.repoFn(session.WithPermissions(&perms.UserPermissions{
|
||||
UserId: authResults.UserId,
|
||||
Permissions: listPerms,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
scopeResourceInfo, err := scopeids.GetListingResourceInformation(
|
||||
ctx,
|
||||
scopeids.GetListingResourceInformationInput{
|
||||
IamRepoFn: s.iamRepoFn,
|
||||
AuthResults: authResults,
|
||||
RootScopeId: req.GetScopeId(),
|
||||
Type: resource.Session,
|
||||
Recursive: req.GetRecursive(),
|
||||
AuthzProtectedEntityProvider: session.ListForAuthzCheck(repo, session.WithTerminated(req.IncludeTerminated)),
|
||||
ActionSet: IdActions,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If no scopes match or we match scopes but there are no resources in them
|
||||
// that we are authorized to see, return an empty response
|
||||
if len(scopeResourceInfo.ScopeIds) == 0 ||
|
||||
len(scopeResourceInfo.ResourceIds) == 0 {
|
||||
return &pbs.ListSessionsResponse{}, nil
|
||||
}
|
||||
|
||||
sesList, err := s.listFromRepo(ctx, scopeResourceInfo.ResourceIds)
|
||||
sesList, err := repo.ListSessions(ctx, session.WithTerminated(req.GetIncludeTerminated()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -183,14 +175,21 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
Type: resource.Session,
|
||||
}
|
||||
for _, item := range sesList {
|
||||
res.Id = item.GetPublicId()
|
||||
res.ScopeId = item.GetProjectId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeResourceInfo.ScopeResourceMap[item.ProjectId].ScopeInfo))
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeIds[item.ProjectId]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(scopeResourceInfo.ScopeResourceMap[item.ProjectId].Resources[item.PublicId].AuthorizedActions.Strings()))
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, outputOpts...)
|
||||
@@ -295,18 +294,6 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*session.Session,
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, sessionIds []string) ([]*session.Session, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sesList, err := repo.ListSessions(ctx, session.WithSessionIds(sessionIds...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sesList, nil
|
||||
}
|
||||
|
||||
func (s Service) cancelInRepo(ctx context.Context, id string, version uint32) (*session.Session, error) {
|
||||
const op = "sessions.(Service).cancelInRepo"
|
||||
repo, err := s.repoFn()
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/server"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
@@ -32,7 +33,7 @@ import (
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
var testAuthorizedActions = []string{"no-op", "read", "read:self", "cancel", "cancel:self"}
|
||||
var testAuthorizedActions = []string{"read:self", "cancel:self"}
|
||||
|
||||
func TestGetSession(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
@@ -42,14 +43,19 @@ func TestGetSession(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
|
||||
rw := db.New(conn)
|
||||
sessRepo, err := session.NewRepository(rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sessRepoFn := func() (*session.Repository, error) {
|
||||
return sessRepo, nil
|
||||
sessRepoFn := func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opt...)
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
o, p := iam.TestScopes(t, iamRepo)
|
||||
@@ -106,7 +112,7 @@ func TestGetSession(t *testing.T) {
|
||||
res: &pbs.GetSessionResponse{Item: wireSess},
|
||||
},
|
||||
{
|
||||
name: "Get a non existant Session",
|
||||
name: "Get a non existent Session",
|
||||
req: &pbs.GetSessionRequest{Id: session.SessionPrefix + "_DoesntExis"},
|
||||
res: nil,
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
@@ -131,7 +137,15 @@ func TestGetSession(t *testing.T) {
|
||||
s, err := sessions.NewService(sessRepoFn, iamRepoFn)
|
||||
require.NoError(err, "Couldn't create new session service.")
|
||||
|
||||
got, gErr := s.GetSession(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
|
||||
got, gErr := s.GetSession(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "GetSession(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -140,7 +154,7 @@ func TestGetSession(t *testing.T) {
|
||||
assert.True(got.GetItem().GetExpirationTime().AsTime().Sub(tc.res.GetItem().GetExpirationTime().AsTime()) < 10*time.Millisecond)
|
||||
tc.res.GetItem().ExpirationTime = got.GetItem().GetExpirationTime()
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "GetSession(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res)
|
||||
assert.Empty(cmp.Diff(tc.res, got, protocmp.Transform()), "GetSession(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -153,11 +167,12 @@ func TestList_Self(t *testing.T) {
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sessRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessRepoFn := func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opt...)
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
@@ -237,14 +252,20 @@ func TestList(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
|
||||
rw := db.New(conn)
|
||||
sessRepo, err := session.NewRepository(rw, rw, kms)
|
||||
sessRepo, err := session.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sessRepoFn := func() (*session.Repository, error) {
|
||||
return sessRepo, nil
|
||||
sessRepoFn := func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opt...)
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, pNoSessions := iam.TestScopes(t, iamRepo)
|
||||
@@ -270,7 +291,7 @@ func TestList(t *testing.T) {
|
||||
tarOther := tcp.TestTarget(ctx, t, conn, pWithOtherSessions.GetPublicId(), "test", target.WithHostSources([]string{hsOther.GetPublicId()}))
|
||||
|
||||
var wantSession []*pb.Session
|
||||
var totalSession []*pb.Session
|
||||
var wantOtherSession []*pb.Session
|
||||
var wantIncludeTerminatedSessions []*pb.Session
|
||||
for i := 0; i < 10; i++ {
|
||||
sess := session.TestSession(t, conn, wrap, session.ComposedOf{
|
||||
@@ -309,7 +330,6 @@ func TestList(t *testing.T) {
|
||||
Connections: []*pb.Connection{}, // connections should not be returned for list
|
||||
})
|
||||
|
||||
totalSession = append(totalSession, wantSession[i])
|
||||
wantIncludeTerminatedSessions = append(wantIncludeTerminatedSessions, wantSession[i])
|
||||
|
||||
sess = session.TestSession(t, conn, wrap, session.ComposedOf{
|
||||
@@ -326,11 +346,11 @@ func TestList(t *testing.T) {
|
||||
|
||||
status, states = convertStates(sess.States)
|
||||
|
||||
totalSession = append(totalSession, &pb.Session{
|
||||
wantOtherSession = append(wantOtherSession, &pb.Session{
|
||||
Id: sess.GetPublicId(),
|
||||
ScopeId: pWithOtherSessions.GetPublicId(),
|
||||
AuthTokenId: atOther.GetPublicId(),
|
||||
UserId: atOther.GetIamUserId(),
|
||||
ScopeId: pWithSessions.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
UserId: at.GetIamUserId(),
|
||||
TargetId: sess.TargetId,
|
||||
Endpoint: sess.Endpoint,
|
||||
HostSetId: sess.HostSetId,
|
||||
@@ -339,7 +359,7 @@ func TestList(t *testing.T) {
|
||||
UpdatedTime: sess.UpdateTime.GetTimestamp(),
|
||||
CreatedTime: sess.CreateTime.GetTimestamp(),
|
||||
ExpirationTime: sess.ExpirationTime.GetTimestamp(),
|
||||
Scope: &scopes.ScopeInfo{Id: pWithOtherSessions.GetPublicId(), Type: scope.Project.String(), ParentScopeId: oOther.GetPublicId()},
|
||||
Scope: &scopes.ScopeInfo{Id: pWithSessions.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()},
|
||||
Status: status,
|
||||
States: states,
|
||||
Certificate: sess.Certificate,
|
||||
@@ -397,35 +417,41 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListSessionsRequest
|
||||
res *pbs.ListSessionsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListSessionsRequest
|
||||
res *pbs.ListSessionsResponse
|
||||
otherRes *pbs.ListSessionsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Many Sessions",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId()},
|
||||
res: &pbs.ListSessionsResponse{Items: wantSession},
|
||||
name: "List Many Sessions",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId()},
|
||||
res: &pbs.ListSessionsResponse{Items: wantSession},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
{
|
||||
name: "List Many Include Terminated",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), IncludeTerminated: true},
|
||||
res: &pbs.ListSessionsResponse{Items: wantIncludeTerminatedSessions},
|
||||
name: "List Many Include Terminated",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), IncludeTerminated: true},
|
||||
res: &pbs.ListSessionsResponse{Items: wantIncludeTerminatedSessions},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
{
|
||||
name: "List No Sessions",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pNoSessions.GetPublicId()},
|
||||
res: &pbs.ListSessionsResponse{},
|
||||
name: "List No Sessions",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pNoSessions.GetPublicId()},
|
||||
res: &pbs.ListSessionsResponse{},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
{
|
||||
name: "List Sessions Recursively",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: scope.Global.String(), Recursive: true},
|
||||
res: &pbs.ListSessionsResponse{Items: totalSession},
|
||||
name: "List Sessions Recursively",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: scope.Global.String(), Recursive: true},
|
||||
res: &pbs.ListSessionsResponse{Items: wantSession},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: wantOtherSession},
|
||||
},
|
||||
{
|
||||
name: "Filter To Single Sessions",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, totalSession[4].Id)},
|
||||
res: &pbs.ListSessionsResponse{Items: totalSession[4:5]},
|
||||
name: "Filter To Single Sessions",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), Filter: fmt.Sprintf(`"/item/id"==%q`, wantSession[4].Id)},
|
||||
res: &pbs.ListSessionsResponse{Items: wantSession[4:5]},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
{
|
||||
name: "Filter To Many Sessions",
|
||||
@@ -433,17 +459,20 @@ func TestList(t *testing.T) {
|
||||
ScopeId: scope.Global.String(), Recursive: true,
|
||||
Filter: fmt.Sprintf(`"/item/scope/id" matches "^%s"`, pWithSessions.GetPublicId()[:8]),
|
||||
},
|
||||
res: &pbs.ListSessionsResponse{Items: wantSession},
|
||||
res: &pbs.ListSessionsResponse{Items: wantSession},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
{
|
||||
name: "Filter To Nothing",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), Filter: `"/item/id" == ""`},
|
||||
res: &pbs.ListSessionsResponse{},
|
||||
name: "Filter To Nothing",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), Filter: `"/item/id" == ""`},
|
||||
res: &pbs.ListSessionsResponse{},
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
{
|
||||
name: "Filter Bad Format",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), Filter: `//badformat/`},
|
||||
err: handlers.InvalidArgumentErrorf("bad format", nil),
|
||||
name: "Filter Bad Format",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId(), Filter: `//badformat/`},
|
||||
err: handlers.InvalidArgumentErrorf("bad format", nil),
|
||||
otherRes: &pbs.ListSessionsResponse{Items: []*pb.Session{}},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -453,7 +482,14 @@ func TestList(t *testing.T) {
|
||||
require.NoError(err, "Couldn't create new session service.")
|
||||
|
||||
// Test without anon user
|
||||
got, gErr := s.ListSessions(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, gErr := s.ListSessions(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListSessions(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -470,22 +506,21 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListSessions(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Test with anon user
|
||||
got, gErr = s.ListSessions(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
// Test with other user
|
||||
otherRequestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: atOther.GetPublicId(),
|
||||
Token: atOther.GetToken(),
|
||||
}
|
||||
otherRequestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
otherCtx := auth.NewVerifierContext(otherRequestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &otherRequestInfo)
|
||||
got, gErr = s.ListSessions(otherCtx, tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Empty(item.Version)
|
||||
require.Empty(item.UserId)
|
||||
require.Empty(item.HostId)
|
||||
require.Empty(item.HostSetId)
|
||||
require.Empty(item.AuthTokenId)
|
||||
require.Empty(item.Endpoint)
|
||||
require.Nil(item.CreatedTime)
|
||||
require.Nil(item.ExpirationTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Empty(item.Certificate)
|
||||
require.Empty(item.TerminationReason)
|
||||
assert.Len(got.Items, len(tc.otherRes.Items))
|
||||
for i, wantSess := range tc.otherRes.GetItems() {
|
||||
assert.True(got.GetItems()[i].GetExpirationTime().AsTime().Sub(wantSess.GetExpirationTime().AsTime()) < 10*time.Millisecond)
|
||||
assert.Equal(0, len(wantSess.GetConnections())) // no connections on list
|
||||
wantSess.ExpirationTime = got.GetItems()[i].GetExpirationTime()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -516,14 +551,19 @@ func TestCancel(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrap)
|
||||
|
||||
rw := db.New(conn)
|
||||
sessRepo, err := session.NewRepository(rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
sessRepoFn := func() (*session.Repository, error) {
|
||||
return sessRepo, nil
|
||||
sessRepoFn := func(opt ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opt...)
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
o, p := iam.TestScopes(t, iamRepo)
|
||||
@@ -613,16 +653,23 @@ func TestCancel(t *testing.T) {
|
||||
|
||||
tc.req.Version = version
|
||||
|
||||
got, gErr := s.CancelSession(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, gErr := s.CancelSession(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
// It's hard to mix and match api/error package errors right now
|
||||
// so use old/new behavior depending on the type. If validate
|
||||
// gets updated this can be standardized.
|
||||
if errors.Match(errors.T(errors.InvalidSessionState), gErr) {
|
||||
assert.True(errors.Match(errors.T(tc.err), gErr), "GetSession(%+v) got error %#v, wanted %#v", tc.req, gErr, tc.err)
|
||||
assert.True(errors.Match(errors.T(tc.err), gErr), "CancelSession(%+v) got error %#v, wanted %#v", tc.req, gErr, tc.err)
|
||||
} else {
|
||||
assert.True(errors.Is(gErr, tc.err), "GetSession(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
assert.True(errors.Is(gErr, tc.err), "CancelSession(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,6 +677,7 @@ func TestCancel(t *testing.T) {
|
||||
require.Nil(got)
|
||||
return
|
||||
}
|
||||
require.NotNil(got)
|
||||
tc.res.GetItem().Version = got.GetItem().Version
|
||||
|
||||
// Compare the new canceling state and then remove it for the rest of the proto comparison
|
||||
@@ -647,7 +695,7 @@ func TestCancel(t *testing.T) {
|
||||
EndTime: got.GetItem().GetUpdatedTime(),
|
||||
},
|
||||
}
|
||||
assert.Empty(cmp.Diff(got.GetItem().GetStates(), wantState, protocmp.Transform()), "GetSession(%q) states")
|
||||
assert.Empty(cmp.Diff(got.GetItem().GetStates(), wantState, protocmp.Transform()), "CancelSession(%q) states")
|
||||
got.GetItem().States = nil
|
||||
got.GetItem().UpdatedTime = nil
|
||||
|
||||
@@ -656,7 +704,7 @@ func TestCancel(t *testing.T) {
|
||||
tc.res.GetItem().ExpirationTime = got.GetItem().GetExpirationTime()
|
||||
}
|
||||
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "GetSession(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "CancelSession(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res)
|
||||
|
||||
if tc.req != nil {
|
||||
require.NotNil(got)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/credential/vault"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
@@ -33,6 +32,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/targets"
|
||||
"github.com/hashicorp/go-bexpr"
|
||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||
@@ -98,10 +98,10 @@ var (
|
||||
type Service struct {
|
||||
pbs.UnsafeTargetServiceServer
|
||||
|
||||
repoFn common.TargetRepoFactory
|
||||
repoFn target.RepositoryFactory
|
||||
iamRepoFn common.IamRepoFactory
|
||||
serversRepoFn common.ServersRepoFactory
|
||||
sessionRepoFn common.SessionRepoFactory
|
||||
sessionRepoFn session.RepositoryFactory
|
||||
pluginHostRepoFn common.PluginHostRepoFactory
|
||||
staticHostRepoFn common.StaticRepoFactory
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory
|
||||
@@ -115,10 +115,10 @@ var _ pbs.TargetServiceServer = (*Service)(nil)
|
||||
func NewService(
|
||||
ctx context.Context,
|
||||
kmsCache *kms.Kms,
|
||||
repoFn common.TargetRepoFactory,
|
||||
repoFn target.RepositoryFactory,
|
||||
iamRepoFn common.IamRepoFactory,
|
||||
serversRepoFn common.ServersRepoFactory,
|
||||
sessionRepoFn common.SessionRepoFactory,
|
||||
sessionRepoFn session.RepositoryFactory,
|
||||
pluginHostRepoFn common.PluginHostRepoFactory,
|
||||
staticHostRepoFn common.StaticRepoFactory,
|
||||
vaultCredRepoFn common.VaultCredentialRepoFactory,
|
||||
@@ -183,34 +183,24 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
}
|
||||
}
|
||||
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
var err error
|
||||
var authzScopes map[string]*scopes.ScopeInfo
|
||||
if req.GetRecursive() {
|
||||
authzScopes, err = authResults.ScopesAuthorizedForList(ctx, req.GetScopeId(), resource.Target)
|
||||
} else {
|
||||
authzScopes = map[string]*scopes.ScopeInfo{authResults.Scope.Id: authResults.Scope}
|
||||
}
|
||||
|
||||
scopeResourceInfo, err := scopeids.GetListingResourceInformation(
|
||||
ctx,
|
||||
scopeids.GetListingResourceInformationInput{
|
||||
IamRepoFn: s.iamRepoFn,
|
||||
AuthResults: authResults,
|
||||
RootScopeId: req.GetScopeId(),
|
||||
Type: resource.Target,
|
||||
Recursive: req.GetRecursive(),
|
||||
AuthzProtectedEntityProvider: repo,
|
||||
ActionSet: IdActions,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If no scopes match, return an empty response
|
||||
if len(scopeResourceInfo.ScopeIds) == 0 ||
|
||||
len(scopeResourceInfo.ResourceIds) == 0 {
|
||||
// Get all user permissions for the requested scope(s).
|
||||
userPerms := authResults.ACL().ListPermissions(authzScopes, resource.Target, IdActions)
|
||||
if len(userPerms) == 0 {
|
||||
return &pbs.ListTargetsResponse{}, nil
|
||||
}
|
||||
|
||||
tl, err := s.listFromRepo(ctx, scopeResourceInfo.ResourceIds)
|
||||
tl, err := s.listFromRepo(ctx, userPerms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,19 +212,21 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
finalItems := make([]*pb.Target, 0, len(tl))
|
||||
res := perms.Resource{
|
||||
Type: resource.Target,
|
||||
}
|
||||
for _, item := range tl {
|
||||
outputFields := authResults.FetchOutputFields(res, action.List).SelfOrDefaults(authResults.UserId)
|
||||
pr := perms.Resource{Id: item.GetPublicId(), ScopeId: item.GetProjectId(), Type: resource.Target}
|
||||
outputFields := authResults.FetchOutputFields(pr, action.List).SelfOrDefaults(authResults.UserId)
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(scopeResourceInfo.ScopeResourceMap[item.GetProjectId()].ScopeInfo))
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authzScopes[item.GetProjectId()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(scopeResourceInfo.ScopeResourceMap[item.GetProjectId()].Resources[item.GetPublicId()].AuthorizedActions.Strings()))
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&pr)).Strings()
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, nil, outputOpts...)
|
||||
@@ -250,6 +242,7 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
return &pbs.ListTargetsResponse{Items: finalItems}, nil
|
||||
}
|
||||
|
||||
@@ -1233,12 +1226,12 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, targetIds []string) ([]target.Target, error) {
|
||||
repo, err := s.repoFn()
|
||||
func (s Service) listFromRepo(ctx context.Context, perms []perms.Permission) ([]target.Target, error) {
|
||||
repo, err := s.repoFn(target.WithPermissions(perms))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListTargets(ctx, target.WithTargetIds(targetIds))
|
||||
ul, err := repo.ListTargets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -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(ctx, rw, rw, kms, o...)
|
||||
}
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iam.TestRepo(t, conn, wrapper), nil
|
||||
@@ -85,8 +85,8 @@ func testService(t *testing.T, ctx context.Context, conn *db.DB, kms *kms.Kms, w
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
@@ -109,12 +109,24 @@ func TestGet(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
o, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, o.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
||||
@@ -179,7 +191,14 @@ func TestGet(t *testing.T) {
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(err, "Couldn't create a new host set service.")
|
||||
|
||||
got, gErr := s.GetTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, gErr := s.GetTarget(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "GetTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -198,14 +217,34 @@ func TestList(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, projNoTar := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
ar := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, ar.GetPublicId(), auth.AnonymousUserId)
|
||||
_ = iam.TestRoleGrant(t, conn, ar.GetPublicId(), "id=*;type=target;actions=*")
|
||||
|
||||
otherOrg, otherProj := iam.TestScopes(t, iamRepo)
|
||||
r = iam.TestRole(t, conn, otherProj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0]
|
||||
otherHc := static.TestCatalogs(t, conn, otherProj.GetPublicId(), 1)[0]
|
||||
hss := static.TestSets(t, conn, hc.GetPublicId(), 2)
|
||||
@@ -292,8 +331,14 @@ func TestList(t *testing.T) {
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test with non-anon user
|
||||
got, gErr := s.ListTargets(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, gErr := s.ListTargets(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListTargets(%q) got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
@@ -312,17 +357,20 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test with anon user
|
||||
got, gErr = s.ListTargets(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Empty(item.Version)
|
||||
require.Nil(item.CreatedTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Nil(item.SessionMaxSeconds)
|
||||
require.Nil(item.SessionConnectionLimit)
|
||||
require.Empty(item.WorkerFilter)
|
||||
require.Nil(item.Attrs)
|
||||
requestInfo = authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeUnknown),
|
||||
}
|
||||
requestContext = context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr = s.ListTargets(ctx, tc.req)
|
||||
require.Error(gErr)
|
||||
|
||||
// For now, due to how recursive checks the additional scopes,
|
||||
// it gets a 403 while a non-recursive expects a 401
|
||||
if tc.req.GetRecursive() {
|
||||
assert.ErrorIs(gErr, handlers.ForbiddenError())
|
||||
} else {
|
||||
assert.ErrorIs(gErr, handlers.UnauthenticatedError())
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -334,13 +382,26 @@ func TestDelete(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
@@ -380,7 +441,14 @@ func TestDelete(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
got, gErr := s.DeleteTarget(auth.DisabledAuthTestContext(iamRepoFn, tc.scopeId), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, gErr := s.DeleteTarget(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "DeleteTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -397,13 +465,25 @@ func TestDelete_twice(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
tar := tcp.TestTarget(ctx, t, conn, proj.GetPublicId(), "test")
|
||||
|
||||
@@ -412,7 +492,13 @@ func TestDelete_twice(t *testing.T) {
|
||||
req := &pbs.DeleteTargetRequest{
|
||||
Id: tar.GetPublicId(),
|
||||
}
|
||||
ctx = auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId())
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.DeleteTarget(ctx, req)
|
||||
assert.NoError(gErr, "First attempt")
|
||||
_, gErr = s.DeleteTarget(ctx, req)
|
||||
@@ -426,12 +512,24 @@ func TestCreate(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -555,7 +653,15 @@ func TestCreate(t *testing.T) {
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(err, "Failed to create a new host set service.")
|
||||
|
||||
got, gErr := s.CreateTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
|
||||
got, gErr := s.CreateTarget(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "CreateTarget(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -586,16 +692,27 @@ func TestUpdate(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
rw := db.New(conn)
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
repo, err := repoFn()
|
||||
require.NoError(t, err, "Couldn't create new target repo.")
|
||||
@@ -613,7 +730,6 @@ func TestUpdate(t *testing.T) {
|
||||
{Id: hs[1].GetPublicId(), HostCatalogId: hs[1].GetCatalogId()},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ttar, err := target.New(ctx, tcp.Subtype, proj.GetPublicId(),
|
||||
target.WithName("default"),
|
||||
target.WithDescription("default"),
|
||||
@@ -960,7 +1076,14 @@ func TestUpdate(t *testing.T) {
|
||||
req := proto.Clone(toMerge).(*pbs.UpdateTargetRequest)
|
||||
proto.Merge(req, tc.req)
|
||||
|
||||
got, gErr := tested.UpdateTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, gErr := tested.UpdateTarget(ctx, req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "UpdateTarget(%+v) got error %v, wanted %v", req, gErr, tc.err)
|
||||
@@ -992,21 +1115,32 @@ func TestUpdate_BadVersion(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
rw := db.New(conn)
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
repo, err := repoFn()
|
||||
require.NoError(t, err, "Couldn't create new target repo.")
|
||||
|
||||
ctx := context.Background()
|
||||
ttar, err := target.New(ctx, tcp.Subtype, proj.GetPublicId(), target.WithName("default"), target.WithDescription("default"))
|
||||
tar := ttar.(*tcp.Target)
|
||||
tar.DefaultPort = 2
|
||||
@@ -1017,7 +1151,14 @@ func TestUpdate_BadVersion(t *testing.T) {
|
||||
tested, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Failed to create a new host set service.")
|
||||
|
||||
upTar, err := tested.UpdateTarget(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), &pbs.UpdateTargetRequest{
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
upTar, err := tested.UpdateTarget(ctx, &pbs.UpdateTargetRequest{
|
||||
Id: gtar.GetPublicId(),
|
||||
Item: &pb.Target{
|
||||
Description: wrapperspb.String("updated"),
|
||||
@@ -1036,12 +1177,24 @@ func TestAddTargetHostSets(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new target service.")
|
||||
@@ -1102,7 +1255,14 @@ func TestAddTargetHostSets(t *testing.T) {
|
||||
HostSetIds: tc.addHostSets,
|
||||
}
|
||||
|
||||
got, err := s.AddTargetHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.AddTargetHostSets(ctx, req)
|
||||
s, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.NoError(t, err, "Got error: %v", s)
|
||||
@@ -1157,7 +1317,14 @@ func TestAddTargetHostSets(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.AddTargetHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.AddTargetHostSets(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "AddTargetHostSets(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -1172,12 +1339,24 @@ func TestSetTargetHostSets(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
@@ -1243,7 +1422,14 @@ func TestSetTargetHostSets(t *testing.T) {
|
||||
HostSetIds: tc.setHostSets,
|
||||
}
|
||||
|
||||
got, err := s.SetTargetHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.SetTargetHostSets(ctx, req)
|
||||
require.NoError(t, err, "Got error: %v", s)
|
||||
assert.ElementsMatch(t, tc.resultHostSets, got.GetItem().GetHostSetIds())
|
||||
})
|
||||
@@ -1287,7 +1473,14 @@ func TestSetTargetHostSets(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.SetTargetHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.SetTargetHostSets(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "SetTargetHostSets(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -1302,12 +1495,24 @@ func TestRemoveTargetHostSets(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
@@ -1369,7 +1574,14 @@ func TestRemoveTargetHostSets(t *testing.T) {
|
||||
HostSetIds: tc.removeHosts,
|
||||
}
|
||||
|
||||
got, err := s.RemoveTargetHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.RemoveTargetHostSets(ctx, req)
|
||||
if tc.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
@@ -1429,7 +1641,14 @@ func TestRemoveTargetHostSets(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.RemoveTargetHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.RemoveTargetHostSets(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "RemoveTargetHostSets(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -1444,12 +1663,24 @@ func TestAddTargetHostSources(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new target service.")
|
||||
@@ -1510,7 +1741,14 @@ func TestAddTargetHostSources(t *testing.T) {
|
||||
HostSourceIds: tc.addHostSources,
|
||||
}
|
||||
|
||||
got, err := s.AddTargetHostSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.AddTargetHostSources(ctx, req)
|
||||
s, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
require.NoError(t, err, "Got error: %v", s)
|
||||
@@ -1565,7 +1803,14 @@ func TestAddTargetHostSources(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.AddTargetHostSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.AddTargetHostSources(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "AddTargetHostSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -1580,12 +1825,24 @@ func TestSetTargetHostSources(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
@@ -1645,7 +1902,14 @@ func TestSetTargetHostSources(t *testing.T) {
|
||||
HostSourceIds: tc.setHostSources,
|
||||
}
|
||||
|
||||
got, err := s.SetTargetHostSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.SetTargetHostSources(ctx, req)
|
||||
require.NoError(t, err, "Got error: %v", s)
|
||||
assert.ElementsMatch(t, tc.resultHostSources, got.GetItem().GetHostSourceIds())
|
||||
})
|
||||
@@ -1689,7 +1953,14 @@ func TestSetTargetHostSources(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.SetTargetHostSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.SetTargetHostSources(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "SetTargetHostSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -1704,12 +1975,24 @@ func TestRemoveTargetHostSources(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
sche := scheduler.TestScheduler(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new host set service.")
|
||||
@@ -1771,7 +2054,14 @@ func TestRemoveTargetHostSources(t *testing.T) {
|
||||
HostSourceIds: tc.removeHostSources,
|
||||
}
|
||||
|
||||
got, err := s.RemoveTargetHostSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.RemoveTargetHostSources(ctx, req)
|
||||
if tc.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
@@ -1831,7 +2121,14 @@ func TestRemoveTargetHostSources(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.RemoveTargetHostSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx = auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.RemoveTargetHostSources(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "RemoveTargetHostSets(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -1845,12 +2142,24 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new target service.")
|
||||
@@ -1926,7 +2235,14 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
BrokeredCredentialSourceIds: tc.addSources,
|
||||
}
|
||||
|
||||
got, err := s.AddTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.AddTargetCredentialSources(ctx, req)
|
||||
require.NoError(t, err, "Got error: %v", s)
|
||||
|
||||
assert.ElementsMatch(t, tc.resultSourceIds, got.GetItem().GetBrokeredCredentialSourceIds())
|
||||
@@ -1987,7 +2303,14 @@ func TestAddTargetCredentialSources(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.AddTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.AddTargetCredentialSources(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "AddTargetCredentialSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -2001,12 +2324,24 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new target service.")
|
||||
@@ -2089,7 +2424,14 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
BrokeredCredentialSourceIds: tc.setCredentialSources,
|
||||
}
|
||||
|
||||
got, err := s.SetTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.SetTargetCredentialSources(ctx, req)
|
||||
require.NoError(t, err, "Got error: %v", s)
|
||||
assert.ElementsMatch(t, tc.resultCredentialSourceIds, got.GetItem().GetBrokeredCredentialSourceIds())
|
||||
assert.Equal(t, len(tc.resultCredentialSourceIds), len(got.GetItem().GetBrokeredCredentialSources()))
|
||||
@@ -2137,7 +2479,14 @@ func TestSetTargetCredentialSources(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.SetTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.SetTargetCredentialSources(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "SetTargetCredentialSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -2151,12 +2500,24 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
rw := db.New(conn)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
return iamRepo, nil
|
||||
}
|
||||
tokenRepoFn := func() (*authtoken.Repository, error) {
|
||||
return authtoken.NewRepository(rw, rw, kms)
|
||||
}
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
org, proj := iam.TestScopes(t, iamRepo)
|
||||
at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId())
|
||||
r := iam.TestRole(t, conn, proj.GetPublicId())
|
||||
_ = iam.TestUserRole(t, conn, r.GetPublicId(), at.GetIamUserId())
|
||||
_ = iam.TestRoleGrant(t, conn, r.GetPublicId(), "id=*;type=*;actions=*")
|
||||
|
||||
s, err := testService(t, context.Background(), conn, kms, wrapper)
|
||||
require.NoError(t, err, "Error when getting new target service.")
|
||||
@@ -2276,7 +2637,14 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
BrokeredCredentialSourceIds: tc.removeCredentialSources,
|
||||
}
|
||||
|
||||
got, err := s.RemoveTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
got, err := s.RemoveTargetCredentialSources(ctx, req)
|
||||
if tc.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
@@ -2344,7 +2712,14 @@ func TestRemoveTargetCredentialSources(t *testing.T) {
|
||||
for _, tc := range failCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
_, gErr := s.RemoveTargetCredentialSources(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
requestInfo := authpb.RequestInfo{
|
||||
TokenFormat: uint32(auth.AuthTokenTypeBearer),
|
||||
PublicId: at.GetPublicId(),
|
||||
Token: at.GetToken(),
|
||||
}
|
||||
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
ctx := auth.NewVerifierContext(requestContext, iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo)
|
||||
_, gErr := s.RemoveTargetCredentialSources(ctx, tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "RemoveTargetCredentialSources(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
@@ -2364,8 +2739,8 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
@@ -2374,8 +2749,8 @@ func TestAuthorizeSession(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
staticRepo, err := static.NewRepository(rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
@@ -2624,8 +2999,8 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
@@ -2634,8 +3009,8 @@ func TestAuthorizeSessionTypedCredentials(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
@@ -3203,8 +3578,8 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
err := vault.RegisterJobs(context.Background(), sche, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
repoFn := func() (*target.Repository, error) {
|
||||
return target.NewRepository(rw, rw, kms)
|
||||
repoFn := func(o ...target.Option) (*target.Repository, error) {
|
||||
return target.NewRepository(ctx, rw, rw, kms)
|
||||
}
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
iamRepoFn := func() (*iam.Repository, error) {
|
||||
@@ -3213,8 +3588,8 @@ func TestAuthorizeSession_Errors(t *testing.T) {
|
||||
serversRepoFn := func() (*server.Repository, error) {
|
||||
return server.NewRepository(rw, rw, kms)
|
||||
}
|
||||
sessionRepoFn := func() (*session.Repository, error) {
|
||||
return session.NewRepository(rw, rw, kms)
|
||||
sessionRepoFn := func(opts ...session.Option) (*session.Repository, error) {
|
||||
return session.NewRepository(ctx, rw, rw, kms, opts...)
|
||||
}
|
||||
staticHostRepoFn := func() (*static.Repository, error) {
|
||||
return static.NewRepository(rw, rw, kms)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
begin;
|
||||
-- Partial index to aid session list requests
|
||||
--
|
||||
-- If a session list request is made using the default list request options
|
||||
-- and using the standard grants created by boundary by default,
|
||||
-- it will include where clauses that:
|
||||
-- * include a project_id paired with a user_id
|
||||
-- * and where termination_reason is null
|
||||
create index session_list_pix on session (project_id, user_id, termination_reason) where termination_reason is null;
|
||||
analyze session;
|
||||
end;
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
)
|
||||
|
||||
const AnonymousUserId = "u_anon"
|
||||
@@ -27,6 +28,24 @@ type ACLResults struct {
|
||||
scopeMap map[string][]Grant
|
||||
}
|
||||
|
||||
// Permission provides information about the specific
|
||||
// resources that a user has been granted access to for a given scope, resource, and action.
|
||||
type Permission struct {
|
||||
ScopeId string // The scope id for which the permission applies.
|
||||
Resource resource.Type
|
||||
Action action.Type
|
||||
|
||||
ResourceIds []string // Any specific resource ids that have been referred in the grant's `id` field, if applicable.
|
||||
OnlySelf bool // The grant only allows actions against the user's own resources.
|
||||
All bool // We got a wildcard in the grant string's `id` field.
|
||||
}
|
||||
|
||||
// UserPermissions is a set of Permissions for a User.
|
||||
type UserPermissions struct {
|
||||
UserId string
|
||||
Permissions []Permission
|
||||
}
|
||||
|
||||
// Resource defines something within boundary that requires authorization
|
||||
// capabilities. Resources must have a ScopeId.
|
||||
type Resource struct {
|
||||
@@ -209,6 +228,73 @@ func (a ACL) Allowed(r Resource, aType action.Type, userId string, opt ...Option
|
||||
return
|
||||
}
|
||||
|
||||
// ListPermissions builds a set of Permissions based on the grants in the ACL.
|
||||
// Permissions are determined for the given resource for each of the provided scopes.
|
||||
// There must be a grant for a given resource for one of the provided "id actions"
|
||||
// or for action.All in order for a Permission to be created for the scope.
|
||||
// The set of "id actions" is resource dependant, but will generally include all
|
||||
// actions that can be taken on an individual resource.
|
||||
func (a ACL) ListPermissions(requestedScopes map[string]*scopes.ScopeInfo, requestedType resource.Type, idActions action.ActionSet) []Permission {
|
||||
perms := make([]Permission, 0, len(requestedScopes))
|
||||
for scopeId := range requestedScopes {
|
||||
p := Permission{
|
||||
ScopeId: scopeId,
|
||||
Resource: requestedType,
|
||||
Action: action.List,
|
||||
}
|
||||
|
||||
// Get grants for a specific scope id from the source of truth.
|
||||
grants := a.scopeMap[scopeId]
|
||||
for _, grant := range grants {
|
||||
// This grant doesn't match what we're looking for, ignore.
|
||||
if grant.typ != requestedType && grant.typ != resource.All {
|
||||
continue
|
||||
}
|
||||
|
||||
// We found a grant that matches the requested resource type:
|
||||
// Search to see if one or all actions in the action set have been granted.
|
||||
found := false
|
||||
if ok := grant.actions[action.All]; ok {
|
||||
found = true
|
||||
} else {
|
||||
for _, a := range idActions {
|
||||
if ok := grant.actions[a]; ok {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found { // In this case, none of the requested actions were granted for the given scope id.
|
||||
continue
|
||||
}
|
||||
|
||||
actions, _ := grant.Actions()
|
||||
excludeList := make(action.ActionSet, 0, len(actions))
|
||||
for _, aa := range actions {
|
||||
if aa != action.List {
|
||||
excludeList = append(excludeList, aa)
|
||||
}
|
||||
}
|
||||
p.OnlySelf = excludeList.OnlySelf()
|
||||
|
||||
switch grant.id {
|
||||
case "*":
|
||||
p.All = true
|
||||
case "":
|
||||
continue
|
||||
default:
|
||||
p.ResourceIds = append(p.ResourceIds, grant.id)
|
||||
}
|
||||
}
|
||||
|
||||
if p.All || len(p.ResourceIds) > 0 {
|
||||
perms = append(perms, p)
|
||||
}
|
||||
}
|
||||
|
||||
return perms
|
||||
}
|
||||
|
||||
func topLevelType(typ resource.Type) bool {
|
||||
switch typ {
|
||||
case resource.AuthMethod,
|
||||
|
||||
+372
-4
@@ -9,17 +9,19 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type scopeGrant struct {
|
||||
scope string
|
||||
grants []string
|
||||
}
|
||||
|
||||
func Test_ACLAllowed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type scopeGrant struct {
|
||||
scope string
|
||||
grants []string
|
||||
}
|
||||
type actionAuthorized struct {
|
||||
action action.Type
|
||||
authorized bool
|
||||
@@ -372,6 +374,372 @@ func Test_ACLAllowed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_ListPermissions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
aclGrants []scopeGrant
|
||||
scopes map[string]*scopes.ScopeInfo // *scopes.ScopeInfo isn't used at the moment.
|
||||
resourceType resource.Type
|
||||
actionSet action.ActionSet
|
||||
expPermissions []Permission
|
||||
}{
|
||||
{
|
||||
name: "Requested scope(s) not present in ACL scope map",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=session;actions=list,read"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{
|
||||
"o_non_existent_scope": nil,
|
||||
"o_this_one_too": nil,
|
||||
},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{},
|
||||
},
|
||||
{
|
||||
name: "Requested resource mismatch",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=target;actions=list,read"}, // List & Read for all Targets
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session, // We're requesting sessions.
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{},
|
||||
},
|
||||
{
|
||||
name: "Requested actions not available for the requested scope id",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=session;actions=delete"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{},
|
||||
},
|
||||
{
|
||||
name: "No specific id or wildcard provided for `id` field",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"type=*;actions=list,read"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{},
|
||||
},
|
||||
{
|
||||
name: "Allow all ids",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=session;actions=list,read"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: false,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Allow all ids, :self actions",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=session;actions=list,read:self"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: true,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Allow specific IDs",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{
|
||||
"id=s_1;type=session;actions=list,read",
|
||||
"id=s_2;type=session;actions=list,read",
|
||||
"id=s_3;type=session;actions=list,read",
|
||||
},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"s_1", "s_2", "s_3"},
|
||||
OnlySelf: false,
|
||||
All: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "No specific type 1",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=*;actions=list,read:self"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: true,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "List + No-op action with id wildcard",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=session;actions=list,no-op"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: false,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "List + No-op action with id wildcard, read requested",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=session;actions=list,no-op"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.Read},
|
||||
expPermissions: []Permission{},
|
||||
},
|
||||
{
|
||||
name: "List + No-op action with specific ids",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{
|
||||
"id=s_1;type=session;actions=list,no-op",
|
||||
"id=s_2;type=session;actions=list,no-op",
|
||||
"id=s_3;type=session;actions=list,no-op",
|
||||
},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"s_1", "s_2", "s_3"},
|
||||
OnlySelf: false,
|
||||
All: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "No specific type 2",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=*;actions=list,read:self"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Host,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Host,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: true,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Grant hierarchy is respected",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{
|
||||
"id=*;type=session;actions=read:self",
|
||||
"id=*;type=*;actions=list,read",
|
||||
},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Target,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Target,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: false,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Full access 1",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=*;actions=*"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read, action.Create, action.Delete},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: false,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Full access 2",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{"id=*;type=*;actions=*"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil},
|
||||
resourceType: resource.Host,
|
||||
actionSet: action.ActionSet{action.List, action.Read, action.Create, action.Delete},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Host,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: false,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple scopes",
|
||||
aclGrants: []scopeGrant{
|
||||
{
|
||||
scope: "o_1",
|
||||
grants: []string{
|
||||
"id=s_1;type=session;actions=list,read",
|
||||
"id=s_2;type=session;actions=list,read",
|
||||
"id=s_3;type=session;actions=list,read",
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: "o_2",
|
||||
grants: []string{"id=*;type=session;actions=list,read:self"},
|
||||
},
|
||||
},
|
||||
scopes: map[string]*scopes.ScopeInfo{"o_1": nil, "o_2": nil},
|
||||
resourceType: resource.Session,
|
||||
actionSet: action.ActionSet{action.List, action.Read},
|
||||
expPermissions: []Permission{
|
||||
{
|
||||
ScopeId: "o_1",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"s_1", "s_2", "s_3"},
|
||||
OnlySelf: false,
|
||||
All: false,
|
||||
},
|
||||
{
|
||||
ScopeId: "o_2",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
ResourceIds: nil,
|
||||
OnlySelf: true,
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var grants []Grant
|
||||
for _, sg := range tt.aclGrants {
|
||||
for _, g := range sg.grants {
|
||||
grant, err := Parse(sg.scope, g, WithSkipFinalValidation(true))
|
||||
require.NoError(t, err)
|
||||
grants = append(grants, grant)
|
||||
}
|
||||
}
|
||||
|
||||
acl := NewACL(grants...)
|
||||
perms := acl.ListPermissions(tt.scopes, tt.resourceType, tt.actionSet)
|
||||
require.ElementsMatch(t, tt.expPermissions, perms)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonMarshal(t *testing.T) {
|
||||
res := &Resource{
|
||||
ScopeId: "scope",
|
||||
|
||||
@@ -189,7 +189,7 @@ func TestLookupWorker(t *testing.T) {
|
||||
server.WithWorkerTags(&server.Tag{"key", "val"}))
|
||||
require.NoError(t, err)
|
||||
|
||||
sessRepo, err := session.NewRepository(rw, rw, kms)
|
||||
sessRepo, err := session.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms, session.WithWorkerStateDelay(0))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestDeleteTermiantedSessionsJob(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
composedOf := TestSessionParams(t, conn, wrapper, iamRepo)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestSessionConnectionCleanupJob(t *testing.T) {
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
serversRepo, err := server.NewRepository(rw, rw, kms)
|
||||
require.NoError(err)
|
||||
sessionRepo, err := NewRepository(rw, rw, kms)
|
||||
sessionRepo, err := NewRepository(ctx, rw, rw, kms)
|
||||
connectionRepo, err := NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -180,7 +180,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
gracePeriod := 1 * time.Second
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms)
|
||||
@@ -306,7 +306,7 @@ func TestCloseConnectionsForDeadWorkers(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
var conns []*Connection
|
||||
require.NoError(repo.list(ctx, &conns, "", nil))
|
||||
require.NoError(rw.SearchWhere(ctx, &conns, "", nil))
|
||||
for i, connId := range connIds {
|
||||
var expected ConnectionStatus
|
||||
switch {
|
||||
@@ -443,7 +443,7 @@ func TestCloseWorkerlessConnections(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -24,7 +24,7 @@ func RegisterJobs(ctx context.Context, scheduler *scheduler.Scheduler, w db.Writ
|
||||
return fmt.Errorf("error registering session cleanup job: %w", err)
|
||||
}
|
||||
|
||||
repo, err := NewRepository(r, w, k)
|
||||
repo, err := NewRepository(ctx, r, w, k)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating repository: %w", err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
)
|
||||
|
||||
// getOpts - iterate the inbound Options and return a struct
|
||||
@@ -32,6 +33,7 @@ type options struct {
|
||||
withDbOpts []db.Option
|
||||
withWorkerStateDelay time.Duration
|
||||
withTerminated bool
|
||||
withPermissions *perms.UserPermissions
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
@@ -120,3 +122,11 @@ func WithTerminated(withTerminated bool) Option {
|
||||
o.withTerminated = withTerminated
|
||||
}
|
||||
}
|
||||
|
||||
// WithPermissions is used to include user permissions when constructing a
|
||||
// Repository.
|
||||
func WithPermissions(p *perms.UserPermissions) Option {
|
||||
return func(o *options) {
|
||||
o.withPermissions = p
|
||||
}
|
||||
}
|
||||
@@ -117,21 +117,6 @@ session_connection_limit(expiration_time, connection_limit) as (
|
||||
select expiration_time, connection_limit, current_connection_count
|
||||
from
|
||||
session_connection_limit, session_connection_count;
|
||||
`
|
||||
nonTerminatedSessionPublicIdList = `
|
||||
select public_id, project_id, user_id from session
|
||||
where
|
||||
session.termination_reason is null
|
||||
and
|
||||
project_id = any(@project_ids)
|
||||
;
|
||||
`
|
||||
|
||||
sessionPublicIdList = `
|
||||
select public_id, project_id, user_id from session
|
||||
where
|
||||
project_id = any(@project_ids)
|
||||
;
|
||||
`
|
||||
|
||||
sessionList = `
|
||||
|
||||
@@ -2,12 +2,18 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
)
|
||||
|
||||
// Clonable provides a cloning interface
|
||||
@@ -23,56 +29,83 @@ type Repository struct {
|
||||
|
||||
// defaultLimit provides a default for limiting the number of results returned from the repo
|
||||
defaultLimit int
|
||||
|
||||
permissions *perms.UserPermissions
|
||||
}
|
||||
|
||||
// RepositoryFactory is a function that creates a Repository.
|
||||
type RepositoryFactory func(opt ...Option) (*Repository, error)
|
||||
|
||||
// NewRepository creates a new session Repository. Supports the options: WithLimit
|
||||
// which sets a default limit on results returned by repo operations.
|
||||
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
func NewRepository(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
const op = "session.NewRepository"
|
||||
if r == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil reader")
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil reader")
|
||||
}
|
||||
if w == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil writer")
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil writer")
|
||||
}
|
||||
if kms == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil kms")
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil kms")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
if opts.withLimit == 0 {
|
||||
// zero signals the boundary defaults should be used.
|
||||
opts.withLimit = db.DefaultLimit
|
||||
}
|
||||
|
||||
if opts.withPermissions != nil {
|
||||
for _, p := range opts.withPermissions.Permissions {
|
||||
if p.Resource != resource.Session {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "permission for incorrect resource")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
reader: r,
|
||||
writer: w,
|
||||
kms: kms,
|
||||
defaultLimit: opts.withLimit,
|
||||
permissions: opts.withPermissions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// list will return a listing of resources and honor the WithLimit option or the
|
||||
// repo defaultLimit. Supports WithOrder option.
|
||||
func (r *Repository) list(ctx context.Context, resources interface{}, where string, args []interface{}, opt ...Option) error {
|
||||
const op = "session.(Repository).list"
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
var dbOpts []db.Option
|
||||
if opts.withLimit != 0 {
|
||||
// non-zero signals an override of the default limit for the repo.
|
||||
limit = opts.withLimit
|
||||
func (r *Repository) listPermissionWhereClauses() ([]string, []interface{}) {
|
||||
var where []string
|
||||
var args []interface{}
|
||||
|
||||
if r.permissions == nil {
|
||||
return where, args
|
||||
}
|
||||
dbOpts = append(dbOpts, db.WithLimit(limit))
|
||||
switch opts.withOrderByCreateTime {
|
||||
case db.AscendingOrderBy:
|
||||
dbOpts = append(dbOpts, db.WithOrder("create_time asc"))
|
||||
case db.DescendingOrderBy:
|
||||
dbOpts = append(dbOpts, db.WithOrder("create_time"))
|
||||
|
||||
inClauseCnt := 0
|
||||
for _, p := range r.permissions.Permissions {
|
||||
if p.Action != action.List {
|
||||
continue
|
||||
}
|
||||
|
||||
inClauseCnt++
|
||||
|
||||
var clauses []string
|
||||
clauses = append(clauses, fmt.Sprintf("project_id = @project_id_%d", inClauseCnt))
|
||||
args = append(args, sql.Named(fmt.Sprintf("project_id_%d", inClauseCnt), p.ScopeId))
|
||||
|
||||
if len(p.ResourceIds) > 0 {
|
||||
clauses = append(clauses, fmt.Sprintf("public_id = any(@public_id_%d)", inClauseCnt))
|
||||
args = append(args, sql.Named(fmt.Sprintf("public_id_%d", inClauseCnt), "{"+strings.Join(p.ResourceIds, ",")+"}"))
|
||||
}
|
||||
|
||||
if p.OnlySelf {
|
||||
inClauseCnt++
|
||||
clauses = append(clauses, fmt.Sprintf("user_id = @user_id_%d", inClauseCnt))
|
||||
args = append(args, sql.Named(fmt.Sprintf("user_id_%d", inClauseCnt), r.permissions.UserId))
|
||||
}
|
||||
|
||||
where = append(where, fmt.Sprintf("(%s)", strings.Join(clauses, " and ")))
|
||||
}
|
||||
if err := r.reader.SearchWhere(ctx, resources, where, args, dbOpts...); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return nil
|
||||
return where, args
|
||||
}
|
||||
|
||||
func (r *Repository) convertToSessions(ctx context.Context, sessionList []*sessionListView, opt ...Option) ([]*Session, error) {
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestRepository_ListConnection(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, WithLimit(testLimit))
|
||||
repo, err := NewRepository(ctx, rw, rw, kms, WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms, WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
@@ -141,7 +141,7 @@ func TestRepository_ConnectConnection(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
@@ -351,7 +351,7 @@ func TestRepository_orphanedConnections(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms, WithWorkerStateDelay(0))
|
||||
require.NoError(err)
|
||||
@@ -446,7 +446,7 @@ func TestRepository_CloseConnections(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -19,7 +19,8 @@ func TestRepository_AddSessionCredentials(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
s := TestSession(t, conn, wrapper, TestSessionParams(t, conn, wrapper, iamRepo))
|
||||
|
||||
@@ -137,7 +138,8 @@ func TestRepository_ListSessionCredentials(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
s1 := TestSession(t, conn, wrapper, TestSessionParams(t, conn, wrapper, iamRepo))
|
||||
s2 := TestSession(t, conn, wrapper, TestSessionParams(t, conn, wrapper, iamRepo))
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
@@ -229,89 +228,28 @@ func (r *Repository) LookupSession(ctx context.Context, sessionId string, _ ...O
|
||||
return &session, authzSummary, nil
|
||||
}
|
||||
|
||||
// fetchAuthzProtectedSessionsByProject fetches sessions for the given projects.
|
||||
// Note that the sessions are not fully populated, and only contain the
|
||||
// necessary information to implement the boundary.AuthzProtectedEntity
|
||||
// interface. Supports the WithTerminated option.
|
||||
func (r *Repository) fetchAuthzProtectedSessionsByProject(
|
||||
ctx context.Context, projectIds []string, opt ...Option,
|
||||
) (map[string][]boundary.AuthzProtectedEntity, error) {
|
||||
const op = "session.(Repository).fetchAuthzProtectedSessionsByProject"
|
||||
|
||||
opts := getOpts(opt...)
|
||||
|
||||
if len(projectIds) == 0 {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "no project ids given")
|
||||
}
|
||||
|
||||
args := []interface{}{
|
||||
sql.Named("project_ids", "{"+strings.Join(projectIds, ",")+"}"),
|
||||
}
|
||||
|
||||
var query string
|
||||
if opts.withTerminated {
|
||||
query = sessionPublicIdList
|
||||
} else {
|
||||
query = nonTerminatedSessionPublicIdList
|
||||
}
|
||||
|
||||
rows, err := r.reader.Query(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
sessionsMap := map[string][]boundary.AuthzProtectedEntity{}
|
||||
for rows.Next() {
|
||||
var ses Session
|
||||
if err := r.reader.ScanRows(ctx, rows, &ses); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed"))
|
||||
}
|
||||
sessionsMap[ses.GetProjectId()] = append(sessionsMap[ses.GetProjectId()], ses)
|
||||
}
|
||||
|
||||
return sessionsMap, nil
|
||||
}
|
||||
|
||||
// ListSessions lists sessions. Supports the WithLimit, WithProjectId, and WithSessionIds options.
|
||||
// ListSessions lists sessions. Sessions returned will be limited by the list
|
||||
// permissions of the repository. Supports the WithTerminated, WithLimit,
|
||||
// WithOrderByCreateTime options.
|
||||
func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Session, error) {
|
||||
const op = "session.(Repository).ListSessions"
|
||||
opts := getOpts(opt...)
|
||||
var where []string
|
||||
var args []interface{}
|
||||
|
||||
inClauseCnt := 0
|
||||
switch len(opts.withProjectIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("project_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withProjectIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.withProjectIds))
|
||||
for _, id := range opts.withProjectIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("project_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
where, args := r.listPermissionWhereClauses()
|
||||
if len(where) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if opts.withUserId != "" {
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("user_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withUserId))
|
||||
}
|
||||
|
||||
switch len(opts.withSessionIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("s.public_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.withSessionIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.withSessionIds))
|
||||
for _, id := range opts.withSessionIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
var whereClause string
|
||||
if len(where) > 0 {
|
||||
whereClause = " where (" + strings.Join(where, " or ") + ")"
|
||||
if !opts.withTerminated {
|
||||
whereClause += "and termination_reason is null"
|
||||
}
|
||||
} else {
|
||||
if !opts.withTerminated {
|
||||
whereClause = "where termination_reason is null"
|
||||
}
|
||||
where = append(where, fmt.Sprintf("s.public_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
var limit string
|
||||
@@ -323,7 +261,6 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
// non-zero signals an override of the default limit for the repo.
|
||||
limit = fmt.Sprintf("limit %d", opts.withLimit)
|
||||
}
|
||||
|
||||
var withOrder string
|
||||
switch opts.withOrderByCreateTime {
|
||||
case db.AscendingOrderBy:
|
||||
@@ -334,10 +271,6 @@ func (r *Repository) ListSessions(ctx context.Context, opt ...Option) ([]*Sessio
|
||||
withOrder = "order by create_time"
|
||||
}
|
||||
|
||||
var whereClause string
|
||||
if len(where) > 0 {
|
||||
whereClause = " where " + strings.Join(where, " and ")
|
||||
}
|
||||
q := sessionList
|
||||
query := fmt.Sprintf(q, whereClause, withOrder, limit, withOrder)
|
||||
|
||||
|
||||
@@ -21,9 +21,12 @@ import (
|
||||
iamStore "github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
tcpStore "github.com/hashicorp/boundary/internal/target/tcp/store"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -39,10 +42,19 @@ func TestRepository_ListSession(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms, WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
composedOf := TestSessionParams(t, conn, wrapper, iamRepo)
|
||||
|
||||
listPerms := &perms.UserPermissions{
|
||||
UserId: composedOf.UserId,
|
||||
Permissions: []perms.Permission{
|
||||
{
|
||||
ScopeId: composedOf.ProjectId,
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
},
|
||||
},
|
||||
}
|
||||
type args struct {
|
||||
opt []Option
|
||||
}
|
||||
@@ -50,58 +62,85 @@ func TestRepository_ListSession(t *testing.T) {
|
||||
name string
|
||||
createCnt int
|
||||
args args
|
||||
perms *perms.UserPermissions
|
||||
wantCnt int
|
||||
wantErr bool
|
||||
withConnections int
|
||||
}{
|
||||
{
|
||||
name: "no-limit",
|
||||
createCnt: repo.defaultLimit + 1,
|
||||
createCnt: testLimit + 1,
|
||||
args: args{
|
||||
opt: []Option{WithLimit(-1)},
|
||||
},
|
||||
wantCnt: repo.defaultLimit + 1,
|
||||
perms: listPerms,
|
||||
wantCnt: testLimit + 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "default-limit",
|
||||
createCnt: repo.defaultLimit + 1,
|
||||
createCnt: testLimit + 1,
|
||||
args: args{},
|
||||
wantCnt: repo.defaultLimit,
|
||||
perms: listPerms,
|
||||
wantCnt: testLimit,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: repo.defaultLimit + 1,
|
||||
createCnt: testLimit + 1,
|
||||
args: args{
|
||||
opt: []Option{WithLimit(3)},
|
||||
},
|
||||
perms: listPerms,
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "withProjectIds",
|
||||
createCnt: repo.defaultLimit + 1,
|
||||
args: args{
|
||||
opt: []Option{WithProjectIds([]string{composedOf.ProjectId})},
|
||||
name: "withNoPerms",
|
||||
createCnt: testLimit + 1,
|
||||
args: args{},
|
||||
perms: &perms.UserPermissions{},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "withPermsDifferentScopeId",
|
||||
createCnt: testLimit + 1,
|
||||
args: args{},
|
||||
perms: &perms.UserPermissions{
|
||||
Permissions: []perms.Permission{
|
||||
{
|
||||
ScopeId: "o_thisIsNotValid",
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCnt: repo.defaultLimit,
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bad-withProjectId",
|
||||
createCnt: repo.defaultLimit + 1,
|
||||
args: args{
|
||||
opt: []Option{WithProjectIds([]string{"o_thisIsNotValid"})},
|
||||
name: "withPermsNonListAction",
|
||||
createCnt: testLimit + 1,
|
||||
args: args{},
|
||||
perms: &perms.UserPermissions{
|
||||
Permissions: []perms.Permission{
|
||||
{
|
||||
ScopeId: composedOf.ProjectId,
|
||||
Resource: resource.Session,
|
||||
Action: action.Read,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "multiple-connections",
|
||||
createCnt: repo.defaultLimit + 1,
|
||||
createCnt: testLimit + 1,
|
||||
args: args{},
|
||||
wantCnt: repo.defaultLimit,
|
||||
perms: listPerms,
|
||||
wantCnt: testLimit,
|
||||
wantErr: false,
|
||||
withConnections: 3,
|
||||
},
|
||||
@@ -109,6 +148,10 @@ func TestRepository_ListSession(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
repo, err := NewRepository(ctx, rw, rw, kms, WithLimit(testLimit), WithPermissions(tt.perms))
|
||||
require.NoError(err)
|
||||
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1")
|
||||
testSessions := []*Session{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
@@ -150,6 +193,10 @@ func TestRepository_ListSession(t *testing.T) {
|
||||
for i := 0; i < wantCnt; i++ {
|
||||
_ = TestSession(t, conn, wrapper, composedOf)
|
||||
}
|
||||
|
||||
repo, err := NewRepository(ctx, rw, rw, kms, WithLimit(testLimit), WithPermissions(listPerms))
|
||||
require.NoError(err)
|
||||
|
||||
got, err := repo.ListSessions(context.Background(), WithOrderByCreateTime(db.AscendingOrderBy))
|
||||
require.NoError(err)
|
||||
assert.Equal(wantCnt, len(got))
|
||||
@@ -160,7 +207,7 @@ func TestRepository_ListSession(t *testing.T) {
|
||||
assert.True(first.Before(second))
|
||||
}
|
||||
})
|
||||
t.Run("withUserId", func(t *testing.T) {
|
||||
t.Run("onlySelf", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1")
|
||||
wantCnt := 5
|
||||
@@ -168,51 +215,25 @@ func TestRepository_ListSession(t *testing.T) {
|
||||
_ = TestSession(t, conn, wrapper, composedOf)
|
||||
}
|
||||
s := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
|
||||
p := &perms.UserPermissions{
|
||||
UserId: s.UserId,
|
||||
Permissions: []perms.Permission{
|
||||
{
|
||||
ScopeId: s.ProjectId,
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
OnlySelf: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
repo, err := NewRepository(ctx, rw, rw, kms, WithLimit(testLimit), WithPermissions(p))
|
||||
require.NoError(err)
|
||||
got, err := repo.ListSessions(context.Background(), WithUserId(s.UserId))
|
||||
require.NoError(err)
|
||||
assert.Equal(1, len(got))
|
||||
assert.Equal(s.UserId, got[0].UserId)
|
||||
})
|
||||
t.Run("withUserIdAndwithScopeId", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1")
|
||||
wantCnt := 5
|
||||
for i := 0; i < wantCnt; i++ {
|
||||
// Scope 1 User 1
|
||||
_ = TestSession(t, conn, wrapper, composedOf)
|
||||
}
|
||||
// Scope 2 User 2
|
||||
s := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
|
||||
// Scope 1 User 2
|
||||
coDiffUser := composedOf
|
||||
coDiffUser.AuthTokenId = s.AuthTokenId
|
||||
coDiffUser.UserId = s.UserId
|
||||
wantS := TestSession(t, conn, wrapper, coDiffUser)
|
||||
|
||||
got, err := repo.ListSessions(context.Background(), WithUserId(coDiffUser.UserId), WithProjectIds([]string{coDiffUser.ProjectId}))
|
||||
require.NoError(err)
|
||||
assert.Equal(1, len(got))
|
||||
assert.Equal(wantS.UserId, got[0].UserId)
|
||||
assert.Equal(wantS.ProjectId, got[0].ProjectId)
|
||||
})
|
||||
t.Run("WithSessionIds", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1")
|
||||
testSessions := []*Session{}
|
||||
for i := 0; i < 10; i++ {
|
||||
s := TestSession(t, conn, wrapper, composedOf)
|
||||
_ = TestState(t, conn, s.PublicId, StatusActive)
|
||||
testSessions = append(testSessions, s)
|
||||
}
|
||||
assert.Equal(10, len(testSessions))
|
||||
withIds := []string{testSessions[0].PublicId, testSessions[1].PublicId}
|
||||
got, err := repo.ListSessions(context.Background(), WithSessionIds(withIds...), WithOrderByCreateTime(db.AscendingOrderBy))
|
||||
require.NoError(err)
|
||||
assert.Equal(2, len(got))
|
||||
assert.Equal(StatusActive, got[0].States[0].Status)
|
||||
assert.Equal(StatusPending, got[0].States[1].Status)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepository_ListSessions_Multiple_Scopes(t *testing.T) {
|
||||
@@ -222,23 +243,30 @@ func TestRepository_ListSessions_Multiple_Scopes(t *testing.T) {
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1")
|
||||
|
||||
const numPerScope = 10
|
||||
var projs []string
|
||||
var p []perms.Permission
|
||||
for i := 0; i < numPerScope; i++ {
|
||||
composedOf := TestSessionParams(t, conn, wrapper, iamRepo)
|
||||
projs = append(projs, composedOf.ProjectId)
|
||||
p = append(p, perms.Permission{
|
||||
ScopeId: composedOf.ProjectId,
|
||||
Resource: resource.Session,
|
||||
Action: action.List,
|
||||
})
|
||||
s := TestSession(t, conn, wrapper, composedOf)
|
||||
_ = TestState(t, conn, s.PublicId, StatusActive)
|
||||
}
|
||||
|
||||
got, err := repo.ListSessions(context.Background(), WithProjectIds(projs))
|
||||
repo, err := NewRepository(ctx, rw, rw, kms, WithPermissions(&perms.UserPermissions{
|
||||
Permissions: p,
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(projs), len(got))
|
||||
got, err := repo.ListSessions(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(p), len(got))
|
||||
}
|
||||
|
||||
func TestRepository_CreateSession(t *testing.T) {
|
||||
@@ -248,7 +276,8 @@ func TestRepository_CreateSession(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
workerAddresses := []string{"1.2.3.4"}
|
||||
@@ -443,7 +472,8 @@ func TestRepository_updateState(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -557,7 +587,8 @@ func TestRepository_transitionState(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
tofu := TestTofu(t)
|
||||
|
||||
@@ -654,7 +685,7 @@ func TestRepository_TerminateCompletedSessions(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -893,7 +924,7 @@ func TestRepository_CancelSession(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, testKms)
|
||||
repo, err := NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
@@ -932,7 +963,7 @@ func TestRepository_CancelSession(t *testing.T) {
|
||||
// is terminated when the one connection is closed.
|
||||
tcpTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, "test target", target.WithSessionConnectionLimit(1))
|
||||
|
||||
targetRepo, err := target.NewRepository(rw, rw, testKms)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
_, _, _, err = targetRepo.AddTargetHostSources(ctx, tcpTarget.GetPublicId(), tcpTarget.GetVersion(), []string{sets[0].PublicId})
|
||||
require.NoError(t, err)
|
||||
@@ -1071,7 +1102,8 @@ func TestRepository_CancelSessionViaFKNull(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
setupFn := func() *Session {
|
||||
session := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
@@ -1250,7 +1282,8 @@ func TestRepository_ActivateSession(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
tofu := TestTofu(t)
|
||||
@@ -1373,7 +1406,8 @@ func TestRepository_DeleteSession(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
@@ -1457,7 +1491,7 @@ func testSessionCredentialParams(t *testing.T, conn *db.DB, wrapper wrapping.Wra
|
||||
ctx := context.Background()
|
||||
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
targetRepo, err := target.NewRepository(rw, rw, kms)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
tar, _, _, err := targetRepo.LookupTarget(ctx, params.TargetId)
|
||||
require.NoError(err)
|
||||
@@ -1494,8 +1528,9 @@ func TestRepository_deleteTargetFKey(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
targetRepo, err := target.NewRepository(rw, rw, kms)
|
||||
ctx := context.Background()
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
@@ -1543,7 +1578,7 @@ func TestRepository_deleteTerminated(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
repo, err := NewRepository(ctx, rw, rw, kms)
|
||||
composedOf := TestSessionParams(t, conn, wrapper, iamRepo)
|
||||
|
||||
cases := []struct {
|
||||
@@ -1623,232 +1658,3 @@ func TestRepository_deleteTerminated(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchAuthzProtectedSessionsByScopes(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
ctx := context.Background()
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
rw := db.New(conn)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
composedOf := TestSessionParams(t, conn, wrapper, iamRepo)
|
||||
|
||||
_, pWithOtherSessions := iam.TestScopes(t, iamRepo)
|
||||
|
||||
hcOther := static.TestCatalogs(t, conn, pWithOtherSessions.GetPublicId(), 1)[0]
|
||||
hsOther := static.TestSets(t, conn, hcOther.GetPublicId(), 1)[0]
|
||||
hOther := static.TestHosts(t, conn, hcOther.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hsOther.GetPublicId(), []*static.Host{hOther})
|
||||
tarOther := tcp.TestTarget(ctx, t, conn, pWithOtherSessions.GetPublicId(), "test", target.WithHostSources([]string{hsOther.GetPublicId()}))
|
||||
|
||||
composedOfOther := ComposedOf{
|
||||
UserId: composedOf.UserId,
|
||||
HostId: hOther.GetPublicId(),
|
||||
TargetId: tarOther.GetPublicId(),
|
||||
HostSetId: hsOther.GetPublicId(),
|
||||
AuthTokenId: composedOf.AuthTokenId,
|
||||
ProjectId: pWithOtherSessions.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
createCnt int
|
||||
terminateCnt int
|
||||
otherCnt int
|
||||
otherTerminateCnt int
|
||||
opts []Option
|
||||
reqScopes []string
|
||||
wantCnt int
|
||||
wantOtherCnt int
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{
|
||||
name: "NonTerminated/none",
|
||||
createCnt: 0,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/one",
|
||||
createCnt: 1,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/many",
|
||||
createCnt: 5,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/many one terminated",
|
||||
createCnt: 5,
|
||||
terminateCnt: 1,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 4,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/many terminated",
|
||||
createCnt: 5,
|
||||
terminateCnt: 3,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 2,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/many multiple projects",
|
||||
createCnt: 5,
|
||||
terminateCnt: 3,
|
||||
reqScopes: []string{composedOf.ProjectId, composedOfOther.ProjectId},
|
||||
wantCnt: 2,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/many multiple projects",
|
||||
createCnt: 5,
|
||||
terminateCnt: 3,
|
||||
otherCnt: 3,
|
||||
otherTerminateCnt: 1,
|
||||
reqScopes: []string{composedOf.ProjectId, composedOfOther.ProjectId},
|
||||
wantCnt: 2,
|
||||
wantOtherCnt: 2,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NonTerminated/no projects",
|
||||
createCnt: 2,
|
||||
terminateCnt: 1,
|
||||
otherCnt: 2,
|
||||
otherTerminateCnt: 1,
|
||||
reqScopes: []string{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "none",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 0,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "one",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 1,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "many",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 5,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "many one terminated",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 5,
|
||||
terminateCnt: 1,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "many terminated",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 5,
|
||||
terminateCnt: 3,
|
||||
reqScopes: []string{composedOf.ProjectId},
|
||||
wantCnt: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "many multiple projects",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 5,
|
||||
terminateCnt: 3,
|
||||
reqScopes: []string{composedOf.ProjectId, composedOfOther.ProjectId},
|
||||
wantCnt: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "many multiple projects",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 5,
|
||||
terminateCnt: 3,
|
||||
otherCnt: 3,
|
||||
otherTerminateCnt: 1,
|
||||
reqScopes: []string{composedOf.ProjectId, composedOfOther.ProjectId},
|
||||
wantCnt: 5,
|
||||
wantOtherCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no projects",
|
||||
opts: []Option{WithTerminated(true)},
|
||||
createCnt: 2,
|
||||
terminateCnt: 1,
|
||||
otherCnt: 2,
|
||||
otherTerminateCnt: 1,
|
||||
reqScopes: []string{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
db.TestDeleteWhere(t, conn, func() interface{} { i := AllocSession(); return &i }(), "1=1")
|
||||
|
||||
testSessions := []*Session{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
s := TestSession(t, conn, wrapper, composedOf)
|
||||
_ = TestState(t, conn, s.PublicId, StatusActive)
|
||||
testSessions = append(testSessions, s)
|
||||
}
|
||||
for i := 0; i < tt.terminateCnt; i++ {
|
||||
_, err := repo.CancelSession(ctx, testSessions[i].PublicId, testSessions[i].Version)
|
||||
require.NoError(err)
|
||||
}
|
||||
terminated, err := repo.TerminateCompletedSessions(ctx)
|
||||
require.NoError(err)
|
||||
require.Equal(tt.terminateCnt, terminated)
|
||||
|
||||
otherTestSessions := []*Session{}
|
||||
for i := 0; i < tt.otherCnt; i++ {
|
||||
s := TestSession(t, conn, wrapper, composedOfOther)
|
||||
_ = TestState(t, conn, s.PublicId, StatusActive)
|
||||
otherTestSessions = append(otherTestSessions, s)
|
||||
}
|
||||
for i := 0; i < tt.otherTerminateCnt; i++ {
|
||||
_, err := repo.CancelSession(ctx, otherTestSessions[i].PublicId, otherTestSessions[i].Version)
|
||||
require.NoError(err)
|
||||
}
|
||||
terminated, err = repo.TerminateCompletedSessions(ctx)
|
||||
require.NoError(err)
|
||||
require.Equal(tt.otherTerminateCnt, terminated)
|
||||
|
||||
assert.Equal(tt.otherCnt, len(otherTestSessions))
|
||||
|
||||
got, err := repo.fetchAuthzProtectedSessionsByProject(ctx, tt.reqScopes, tt.opts...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got[composedOf.ProjectId]))
|
||||
assert.Equal(tt.wantOtherCnt, len(got[composedOfOther.ProjectId]))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestNewRepository(t *testing.T) {
|
||||
@@ -15,6 +16,7 @@ func TestNewRepository(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
ctx := context.Background()
|
||||
|
||||
type args struct {
|
||||
r db.Reader
|
||||
@@ -36,7 +38,7 @@ func TestNewRepository(t *testing.T) {
|
||||
k: testKms,
|
||||
},
|
||||
want: func() *Repository {
|
||||
ret, err := NewRepository(rw, rw, testKms)
|
||||
ret, err := NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
return ret
|
||||
}(),
|
||||
@@ -66,7 +68,7 @@ func TestNewRepository(t *testing.T) {
|
||||
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.k)
|
||||
got, err := NewRepository(ctx, tt.args.r, tt.args.w, tt.args.k)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Equal(tt.wantErrString, err.Error())
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestService_AuthorizeConnection(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, testKms)
|
||||
repo, err := NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
@@ -90,7 +90,7 @@ func TestService_AuthorizeConnection(t *testing.T) {
|
||||
// is terminated when the one connection is closed.
|
||||
tcpTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, "test target", target.WithSessionConnectionLimit(1))
|
||||
|
||||
targetRepo, err := target.NewRepository(rw, rw, testKms)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
_, _, _, err = targetRepo.AddTargetHostSources(ctx, tcpTarget.GetPublicId(), tcpTarget.GetVersion(), []string{sets[0].PublicId})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestServiceCloseConnections(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := NewRepository(rw, rw, testKms)
|
||||
repo, err := NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := NewConnectionRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
@@ -51,7 +51,7 @@ func TestServiceCloseConnections(t *testing.T) {
|
||||
// is terminated when the one connection is closed.
|
||||
tcpTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, "test target", target.WithSessionConnectionLimit(1))
|
||||
|
||||
targetRepo, err := target.NewRepository(rw, rw, testKms)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
_, _, _, err = targetRepo.AddTargetHostSources(ctx, tcpTarget.GetPublicId(), tcpTarget.GetVersion(), []string{sets[0].PublicId})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/boundary"
|
||||
)
|
||||
|
||||
type listForAuthzCheckFunc func(ctx context.Context, projectIds []string) (map[string][]boundary.AuthzProtectedEntity, error)
|
||||
|
||||
func (a listForAuthzCheckFunc) FetchAuthzProtectedEntitiesByScope(ctx context.Context, projectIds []string) (map[string][]boundary.AuthzProtectedEntity, error) {
|
||||
return a(ctx, projectIds)
|
||||
}
|
||||
|
||||
// ListForAuthzCheck returns a functions that fetches sessions for the given
|
||||
// projects. Note that the sessions are not fully populated, and only contain the
|
||||
// necessary information to implement the boundary.AuthzProtectedEntity
|
||||
// interface. Supports the WithTerminated option.
|
||||
func ListForAuthzCheck(repo *Repository, opt ...Option) listForAuthzCheckFunc {
|
||||
return func(ctx context.Context, projectIds []string) (map[string][]boundary.AuthzProtectedEntity, error) {
|
||||
return repo.fetchAuthzProtectedSessionsByProject(ctx, projectIds, opt...)
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func TestWorkerStatusReport(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
repo, err := session.NewRepository(rw, rw, kms)
|
||||
repo, err := session.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(t, err)
|
||||
connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms, session.WithWorkerStateDelay(0))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -144,7 +144,7 @@ func TestSessionParams(t testing.TB, conn *db.DB, wrapper wrapping.Wrapper, iamR
|
||||
tcpTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, "test target")
|
||||
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
targetRepo, err := target.NewRepository(rw, rw, kms)
|
||||
targetRepo, err := target.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
_, _, _, err = targetRepo.AddTargetHostSources(ctx, tcpTarget.GetPublicId(), tcpTarget.GetVersion(), []string{sets[0].PublicId})
|
||||
require.NoError(err)
|
||||
|
||||
@@ -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,10 +11,17 @@ 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/action"
|
||||
"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(...Option) (*Repository, error)
|
||||
|
||||
// Cloneable provides a cloning interface
|
||||
type Cloneable interface {
|
||||
Clone() Target
|
||||
@@ -28,31 +35,49 @@ 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.
|
||||
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
// 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(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
const op = "target.NewRepository"
|
||||
if r == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil reader")
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil reader")
|
||||
}
|
||||
if w == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil writer")
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil writer")
|
||||
}
|
||||
if kms == nil {
|
||||
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil kms")
|
||||
return nil, errors.New(ctx, 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(ctx, errors.InvalidParameter, op, "permission for incorrect resource found")
|
||||
}
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
reader: r,
|
||||
writer: w,
|
||||
kms: kms,
|
||||
defaultLimit: opts.WithLimit,
|
||||
permissions: opts.WithPermissions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -188,59 +213,36 @@ func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, pro
|
||||
return targetsMap, nil
|
||||
}
|
||||
|
||||
// ListTargets in targets in a project. Supports the WithProjectId, WithLimit, WithType options.
|
||||
// ListTargets lists targets in a project based on the data in the WithPermissions option
|
||||
// provided to the Repository constructor. If no permissions are available, this function
|
||||
// is a no-op.
|
||||
// Supports WithLimit which overrides the limit set in the Repository object.
|
||||
func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, error) {
|
||||
const op = "target.(Repository).ListTargets"
|
||||
|
||||
if len(r.permissions) == 0 {
|
||||
return []Target{}, nil
|
||||
}
|
||||
|
||||
where, args := r.listPermissionWhereClauses()
|
||||
if len(where) == 0 {
|
||||
return []Target{}, nil
|
||||
}
|
||||
|
||||
opts := GetOpts(opt...)
|
||||
if len(opts.WithProjectIds) == 0 && opts.WithUserId == "" && len(opts.WithTargetIds) == 0 {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "must specify either project ids, target ids, or user id")
|
||||
}
|
||||
// TODO (jimlambrt 8/2020) - implement WithUserId() optional filtering.
|
||||
var where []string
|
||||
var args []interface{}
|
||||
inClauseCnt := 0
|
||||
|
||||
switch len(opts.WithProjectIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("project_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithProjectIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.WithProjectIds))
|
||||
for _, id := range opts.WithProjectIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("project_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
switch len(opts.WithTargetIds) {
|
||||
case 0:
|
||||
case 1:
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("public_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithTargetIds[0]))
|
||||
default:
|
||||
idsInClause := make([]string, 0, len(opts.WithTargetIds))
|
||||
for _, id := range opts.WithTargetIds {
|
||||
inClauseCnt += 1
|
||||
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
|
||||
}
|
||||
where = append(where, fmt.Sprintf("public_id in (%s)", strings.Join(idsInClause, ",")))
|
||||
}
|
||||
|
||||
if opts.WithType != "" {
|
||||
inClauseCnt += 1
|
||||
where, args = append(where, fmt.Sprintf("type = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithType.String()))
|
||||
limit := r.defaultLimit
|
||||
if opts.WithLimit != 0 {
|
||||
limit = opts.WithLimit
|
||||
}
|
||||
|
||||
var foundTargets []*targetView
|
||||
err := r.list(ctx, &foundTargets, strings.Join(where, " and "), args, opt...)
|
||||
err := r.reader.SearchWhere(ctx, &foundTargets, strings.Join(where, " or "), args,
|
||||
db.WithLimit(limit))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
targets := make([]Target, 0, len(foundTargets))
|
||||
|
||||
for _, t := range foundTargets {
|
||||
subtype, err := t.targetSubtype(ctx)
|
||||
if err != nil {
|
||||
@@ -248,25 +250,34 @@ func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target,
|
||||
}
|
||||
targets = append(targets, subtype)
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
// list will return a listing of resources and honor the WithLimit option or the
|
||||
// repo defaultLimit
|
||||
func (r *Repository) list(ctx context.Context, resources interface{}, where string, args []interface{}, opt ...Option) error {
|
||||
const op = "target.(Repository).list"
|
||||
opts := GetOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
var dbOpts []db.Option
|
||||
if opts.WithLimit != 0 {
|
||||
// non-zero signals an override of the default limit for the repo.
|
||||
limit = opts.WithLimit
|
||||
func (r *Repository) listPermissionWhereClauses() ([]string, []interface{}) {
|
||||
var where []string
|
||||
var args []interface{}
|
||||
|
||||
inClauseCnt := 0
|
||||
for _, p := range r.permissions {
|
||||
if p.Action != action.List {
|
||||
continue
|
||||
}
|
||||
inClauseCnt++
|
||||
|
||||
var clauses []string
|
||||
clauses = append(clauses, fmt.Sprintf("project_id = @project_id_%d", inClauseCnt))
|
||||
args = append(args, sql.Named(fmt.Sprintf("project_id_%d", inClauseCnt), p.ScopeId))
|
||||
|
||||
if len(p.ResourceIds) > 0 {
|
||||
clauses = append(clauses, fmt.Sprintf("public_id = any(@public_id_%d)", inClauseCnt))
|
||||
args = append(args, sql.Named(fmt.Sprintf("public_id_%d", inClauseCnt), "{"+strings.Join(p.ResourceIds, ",")+"}"))
|
||||
}
|
||||
|
||||
where = append(where, fmt.Sprintf("(%s)", strings.Join(clauses, " and ")))
|
||||
}
|
||||
dbOpts = append(dbOpts, db.WithLimit(limit))
|
||||
if err := r.reader.SearchWhere(ctx, resources, where, args, dbOpts...); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
return nil
|
||||
|
||||
return where, args
|
||||
}
|
||||
|
||||
// DeleteTarget will delete a target from the repository.
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(context.Background(), rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -16,9 +21,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 +81,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(context.Background(), tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
assert.Equal(tt.wantErrString, err.Error())
|
||||
@@ -91,3 +139,173 @@ func TestNewRepository(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryListPermissionWhereClauses(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
perms []perms.Permission
|
||||
expWhere []string
|
||||
expArgs []any
|
||||
}{
|
||||
{
|
||||
name: "nilPerms",
|
||||
perms: nil,
|
||||
expWhere: []string{},
|
||||
expArgs: []any{},
|
||||
},
|
||||
{
|
||||
name: "emptyPerms",
|
||||
perms: []perms.Permission{},
|
||||
expWhere: []string{},
|
||||
expArgs: []any{},
|
||||
},
|
||||
{
|
||||
name: "noListActionPerms",
|
||||
perms: []perms.Permission{
|
||||
{
|
||||
ScopeId: "scope_a",
|
||||
Action: action.Create,
|
||||
},
|
||||
{
|
||||
ScopeId: "scope_b",
|
||||
Action: action.Read,
|
||||
},
|
||||
{
|
||||
ScopeId: "scope_c",
|
||||
Action: action.Delete,
|
||||
},
|
||||
},
|
||||
expWhere: []string{},
|
||||
expArgs: []any{},
|
||||
},
|
||||
{
|
||||
name: "onePermissionAllResources",
|
||||
perms: []perms.Permission{
|
||||
{
|
||||
ScopeId: "scope_a",
|
||||
Action: action.List,
|
||||
},
|
||||
},
|
||||
expWhere: []string{"(project_id = @project_id_1)"},
|
||||
expArgs: []any{sql.Named("project_id_1", "scope_a")},
|
||||
},
|
||||
{
|
||||
name: "onePermissionAllResourcesNonListIgnored",
|
||||
perms: []perms.Permission{
|
||||
{
|
||||
ScopeId: "scope_a",
|
||||
Action: action.List,
|
||||
},
|
||||
{
|
||||
ScopeId: "scope_b",
|
||||
Action: action.Create,
|
||||
},
|
||||
},
|
||||
expWhere: []string{"(project_id = @project_id_1)"},
|
||||
expArgs: []any{sql.Named("project_id_1", "scope_a")},
|
||||
},
|
||||
{
|
||||
name: "onePermissionResourceIds",
|
||||
perms: []perms.Permission{
|
||||
{
|
||||
ScopeId: "scope_a",
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"resourceid1", "resourceid2"},
|
||||
},
|
||||
},
|
||||
expWhere: []string{"(project_id = @project_id_1 and public_id = any(@public_id_1))"},
|
||||
expArgs: []any{
|
||||
sql.Named("project_id_1", "scope_a"),
|
||||
sql.Named("public_id_1", "{resourceid1,resourceid2}"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiplePermissionsAllResources",
|
||||
perms: []perms.Permission{
|
||||
{ScopeId: "scope_a", Action: action.List},
|
||||
{ScopeId: "scope_b", Action: action.List},
|
||||
{ScopeId: "scope_c", Action: action.List},
|
||||
{ScopeId: "scope_d", Action: action.List},
|
||||
},
|
||||
expWhere: []string{
|
||||
"(project_id = @project_id_1)",
|
||||
"(project_id = @project_id_2)",
|
||||
"(project_id = @project_id_3)",
|
||||
"(project_id = @project_id_4)",
|
||||
},
|
||||
expArgs: []any{
|
||||
sql.Named("project_id_1", "scope_a"),
|
||||
sql.Named("project_id_2", "scope_b"),
|
||||
sql.Named("project_id_3", "scope_c"),
|
||||
sql.Named("project_id_4", "scope_d"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiplePermissionsResourceIds",
|
||||
perms: []perms.Permission{
|
||||
{
|
||||
ScopeId: "scope_a",
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"resourceid1", "resourceid2"},
|
||||
},
|
||||
{
|
||||
ScopeId: "scope_b",
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"resourceid3", "resourceid4"},
|
||||
},
|
||||
},
|
||||
expWhere: []string{
|
||||
"(project_id = @project_id_1 and public_id = any(@public_id_1))",
|
||||
"(project_id = @project_id_2 and public_id = any(@public_id_2))",
|
||||
},
|
||||
expArgs: []any{
|
||||
sql.Named("project_id_1", "scope_a"),
|
||||
sql.Named("project_id_2", "scope_b"),
|
||||
sql.Named("public_id_1", "{resourceid1,resourceid2}"),
|
||||
sql.Named("public_id_2", "{resourceid3,resourceid4}"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiplePermissionsMix",
|
||||
perms: []perms.Permission{
|
||||
{
|
||||
ScopeId: "scope_a",
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"resourceid1", "resourceid2"},
|
||||
},
|
||||
{
|
||||
ScopeId: "scope_b",
|
||||
Action: action.List,
|
||||
ResourceIds: []string{"resourceid3", "resourceid4"},
|
||||
},
|
||||
{ScopeId: "scope_c", Action: action.List},
|
||||
{ScopeId: "scope_d", Action: action.List},
|
||||
},
|
||||
expWhere: []string{
|
||||
"(project_id = @project_id_1 and public_id = any(@public_id_1))",
|
||||
"(project_id = @project_id_2 and public_id = any(@public_id_2))",
|
||||
"(project_id = @project_id_3)",
|
||||
"(project_id = @project_id_4)",
|
||||
},
|
||||
expArgs: []any{
|
||||
sql.Named("project_id_1", "scope_a"),
|
||||
sql.Named("public_id_1", "{resourceid1,resourceid2}"),
|
||||
sql.Named("project_id_2", "scope_b"),
|
||||
sql.Named("public_id_2", "{resourceid3,resourceid4}"),
|
||||
sql.Named("project_id_3", "scope_c"),
|
||||
sql.Named("project_id_4", "scope_d"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
repo := Repository{}
|
||||
repo.permissions = tt.perms
|
||||
|
||||
where, args := repo.listPermissionWhereClauses()
|
||||
require.ElementsMatch(t, tt.expWhere, where)
|
||||
require.ElementsMatch(t, tt.expArgs, args)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -158,14 +158,15 @@ func TestTargetHostSet_ImmutableFields(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
ctx := context.Background()
|
||||
rw := db.New(conn)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
ctx := context.Background()
|
||||
projTarget := tcp.TestTarget(ctx, t, conn, proj.PublicId, tcp.TestId(t))
|
||||
testCats := static.TestCatalogs(t, conn, proj.PublicId, 1)
|
||||
hsets := static.TestSets(t, conn, testCats[0].GetPublicId(), 2)
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, staticProj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(context.Background(), rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
csVault := vault.TestCredentialStores(t, conn, wrapper, staticProj.GetPublicId(), 1)[0]
|
||||
@@ -336,7 +336,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(context.Background(), rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
@@ -563,7 +563,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(context.Background(), rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestRepository_AddTargetHostSets(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, staticProj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(context.Background(), rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
createHostSetsFn := func(projects []string) []string {
|
||||
@@ -199,10 +199,10 @@ func TestRepository_DeleteTargetHosts(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
target target.Target
|
||||
@@ -404,7 +404,9 @@ func TestRepository_SetTargetHostSets(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
@@ -427,8 +429,6 @@ func TestRepository_SetTargetHostSets(t *testing.T) {
|
||||
return results
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
setupFn := func(target target.Target) []target.HostSource {
|
||||
hs := createHostSetsFn()
|
||||
_, created, _, err := repo.AddTargetHostSources(context.Background(), target.GetPublicId(), 1, hs)
|
||||
|
||||
@@ -28,7 +28,9 @@ func TestRepository_CreateTarget(t *testing.T) {
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
|
||||
@@ -38,8 +40,6 @@ func TestRepository_CreateTarget(t *testing.T) {
|
||||
cs := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 2)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
type args struct {
|
||||
target target.Target
|
||||
opt []target.Option
|
||||
@@ -165,7 +165,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(context.Background(), rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
id := tcp.TestId(t)
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package tcp_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -12,8 +11,11 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -30,7 +32,7 @@ func TestRepository_LookupTarget(t *testing.T) {
|
||||
_, _, err := iamRepo.UpdateScope(ctx, proj, 1, []string{"name"})
|
||||
require.NoError(t, err)
|
||||
rw := db.New(conn)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
tgt := tcp.TestTarget(ctx, t, conn, proj.PublicId, "target-name")
|
||||
|
||||
@@ -135,119 +137,15 @@ func TestRepository_LookupTarget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListTargets(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
const testLimit = 10
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
rw := db.New(conn)
|
||||
repo, err := target.NewRepository(rw, rw, testKms, target.WithLimit(testLimit))
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
opt []target.Option
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
createCnt int
|
||||
createProjectId string
|
||||
createProjectId2 string
|
||||
grantUserId string
|
||||
args args
|
||||
wantCnt int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "tcp-target",
|
||||
createCnt: 5,
|
||||
createProjectId: proj.PublicId,
|
||||
args: args{
|
||||
opt: []target.Option{target.WithType(tcp.Subtype), target.WithProjectIds([]string{proj.PublicId})},
|
||||
},
|
||||
wantCnt: 5,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no-limit",
|
||||
createCnt: testLimit + 1,
|
||||
createProjectId: proj.PublicId,
|
||||
args: args{
|
||||
opt: []target.Option{target.WithLimit(-1), target.WithProjectIds([]string{proj.PublicId})},
|
||||
},
|
||||
wantCnt: testLimit + 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "default-limit",
|
||||
createCnt: testLimit + 1,
|
||||
createProjectId: proj.PublicId,
|
||||
args: args{
|
||||
opt: []target.Option{target.WithProjectIds([]string{proj.PublicId})},
|
||||
},
|
||||
wantCnt: testLimit,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom-limit",
|
||||
createCnt: testLimit + 1,
|
||||
createProjectId: proj.PublicId,
|
||||
args: args{
|
||||
opt: []target.Option{target.WithLimit(3), target.WithProjectIds([]string{proj.PublicId})},
|
||||
},
|
||||
wantCnt: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "bad-org",
|
||||
createCnt: 1,
|
||||
createProjectId: proj.PublicId,
|
||||
args: args{
|
||||
opt: []target.Option{target.WithProjectIds([]string{"bad-id"})},
|
||||
},
|
||||
wantCnt: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
db.TestDeleteWhere(t, conn, tcp.NewTestTarget(""), "1=1")
|
||||
testGroups := []target.Target{}
|
||||
for i := 0; i < tt.createCnt; i++ {
|
||||
switch {
|
||||
case tt.createProjectId2 != "" && i%2 == 0:
|
||||
testGroups = append(testGroups, tcp.TestTarget(ctx, t, conn, tt.createProjectId2, strconv.Itoa(i)))
|
||||
default:
|
||||
testGroups = append(testGroups, tcp.TestTarget(ctx, t, conn, tt.createProjectId, strconv.Itoa(i)))
|
||||
}
|
||||
}
|
||||
assert.Equal(tt.createCnt, len(testGroups))
|
||||
got, err := repo.ListTargets(ctx, tt.args.opt...)
|
||||
if tt.wantErr {
|
||||
require.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tt.wantCnt, len(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_ListRoles_Multiple_Scopes(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
wrapper := db.TestWrapper(t)
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
_, proj1 := iam.TestScopes(t, iamRepo)
|
||||
_, proj2 := iam.TestScopes(t, iamRepo)
|
||||
rw := db.New(conn)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
db.TestDeleteWhere(t, conn, tcp.NewTestTarget(""), "1=1")
|
||||
|
||||
@@ -261,7 +159,26 @@ func TestRepository_ListRoles_Multiple_Scopes(t *testing.T) {
|
||||
total++
|
||||
}
|
||||
|
||||
got, err := repo.ListTargets(ctx, target.WithProjectIds([]string{"global", proj1.GetPublicId(), proj2.GetPublicId()}))
|
||||
rw := db.New(conn)
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms,
|
||||
target.WithPermissions([]perms.Permission{
|
||||
{
|
||||
ScopeId: proj1.PublicId,
|
||||
Resource: resource.Target,
|
||||
Action: action.List,
|
||||
All: true,
|
||||
},
|
||||
{
|
||||
ScopeId: proj2.PublicId,
|
||||
Resource: resource.Target,
|
||||
Action: action.List,
|
||||
All: true,
|
||||
},
|
||||
}),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := repo.ListTargets(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, total, len(got))
|
||||
}
|
||||
@@ -274,10 +191,11 @@ func TestRepository_DeleteTarget(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
target target.Target
|
||||
opt []target.Option
|
||||
|
||||
@@ -23,9 +23,10 @@ func Test_TestTcpTarget(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
require.NoError(err)
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(err)
|
||||
|
||||
cats := static.TestCatalogs(t, conn, proj.PublicId, 1)
|
||||
hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 2)
|
||||
@@ -56,9 +57,10 @@ func Test_TestCredentialLibrary(t *testing.T) {
|
||||
testKms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
repo, err := target.NewRepository(rw, rw, testKms)
|
||||
require.NoError(err)
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := target.NewRepository(ctx, rw, rw, testKms)
|
||||
require.NoError(err)
|
||||
|
||||
tar := tcp.TestTarget(ctx, t, conn, proj.PublicId, t.Name())
|
||||
store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0]
|
||||
|
||||
@@ -126,7 +126,7 @@ func TestGenerateSessionBenchmarkTemplateDumps(t *testing.T) {
|
||||
require.NoError(err)
|
||||
pwRepo, err := password.NewRepository(rw, rw, kms)
|
||||
require.NoError(err)
|
||||
sessRepo, err := session.NewRepository(rw, rw, kms)
|
||||
sessRepo, err := session.NewRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms)
|
||||
require.NoError(err)
|
||||
|
||||
Reference in new issue
Block a user