Add support for recursive listing (#885)
This commit is contained in:
1 parent
1f9fcad1cd
commit
6cd97a4a6e
77 files changed
+2244
-1448
No files matched your search
@@ -0,0 +1,92 @@
|
||||
package scopeids
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
)
|
||||
|
||||
// GetScopeIds, given common parameters for List calls, returns the set of scope
|
||||
// IDs in which to search for resources. It also returns a memoized map of the
|
||||
// scopes to their info for populating returned values.
|
||||
//
|
||||
// Note: This was originally pulled out 1:1 from the role service. It and other
|
||||
// tests in the other service handlers test this function extensively as it
|
||||
// forms the basis for all recursive listing tests; see those tests for list
|
||||
// functionality in the various service handlers.
|
||||
func GetScopeIds(
|
||||
// The context to use when listing in the DB, if required
|
||||
ctx context.Context,
|
||||
// An IAM repo function to use for a listing call, if required
|
||||
repoFn common.IamRepoFactory,
|
||||
// The original auth results from the list command
|
||||
authResults auth.VerifyResults,
|
||||
// The scope ID to use, or to use as the starting point for a recursive
|
||||
// search
|
||||
rootScopeId string,
|
||||
// Whether or not the search should be recursive
|
||||
recursive bool) ([]string, map[string]*scopes.ScopeInfo, error) {
|
||||
// Get the list of scope IDs. If it's recursive, check ACLs.
|
||||
var scopeIds []string
|
||||
// This will be used to memoize scope info so we can put the right scope
|
||||
// info for each returned value
|
||||
scopeInfoMap := map[string]*scopes.ScopeInfo{
|
||||
rootScopeId: authResults.Scope,
|
||||
}
|
||||
switch recursive {
|
||||
case true:
|
||||
repo, err := repoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Get all scopes recursively
|
||||
scps, err := repo.ListScopesRecursively(ctx, rootScopeId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
scopeIds = make([]string, 0, len(scps))
|
||||
res := perms.Resource{
|
||||
Type: resource.Role,
|
||||
}
|
||||
// For each scope, see if we have permission to list on that scope
|
||||
for _, scp := range scps {
|
||||
scpId := scp.GetPublicId()
|
||||
// We already checked the incoming ID so we can definitely add it
|
||||
if scpId == authResults.Scope.Id {
|
||||
scopeIds = append(scopeIds, scp.GetPublicId())
|
||||
continue
|
||||
}
|
||||
res.ScopeId = scpId
|
||||
aSet := authResults.FetchActionSetForType(ctx,
|
||||
resource.Role,
|
||||
action.ActionSet{action.List},
|
||||
auth.WithResource(&res),
|
||||
)
|
||||
// We only passed one action in, so anything other than that one
|
||||
// action back should not be included. Assuming it's correct, add
|
||||
// the scope ID for lookup and memoize the scope info if needed.
|
||||
if len(aSet) == 1 && aSet[0] == action.List {
|
||||
scopeIds = append(scopeIds, scpId)
|
||||
if scopeInfoMap[scpId] == nil {
|
||||
scopeInfo := &scopes.ScopeInfo{
|
||||
Id: scp.GetPublicId(),
|
||||
Type: scp.GetType(),
|
||||
Name: scp.GetName(),
|
||||
Description: scp.GetDescription(),
|
||||
}
|
||||
scopeInfoMap[scpId] = scopeInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
scopeIds = []string{rootScopeId}
|
||||
}
|
||||
|
||||
return scopeIds, scopeInfoMap, nil
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/accounts"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens"
|
||||
@@ -129,17 +130,24 @@ func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
ul, err := s.listFromRepo(ctx, authResults.Scope.GetId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.AuthMethod, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.AuthMethod,
|
||||
Type: resource.AuthMethod,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -270,12 +278,12 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.AuthMethod, er
|
||||
return toAuthMethodProto(u)
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.AuthMethod, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.AuthMethod, error) {
|
||||
repo, err := s.pwRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListAuthMethods(ctx, scopeId)
|
||||
ul, err := repo.ListAuthMethods(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -194,30 +194,37 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
scopeId string
|
||||
res *pbs.ListAuthMethodsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListAuthMethodsRequest
|
||||
res *pbs.ListAuthMethodsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Some Auth Methods",
|
||||
scopeId: oWithAuthMethods.GetPublicId(),
|
||||
res: &pbs.ListAuthMethodsResponse{Items: wantSomeAuthMethods},
|
||||
name: "List Some Auth Methods",
|
||||
req: &pbs.ListAuthMethodsRequest{ScopeId: oWithAuthMethods.GetPublicId()},
|
||||
res: &pbs.ListAuthMethodsResponse{Items: wantSomeAuthMethods},
|
||||
},
|
||||
{
|
||||
name: "List Other Auth Methods",
|
||||
scopeId: oWithOtherAuthMethods.GetPublicId(),
|
||||
res: &pbs.ListAuthMethodsResponse{Items: wantOtherAuthMethods},
|
||||
name: "List Other Auth Methods",
|
||||
req: &pbs.ListAuthMethodsRequest{ScopeId: oWithOtherAuthMethods.GetPublicId()},
|
||||
res: &pbs.ListAuthMethodsResponse{Items: wantOtherAuthMethods},
|
||||
},
|
||||
{
|
||||
name: "List No Auth Methods",
|
||||
scopeId: oNoAuthMethods.GetPublicId(),
|
||||
res: &pbs.ListAuthMethodsResponse{},
|
||||
name: "List No Auth Methods",
|
||||
req: &pbs.ListAuthMethodsRequest{ScopeId: oNoAuthMethods.GetPublicId()},
|
||||
res: &pbs.ListAuthMethodsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Unfound Auth Method",
|
||||
scopeId: "o_DoesntExis",
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
name: "Unfound Auth Method",
|
||||
req: &pbs.ListAuthMethodsRequest{ScopeId: "o_DoesntExis"},
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
},
|
||||
{
|
||||
name: "List All Auth Methods Recursively",
|
||||
req: &pbs.ListAuthMethodsRequest{ScopeId: "global", Recursive: true},
|
||||
res: &pbs.ListAuthMethodsResponse{
|
||||
Items: append(wantSomeAuthMethods, wantOtherAuthMethods...),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -226,12 +233,12 @@ func TestList(t *testing.T) {
|
||||
s, err := authmethods.NewService(kms, pwRepoFn, iamRepoFn, atRepoFn)
|
||||
require.NoError(err, "Couldn't create new auth_method service.")
|
||||
|
||||
got, gErr := s.ListAuthMethods(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListAuthMethodsRequest{ScopeId: tc.scopeId})
|
||||
got, gErr := s.ListAuthMethods(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetScopeId())), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListAuthMethods() for scope %q got error %v, wanted %v", tc.scopeId, gErr, tc.err)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListAuthMethods() for scope %q got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListAuthMethods() for scope %q got response %q, wanted %q", tc.scopeId, got, tc.res)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListAuthMethods() for scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
@@ -62,17 +63,24 @@ func (s Service) ListAuthTokens(ctx context.Context, req *pbs.ListAuthTokensRequ
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
ul, err := s.listFromRepo(ctx, req.GetScopeId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.AuthToken, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.AuthToken,
|
||||
Type: resource.AuthToken,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -148,13 +156,13 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, orgId string) ([]*pb.AuthToken, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.AuthToken, error) {
|
||||
repo, err := s.repoFn()
|
||||
_ = repo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListAuthTokens(ctx, orgId)
|
||||
ul, err := repo.ListAuthTokens(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -113,6 +113,24 @@ func TestList(t *testing.T) {
|
||||
|
||||
orgNoTokens, _ := iam.TestScopes(t, iamRepo)
|
||||
|
||||
var globalTokens []*pb.AuthToken
|
||||
for i := 0; i < 3; i++ {
|
||||
at := authtoken.TestAuthToken(t, conn, kms, scope.Global.String())
|
||||
globalTokens = append(globalTokens, &pb.AuthToken{
|
||||
Id: at.GetPublicId(),
|
||||
ScopeId: at.GetScopeId(),
|
||||
UserId: at.GetIamUserId(),
|
||||
AuthMethodId: at.GetAuthMethodId(),
|
||||
AccountId: at.GetAuthAccountId(),
|
||||
CreatedTime: at.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: at.GetUpdateTime().GetTimestamp(),
|
||||
ApproximateLastUsedTime: at.GetApproximateLastAccessTime().GetTimestamp(),
|
||||
ExpirationTime: at.GetExpirationTime().GetTimestamp(),
|
||||
Scope: &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String()},
|
||||
AuthorizedActions: []string{"read", "delete"},
|
||||
})
|
||||
}
|
||||
|
||||
orgWithSomeTokens, _ := iam.TestScopes(t, iamRepo)
|
||||
var wantSomeTokens []*pb.AuthToken
|
||||
for i := 0; i < 3; i++ {
|
||||
@@ -151,32 +169,40 @@ func TestList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
allTokens := append(globalTokens, wantSomeTokens...)
|
||||
allTokens = append(allTokens, wantOtherTokens...)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
scope string
|
||||
res *pbs.ListAuthTokensResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListAuthTokensRequest
|
||||
res *pbs.ListAuthTokensResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Some Tokens",
|
||||
scope: orgWithSomeTokens.GetPublicId(),
|
||||
res: &pbs.ListAuthTokensResponse{Items: wantSomeTokens},
|
||||
name: "List Some Tokens",
|
||||
req: &pbs.ListAuthTokensRequest{ScopeId: orgWithSomeTokens.GetPublicId()},
|
||||
res: &pbs.ListAuthTokensResponse{Items: wantSomeTokens},
|
||||
},
|
||||
{
|
||||
name: "List Other Tokens",
|
||||
scope: orgWithOtherTokens.GetPublicId(),
|
||||
res: &pbs.ListAuthTokensResponse{Items: wantOtherTokens},
|
||||
name: "List Other Tokens",
|
||||
req: &pbs.ListAuthTokensRequest{ScopeId: orgWithOtherTokens.GetPublicId()},
|
||||
res: &pbs.ListAuthTokensResponse{Items: wantOtherTokens},
|
||||
},
|
||||
{
|
||||
name: "List No Token",
|
||||
scope: orgNoTokens.GetPublicId(),
|
||||
res: &pbs.ListAuthTokensResponse{},
|
||||
name: "List No Token",
|
||||
req: &pbs.ListAuthTokensRequest{ScopeId: orgNoTokens.GetPublicId()},
|
||||
res: &pbs.ListAuthTokensResponse{},
|
||||
},
|
||||
// TODO: When an org doesn't exist, we should return a 404 instead of an empty list.
|
||||
{
|
||||
name: "Unfound Org",
|
||||
scope: scope.Org.Prefix() + "_DoesntExis",
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
name: "Unfound Org",
|
||||
req: &pbs.ListAuthTokensRequest{ScopeId: scope.Org.Prefix() + "_DoesntExis"},
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
},
|
||||
{
|
||||
name: "List Recursively",
|
||||
req: &pbs.ListAuthTokensRequest{ScopeId: "global", Recursive: true},
|
||||
res: &pbs.ListAuthTokensResponse{Items: allTokens},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -184,12 +210,12 @@ func TestList(t *testing.T) {
|
||||
s, err := authtokens.NewService(repoFn, iamRepoFn)
|
||||
require.NoError(t, err, "Couldn't create new user service.")
|
||||
|
||||
got, gErr := s.ListAuthTokens(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scope)), &pbs.ListAuthTokensRequest{ScopeId: tc.scope})
|
||||
got, gErr := s.ListAuthTokens(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetScopeId())), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err), "ListAuthTokens() with scope %q got error %v, wanted %v", tc.scope, gErr, tc.err)
|
||||
assert.True(t, errors.Is(gErr, tc.err), "ListAuthTokens() with scope %q got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
}
|
||||
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "ListAuthTokens() with scope %q got response %q, wanted %q", tc.scope, got, tc.res)
|
||||
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "ListAuthTokens() with scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
@@ -76,17 +77,24 @@ func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*p
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
gl, err := s.listFromRepo(ctx, req.GetScopeId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gl, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Group, 0, len(gl))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Group,
|
||||
Type: resource.Group,
|
||||
}
|
||||
for _, item := range gl {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -310,12 +318,12 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Group, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Group, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gl, err := repo.ListGroups(ctx, scopeId)
|
||||
gl, err := repo.ListGroups(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to list groups: %w", err)
|
||||
}
|
||||
|
||||
@@ -217,6 +217,7 @@ func TestList(t *testing.T) {
|
||||
oWithGroups, pNoGroups := iam.TestScopes(t, iamRepo)
|
||||
var wantOrgGroups []*pb.Group
|
||||
var wantProjGroups []*pb.Group
|
||||
var totalGroups []*pb.Group
|
||||
for i := 0; i < 10; i++ {
|
||||
og := iam.TestGroup(t, conn, oWithGroups.GetPublicId())
|
||||
wantOrgGroups = append(wantOrgGroups, &pb.Group{
|
||||
@@ -228,6 +229,7 @@ func TestList(t *testing.T) {
|
||||
Version: 1,
|
||||
AuthorizedActions: []string{"read", "update", "delete", "add-members", "set-members", "remove-members"},
|
||||
})
|
||||
totalGroups = append(totalGroups, wantOrgGroups[i])
|
||||
pg := iam.TestGroup(t, conn, pWithGroups.GetPublicId())
|
||||
wantProjGroups = append(wantProjGroups, &pb.Group{
|
||||
Id: pg.GetPublicId(),
|
||||
@@ -238,33 +240,55 @@ func TestList(t *testing.T) {
|
||||
Version: 1,
|
||||
AuthorizedActions: []string{"read", "update", "delete", "add-members", "set-members", "remove-members"},
|
||||
})
|
||||
totalGroups = append(totalGroups, wantProjGroups[i])
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
scopeId string
|
||||
res *pbs.ListGroupsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListGroupsRequest
|
||||
res *pbs.ListGroupsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Many Group",
|
||||
scopeId: oWithGroups.GetPublicId(),
|
||||
res: &pbs.ListGroupsResponse{Items: wantOrgGroups},
|
||||
name: "List Many Group",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: oWithGroups.GetPublicId()},
|
||||
res: &pbs.ListGroupsResponse{Items: wantOrgGroups},
|
||||
},
|
||||
{
|
||||
name: "List No Groups",
|
||||
scopeId: oNoGroups.GetPublicId(),
|
||||
res: &pbs.ListGroupsResponse{},
|
||||
name: "List No Groups",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: oNoGroups.GetPublicId()},
|
||||
res: &pbs.ListGroupsResponse{},
|
||||
},
|
||||
{
|
||||
name: "List Many Project Group",
|
||||
scopeId: pWithGroups.GetPublicId(),
|
||||
res: &pbs.ListGroupsResponse{Items: wantProjGroups},
|
||||
name: "List Many Project Group",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: pWithGroups.GetPublicId()},
|
||||
res: &pbs.ListGroupsResponse{Items: wantProjGroups},
|
||||
},
|
||||
{
|
||||
name: "List No Project Groups",
|
||||
scopeId: pNoGroups.GetPublicId(),
|
||||
res: &pbs.ListGroupsResponse{},
|
||||
name: "List No Project Groups",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: pNoGroups.GetPublicId()},
|
||||
res: &pbs.ListGroupsResponse{},
|
||||
},
|
||||
{
|
||||
name: "List proj groups recursively",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: pWithGroups.GetPublicId(), Recursive: true},
|
||||
res: &pbs.ListGroupsResponse{
|
||||
Items: wantProjGroups,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "List org groups recursively",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: oNoGroups.GetPublicId(), Recursive: true},
|
||||
res: &pbs.ListGroupsResponse{
|
||||
Items: wantProjGroups,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "List global groups recursively",
|
||||
req: &pbs.ListGroupsRequest{ScopeId: "global", Recursive: true},
|
||||
res: &pbs.ListGroupsResponse{
|
||||
Items: totalGroups,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -273,12 +297,12 @@ func TestList(t *testing.T) {
|
||||
s, err := groups.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create new group service.")
|
||||
|
||||
got, gErr := s.ListGroups(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListGroupsRequest{ScopeId: tc.scopeId})
|
||||
got, gErr := s.ListGroups(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetScopeId())), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListGroups(%q) got error %v, wanted %v", tc.scopeId, gErr, tc.err)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListGroups(%q) got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListGroups(%q) got response %q, wanted %q", tc.scopeId, got, tc.res)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListGroups(%q) got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/host/static/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/hosts"
|
||||
@@ -112,17 +113,25 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
ul, err := s.listFromRepo(ctx, authResults.Scope.GetId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
finalItems := make([]*pb.HostCatalog, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.HostCatalog,
|
||||
Type: resource.HostCatalog,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -231,12 +240,12 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.HostCatalog, e
|
||||
return toProto(hc), nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.HostCatalog, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.HostCatalog, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListCatalogs(ctx, scopeId)
|
||||
ul, err := repo.ListCatalogs(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -444,8 +453,9 @@ func validateDeleteRequest(req *pbs.DeleteHostCatalogRequest) error {
|
||||
|
||||
func validateListRequest(req *pbs.ListHostCatalogsRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(scope.Project.Prefix(), req.GetScopeId()) {
|
||||
badFields["scope_id"] = "This field must be a valid project scope id."
|
||||
if !handlers.ValidId(scope.Project.Prefix(), req.GetScopeId()) &&
|
||||
!req.GetRecursive() {
|
||||
badFields["scope_id"] = "This field must be a valid project scope ID or the list operation must be recursive."
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
|
||||
|
||||
@@ -195,35 +195,42 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
scopeId string
|
||||
res *pbs.ListHostCatalogsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListHostCatalogsRequest
|
||||
res *pbs.ListHostCatalogsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Some Catalogs",
|
||||
scopeId: pWithCatalogs.GetPublicId(),
|
||||
res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs},
|
||||
name: "List Some Catalogs",
|
||||
req: &pbs.ListHostCatalogsRequest{ScopeId: pWithCatalogs.GetPublicId()},
|
||||
res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs},
|
||||
},
|
||||
{
|
||||
name: "List Other Catalogs",
|
||||
scopeId: pWithOtherCatalogs.GetPublicId(),
|
||||
res: &pbs.ListHostCatalogsResponse{Items: wantOtherCatalogs},
|
||||
name: "List Other Catalogs",
|
||||
req: &pbs.ListHostCatalogsRequest{ScopeId: pWithOtherCatalogs.GetPublicId()},
|
||||
res: &pbs.ListHostCatalogsResponse{Items: wantOtherCatalogs},
|
||||
},
|
||||
{
|
||||
name: "List No Catalogs",
|
||||
scopeId: pNoCatalogs.GetPublicId(),
|
||||
res: &pbs.ListHostCatalogsResponse{},
|
||||
name: "List No Catalogs",
|
||||
req: &pbs.ListHostCatalogsRequest{ScopeId: pNoCatalogs.GetPublicId()},
|
||||
res: &pbs.ListHostCatalogsResponse{},
|
||||
},
|
||||
{
|
||||
name: "Unfound Catalogs",
|
||||
scopeId: scope.Project.Prefix() + "_DoesntExis",
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
name: "Unfound Catalogs",
|
||||
req: &pbs.ListHostCatalogsRequest{ScopeId: scope.Project.Prefix() + "_DoesntExis"},
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
},
|
||||
{
|
||||
name: "Bad scope level",
|
||||
scopeId: scope.Global.String(),
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
name: "Bad scope level (not recursive)",
|
||||
req: &pbs.ListHostCatalogsRequest{ScopeId: scope.Global.String()},
|
||||
err: handlers.ApiErrorWithCode(codes.InvalidArgument),
|
||||
},
|
||||
{
|
||||
name: "List recursively",
|
||||
req: &pbs.ListHostCatalogsRequest{ScopeId: scope.Global.String(), Recursive: true},
|
||||
res: &pbs.ListHostCatalogsResponse{
|
||||
Items: append(wantSomeCatalogs, wantOtherCatalogs...),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -232,12 +239,12 @@ func TestList(t *testing.T) {
|
||||
s, err := host_catalogs.NewService(repoFn, iamRepoFn)
|
||||
require.NoError(err, "Couldn't create new auth_method service.")
|
||||
|
||||
got, gErr := s.ListHostCatalogs(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListHostCatalogsRequest{ScopeId: tc.scopeId})
|
||||
got, gErr := s.ListHostCatalogs(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetScopeId())), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListHostCatalogs() for scope %q got error %v, wanted %v", tc.scopeId, gErr, tc.err)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListHostCatalogs() for scope %q got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.scopeId, got, tc.res)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/roles"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
@@ -81,61 +81,10 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
|
||||
return nil, authResults.Error
|
||||
}
|
||||
|
||||
// Get the list of scope IDs. If it's recursive, check ACLs.
|
||||
var scopeIds []string
|
||||
// This will be used to memoize scope info so we can put the right scope
|
||||
// info for each returned value
|
||||
scopeInfoMap := map[string]*scopes.ScopeInfo{
|
||||
req.GetScopeId(): authResults.Scope,
|
||||
}
|
||||
var err error
|
||||
switch req.GetRecursive() {
|
||||
case true:
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get all scopes recursively
|
||||
scps, err := repo.ListScopesRecursively(ctx, req.GetScopeId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scopeIds = make([]string, 0, len(scps))
|
||||
res := perms.Resource{
|
||||
Type: resource.Role,
|
||||
}
|
||||
// For each scope, see if we have permission to list on that scope
|
||||
for _, scp := range scps {
|
||||
scpId := scp.GetPublicId()
|
||||
// We already checked the incoming ID so we can definitely add it
|
||||
if scpId == authResults.Scope.Id {
|
||||
scopeIds = append(scopeIds, scp.GetPublicId())
|
||||
continue
|
||||
}
|
||||
res.ScopeId = scpId
|
||||
aSet := authResults.FetchActionSetForType(ctx,
|
||||
resource.Role,
|
||||
action.ActionSet{action.List},
|
||||
auth.WithResource(&res),
|
||||
)
|
||||
// We only passed one action in, so anything other than that one
|
||||
// action back should not be included. Assuming it's correct, add
|
||||
// the scope ID for lookup and memoize the scope info if needed.
|
||||
if len(aSet) == 1 && aSet[0] == action.List {
|
||||
scopeIds = append(scopeIds, scpId)
|
||||
if scopeInfoMap[scpId] == nil {
|
||||
scopeInfo := &scopes.ScopeInfo{
|
||||
Id: scp.GetPublicId(),
|
||||
Type: scp.GetType(),
|
||||
Name: scp.GetName(),
|
||||
Description: scp.GetDescription(),
|
||||
}
|
||||
scopeInfoMap[scpId] = scopeInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
scopeIds = []string{req.GetScopeId()}
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items, err := s.listFromRepo(ctx, scopeIds)
|
||||
@@ -145,11 +94,11 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
|
||||
|
||||
finalItems := make([]*pb.Role, 0, len(items))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Role,
|
||||
Type: resource.Role,
|
||||
}
|
||||
for _, item := range items {
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
|
||||
@@ -244,7 +244,14 @@ func TestList(t *testing.T) {
|
||||
res: &pbs.ListRolesResponse{},
|
||||
},
|
||||
{
|
||||
name: "List org role recursively",
|
||||
name: "List proj roles recursively",
|
||||
req: &pbs.ListRolesRequest{ScopeId: pWithRoles.GetPublicId(), Recursive: true},
|
||||
res: &pbs.ListRolesResponse{
|
||||
Items: wantProjRoles,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "List org roles recursively",
|
||||
req: &pbs.ListRolesRequest{ScopeId: oNoRoles.GetPublicId(), Recursive: true},
|
||||
res: &pbs.ListRolesResponse{
|
||||
Items: wantProjRoles,
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/authmethods"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens"
|
||||
@@ -134,17 +135,25 @@ func (s Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*p
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
pl, err := s.listFromRepo(ctx, authResults.Scope.GetId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pl, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
finalItems := make([]*pb.Scope, 0, len(pl))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Scope,
|
||||
Type: resource.Scope,
|
||||
}
|
||||
for _, item := range pl {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -354,22 +363,12 @@ func SortScopes(scps []*pb.Scope) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Scope, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Scope, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var scps []*iam.Scope
|
||||
switch {
|
||||
case scopeId == "global":
|
||||
scps, err = repo.ListOrgs(ctx)
|
||||
case strings.HasPrefix(scopeId, scope.Org.Prefix()):
|
||||
scps, err = repo.ListProjects(ctx, scopeId)
|
||||
default:
|
||||
return nil, handlers.InvalidArgumentErrorf("Error in provided request.",
|
||||
map[string]string{"scope_id": "This field must be 'global' or a valid org scope id."})
|
||||
}
|
||||
scps, err := repo.ListScopes(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to list scopes: %v", err)
|
||||
}
|
||||
|
||||
@@ -340,6 +340,9 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
scopes.SortScopes(wantProjects)
|
||||
|
||||
totalScopes := append(wantOrgs, wantProjects...)
|
||||
scopes.SortScopes(totalScopes)
|
||||
|
||||
cases = []struct {
|
||||
name string
|
||||
scopeId string
|
||||
@@ -359,6 +362,12 @@ func TestList(t *testing.T) {
|
||||
req: &pbs.ListScopesRequest{ScopeId: oWithProjects.GetPublicId()},
|
||||
res: &pbs.ListScopesResponse{Items: wantProjects},
|
||||
},
|
||||
{
|
||||
name: "List Global Recursively",
|
||||
scopeId: scope.Global.String(),
|
||||
req: &pbs.ListScopesRequest{ScopeId: scope.Global.String(), Recursive: true},
|
||||
res: &pbs.ListScopesResponse{Items: totalScopes},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
@@ -82,17 +83,25 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
seslist, err := s.listFromRepo(ctx, authResults.Scope.GetId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seslist, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
finalItems := make([]*pb.Session, 0, len(seslist))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Session,
|
||||
Type: resource.Session,
|
||||
}
|
||||
for _, item := range seslist {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -137,12 +146,12 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Session, error
|
||||
return toProto(sess), nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Session, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Session, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seslist, err := repo.ListSessions(ctx, session.WithScopeId(scopeId))
|
||||
seslist, err := repo.ListSessions(ctx, session.WithScopeIds(scopeIds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -261,8 +270,9 @@ func validateGetRequest(req *pbs.GetSessionRequest) error {
|
||||
|
||||
func validateListRequest(req *pbs.ListSessionsRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(scope.Project.Prefix(), req.GetScopeId()) {
|
||||
badFields["scope_id"] = "This field is required to have a properly formatted project scope id."
|
||||
if !handlers.ValidId(scope.Project.Prefix(), req.GetScopeId()) &&
|
||||
!req.GetRecursive() {
|
||||
badFields["scope_id"] = "This field must be a valid project scope ID or the list operation must be recursive."
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
|
||||
|
||||
@@ -157,16 +157,28 @@ func TestList(t *testing.T) {
|
||||
|
||||
_, pNoSessions := iam.TestScopes(t, iamRepo)
|
||||
o, pWithSessions := iam.TestScopes(t, iamRepo)
|
||||
oOther, pWithOtherSessions := iam.TestScopes(t, iamRepo)
|
||||
|
||||
at := authtoken.TestAuthToken(t, conn, kms, o.GetPublicId())
|
||||
uId := at.GetIamUserId()
|
||||
|
||||
atOther := authtoken.TestAuthToken(t, conn, kms, oOther.GetPublicId())
|
||||
uIdOther := atOther.GetIamUserId()
|
||||
|
||||
hc := static.TestCatalogs(t, conn, pWithSessions.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
tar := target.TestTcpTarget(t, conn, pWithSessions.GetPublicId(), "test", target.WithHostSets([]string{hs.GetPublicId()}))
|
||||
|
||||
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 := target.TestTcpTarget(t, conn, pWithOtherSessions.GetPublicId(), "test", target.WithHostSets([]string{hsOther.GetPublicId()}))
|
||||
|
||||
var wantSession []*pb.Session
|
||||
var totalSession []*pb.Session
|
||||
for i := 0; i < 10; i++ {
|
||||
sess := session.TestSession(t, conn, wrap, session.ComposedOf{
|
||||
UserId: uId,
|
||||
@@ -200,6 +212,41 @@ func TestList(t *testing.T) {
|
||||
Type: target.TcpSubType.String(),
|
||||
AuthorizedActions: []string{"read", "cancel"},
|
||||
})
|
||||
|
||||
totalSession = append(totalSession, wantSession[i])
|
||||
|
||||
sess = session.TestSession(t, conn, wrap, session.ComposedOf{
|
||||
UserId: uIdOther,
|
||||
HostId: hOther.GetPublicId(),
|
||||
TargetId: tarOther.GetPublicId(),
|
||||
HostSetId: hsOther.GetPublicId(),
|
||||
AuthTokenId: atOther.GetPublicId(),
|
||||
ScopeId: pWithOtherSessions.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
})
|
||||
|
||||
status, states = convertStates(sess.States)
|
||||
|
||||
totalSession = append(totalSession, &pb.Session{
|
||||
Id: sess.GetPublicId(),
|
||||
ScopeId: pWithOtherSessions.GetPublicId(),
|
||||
AuthTokenId: atOther.GetPublicId(),
|
||||
UserId: atOther.GetIamUserId(),
|
||||
TargetId: sess.TargetId,
|
||||
Endpoint: sess.Endpoint,
|
||||
HostSetId: sess.HostSetId,
|
||||
HostId: sess.HostId,
|
||||
Version: sess.Version,
|
||||
UpdatedTime: sess.UpdateTime.GetTimestamp(),
|
||||
CreatedTime: sess.CreateTime.GetTimestamp(),
|
||||
ExpirationTime: sess.ExpirationTime.GetTimestamp(),
|
||||
Scope: &scopes.ScopeInfo{Id: pWithOtherSessions.GetPublicId(), Type: scope.Project.String()},
|
||||
Status: status,
|
||||
States: states,
|
||||
Certificate: sess.Certificate,
|
||||
Type: target.TcpSubType.String(),
|
||||
AuthorizedActions: []string{"read", "cancel"},
|
||||
})
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
@@ -218,6 +265,11 @@ func TestList(t *testing.T) {
|
||||
req: &pbs.ListSessionsRequest{ScopeId: pNoSessions.GetPublicId()},
|
||||
res: &pbs.ListSessionsResponse{},
|
||||
},
|
||||
{
|
||||
name: "List Sessions Recursively",
|
||||
req: &pbs.ListSessionsRequest{ScopeId: scope.Global.String(), Recursive: true},
|
||||
res: &pbs.ListSessionsResponse{Items: totalSession},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
@@ -122,17 +123,25 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
ul, err := s.listFromRepo(ctx, authResults.Scope.GetId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.iamRepoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
finalItems := make([]*pb.Target, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Target,
|
||||
Type: resource.Target,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -660,12 +669,12 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.Target, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Target, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListTargets(ctx, target.WithScopeId(scopeId))
|
||||
ul, err := repo.ListTargets(ctx, target.WithScopeIds(scopeIds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -931,8 +940,9 @@ func validateDeleteRequest(req *pbs.DeleteTargetRequest) error {
|
||||
|
||||
func validateListRequest(req *pbs.ListTargetsRequest) error {
|
||||
badFields := map[string]string{}
|
||||
if !handlers.ValidId(scope.Project.Prefix(), req.GetScopeId()) {
|
||||
badFields["scope_id"] = "This field is required to have a properly formatted project scope id."
|
||||
if !handlers.ValidId(scope.Project.Prefix(), req.GetScopeId()) &&
|
||||
!req.GetRecursive() {
|
||||
badFields["scope_id"] = "This field must be a valid project scope ID or the list operation must be recursive."
|
||||
}
|
||||
if len(badFields) > 0 {
|
||||
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
|
||||
|
||||
@@ -143,12 +143,17 @@ func TestList(t *testing.T) {
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
_, projNoTar := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
_, projNoTar := iam.TestScopes(t, iamRepo)
|
||||
_, proj := iam.TestScopes(t, iamRepo)
|
||||
_, otherProj := iam.TestScopes(t, iamRepo)
|
||||
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)
|
||||
otherHss := static.TestSets(t, conn, otherHc.GetPublicId(), 2)
|
||||
|
||||
var wantTars []*pb.Target
|
||||
var totalTars []*pb.Target
|
||||
for i := 0; i < 5; i++ {
|
||||
name := fmt.Sprintf("tar%d", i)
|
||||
tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), name, target.WithHostSets([]string{hss[0].GetPublicId(), hss[1].GetPublicId()}))
|
||||
@@ -166,23 +171,44 @@ func TestList(t *testing.T) {
|
||||
SessionConnectionLimit: wrapperspb.Int32(1),
|
||||
AuthorizedActions: targets.IdActions.Strings(),
|
||||
})
|
||||
totalTars = append(totalTars, wantTars[i])
|
||||
tar = target.TestTcpTarget(t, conn, otherProj.GetPublicId(), name, target.WithHostSets([]string{otherHss[0].GetPublicId(), otherHss[1].GetPublicId()}))
|
||||
totalTars = append(totalTars, &pb.Target{
|
||||
Id: tar.GetPublicId(),
|
||||
ScopeId: otherProj.GetPublicId(),
|
||||
Name: wrapperspb.String(name),
|
||||
Scope: &scopes.ScopeInfo{Id: otherProj.GetPublicId(), Type: scope.Project.String()},
|
||||
CreatedTime: tar.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: tar.GetUpdateTime().GetTimestamp(),
|
||||
Version: tar.GetVersion(),
|
||||
Type: target.TcpTargetType.String(),
|
||||
Attributes: new(structpb.Struct),
|
||||
SessionMaxSeconds: wrapperspb.UInt32(28800),
|
||||
SessionConnectionLimit: wrapperspb.Int32(1),
|
||||
AuthorizedActions: targets.IdActions.Strings(),
|
||||
})
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
scopeId string
|
||||
res *pbs.ListTargetsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListTargetsRequest
|
||||
res *pbs.ListTargetsResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "List Many Host Sets",
|
||||
scopeId: proj.GetPublicId(),
|
||||
res: &pbs.ListTargetsResponse{Items: wantTars},
|
||||
name: "List Many Targets",
|
||||
req: &pbs.ListTargetsRequest{ScopeId: proj.GetPublicId()},
|
||||
res: &pbs.ListTargetsResponse{Items: wantTars},
|
||||
},
|
||||
{
|
||||
name: "List No Host Sets",
|
||||
scopeId: projNoTar.GetPublicId(),
|
||||
res: &pbs.ListTargetsResponse{},
|
||||
name: "List No Targets",
|
||||
req: &pbs.ListTargetsRequest{ScopeId: projNoTar.GetPublicId()},
|
||||
res: &pbs.ListTargetsResponse{},
|
||||
},
|
||||
{
|
||||
name: "List Targets Recursively",
|
||||
req: &pbs.ListTargetsRequest{ScopeId: scope.Global.String(), Recursive: true},
|
||||
res: &pbs.ListTargetsResponse{Items: totalTars},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -191,12 +217,12 @@ func TestList(t *testing.T) {
|
||||
s, err := testService(t, conn, kms, wrapper)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
got, gErr := s.ListTargets(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListTargetsRequest{ScopeId: tc.scopeId})
|
||||
got, gErr := s.ListTargets(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetScopeId())), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListTargets(%q) got error %v, wanted %v", tc.scopeId, gErr, tc.err)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListTargets(%q) got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListTargets(%q) scope %q, got response %q, wanted %q", tc.name, tc.scopeId, got, tc.res)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListTargets(%q) scope %q, got response %q, wanted %q", tc.name, tc.req.GetScopeId(), got, tc.res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
@@ -77,17 +78,24 @@ func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
ul, err := s.listFromRepo(ctx, req.GetScopeId())
|
||||
|
||||
scopeIds, scopeInfoMap, err := scopeids.GetScopeIds(
|
||||
ctx, s.repoFn, authResults, req.GetScopeId(), req.GetRecursive())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.User, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.User,
|
||||
Type: resource.User,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = authResults.Scope
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) > 0 {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -311,12 +319,12 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, orgId string) ([]*pb.User, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.User, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ul, err := repo.ListUsers(ctx, orgId)
|
||||
ul, err := repo.ListUsers(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -122,7 +122,24 @@ func TestList(t *testing.T) {
|
||||
oNoUsers, _ := iam.TestScopes(t, repo)
|
||||
oWithUsers, _ := iam.TestScopes(t, repo)
|
||||
|
||||
s, err := users.NewService(repoFn)
|
||||
|
||||
var wantUsers []*pb.User
|
||||
|
||||
// Populate expected values for recursive test
|
||||
var totalUsers []*pb.User
|
||||
ctx := auth.DisabledAuthTestContext(auth.WithScopeId("global"))
|
||||
anon, err := s.GetUser(ctx, &pbs.GetUserRequest{Id: "u_anon"})
|
||||
require.NoError(t, err)
|
||||
totalUsers = append(totalUsers, anon.GetItem())
|
||||
authUser, err := s.GetUser(ctx, &pbs.GetUserRequest{Id: "u_auth"})
|
||||
require.NoError(t, err)
|
||||
totalUsers = append(totalUsers, authUser.GetItem())
|
||||
recovery, err := s.GetUser(ctx, &pbs.GetUserRequest{Id: "u_recovery"})
|
||||
require.NoError(t, err)
|
||||
totalUsers = append(totalUsers, recovery.GetItem())
|
||||
|
||||
// Add new users
|
||||
for i := 0; i < 10; i++ {
|
||||
newU, err := iam.NewUser(oWithUsers.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
@@ -139,6 +156,11 @@ func TestList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// Populate these users into the total
|
||||
ctx = auth.DisabledAuthTestContext(auth.WithScopeId(oWithUsers.GetPublicId()))
|
||||
usersInOrg, err := s.ListUsers(ctx, &pbs.ListUsersRequest{ScopeId: oWithUsers.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
totalUsers = append(totalUsers, usersInOrg.GetItems()...)
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListUsersRequest
|
||||
@@ -155,11 +177,20 @@ func TestList(t *testing.T) {
|
||||
req: &pbs.ListUsersRequest{ScopeId: oNoUsers.GetPublicId()},
|
||||
res: &pbs.ListUsersResponse{},
|
||||
},
|
||||
{
|
||||
name: "List Recursively in Org",
|
||||
req: &pbs.ListUsersRequest{ScopeId: oWithUsers.GetPublicId(), Recursive: true},
|
||||
res: &pbs.ListUsersResponse{Items: wantUsers},
|
||||
},
|
||||
{
|
||||
name: "List Recursively in Global",
|
||||
req: &pbs.ListUsersRequest{ScopeId: "global", Recursive: true},
|
||||
res: &pbs.ListUsersResponse{Items: totalUsers},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
s, err := users.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create new user service.")
|
||||
|
||||
got, gErr := s.ListUsers(auth.DisabledAuthTestContext(auth.WithScopeId(tc.req.GetScopeId())), tc.req)
|
||||
|
||||
Reference in new issue
Block a user