feat(auth): Get all authorised scope info for a given list request
This commit is contained in:
1 parent
9ee5a9f713
commit
ccb17df01a
1 file changed
+121
@@ -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"
|
||||
@@ -694,3 +695,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
|
||||
}
|
||||
Reference in new issue
Block a user