When using a disabled verifier context we were faking scope information. Due to other recent changes, this was now causing test errors. This adjusts that flow to actually query the DB, fixing tests and making them more like real code.
180 lines
6.0 KiB
Go
180 lines
6.0 KiB
Go
package scopeids
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/hashicorp/boundary/internal/auth"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
|
|
"github.com/hashicorp/boundary/internal/iam"
|
|
"github.com/hashicorp/boundary/internal/perms"
|
|
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
|
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
|
"github.com/hashicorp/boundary/internal/types/action"
|
|
"github.com/hashicorp/boundary/internal/types/resource"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
)
|
|
|
|
// GetListingScopeIds, 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 GetListingScopeIds(
|
|
// 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,
|
|
// The type of resource we are listing
|
|
typ resource.Type,
|
|
// Whether or not the search should be recursive
|
|
recursive bool,
|
|
// Whether to only return scopes with exact permissions, or whether parent
|
|
// scopes with appropriate permissions are sufficient
|
|
directOnly bool) ([]string, map[string]*scopes.ScopeInfo, error) {
|
|
const op = "GetListingScopeIds"
|
|
|
|
// Validation
|
|
switch {
|
|
case typ == resource.Unknown:
|
|
return nil, nil, errors.New(errors.InvalidParameter, op, "unknown resource")
|
|
case repoFn == nil:
|
|
return nil, nil, errors.New(errors.InvalidParameter, op, "nil iam repo")
|
|
case rootScopeId == "":
|
|
return nil, nil, errors.New(errors.InvalidParameter, op, "missing root scope id")
|
|
case authResults.Scope == nil:
|
|
return nil, nil, errors.New(errors.InvalidParameter, op, "nil scope in auth results")
|
|
}
|
|
|
|
// Base case: if not recursive, return the scope we were given and the
|
|
// already-looked-up info
|
|
if !recursive {
|
|
return []string{authResults.Scope.Id}, map[string]*scopes.ScopeInfo{authResults.Scope.Id: authResults.Scope}, nil
|
|
}
|
|
|
|
// 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{}
|
|
|
|
repo, err := repoFn()
|
|
if err != nil {
|
|
return nil, 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, nil, err
|
|
}
|
|
|
|
res := perms.Resource{
|
|
Type: typ,
|
|
}
|
|
// For each scope, see if we have permission to list that type in that
|
|
// scope
|
|
var deferredScopes []*iam.Scope
|
|
// Store whether global has list permission
|
|
var globalHasList bool
|
|
for _, scp := range scps {
|
|
scpId := scp.GetPublicId()
|
|
res.ScopeId = scpId
|
|
aSet := authResults.FetchActionSetForType(ctx,
|
|
// This is overridden by WithResource
|
|
resource.Unknown,
|
|
action.ActionSet{action.List},
|
|
auth.WithResource(&res),
|
|
)
|
|
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 ording our map
|
|
// lookup might fail.
|
|
if !directOnly {
|
|
deferredScopes = append(deferredScopes, scp)
|
|
}
|
|
case 1:
|
|
if aSet[0] != action.List {
|
|
return nil, nil, errors.New(errors.Internal, op, "unexpected action in set")
|
|
}
|
|
if scopeInfoMap[scpId] == nil {
|
|
scopeInfo := &scopes.ScopeInfo{
|
|
Id: scp.GetPublicId(),
|
|
Type: scp.GetType(),
|
|
Name: scp.GetName(),
|
|
Description: scp.GetDescription(),
|
|
ParentScopeId: scp.GetParentId(),
|
|
}
|
|
scopeInfoMap[scpId] = scopeInfo
|
|
}
|
|
if scpId == scope.Global.String() {
|
|
globalHasList = true
|
|
}
|
|
default:
|
|
return nil, nil, errors.New(errors.Internal, op, "unexpected number of actions back in set")
|
|
}
|
|
}
|
|
|
|
// Now go through these 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 || scopeInfoMap[scp.GetParentId()] != nil {
|
|
scpId := scp.GetPublicId()
|
|
if scopeInfoMap[scpId] == nil {
|
|
scopeInfo := &scopes.ScopeInfo{
|
|
Id: scp.GetPublicId(),
|
|
Type: scp.GetType(),
|
|
Name: scp.GetName(),
|
|
Description: scp.GetDescription(),
|
|
ParentScopeId: scp.GetParentId(),
|
|
}
|
|
scopeInfoMap[scpId] = scopeInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have nothing in scopeInfoMap at this point, we aren't authorized
|
|
// anywhere so return 403.
|
|
if len(scopeInfoMap) == 0 {
|
|
return nil, nil, handlers.ForbiddenError()
|
|
}
|
|
|
|
// Now elide out any that aren't under the root scope ID
|
|
elideScopes := make([]string, 0, len(scopeInfoMap))
|
|
for scpId, scp := range scopeInfoMap {
|
|
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.GetParentScopeId():
|
|
default:
|
|
elideScopes = append(elideScopes, scpId)
|
|
}
|
|
}
|
|
|
|
for _, scpId := range elideScopes {
|
|
delete(scopeInfoMap, scpId)
|
|
}
|
|
|
|
scopeIds := make([]string, 0, len(scopeInfoMap))
|
|
for k := range scopeInfoMap {
|
|
scopeIds = append(scopeIds, k)
|
|
}
|
|
|
|
return scopeIds, scopeInfoMap, nil
|
|
}
|