Update targets to new listing method (#2045)

This uses the prefetch method to fetch resource IDs and validate them
for authz by the user before then fetching the full list of targets.
pull/2049/head
Jeff Mitchell 4 years ago committed by Timothy Messier
parent 21b0d584ad
commit 4cf2a87e8d
No known key found for this signature in database
GPG Key ID: EFD2F184F7600572

@ -366,7 +366,7 @@ func Test_MakeInactive_MakePrivate_MakePublic(t *testing.T) {
}(),
version: 1,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "certificate signed by unknown authority",
wantErrContains: "certificate",
},
}

@ -2,6 +2,7 @@ package scopeids
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/boundary"
"github.com/hashicorp/boundary/internal/errors"
@ -265,7 +266,7 @@ func filterAuthorizedResourceIds(
}
res := perms.Resource{
Type: resource.Session,
Type: input.Type,
}
// Now run authorization checks against each so we know if there is a point
@ -285,6 +286,9 @@ func filterAuthorizedResourceIds(
}
}
if output.ScopeResourceMap[scopeId] == nil {
return errors.New(ctx, errors.Internal, op, fmt.Sprintf("scope id %s returned from fetching authz protected entities not found in scope resource map", scopeId))
}
if output.ScopeResourceMap[scopeId].Resources == nil {
output.ScopeResourceMap[scopeId].Resources = make(map[string]ResourceInfo)
}

