2021-05-04 19:57:35 -05:00
// Copyright (c) HashiCorp, Inc.
2020-07-02 08:17:52 -07:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-06-09 11:20:59 -07:00
package users
import (
"context"
"fmt"
2021-05-04 19:57:35 -05:00
"github.com/hashicorp/boundary/globals"
2024-04-17 08:26:46 -07:00
talias "github.com/hashicorp/boundary/internal/alias/target"
2022-05-09 12:45:10 -07:00
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
"github.com/hashicorp/boundary/internal/daemon/controller/common/scopeids"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
2024-04-17 08:26:46 -07:00
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2020-08-10 17:15:02 -04:00
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/iam/store"
2023-12-20 16:51:57 -08:00
"github.com/hashicorp/boundary/internal/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
2021-01-20 10:53:01 -05:00
"github.com/hashicorp/boundary/internal/perms"
2021-05-04 19:57:35 -05:00
"github.com/hashicorp/boundary/internal/requests"
2020-09-04 14:48:11 -07:00
"github.com/hashicorp/boundary/internal/types/action"
"github.com/hashicorp/boundary/internal/types/resource"
2020-09-02 19:43:41 -07:00
"github.com/hashicorp/boundary/internal/types/scope"
2024-04-17 08:26:46 -07:00
aliaspb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/aliases"
2023-12-20 16:51:57 -08:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
2021-08-26 10:44:41 -04:00
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/users"
2021-07-14 17:37:40 -07:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2020-06-09 11:20:59 -07:00
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/wrapperspb"
)
2021-01-20 10:53:01 -05:00
var (
maskManager handlers . MaskManager
// IdActions contains the set of actions that can be performed on
// individual resources
2023-10-31 13:44:17 +00:00
IdActions = action . NewActionSet (
2021-04-23 09:13:37 -05:00
action . NoOp ,
2021-01-20 10:53:01 -05:00
action . Read ,
action . Update ,
action . Delete ,
action . AddAccounts ,
action . SetAccounts ,
action . RemoveAccounts ,
2024-04-17 08:26:46 -07:00
action . ListResolvableAliases ,
2023-10-31 13:44:17 +00:00
)
2021-01-20 10:53:01 -05:00
// CollectionActions contains the set of actions that can be performed on
// this collection
2023-10-31 13:44:17 +00:00
CollectionActions = action . NewActionSet (
2021-01-20 10:53:01 -05:00
action . Create ,
action . List ,
2023-10-31 13:44:17 +00:00
)
2021-01-20 10:53:01 -05:00
)
2020-06-09 11:20:59 -07:00
2020-07-20 11:17:09 -07:00
func init () {
var err error
2023-06-06 17:09:08 -04:00
if maskManager , err = handlers . NewMaskManager (
context . Background (),
handlers . MaskDestination { & store . User {}},
handlers . MaskSource { & pb . User {}},
); err != nil {
2020-07-20 11:17:09 -07:00
panic ( err )
}
2023-10-11 18:26:16 +00:00
// TODO: refactor to remove IdActions and CollectionActions package variables
action . RegisterResource ( resource . User , IdActions , CollectionActions )
2020-07-20 11:17:09 -07:00
}
2020-06-09 11:20:59 -07:00
// Service handles request as described by the pbs.UserServiceServer interface.
type Service struct {
2022-08-30 14:22:10 -04:00
pbs . UnsafeUserServiceServer
2020-11-11 14:41:05 -05:00
2023-12-20 16:51:57 -08:00
repoFn common . IamRepoFactory
2024-04-17 08:26:46 -07:00
aliasRepoFn common . TargetAliasRepoFactory
2023-12-20 16:51:57 -08:00
maxPageSize uint
2020-06-09 11:20:59 -07:00
}
2022-08-30 14:22:10 -04:00
var _ pbs . UserServiceServer = ( * Service )( nil )
2020-08-10 17:15:02 -04:00
// NewService returns a user service which handles user related requests to boundary.
2024-04-17 08:26:46 -07:00
func NewService ( ctx context . Context , repo common . IamRepoFactory , aliasRepoFn common . TargetAliasRepoFactory , maxPageSize uint ) ( Service , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.NewService"
2024-04-17 08:26:46 -07:00
switch {
case repo == nil :
2023-06-06 17:09:08 -04:00
return Service {}, errors . New ( ctx , errors . InvalidParameter , op , "missing iam repository" )
2024-04-17 08:26:46 -07:00
case aliasRepoFn == nil :
return Service {}, errors . New ( ctx , errors . InvalidParameter , op , "missing alias repository" )
2020-06-09 11:20:59 -07:00
}
2023-12-20 16:51:57 -08:00
if maxPageSize == 0 {
maxPageSize = uint ( globals . DefaultMaxPageSize )
}
2024-04-17 08:26:46 -07:00
return Service { repoFn : repo , aliasRepoFn : aliasRepoFn , maxPageSize : maxPageSize }, nil
2020-06-09 11:20:59 -07:00
}
2020-07-02 08:17:52 -07:00
// ListUsers implements the interface pbs.UserServiceServer.
func ( s Service ) ListUsers ( ctx context . Context , req * pbs . ListUsersRequest ) ( * pbs . ListUsersResponse , error ) {
2023-12-20 16:51:57 -08:00
const op = "users.(Service).ListUsers"
2023-06-06 17:09:08 -04:00
if err := validateListRequest ( ctx , req ); err != nil {
2020-07-02 08:17:52 -07:00
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetScopeId (), action . List )
if authResults . Error != nil {
2021-03-24 15:33:04 -04:00
// If it's forbidden, and it's a recursive request, and they're
// successfully authenticated but just not authorized, keep going as we
2021-09-02 12:55:04 -04:00
// may have authorization on downstream scopes. Or, if they've not
// authenticated, still process in case u_anon has permissions.
if ( authResults . Error == handlers . ForbiddenError () || authResults . Error == handlers . UnauthenticatedError ()) &&
2021-03-24 15:33:04 -04:00
req . GetRecursive () &&
2021-04-13 16:55:40 -05:00
authResults . AuthenticationFinished {
2021-03-24 15:33:04 -04:00
} else {
return nil , authResults . Error
}
2020-09-04 14:48:11 -07:00
}
2021-01-25 19:37:40 -05:00
2021-03-24 15:33:04 -04:00
scopeIds , scopeInfoMap , err := scopeids . GetListingScopeIds (
2022-04-29 09:52:01 -04:00
ctx , s . repoFn , authResults , req . GetScopeId (), resource . User , req . GetRecursive ())
2021-01-25 19:37:40 -05:00
if err != nil {
return nil , err
}
2023-12-20 16:51:57 -08:00
pageSize := int ( s . maxPageSize )
// Use the requested page size only if it is smaller than
// the configured max.
if req . GetPageSize () != 0 && uint ( req . GetPageSize ()) < s . maxPageSize {
pageSize = int ( req . GetPageSize ())
2020-07-02 08:17:52 -07:00
}
2023-12-20 16:51:57 -08:00
var filterItemFn func ( ctx context . Context , item * iam . User ) ( bool , error )
switch {
case req . GetFilter () != "" :
// Only use a filter if we need to
filter , err := handlers . NewFilter ( ctx , req . GetFilter ())
if err != nil {
return nil , err
}
filterItemFn = func ( ctx context . Context , item * iam . User ) ( bool , error ) {
outputOpts , ok := newOutputOpts ( ctx , item , scopeInfoMap , authResults )
if ! ok {
return false , nil
}
pbItem , err := toProto ( ctx , item , nil , outputOpts ... )
if err != nil {
return false , err
}
return filter . Match ( pbItem ), nil
}
default :
filterItemFn = func ( ctx context . Context , item * iam . User ) ( bool , error ) {
return true , nil
}
2021-05-04 19:57:35 -05:00
}
2023-12-20 16:51:57 -08:00
grantsHash , err := authResults . GrantsHash ( ctx )
2021-02-22 10:09:25 -07:00
if err != nil {
2023-12-20 16:51:57 -08:00
return nil , errors . Wrap ( ctx , err , op )
2021-02-22 10:09:25 -07:00
}
2023-12-20 16:51:57 -08:00
repo , err := s . repoFn ()
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
2021-01-20 10:53:01 -05:00
}
2023-12-20 16:51:57 -08:00
var listResp * pagination . ListResponse [ * iam . User ]
var sortBy string
if req . GetListToken () == "" {
sortBy = "created_time"
listResp , err = iam . ListUsers ( ctx , grantsHash , pageSize , filterItemFn , repo , scopeIds )
if err != nil {
return nil , err
2021-02-22 10:09:25 -07:00
}
2023-12-20 16:51:57 -08:00
} else {
listToken , err := handlers . ParseListToken ( ctx , req . GetListToken (), resource . User , grantsHash )
if err != nil {
return nil , err
2021-05-04 19:57:35 -05:00
}
2023-12-20 16:51:57 -08:00
switch st := listToken . Subtype .( type ) {
case * listtoken . PaginationToken :
sortBy = "created_time"
listResp , err = iam . ListUsersPage ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , scopeIds )
if err != nil {
return nil , err
}
case * listtoken . StartRefreshToken :
sortBy = "updated_time"
listResp , err = iam . ListUsersRefresh ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , scopeIds )
if err != nil {
return nil , err
}
case * listtoken . RefreshToken :
sortBy = "updated_time"
listResp , err = iam . ListUsersRefreshPage ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , scopeIds )
if err != nil {
return nil , err
}
default :
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . InvalidArgument , "unexpected list token subtype: %T" , st )
2021-05-04 19:57:35 -05:00
}
2023-12-20 16:51:57 -08:00
}
2021-05-04 19:57:35 -05:00
2023-12-20 16:51:57 -08:00
finalItems := make ([] * pb . User , 0 , len ( listResp . Items ))
for _ , item := range listResp . Items {
outputOpts , ok := newOutputOpts ( ctx , item , scopeInfoMap , authResults )
if ! ok {
continue
}
2021-05-04 19:57:35 -05:00
item , err := toProto ( ctx , item , nil , outputOpts ... )
if err != nil {
2023-12-20 16:51:57 -08:00
return nil , errors . Wrap ( ctx , err , op )
2021-05-04 19:57:35 -05:00
}
2023-12-20 16:51:57 -08:00
finalItems = append ( finalItems , item )
}
respType := "delta"
if listResp . CompleteListing {
respType = "complete"
}
resp := & pbs . ListUsersResponse {
Items : finalItems ,
EstItemCount : uint32 ( listResp . EstimatedItemCount ),
RemovedIds : listResp . DeletedIds ,
ResponseType : respType ,
SortBy : sortBy ,
SortDir : "desc" ,
}
if listResp . ListToken != nil {
resp . ListToken , err = handlers . MarshalListToken ( ctx , listResp . ListToken , pbs . ResourceType_RESOURCE_TYPE_USER )
if err != nil {
return nil , err
2021-01-20 10:53:01 -05:00
}
2020-07-22 00:14:28 -04:00
}
2023-12-20 16:51:57 -08:00
return resp , nil
2020-06-09 11:20:59 -07:00
}
// GetUsers implements the interface pbs.UserServiceServer.
func ( s Service ) GetUser ( ctx context . Context , req * pbs . GetUserRequest ) ( * pbs . GetUserResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "users.(Service).GetUser"
2020-06-09 11:20:59 -07:00
if err := validateGetRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . Read )
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
u , accts , err := s . getFromRepo ( ctx , req . GetId ())
2020-06-09 11:20:59 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
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
2020-06-09 11:20:59 -07:00
}
// CreateUser implements the interface pbs.UserServiceServer.
func ( s Service ) CreateUser ( ctx context . Context , req * pbs . CreateUserRequest ) ( * pbs . CreateUserResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "users.(Service).CreateUser"
2020-06-09 11:20:59 -07:00
if err := validateCreateRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetItem (). GetScopeId (), action . Create )
if authResults . Error != nil {
return nil , authResults . Error
}
2020-07-22 00:14:28 -04:00
u , err := s . createInRepo ( ctx , authResults . Scope . GetId (), req . GetItem ())
2020-06-09 11:20:59 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
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
2020-06-09 11:20:59 -07:00
}
// UpdateUser implements the interface pbs.UserServiceServer.
func ( s Service ) UpdateUser ( ctx context . Context , req * pbs . UpdateUserRequest ) ( * pbs . UpdateUserResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "users.(Service).UpdateUser"
2020-06-09 11:20:59 -07:00
if err := validateUpdateRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . Update )
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
u , accts , err := s . updateInRepo ( ctx , authResults . Scope . GetId (), req . GetId (), req . GetUpdateMask (). GetPaths (), req . GetItem ())
2020-06-09 11:20:59 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
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
2020-06-09 11:20:59 -07:00
}
// DeleteUser implements the interface pbs.UserServiceServer.
func ( s Service ) DeleteUser ( ctx context . Context , req * pbs . DeleteUserRequest ) ( * pbs . DeleteUserResponse , error ) {
if err := validateDeleteRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . Delete )
if authResults . Error != nil {
return nil , authResults . Error
}
2020-09-08 23:57:26 -04:00
_ , err := s . deleteFromRepo ( ctx , req . GetId ())
2020-06-09 11:20:59 -07:00
if err != nil {
return nil , err
}
2020-09-08 23:57:26 -04:00
return nil , nil
2020-06-09 11:20:59 -07:00
}
2020-09-25 20:44:55 -07:00
// AddUserAccounts implements the interface pbs.GroupServiceServer.
func ( s Service ) AddUserAccounts ( ctx context . Context , req * pbs . AddUserAccountsRequest ) ( * pbs . AddUserAccountsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "users.(Service).AddUserAccounts"
2020-09-25 20:44:55 -07:00
if err := validateAddUserAccountsRequest ( req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . AddAccounts )
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
u , accts , err := s . addInRepo ( ctx , req . GetId (), req . GetAccountIds (), req . GetVersion ())
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
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 ... )
2020-09-25 20:44:55 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . AddUserAccountsResponse { Item : item }, nil
2020-09-25 20:44:55 -07:00
}
// SetUserAccounts implements the interface pbs.GroupServiceServer.
func ( s Service ) SetUserAccounts ( ctx context . Context , req * pbs . SetUserAccountsRequest ) ( * pbs . SetUserAccountsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "users.(Service).SetUserAccounts"
2020-09-25 20:44:55 -07:00
if err := validateSetUserAccountsRequest ( req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . SetAccounts )
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
u , accts , err := s . setInRepo ( ctx , req . GetId (), req . GetAccountIds (), req . GetVersion ())
2020-09-25 20:44:55 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
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
2020-09-25 20:44:55 -07:00
}
// RemoveUserAccounts implements the interface pbs.GroupServiceServer.
func ( s Service ) RemoveUserAccounts ( ctx context . Context , req * pbs . RemoveUserAccountsRequest ) ( * pbs . RemoveUserAccountsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "users.(Service).RemoveUserAccounts"
2020-09-25 20:44:55 -07:00
if err := validateRemoveUserAccountsRequest ( req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . RemoveAccounts )
if authResults . Error != nil {
return nil , authResults . Error
}
2021-05-04 19:57:35 -05:00
u , accts , err := s . removeInRepo ( ctx , req . GetId (), req . GetAccountIds (), req . GetVersion ())
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
2021-05-04 19:57:35 -05:00
}
outputOpts := make ([] handlers . Option , 0 , 3 )
2023-01-06 17:27:19 -06:00
outputOpts = append ( outputOpts , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
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 ... )
2020-09-25 20:44:55 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . RemoveUserAccountsResponse { Item : item }, nil
2020-09-25 20:44:55 -07:00
}
2024-04-17 08:26:46 -07:00
// ListResolvableAliases implements the interface pbs.AliasServiceServer.
func ( s Service ) ListResolvableAliases ( ctx context . Context , req * pbs . ListResolvableAliasesRequest ) ( * pbs . ListResolvableAliasesResponse , error ) {
const op = "users.(Service).ListResolvableAliases"
if err := validateListResolvableAliasesRequest ( req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . ListResolvableAliases )
if authResults . Error != nil {
return nil , authResults . Error
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
return nil , errors . New ( ctx , errors . Internal , op , "no request context found" )
}
acl := authResults . ACL ()
grantsHash , err := authResults . GrantsHash ( ctx )
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
}
if req . GetId () != authResults . UserId {
var err error
acl , grantsHash , err = s . aclAndGrantHashForUser ( ctx , req . GetId ())
if err != nil {
return nil , errors . Wrap ( ctx , err , op , errors . WithoutEvent ())
}
}
2024-09-17 10:07:43 -04:00
permissions := acl . ListResolvableAliasesPermissions ( resource . Target , targets . IdActions )
2024-04-17 08:26:46 -07:00
if len ( permissions ) == 0 {
// if there are no permitted targets then there will be no aliases that
// can resolve to them.
return & pbs . ListResolvableAliasesResponse {
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
}, nil
}
pageSize := int ( s . maxPageSize )
// Use the requested page size only if it is smaller than
// the configured max.
if req . GetPageSize () != 0 && uint ( req . GetPageSize ()) < s . maxPageSize {
pageSize = int ( req . GetPageSize ())
}
repo , err := s . aliasRepoFn ()
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
}
var listResp * pagination . ListResponse [ * talias . Alias ]
var sortBy string
if req . GetListToken () == "" {
sortBy = "created_time"
listResp , err = talias . ListResolvableAliases ( ctx , grantsHash , pageSize , repo , permissions )
if err != nil {
return nil , err
}
} else {
listToken , err := handlers . ParseListToken ( ctx , req . GetListToken (), resource . Alias , grantsHash )
if err != nil {
return nil , err
}
switch st := listToken . Subtype .( type ) {
case * listtoken . PaginationToken :
sortBy = "created_time"
listResp , err = talias . ListResolvableAliasesPage ( ctx , grantsHash , pageSize , listToken , repo , permissions )
if err != nil {
return nil , err
}
case * listtoken . StartRefreshToken :
sortBy = "updated_time"
listResp , err = talias . ListResolvableAliasesRefresh ( ctx , grantsHash , pageSize , listToken , repo , permissions )
if err != nil {
return nil , err
}
case * listtoken . RefreshToken :
sortBy = "updated_time"
listResp , err = talias . ListResolvableAliasesRefreshPage ( ctx , grantsHash , pageSize , listToken , repo , permissions )
if err != nil {
return nil , err
}
default :
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . InvalidArgument , "unexpected list token subtype: %T" , st )
}
}
finalItems := make ([] * aliaspb . Alias , 0 , len ( listResp . Items ))
for _ , item := range listResp . Items {
item , err := toResolvableAliasProto ( item , handlers . WithOutputFields ( outputFields ))
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
}
finalItems = append ( finalItems , item )
}
respType := "delta"
if listResp . CompleteListing {
respType = "complete"
}
resp := & pbs . ListResolvableAliasesResponse {
Items : finalItems ,
EstItemCount : uint32 ( listResp . EstimatedItemCount ),
RemovedIds : listResp . DeletedIds ,
ResponseType : respType ,
SortBy : sortBy ,
SortDir : "desc" ,
}
if listResp . ListToken != nil {
resp . ListToken , err = handlers . MarshalListToken ( ctx , listResp . ListToken , pbs . ResourceType_RESOURCE_TYPE_ALIAS )
if err != nil {
return nil , err
}
}
return resp , nil
}
// aclAndGrantHashForUser returns an ACL from the grants provided to the user and
// the hash of those grants.
func ( s Service ) aclAndGrantHashForUser ( ctx context . Context , userId string ) ( perms . ACL , [] byte , error ) {
const op = "users.(Service).aclAndGrantHashForUser"
iamRepo , err := s . repoFn ()
if err != nil {
return perms . ACL {}, nil , errors . Wrap ( ctx , err , op , errors . WithoutEvent ())
}
grantTuples , err := iamRepo . GrantsForUser ( ctx , userId )
if err != nil {
return perms . ACL {}, nil , errors . Wrap ( ctx , err , op , errors . WithoutEvent ())
}
hash , err := grantTuples . GrantHash ( ctx )
if err != nil {
return perms . ACL {}, nil , errors . Wrap ( ctx , err , op , errors . WithoutEvent ())
}
parsedGrants := make ([] perms . Grant , 0 , len ( grantTuples ))
// Note: Below, we always skip validation so that we don't error on formats
// that we've since restricted, e.g. "ids=foo;actions=create,read". These
// will simply not have an effect.
2024-09-17 10:07:43 -04:00
for _ , tuple := range grantTuples {
2024-04-17 08:26:46 -07:00
permsOpts := [] perms . Option {
perms . WithUserId ( userId ),
perms . WithSkipFinalValidation ( true ),
}
parsed , err := perms . Parse (
ctx ,
2024-09-17 10:07:43 -04:00
tuple ,
2024-04-17 08:26:46 -07:00
permsOpts ... )
if err != nil {
return perms . ACL {}, nil , errors . Wrap ( ctx , err , op )
}
parsedGrants = append ( parsedGrants , parsed )
}
return perms . NewACL ( parsedGrants ... ), hash , nil
}
2021-05-04 19:57:35 -05:00
func ( s Service ) getFromRepo ( ctx context . Context , id string ) ( * iam . User , [] string , error ) {
2020-06-09 11:20:59 -07:00
repo , err := s . repoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-06-09 11:20:59 -07:00
}
2020-09-25 20:44:55 -07:00
u , accts , err := repo . LookupUser ( ctx , id )
2020-06-09 11:20:59 -07:00
if err != nil {
2020-12-21 10:07:34 -08:00
if errors . IsNotFoundError ( err ) {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . NotFoundErrorf ( "User %q doesn't exist." , id )
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-06-09 11:20:59 -07:00
}
if u == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . NotFoundErrorf ( "User %q doesn't exist." , id )
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
return u , accts , nil
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
func ( s Service ) createInRepo ( ctx context . Context , orgId string , item * pb . User ) ( * iam . User , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.(Service).createInRepo"
2020-06-09 11:20:59 -07:00
var opts [] iam . Option
if item . GetName () != nil {
opts = append ( opts , iam . WithName ( item . GetName (). GetValue ()))
}
if item . GetDescription () != nil {
opts = append ( opts , iam . WithDescription ( item . GetDescription (). GetValue ()))
}
2023-06-06 17:09:08 -04:00
u , err := iam . NewUser ( ctx , orgId , opts ... )
2020-06-09 11:20:59 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to build user for creation: %v." , err )
2020-06-09 11:20:59 -07:00
}
repo , err := s . repoFn ()
if err != nil {
return nil , err
}
out , err := repo . CreateUser ( ctx , u )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to create user" ))
2020-06-09 11:20:59 -07:00
}
if out == nil {
2020-09-25 13:41:20 -07:00
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to create user but no error returned from repository." )
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
return out , nil
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
func ( s Service ) updateInRepo ( ctx context . Context , orgId , id string , mask [] string , item * pb . User ) ( * iam . User , [] string , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.(Service).updateInRepo"
2020-06-09 11:20:59 -07:00
var opts [] iam . Option
if desc := item . GetDescription (); desc != nil {
opts = append ( opts , iam . WithDescription ( desc . GetValue ()))
}
if name := item . GetName (); name != nil {
opts = append ( opts , iam . WithName ( name . GetValue ()))
}
2020-08-18 17:38:38 -07:00
version := item . GetVersion ()
2023-06-06 17:09:08 -04:00
u , err := iam . NewUser ( ctx , orgId , opts ... )
2020-06-09 11:20:59 -07:00
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to build user for update: %v." , err )
2020-06-09 11:20:59 -07:00
}
u . PublicId = id
2020-07-20 11:17:09 -07:00
dbMask := maskManager . Translate ( mask )
2020-06-09 11:20:59 -07:00
if len ( dbMask ) == 0 {
2021-05-04 19:57:35 -05:00
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." })
2020-06-09 11:20:59 -07:00
}
repo , err := s . repoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-06-09 11:20:59 -07:00
}
2020-09-25 20:44:55 -07:00
out , accts , rowsUpdated , err := repo . UpdateUser ( ctx , u , version , dbMask )
2020-06-09 11:20:59 -07:00
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to update user" ))
2020-06-09 11:20:59 -07:00
}
if rowsUpdated == 0 {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . NotFoundErrorf ( "User %q doesn't exist or incorrect version provided." , id )
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
return out , accts , nil
2020-06-09 11:20:59 -07:00
}
func ( s Service ) deleteFromRepo ( ctx context . Context , id string ) ( bool , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.(Service).deleteFromRepo"
2020-06-09 11:20:59 -07:00
repo , err := s . repoFn ()
if err != nil {
return false , err
}
rows , err := repo . DeleteUser ( ctx , id )
if err != nil {
2020-12-21 10:07:34 -08:00
if errors . IsNotFoundError ( err ) {
2020-06-09 11:20:59 -07:00
return false , nil
}
2021-07-29 18:52:56 -06:00
return false , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to delete user" ))
2020-06-09 11:20:59 -07:00
}
return rows > 0 , nil
}
2021-05-04 19:57:35 -05:00
func ( s Service ) addInRepo ( ctx context . Context , userId string , accountIds [] string , version uint32 ) ( * iam . User , [] string , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.(Service).addInRepo"
2020-09-25 20:44:55 -07:00
repo , err := s . repoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-09-25 20:44:55 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . AddUserAccounts ( ctx , userId , version , strutil . RemoveDuplicates ( accountIds , false ))
2020-09-25 20:44:55 -07:00
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to add accounts to user: %v." , err )
2020-09-25 20:44:55 -07:00
}
out , accts , err := repo . LookupUser ( ctx , userId )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up user after adding accounts" ))
2020-09-25 20:44:55 -07:00
}
if out == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup user after adding accounts to it." )
2020-09-25 20:44:55 -07:00
}
2021-05-04 19:57:35 -05:00
return out , accts , nil
2020-09-25 20:44:55 -07:00
}
2021-05-04 19:57:35 -05:00
func ( s Service ) setInRepo ( ctx context . Context , userId string , accountIds [] string , version uint32 ) ( * iam . User , [] string , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.(Service).setInRepo"
2020-09-25 20:44:55 -07:00
repo , err := s . repoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-09-25 20:44:55 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . SetUserAccounts ( ctx , userId , version , strutil . RemoveDuplicates ( accountIds , false ))
2020-09-25 20:44:55 -07:00
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to set accounts for the user: %v." , err )
2020-09-25 20:44:55 -07:00
}
out , accts , err := repo . LookupUser ( ctx , userId )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up user after setting accounts" ))
2020-09-25 20:44:55 -07:00
}
if out == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup user after setting accounts for it." )
2020-09-25 20:44:55 -07:00
}
2021-05-04 19:57:35 -05:00
return out , accts , nil
2020-09-25 20:44:55 -07:00
}
2021-05-04 19:57:35 -05:00
func ( s Service ) removeInRepo ( ctx context . Context , userId string , accountIds [] string , version uint32 ) ( * iam . User , [] string , error ) {
2021-04-16 12:06:03 -05:00
const op = "users.(Service).removeInRepo"
2020-09-25 20:44:55 -07:00
repo , err := s . repoFn ()
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , err
2020-09-25 20:44:55 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . DeleteUserAccounts ( ctx , userId , version , strutil . RemoveDuplicates ( accountIds , false ))
2020-09-25 20:44:55 -07:00
if err != nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to remove accounts from user: %v." , err )
2020-09-25 20:44:55 -07:00
}
out , accts , err := repo . LookupUser ( ctx , userId )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up user after removing accounts" ))
2020-09-25 20:44:55 -07:00
}
if out == nil {
2021-05-04 19:57:35 -05:00
return nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup user after removing accounts from it." )
2020-09-25 20:44:55 -07:00
}
2021-05-04 19:57:35 -05:00
return out , accts , nil
2020-09-25 20:44:55 -07:00
}
2020-09-04 14:48:11 -07:00
func ( s Service ) authResult ( ctx context . Context , id string , a action . Type ) auth . VerifyResults {
res := auth . VerifyResults {}
repo , err := s . repoFn ()
if err != nil {
res . Error = err
return res
}
var parentId string
opts := [] auth . Option { auth . WithType ( resource . User ), auth . WithAction ( a )}
switch a {
case action . List , action . Create :
parentId = id
scp , err := repo . LookupScope ( ctx , parentId )
if err != nil {
res . Error = err
return res
}
if scp == nil {
2020-09-08 23:57:26 -04:00
res . Error = handlers . NotFoundError ()
2020-09-04 14:48:11 -07:00
return res
}
default :
2020-09-25 13:35:32 -04:00
u , _ , err := repo . LookupUser ( ctx , id )
2020-09-04 14:48:11 -07:00
if err != nil {
res . Error = err
return res
}
if u == nil {
2020-09-08 23:57:26 -04:00
res . Error = handlers . NotFoundError ()
2020-09-04 14:48:11 -07:00
return res
}
parentId = u . GetScopeId ()
opts = append ( opts , auth . WithId ( id ))
}
opts = append ( opts , auth . WithScopeId ( parentId ))
return auth . Verify ( ctx , opts ... )
}
2021-05-04 19:57:35 -05:00
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" )
}
outputFields := * opts . WithOutputFields
out := pb . User {}
if outputFields . Has ( globals . IdField ) {
out . Id = in . GetPublicId ()
}
if outputFields . Has ( globals . ScopeIdField ) {
out . ScopeId = in . GetScopeId ()
}
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 ())
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . CreatedTimeField ) {
out . CreatedTime = in . GetCreateTime (). GetTimestamp ()
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . UpdatedTimeField ) {
out . UpdatedTime = in . GetUpdateTime (). GetTimestamp ()
2020-06-09 11:20:59 -07:00
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . VersionField ) {
out . Version = in . GetVersion ()
}
if outputFields . Has ( globals . AccountIdsField ) {
out . AccountIds = accts
}
if outputFields . Has ( globals . PrimaryAccountIdField ) {
2021-04-20 20:33:56 -04:00
out . PrimaryAccountId = in . GetPrimaryAccountId ()
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . LoginNameField ) {
2021-04-20 20:33:56 -04:00
out . LoginName = in . GetLoginName ()
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . FullNameField ) {
2021-04-20 20:33:56 -04:00
out . FullName = in . GetFullName ()
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . EmailField ) {
2021-04-20 20:33:56 -04:00
out . Email = in . GetEmail ()
}
2021-05-04 19:57:35 -05:00
if outputFields . Has ( globals . ScopeField ) {
out . Scope = opts . WithScope
}
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 (),
})
}
2020-09-25 20:44:55 -07:00
}
2021-05-04 19:57:35 -05:00
return & out , nil
2020-06-09 11:20:59 -07:00
}
2024-04-17 08:26:46 -07:00
func toResolvableAliasProto ( a * talias . Alias , opt ... handlers . Option ) ( * aliaspb . Alias , error ) {
opts := handlers . GetOpts ( opt ... )
if opts . WithOutputFields == nil {
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "output fields not found when building user proto" )
}
outputFields := * opts . WithOutputFields
pbItem := & aliaspb . Alias {}
if a == nil {
return pbItem , nil
}
if outputFields . Has ( globals . IdField ) {
pbItem . Id = a . GetPublicId ()
}
if outputFields . Has ( globals . CreatedTimeField ) {
pbItem . CreatedTime = a . GetCreateTime (). GetTimestamp ()
}
if outputFields . Has ( globals . UpdatedTimeField ) {
pbItem . UpdatedTime = a . GetUpdateTime (). GetTimestamp ()
}
if outputFields . Has ( globals . ValueField ) {
pbItem . Value = a . GetValue ()
}
if outputFields . Has ( globals . DestinationIdField ) && a . GetDestinationId () != "" {
pbItem . DestinationId = wrapperspb . String ( a . GetDestinationId ())
}
if outputFields . Has ( globals . TypeField ) {
pbItem . Type = "target"
}
return pbItem , nil
}
2020-06-09 11:20:59 -07:00
// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce
// requirements on the structure of the request. They verify that:
2022-08-25 09:12:24 -07:00
// - The path passed in is correctly formatted
// - All required parameters are set
// - There are no conflicting parameters provided
2020-06-09 11:20:59 -07:00
func validateGetRequest ( req * pbs . GetUserRequest ) error {
2023-03-10 15:06:39 -05:00
return handlers . ValidateGetRequest ( handlers . NoopValidatorFn , req , globals . UserPrefix )
2020-06-09 11:20:59 -07:00
}
func validateCreateRequest ( req * pbs . CreateUserRequest ) error {
2020-09-02 19:43:41 -07:00
return handlers . ValidateCreateRequest ( req . GetItem (), func () map [ string ] string {
badFields := map [ string ] string {}
2021-04-08 20:54:32 -04:00
if ! handlers . ValidId ( handlers . Id ( req . GetItem (). GetScopeId ()), scope . Org . Prefix ()) &&
2020-09-03 08:02:30 -07:00
scope . Global . String () != req . GetItem (). GetScopeId () {
2020-10-08 12:36:01 -07:00
badFields [ "scope_id" ] = "Must be 'global' or a valid org scope id."
2020-09-02 19:43:41 -07:00
}
return badFields
})
2020-06-09 11:20:59 -07:00
}
func validateUpdateRequest ( req * pbs . UpdateUserRequest ) error {
2023-03-10 15:06:39 -05:00
return handlers . ValidateUpdateRequest ( req , req . GetItem (), handlers . NoopValidatorFn , globals . UserPrefix )
2020-06-09 11:20:59 -07:00
}
func validateDeleteRequest ( req * pbs . DeleteUserRequest ) error {
2023-03-10 15:06:39 -05:00
return handlers . ValidateDeleteRequest ( handlers . NoopValidatorFn , req , globals . UserPrefix )
2020-06-09 11:20:59 -07:00
}
2023-06-06 17:09:08 -04:00
func validateListRequest ( ctx context . Context , req * pbs . ListUsersRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2021-04-08 20:54:32 -04:00
if ! handlers . ValidId ( handlers . Id ( req . GetScopeId ()), scope . Org . Prefix ()) &&
2020-09-08 23:57:26 -04:00
req . GetScopeId () != scope . Global . String () {
2020-10-08 12:36:01 -07:00
badFields [ "scope_id" ] = "Must be 'global' or a valid org scope id when listing."
2020-09-02 19:43:41 -07:00
}
2023-06-06 17:09:08 -04:00
if _ , err := handlers . NewFilter ( ctx , req . GetFilter ()); err != nil {
2021-02-22 10:09:25 -07:00
badFields [ "filter" ] = fmt . Sprintf ( "This field could not be parsed. %v" , err )
}
2020-07-02 08:17:52 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Improperly formatted identifier." , badFields )
}
return nil
}
2020-09-25 20:44:55 -07:00
func validateAddUserAccountsRequest ( req * pbs . AddUserAccountsRequest ) error {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . UserPrefix ) {
2020-09-25 20:44:55 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
if req . GetVersion () == 0 {
badFields [ "version" ] = "Required field."
}
if len ( req . GetAccountIds ()) == 0 {
badFields [ "account_ids" ] = "Must be non-empty."
}
2020-10-02 16:45:32 -07:00
for _ , a := range req . GetAccountIds () {
// TODO: Increase the type of auth accounts that can be added to a user.
2021-06-08 11:31:58 -05:00
if ! handlers . ValidId ( handlers . Id ( a ),
2023-03-10 15:06:39 -05:00
globals . PasswordAccountPreviousPrefix ,
globals . PasswordAccountPrefix ,
globals . OidcAccountPrefix ,
2023-05-03 09:45:29 -04:00
globals . LdapAccountPrefix ,
2021-06-08 11:31:58 -05:00
) {
2020-10-02 16:45:32 -07:00
badFields [ "account_ids" ] = "Values must be valid account ids."
break
}
}
2020-09-25 20:44:55 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
func validateSetUserAccountsRequest ( req * pbs . SetUserAccountsRequest ) error {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . UserPrefix ) {
2020-09-25 20:44:55 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
if req . GetVersion () == 0 {
badFields [ "version" ] = "Required field."
}
2020-10-02 16:45:32 -07:00
for _ , a := range req . GetAccountIds () {
// TODO: Increase the type of auth accounts that can be added to a user.
2021-06-08 11:31:58 -05:00
if ! handlers . ValidId ( handlers . Id ( a ),
2023-03-10 15:06:39 -05:00
globals . PasswordAccountPreviousPrefix ,
globals . PasswordAccountPrefix ,
globals . OidcAccountPrefix ,
2023-05-03 09:45:29 -04:00
globals . LdapAccountPrefix ,
2021-06-08 11:31:58 -05:00
) {
2020-10-02 16:45:32 -07:00
badFields [ "account_ids" ] = "Values must be valid account ids."
break
}
}
2020-09-25 20:44:55 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
func validateRemoveUserAccountsRequest ( req * pbs . RemoveUserAccountsRequest ) error {
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . UserPrefix ) {
2020-09-25 20:44:55 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
if req . GetVersion () == 0 {
badFields [ "version" ] = "Required field."
}
if len ( req . GetAccountIds ()) == 0 {
badFields [ "account_ids" ] = "Must be non-empty."
}
2020-10-02 16:45:32 -07:00
for _ , a := range req . GetAccountIds () {
// TODO: Increase the type of auth accounts that can be added to a user.
2021-06-08 11:31:58 -05:00
if ! handlers . ValidId ( handlers . Id ( a ),
2023-03-10 15:06:39 -05:00
globals . PasswordAccountPreviousPrefix ,
globals . PasswordAccountPrefix ,
globals . OidcAccountPrefix ,
2023-05-03 09:45:29 -04:00
globals . LdapAccountPrefix ,
2021-06-08 11:31:58 -05:00
) {
2020-10-02 16:45:32 -07:00
badFields [ "account_ids" ] = "Values must be valid account ids."
break
}
}
2020-09-25 20:44:55 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
2024-04-17 08:26:46 -07:00
}
return nil
}
func validateListResolvableAliasesRequest ( req * pbs . ListResolvableAliasesRequest ) error {
badFields := map [ string ] string {}
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . UserPrefix ) {
badFields [ globals . IdField ] = "Incorrectly formatted identifier."
}
if req . GetId () == globals . RecoveryUserId {
badFields [ "principal_ids" ] = "Cannot list resolvable aliases for the recovery user."
}
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
2020-09-25 20:44:55 -07:00
}
return nil
}
2023-12-20 16:51:57 -08:00
func newOutputOpts ( ctx context . Context , item * iam . User , scopeInfoMap map [ string ] * scopes . ScopeInfo , authResults auth . VerifyResults ) ([] handlers . Option , bool ) {
res := perms . Resource {
Type : resource . User ,
}
res . Id = item . GetPublicId ()
res . ScopeId = item . GetScopeId ()
2025-01-24 10:23:33 -08:00
res . ParentScopeId = scopeInfoMap [ item . GetScopeId ()]. GetParentScopeId ()
2023-12-20 16:51:57 -08:00
authorizedActions := authResults . FetchActionSetForId ( ctx , item . GetPublicId (), IdActions , auth . WithResource ( & res ))
if len ( authorizedActions ) == 0 {
return nil , false
}
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 . Strings ()))
}
return outputOpts , true
}