Add Output Fields support (#1192)
This commit is contained in:
1 parent
1948984ca2
commit
22d5c88a0b
84 files changed
+5688
-2122
No files matched your search
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/accounts"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/authmethods"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets"
|
||||
@@ -228,6 +229,18 @@ func wrapHandlerWithCommonFuncs(h http.Handler, c *Controller, props HandlerProp
|
||||
requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = auth.GetTokenFromRequest(c.logger, c.kms, r)
|
||||
ctx = auth.NewVerifierContext(ctx, c.logger, c.IamRepoFn, c.AuthTokenRepoFn, c.ServersRepoFn, c.kms, requestInfo)
|
||||
|
||||
// Add general request information to the context. The information from
|
||||
// the auth verifier context is pretty specifically curated to
|
||||
// authentication/authorization verification so this is more
|
||||
// general-purpose.
|
||||
//
|
||||
// We could use requests.NewRequestContext but this saves an immediate
|
||||
// lookup.
|
||||
ctx = context.WithValue(ctx, requests.ContextRequestInformationKey, &requests.RequestContext{
|
||||
Path: r.URL.Path,
|
||||
Method: r.Method,
|
||||
})
|
||||
|
||||
// Set the context back on the request
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
oidcstore "github.com/hashicorp/boundary/internal/auth/oidc/store"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/accounts"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
@@ -120,22 +122,45 @@ func (s Service) ListAccounts(ctx context.Context, req *pbs.ListAccountsRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ul) == 0 {
|
||||
return &pbs.ListAccountsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Account, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
|
||||
res := perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Account,
|
||||
Pin: req.GetAuthMethodId(),
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = authResults.Scope
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions[auth.SubtypeFromId(item.Id)], auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) == 0 {
|
||||
for _, acct := range ul {
|
||||
res.Id = acct.GetPublicId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, acct.GetPublicId(), IdActions[auth.SubtypeFromId(acct.GetPublicId())], 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(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, acct, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This comes last so that we can use item fields in the filter after
|
||||
// the allowed fields are populated above
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -145,56 +170,119 @@ func (s Service) ListAccounts(ctx context.Context, req *pbs.ListAccountsRequest)
|
||||
|
||||
// GetAccount implements the interface pbs.AccountServiceServer.
|
||||
func (s Service) GetAccount(ctx context.Context, req *pbs.GetAccountRequest) (*pbs.GetAccountResponse, error) {
|
||||
const op = "accounts.(Service).GetAccount"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Read)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
acct, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
return &pbs.GetAccountResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, acct.GetPublicId(), IdActions[auth.SubtypeFromId(acct.GetPublicId())]).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, acct, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetAccountResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateAccount implements the interface pbs.AccountServiceServer.
|
||||
func (s Service) CreateAccount(ctx context.Context, req *pbs.CreateAccountRequest) (*pbs.CreateAccountResponse, error) {
|
||||
const op = "accounts.(Service).CreateAccount"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetItem().GetAuthMethodId(), action.Create)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.createInRepo(ctx, authMeth, req.GetItem())
|
||||
acct, err := s.createInRepo(ctx, authMeth, req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(req.GetItem().GetAuthMethodId())]).Strings()
|
||||
return &pbs.CreateAccountResponse{Item: u, Uri: fmt.Sprintf("accounts/%s", u.GetId())}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, acct.GetPublicId(), IdActions[auth.SubtypeFromId(acct.GetPublicId())]).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, acct, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateAccountResponse{Item: item, Uri: fmt.Sprintf("accounts/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateAccount implements the interface pbs.AccountServiceServer.
|
||||
func (s Service) UpdateAccount(ctx context.Context, req *pbs.UpdateAccountRequest) (*pbs.UpdateAccountResponse, error) {
|
||||
const op = "accounts.(Service).UpdateAccount"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authMeth, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.Update)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), authMeth.GetPublicId(), req)
|
||||
acct, err := s.updateInRepo(ctx, authResults.Scope.GetId(), authMeth.GetPublicId(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
return &pbs.UpdateAccountResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, acct.GetPublicId(), IdActions[auth.SubtypeFromId(acct.GetPublicId())]).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, acct, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateAccountResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteAccount implements the interface pbs.AccountServiceServer.
|
||||
@@ -215,41 +303,83 @@ func (s Service) DeleteAccount(ctx context.Context, req *pbs.DeleteAccountReques
|
||||
|
||||
// ChangePassword implements the interface pbs.AccountServiceServer.
|
||||
func (s Service) ChangePassword(ctx context.Context, req *pbs.ChangePasswordRequest) (*pbs.ChangePasswordResponse, error) {
|
||||
const op = "accounts.(Service).ChangePassword"
|
||||
|
||||
if err := validateChangePasswordRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.ChangePassword)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.changePasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetCurrentPassword(), req.GetNewPassword())
|
||||
acct, err := s.changePasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetCurrentPassword(), req.GetNewPassword())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
return &pbs.ChangePasswordResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, acct.GetPublicId(), IdActions[auth.SubtypeFromId(acct.GetPublicId())]).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, acct, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.ChangePasswordResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetPassword implements the interface pbs.AccountServiceServer.
|
||||
func (s Service) SetPassword(ctx context.Context, req *pbs.SetPasswordRequest) (*pbs.SetPasswordResponse, error) {
|
||||
const op = "accounts.(Service).SetPassword"
|
||||
|
||||
if err := validateSetPasswordRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, authResults := s.parentAndAuthResult(ctx, req.GetId(), action.SetPassword)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.setPasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetPassword())
|
||||
acct, err := s.setPasswordInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetVersion(), req.GetPassword())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
return &pbs.SetPasswordResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, acct.GetPublicId(), IdActions[auth.SubtypeFromId(acct.GetPublicId())]).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, acct, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetPasswordResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Account, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (auth.Account, error) {
|
||||
var acct auth.Account
|
||||
switch auth.SubtypeFromId(id) {
|
||||
case auth.PasswordSubtype:
|
||||
@@ -281,7 +411,7 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Account, error
|
||||
default:
|
||||
return nil, handlers.NotFoundErrorf("Unrecognized id.")
|
||||
}
|
||||
return toProto(acct)
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
func (s Service) createPwInRepo(ctx context.Context, am auth.AuthMethod, item *pb.Account) (*password.Account, error) {
|
||||
@@ -367,7 +497,7 @@ func (s Service) createOidcInRepo(ctx context.Context, am auth.AuthMethod, item
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, am auth.AuthMethod, item *pb.Account) (*pb.Account, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, am auth.AuthMethod, item *pb.Account) (auth.Account, error) {
|
||||
const op = "accounts.(Service).createInRepo"
|
||||
if item == nil {
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing item")
|
||||
@@ -393,7 +523,7 @@ func (s Service) createInRepo(ctx context.Context, am auth.AuthMethod, item *pb.
|
||||
}
|
||||
out = am
|
||||
}
|
||||
return toProto(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updatePwInRepo(ctx context.Context, scopeId, authMethId, id string, mask []string, item *pb.Account) (*password.Account, error) {
|
||||
@@ -463,7 +593,7 @@ func (s Service) updateOidcInRepo(ctx context.Context, scopeId, amId, id string,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, authMethodId string, req *pbs.UpdateAccountRequest) (*pb.Account, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, authMethodId string, req *pbs.UpdateAccountRequest) (auth.Account, error) {
|
||||
const op = "accounts.(Service).updateInRepo"
|
||||
var out auth.Account
|
||||
switch auth.SubtypeFromId(req.GetId()) {
|
||||
@@ -486,7 +616,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, authMethodId string,
|
||||
}
|
||||
out = a
|
||||
}
|
||||
return toProto(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
|
||||
@@ -516,46 +646,40 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, authMethodId string) ([]*pb.Account, error) {
|
||||
var outUl []*pb.Account
|
||||
func (s Service) listFromRepo(ctx context.Context, authMethodId string) ([]auth.Account, error) {
|
||||
const op = "accounts.(Service).listFromRepo"
|
||||
|
||||
var outUl []auth.Account
|
||||
switch auth.SubtypeFromId(authMethodId) {
|
||||
case auth.PasswordSubtype:
|
||||
pwRepo, err := s.pwRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
pwl, err := pwRepo.ListAccounts(ctx, authMethodId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
for _, u := range pwl {
|
||||
ou, err := toProto(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outUl = append(outUl, ou)
|
||||
for _, a := range pwl {
|
||||
outUl = append(outUl, a)
|
||||
}
|
||||
case auth.OidcSubtype:
|
||||
oidcRepo, err := s.oidcRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
oidcl, err := oidcRepo.ListAccounts(ctx, authMethodId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
for _, u := range oidcl {
|
||||
ou, err := toProto(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outUl = append(outUl, ou)
|
||||
for _, a := range oidcl {
|
||||
outUl = append(outUl, a)
|
||||
}
|
||||
}
|
||||
return outUl, nil
|
||||
}
|
||||
|
||||
func (s Service) changePasswordInRepo(ctx context.Context, scopeId, id string, version uint32, currentPassword, newPassword string) (*pb.Account, error) {
|
||||
func (s Service) changePasswordInRepo(ctx context.Context, scopeId, id string, version uint32, currentPassword, newPassword string) (auth.Account, error) {
|
||||
const op = "account.(Service).changePasswordInRepo"
|
||||
repo, err := s.pwRepoFn()
|
||||
if err != nil {
|
||||
@@ -578,10 +702,10 @@ func (s Service) changePasswordInRepo(ctx context.Context, scopeId, id string, v
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.PermissionDenied, "Failed to change password.")
|
||||
}
|
||||
return toProto(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) setPasswordInRepo(ctx context.Context, scopeId, id string, version uint32, pw string) (*pb.Account, error) {
|
||||
func (s Service) setPasswordInRepo(ctx context.Context, scopeId, id string, version uint32, pw string) (auth.Account, error) {
|
||||
const op = "accounts.(Service).setPasswordInRepo"
|
||||
|
||||
repo, err := s.pwRepoFn()
|
||||
@@ -599,7 +723,7 @@ func (s Service) setPasswordInRepo(ctx context.Context, scopeId, id string, vers
|
||||
}
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return toProto(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (auth.AuthMethod, auth.VerifyResults) {
|
||||
@@ -677,30 +801,61 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
return authMeth, auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in auth.Account) (*pb.Account, error) {
|
||||
out := pb.Account{
|
||||
Id: in.GetPublicId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
AuthMethodId: in.GetAuthMethodId(),
|
||||
Version: in.GetVersion(),
|
||||
func toProto(ctx context.Context, in auth.Account, opt ...handlers.Option) (*pb.Account, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building account proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Account{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if outputFields.Has(globals.AuthMethodIdField) {
|
||||
out.AuthMethodId = in.GetAuthMethodId()
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
switch i := in.(type) {
|
||||
case *password.Account:
|
||||
out.Type = auth.PasswordSubtype.String()
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = auth.PasswordSubtype.String()
|
||||
}
|
||||
if !outputFields.Has(globals.AttributesField) {
|
||||
break
|
||||
}
|
||||
st, err := handlers.ProtoToStruct(&pb.PasswordAccountAttributes{LoginName: i.GetLoginName()})
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "failed building password attribute struct: %v", err)
|
||||
}
|
||||
out.Attributes = st
|
||||
case *oidc.Account:
|
||||
out.Type = auth.OidcSubtype.String()
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = auth.OidcSubtype.String()
|
||||
}
|
||||
if !outputFields.Has(globals.AttributesField) {
|
||||
break
|
||||
}
|
||||
attrs := &pb.OidcAccountAttributes{
|
||||
Issuer: i.GetIssuer(),
|
||||
Subject: i.GetSubject(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
@@ -268,10 +269,11 @@ func TestListPassword(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListAccountsRequest
|
||||
res *pbs.ListAccountsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListAccountsRequest
|
||||
res *pbs.ListAccountsResponse
|
||||
err error
|
||||
skipAnon bool
|
||||
}{
|
||||
{
|
||||
name: "List Some Accounts",
|
||||
@@ -299,7 +301,8 @@ func TestListPassword(t *testing.T) {
|
||||
AuthMethodId: amSomeAccounts.GetPublicId(),
|
||||
Filter: fmt.Sprintf(`"/item/attributes/login_name"==%q`, wantSomeAccounts[1].Attributes.AsMap()["login_name"]),
|
||||
},
|
||||
res: &pbs.ListAccountsResponse{Items: wantSomeAccounts[1:2]},
|
||||
res: &pbs.ListAccountsResponse{Items: wantSomeAccounts[1:2]},
|
||||
skipAnon: true,
|
||||
},
|
||||
{
|
||||
name: "Filter All Accounts",
|
||||
@@ -321,6 +324,7 @@ func TestListPassword(t *testing.T) {
|
||||
s, err := accounts.NewService(pwRepoFn, oidcRepoFn)
|
||||
require.NoError(err, "Couldn't create new user service.")
|
||||
|
||||
// Test non-anon first
|
||||
got, gErr := s.ListAccounts(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
@@ -330,6 +334,20 @@ func TestListPassword(t *testing.T) {
|
||||
require.NoError(gErr)
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform(), protocmp.SortRepeatedFields(got)), "ListAccounts() with scope %q got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Now test with anon
|
||||
if tc.skipAnon {
|
||||
return
|
||||
}
|
||||
got, gErr = s.ListAccounts(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, g := range got.GetItems() {
|
||||
assert.Nil(g.Attributes)
|
||||
assert.Nil(g.CreatedTime)
|
||||
assert.Nil(g.UpdatedTime)
|
||||
assert.Empty(g.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -401,10 +419,11 @@ func TestListOidc(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.ListAccountsRequest
|
||||
res *pbs.ListAccountsResponse
|
||||
err error
|
||||
name string
|
||||
req *pbs.ListAccountsRequest
|
||||
res *pbs.ListAccountsResponse
|
||||
err error
|
||||
skipAnon bool
|
||||
}{
|
||||
{
|
||||
name: "List Some Accounts",
|
||||
@@ -432,7 +451,8 @@ func TestListOidc(t *testing.T) {
|
||||
AuthMethodId: amSomeAccounts.GetPublicId(),
|
||||
Filter: fmt.Sprintf(`"/item/attributes/subject"==%q`, wantSomeAccounts[1].Attributes.AsMap()["subject"]),
|
||||
},
|
||||
res: &pbs.ListAccountsResponse{Items: wantSomeAccounts[1:2]},
|
||||
res: &pbs.ListAccountsResponse{Items: wantSomeAccounts[1:2]},
|
||||
skipAnon: true,
|
||||
},
|
||||
{
|
||||
name: "Filter All Accounts",
|
||||
@@ -467,6 +487,20 @@ func TestListOidc(t *testing.T) {
|
||||
got.Items[j].GetAttributes().GetFields()["subject"].GetStringValue()) < 0
|
||||
})
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListAccounts() with scope %q got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Now test with anon
|
||||
if tc.skipAnon {
|
||||
return
|
||||
}
|
||||
got, gErr = s.ListAccounts(auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, g := range got.GetItems() {
|
||||
assert.Nil(g.Attributes)
|
||||
assert.Nil(g.CreatedTime)
|
||||
assert.Nil(g.UpdatedTime)
|
||||
assert.Empty(g.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1067,7 +1101,7 @@ func TestUpdatePassword(t *testing.T) {
|
||||
name: "Update an Existing AuthMethod",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name", "description"},
|
||||
Paths: []string{globals.NameField, globals.DescriptionField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "new"},
|
||||
@@ -1126,7 +1160,7 @@ func TestUpdatePassword(t *testing.T) {
|
||||
name: "Cant change type",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name"},
|
||||
Paths: []string{globals.NameField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: ""},
|
||||
@@ -1163,7 +1197,7 @@ func TestUpdatePassword(t *testing.T) {
|
||||
name: "Unset Name",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name"},
|
||||
Paths: []string{globals.NameField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Description: &wrapperspb.StringValue{Value: "ignored"},
|
||||
@@ -1185,7 +1219,7 @@ func TestUpdatePassword(t *testing.T) {
|
||||
name: "Update Only Name",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name"},
|
||||
Paths: []string{globals.NameField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "updated"},
|
||||
@@ -1209,7 +1243,7 @@ func TestUpdatePassword(t *testing.T) {
|
||||
name: "Update Only Description",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"description"},
|
||||
Paths: []string{globals.DescriptionField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "ignored"},
|
||||
@@ -1258,7 +1292,7 @@ func TestUpdatePassword(t *testing.T) {
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
Id: password.AccountPrefix + "_DoesntExis",
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"description"},
|
||||
Paths: []string{globals.DescriptionField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "new"},
|
||||
@@ -1434,7 +1468,7 @@ func TestUpdateOidc(t *testing.T) {
|
||||
name: "Update an Existing AuthMethod",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name", "description"},
|
||||
Paths: []string{globals.NameField, globals.DescriptionField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "new"},
|
||||
@@ -1493,7 +1527,7 @@ func TestUpdateOidc(t *testing.T) {
|
||||
name: "Cant change type",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name"},
|
||||
Paths: []string{globals.NameField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: ""},
|
||||
@@ -1530,7 +1564,7 @@ func TestUpdateOidc(t *testing.T) {
|
||||
name: "Unset Name",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name"},
|
||||
Paths: []string{globals.NameField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Description: &wrapperspb.StringValue{Value: "ignored"},
|
||||
@@ -1552,7 +1586,7 @@ func TestUpdateOidc(t *testing.T) {
|
||||
name: "Update Only Name",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"name"},
|
||||
Paths: []string{globals.NameField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "updated"},
|
||||
@@ -1576,7 +1610,7 @@ func TestUpdateOidc(t *testing.T) {
|
||||
name: "Update Only Description",
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"description"},
|
||||
Paths: []string{globals.DescriptionField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "ignored"},
|
||||
@@ -1615,7 +1649,7 @@ func TestUpdateOidc(t *testing.T) {
|
||||
req: &pbs.UpdateAccountRequest{
|
||||
Id: password.AccountPrefix + "_DoesntExis",
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"description"},
|
||||
Paths: []string{globals.DescriptionField},
|
||||
},
|
||||
Item: &pb.Account{
|
||||
Name: &wrapperspb.StringValue{Value: "new"},
|
||||
@@ -1864,7 +1898,7 @@ func TestSetPassword(t *testing.T) {
|
||||
password: "anewpassword",
|
||||
},
|
||||
{
|
||||
name: "password to short",
|
||||
name: "password too short",
|
||||
accountId: defaultAcct.GetId(),
|
||||
version: defaultAcct.GetVersion(),
|
||||
password: "123",
|
||||
@@ -2027,7 +2061,7 @@ func TestChangePassword(t *testing.T) {
|
||||
newPW: "anewpassword",
|
||||
},
|
||||
{
|
||||
name: "new password to short",
|
||||
name: "new password too short",
|
||||
authMethodId: defaultAcct.GetAuthMethodId(),
|
||||
accountId: defaultAcct.GetId(),
|
||||
version: defaultAcct.GetVersion(),
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -61,33 +63,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func populateCollectionAuthorizedActions(ctx context.Context,
|
||||
authResults auth.VerifyResults,
|
||||
item *pb.AuthMethod) error {
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Pin: item.Id,
|
||||
}
|
||||
// Range over the defined collections and check permissions against those
|
||||
// collections. We use the ID of this scope being returned, not its parent,
|
||||
// hence passing in a resource here.
|
||||
for k, v := range collectionTypeMap {
|
||||
res.Type = k
|
||||
acts := authResults.FetchActionSetForType(ctx, k, v, auth.WithResource(res)).Strings()
|
||||
if len(acts) > 0 {
|
||||
if item.AuthorizedCollectionActions == nil {
|
||||
item.AuthorizedCollectionActions = make(map[string]*structpb.ListValue)
|
||||
}
|
||||
lv, err := structpb.NewList(strutil.StringListToInterfaceList(acts))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.AuthorizedCollectionActions[k.String()+"s"] = lv
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Service handles request as described by the pbs.AuthMethodServiceServer interface.
|
||||
type Service struct {
|
||||
pbs.UnimplementedAuthMethodServiceServer
|
||||
@@ -157,30 +132,54 @@ func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRe
|
||||
return &pbs.ListAuthMethodsResponse{}, nil
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds, authResults.UserId == auth.AnonymousUserId)
|
||||
ul, err := s.listFromRepo(ctx, scopeIds, authResults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ul) == 0 {
|
||||
return &pbs.ListAuthMethodsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.AuthMethod, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
Type: resource.AuthMethod,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions[auth.SubtypeFromId(item.Id)], auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) == 0 {
|
||||
for _, am := range ul {
|
||||
res.Id = am.GetPublicId()
|
||||
res.ScopeId = am.GetScopeId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, am.GetPublicId(), IdActions[auth.SubtypeFromId(am.GetPublicId())], auth.WithResource(&res)).Strings()
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, item); err != nil {
|
||||
|
||||
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[am.GetScopeId()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, am.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toAuthMethodProto(ctx, am, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
}
|
||||
return &pbs.ListAuthMethodsResponse{Items: finalItems}, nil
|
||||
@@ -188,6 +187,8 @@ func (s Service) ListAuthMethods(ctx context.Context, req *pbs.ListAuthMethodsRe
|
||||
|
||||
// GetAuthMethod implements the interface pbs.AuthMethodServiceServer.
|
||||
func (s Service) GetAuthMethod(ctx context.Context, req *pbs.GetAuthMethodRequest) (*pbs.GetAuthMethodResponse, error) {
|
||||
const op = "authmethods.(Service).GetAuthMethod"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -195,20 +196,44 @@ func (s Service) GetAuthMethod(ctx context.Context, req *pbs.GetAuthMethodReques
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
am, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, u); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, am.GetPublicId(), IdActions[auth.SubtypeFromId(am.GetPublicId())]).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, am.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toAuthMethodProto(ctx, am, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.GetAuthMethodResponse{Item: u}, nil
|
||||
|
||||
return &pbs.GetAuthMethodResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateAuthMethod implements the interface pbs.AuthMethodServiceServer.
|
||||
func (s Service) CreateAuthMethod(ctx context.Context, req *pbs.CreateAuthMethodRequest) (*pbs.CreateAuthMethodResponse, error) {
|
||||
const op = "authmethods.(Service).CreateAuthMethod"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -216,20 +241,44 @@ func (s Service) CreateAuthMethod(ctx context.Context, req *pbs.CreateAuthMethod
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
|
||||
am, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, u); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, am.GetPublicId(), IdActions[auth.SubtypeFromId(am.GetPublicId())]).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, am.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toAuthMethodProto(ctx, am, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.CreateAuthMethodResponse{Item: u, Uri: fmt.Sprintf("auth-methods/%s", u.GetId())}, nil
|
||||
|
||||
return &pbs.CreateAuthMethodResponse{Item: item, Uri: fmt.Sprintf("auth-methods/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateAuthMethod implements the interface pbs.AuthMethodServiceServer.
|
||||
func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethodRequest) (*pbs.UpdateAuthMethodResponse, error) {
|
||||
const op = "authmethods.(Service).UpdateAuthMethod"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -237,7 +286,7 @@ func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethod
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req)
|
||||
am, dryRun, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Match(errors.T(errors.InvalidParameter), err):
|
||||
@@ -246,16 +295,44 @@ func (s Service) UpdateAuthMethod(ctx context.Context, req *pbs.UpdateAuthMethod
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions[auth.SubtypeFromId(u.Id)]).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, u); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, am.GetPublicId(), IdActions[auth.SubtypeFromId(am.GetPublicId())]).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, am.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toAuthMethodProto(ctx, am, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.UpdateAuthMethodResponse{Item: u}, nil
|
||||
|
||||
if item.GetAttributes() != nil && dryRun {
|
||||
item.GetAttributes().Fields["dry_run"] = structpb.NewBoolValue(true)
|
||||
}
|
||||
|
||||
return &pbs.UpdateAuthMethodResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// ChangeState implements the interface pbs.AuthMethodServiceServer.
|
||||
func (s Service) ChangeState(ctx context.Context, req *pbs.ChangeStateRequest) (*pbs.ChangeStateResponse, error) {
|
||||
const op = "authmethods.(Service).ChangeState"
|
||||
|
||||
if err := validateChangeStateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -272,12 +349,34 @@ func (s Service) ChangeState(ctx context.Context, req *pbs.ChangeStateRequest) (
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
am.Scope = authResults.Scope
|
||||
am.AuthorizedActions = authResults.FetchActionSetForId(ctx, am.Id, IdActions[auth.OidcSubtype]).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, am); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, am.GetPublicId(), IdActions[auth.SubtypeFromId(am.GetPublicId())]).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, am.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toAuthMethodProto(ctx, am, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.ChangeStateResponse{Item: am}, nil
|
||||
|
||||
return &pbs.ChangeStateResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteAuthMethod implements the interface pbs.AuthMethodServiceServer.
|
||||
@@ -351,7 +450,7 @@ func (s Service) AuthenticateLogin(ctx context.Context, req *pbs.AuthenticateLog
|
||||
return &pbs.AuthenticateLoginResponse{Item: tok, TokenType: req.GetTokenType()}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.AuthMethod, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (auth.AuthMethod, error) {
|
||||
var lookupErr error
|
||||
var am auth.AuthMethod
|
||||
switch auth.SubtypeFromId(id) {
|
||||
@@ -383,26 +482,28 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.AuthMethod, er
|
||||
return nil, handlers.NotFoundErrorf("AuthMethod %q doesn't exist.", id)
|
||||
}
|
||||
|
||||
return toAuthMethodProto(am)
|
||||
return am, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string, anonUser bool) ([]*pb.AuthMethod, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string, authResults auth.VerifyResults) ([]auth.AuthMethod, error) {
|
||||
const op = "authmethods.(Service).listFromRepo"
|
||||
reqCtx, ok := requests.RequestContextFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
var outUl []auth.AuthMethod
|
||||
|
||||
oidcRepo, err := s.oidcRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
ol, err := oidcRepo.ListAuthMethods(ctx, scopeIds, oidc.WithUnauthenticatedUser(anonUser))
|
||||
ol, err := oidcRepo.ListAuthMethods(ctx, scopeIds, oidc.WithUnauthenticatedUser(reqCtx.UserId == auth.AnonymousUserId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
var outUl []*pb.AuthMethod
|
||||
for _, u := range ol {
|
||||
ou, err := toAuthMethodProto(u, handlers.WithAnonymousListing(anonUser))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outUl = append(outUl, ou)
|
||||
for _, item := range ol {
|
||||
outUl = append(outUl, item)
|
||||
}
|
||||
|
||||
repo, err := s.pwRepoFn()
|
||||
@@ -413,17 +514,13 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string, anonUser b
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
for _, u := range pl {
|
||||
ou, err := toAuthMethodProto(u, handlers.WithAnonymousListing(anonUser))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
outUl = append(outUl, ou)
|
||||
for _, item := range pl {
|
||||
outUl = append(outUl, item)
|
||||
}
|
||||
return outUl, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.AuthMethod) (*pb.AuthMethod, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.AuthMethod) (auth.AuthMethod, error) {
|
||||
const op = "authmethods.(Service).createInRepo"
|
||||
var out auth.AuthMethod
|
||||
switch auth.SubtypeFromType(item.GetType()) {
|
||||
@@ -446,51 +543,39 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Auth
|
||||
}
|
||||
out = am
|
||||
}
|
||||
return toAuthMethodProto(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId string, req *pbs.UpdateAuthMethodRequest) (*pb.AuthMethod, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId string, req *pbs.UpdateAuthMethodRequest) (auth.AuthMethod, bool, error) {
|
||||
const op = "authmethods.(Service).updateInRepo"
|
||||
storageToWire := toAuthMethodProto
|
||||
var out auth.AuthMethod
|
||||
|
||||
var am auth.AuthMethod
|
||||
var dryRun bool
|
||||
|
||||
switch auth.SubtypeFromId(req.GetId()) {
|
||||
case auth.PasswordSubtype:
|
||||
am, err := s.updatePwInRepo(ctx, scopeId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
pam, err := s.updatePwInRepo(ctx, scopeId, req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, false, errors.Wrap(err, op)
|
||||
}
|
||||
if am == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to update auth method but no error returned from repository.")
|
||||
if pam == nil {
|
||||
return nil, false, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to update auth method but no error returned from repository.")
|
||||
}
|
||||
out = am
|
||||
am = pam
|
||||
|
||||
case auth.OidcSubtype:
|
||||
am, dryRun, err := s.updateOidcInRepo(ctx, scopeId, req)
|
||||
oam, dr, err := s.updateOidcInRepo(ctx, scopeId, req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, false, errors.Wrap(err, op)
|
||||
}
|
||||
if am == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to update auth method but no error returned from repository.")
|
||||
if oam == nil {
|
||||
return nil, false, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to update auth method but no error returned from repository.")
|
||||
}
|
||||
if dryRun {
|
||||
storageToWire = func(in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMethod, error) {
|
||||
am, err := toAuthMethodProto(in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
attrs := &pb.OidcAuthMethodAttributes{}
|
||||
if err := handlers.StructToProto(am.GetAttributes(), attrs); err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("can't convert from attribute struct to proto"))
|
||||
}
|
||||
attrs.DryRun = true
|
||||
if am.Attributes, err = handlers.ProtoToStruct(attrs); err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("can't convert from attribute proto to struct"))
|
||||
}
|
||||
return am, nil
|
||||
}
|
||||
}
|
||||
out = am
|
||||
am = oam
|
||||
dryRun = dr
|
||||
}
|
||||
return storageToWire(out)
|
||||
|
||||
return am, dryRun, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
|
||||
@@ -523,7 +608,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) changeStateInRepo(ctx context.Context, req *pbs.ChangeStateRequest) (*pb.AuthMethod, error) {
|
||||
func (s Service) changeStateInRepo(ctx context.Context, req *pbs.ChangeStateRequest) (auth.AuthMethod, error) {
|
||||
const op = "authmethod_service.(Service).changeStateInRepo"
|
||||
|
||||
switch auth.SubtypeFromId(req.GetId()) {
|
||||
@@ -558,7 +643,7 @@ func (s Service) changeStateInRepo(ctx context.Context, req *pbs.ChangeStateRequ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toAuthMethodProto(am)
|
||||
return am, nil
|
||||
}
|
||||
|
||||
return nil, errors.New(errors.InvalidParameter, op, "Given auth method type does not support changing state")
|
||||
@@ -633,28 +718,53 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toAuthMethodProto(in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMethod, error) {
|
||||
anonListing := handlers.GetOpts(opt...).WithAnonymousListing
|
||||
out := &pb.AuthMethod{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
IsPrimary: in.GetIsPrimaryAuthMethod(),
|
||||
func toAuthMethodProto(ctx context.Context, in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMethod, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building auth method proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.AuthMethod{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
if outputFields.Has(globals.IsPrimaryField) {
|
||||
out.IsPrimary = in.GetIsPrimaryAuthMethod()
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if !anonListing {
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
out.AuthorizedCollectionActions = opts.WithAuthorizedCollectionActions
|
||||
}
|
||||
switch i := in.(type) {
|
||||
case *password.AuthMethod:
|
||||
out.Type = auth.PasswordSubtype.String()
|
||||
if anonListing {
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = auth.PasswordSubtype.String()
|
||||
}
|
||||
if !outputFields.Has(globals.AttributesField) {
|
||||
break
|
||||
}
|
||||
st, err := handlers.ProtoToStruct(&pb.PasswordAuthMethodAttributes{
|
||||
@@ -666,8 +776,10 @@ func toAuthMethodProto(in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMeth
|
||||
}
|
||||
out.Attributes = st
|
||||
case *oidc.AuthMethod:
|
||||
out.Type = auth.OidcSubtype.String()
|
||||
if anonListing {
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = auth.OidcSubtype.String()
|
||||
}
|
||||
if !outputFields.Has(globals.AttributesField) {
|
||||
break
|
||||
}
|
||||
attrs := &pb.OidcAuthMethodAttributes{
|
||||
@@ -704,7 +816,7 @@ func toAuthMethodProto(in auth.AuthMethod, opt ...handlers.Option) (*pb.AuthMeth
|
||||
}
|
||||
out.Attributes = st
|
||||
}
|
||||
return out, nil
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func toAuthTokenProto(t *authtoken.AuthToken) *pba.AuthToken {
|
||||
@@ -1080,8 +1192,8 @@ func validateAuthenticateLoginRequest(req *pbs.AuthenticateLoginRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Service) convertInternalAuthTokenToApiAuthToken(ctx context.Context, tok *authtoken.AuthToken) (*pba.AuthToken, error) {
|
||||
const op = "authmethod.convertInternalAuthTokenToApiAuthToken"
|
||||
func (s Service) ConvertInternalAuthTokenToApiAuthToken(ctx context.Context, tok *authtoken.AuthToken) (*pba.AuthToken, error) {
|
||||
const op = "authmethod.ConvertInternalAuthTokenToApiAuthToken"
|
||||
iamRepo, err := s.iamRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -353,7 +353,7 @@ func TestList(t *testing.T) {
|
||||
require.NoError(err, "Couldn't create new auth_method service.")
|
||||
|
||||
// First check with non-anonymous user
|
||||
got, gErr := s.ListAuthMethods(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId("u_auth")), tc.req)
|
||||
got, gErr := s.ListAuthMethods(auth.DisabledAuthTestContext(iamRepoFn, 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.req.GetScopeId(), gErr, tc.err)
|
||||
@@ -370,8 +370,9 @@ func TestList(t *testing.T) {
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListAuthMethods() for scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
|
||||
// Now check with anonymous user
|
||||
got, gErr = s.ListAuthMethods(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId()), tc.req)
|
||||
got, gErr = s.ListAuthMethods(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, g := range got.GetItems() {
|
||||
assert.Nil(g.Attributes)
|
||||
assert.Nil(g.CreatedTime)
|
||||
|
||||
@@ -324,7 +324,7 @@ func (s Service) authenticateOidcToken(ctx context.Context, req *pbs.Authenticat
|
||||
}, nil
|
||||
}
|
||||
|
||||
responseToken, err := s.convertInternalAuthTokenToApiAuthToken(
|
||||
responseToken, err := s.ConvertInternalAuthTokenToApiAuthToken(
|
||||
ctx,
|
||||
token,
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
@@ -153,6 +154,7 @@ func TestList_FilterNonPublic(t *testing.T) {
|
||||
serversRepoFn := func() (*servers.Repository, error) {
|
||||
return servers.NewRepository(rw, rw, kmsCache)
|
||||
}
|
||||
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
o, _ := iam.TestScopes(t, iamRepo)
|
||||
@@ -194,14 +196,14 @@ func TestList_FilterNonPublic(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "unauthenticated",
|
||||
reqCtx: auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId()),
|
||||
reqCtx: auth.DisabledAuthTestContext(iamRepoFn, o.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)),
|
||||
respCount: 1,
|
||||
},
|
||||
{
|
||||
name: "authenticated",
|
||||
reqCtx: func() context.Context {
|
||||
at := authtoken.TestAuthToken(t, conn, kmsCache, o.GetPublicId())
|
||||
return auth.NewVerifierContext(context.Background(),
|
||||
return auth.NewVerifierContext(requests.NewRequestContext(context.Background()),
|
||||
nil,
|
||||
iamRepoFn,
|
||||
authTokenRepoFn,
|
||||
|
||||
@@ -127,7 +127,7 @@ func (s Service) authenticateWithPwRepo(ctx context.Context, scopeId, authMethod
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.convertInternalAuthTokenToApiAuthToken(
|
||||
return s.ConvertInternalAuthTokenToApiAuthToken(
|
||||
ctx,
|
||||
tok,
|
||||
)
|
||||
|
||||
@@ -4,18 +4,21 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/authtokens"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -90,37 +93,57 @@ func (s Service) ListAuthTokens(ctx context.Context, req *pbs.ListAuthTokensRequ
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ul) == 0 {
|
||||
return &pbs.ListAuthTokensResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.AuthToken, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
Type: resource.AuthToken,
|
||||
}
|
||||
for _, item := range ul {
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res))
|
||||
for _, at := range ul {
|
||||
res.Id = at.GetPublicId()
|
||||
res.ScopeId = at.GetScopeId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, at.GetPublicId(), IdActions, auth.WithResource(&res))
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if authorizedActions.OnlySelf() && item.GetUserId() != authResults.UserId {
|
||||
if authorizedActions.OnlySelf() && at.GetIamUserId() != authResults.UserId {
|
||||
continue
|
||||
}
|
||||
|
||||
item.AuthorizedActions = authorizedActions.Strings()
|
||||
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[at.GetScopeId()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, at, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
return &pbs.ListAuthTokensResponse{Items: finalItems}, nil
|
||||
}
|
||||
|
||||
// GetAuthToken implements the interface pbs.AuthTokenServiceServer.
|
||||
func (s Service) GetAuthToken(ctx context.Context, req *pbs.GetAuthTokenRequest) (*pbs.GetAuthTokenResponse, error) {
|
||||
const op = "authtokens.(Service).GetAuthToken"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,22 +151,47 @@ func (s Service) GetAuthToken(ctx context.Context, req *pbs.GetAuthTokenRequest)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
at, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authzdActions := authResults.FetchActionSetForId(ctx, u.Id, IdActions)
|
||||
var outputFields perms.OutputFieldsMap
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, at.GetPublicId(), IdActions)
|
||||
|
||||
// Check to see if we need to verify Read vs. just ReadSelf
|
||||
if u.GetUserId() != authResults.UserId {
|
||||
if !authzdActions.HasAction(action.Read) {
|
||||
if at.GetIamUserId() != authResults.UserId {
|
||||
if !authorizedActions.HasAction(action.Read) {
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
outputFields = authResults.FetchOutputFields(perms.Resource{
|
||||
Id: at.GetPublicId(),
|
||||
ScopeId: at.GetScopeId(),
|
||||
Type: resource.AuthToken,
|
||||
}, action.Read).SelfOrDefaults(authResults.UserId)
|
||||
} else {
|
||||
var ok bool
|
||||
outputFields, ok = requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
}
|
||||
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.GetAuthTokenResponse{Item: u}, nil
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, at, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetAuthTokenResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteAuthToken implements the interface pbs.AuthTokenServiceServer.
|
||||
@@ -160,10 +208,12 @@ func (s Service) DeleteAuthToken(ctx context.Context, req *pbs.DeleteAuthTokenRe
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authzdActions := authResults.FetchActionSetForId(ctx, at.Id, IdActions)
|
||||
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, at.GetPublicId(), IdActions)
|
||||
|
||||
// Check to see if we need to verify Delete vs. just DeleteSelf
|
||||
if at.GetUserId() != authResults.UserId {
|
||||
if !authzdActions.HasAction(action.Delete) {
|
||||
if at.GetIamUserId() != authResults.UserId {
|
||||
if !authorizedActions.HasAction(action.Delete) {
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
}
|
||||
@@ -175,20 +225,20 @@ func (s Service) DeleteAuthToken(ctx context.Context, req *pbs.DeleteAuthTokenRe
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.AuthToken, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*authtoken.AuthToken, error) {
|
||||
const op = "authtokens.(Service).getFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
u, err := repo.LookupAuthToken(ctx, id)
|
||||
at, err := repo.LookupAuthToken(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if u == nil {
|
||||
if at == nil {
|
||||
return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("AuthToken %q not found", id))
|
||||
}
|
||||
return toProto(u), nil
|
||||
return at, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
@@ -207,7 +257,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.AuthToken, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*authtoken.AuthToken, error) {
|
||||
repo, err := s.repoFn()
|
||||
_ = repo
|
||||
if err != nil {
|
||||
@@ -217,11 +267,7 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Aut
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outUl []*pb.AuthToken
|
||||
for _, u := range ul {
|
||||
outUl = append(outUl, toProto(u))
|
||||
}
|
||||
return outUl, nil
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
@@ -268,19 +314,49 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *authtoken.AuthToken) *pb.AuthToken {
|
||||
out := pb.AuthToken{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
ApproximateLastUsedTime: in.GetApproximateLastAccessTime().GetTimestamp(),
|
||||
ExpirationTime: in.GetExpirationTime().GetTimestamp(),
|
||||
UserId: in.GetIamUserId(),
|
||||
AuthMethodId: in.GetAuthMethodId(),
|
||||
AccountId: in.GetAuthAccountId(),
|
||||
func toProto(ctx context.Context, in *authtoken.AuthToken, opt ...handlers.Option) (*pb.AuthToken, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building auth token proto")
|
||||
}
|
||||
return &out
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.AuthToken{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
if outputFields.Has(globals.UserIdField) {
|
||||
out.UserId = in.GetIamUserId()
|
||||
}
|
||||
if outputFields.Has(globals.AuthMethodIdField) {
|
||||
out.AuthMethodId = in.GetAuthMethodId()
|
||||
}
|
||||
if outputFields.Has(globals.AccountIdField) {
|
||||
out.AccountId = in.GetAuthAccountId()
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.ApproximateLastUsedTimeField) {
|
||||
out.ApproximateLastUsedTime = in.GetApproximateLastAccessTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.ExpirationTimeField) {
|
||||
out.ExpirationTime = in.GetExpirationTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens"
|
||||
@@ -97,6 +98,7 @@ func TestGetSelf(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := auth.NewVerifierContext(context.Background(), logger, iamRepoFn, tokenRepoFn, serversRepoFn, kms, requestInfo)
|
||||
ctx = context.WithValue(ctx, requests.ContextRequestInformationKey, &requests.RequestContext{})
|
||||
got, err := a.GetAuthToken(ctx, &pbs.GetAuthTokenRequest{Id: tc.readId})
|
||||
if tc.err != nil {
|
||||
require.EqualError(err, tc.err.Error())
|
||||
@@ -105,9 +107,9 @@ func TestGetSelf(t *testing.T) {
|
||||
}
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.Equal(got.GetItem().GetId(), tc.token.GetPublicId())
|
||||
assert.Equal(tc.token.GetPublicId(), got.GetItem().GetId())
|
||||
// Ensure we didn't simply have e.g. read on all tokens
|
||||
assert.Equal(got.Item.GetAuthorizedActions(), []string{"read:self", "delete:self"})
|
||||
assert.Equal([]string{"read:self", "delete:self"}, got.Item.GetAuthorizedActions())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -384,16 +386,30 @@ func TestList(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s, err := authtokens.NewService(repoFn, iamRepoFn)
|
||||
require.NoError(t, err, "Couldn't create new user service.")
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NoError(err, "Couldn't create new user service.")
|
||||
|
||||
got, gErr := s.ListAuthTokens(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId()), tc.req)
|
||||
// Check non-anon listing
|
||||
got, gErr := s.ListAuthTokens(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId("u_auth")), 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.req.GetScopeId(), gErr, tc.err)
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListAuthTokens() with scope %q got error %v, wanted %v", tc.req.GetScopeId(), gErr, tc.err)
|
||||
return
|
||||
} else {
|
||||
require.NoError(t, gErr)
|
||||
require.NoError(gErr)
|
||||
}
|
||||
assert.Empty(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)
|
||||
|
||||
// Now check anon listing
|
||||
got, gErr = s.ListAuthTokens(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.Nil(item.CreatedTime)
|
||||
require.Nil(item.ExpirationTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Nil(item.ApproximateLastUsedTime)
|
||||
}
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/groups"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -102,21 +104,41 @@ func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*p
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(gl) == 0 {
|
||||
return &pbs.ListGroupsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Group, 0, len(gl))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
Type: resource.Group,
|
||||
}
|
||||
for _, item := range gl {
|
||||
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 {
|
||||
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()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -126,6 +148,8 @@ func (s Service) ListGroups(ctx context.Context, req *pbs.ListGroupsRequest) (*p
|
||||
|
||||
// GetGroups implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) GetGroup(ctx context.Context, req *pbs.GetGroupRequest) (*pbs.GetGroupResponse, error) {
|
||||
const op = "groups.(Service).GetGroup"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,17 +157,37 @@ func (s Service) GetGroup(ctx context.Context, req *pbs.GetGroupRequest) (*pbs.G
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
u, members, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.GetGroupResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, members, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetGroupResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateGroup implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) CreateGroup(ctx context.Context, req *pbs.CreateGroupRequest) (*pbs.CreateGroupResponse, error) {
|
||||
const op = "groups.(Service).CreateGroup"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,13 +199,33 @@ func (s Service) CreateGroup(ctx context.Context, req *pbs.CreateGroupRequest) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.CreateGroupResponse{Item: u, Uri: fmt.Sprintf("groups/%s", u.GetId())}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateGroupResponse{Item: item, Uri: fmt.Sprintf("groups/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateGroup implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) UpdateGroup(ctx context.Context, req *pbs.UpdateGroupRequest) (*pbs.UpdateGroupResponse, error) {
|
||||
const op = "groups.(Service).UpdateGroup"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -169,13 +233,31 @@ func (s Service) UpdateGroup(ctx context.Context, req *pbs.UpdateGroupRequest) (
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
u, members, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.UpdateGroupResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, members, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateGroupResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteGroup implements the interface pbs.GroupServiceServer.
|
||||
@@ -196,6 +278,8 @@ func (s Service) DeleteGroup(ctx context.Context, req *pbs.DeleteGroupRequest) (
|
||||
|
||||
// AddGroupMembers implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) AddGroupMembers(ctx context.Context, req *pbs.AddGroupMembersRequest) (*pbs.AddGroupMembersResponse, error) {
|
||||
const op = "groups.(Service).AddGroupMembers"
|
||||
|
||||
if err := validateAddGroupMembersRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -203,17 +287,37 @@ func (s Service) AddGroupMembers(ctx context.Context, req *pbs.AddGroupMembersRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
g, err := s.addMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion())
|
||||
g, members, err := s.addMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Scope = authResults.Scope
|
||||
g.AuthorizedActions = authResults.FetchActionSetForId(ctx, g.Id, IdActions).Strings()
|
||||
return &pbs.AddGroupMembersResponse{Item: g}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, g.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, g, members, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddGroupMembersResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetGroupMembers implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) SetGroupMembers(ctx context.Context, req *pbs.SetGroupMembersRequest) (*pbs.SetGroupMembersResponse, error) {
|
||||
const op = "groups.(Service).SetGroupMembers"
|
||||
|
||||
if err := validateSetGroupMembersRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -221,17 +325,37 @@ func (s Service) SetGroupMembers(ctx context.Context, req *pbs.SetGroupMembersRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
g, err := s.setMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion())
|
||||
g, members, err := s.setMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Scope = authResults.Scope
|
||||
g.AuthorizedActions = authResults.FetchActionSetForId(ctx, g.Id, IdActions).Strings()
|
||||
return &pbs.SetGroupMembersResponse{Item: g}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, g.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, g, members, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetGroupMembersResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// RemoveGroupMembers implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) RemoveGroupMembers(ctx context.Context, req *pbs.RemoveGroupMembersRequest) (*pbs.RemoveGroupMembersResponse, error) {
|
||||
const op = "groups.(Service).RemoveGroupMembers"
|
||||
|
||||
if err := validateRemoveGroupMembersRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -239,32 +363,50 @@ func (s Service) RemoveGroupMembers(ctx context.Context, req *pbs.RemoveGroupMem
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
g, err := s.removeMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion())
|
||||
g, members, err := s.removeMembersInRepo(ctx, req.GetId(), req.GetMemberIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Scope = authResults.Scope
|
||||
g.AuthorizedActions = authResults.FetchActionSetForId(ctx, g.Id, IdActions).Strings()
|
||||
return &pbs.RemoveGroupMembersResponse{Item: g}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, g.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, g, members, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveGroupMembersResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Group, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*iam.Group, []*iam.GroupMember, error) {
|
||||
const op = "groups.(Service).getFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
g, m, err := repo.LookupGroup(ctx, id)
|
||||
if err != nil && !errors.IsNotFoundError(err) {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
if g == nil {
|
||||
return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("group %q not found", id))
|
||||
return nil, nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("group %q not found", id))
|
||||
}
|
||||
return toProto(g, m), nil
|
||||
return g, m, err
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Group) (*pb.Group, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Group) (*iam.Group, error) {
|
||||
const op = "groups.(Service).createInRepo"
|
||||
var opts []iam.Option
|
||||
if item.GetName() != nil {
|
||||
@@ -288,10 +430,10 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Grou
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create group but no error returned from repository.")
|
||||
}
|
||||
return toProto(out, nil), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Group) (*pb.Group, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Group) (*iam.Group, []*iam.GroupMember, error) {
|
||||
const op = "groups.(Service).updateInRepo"
|
||||
var opts []iam.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
@@ -303,25 +445,25 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
|
||||
version := item.GetVersion()
|
||||
g, err := iam.NewGroup(scopeId, opts...)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build group for update: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build group for update: %v.", err)
|
||||
}
|
||||
g.PublicId = id
|
||||
dbMask := maskManager.Translate(mask)
|
||||
if len(dbMask) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
out, m, rowsUpdated, err := repo.UpdateGroup(ctx, g, version, dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Group %q doesn't exist or incorrect version provided.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Group %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
@@ -340,7 +482,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Group, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*iam.Group, error) {
|
||||
const op = "groups.(Service).listFromRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -350,74 +492,70 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Gro
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
var outGl []*pb.Group
|
||||
for _, g := range gl {
|
||||
outGl = append(outGl, toProto(g, nil))
|
||||
}
|
||||
return outGl, nil
|
||||
return gl, nil
|
||||
}
|
||||
|
||||
func (s Service) addMembersInRepo(ctx context.Context, groupId string, userIds []string, version uint32) (*pb.Group, error) {
|
||||
func (s Service) addMembersInRepo(ctx context.Context, groupId string, userIds []string, version uint32) (*iam.Group, []*iam.GroupMember, error) {
|
||||
const op = "groups.(Service).addMembersInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
_, err = repo.AddGroupMembers(ctx, groupId, version, strutil.RemoveDuplicates(userIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add members to group: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add members to group: %v.", err)
|
||||
}
|
||||
out, m, err := repo.LookupGroup(ctx, groupId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up group after adding memebers"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up group after adding memebers"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup group after adding member to it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup group after adding member to it.")
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) setMembersInRepo(ctx context.Context, groupId string, userIds []string, version uint32) (*pb.Group, error) {
|
||||
func (s Service) setMembersInRepo(ctx context.Context, groupId string, userIds []string, version uint32) (*iam.Group, []*iam.GroupMember, error) {
|
||||
const op = "groups.(Service).setMembersInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
_, _, err = repo.SetGroupMembers(ctx, groupId, version, strutil.RemoveDuplicates(userIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set members on group: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set members on group: %v.", err)
|
||||
}
|
||||
out, m, err := repo.LookupGroup(ctx, groupId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up group after setting members"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up group after setting members"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup group after setting members for it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup group after setting members for it.")
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) removeMembersInRepo(ctx context.Context, groupId string, userIds []string, version uint32) (*pb.Group, error) {
|
||||
func (s Service) removeMembersInRepo(ctx context.Context, groupId string, userIds []string, version uint32) (*iam.Group, []*iam.GroupMember, error) {
|
||||
const op = "groups.(Service).removeMembersInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
_, err = repo.DeleteGroupMembers(ctx, groupId, version, strutil.RemoveDuplicates(userIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove members from group: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove members from group: %v.", err)
|
||||
}
|
||||
out, m, err := repo.LookupGroup(ctx, groupId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up group after removing members"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up group after removing members"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup group after removing members from it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup group after removing members from it.")
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
@@ -459,28 +597,55 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *iam.Group, members []*iam.GroupMember) *pb.Group {
|
||||
out := pb.Group{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.Version,
|
||||
func toProto(ctx context.Context, in *iam.Group, members []*iam.GroupMember, opt ...handlers.Option) (*pb.Group, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building group proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Group{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
for _, m := range members {
|
||||
out.MemberIds = append(out.MemberIds, m.GetMemberId())
|
||||
out.Members = append(out.Members, &pb.Member{
|
||||
Id: m.GetMemberId(),
|
||||
ScopeId: m.GetMemberScopeId(),
|
||||
})
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
return &out
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.MemberIdsField) {
|
||||
for _, m := range members {
|
||||
out.MemberIds = append(out.MemberIds, m.GetMemberId())
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.MembersField) {
|
||||
for _, m := range members {
|
||||
out.Members = append(out.Members, &pb.Member{
|
||||
Id: m.GetMemberId(),
|
||||
ScopeId: m.GetMemberScopeId(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -319,12 +319,26 @@ func TestList(t *testing.T) {
|
||||
s, err := groups.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create new group service.")
|
||||
|
||||
// Test with a non-anon user
|
||||
got, gErr := s.ListGroups(auth.DisabledAuthTestContext(repoFn, 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.req.GetScopeId(), gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListGroups(%q) got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
|
||||
// Test the anon case
|
||||
got, gErr = s.ListGroups(auth.DisabledAuthTestContext(repoFn, 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.Nil(item.CreatedTime)
|
||||
require.Nil(item.Members)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Zero(item.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostcatalogs"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/host/static/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -20,9 +22,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/strutil"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
@@ -67,33 +67,6 @@ type Service struct {
|
||||
|
||||
var _ pbs.HostCatalogServiceServer = Service{}
|
||||
|
||||
func populateCollectionAuthorizedActions(ctx context.Context,
|
||||
authResults auth.VerifyResults,
|
||||
item *pb.HostCatalog) error {
|
||||
res := &perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Pin: item.Id,
|
||||
}
|
||||
// Range over the defined collections and check permissions against those
|
||||
// collections. We use the ID of this scope being returned, not its parent,
|
||||
// hence passing in a resource here.
|
||||
for k, v := range collectionTypeMap {
|
||||
res.Type = k
|
||||
acts := authResults.FetchActionSetForType(ctx, k, v, auth.WithResource(res)).Strings()
|
||||
if len(acts) > 0 {
|
||||
if item.AuthorizedCollectionActions == nil {
|
||||
item.AuthorizedCollectionActions = make(map[string]*structpb.ListValue)
|
||||
}
|
||||
lv, err := structpb.NewList(strutil.StringListToInterfaceList(acts))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.AuthorizedCollectionActions[k.String()+"s"] = lv
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewService returns a host catalog Service which handles host catalog related requests to boundary and uses the provided
|
||||
// repositories for storage and retrieval.
|
||||
func NewService(repoFn common.StaticRepoFactory, iamRepoFn common.IamRepoFactory) (Service, error) {
|
||||
@@ -138,27 +111,50 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ul) == 0 {
|
||||
return &pbs.ListHostCatalogsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.HostCatalog, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
Type: resource.HostCatalog,
|
||||
}
|
||||
for _, item := range ul {
|
||||
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 {
|
||||
res.Id = item.GetPublicId()
|
||||
res.ScopeId = item.GetScopeId()
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, item); err != nil {
|
||||
|
||||
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()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, item.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
}
|
||||
return &pbs.ListHostCatalogsResponse{Items: finalItems}, nil
|
||||
@@ -166,6 +162,8 @@ func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogs
|
||||
|
||||
// GetHostCatalog implements the interface pbs.HostCatalogServiceServer.
|
||||
func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequest) (*pbs.GetHostCatalogResponse, error) {
|
||||
const op = "host_catalogs.(Service).GetostCatalog"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -177,16 +175,40 @@ func (s Service) GetHostCatalog(ctx context.Context, req *pbs.GetHostCatalogRequ
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, hc); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, hc.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hc, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.GetHostCatalogResponse{Item: hc}, nil
|
||||
|
||||
return &pbs.GetHostCatalogResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateHostCatalog implements the interface pbs.HostCatalogServiceServer.
|
||||
func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatalogRequest) (*pbs.CreateHostCatalogResponse, error) {
|
||||
const op = "host_catalogs.(Service).CreateHostCatalog"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -198,19 +220,43 @@ func (s Service) CreateHostCatalog(ctx context.Context, req *pbs.CreateHostCatal
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, hc); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, hc.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hc, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateHostCatalogResponse{
|
||||
Item: hc,
|
||||
Uri: fmt.Sprintf("host-catalogs/%s", hc.GetId()),
|
||||
Item: item,
|
||||
Uri: fmt.Sprintf("host-catalogs/%s", item.GetId()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateHostCatalog implements the interface pbs.HostCatalogServiceServer.
|
||||
func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatalogRequest) (*pbs.UpdateHostCatalogResponse, error) {
|
||||
const op = "host_catalogs.(Service).UpdateHostCatalog"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,12 +268,34 @@ func (s Service) UpdateHostCatalog(ctx context.Context, req *pbs.UpdateHostCatal
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, hc); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hc.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, collectionTypeMap, authResults.Scope.Id, hc.GetPublicId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hc, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.UpdateHostCatalogResponse{Item: hc}, nil
|
||||
|
||||
return &pbs.UpdateHostCatalogResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteHostCatalog implements the interface pbs.HostCatalogServiceServer.
|
||||
@@ -246,7 +314,7 @@ func (s Service) DeleteHostCatalog(ctx context.Context, req *pbs.DeleteHostCatal
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.HostCatalog, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*static.HostCatalog, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -258,10 +326,10 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.HostCatalog, e
|
||||
if hc == nil {
|
||||
return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(hc), nil
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.HostCatalog, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*static.HostCatalog, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -270,14 +338,10 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Hos
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outUl []*pb.HostCatalog
|
||||
for _, u := range ul {
|
||||
outUl = append(outUl, toProto(u))
|
||||
}
|
||||
return outUl, nil
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*pb.HostCatalog, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*static.HostCatalog, error) {
|
||||
const op = "host_catalogs.(Servivce).createInRepo"
|
||||
var opts []static.Option
|
||||
if item.GetName() != nil {
|
||||
@@ -301,11 +365,11 @@ func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostC
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host catalog but no error returned from repository.")
|
||||
}
|
||||
return toProto(out), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*pb.HostCatalog, error) {
|
||||
const op = "service.(Service).updateInRepo"
|
||||
func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []string, item *pb.HostCatalog) (*static.HostCatalog, error) {
|
||||
const op = "host_catalogs.(Service).updateInRepo"
|
||||
var opts []static.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
opts = append(opts, static.WithDescription(desc.GetValue()))
|
||||
@@ -334,11 +398,11 @@ func (s Service) updateInRepo(ctx context.Context, projId, id string, mask []str
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Host Catalog %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
const op = "service.(Service).deleteFromRepo"
|
||||
const op = "host_catalogs.(Service).deleteFromRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, op)
|
||||
@@ -394,22 +458,48 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *static.HostCatalog) *pb.HostCatalog {
|
||||
out := pb.HostCatalog{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.GetVersion(),
|
||||
Type: host.StaticSubtype.String(),
|
||||
func toProto(ctx context.Context, in *static.HostCatalog, opt ...handlers.Option) (*pb.HostCatalog, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building auth method proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.HostCatalog{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
return &out
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = host.StaticSubtype.String()
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
out.AuthorizedCollectionActions = opts.WithAuthorizedCollectionActions
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -259,12 +259,24 @@ func TestList(t *testing.T) {
|
||||
s, err := host_catalogs.NewService(repoFn, iamRepoFn)
|
||||
require.NoError(err, "Couldn't create new auth_method service.")
|
||||
|
||||
// Test with non-anon user
|
||||
got, gErr := s.ListHostCatalogs(auth.DisabledAuthTestContext(iamRepoFn, 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.req.GetScopeId(), gErr, tc.err)
|
||||
return
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.req.GetScopeId(), got, tc.res)
|
||||
|
||||
// Test with anon user
|
||||
got, gErr = s.ListHostCatalogs(auth.DisabledAuthTestContext(iamRepoFn, tc.req.GetScopeId(), auth.WithUserId("u_anon")), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Nil(item.CreatedTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Zero(item.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostsets"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/host/static/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
@@ -81,22 +83,43 @@ func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hl) == 0 {
|
||||
return &pbs.ListHostSetsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.HostSet, 0, len(hl))
|
||||
res := &perms.Resource{
|
||||
|
||||
res := perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.HostSet,
|
||||
Pin: req.GetHostCatalogId(),
|
||||
}
|
||||
for _, item := range hl {
|
||||
item.Scope = authResults.Scope
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) == 0 {
|
||||
res.Id = item.GetPublicId()
|
||||
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(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -106,6 +129,8 @@ func (s Service) ListHostSets(ctx context.Context, req *pbs.ListHostSetsRequest)
|
||||
|
||||
// GetHostSet implements the interface pbs.HostSetServiceServer.
|
||||
func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*pbs.GetHostSetResponse, error) {
|
||||
const op = "host_sets.(Service).GetHostSet"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -113,17 +138,37 @@ func (s Service) GetHostSet(ctx context.Context, req *pbs.GetHostSetRequest) (*p
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
hc, err := s.getFromRepo(ctx, req.GetId())
|
||||
hs, hosts, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
return &pbs.GetHostSetResponse{Item: hc}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hs, hosts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetHostSetResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateHostSet implements the interface pbs.HostSetServiceServer.
|
||||
func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetRequest) (*pbs.CreateHostSetResponse, error) {
|
||||
const op = "host_sets.(Service).CreateHostSet"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -131,20 +176,40 @@ func (s Service) CreateHostSet(ctx context.Context, req *pbs.CreateHostSetReques
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
h, err := s.createInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetItem())
|
||||
hs, err := s.createInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.Scope = authResults.Scope
|
||||
h.AuthorizedActions = authResults.FetchActionSetForId(ctx, h.Id, IdActions).Strings()
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hs, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateHostSetResponse{
|
||||
Item: h,
|
||||
Uri: fmt.Sprintf("host-sets/%s", h.GetId()),
|
||||
Item: item,
|
||||
Uri: fmt.Sprintf("host-sets/%s", item.GetId()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateHostSet implements the interface pbs.HostSetServiceServer.
|
||||
func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetRequest) (*pbs.UpdateHostSetResponse, error) {
|
||||
const op = "host_sets.(Service).UpdateHostSet"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -152,13 +217,31 @@ func (s Service) UpdateHostSet(ctx context.Context, req *pbs.UpdateHostSetReques
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
hs, hosts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
return &pbs.UpdateHostSetResponse{Item: hc}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hs, hosts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateHostSetResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteHostSet implements the interface pbs.HostSetServiceServer.
|
||||
@@ -179,6 +262,8 @@ func (s Service) DeleteHostSet(ctx context.Context, req *pbs.DeleteHostSetReques
|
||||
|
||||
// AddHostSetHosts implements the interface pbs.HostSetServiceServer.
|
||||
func (s Service) AddHostSetHosts(ctx context.Context, req *pbs.AddHostSetHostsRequest) (*pbs.AddHostSetHostsResponse, error) {
|
||||
const op = "host_sets.(Service).AddHostSetHosts"
|
||||
|
||||
if err := validateAddRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,17 +271,37 @@ func (s Service) AddHostSetHosts(ctx context.Context, req *pbs.AddHostSetHostsRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
g, err := s.addInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion())
|
||||
hs, hosts, err := s.addInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Scope = authResults.Scope
|
||||
g.AuthorizedActions = authResults.FetchActionSetForId(ctx, g.Id, IdActions).Strings()
|
||||
return &pbs.AddHostSetHostsResponse{Item: g}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hs, hosts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddHostSetHostsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetHostSetHosts implements the interface pbs.HostSetServiceServer.
|
||||
func (s Service) SetHostSetHosts(ctx context.Context, req *pbs.SetHostSetHostsRequest) (*pbs.SetHostSetHostsResponse, error) {
|
||||
const op = "host_sets.(Service).SetHostSetHosts"
|
||||
|
||||
if err := validateSetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -204,17 +309,36 @@ func (s Service) SetHostSetHosts(ctx context.Context, req *pbs.SetHostSetHostsRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
g, err := s.setInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion())
|
||||
hs, hosts, err := s.setInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Scope = authResults.Scope
|
||||
g.AuthorizedActions = authResults.FetchActionSetForId(ctx, g.Id, IdActions).Strings()
|
||||
return &pbs.SetHostSetHostsResponse{Item: g}, nil
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hs, hosts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetHostSetHostsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// RemoveHostSetHosts implements the interface pbs.HostSetServiceServer.
|
||||
func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetHostsRequest) (*pbs.RemoveHostSetHostsResponse, error) {
|
||||
const op = "host_sets.(Service).RemoveHostSetHosts"
|
||||
|
||||
if err := validateRemoveRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,31 +346,49 @@ func (s Service) RemoveHostSetHosts(ctx context.Context, req *pbs.RemoveHostSetH
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
g, err := s.removeInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion())
|
||||
hs, hosts, err := s.removeInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetHostIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Scope = authResults.Scope
|
||||
g.AuthorizedActions = authResults.FetchActionSetForId(ctx, g.Id, IdActions).Strings()
|
||||
return &pbs.RemoveHostSetHostsResponse{Item: g}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.HostSet, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, hs.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, hs, hosts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveHostSetHostsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*static.HostSet, []*static.Host, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h, m, err := repo.LookupSet(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if h == nil {
|
||||
return nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(h, m), nil
|
||||
return h, m, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (*pb.HostSet, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (*static.HostSet, error) {
|
||||
const op = "host_sets.(Service).createInRepo"
|
||||
var opts []static.Option
|
||||
if item.GetName() != nil {
|
||||
@@ -270,10 +412,10 @@ func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, it
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host set but no error returned from repository.")
|
||||
}
|
||||
return toProto(out, nil), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.HostSet) (*pb.HostSet, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.HostSet) (*static.HostSet, []*static.Host, error) {
|
||||
const op = "host_sets.(Service).updateInRepo"
|
||||
var opts []static.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
@@ -284,25 +426,25 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string
|
||||
}
|
||||
h, err := static.NewHostSet(catalogId, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("Unable to build host set for update"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("Unable to build host set for update"))
|
||||
}
|
||||
h.PublicId = id
|
||||
dbMask := maskManager.Translate(mask)
|
||||
if len(dbMask) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
out, m, rowsUpdated, err := repo.UpdateSet(ctx, scopeId, h, item.GetVersion(), dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to update host set"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to update host set"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Host Set %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
|
||||
@@ -318,7 +460,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*pb.HostSet, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*static.HostSet, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -327,72 +469,68 @@ func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*pb.Host
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outH []*pb.HostSet
|
||||
for _, h := range hl {
|
||||
outH = append(outH, toProto(h, nil))
|
||||
}
|
||||
return outH, nil
|
||||
return hl, nil
|
||||
}
|
||||
|
||||
func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*pb.HostSet, error) {
|
||||
func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) {
|
||||
const op = "host_sets.(Service).addInRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = repo.AddSetMembers(ctx, scopeId, setId, version, strutil.RemoveDuplicates(hostIds, false))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("Unable to add hosts to host set"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("Unable to add hosts to host set"))
|
||||
}
|
||||
out, m, err := repo.LookupSet(ctx, setId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up host set after adding hosts"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up host set after adding hosts"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after adding hosts to it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after adding hosts to it.")
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*pb.HostSet, error) {
|
||||
func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) {
|
||||
const op = "host_sets.(Service).setInRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
_, _, err = repo.SetSetMembers(ctx, scopeId, setId, version, strutil.RemoveDuplicates(hostIds, false))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("Unable to set hosts in host set"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("Unable to set hosts in host set"))
|
||||
}
|
||||
|
||||
out, m, err := repo.LookupSet(ctx, setId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up host set after setting hosts"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up host set after setting hosts"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after setting hosts for it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after setting hosts for it.")
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*pb.HostSet, error) {
|
||||
func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*static.HostSet, []*static.Host, error) {
|
||||
const op = "host_sets.(Service).removeInRepo"
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
_, err = repo.DeleteSetMembers(ctx, scopeId, setId, version, strutil.RemoveDuplicates(hostIds, false))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("Unable to remove hosts from host set"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("Unable to remove hosts from host set"))
|
||||
}
|
||||
out, m, err := repo.LookupSet(ctx, setId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up host set"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up host set"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after removing hosts from it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup host set after removing hosts from it.")
|
||||
}
|
||||
return toProto(out, m), nil
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) {
|
||||
@@ -435,25 +573,50 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
return cat, auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *static.HostSet, hs []*static.Host) *pb.HostSet {
|
||||
out := pb.HostSet{
|
||||
Id: in.GetPublicId(),
|
||||
HostCatalogId: in.GetCatalogId(),
|
||||
Type: host.StaticSubtype.String(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.GetVersion(),
|
||||
func toProto(ctx context.Context, in *static.HostSet, hosts []*static.Host, opt ...handlers.Option) (*pb.HostSet, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building hostset proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.HostSet{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
if outputFields.Has(globals.HostCatalogIdField) {
|
||||
out.HostCatalogId = in.GetCatalogId()
|
||||
}
|
||||
for _, h := range hs {
|
||||
out.HostIds = append(out.HostIds, h.GetPublicId())
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = host.StaticSubtype.String()
|
||||
}
|
||||
return &out
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.HostIdsField) {
|
||||
for _, h := range hosts {
|
||||
out.HostIds = append(out.HostIds, h.GetPublicId())
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -194,14 +194,26 @@ func TestList(t *testing.T) {
|
||||
s, err := host_sets.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test with non-anon user
|
||||
got, gErr := s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListHostSets(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
} else {
|
||||
require.NoError(gErr)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostSets(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Test with anon user
|
||||
got, gErr = s.ListHostSets(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Nil(item.CreatedTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Zero(item.Version)
|
||||
require.Empty(item.HostIds)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/hosts"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/host/static/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
@@ -79,22 +81,43 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hl) == 0 {
|
||||
return &pbs.ListHostsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Host, 0, len(hl))
|
||||
res := &perms.Resource{
|
||||
|
||||
res := perms.Resource{
|
||||
ScopeId: authResults.Scope.Id,
|
||||
Type: resource.Host,
|
||||
Pin: req.GetHostCatalogId(),
|
||||
}
|
||||
for _, item := range hl {
|
||||
item.Scope = authResults.Scope
|
||||
item.AuthorizedActions = authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res)).Strings()
|
||||
if len(item.AuthorizedActions) == 0 {
|
||||
res.Id = item.GetPublicId()
|
||||
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(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -104,6 +127,8 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
|
||||
|
||||
// GetHost implements the interface pbs.HostServiceServer.
|
||||
func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.GetHostResponse, error) {
|
||||
const op = "hosts.(Service).GetHost"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -111,17 +136,37 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
hc, err := s.getFromRepo(ctx, req.GetId())
|
||||
h, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
return &pbs.GetHostResponse{Item: hc}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, h, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetHostResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateHost implements the interface pbs.HostServiceServer.
|
||||
func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*pbs.CreateHostResponse, error) {
|
||||
const op = "hosts.(Service).CreateHost"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,16 +178,36 @@ func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*p
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.Scope = authResults.Scope
|
||||
h.AuthorizedActions = authResults.FetchActionSetForId(ctx, h.Id, IdActions).Strings()
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, h, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateHostResponse{
|
||||
Item: h,
|
||||
Uri: fmt.Sprintf("hosts/%s", h.GetId()),
|
||||
Item: item,
|
||||
Uri: fmt.Sprintf("hosts/%s", item.GetId()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateHost implements the interface pbs.HostServiceServer.
|
||||
func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*pbs.UpdateHostResponse, error) {
|
||||
const op = "hosts.(Service).UpdateHost"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -150,13 +215,31 @@ func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*p
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
hc, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
h, err := s.updateInRepo(ctx, authResults.Scope.GetId(), cat.GetPublicId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc.Scope = authResults.Scope
|
||||
hc.AuthorizedActions = authResults.FetchActionSetForId(ctx, hc.Id, IdActions).Strings()
|
||||
return &pbs.UpdateHostResponse{Item: hc}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, h, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateHostResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteHost implements the interface pbs.HostServiceServer.
|
||||
@@ -175,7 +258,7 @@ func (s Service) DeleteHost(ctx context.Context, req *pbs.DeleteHostRequest) (*p
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Host, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*static.Host, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -187,10 +270,10 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Host, error) {
|
||||
if h == nil {
|
||||
return nil, handlers.NotFoundErrorf("Host %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(h, nil)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.Host) (*pb.Host, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.Host) (*static.Host, error) {
|
||||
const op = "hosts.(Service).createInRepo"
|
||||
ha := &pb.StaticHostAttributes{}
|
||||
if err := handlers.StructToProto(item.GetAttributes(), ha); err != nil {
|
||||
@@ -222,10 +305,10 @@ func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, it
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create host but no error returned from repository.")
|
||||
}
|
||||
return toProto(out, nil)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.Host) (*pb.Host, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string, mask []string, item *pb.Host) (*static.Host, error) {
|
||||
const op = "hosts.(Service).updateInRepo"
|
||||
ha := &pb.StaticHostAttributes{}
|
||||
if err := handlers.StructToProto(item.GetAttributes(), ha); err != nil {
|
||||
@@ -261,7 +344,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Host %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out, nil)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) {
|
||||
@@ -277,7 +360,7 @@ func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool,
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*pb.Host, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*static.Host, error) {
|
||||
repo, err := s.staticRepoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -286,15 +369,7 @@ func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*pb.Host
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outHl []*pb.Host
|
||||
for _, h := range hl {
|
||||
p, err := toProto(h, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outHl = append(outHl, p)
|
||||
}
|
||||
return outHl, nil
|
||||
return hl, nil
|
||||
}
|
||||
|
||||
func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Type) (*static.HostCatalog, auth.VerifyResults) {
|
||||
@@ -337,29 +412,56 @@ func (s Service) parentAndAuthResult(ctx context.Context, id string, a action.Ty
|
||||
return cat, auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *static.Host, members []*static.HostSet) (*pb.Host, error) {
|
||||
out := pb.Host{
|
||||
Id: in.GetPublicId(),
|
||||
HostCatalogId: in.GetCatalogId(),
|
||||
Type: host.StaticSubtype.String(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.GetVersion(),
|
||||
func toProto(ctx context.Context, in *static.Host, hostSets []*static.HostSet, opt ...handlers.Option) (*pb.Host, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building host proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Host{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
if outputFields.Has(globals.HostCatalogIdField) {
|
||||
out.HostCatalogId = in.GetCatalogId()
|
||||
}
|
||||
for _, m := range members {
|
||||
out.HostSetIds = append(out.HostSetIds, m.GetPublicId())
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = host.StaticSubtype.String()
|
||||
}
|
||||
st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(in.GetAddress())})
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert static attribute to struct: %s", err)
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.HostSetIdsField) {
|
||||
for _, hs := range hostSets {
|
||||
out.HostSetIds = append(out.HostSetIds, hs.GetPublicId())
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(in.GetAddress())})
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert static attribute to struct: %s", err)
|
||||
}
|
||||
out.Attributes = st
|
||||
}
|
||||
out.Attributes = st
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -189,14 +189,25 @@ func TestList(t *testing.T) {
|
||||
s, err := hosts.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create new host set service.")
|
||||
|
||||
// Test non-anonymous listing
|
||||
got, gErr := s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListHosts(%q) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
} else {
|
||||
require.NoError(gErr)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHosts(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Test anonymous listing
|
||||
got, gErr = s.ListHosts(auth.DisabledAuthTestContext(iamRepoFn, proj.GetPublicId(), auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
require.Nil(item.CreatedTime)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Zero(item.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package handlers
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test_GetOpts provides unit tests for GetOpts and all the options
|
||||
@@ -36,14 +38,26 @@ func Test_GetOpts(t *testing.T) {
|
||||
opts = GetOpts(WithLogger(hclog.New(nil)))
|
||||
assert.NotNil(opts.WithLogger)
|
||||
})
|
||||
|
||||
t.Run("WithAnonymousListing", func(t *testing.T) {
|
||||
t.Run("WithUserIsAnonymous", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
opts := GetOpts()
|
||||
assert.False(opts.WithAnonymousListing)
|
||||
assert.False(opts.WithUserIsAnonymous)
|
||||
|
||||
opts = GetOpts(WithAnonymousListing(true))
|
||||
assert.True(opts.WithAnonymousListing)
|
||||
opts = GetOpts(WithUserIsAnonymous(true))
|
||||
assert.True(opts.WithUserIsAnonymous)
|
||||
})
|
||||
t.Run("WithOutputFields", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
opts := GetOpts()
|
||||
assert.Nil(opts.WithOutputFields)
|
||||
|
||||
var out perms.OutputFieldsMap
|
||||
|
||||
opts = GetOpts(WithOutputFields(&out))
|
||||
require.NotNil(opts.WithOutputFields)
|
||||
assert.Nil(*opts.WithOutputFields)
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package handlers
|
||||
|
||||
import "github.com/hashicorp/go-hclog"
|
||||
import (
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
// GetOpts - iterate the inbound Options and return a struct
|
||||
func GetOpts(opt ...Option) options {
|
||||
@@ -18,9 +23,13 @@ type Option func(*options)
|
||||
|
||||
// options = how options are represented
|
||||
type options struct {
|
||||
withDiscardUnknownFields bool
|
||||
WithLogger hclog.Logger
|
||||
WithAnonymousListing bool
|
||||
withDiscardUnknownFields bool
|
||||
WithLogger hclog.Logger
|
||||
WithUserIsAnonymous bool
|
||||
WithOutputFields *perms.OutputFieldsMap
|
||||
WithScope *scopes.ScopeInfo
|
||||
WithAuthorizedActions []string
|
||||
WithAuthorizedCollectionActions map[string]*structpb.ListValue
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
@@ -43,10 +52,42 @@ func WithLogger(logger hclog.Logger) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithAnonymousListing provides an option when creating responses to only include those
|
||||
// desired for listing to anonymous users.
|
||||
func WithAnonymousListing(anonListing bool) Option {
|
||||
// DEPRECATED: Superceded by WithOutputFields. Will be removed once all handlers
|
||||
// have been migrated to that.
|
||||
func WithUserIsAnonymous(anonListing bool) Option {
|
||||
return func(o *options) {
|
||||
o.WithAnonymousListing = anonListing
|
||||
o.WithUserIsAnonymous = anonListing
|
||||
}
|
||||
}
|
||||
|
||||
// WithOutputFields provides an option when creating responses to only include
|
||||
// specific fields
|
||||
func WithOutputFields(fields *perms.OutputFieldsMap) Option {
|
||||
return func(o *options) {
|
||||
o.WithOutputFields = fields
|
||||
}
|
||||
}
|
||||
|
||||
// WithScope provides an option when creating responses to include the given
|
||||
// scope if allowed
|
||||
func WithScope(scp *scopes.ScopeInfo) Option {
|
||||
return func(o *options) {
|
||||
o.WithScope = scp
|
||||
}
|
||||
}
|
||||
|
||||
// WithAuthorizedActions provides an option when creating responses to include the given
|
||||
// authorized actions if allowed
|
||||
func WithAuthorizedActions(acts []string) Option {
|
||||
return func(o *options) {
|
||||
o.WithAuthorizedActions = acts
|
||||
}
|
||||
}
|
||||
|
||||
// WithAuthorizedCollectionActions provides an option when creating responses to include the given
|
||||
// authorized collection actions if allowed
|
||||
func WithAuthorizedCollectionActions(colActs map[string]*structpb.ListValue) Option {
|
||||
return func(o *options) {
|
||||
o.WithAuthorizedCollectionActions = colActs
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/roles"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -105,22 +107,41 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(items) == 0 {
|
||||
return &pbs.ListRolesResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Role, 0, len(items))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
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 {
|
||||
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()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -130,6 +151,8 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
|
||||
|
||||
// GetRoles implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.GetRoleResponse, error) {
|
||||
const op = "roles.(Service).GetRole"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,17 +160,37 @@ func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.Get
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
r, prs, rgs, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.GetRoleResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetRoleResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateRole implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*pbs.CreateRoleResponse, error) {
|
||||
const op = "roles.(Service).CreateRole"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -159,13 +202,33 @@ func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*p
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.CreateRoleResponse{Item: r, Uri: fmt.Sprintf("roles/%s", r.GetId())}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, nil, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateRoleResponse{Item: item, Uri: fmt.Sprintf("roles/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateRole implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*pbs.UpdateRoleResponse, error) {
|
||||
const op = "roles.(Service).UpdateRole"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -173,13 +236,31 @@ func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*p
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
r, prs, rgs, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.UpdateRoleResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateRoleResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteRole implements the interface pbs.RoleServiceServer.
|
||||
@@ -200,6 +281,8 @@ func (s Service) DeleteRole(ctx context.Context, req *pbs.DeleteRoleRequest) (*p
|
||||
|
||||
// AddRolePrincipals implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipalsRequest) (*pbs.AddRolePrincipalsResponse, error) {
|
||||
const op = "roles.(Service).AddRolePrincipals"
|
||||
|
||||
if err := validateAddRolePrincipalsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -207,17 +290,37 @@ func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipa
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
r, err := s.addPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
|
||||
r, prs, rgs, err := s.addPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.AddRolePrincipalsResponse{Item: r}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddRolePrincipalsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetRolePrincipals implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipalsRequest) (*pbs.SetRolePrincipalsResponse, error) {
|
||||
const op = "roles.(Service).SetRolePrincipals"
|
||||
|
||||
if err := validateSetRolePrincipalsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -225,17 +328,37 @@ func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipa
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
r, err := s.setPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
|
||||
r, prs, rgs, err := s.setPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.SetRolePrincipalsResponse{Item: r}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetRolePrincipalsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// RemoveRolePrincipals implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePrincipalsRequest) (*pbs.RemoveRolePrincipalsResponse, error) {
|
||||
const op = "roles.(Service).RemoveRolePrincipals"
|
||||
|
||||
if err := validateRemoveRolePrincipalsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -243,17 +366,37 @@ func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePr
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
r, err := s.removePrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
|
||||
r, prs, rgs, err := s.removePrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.RemoveRolePrincipalsResponse{Item: r}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveRolePrincipalsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// AddRoleGrants implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsRequest) (*pbs.AddRoleGrantsResponse, error) {
|
||||
const op = "roles.(Service).AddRoleGrants"
|
||||
|
||||
if err := validateAddRoleGrantsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -261,17 +404,37 @@ func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsReques
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
r, err := s.addGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
|
||||
r, prs, rgs, err := s.addGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.AddRoleGrantsResponse{Item: r}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddRoleGrantsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetRoleGrants implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsRequest) (*pbs.SetRoleGrantsResponse, error) {
|
||||
const op = "roles.(Service).SetRoleGrants"
|
||||
|
||||
if err := validateSetRoleGrantsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -279,17 +442,37 @@ func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsReques
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
r, err := s.setGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
|
||||
r, prs, rgs, err := s.setGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.SetRoleGrantsResponse{Item: r}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetRoleGrantsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// RemoveRoleGrants implements the interface pbs.RoleServiceServer.
|
||||
func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrantsRequest) (*pbs.RemoveRoleGrantsResponse, error) {
|
||||
const op = "roles.(Service).RemoveRoleGrants"
|
||||
|
||||
if err := validateRemoveRoleGrantsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -297,34 +480,52 @@ func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrants
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
r, err := s.removeGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
|
||||
r, prs, rgs, err := s.removeGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Scope = authResults.Scope
|
||||
r.AuthorizedActions = authResults.FetchActionSetForId(ctx, r.Id, IdActions).Strings()
|
||||
return &pbs.RemoveRoleGrantsResponse{Item: r}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Role, error) {
|
||||
repo, err := s.repoFn()
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveRoleGrantsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
|
||||
return nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
|
||||
}
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
|
||||
return nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Role) (*pb.Role, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Role) (*iam.Role, error) {
|
||||
const op = "roles.(Service).createInRepo"
|
||||
var opts []iam.Option
|
||||
if item.GetName() != nil {
|
||||
@@ -351,10 +552,10 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Role
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create role but no error returned from repository.")
|
||||
}
|
||||
return toProto(out, nil, nil), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Role) (*pb.Role, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Role) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "roles.(Service).updateInRepo"
|
||||
var opts []iam.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
@@ -370,25 +571,25 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
|
||||
|
||||
u, err := iam.NewRole(scopeId, opts...)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build role for update: %v.", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build role for update: %v.", err)
|
||||
}
|
||||
u.PublicId = id
|
||||
dbMask := maskManager.Translate(mask)
|
||||
if len(dbMask) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
return nil, nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
out, pr, gr, rowsUpdated, err := repo.UpdateRole(ctx, u, version, dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
return nil, nil, nil, errors.Wrap(err, op)
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Role %q doesn't exist or incorrect version provided.", id)
|
||||
return nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out, pr, gr), nil
|
||||
return out, pr, gr, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
@@ -407,7 +608,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Role, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*iam.Role, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -416,103 +617,98 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Rol
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outRl []*pb.Role
|
||||
for _, g := range rl {
|
||||
// TODO: Attach principals and grants to ListRoles response.
|
||||
outRl = append(outRl, toProto(g, nil, nil))
|
||||
}
|
||||
return outRl, nil
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
func (s Service) addPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*pb.Role, error) {
|
||||
func (s Service) addPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "roles.(Service).addPrincpleInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_, err = repo.AddPrincipalRoles(ctx, roleId, version, strutil.RemoveDuplicates(principalIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add principals to role: %v.", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add principals to role: %v.", err)
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after adding principals"))
|
||||
return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after adding principals"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding principals to it.")
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding principals to it.")
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) setPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*pb.Role, error) {
|
||||
func (s Service) setPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "roles.(Service).setPrinciplesInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_, _, err = repo.SetPrincipalRoles(ctx, roleId, version, strutil.RemoveDuplicates(principalIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set principals on role: %v.", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set principals on role: %v.", err)
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after setting principals"))
|
||||
return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after setting principals"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting principals for it.")
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting principals for it.")
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) removePrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*pb.Role, error) {
|
||||
func (s Service) removePrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "roles.(Service).removePrinciplesInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_, err = repo.DeletePrincipalRoles(ctx, roleId, version, strutil.RemoveDuplicates(principalIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove principals from role: %v.", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove principals from role: %v.", err)
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after removing principals"))
|
||||
return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after removing principals"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing principals from it.")
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing principals from it.")
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) addGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*pb.Role, error) {
|
||||
func (s Service) addGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "service.(Service).addGrantsInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_, err = repo.AddRoleGrants(ctx, roleId, version, strutil.RemoveDuplicates(grants, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add grants to role: %v.", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add grants to role: %v.", err)
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after adding grants"))
|
||||
return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after adding grants"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding grants to it.")
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding grants to it.")
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) setGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*pb.Role, error) {
|
||||
func (s Service) setGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "roles.(Service).setGrantsInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// If no grant was provided, we clear the grants.
|
||||
if grants == nil {
|
||||
@@ -521,37 +717,37 @@ func (s Service) setGrantsInRepo(ctx context.Context, roleId string, grants []st
|
||||
_, _, err = repo.SetRoleGrants(ctx, roleId, version, strutil.RemoveDuplicates(grants, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set grants on role: %v.", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set grants on role: %v.", err)
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after setting grants"))
|
||||
return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up role after setting grants"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting grants on it.")
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting grants on it.")
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) removeGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*pb.Role, error) {
|
||||
func (s Service) removeGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []iam.PrincipalRole, []*iam.RoleGrant, error) {
|
||||
const op = "roles.(Service).removeGrantsInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
_, err = repo.DeleteRoleGrants(ctx, roleId, version, strutil.RemoveDuplicates(grants, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove grants from role: %v", err)
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove grants from role: %v", err)
|
||||
}
|
||||
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("uable to look up role after removing grant"))
|
||||
return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg("uable to look up role after removing grant"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing grants from it.")
|
||||
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing grants from it.")
|
||||
}
|
||||
return toProto(out, pr, roleGrants), nil
|
||||
return out, pr, roleGrants, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
@@ -593,57 +789,91 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *iam.Role, principals []iam.PrincipalRole, grants []*iam.RoleGrant) *pb.Role {
|
||||
out := pb.Role{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.GetVersion(),
|
||||
func toProto(ctx context.Context, in *iam.Role, principals []iam.PrincipalRole, grants []*iam.RoleGrant, opt ...handlers.Option) (*pb.Role, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building role proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Role{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
for _, p := range principals {
|
||||
principal := &pb.Principal{
|
||||
Id: p.GetPrincipalId(),
|
||||
Type: p.GetType(),
|
||||
ScopeId: p.GetPrincipalScopeId(),
|
||||
}
|
||||
out.Principals = append(out.Principals, principal)
|
||||
out.PrincipalIds = append(out.PrincipalIds, p.GetPrincipalId())
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
for _, g := range grants {
|
||||
out.GrantStrings = append(out.GrantStrings, g.GetRawGrant())
|
||||
parsed, err := perms.Parse(in.GetGrantScopeId(), g.GetRawGrant())
|
||||
if err != nil {
|
||||
// This should never happen as we validate on the way in, but let's
|
||||
// return what we can since we are still returning the raw grant
|
||||
out.Grants = append(out.Grants, &pb.Grant{
|
||||
Raw: g.GetRawGrant(),
|
||||
Canonical: "<parse_error>",
|
||||
Json: nil,
|
||||
})
|
||||
} else {
|
||||
_, actions := parsed.Actions()
|
||||
out.Grants = append(out.Grants, &pb.Grant{
|
||||
Raw: g.GetRawGrant(),
|
||||
Canonical: g.GetCanonicalGrant(),
|
||||
Json: &pb.GrantJson{
|
||||
Id: parsed.Id(),
|
||||
Type: parsed.Type().String(),
|
||||
Actions: actions,
|
||||
},
|
||||
})
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if in.GetGrantScopeId() != "" {
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.GrantScopeIdField) && in.GetGrantScopeId() != "" {
|
||||
out.GrantScopeId = &wrapperspb.StringValue{Value: in.GetGrantScopeId()}
|
||||
}
|
||||
return &out
|
||||
if outputFields.Has(globals.PrincipalIdsField) {
|
||||
for _, p := range principals {
|
||||
out.PrincipalIds = append(out.PrincipalIds, p.GetPrincipalId())
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.PrincipalsField) {
|
||||
for _, p := range principals {
|
||||
principal := &pb.Principal{
|
||||
Id: p.GetPrincipalId(),
|
||||
Type: p.GetType(),
|
||||
ScopeId: p.GetPrincipalScopeId(),
|
||||
}
|
||||
out.Principals = append(out.Principals, principal)
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.GrantStringsField) {
|
||||
for _, g := range grants {
|
||||
out.GrantStrings = append(out.GrantStrings, g.GetRawGrant())
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.GrantsField) {
|
||||
for _, g := range grants {
|
||||
parsed, err := perms.Parse(in.GetGrantScopeId(), g.GetRawGrant())
|
||||
if err != nil {
|
||||
// This should never happen as we validate on the way in, but let's
|
||||
// return what we can since we are still returning the raw grant
|
||||
out.Grants = append(out.Grants, &pb.Grant{
|
||||
Raw: g.GetRawGrant(),
|
||||
Canonical: "<parse_error>",
|
||||
Json: nil,
|
||||
})
|
||||
} else {
|
||||
_, actions := parsed.Actions()
|
||||
out.Grants = append(out.Grants, &pb.Grant{
|
||||
Raw: g.GetRawGrant(),
|
||||
Canonical: g.GetCanonicalGrant(),
|
||||
Json: &pb.GrantJson{
|
||||
Id: parsed.Id(),
|
||||
Type: parsed.Type().String(),
|
||||
Actions: actions,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -294,14 +294,29 @@ func TestList(t *testing.T) {
|
||||
s, err := roles.NewService(repoFn)
|
||||
require.NoError(err, "Couldn't create new role service.")
|
||||
|
||||
// Test the non-anon case
|
||||
got, gErr := s.ListRoles(auth.DisabledAuthTestContext(repoFn, tc.req.GetScopeId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err))
|
||||
} else {
|
||||
require.NoError(gErr)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListRoles(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
|
||||
// Test the anon case
|
||||
got, gErr = s.ListRoles(auth.DisabledAuthTestContext(repoFn, 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.Nil(item.CreatedTime)
|
||||
require.Nil(item.PrincipalIds)
|
||||
require.Nil(item.Principals)
|
||||
require.Nil(item.Grants)
|
||||
require.Nil(item.GrantStrings)
|
||||
require.Nil(item.UpdatedTime)
|
||||
require.Zero(item.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -29,9 +31,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/strutil"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
@@ -54,21 +54,33 @@ var (
|
||||
action.List,
|
||||
}
|
||||
|
||||
orgCollectionTypeMap = map[resource.Type]action.ActionSet{
|
||||
resource.AuthMethod: authmethods.CollectionActions,
|
||||
resource.AuthToken: authtokens.CollectionActions,
|
||||
resource.Group: groups.CollectionActions,
|
||||
resource.Role: roles.CollectionActions,
|
||||
resource.Scope: CollectionActions,
|
||||
resource.Session: sessions.CollectionActions,
|
||||
resource.User: users.CollectionActions,
|
||||
}
|
||||
scopeCollectionTypeMapMap = map[string]map[resource.Type]action.ActionSet{
|
||||
scope.Global.String(): {
|
||||
resource.AuthMethod: authmethods.CollectionActions,
|
||||
resource.AuthToken: authtokens.CollectionActions,
|
||||
resource.Group: groups.CollectionActions,
|
||||
resource.Role: roles.CollectionActions,
|
||||
resource.Scope: CollectionActions,
|
||||
resource.Session: sessions.CollectionActions,
|
||||
resource.User: users.CollectionActions,
|
||||
},
|
||||
|
||||
projectCollectionTypeMap = map[resource.Type]action.ActionSet{
|
||||
resource.Group: groups.CollectionActions,
|
||||
resource.HostCatalog: host_catalogs.CollectionActions,
|
||||
resource.Role: roles.CollectionActions,
|
||||
resource.Target: targets.CollectionActions,
|
||||
scope.Org.String(): {
|
||||
resource.AuthMethod: authmethods.CollectionActions,
|
||||
resource.AuthToken: authtokens.CollectionActions,
|
||||
resource.Group: groups.CollectionActions,
|
||||
resource.Role: roles.CollectionActions,
|
||||
resource.Scope: CollectionActions,
|
||||
resource.Session: sessions.CollectionActions,
|
||||
resource.User: users.CollectionActions,
|
||||
},
|
||||
|
||||
scope.Project.String(): {
|
||||
resource.Group: groups.CollectionActions,
|
||||
resource.HostCatalog: host_catalogs.CollectionActions,
|
||||
resource.Role: roles.CollectionActions,
|
||||
resource.Target: targets.CollectionActions,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -97,36 +109,6 @@ func NewService(repo common.IamRepoFactory) (Service, error) {
|
||||
|
||||
var _ pbs.ScopeServiceServer = Service{}
|
||||
|
||||
func populateCollectionAuthorizedActions(ctx context.Context,
|
||||
authResults auth.VerifyResults,
|
||||
item *pb.Scope) error {
|
||||
res := &perms.Resource{
|
||||
ScopeId: item.Id,
|
||||
}
|
||||
mapToRange := orgCollectionTypeMap
|
||||
if item.Type == "project" {
|
||||
mapToRange = projectCollectionTypeMap
|
||||
}
|
||||
// Range over the defined collections and check permissions against those
|
||||
// collections. We use the ID of this scope being returned, not its parent,
|
||||
// hence passing in a resource here.
|
||||
for k, v := range mapToRange {
|
||||
res.Type = k
|
||||
acts := authResults.FetchActionSetForType(ctx, k, v, auth.WithResource(res)).Strings()
|
||||
if len(acts) > 0 {
|
||||
if item.AuthorizedCollectionActions == nil {
|
||||
item.AuthorizedCollectionActions = make(map[string]*structpb.ListValue)
|
||||
}
|
||||
lv, err := structpb.NewList(strutil.StringListToInterfaceList(acts))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.AuthorizedCollectionActions[k.String()+"s"] = lv
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListScopes implements the interface pbs.ScopeServiceServer.
|
||||
func (s Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*pbs.ListScopesResponse, error) {
|
||||
if req.GetScopeId() == "" {
|
||||
@@ -158,39 +140,65 @@ func (s Service) ListScopes(ctx context.Context, req *pbs.ListScopesRequest) (*p
|
||||
return &pbs.ListScopesResponse{}, nil
|
||||
}
|
||||
|
||||
pl, err := s.listFromRepo(ctx, scopeIds, authResults.UserId == auth.AnonymousUserId)
|
||||
pl, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pl) == 0 {
|
||||
return &pbs.ListScopesResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Scope, 0, len(pl))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
Type: resource.Scope,
|
||||
}
|
||||
for _, item := range pl {
|
||||
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 {
|
||||
res.Id = item.GetPublicId()
|
||||
res.ScopeId = item.GetParentId()
|
||||
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, item); err != nil {
|
||||
|
||||
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.GetParentId()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, scopeCollectionTypeMapMap[item.Type], item.GetPublicId(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := ToProto(ctx, item, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
SortScopes(finalItems)
|
||||
return &pbs.ListScopesResponse{Items: finalItems}, nil
|
||||
}
|
||||
|
||||
// GetScopes implements the interface pbs.ScopeServiceServer.
|
||||
func (s Service) GetScope(ctx context.Context, req *pbs.GetScopeRequest) (*pbs.GetScopeResponse, error) {
|
||||
const op = "scopes.(Service).GetScope"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -202,21 +210,45 @@ func (s Service) GetScope(ctx context.Context, req *pbs.GetScopeRequest) (*pbs.G
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Scope = authResults.Scope
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
act := IdActions
|
||||
// Can't delete global so elide it
|
||||
if p.Id == "global" {
|
||||
act = act[0:2]
|
||||
if p.GetPublicId() == "global" {
|
||||
act = act[0:3]
|
||||
}
|
||||
p.AuthorizedActions = authResults.FetchActionSetForId(ctx, p.Id, act).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, p); err != nil {
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, p.GetPublicId(), act).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, scopeCollectionTypeMapMap[p.Type], p.GetPublicId(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := ToProto(ctx, p, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.GetScopeResponse{Item: p}, nil
|
||||
|
||||
return &pbs.GetScopeResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateScope implements the interface pbs.ScopeServiceServer.
|
||||
func (s Service) CreateScope(ctx context.Context, req *pbs.CreateScopeRequest) (*pbs.CreateScopeResponse, error) {
|
||||
const op = "scopes.(Service).CreateScope"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -228,16 +260,40 @@ func (s Service) CreateScope(ctx context.Context, req *pbs.CreateScopeRequest) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Scope = authResults.Scope
|
||||
p.AuthorizedActions = authResults.FetchActionSetForId(ctx, p.Id, IdActions).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, p); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, p.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, scopeCollectionTypeMapMap[p.Type], p.GetPublicId(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := ToProto(ctx, p, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.CreateScopeResponse{Item: p, Uri: fmt.Sprintf("scopes/%s", p.GetId())}, nil
|
||||
|
||||
return &pbs.CreateScopeResponse{Item: item, Uri: fmt.Sprintf("scopes/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateScope implements the interface pbs.ScopeServiceServer.
|
||||
func (s Service) UpdateScope(ctx context.Context, req *pbs.UpdateScopeRequest) (*pbs.UpdateScopeResponse, error) {
|
||||
const op = "scopes.(Service).UpdateScope"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -249,12 +305,34 @@ func (s Service) UpdateScope(ctx context.Context, req *pbs.UpdateScopeRequest) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Scope = authResults.Scope
|
||||
p.AuthorizedActions = authResults.FetchActionSetForId(ctx, p.Id, IdActions).Strings()
|
||||
if err := populateCollectionAuthorizedActions(ctx, authResults, p); err != nil {
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, p.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
collectionActions, err := auth.CalculateAuthorizedCollectionActions(ctx, authResults, scopeCollectionTypeMapMap[p.Type], p.GetPublicId(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedCollectionActions(collectionActions))
|
||||
}
|
||||
|
||||
item, err := ToProto(ctx, p, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.UpdateScopeResponse{Item: p}, nil
|
||||
|
||||
return &pbs.UpdateScopeResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteScope implements the interface pbs.ScopeServiceServer.
|
||||
@@ -273,22 +351,22 @@ func (s Service) DeleteScope(ctx context.Context, req *pbs.DeleteScopeRequest) (
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Scope, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*iam.Scope, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := repo.LookupScope(ctx, id)
|
||||
out, err := repo.LookupScope(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p == nil {
|
||||
if out == nil {
|
||||
return nil, handlers.NotFoundErrorf("Scope %q doesn't exist.", id)
|
||||
}
|
||||
return ToProto(p), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, authResults auth.VerifyResults, req *pbs.CreateScopeRequest) (*pb.Scope, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, authResults auth.VerifyResults, req *pbs.CreateScopeRequest) (*iam.Scope, error) {
|
||||
const op = "scopes.(Service).createInRepo"
|
||||
item := req.GetItem()
|
||||
var opts []iam.Option
|
||||
@@ -324,10 +402,10 @@ func (s Service) createInRepo(ctx context.Context, authResults auth.VerifyResult
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create scope but no error returned from repository.")
|
||||
}
|
||||
return ToProto(out), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, parentScope *pb.ScopeInfo, scopeId string, mask []string, item *pb.Scope) (*pb.Scope, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, parentScope *pb.ScopeInfo, scopeId string, mask []string, item *pb.Scope) (*iam.Scope, error) {
|
||||
const op = "scope.(Service).updateInRepo"
|
||||
var opts []iam.Option
|
||||
var scopeDesc, scopeName, scopePrimaryAuthMethodId string
|
||||
@@ -384,7 +462,7 @@ func (s Service) updateInRepo(ctx context.Context, parentScope *pb.ScopeInfo, sc
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Scope %q doesn't exist or incorrect version provided.", scopeId)
|
||||
}
|
||||
return ToProto(out), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, scopeId string) (bool, error) {
|
||||
@@ -408,7 +486,7 @@ func SortScopes(scps []*pb.Scope) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string, anonUser bool) ([]*pb.Scope, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*iam.Scope, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -417,13 +495,7 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string, anonUser b
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to list scopes: %v", err)
|
||||
}
|
||||
|
||||
var outPl []*pb.Scope
|
||||
for _, scp := range scps {
|
||||
outPl = append(outPl, ToProto(scp, handlers.WithAnonymousListing(anonUser)))
|
||||
}
|
||||
SortScopes(outPl)
|
||||
return outPl, nil
|
||||
return scps, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
@@ -465,29 +537,52 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func ToProto(in *iam.Scope, opt ...handlers.Option) *pb.Scope {
|
||||
anonListing := handlers.GetOpts(opt...).WithAnonymousListing
|
||||
out := pb.Scope{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetParentId(),
|
||||
Type: in.GetType(),
|
||||
func ToProto(ctx context.Context, in *iam.Scope, opt ...handlers.Option) (*pb.Scope, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building scope proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Scope{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetParentId()
|
||||
}
|
||||
if in.GetPrimaryAuthMethodId() != "" {
|
||||
out.PrimaryAuthMethodId = &wrapperspb.StringValue{Value: in.GetPrimaryAuthMethodId()}
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = in.GetType()
|
||||
}
|
||||
if !anonListing {
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedCollectionActionsField) {
|
||||
out.AuthorizedCollectionActions = opts.WithAuthorizedCollectionActions
|
||||
}
|
||||
if outputFields.Has(globals.PrimaryAuthMethodId) && in.GetPrimaryAuthMethodId() != "" {
|
||||
out.PrimaryAuthMethodId = &wrapperspb.StringValue{Value: in.GetPrimaryAuthMethodId()}
|
||||
}
|
||||
|
||||
return &out
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
pb "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/perms"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers/scopes"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
@@ -290,13 +291,16 @@ func TestList(t *testing.T) {
|
||||
_, err = repo.DeleteScope(context.Background(), p2.GetPublicId())
|
||||
require.NoError(t, err)
|
||||
|
||||
outputFields := perms.OutputFieldsMap(nil).SelfOrDefaults("u_auth")
|
||||
var initialOrgs []*pb.Scope
|
||||
globalScope := &pb.ScopeInfo{Id: "global", Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}
|
||||
oNoProjectsProto := scopes.ToProto(oNoProjects)
|
||||
oNoProjectsProto, err := scopes.ToProto(context.Background(), oNoProjects, handlers.WithOutputFields(&outputFields))
|
||||
require.NoError(t, err)
|
||||
oNoProjectsProto.Scope = globalScope
|
||||
oNoProjectsProto.AuthorizedActions = testAuthorizedActions
|
||||
oNoProjectsProto.AuthorizedCollectionActions = orgAuthorizedCollectionActions
|
||||
oWithProjectsProto := scopes.ToProto(oWithProjects)
|
||||
oWithProjectsProto, err := scopes.ToProto(context.Background(), oWithProjects, handlers.WithOutputFields(&outputFields))
|
||||
require.NoError(t, err)
|
||||
oWithProjectsProto.Scope = globalScope
|
||||
oWithProjectsProto.AuthorizedActions = testAuthorizedActions
|
||||
oWithProjectsProto.AuthorizedCollectionActions = orgAuthorizedCollectionActions
|
||||
@@ -342,7 +346,7 @@ func TestList(t *testing.T) {
|
||||
require.NoError(err, "Couldn't create new role service.")
|
||||
|
||||
// Test with non-anonymous listing first
|
||||
got, gErr := s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId, auth.WithUserId("u_auth")), tc.req)
|
||||
got, gErr := s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListScopes(%+v) got error\n%v, wanted\n%v", tc.req, gErr, tc.err)
|
||||
@@ -351,8 +355,9 @@ func TestList(t *testing.T) {
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListScopes(%q) got response\n%q\nwanted\n%q", tc.req, got, tc.res)
|
||||
|
||||
// Now test with anonymous listing
|
||||
got, gErr = s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), tc.req)
|
||||
got, gErr = s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId, auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
assert.Nil(item.CreatedTime)
|
||||
assert.Nil(item.UpdatedTime)
|
||||
@@ -455,7 +460,7 @@ func TestList(t *testing.T) {
|
||||
require.NoError(err, "Couldn't create new role service.")
|
||||
|
||||
// Test with non-anonymous listing first
|
||||
got, gErr := s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId, auth.WithUserId("u_auth")), tc.req)
|
||||
got, gErr := s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListScopes(%+v) got error\n%v, wanted\n%v", tc.req, gErr, tc.err)
|
||||
@@ -465,8 +470,9 @@ func TestList(t *testing.T) {
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListScopes(%q) got response\n%q, wanted\n%q", tc.req, got, tc.res)
|
||||
|
||||
// Now test with anonymous listing
|
||||
got, gErr = s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId), tc.req)
|
||||
got, gErr = s.ListScopes(auth.DisabledAuthTestContext(repoFn, tc.scopeId, auth.WithUserId(auth.AnonymousUserId)), tc.req)
|
||||
require.NoError(gErr)
|
||||
assert.Len(got.Items, len(tc.res.Items))
|
||||
for _, item := range got.GetItems() {
|
||||
assert.Nil(item.CreatedTime)
|
||||
assert.Nil(item.UpdatedTime)
|
||||
|
||||
@@ -5,11 +5,13 @@ import (
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/sessions"
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -18,6 +20,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/types/action"
|
||||
"github.com/hashicorp/boundary/internal/types/resource"
|
||||
"github.com/hashicorp/boundary/internal/types/scope"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -62,6 +65,8 @@ var _ pbs.SessionServiceServer = Service{}
|
||||
|
||||
// GetSessions implements the interface pbs.SessionServiceServer.
|
||||
func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*pbs.GetSessionResponse, error) {
|
||||
const op = "sessions.(Service).GetSession"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -74,17 +79,42 @@ func (s Service) GetSession(ctx context.Context, req *pbs.GetSessionRequest) (*p
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authzdActions := authResults.FetchActionSetForId(ctx, ses.Id, IdActions)
|
||||
var outputFields perms.OutputFieldsMap
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, ses.GetPublicId(), IdActions)
|
||||
|
||||
// Check to see if we need to verify Read vs. just ReadSelf
|
||||
if ses.GetUserId() != authResults.UserId {
|
||||
if !authzdActions.HasAction(action.Read) {
|
||||
if ses.UserId != authResults.UserId {
|
||||
if !authorizedActions.HasAction(action.Read) {
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
outputFields = authResults.FetchOutputFields(perms.Resource{
|
||||
Id: ses.GetPublicId(),
|
||||
ScopeId: ses.ScopeId,
|
||||
Type: resource.Session,
|
||||
}, action.Read).SelfOrDefaults(authResults.UserId)
|
||||
} else {
|
||||
var ok bool
|
||||
outputFields, ok = requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
}
|
||||
|
||||
ses.Scope = authResults.Scope
|
||||
ses.AuthorizedActions = authzdActions.Strings()
|
||||
return &pbs.GetSessionResponse{Item: ses}, nil
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, ses, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetSessionResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// ListSessions implements the interface pbs.SessionServiceServer.
|
||||
@@ -116,32 +146,48 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
return &pbs.ListSessionsResponse{}, nil
|
||||
}
|
||||
|
||||
seslist, err := s.listFromRepo(ctx, session.WithScopeIds(scopeIds))
|
||||
sesList, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(sesList) == 0 {
|
||||
return &pbs.ListSessionsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Session, 0, len(seslist))
|
||||
res := &perms.Resource{
|
||||
finalItems := make([]*pb.Session, 0, len(sesList))
|
||||
res := perms.Resource{
|
||||
Type: resource.Session,
|
||||
}
|
||||
for _, item := range seslist {
|
||||
item.Scope = scopeInfoMap[item.GetScopeId()]
|
||||
res.ScopeId = item.Scope.Id
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.Id, IdActions, auth.WithResource(res))
|
||||
for _, item := range sesList {
|
||||
res.Id = item.GetPublicId()
|
||||
res.ScopeId = item.ScopeId
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
|
||||
if len(authorizedActions) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if authorizedActions.OnlySelf() && item.GetUserId() != authResults.UserId {
|
||||
if authorizedActions.OnlySelf() && item.UserId != authResults.UserId {
|
||||
continue
|
||||
}
|
||||
|
||||
item.AuthorizedActions = authorizedActions.Strings()
|
||||
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.ScopeId]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
@@ -153,6 +199,8 @@ func (s Service) ListSessions(ctx context.Context, req *pbs.ListSessionsRequest)
|
||||
|
||||
// CancelSession implements the interface pbs.SessionServiceServer.
|
||||
func (s Service) CancelSession(ctx context.Context, req *pbs.CancelSessionRequest) (*pbs.CancelSessionResponse, error) {
|
||||
const op = "sessions.(Service).CancelSession"
|
||||
|
||||
if err := validateCancelRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,24 +213,50 @@ func (s Service) CancelSession(ctx context.Context, req *pbs.CancelSessionReques
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authzdActions := authResults.FetchActionSetForId(ctx, ses.Id, IdActions)
|
||||
// Check to see if we need to verify Cancel vs. just CancelSelf
|
||||
if ses.GetUserId() != authResults.UserId {
|
||||
if !authzdActions.HasAction(action.Cancel) {
|
||||
|
||||
var outputFields perms.OutputFieldsMap
|
||||
authorizedActions := authResults.FetchActionSetForId(ctx, ses.GetPublicId(), IdActions)
|
||||
|
||||
// Check to see if we need to verify Read vs. just ReadSelf
|
||||
if ses.UserId != authResults.UserId {
|
||||
if !authorizedActions.HasAction(action.Cancel) {
|
||||
return nil, handlers.ForbiddenError()
|
||||
}
|
||||
outputFields = authResults.FetchOutputFields(perms.Resource{
|
||||
Id: ses.GetPublicId(),
|
||||
ScopeId: ses.ScopeId,
|
||||
Type: resource.Session,
|
||||
}, action.Cancel).SelfOrDefaults(authResults.UserId)
|
||||
} else {
|
||||
var ok bool
|
||||
outputFields, ok = requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
}
|
||||
|
||||
ses, err = s.cancelInRepo(ctx, req.GetId(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ses.Scope = authResults.Scope
|
||||
ses.AuthorizedActions = authzdActions.Strings()
|
||||
return &pbs.CancelSessionResponse{Item: ses}, nil
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions.Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, ses, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.CancelSessionResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Session, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*session.Session, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -197,26 +271,22 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Session, error
|
||||
if sess == nil {
|
||||
return nil, handlers.NotFoundErrorf("Session %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(sess), nil
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, opts ...session.Option) ([]*pb.Session, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*session.Session, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seslist, err := repo.ListSessions(ctx, opts...)
|
||||
sesList, err := repo.ListSessions(ctx, session.WithScopeIds(scopeIds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outSl []*pb.Session
|
||||
for _, ses := range seslist {
|
||||
outSl = append(outSl, toProto(ses))
|
||||
}
|
||||
return outSl, nil
|
||||
return sesList, nil
|
||||
}
|
||||
|
||||
func (s Service) cancelInRepo(ctx context.Context, id string, version uint32) (*pb.Session, error) {
|
||||
func (s Service) cancelInRepo(ctx context.Context, id string, version uint32) (*session.Session, error) {
|
||||
const op = "sessions.(Service).cancelInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
@@ -226,7 +296,7 @@ func (s Service) cancelInRepo(ctx context.Context, id string, version uint32) (*
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to update session"))
|
||||
}
|
||||
return toProto(out), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
@@ -276,42 +346,89 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *session.Session) *pb.Session {
|
||||
out := pb.Session{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.ScopeId,
|
||||
TargetId: in.TargetId,
|
||||
Version: in.Version,
|
||||
UserId: in.UserId,
|
||||
HostId: in.HostId,
|
||||
HostSetId: in.HostSetId,
|
||||
AuthTokenId: in.AuthTokenId,
|
||||
Endpoint: in.Endpoint,
|
||||
Type: target.SubtypeFromId(in.TargetId).String(),
|
||||
// TODO: Provide the ServerType and the ServerId when that information becomes relevant in the API.
|
||||
func toProto(ctx context.Context, in *session.Session, opt ...handlers.Option) (*pb.Session, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building auth token proto")
|
||||
}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
CreatedTime: in.CreateTime.GetTimestamp(),
|
||||
UpdatedTime: in.UpdateTime.GetTimestamp(),
|
||||
ExpirationTime: in.ExpirationTime.GetTimestamp(),
|
||||
Certificate: in.Certificate,
|
||||
TerminationReason: in.TerminationReason,
|
||||
out := pb.Session{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.ScopeId
|
||||
}
|
||||
if outputFields.Has(globals.TargetIdField) {
|
||||
out.TargetId = in.TargetId
|
||||
}
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = target.SubtypeFromId(in.TargetId).String()
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.CreateTime.GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.UpdateTime.GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.Version
|
||||
}
|
||||
if outputFields.Has(globals.UserIdField) {
|
||||
out.UserId = in.UserId
|
||||
}
|
||||
if outputFields.Has(globals.HostIdField) {
|
||||
out.HostId = in.HostId
|
||||
}
|
||||
if outputFields.Has(globals.HostSetIdField) {
|
||||
out.HostSetId = in.HostSetId
|
||||
}
|
||||
if outputFields.Has(globals.AuthTokenIdField) {
|
||||
out.AuthTokenId = in.AuthTokenId
|
||||
}
|
||||
if outputFields.Has(globals.EndpointField) {
|
||||
out.Endpoint = in.Endpoint
|
||||
}
|
||||
if outputFields.Has(globals.HostIdField) {
|
||||
out.HostId = in.HostId
|
||||
}
|
||||
if outputFields.Has(globals.ExpirationTimeField) {
|
||||
out.ExpirationTime = in.ExpirationTime.GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.CertificateField) {
|
||||
out.Certificate = in.Certificate
|
||||
}
|
||||
if outputFields.Has(globals.TerminationReasonField) {
|
||||
out.TerminationReason = in.TerminationReason
|
||||
}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
// TODO: Provide the ServerType and the ServerId when that information becomes relevant in the API.
|
||||
if len(in.States) > 0 {
|
||||
out.Status = in.States[0].Status.String()
|
||||
if outputFields.Has(globals.StatusField) {
|
||||
out.Status = in.States[0].Status.String()
|
||||
}
|
||||
if outputFields.Has(globals.StatesField) {
|
||||
for _, s := range in.States {
|
||||
sessState := &pb.SessionState{
|
||||
Status: s.Status.String(),
|
||||
}
|
||||
if s.StartTime != nil {
|
||||
sessState.StartTime = s.StartTime.GetTimestamp()
|
||||
}
|
||||
if s.EndTime != nil {
|
||||
sessState.EndTime = s.EndTime.GetTimestamp()
|
||||
}
|
||||
out.States = append(out.States, sessState)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, s := range in.States {
|
||||
sessState := &pb.SessionState{
|
||||
Status: s.Status.String(),
|
||||
}
|
||||
if s.StartTime != nil {
|
||||
sessState.StartTime = s.StartTime.GetTimestamp()
|
||||
}
|
||||
if s.EndTime != nil {
|
||||
sessState.EndTime = s.EndTime.GetTimestamp()
|
||||
}
|
||||
out.States = append(out.States, sessState)
|
||||
}
|
||||
return &out
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -387,24 +387,44 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require, assert := require.New(t), assert.New(t)
|
||||
s, err := sessions.NewService(sessRepoFn, iamRepoFn)
|
||||
require.NoError(t, err, "Couldn't create new session service.")
|
||||
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)
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err), "ListSessions(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
} else {
|
||||
require.NoError(t, gErr)
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListSessions(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
if tc.res != nil {
|
||||
require.Equal(t, len(tc.res.GetItems()), len(got.GetItems()), "Didn't get expected number of sessions: %v", got.GetItems())
|
||||
require.Equal(len(tc.res.GetItems()), len(got.GetItems()), "Didn't get expected number of sessions: %v", got.GetItems())
|
||||
for i, wantSess := range tc.res.GetItems() {
|
||||
assert.True(t, got.GetItems()[i].GetExpirationTime().AsTime().Sub(wantSess.GetExpirationTime().AsTime()) < 10*time.Millisecond)
|
||||
assert.True(got.GetItems()[i].GetExpirationTime().AsTime().Sub(wantSess.GetExpirationTime().AsTime()) < 10*time.Millisecond)
|
||||
wantSess.ExpirationTime = got.GetItems()[i].GetExpirationTime()
|
||||
}
|
||||
}
|
||||
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform()), "ListSessions(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
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)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/db/timestamp"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"github.com/hashicorp/boundary/internal/servers"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/common/scopeids"
|
||||
@@ -145,26 +147,45 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
return &pbs.ListTargetsResponse{}, nil
|
||||
}
|
||||
|
||||
ul, err := s.listFromRepo(ctx, scopeIds)
|
||||
tl, err := s.listFromRepo(ctx, scopeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(tl) == 0 {
|
||||
return &pbs.ListTargetsResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.Target, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
finalItems := make([]*pb.Target, 0, len(tl))
|
||||
res := perms.Resource{
|
||||
Type: resource.Target,
|
||||
}
|
||||
for _, item := range ul {
|
||||
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 {
|
||||
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()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -174,6 +195,8 @@ func (s Service) ListTargets(ctx context.Context, req *pbs.ListTargetsRequest) (
|
||||
|
||||
// GetTargets implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) GetTarget(ctx context.Context, req *pbs.GetTargetRequest) (*pbs.GetTargetResponse, error) {
|
||||
const op = "targets.(Service).GetTarget"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -181,17 +204,37 @@ func (s Service) GetTarget(ctx context.Context, req *pbs.GetTargetRequest) (*pbs
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
t, ts, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.GetTargetResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetTargetResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateTarget implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) CreateTarget(ctx context.Context, req *pbs.CreateTargetRequest) (*pbs.CreateTargetResponse, error) {
|
||||
const op = "targets.(Service).CreateTarget"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -199,17 +242,37 @@ func (s Service) CreateTarget(ctx context.Context, req *pbs.CreateTargetRequest)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.createInRepo(ctx, req.GetItem())
|
||||
t, ts, err := s.createInRepo(ctx, req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.CreateTargetResponse{Item: u, Uri: fmt.Sprintf("targets/%s", u.GetId())}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.CreateTargetResponse{Item: item, Uri: fmt.Sprintf("targets/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateTarget implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) UpdateTarget(ctx context.Context, req *pbs.UpdateTargetRequest) (*pbs.UpdateTargetResponse, error) {
|
||||
const op = "targets.(Service).UpdateTarget"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -217,13 +280,31 @@ func (s Service) UpdateTarget(ctx context.Context, req *pbs.UpdateTargetRequest)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
t, ts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.UpdateTargetResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateTargetResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteTarget implements the interface pbs.TargetServiceServer.
|
||||
@@ -244,6 +325,8 @@ func (s Service) DeleteTarget(ctx context.Context, req *pbs.DeleteTargetRequest)
|
||||
|
||||
// AddTargetHostSets implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) AddTargetHostSets(ctx context.Context, req *pbs.AddTargetHostSetsRequest) (*pbs.AddTargetHostSetsResponse, error) {
|
||||
const op = "targets.(Service).AddTargetHostSets"
|
||||
|
||||
if err := validateAddRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -251,17 +334,37 @@ func (s Service) AddTargetHostSets(ctx context.Context, req *pbs.AddTargetHostSe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.addInRepo(ctx, req.GetId(), req.GetHostSetIds(), req.GetVersion())
|
||||
t, ts, err := s.addInRepo(ctx, req.GetId(), req.GetHostSetIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.AddTargetHostSetsResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddTargetHostSetsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetTargetHostSets implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) SetTargetHostSets(ctx context.Context, req *pbs.SetTargetHostSetsRequest) (*pbs.SetTargetHostSetsResponse, error) {
|
||||
const op = "targets.(Service).SetTargetHostSets"
|
||||
|
||||
if err := validateSetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -269,17 +372,37 @@ func (s Service) SetTargetHostSets(ctx context.Context, req *pbs.SetTargetHostSe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.setInRepo(ctx, req.GetId(), req.GetHostSetIds(), req.GetVersion())
|
||||
t, ts, err := s.setInRepo(ctx, req.GetId(), req.GetHostSetIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.SetTargetHostSetsResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetTargetHostSetsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// RemoveTargetHostSets implements the interface pbs.TargetServiceServer.
|
||||
func (s Service) RemoveTargetHostSets(ctx context.Context, req *pbs.RemoveTargetHostSetsRequest) (*pbs.RemoveTargetHostSetsResponse, error) {
|
||||
const op = "targets.(Service).RemoveTargetHostSets"
|
||||
|
||||
if err := validateRemoveRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -287,13 +410,31 @@ func (s Service) RemoveTargetHostSets(ctx context.Context, req *pbs.RemoveTarget
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.removeInRepo(ctx, req.GetId(), req.GetHostSetIds(), req.GetVersion())
|
||||
t, ts, err := s.removeInRepo(ctx, req.GetId(), req.GetHostSetIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.RemoveTargetHostSetsResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, t.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, t, ts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveTargetHostSetsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSessionRequest) (*pbs.AuthorizeSessionResponse, error) {
|
||||
@@ -574,25 +715,25 @@ HostSetIterationLoop:
|
||||
return &pbs.AuthorizeSessionResponse{Item: ret}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Target, error) {
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (target.Target, []*target.TargetSet, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
u, m, err := repo.LookupTarget(ctx, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, handlers.NotFoundErrorf("Target %q doesn't exist.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Target %q doesn't exist.", id)
|
||||
}
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if u == nil {
|
||||
return nil, handlers.NotFoundErrorf("Target %q doesn't exist.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Target %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(u, m)
|
||||
return u, m, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, item *pb.Target) (*pb.Target, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Target, []*target.TargetSet, error) {
|
||||
const op = "targets.(Service).createInRepo"
|
||||
opts := []target.Option{target.WithName(item.GetName().GetValue())}
|
||||
if item.GetDescription() != nil {
|
||||
@@ -609,30 +750,30 @@ func (s Service) createInRepo(ctx context.Context, item *pb.Target) (*pb.Target,
|
||||
}
|
||||
tcpAttrs := &pb.TcpTargetAttributes{}
|
||||
if err := handlers.StructToProto(item.GetAttributes(), tcpAttrs); err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Provided attributes don't match expected format.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Provided attributes don't match expected format.")
|
||||
}
|
||||
if tcpAttrs.GetDefaultPort().GetValue() != 0 {
|
||||
opts = append(opts, target.WithDefaultPort(tcpAttrs.GetDefaultPort().GetValue()))
|
||||
}
|
||||
u, err := target.NewTcpTarget(item.GetScopeId(), opts...)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for creation: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for creation: %v.", err)
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
out, m, err := repo.CreateTcpTarget(ctx, u)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to create target"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to create target"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create target but no error returned from repository.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create target but no error returned from repository.")
|
||||
}
|
||||
return toProto(out, m)
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Target) (*pb.Target, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Target) (target.Target, []*target.TargetSet, error) {
|
||||
const op = "targets.(Service).updateInRepo"
|
||||
var opts []target.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
@@ -652,7 +793,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
|
||||
}
|
||||
tcpAttrs := &pb.TcpTargetAttributes{}
|
||||
if err := handlers.StructToProto(item.GetAttributes(), tcpAttrs); err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Provided attributes don't match expected format.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.InvalidArgument, "Provided attributes don't match expected format.")
|
||||
}
|
||||
if tcpAttrs.GetDefaultPort().GetValue() != 0 {
|
||||
opts = append(opts, target.WithDefaultPort(tcpAttrs.GetDefaultPort().GetValue()))
|
||||
@@ -660,25 +801,25 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
|
||||
version := item.GetVersion()
|
||||
u, err := target.NewTcpTarget(scopeId, opts...)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for update: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for update: %v.", err)
|
||||
}
|
||||
u.PublicId = id
|
||||
dbMask := maskManager.Translate(mask)
|
||||
if len(dbMask) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid paths provided in the update mask."})
|
||||
return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid paths provided in the update mask."})
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
out, m, rowsUpdated, err := repo.UpdateTcpTarget(ctx, u, version, dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to update target"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to update target"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("Target %q not found or incorrect version provided.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("Target %q not found or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out, m)
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
@@ -697,7 +838,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Target, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]target.Target, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -706,74 +847,66 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Tar
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outUl []*pb.Target
|
||||
for _, u := range ul {
|
||||
o, err := toProto(u, nil)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to convert value to proto: %v.", err)
|
||||
}
|
||||
outUl = append(outUl, o)
|
||||
}
|
||||
return outUl, nil
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (s Service) addInRepo(ctx context.Context, targetId string, hostSetId []string, version uint32) (*pb.Target, error) {
|
||||
func (s Service) addInRepo(ctx context.Context, targetId string, hostSetId []string, version uint32) (target.Target, []*target.TargetSet, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
out, m, err := repo.AddTargetHostSets(ctx, targetId, version, strutil.RemoveDuplicates(hostSetId, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add host sets to target: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add host sets to target: %v.", err)
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after adding host sets to it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after adding host sets to it.")
|
||||
}
|
||||
return toProto(out, m)
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) setInRepo(ctx context.Context, targetId string, hostSetIds []string, version uint32) (*pb.Target, error) {
|
||||
func (s Service) setInRepo(ctx context.Context, targetId string, hostSetIds []string, version uint32) (target.Target, []*target.TargetSet, error) {
|
||||
const op = "targets.(Service).setInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
_, _, err = repo.SetTargetHostSets(ctx, targetId, version, strutil.RemoveDuplicates(hostSetIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set host sets in target: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set host sets in target: %v.", err)
|
||||
}
|
||||
|
||||
out, m, err := repo.LookupTarget(ctx, targetId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up target after setting host sets"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up target after setting host sets"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after setting host sets for it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after setting host sets for it.")
|
||||
}
|
||||
return toProto(out, m)
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) removeInRepo(ctx context.Context, targetId string, hostSetIds []string, version uint32) (*pb.Target, error) {
|
||||
func (s Service) removeInRepo(ctx context.Context, targetId string, hostSetIds []string, version uint32) (target.Target, []*target.TargetSet, error) {
|
||||
const op = "targets.(Service).removeInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = repo.DeleteTargeHostSets(ctx, targetId, version, strutil.RemoveDuplicates(hostSetIds, false))
|
||||
if err != nil {
|
||||
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove host sets from target: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove host sets from target: %v.", err)
|
||||
}
|
||||
out, m, err := repo.LookupTarget(ctx, targetId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up target after removing host sets"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up target after removing host sets"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after removing host sets from it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup target after removing host sets from it.")
|
||||
}
|
||||
return toProto(out, m)
|
||||
return out, m, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type, lookupOpt ...target.Option) auth.VerifyResults {
|
||||
@@ -829,41 +962,76 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type, looku
|
||||
return ret
|
||||
}
|
||||
|
||||
func toProto(in target.Target, m []*target.TargetSet) (*pb.Target, error) {
|
||||
out := pb.Target{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.GetVersion(),
|
||||
Type: target.TcpTargetType.String(),
|
||||
SessionMaxSeconds: wrapperspb.UInt32(in.GetSessionMaxSeconds()),
|
||||
SessionConnectionLimit: wrapperspb.Int32(in.GetSessionConnectionLimit()),
|
||||
func toProto(ctx context.Context, in target.Target, m []*target.TargetSet, opt ...handlers.Option) (*pb.Target, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building target proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.Target{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
if outputFields.Has(globals.TypeField) {
|
||||
out.Type = target.TcpTargetType.String()
|
||||
}
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if in.GetWorkerFilter() != "" {
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.SessionMaxSecondsField) {
|
||||
out.SessionMaxSeconds = wrapperspb.UInt32(in.GetSessionMaxSeconds())
|
||||
}
|
||||
if outputFields.Has(globals.SessionConnectionLimitField) {
|
||||
out.SessionConnectionLimit = wrapperspb.Int32(in.GetSessionConnectionLimit())
|
||||
}
|
||||
if outputFields.Has(globals.WorkerFilterField) && in.GetWorkerFilter() != "" {
|
||||
out.WorkerFilter = wrapperspb.String(in.GetWorkerFilter())
|
||||
}
|
||||
attrs := &pb.TcpTargetAttributes{}
|
||||
if in.GetDefaultPort() > 0 {
|
||||
attrs.DefaultPort = &wrappers.UInt32Value{Value: in.GetDefaultPort()}
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
st, err := handlers.ProtoToStruct(attrs)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "failed building password attribute struct: %v", err)
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
out.Attributes = st
|
||||
for _, hs := range m {
|
||||
out.HostSetIds = append(out.HostSetIds, hs.GetPublicId())
|
||||
out.HostSets = append(out.HostSets, &pb.HostSet{
|
||||
Id: hs.GetPublicId(),
|
||||
HostCatalogId: hs.GetCatalogId(),
|
||||
})
|
||||
if outputFields.Has(globals.HostSetIdsField) {
|
||||
for _, hs := range m {
|
||||
out.HostSetIds = append(out.HostSetIds, hs.GetPublicId())
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.HostSetsField) {
|
||||
for _, hs := range m {
|
||||
out.HostSets = append(out.HostSets, &pb.HostSet{
|
||||
Id: hs.GetPublicId(),
|
||||
HostCatalogId: hs.GetCatalogId(),
|
||||
})
|
||||
}
|
||||
}
|
||||
if outputFields.Has(globals.AttributesField) {
|
||||
attrs := &pb.TcpTargetAttributes{}
|
||||
if in.GetDefaultPort() > 0 {
|
||||
attrs.DefaultPort = &wrappers.UInt32Value{Value: in.GetDefaultPort()}
|
||||
}
|
||||
st, err := handlers.ProtoToStruct(attrs)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "failed building password attribute struct: %v", err)
|
||||
}
|
||||
out.Attributes = st
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
@@ -243,14 +243,29 @@ func TestList(t *testing.T) {
|
||||
s, err := testService(t, 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)
|
||||
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)
|
||||
} else {
|
||||
require.NoError(gErr)
|
||||
return
|
||||
}
|
||||
require.NoError(gErr)
|
||||
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)
|
||||
|
||||
// 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.Attributes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/auth/password"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/iam/store"
|
||||
"github.com/hashicorp/boundary/internal/perms"
|
||||
"github.com/hashicorp/boundary/internal/requests"
|
||||
"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"
|
||||
@@ -104,21 +106,41 @@ func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ul) == 0 {
|
||||
return &pbs.ListUsersResponse{}, nil
|
||||
}
|
||||
|
||||
filter, err := handlers.NewFilter(req.GetFilter())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalItems := make([]*pb.User, 0, len(ul))
|
||||
res := &perms.Resource{
|
||||
res := perms.Resource{
|
||||
Type: resource.User,
|
||||
}
|
||||
for _, item := range ul {
|
||||
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 {
|
||||
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()]))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, item, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filter.Match(item) {
|
||||
finalItems = append(finalItems, item)
|
||||
}
|
||||
@@ -128,6 +150,8 @@ func (s Service) ListUsers(ctx context.Context, req *pbs.ListUsersRequest) (*pbs
|
||||
|
||||
// GetUsers implements the interface pbs.UserServiceServer.
|
||||
func (s Service) GetUser(ctx context.Context, req *pbs.GetUserRequest) (*pbs.GetUserResponse, error) {
|
||||
const op = "users.(Service).GetUser"
|
||||
|
||||
if err := validateGetRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,17 +159,37 @@ func (s Service) GetUser(ctx context.Context, req *pbs.GetUserRequest) (*pbs.Get
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.getFromRepo(ctx, req.GetId())
|
||||
u, accts, err := s.getFromRepo(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.GetUserResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, accts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.GetUserResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// CreateUser implements the interface pbs.UserServiceServer.
|
||||
func (s Service) CreateUser(ctx context.Context, req *pbs.CreateUserRequest) (*pbs.CreateUserResponse, error) {
|
||||
const op = "users.(Service).CreateUser"
|
||||
|
||||
if err := validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -157,13 +201,32 @@ func (s Service) CreateUser(ctx context.Context, req *pbs.CreateUserRequest) (*p
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.CreateUserResponse{Item: u, Uri: fmt.Sprintf("users/%s", u.GetId())}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, nil, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbs.CreateUserResponse{Item: item, Uri: fmt.Sprintf("users/%s", item.GetId())}, nil
|
||||
}
|
||||
|
||||
// UpdateUser implements the interface pbs.UserServiceServer.
|
||||
func (s Service) UpdateUser(ctx context.Context, req *pbs.UpdateUserRequest) (*pbs.UpdateUserResponse, error) {
|
||||
const op = "users.(Service).UpdateUser"
|
||||
|
||||
if err := validateUpdateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -171,13 +234,31 @@ func (s Service) UpdateUser(ctx context.Context, req *pbs.UpdateUserRequest) (*p
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
u, accts, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.UpdateUserResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, accts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.UpdateUserResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// DeleteUser implements the interface pbs.UserServiceServer.
|
||||
@@ -198,6 +279,8 @@ func (s Service) DeleteUser(ctx context.Context, req *pbs.DeleteUserRequest) (*p
|
||||
|
||||
// AddUserAccounts implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) AddUserAccounts(ctx context.Context, req *pbs.AddUserAccountsRequest) (*pbs.AddUserAccountsResponse, error) {
|
||||
const op = "users.(Service).AddUserAccounts"
|
||||
|
||||
if err := validateAddUserAccountsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -205,17 +288,37 @@ func (s Service) AddUserAccounts(ctx context.Context, req *pbs.AddUserAccountsRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.addInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion())
|
||||
u, accts, err := s.addInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.AddUserAccountsResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, accts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.AddUserAccountsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// SetUserAccounts implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) SetUserAccounts(ctx context.Context, req *pbs.SetUserAccountsRequest) (*pbs.SetUserAccountsResponse, error) {
|
||||
const op = "users.(Service).SetUserAccounts"
|
||||
|
||||
if err := validateSetUserAccountsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -223,17 +326,37 @@ func (s Service) SetUserAccounts(ctx context.Context, req *pbs.SetUserAccountsRe
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.setInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion())
|
||||
u, accts, err := s.setInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.SetUserAccountsResponse{Item: u}, nil
|
||||
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, accts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.SetUserAccountsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
// RemoveUserAccounts implements the interface pbs.GroupServiceServer.
|
||||
func (s Service) RemoveUserAccounts(ctx context.Context, req *pbs.RemoveUserAccountsRequest) (*pbs.RemoveUserAccountsResponse, error) {
|
||||
const op = "users.(Service).RemoveUserAccounts"
|
||||
|
||||
if err := validateRemoveUserAccountsRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -241,34 +364,52 @@ func (s Service) RemoveUserAccounts(ctx context.Context, req *pbs.RemoveUserAcco
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
u, err := s.removeInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion())
|
||||
u, accts, err := s.removeInRepo(ctx, req.GetId(), req.GetAccountIds(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Scope = authResults.Scope
|
||||
u.AuthorizedActions = authResults.FetchActionSetForId(ctx, u.Id, IdActions).Strings()
|
||||
return &pbs.RemoveUserAccountsResponse{Item: u}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*pb.User, error) {
|
||||
repo, err := s.repoFn()
|
||||
outputFields, ok := requests.OutputFields(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.Internal, op, "no request context found")
|
||||
}
|
||||
|
||||
outputOpts := make([]handlers.Option, 0, 3)
|
||||
outputOpts = append(outputOpts, handlers.WithOutputFields(&outputFields))
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
outputOpts = append(outputOpts, handlers.WithScope(authResults.Scope))
|
||||
}
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, u.GetPublicId(), IdActions).Strings()))
|
||||
}
|
||||
|
||||
item, err := toProto(ctx, u, accts, outputOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pbs.RemoveUserAccountsResponse{Item: item}, nil
|
||||
}
|
||||
|
||||
func (s Service) getFromRepo(ctx context.Context, id string) (*iam.User, []string, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
u, accts, err := repo.LookupUser(ctx, id)
|
||||
if err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, handlers.NotFoundErrorf("User %q doesn't exist.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("User %q doesn't exist.", id)
|
||||
}
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if u == nil {
|
||||
return nil, handlers.NotFoundErrorf("User %q doesn't exist.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("User %q doesn't exist.", id)
|
||||
}
|
||||
return toProto(u, accts), nil
|
||||
return u, accts, nil
|
||||
}
|
||||
|
||||
func (s Service) createInRepo(ctx context.Context, orgId string, item *pb.User) (*pb.User, error) {
|
||||
func (s Service) createInRepo(ctx context.Context, orgId string, item *pb.User) (*iam.User, error) {
|
||||
const op = "users.(Service).createInRepo"
|
||||
var opts []iam.Option
|
||||
if item.GetName() != nil {
|
||||
@@ -292,10 +433,10 @@ func (s Service) createInRepo(ctx context.Context, orgId string, item *pb.User)
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create user but no error returned from repository.")
|
||||
}
|
||||
return toProto(out, nil), nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) updateInRepo(ctx context.Context, orgId, id string, mask []string, item *pb.User) (*pb.User, error) {
|
||||
func (s Service) updateInRepo(ctx context.Context, orgId, id string, mask []string, item *pb.User) (*iam.User, []string, error) {
|
||||
const op = "users.(Service).updateInRepo"
|
||||
var opts []iam.Option
|
||||
if desc := item.GetDescription(); desc != nil {
|
||||
@@ -307,25 +448,25 @@ func (s Service) updateInRepo(ctx context.Context, orgId, id string, mask []stri
|
||||
version := item.GetVersion()
|
||||
u, err := iam.NewUser(orgId, opts...)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build user for update: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build user for update: %v.", err)
|
||||
}
|
||||
u.PublicId = id
|
||||
dbMask := maskManager.Translate(mask)
|
||||
if len(dbMask) == 0 {
|
||||
return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
return nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
|
||||
}
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
out, accts, rowsUpdated, err := repo.UpdateUser(ctx, u, version, dbMask)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to update user"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to update user"))
|
||||
}
|
||||
if rowsUpdated == 0 {
|
||||
return nil, handlers.NotFoundErrorf("User %q doesn't exist or incorrect version provided.", id)
|
||||
return nil, nil, handlers.NotFoundErrorf("User %q doesn't exist or incorrect version provided.", id)
|
||||
}
|
||||
return toProto(out, accts), nil
|
||||
return out, accts, nil
|
||||
}
|
||||
|
||||
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
@@ -344,7 +485,7 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.User, error) {
|
||||
func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*iam.User, error) {
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -353,71 +494,67 @@ func (s Service) listFromRepo(ctx context.Context, scopeIds []string) ([]*pb.Use
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var outUl []*pb.User
|
||||
for _, u := range ul {
|
||||
outUl = append(outUl, toProto(u, nil))
|
||||
}
|
||||
return outUl, nil
|
||||
return ul, nil
|
||||
}
|
||||
|
||||
func (s Service) addInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*pb.User, error) {
|
||||
func (s Service) addInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*iam.User, []string, error) {
|
||||
const op = "users.(Service).addInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = repo.AddUserAccounts(ctx, userId, version, strutil.RemoveDuplicates(accountIds, false))
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add accounts to user: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add accounts to user: %v.", err)
|
||||
}
|
||||
out, accts, err := repo.LookupUser(ctx, userId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up user after adding accounts"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up user after adding accounts"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after adding accounts to it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after adding accounts to it.")
|
||||
}
|
||||
return toProto(out, accts), nil
|
||||
return out, accts, nil
|
||||
}
|
||||
|
||||
func (s Service) setInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*pb.User, error) {
|
||||
func (s Service) setInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*iam.User, []string, error) {
|
||||
const op = "users.(Service).setInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = repo.SetUserAccounts(ctx, userId, version, strutil.RemoveDuplicates(accountIds, false))
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set accounts for the user: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set accounts for the user: %v.", err)
|
||||
}
|
||||
out, accts, err := repo.LookupUser(ctx, userId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up user after setting accounts"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up user after setting accounts"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after setting accounts for it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after setting accounts for it.")
|
||||
}
|
||||
return toProto(out, accts), nil
|
||||
return out, accts, nil
|
||||
}
|
||||
|
||||
func (s Service) removeInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*pb.User, error) {
|
||||
func (s Service) removeInRepo(ctx context.Context, userId string, accountIds []string, version uint32) (*iam.User, []string, error) {
|
||||
const op = "users.(Service).removeInRepo"
|
||||
repo, err := s.repoFn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = repo.DeleteUserAccounts(ctx, userId, version, strutil.RemoveDuplicates(accountIds, false))
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove accounts from user: %v.", err)
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove accounts from user: %v.", err)
|
||||
}
|
||||
out, accts, err := repo.LookupUser(ctx, userId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to look up user after removing accounts"))
|
||||
return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to look up user after removing accounts"))
|
||||
}
|
||||
if out == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after removing accounts from it.")
|
||||
return nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup user after removing accounts from it.")
|
||||
}
|
||||
return toProto(out, accts), nil
|
||||
return out, accts, nil
|
||||
}
|
||||
|
||||
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
|
||||
@@ -459,41 +596,66 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
|
||||
return auth.Verify(ctx, opts...)
|
||||
}
|
||||
|
||||
func toProto(in *iam.User, accts []string) *pb.User {
|
||||
out := pb.User{
|
||||
Id: in.GetPublicId(),
|
||||
ScopeId: in.GetScopeId(),
|
||||
CreatedTime: in.GetCreateTime().GetTimestamp(),
|
||||
UpdatedTime: in.GetUpdateTime().GetTimestamp(),
|
||||
Version: in.GetVersion(),
|
||||
AccountIds: accts,
|
||||
func toProto(ctx context.Context, in *iam.User, accts []string, opt ...handlers.Option) (*pb.User, error) {
|
||||
opts := handlers.GetOpts(opt...)
|
||||
if opts.WithOutputFields == nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building user proto")
|
||||
}
|
||||
if in.GetDescription() != "" {
|
||||
out.Description = &wrapperspb.StringValue{Value: in.GetDescription()}
|
||||
outputFields := *opts.WithOutputFields
|
||||
|
||||
out := pb.User{}
|
||||
if outputFields.Has(globals.IdField) {
|
||||
out.Id = in.GetPublicId()
|
||||
}
|
||||
if in.GetName() != "" {
|
||||
out.Name = &wrapperspb.StringValue{Value: in.GetName()}
|
||||
if outputFields.Has(globals.ScopeIdField) {
|
||||
out.ScopeId = in.GetScopeId()
|
||||
}
|
||||
if in.GetPrimaryAccountId() != "" {
|
||||
if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" {
|
||||
out.Description = wrapperspb.String(in.GetDescription())
|
||||
}
|
||||
if outputFields.Has(globals.NameField) && in.GetName() != "" {
|
||||
out.Name = wrapperspb.String(in.GetName())
|
||||
}
|
||||
if outputFields.Has(globals.CreatedTimeField) {
|
||||
out.CreatedTime = in.GetCreateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.UpdatedTimeField) {
|
||||
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
|
||||
}
|
||||
if outputFields.Has(globals.VersionField) {
|
||||
out.Version = in.GetVersion()
|
||||
}
|
||||
if outputFields.Has(globals.AccountIdsField) {
|
||||
out.AccountIds = accts
|
||||
}
|
||||
if outputFields.Has(globals.PrimaryAccountIdField) {
|
||||
out.PrimaryAccountId = in.GetPrimaryAccountId()
|
||||
}
|
||||
if in.GetLoginName() != "" {
|
||||
if outputFields.Has(globals.LoginNameField) {
|
||||
out.LoginName = in.GetLoginName()
|
||||
}
|
||||
if in.GetFullName() != "" {
|
||||
if outputFields.Has(globals.FullNameField) {
|
||||
out.FullName = in.GetFullName()
|
||||
}
|
||||
if in.GetEmail() != "" {
|
||||
if outputFields.Has(globals.EmailField) {
|
||||
out.Email = in.GetEmail()
|
||||
}
|
||||
for _, a := range accts {
|
||||
out.Accounts = append(out.Accounts, &pb.Account{
|
||||
Id: a,
|
||||
// TODO: Update this when an account can be associated with a user from a different scope.
|
||||
ScopeId: in.GetScopeId(),
|
||||
})
|
||||
if outputFields.Has(globals.ScopeField) {
|
||||
out.Scope = opts.WithScope
|
||||
}
|
||||
return &out
|
||||
if outputFields.Has(globals.AuthorizedActionsField) {
|
||||
out.AuthorizedActions = opts.WithAuthorizedActions
|
||||
}
|
||||
if outputFields.Has(globals.AccountsField) {
|
||||
for _, a := range accts {
|
||||
out.Accounts = append(out.Accounts, &pb.Account{
|
||||
Id: a,
|
||||
// TODO: Update this when an account can be associated with a user from a different scope.
|
||||
ScopeId: in.GetScopeId(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
|
||||
|
||||
@@ -227,7 +227,7 @@ func TestList(t *testing.T) {
|
||||
}
|
||||
|
||||
// Populate these users into the total
|
||||
ctx = auth.DisabledAuthTestContext(repoFn, oWithUsers.GetPublicId())
|
||||
ctx = auth.DisabledAuthTestContext(repoFn, oWithUsers.GetPublicId(), auth.WithUserId("u_auth"))
|
||||
usersInOrg, err := s.ListUsers(ctx, &pbs.ListUsersRequest{ScopeId: oWithUsers.GetPublicId()})
|
||||
require.NoError(t, err)
|
||||
totalUsers = append(totalUsers, usersInOrg.GetItems()...)
|
||||
@@ -278,10 +278,12 @@ func TestList(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require.NoError(err, "Couldn't create new user service.")
|
||||
|
||||
// Test with non-anon user
|
||||
got, gErr := s.ListUsers(auth.DisabledAuthTestContext(repoFn, tc.req.GetScopeId()), tc.req)
|
||||
if tc.err != nil {
|
||||
require.Error(gErr)
|
||||
assert.True(errors.Is(gErr, tc.err), "ListUsers(%+v) got error %v, wanted %v", tc.req, gErr, tc.err)
|
||||
return
|
||||
}
|
||||
gotUsers := sortableUsers{
|
||||
users: got.GetItems(),
|
||||
@@ -298,6 +300,18 @@ func TestList(t *testing.T) {
|
||||
tc.res.Items = resUsers.users
|
||||
}
|
||||
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListUsers(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
||||
// Test with anon user
|
||||
|
||||
got, gErr = s.ListUsers(auth.DisabledAuthTestContext(repoFn, 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.Accounts)
|
||||
require.Nil(item.AccountIds)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user