@ -146,6 +146,8 @@ var _ pbs.TargetServiceServer = Service{}
// ListTargets implements the interface pbs.TargetServiceServer.
func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (*pbs.ListTargetsResponse, error) {
const op = "targets.(Service).ListSessions"
if err := validateListRequest(req); err != nil {
return nil, err
}
@ -163,17 +165,34 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
}
}
scopeIds, scopeInfoMap, err := scopeids.GetListingScopeIds(
ctx, s.iamRepoFn, authResults, req.GetScopeId(), resource.Target, req.GetRecursive())
repo, err := s.repoFn()
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.Target,
Recursive: req.GetRecursive(),
AuthzProtectedEntityProvider: repo,
ActionSet: IdActions,
},
)
if err != nil {
return nil, err
}
// If no scopes match, return an empty response
if len(scopeIds) == 0 {
if len(scopeResourceInfo.ScopeIds) == 0 ||
len(scopeResourceInfo.ResourceIds) == 0 {
return &pbs.ListTargetsResponse{}, nil
}
tl, err := s.listFromRepo(ctx, scopeIds)
tl, err := s.listFromRepo(ctx, scopeResourceInfo.ResourceIds)
if err != nil {
return nil, err
}
@ -190,21 +209,14 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
Type: resource.Target,
}
for _, item := range tl {
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
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(scopeInfoMap[item.GetScopeId()]))
outputOpts = append(outputOpts, handlers.WithScope(scopeResourceInfo.ScopeResourceMap[item.GetScopeId()].ScopeInfo))
}
if outputFields.Has(globals.AuthorizedActionsField) {
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(scopeResourceInfo.ScopeResourceMap[item.GetScopeId()].Resources[item.GetPublicId()].AuthorizedActions.Strings()))
}
item, err := toProto(ctx, item, nil, nil, outputOpts...)
@ -1346,12 +1358,12 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
return rows > 0, nil
}
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]target.Target, error) {
func (s Service) listFromRepo(ctx context.Context, targetIds []string) ([]target.Target, error) {
repo, err := s.repoFn()
if err != nil {
return nil, err
}
ul, err := repo.ListTargets(ctx, target.WithScopeIds(scopeIds))
ul, err := repo.ListTargets(ctx, target.WithTargetIds(targetIds))
if err != nil {
return nil, err
}

@ -220,7 +220,7 @@ func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, sco
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scopes given")
case 1:
inClauseCnt += 1
where, args = fmt.Sprintf("where scope_id = @%d", inClauseCnt), append(args, sql.Named("1", scopeIds[0]))
where, args = fmt.Sprintf("where scope_id = @%d", inClauseCnt), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), scopeIds[0]))
default:
idsInClause := make([]string, 0, len(scopeIds))
for _, id := range scopeIds {

@ -35,6 +35,7 @@ type options struct {
WithSessionConnectionLimit int32
WithPublicId string
WithWorkerFilter string
WithTargetIds []string
}
func getDefaultOptions() options {
@ -161,3 +162,10 @@ func WithWorkerFilter(filter string) Option {
o.WithWorkerFilter = filter
}
}
// WithTargetIds provides an option to search by specific target IDs
func WithTargetIds(with []string) Option {
return func(o *options) {
o.WithTargetIds = with
}
}

@ -43,5 +43,11 @@ final (action, library_id) as (
)
select * from final
order by action, library_id;
`
targetPublicIdList = `
select public_id, scope_id from target
%s
;
`
)

@ -2,14 +2,17 @@ package target
import (
"context"
"database/sql"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/boundary"
"github.com/hashicorp/boundary/internal/db"
dbcommon "github.com/hashicorp/boundary/internal/db/common"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/hashicorp/boundary/internal/types/scope"
)
// Cloneable provides a cloning interface
@ -138,21 +141,96 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op
return subtype, hostSources, credSources, nil
}
// FetchAuthzProtectedEntitiesByScope implements boundary.AuthzProtectedEntityProvider
func (r *Repository) FetchAuthzProtectedEntitiesByScope(ctx context.Context, scopeIds []string) (map[string][]boundary.AuthzProtectedEntity, error) {
const op = "target.(Repository).FetchAuthzProtectedEntitiesByScope"
var where string
var args []interface{}
inClauseCnt := 0
switch len(scopeIds) {
case 0:
return nil, errors.New(ctx, errors.InvalidParameter, op, "no scopes given")
case 1:
if scopeIds[0] != scope.Global.String() {
inClauseCnt += 1
where, args = fmt.Sprintf("where scope_id = @%d", inClauseCnt), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), scopeIds[0]))
}
default:
idsInClause := make([]string, 0, len(scopeIds))
for _, id := range scopeIds {
inClauseCnt += 1
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
}
where = fmt.Sprintf("where scope_id in (%s)", strings.Join(idsInClause, ","))
}
q := targetPublicIdList
query := fmt.Sprintf(q, where)
rows, err := r.reader.Query(ctx, query, args)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
defer rows.Close()
targetsMap := map[string][]boundary.AuthzProtectedEntity{}
for rows.Next() {
var tv targetView
if err := r.reader.ScanRows(ctx, rows, &tv); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("scan row failed"))
}
targetsMap[tv.GetScopeId()] = append(targetsMap[tv.GetScopeId()], tv)
}
return targetsMap, nil
}
// ListTargets in targets in a scope. Supports the WithScopeId, WithLimit, WithType options.
func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, error) {
const op = "target.(Repository).ListTargets"
opts := GetOpts(opt...)
if len(opts.WithScopeIds) == 0 && opts.WithUserId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "must specify either scope id or user id")
if len(opts.WithScopeIds) == 0 && opts.WithUserId == "" && len(opts.WithTargetIds) == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, "must specify either scope ids, target ids, or user id")
}
// TODO (jimlambrt 8/2020) - implement WithUserId() optional filtering.
var where []string
var args []interface{}
if len(opts.WithScopeIds) != 0 {
where, args = append(where, "scope_id in (?)"), append(args, opts.WithScopeIds)
inClauseCnt := 0
switch len(opts.WithScopeIds) {
case 0:
case 1:
inClauseCnt += 1
where, args = append(where, fmt.Sprintf("scope_id = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithScopeIds[0]))
default:
idsInClause := make([]string, 0, len(opts.WithScopeIds))
for _, id := range opts.WithScopeIds {
inClauseCnt += 1
idsInClause, args = append(idsInClause, fmt.Sprintf("@%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), id))
}
where = append(where, fmt.Sprintf("scope_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 != "" {
where, args = append(where, "type = ?"), append(args, opts.WithType.String())
inClauseCnt += 1
where, args = append(where, fmt.Sprintf("type = @%d", inClauseCnt)), append(args, sql.Named(fmt.Sprintf("%d", inClauseCnt), opts.WithType.String()))
}
var foundTargets []*targetView

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/boundary"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/oplog"
@ -44,6 +45,8 @@ const (
targetsViewDefaultTable = "target_all_subtypes"
)
var _ boundary.AuthzProtectedEntity = (*targetView)(nil)
// targetView provides a common way to return targets regardless of their
// underlying type.
type targetView struct {
@ -77,6 +80,22 @@ func (t *targetView) SetTableName(n string) {
}
}
// GetPublicId satisfies boundary.AuthzProtectedEntity
func (t targetView) GetPublicId() string {
return t.PublicId
}
// GetScopeId satisfies boundary.AuthzProtectedEntity
func (t targetView) GetScopeId() string {
return t.ScopeId
}
// GetUserId satisfies boundary.AuthzProtectedEntity; targets are not associated
// with a user ID so this always returns an empty string
func (t targetView) GetUserId() string {
return ""
}
func (t *targetView) Subtype() subtypes.Subtype {
return subtypes.Subtype(t.Type)
}

Loading…
Cancel
Save