2022-08-10 17:20:06 -04:00
// Copyright (c) HashiCorp, Inc.
2020-06-22 06:56:27 -07:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-06-22 06:56:27 -07:00
package roles
import (
"context"
"fmt"
2024-01-23 14:33:09 -05:00
"sort"
"strings"
2020-06-22 06:56:27 -07:00
2021-05-04 19:57:35 -05:00
"github.com/hashicorp/boundary/globals"
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"
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-15 11:49:59 -08:00
"github.com/hashicorp/boundary/internal/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
2020-08-10 17:15:02 -04: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-08-21 08:15:32 -07:00
"github.com/hashicorp/boundary/internal/types/scope"
2021-08-26 10:44:41 -04:00
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/roles"
2023-12-15 11:49:59 -08:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
2021-07-14 17:37:40 -07:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2020-06-22 06:56:27 -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 . AddPrincipals ,
action . SetPrincipals ,
action . RemovePrincipals ,
action . AddGrants ,
action . SetGrants ,
action . RemoveGrants ,
2024-01-23 14:33:09 -05:00
action . AddGrantScopes ,
action . SetGrantScopes ,
action . RemoveGrantScopes ,
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-22 06:56:27 -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 . Role {}},
handlers . MaskSource { & pb . Role {}},
); 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 . Role , IdActions , CollectionActions )
2020-07-20 11:17:09 -07:00
}
2020-06-22 06:56:27 -07:00
// Service handles request as described by the pbs.RoleServiceServer interface.
type Service struct {
2022-08-30 14:22:10 -04:00
pbs . UnsafeRoleServiceServer
2020-11-11 14:41:05 -05:00
2023-12-15 11:49:59 -08:00
repoFn common . IamRepoFactory
maxPageSize uint
2020-06-22 06:56:27 -07:00
}
2022-08-30 14:22:10 -04:00
var _ pbs . RoleServiceServer = ( * Service )( nil )
2020-08-10 17:15:02 -04:00
// NewService returns a role service which handles role related requests to boundary.
2023-12-15 11:49:59 -08:00
func NewService ( ctx context . Context , repo common . IamRepoFactory , maxPageSize uint ) ( Service , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.NewService"
2020-06-22 06:56:27 -07:00
if repo == nil {
2023-06-06 17:09:08 -04:00
return Service {}, errors . New ( ctx , errors . InvalidParameter , op , "missing iam repository" )
2020-06-22 06:56:27 -07:00
}
2023-12-15 11:49:59 -08:00
if maxPageSize == 0 {
maxPageSize = uint ( globals . DefaultMaxPageSize )
}
return Service { repoFn : repo , maxPageSize : maxPageSize }, nil
2020-06-22 06:56:27 -07:00
}
// ListRoles implements the interface pbs.RoleServiceServer.
func ( s Service ) ListRoles ( ctx context . Context , req * pbs . ListRolesRequest ) ( * pbs . ListRolesResponse , error ) {
2023-12-15 11:49:59 -08:00
const op = "roles.(Service).ListRoles"
2023-06-06 17:09:08 -04:00
if err := validateListRequest ( ctx , req ); err != nil {
2020-06-22 06:56:27 -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-23 13:39:04 -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 . Role , req . GetRecursive ())
2021-01-25 19:37:40 -05:00
if err != nil {
return nil , err
2021-01-23 13:39:04 -05:00
}
2023-12-15 11:49:59 -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-06-22 06:56:27 -07:00
}
2023-12-15 11:49:59 -08:00
var filterItemFn func ( ctx context . Context , item * iam . Role ) ( 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 . Role ) ( bool , error ) {
outputOpts , ok := newOutputOpts ( ctx , item , scopeInfoMap , authResults )
if ! ok {
return false , nil
}
2024-01-23 14:33:09 -05:00
pbItem , err := toProto ( ctx , item , nil , nil , nil , outputOpts ... )
2023-12-15 11:49:59 -08:00
if err != nil {
return false , err
}
return filter . Match ( pbItem ), nil
}
default :
filterItemFn = func ( ctx context . Context , item * iam . Role ) ( bool , error ) {
return true , nil
}
2021-05-04 19:57:35 -05:00
}
2021-01-23 13:39:04 -05:00
2023-12-15 11:49:59 -08:00
grantsHash , err := authResults . GrantsHash ( ctx )
2021-02-22 10:09:25 -07:00
if err != nil {
2023-12-15 11:49:59 -08:00
return nil , errors . Wrap ( ctx , err , op )
2021-02-22 10:09:25 -07:00
}
2023-12-15 11:49:59 -08:00
repo , err := s . repoFn ()
if err != nil {
return nil , errors . Wrap ( ctx , err , op )
2021-01-20 10:53:01 -05:00
}
2021-05-04 19:57:35 -05:00
2023-12-15 11:49:59 -08:00
var listResp * pagination . ListResponse [ * iam . Role ]
var sortBy string
if req . GetListToken () == "" {
sortBy = "created_time"
listResp , err = iam . ListRoles ( ctx , grantsHash , pageSize , filterItemFn , repo , scopeIds )
if err != nil {
return nil , err
}
} else {
listToken , err := handlers . ParseListToken ( ctx , req . GetListToken (), resource . Role , grantsHash )
if err != nil {
return nil , err
2021-05-04 19:57:35 -05:00
}
2023-12-15 11:49:59 -08:00
switch st := listToken . Subtype .( type ) {
case * listtoken . PaginationToken :
sortBy = "created_time"
listResp , err = iam . ListRolesPage ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , scopeIds )
if err != nil {
return nil , err
}
case * listtoken . StartRefreshToken :
sortBy = "updated_time"
listResp , err = iam . ListRolesRefresh ( ctx , grantsHash , pageSize , filterItemFn , listToken , repo , scopeIds )
if err != nil {
return nil , err
}
case * listtoken . RefreshToken :
sortBy = "updated_time"
listResp , err = iam . ListRolesRefreshPage ( 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-15 11:49:59 -08:00
}
2021-05-04 19:57:35 -05:00
2023-12-15 11:49:59 -08:00
finalItems := make ([] * pb . Role , 0 , len ( listResp . Items ))
for _ , item := range listResp . Items {
outputOpts , ok := newOutputOpts ( ctx , item , scopeInfoMap , authResults )
if ! ok {
continue
}
2024-06-25 13:05:52 -04:00
item , err := toProto ( ctx , item , nil , nil , item . GrantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
2023-12-15 11:49:59 -08:00
return nil , errors . Wrap ( ctx , err , op )
2021-05-04 19:57:35 -05:00
}
2023-12-15 11:49:59 -08:00
finalItems = append ( finalItems , item )
}
respType := "delta"
if listResp . CompleteListing {
respType = "complete"
}
resp := & pbs . ListRolesResponse {
Items : finalItems ,
EstItemCount : uint32 ( listResp . EstimatedItemCount ),
RemovedIds : listResp . DeletedIds ,
ResponseType : respType ,
SortBy : sortBy ,
SortDir : "desc" ,
}
2021-05-04 19:57:35 -05:00
2023-12-15 11:49:59 -08:00
if listResp . ListToken != nil {
resp . ListToken , err = handlers . MarshalListToken ( ctx , listResp . ListToken , pbs . ResourceType_RESOURCE_TYPE_ROLE )
if err != nil {
return nil , err
2021-01-20 10:53:01 -05:00
}
2020-07-22 00:14:28 -04:00
}
2023-12-15 11:49:59 -08:00
return resp , nil
2020-06-22 06:56:27 -07:00
}
// GetRoles implements the interface pbs.RoleServiceServer.
func ( s Service ) GetRole ( ctx context . Context , req * pbs . GetRoleRequest ) ( * pbs . GetRoleResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).GetRole"
2020-06-22 06:56:27 -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
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . getFromRepo ( ctx , req . GetId ())
2020-06-22 06:56:27 -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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
return & pbs . GetRoleResponse { Item : item }, nil
2020-06-22 06:56:27 -07:00
}
// CreateRole implements the interface pbs.RoleServiceServer.
func ( s Service ) CreateRole ( ctx context . Context , req * pbs . CreateRoleRequest ) ( * pbs . CreateRoleResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).CreateRole"
2020-09-04 14:48:11 -07:00
if err := validateCreateRequest ( req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetItem (). GetScopeId (), action . Create )
2020-08-16 00:42:36 -04:00
if authResults . Error != nil {
return nil , authResults . Error
2020-07-22 00:14:28 -04:00
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . createInRepo ( ctx , authResults . Scope . GetId (), req . GetItem ())
2020-06-22 06:56:27 -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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
return & pbs . CreateRoleResponse { Item : item , Uri : fmt . Sprintf ( "roles/%s" , item . GetId ())}, nil
2020-06-22 06:56:27 -07:00
}
// UpdateRole implements the interface pbs.RoleServiceServer.
func ( s Service ) UpdateRole ( ctx context . Context , req * pbs . UpdateRoleRequest ) ( * pbs . UpdateRoleResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).UpdateRole"
2020-09-04 14:48:11 -07:00
if err := validateUpdateRequest ( req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . Update )
2020-08-16 00:42:36 -04:00
if authResults . Error != nil {
return nil , authResults . Error
2020-07-22 00:14:28 -04:00
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . updateInRepo ( ctx , authResults . Scope . GetId (), req . GetId (), req . GetUpdateMask (). GetPaths (), req . GetItem ())
2021-05-04 19:57:35 -05:00
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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2020-06-22 06:56:27 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . UpdateRoleResponse { Item : item }, nil
2020-06-22 06:56:27 -07:00
}
// DeleteRole implements the interface pbs.RoleServiceServer.
func ( s Service ) DeleteRole ( ctx context . Context , req * pbs . DeleteRoleRequest ) ( * pbs . DeleteRoleResponse , 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-22 06:56:27 -07:00
if err != nil {
return nil , err
}
2021-04-26 19:38:55 -05:00
return nil , nil
2020-06-22 06:56:27 -07:00
}
2020-07-10 11:21:11 -07:00
// AddRolePrincipals implements the interface pbs.RoleServiceServer.
func ( s Service ) AddRolePrincipals ( ctx context . Context , req * pbs . AddRolePrincipalsRequest ) ( * pbs . AddRolePrincipalsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).AddRolePrincipals"
2020-07-10 11:21:11 -07:00
if err := validateAddRolePrincipalsRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . AddPrincipals )
if authResults . Error != nil {
return nil , authResults . Error
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . addPrincipalsInRepo ( ctx , req . GetId (), req . GetPrincipalIds (), req . GetVersion ())
2021-05-04 19:57:35 -05:00
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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2020-07-10 11:21:11 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . AddRolePrincipalsResponse { Item : item }, nil
2020-07-10 11:21:11 -07:00
}
// SetRolePrincipals implements the interface pbs.RoleServiceServer.
func ( s Service ) SetRolePrincipals ( ctx context . Context , req * pbs . SetRolePrincipalsRequest ) ( * pbs . SetRolePrincipalsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).SetRolePrincipals"
2020-07-10 11:21:11 -07:00
if err := validateSetRolePrincipalsRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . SetPrincipals )
if authResults . Error != nil {
return nil , authResults . Error
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . setPrincipalsInRepo ( ctx , req . GetId (), req . GetPrincipalIds (), req . GetVersion ())
2020-07-10 11:21:11 -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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
return & pbs . SetRolePrincipalsResponse { Item : item }, nil
2020-07-10 11:21:11 -07:00
}
// RemoveRolePrincipals implements the interface pbs.RoleServiceServer.
func ( s Service ) RemoveRolePrincipals ( ctx context . Context , req * pbs . RemoveRolePrincipalsRequest ) ( * pbs . RemoveRolePrincipalsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).RemoveRolePrincipals"
2020-07-10 11:21:11 -07:00
if err := validateRemoveRolePrincipalsRequest ( req ); err != nil {
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . RemovePrincipals )
if authResults . Error != nil {
return nil , authResults . Error
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . removePrincipalsInRepo ( ctx , req . GetId (), req . GetPrincipalIds (), req . GetVersion ())
2020-07-10 11:21:11 -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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
return & pbs . RemoveRolePrincipalsResponse { Item : item }, nil
2020-07-10 11:21:11 -07:00
}
2020-07-10 17:42:47 -07:00
// AddRoleGrants implements the interface pbs.RoleServiceServer.
func ( s Service ) AddRoleGrants ( ctx context . Context , req * pbs . AddRoleGrantsRequest ) ( * pbs . AddRoleGrantsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).AddRoleGrants"
2023-06-06 16:13:44 -04:00
if err := validateAddRoleGrantsRequest ( ctx , req ); err != nil {
2020-07-10 17:42:47 -07:00
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . AddGrants )
if authResults . Error != nil {
return nil , authResults . Error
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . addGrantsInRepo ( ctx , req . GetId (), req . GetGrantStrings (), req . GetVersion ())
2021-05-04 19:57:35 -05:00
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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2020-07-10 17:42:47 -07:00
if err != nil {
return nil , err
}
2021-05-04 19:57:35 -05:00
return & pbs . AddRoleGrantsResponse { Item : item }, nil
2020-07-10 17:42:47 -07:00
}
// SetRoleGrants implements the interface pbs.RoleServiceServer.
func ( s Service ) SetRoleGrants ( ctx context . Context , req * pbs . SetRoleGrantsRequest ) ( * pbs . SetRoleGrantsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).SetRoleGrants"
2023-06-06 16:13:44 -04:00
if err := validateSetRoleGrantsRequest ( ctx , req ); err != nil {
2020-07-10 17:42:47 -07:00
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . SetGrants )
if authResults . Error != nil {
return nil , authResults . Error
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . setGrantsInRepo ( ctx , req . GetId (), req . GetGrantStrings (), req . GetVersion ())
2020-07-10 17:42:47 -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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
return & pbs . SetRoleGrantsResponse { Item : item }, nil
2020-07-10 17:42:47 -07:00
}
// RemoveRoleGrants implements the interface pbs.RoleServiceServer.
func ( s Service ) RemoveRoleGrants ( ctx context . Context , req * pbs . RemoveRoleGrantsRequest ) ( * pbs . RemoveRoleGrantsResponse , error ) {
2021-05-04 19:57:35 -05:00
const op = "roles.(Service).RemoveRoleGrants"
2023-06-06 16:13:44 -04:00
if err := validateRemoveRoleGrantsRequest ( ctx , req ); err != nil {
2020-07-10 17:42:47 -07:00
return nil , err
}
2020-09-04 14:48:11 -07:00
authResults := s . authResult ( ctx , req . GetId (), action . RemoveGrants )
if authResults . Error != nil {
return nil , authResults . Error
}
2024-01-23 14:33:09 -05:00
r , prs , rgs , grantScopes , err := s . removeGrantsInRepo ( ctx , req . GetId (), req . GetGrantStrings (), req . GetVersion ())
2020-07-10 17:42:47 -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 , r . GetPublicId (), IdActions ). Strings ()))
}
2024-01-23 14:33:09 -05:00
item , err := toProto ( ctx , r , prs , rgs , grantScopes , outputOpts ... )
2021-05-04 19:57:35 -05:00
if err != nil {
return nil , err
}
return & pbs . RemoveRoleGrantsResponse { Item : item }, nil
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
// AddRoleGrantScopes implements the interface pbs.RoleServiceServer.
func ( s Service ) AddRoleGrantScopes ( ctx context . Context , req * pbs . AddRoleGrantScopesRequest ) ( * pbs . AddRoleGrantScopesResponse , error ) {
const op = "roles.(Service).AddRoleGrantScopes"
if err := validateRoleGrantScopesRequest ( ctx , req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . AddGrantScopes )
if authResults . Error != nil {
return nil , authResults . Error
}
r , prs , rgs , grantScopes , err := s . addGrantScopesInRepo ( ctx , req )
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
return nil , errors . New ( ctx , 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 , grantScopes , outputOpts ... )
if err != nil {
return nil , err
}
return & pbs . AddRoleGrantScopesResponse { Item : item }, nil
}
// SetRoleGrantScopes implements the interface pbs.RoleServiceServer.
func ( s Service ) SetRoleGrantScopes ( ctx context . Context , req * pbs . SetRoleGrantScopesRequest ) ( * pbs . SetRoleGrantScopesResponse , error ) {
const op = "roles.(Service).SetRoleGrantScopes"
if err := validateRoleGrantScopesRequest ( ctx , req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . SetGrantScopes )
if authResults . Error != nil {
return nil , authResults . Error
}
r , prs , rgs , grantScopes , err := s . setGrantScopesInRepo ( ctx , req )
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
return nil , errors . New ( ctx , 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 , grantScopes , outputOpts ... )
if err != nil {
return nil , err
}
return & pbs . SetRoleGrantScopesResponse { Item : item }, nil
}
// RemoveRoleGrantScopes implements the interface pbs.RoleServiceServer.
func ( s Service ) RemoveRoleGrantScopes ( ctx context . Context , req * pbs . RemoveRoleGrantScopesRequest ) ( * pbs . RemoveRoleGrantScopesResponse , error ) {
const op = "roles.(Service).RemoveRoleGrantScopes"
if err := validateRoleGrantScopesRequest ( ctx , req ); err != nil {
return nil , err
}
authResults := s . authResult ( ctx , req . GetId (), action . RemoveGrants )
if authResults . Error != nil {
return nil , authResults . Error
}
r , prs , rgs , grantScopes , err := s . removeGrantScopesInRepo ( ctx , req )
if err != nil {
return nil , err
}
outputFields , ok := requests . OutputFields ( ctx )
if ! ok {
return nil , errors . New ( ctx , 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 , grantScopes , outputOpts ... )
if err != nil {
return nil , err
}
return & pbs . RemoveRoleGrantScopesResponse { Item : item }, nil
}
func ( s Service ) getFromRepo ( ctx context . Context , id string ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2020-06-22 06:56:27 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , roleGrantScopes , err := repo . LookupRole ( ctx , id )
2020-06-22 06:56:27 -07:00
if err != nil {
2020-12-21 10:07:34 -08:00
if errors . IsNotFoundError ( err ) {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . NotFoundErrorf ( "Role %q doesn't exist." , id )
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-06-22 06:56:27 -07:00
}
2020-07-10 11:21:11 -07:00
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . NotFoundErrorf ( "Role %q doesn't exist." , id )
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , roleGrantScopes , nil
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) createInRepo ( ctx context . Context , scopeId string , item * pb . Role ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.(Service).createInRepo"
2020-06-22 06:56:27 -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 . NewRole ( ctx , scopeId , opts ... )
2020-06-22 06:56:27 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to build role for creation: %v." , err )
2020-06-22 06:56:27 -07:00
}
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , roleGrantScopes , err := repo . CreateRole ( ctx , u , opts ... )
2020-06-22 06:56:27 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op )
2020-06-22 06:56:27 -07:00
}
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to create role but no error returned from repository." )
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , roleGrantScopes , nil
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) updateInRepo ( ctx context . Context , scopeId , id string , mask [] string , item * pb . Role ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.(Service).updateInRepo"
2020-06-22 06:56:27 -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 ()
2020-07-10 17:56:33 -04:00
2023-06-06 17:09:08 -04:00
u , err := iam . NewRole ( ctx , scopeId , opts ... )
2020-06-22 06:56:27 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to build role for update: %v." , err )
2020-06-22 06:56:27 -07:00
}
u . PublicId = id
2024-01-23 14:33:09 -05:00
2020-06-22 06:56:27 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
dbMask := maskManager . Translate ( mask )
2024-06-17 12:09:33 -07:00
if len ( dbMask ) == 0 {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . InvalidArgumentErrorf ( "No valid fields provided in the update mask." , map [ string ] string { "update_mask" : "No valid fields provided in the update mask." })
}
out , pr , gr , grantScopes , rowsUpdated , err := repo . UpdateRole ( ctx , u , version , dbMask , opts ... )
2020-06-22 06:56:27 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op )
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
// This is slightly problematic but it's a very unlikely error case and when
// we remove the ability to update grant scope ID via here in 0.17 it will
// go away.
2024-06-17 12:09:33 -07:00
if rowsUpdated == 0 {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . NotFoundErrorf ( "Role %q doesn't exist or incorrect version provided." , id )
2020-06-22 06:56:27 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , gr , grantScopes , nil
2020-06-22 06:56:27 -07:00
}
func ( s Service ) deleteFromRepo ( ctx context . Context , id string ) ( bool , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.(Service).deleteFromRepo"
2020-06-22 06:56:27 -07:00
repo , err := s . repoFn ()
if err != nil {
return false , err
}
rows , err := repo . DeleteRole ( ctx , id )
if err != nil {
2020-12-21 10:07:34 -08:00
if errors . IsNotFoundError ( err ) {
2020-06-22 06:56:27 -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 role" ))
2020-06-22 06:56:27 -07:00
}
return rows > 0 , nil
}
2024-01-23 14:33:09 -05:00
func ( s Service ) addPrincipalsInRepo ( ctx context . Context , roleId string , principalIds [] string , version uint32 ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.(Service).addPrincpleInRepo"
2020-07-10 11:21:11 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-07-10 11:21:11 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . AddPrincipalRoles ( ctx , roleId , version , strutil . RemoveDuplicates ( principalIds , false ))
2020-07-10 11:21:11 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to add principals to role: %v." , err )
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , roleId )
2020-07-10 15:46:26 -04:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after adding principals" ))
2020-07-10 15:46:26 -04:00
}
2020-07-10 11:21:11 -07:00
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after adding principals to it." )
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , grantScopes , nil
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) setPrincipalsInRepo ( ctx context . Context , roleId string , principalIds [] string , version uint32 ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
const op = "roles.(Service).setPrincipalsInRepo"
2020-07-10 11:21:11 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-07-10 11:21:11 -07:00
}
2020-10-02 16:45:32 -07:00
_ , _ , err = repo . SetPrincipalRoles ( ctx , roleId , version , strutil . RemoveDuplicates ( principalIds , false ))
2020-07-10 11:21:11 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to set principals on role: %v." , err )
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , roleId )
2020-07-10 15:46:26 -04:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after setting principals" ))
2020-07-10 15:46:26 -04:00
}
2020-07-10 11:21:11 -07:00
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after setting principals for it." )
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , grantScopes , nil
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) removePrincipalsInRepo ( ctx context . Context , roleId string , principalIds [] string , version uint32 ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
const op = "roles.(Service).removePrincipalsInRepo"
2020-07-10 11:21:11 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-07-10 11:21:11 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . DeletePrincipalRoles ( ctx , roleId , version , strutil . RemoveDuplicates ( principalIds , false ))
2020-07-10 11:21:11 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to remove principals from role: %v." , err )
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , roleId )
2020-07-10 15:46:26 -04:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after removing principals" ))
2020-07-10 15:46:26 -04:00
}
2020-07-10 11:21:11 -07:00
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after removing principals from it." )
2020-07-10 11:21:11 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , grantScopes , nil
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) addGrantsInRepo ( ctx context . Context , roleId string , grants [] string , version uint32 ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2021-04-16 12:06:03 -05:00
const op = "service.(Service).addGrantsInRepo"
2020-07-10 17:42:47 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-07-10 17:42:47 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . AddRoleGrants ( ctx , roleId , version , strutil . RemoveDuplicates ( grants , false ))
2020-07-10 17:42:47 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to add grants to role: %v." , err )
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , roleId )
2020-07-10 17:42:47 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after adding grants" ))
2020-07-10 17:42:47 -07:00
}
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after adding grants to it." )
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , grantScopes , nil
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) setGrantsInRepo ( ctx context . Context , roleId string , grants [] string , version uint32 ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.(Service).setGrantsInRepo"
2020-07-10 17:42:47 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-07-10 17:42:47 -07:00
}
// If no grant was provided, we clear the grants.
if grants == nil {
grants = [] string {}
}
2020-10-02 16:45:32 -07:00
_ , _ , err = repo . SetRoleGrants ( ctx , roleId , version , strutil . RemoveDuplicates ( grants , false ))
2020-07-10 17:42:47 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to set grants on role: %v." , err )
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , roleId )
2020-07-10 17:42:47 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after setting grants" ))
2020-07-10 17:42:47 -07:00
}
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after setting grants on it." )
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , grantScopes , nil
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
func ( s Service ) removeGrantsInRepo ( ctx context . Context , roleId string , grants [] string , version uint32 ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
2021-04-16 12:06:03 -05:00
const op = "roles.(Service).removeGrantsInRepo"
2020-07-10 17:42:47 -07:00
repo , err := s . repoFn ()
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , err
2020-07-10 17:42:47 -07:00
}
2020-10-02 16:45:32 -07:00
_ , err = repo . DeleteRoleGrants ( ctx , roleId , version , strutil . RemoveDuplicates ( grants , false ))
2020-07-10 17:42:47 -07:00
if err != nil {
2020-09-25 13:41:20 -07:00
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to remove grants from role: %v" , err )
}
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , roleId )
if err != nil {
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "uable to look up role after removing grant" ))
}
if out == nil {
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after removing grants from it." )
}
return out , pr , roleGrants , grantScopes , nil
}
func ( s Service ) addGrantScopesInRepo ( ctx context . Context , req grantScopeRequest ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
const op = "service.(Service).addGrantScopesInRepo"
repo , err := s . repoFn ()
if err != nil {
return nil , nil , nil , nil , err
}
deduped := strutil . RemoveDuplicates ( req . GetGrantScopeIds (), false )
if err := validateRoleGrantScopesHierarchy ( ctx , repo , req . GetId (), deduped ); err != nil {
return nil , nil , nil , nil , err
}
_ , err = repo . AddRoleGrantScopes ( ctx , req . GetId (), req . GetVersion (), deduped )
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to add grant scopes to role: %v." , err )
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , req . GetId ())
2020-07-10 17:42:47 -07:00
if err != nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after adding grant scopes" ))
2020-07-10 17:42:47 -07:00
}
if out == nil {
2024-01-23 14:33:09 -05:00
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after adding grant scopes to it." )
2020-07-10 17:42:47 -07:00
}
2024-01-23 14:33:09 -05:00
return out , pr , roleGrants , grantScopes , nil
}
func ( s Service ) setGrantScopesInRepo ( ctx context . Context , req grantScopeRequest ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
const op = "service.(Service).setGrantScopesInRepo"
repo , err := s . repoFn ()
if err != nil {
return nil , nil , nil , nil , err
}
deduped := strutil . RemoveDuplicates ( req . GetGrantScopeIds (), false )
if err := validateRoleGrantScopesHierarchy ( ctx , repo , req . GetId (), deduped ); err != nil {
return nil , nil , nil , nil , err
}
_ , _ , err = repo . SetRoleGrantScopes ( ctx , req . GetId (), req . GetVersion (), deduped )
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to set grant scopes on role: %v." , err )
}
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , req . GetId ())
if err != nil {
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after setting grant scopes" ))
}
if out == nil {
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after setting grant scopes to it." )
}
return out , pr , roleGrants , grantScopes , nil
}
func ( s Service ) removeGrantScopesInRepo ( ctx context . Context , req grantScopeRequest ) ( * iam . Role , [] * iam . PrincipalRole , [] * iam . RoleGrant , [] * iam . RoleGrantScope , error ) {
const op = "service.(Service).setGrantScopesInRepo"
repo , err := s . repoFn ()
if err != nil {
return nil , nil , nil , nil , err
}
_ , err = repo . DeleteRoleGrantScopes ( ctx , req . GetId (), req . GetVersion (), strutil . RemoveDuplicates ( req . GetGrantScopeIds (), false ))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to remove grant scopes from role: %v." , err )
}
out , pr , roleGrants , grantScopes , err := repo . LookupRole ( ctx , req . GetId ())
if err != nil {
return nil , nil , nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "unable to look up role after removing grant scopes" ))
}
if out == nil {
return nil , nil , nil , nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "Unable to lookup role after removing grant scopes to it." )
}
return out , pr , roleGrants , grantScopes , nil
2020-07-10 11:21:11 -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 . Role ), 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 :
2024-01-23 14:33:09 -05:00
r , _ , _ , _ , err := repo . LookupRole ( ctx , id )
2020-09-04 14:48:11 -07:00
if err != nil {
res . Error = err
return res
}
if r == nil {
2020-09-08 23:57:26 -04:00
res . Error = handlers . NotFoundError ()
2020-09-04 14:48:11 -07:00
return res
}
parentId = r . GetScopeId ()
opts = append ( opts , auth . WithId ( id ))
}
opts = append ( opts , auth . WithScopeId ( parentId ))
return auth . Verify ( ctx , opts ... )
}
2024-01-23 14:33:09 -05:00
func toProto ( ctx context . Context , in * iam . Role , principals [] * iam . PrincipalRole , grants [] * iam . RoleGrant , grantScopes [] * iam . RoleGrantScope , opt ... handlers . Option ) ( * pb . Role , error ) {
2021-05-04 19:57:35 -05:00
opts := handlers . GetOpts ( opt ... )
if opts . WithOutputFields == nil {
return nil , handlers . ApiErrorWithCodeAndMessage ( codes . Internal , "output fields not found when building role proto" )
}
outputFields := * opts . WithOutputFields
out := pb . Role {}
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 ())
}
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 . PrincipalIdsField ) {
for _ , p := range principals {
out . PrincipalIds = append ( out . PrincipalIds , p . GetPrincipalId ())
2020-07-10 11:21:11 -07:00
}
}
2024-01-23 14:33:09 -05:00
if outputFields . Has ( globals . GrantScopeIdsField ) {
for _ , gs := range grantScopes {
out . GrantScopeIds = append ( out . GrantScopeIds , gs . GetScopeIdOrSpecial ())
}
sort . Strings ( out . GrantScopeIds )
}
2021-05-04 19:57:35 -05:00
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 )
2020-07-10 17:42:47 -07:00
}
}
2021-05-04 19:57:35 -05:00
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 {
2024-09-17 10:07:43 -04:00
parsed , err := perms . Parse ( ctx , perms . GrantTuple { RoleScopeId : in . GetPublicId (), GrantScopeId : in . GetScopeId (), Grant : g . GetRawGrant ()})
2021-05-04 19:57:35 -05:00
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 (),
2023-06-06 16:13:44 -04:00
Ids : parsed . Ids (),
2021-05-04 19:57:35 -05:00
Type : parsed . Type (). String (),
Actions : actions ,
},
})
}
}
2020-07-10 17:56:33 -04:00
}
2021-05-04 19:57:35 -05:00
return & out , nil
2020-06-22 06:56:27 -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-22 06:56:27 -07:00
func validateGetRequest ( req * pbs . GetRoleRequest ) error {
2023-03-10 15:06:39 -05:00
return handlers . ValidateGetRequest ( handlers . NoopValidatorFn , req , globals . RolePrefix )
2020-06-22 06:56:27 -07:00
}
2020-09-04 14:48:11 -07:00
func validateCreateRequest ( req * pbs . CreateRoleRequest ) error {
2020-08-21 08:15:32 -07:00
return handlers . ValidateCreateRequest ( req . GetItem (), func () map [ string ] string {
badFields := map [ string ] string {}
item := req . GetItem ()
2021-04-08 20:54:32 -04:00
if ! handlers . ValidId ( handlers . Id ( item . GetScopeId ()), scope . Org . Prefix ()) &&
! handlers . ValidId ( handlers . Id ( item . GetScopeId ()), scope . Project . Prefix ()) &&
2020-09-02 19:43:41 -07:00
scope . Global . String () != item . GetScopeId () {
2020-09-03 08:02:30 -07:00
badFields [ "scope_id" ] = "This field is missing or improperly formatted."
2020-09-02 19:43:41 -07:00
}
2020-08-21 08:15:32 -07:00
if item . GetPrincipals () != nil {
badFields [ "principals" ] = "This is a read only field."
}
if item . GetGrants () != nil {
badFields [ "grant_strings" ] = "This is a read only field."
}
return badFields
})
2020-06-22 06:56:27 -07:00
}
2020-09-04 14:48:11 -07:00
func validateUpdateRequest ( req * pbs . UpdateRoleRequest ) error {
2021-04-08 20:54:32 -04:00
return handlers . ValidateUpdateRequest ( req , req . GetItem (), func () map [ string ] string {
2020-08-21 08:15:32 -07:00
badFields := map [ string ] string {}
if req . GetItem (). GetPrincipalIds () != nil {
badFields [ "principal_ids" ] = "This is a read only field and cannot be specified in an update request."
2020-07-10 17:56:33 -04:00
}
2020-08-21 08:15:32 -07:00
if req . GetItem (). GetPrincipals () != nil {
badFields [ "principals" ] = "This is a read only field and cannot be specified in an update request."
}
if req . GetItem (). GetGrants () != nil {
badFields [ "grants" ] = "This is a read only field and cannot be specified in an update request."
}
if req . GetItem (). GetGrantStrings () != nil {
badFields [ "grant_strings" ] = "This is a read only field and cannot be specified in an update request."
}
return badFields
2023-03-10 15:06:39 -05:00
}, globals . RolePrefix )
2020-06-22 06:56:27 -07:00
}
func validateDeleteRequest ( req * pbs . DeleteRoleRequest ) error {
2021-04-08 20:54:32 -04:00
return handlers . ValidateDeleteRequest ( func () map [ string ] string {
2020-09-21 16:50:39 -04:00
return nil
2023-03-10 15:06:39 -05:00
}, req , globals . RolePrefix )
2020-06-22 06:56:27 -07:00
}
2023-06-06 17:09:08 -04:00
func validateListRequest ( ctx context . Context , req * pbs . ListRolesRequest ) 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 ()) &&
! handlers . ValidId ( handlers . Id ( req . GetScopeId ()), scope . Project . Prefix ()) &&
2020-09-08 23:57:26 -04:00
req . GetScopeId () != scope . Global . String () {
2020-09-02 19:43:41 -07:00
badFields [ "scope_id" ] = "Improperly formatted field."
}
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-06-22 06:56:27 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Improperly formatted identifier." , badFields )
}
return nil
}
2020-07-10 11:21:11 -07:00
func validateAddRolePrincipalsRequest ( req * pbs . AddRolePrincipalsRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
2020-07-10 11:21:11 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
2020-08-07 12:55:02 -04:00
if req . GetVersion () == 0 {
2020-07-10 11:21:11 -07:00
badFields [ "version" ] = "Required field."
}
2020-08-18 17:38:38 -07:00
if len ( req . GetPrincipalIds ()) == 0 {
2020-07-14 14:32:47 -07:00
badFields [ "principal_ids" ] = "Must be non-empty."
2020-07-10 11:21:11 -07:00
}
2020-08-20 13:34:03 -04:00
for _ , id := range req . GetPrincipalIds () {
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( id ), globals . GroupPrefix ) &&
! handlers . ValidId ( handlers . Id ( id ), globals . UserPrefix ) &&
2023-06-26 17:05:54 -04:00
! handlers . ValidId ( handlers . Id ( id ), globals . OidcManagedGroupPrefix ) &&
! handlers . ValidId ( handlers . Id ( id ), globals . LdapManagedGroupPrefix ) {
2021-06-04 09:44:57 -05:00
badFields [ "principal_ids" ] = "Must only have valid user, group, and/or managed group ids."
2020-10-02 16:45:32 -07:00
break
}
2022-10-28 15:52:06 -04:00
if id == globals . RecoveryUserId {
2020-08-20 13:34:03 -04:00
badFields [ "principal_ids" ] = "u_recovery cannot be assigned to a role"
2020-10-02 16:45:32 -07:00
break
2020-08-20 13:34:03 -04:00
}
}
2020-07-10 11:21:11 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
func validateSetRolePrincipalsRequest ( req * pbs . SetRolePrincipalsRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
2020-07-10 11:21:11 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
2020-08-07 12:55:02 -04:00
if req . GetVersion () == 0 {
2020-07-10 11:21:11 -07:00
badFields [ "version" ] = "Required field."
}
2020-08-20 13:34:03 -04:00
for _ , id := range req . GetPrincipalIds () {
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( id ), globals . GroupPrefix ) &&
! handlers . ValidId ( handlers . Id ( id ), globals . UserPrefix ) &&
2023-06-27 12:13:13 -04:00
! handlers . ValidId ( handlers . Id ( id ), globals . OidcManagedGroupPrefix ) &&
! handlers . ValidId ( handlers . Id ( id ), globals . LdapManagedGroupPrefix ) {
2021-06-04 09:44:57 -05:00
badFields [ "principal_ids" ] = "Must only have valid user, group, and/or managed group ids."
2020-10-02 16:45:32 -07:00
break
}
2022-10-28 15:52:06 -04:00
if id == globals . RecoveryUserId {
2020-08-20 13:34:03 -04:00
badFields [ "principal_ids" ] = "u_recovery cannot be assigned to a role"
2020-10-02 16:45:32 -07:00
break
2020-08-20 13:34:03 -04:00
}
}
2020-07-10 11:21:11 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
func validateRemoveRolePrincipalsRequest ( req * pbs . RemoveRolePrincipalsRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
2020-07-10 11:21:11 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
2020-08-07 12:55:02 -04:00
if req . GetVersion () == 0 {
2020-07-10 11:21:11 -07:00
badFields [ "version" ] = "Required field."
}
2020-08-18 17:38:38 -07:00
if len ( req . GetPrincipalIds ()) == 0 {
2020-07-14 14:32:47 -07:00
badFields [ "principal_ids" ] = "Must be non-empty."
2020-07-10 11:21:11 -07:00
}
2020-10-02 16:45:32 -07:00
for _ , id := range req . GetPrincipalIds () {
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( id ), globals . GroupPrefix ) &&
! handlers . ValidId ( handlers . Id ( id ), globals . UserPrefix ) &&
2023-06-27 12:13:13 -04:00
! handlers . ValidId ( handlers . Id ( id ), globals . OidcManagedGroupPrefix ) &&
! handlers . ValidId ( handlers . Id ( id ), globals . LdapManagedGroupPrefix ) {
2021-06-04 09:44:57 -05:00
badFields [ "principal_ids" ] = "Must only have valid user, group, and/or managed group ids."
2020-10-02 16:45:32 -07:00
break
}
}
2020-07-10 11:21:11 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
2023-06-06 16:13:44 -04:00
func validateAddRoleGrantsRequest ( ctx context . Context , req * pbs . AddRoleGrantsRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
2020-07-10 17:42:47 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
2020-08-07 12:55:02 -04:00
if req . GetVersion () == 0 {
2020-07-10 17:42:47 -07:00
badFields [ "version" ] = "Required field."
}
2020-08-18 17:38:38 -07:00
if len ( req . GetGrantStrings ()) == 0 {
2020-07-16 22:18:27 -04:00
badFields [ "grant_strings" ] = "Must be non-empty."
2020-07-10 17:42:47 -07:00
}
2020-10-02 16:45:32 -07:00
for _ , v := range req . GetGrantStrings () {
if len ( v ) == 0 {
badFields [ "grant_strings" ] = "Grant strings must not be empty."
break
}
2024-09-17 10:07:43 -04:00
grant , err := perms . Parse ( ctx , perms . GrantTuple { RoleScopeId : req . GetId (), GrantScopeId : "p_anything" , Grant : v })
2022-09-15 09:20:08 -04:00
if err != nil {
2020-10-02 16:45:32 -07:00
badFields [ "grant_strings" ] = fmt . Sprintf ( "Improperly formatted grant %q." , v )
break
}
2022-09-15 09:20:08 -04:00
_ , actStrs := grant . Actions ()
for _ , actStr := range actStrs {
if depAct := action . DeprecatedMap [ actStr ]; depAct != action . Unknown {
badFields [ "grant_strings" ] = fmt . Sprintf ( "Action %q has been deprecated and is not allowed to be set in grants. Use %q instead." , actStr , depAct . String ())
}
}
2023-06-06 16:13:44 -04:00
switch {
case grant . Id () == "" :
// Nothing
default :
badFields [ "grant_strings" ] = fmt . Sprintf ( "Grant %q uses the %q field which is no longer supported. Please use %q instead." , v , "id" , "ids" )
}
2020-10-02 16:45:32 -07:00
}
2020-07-10 17:42:47 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
2023-06-06 16:13:44 -04:00
func validateSetRoleGrantsRequest ( ctx context . Context , req * pbs . SetRoleGrantsRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
2020-07-10 17:42:47 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
2020-08-07 12:55:02 -04:00
if req . GetVersion () == 0 {
2020-07-10 17:42:47 -07:00
badFields [ "version" ] = "Required field."
}
2020-10-02 16:45:32 -07:00
for _ , v := range req . GetGrantStrings () {
if len ( v ) == 0 {
badFields [ "grant_strings" ] = "Grant strings must not be empty."
break
}
2024-09-17 10:07:43 -04:00
grant , err := perms . Parse ( ctx , perms . GrantTuple { RoleScopeId : req . GetId (), GrantScopeId : "p_anything" , Grant : v })
2022-09-15 09:20:08 -04:00
if err != nil {
2024-09-17 10:07:43 -04:00
badFields [ "grant_strings" ] = fmt . Sprintf ( "Improperly formatted grant %q: %s." , v , err . Error ())
2020-10-02 16:45:32 -07:00
break
}
2022-09-15 09:20:08 -04:00
_ , actStrs := grant . Actions ()
for _ , actStr := range actStrs {
if depAct := action . DeprecatedMap [ actStr ]; depAct != action . Unknown {
badFields [ "grant_strings" ] = fmt . Sprintf ( "Action %q has been deprecated and is not allowed to be set in grants. Use %q instead." , actStr , depAct . String ())
}
}
2023-06-06 16:13:44 -04:00
switch {
case grant . Id () == "" :
// Nothing
default :
badFields [ "grant_strings" ] = fmt . Sprintf ( "Grant %q uses the %q field which is no longer supported. Please use %q instead." , v , "id" , "ids" )
}
2020-10-02 16:45:32 -07:00
}
2020-07-10 17:42:47 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
2023-06-06 16:13:44 -04:00
func validateRemoveRoleGrantsRequest ( ctx context . Context , req * pbs . RemoveRoleGrantsRequest ) error {
2020-07-22 00:14:28 -04:00
badFields := map [ string ] string {}
2023-03-10 15:06:39 -05:00
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
2020-07-10 17:42:47 -07:00
badFields [ "id" ] = "Incorrectly formatted identifier."
}
2020-08-07 12:55:02 -04:00
if req . GetVersion () == 0 {
2020-07-10 17:42:47 -07:00
badFields [ "version" ] = "Required field."
}
2020-08-18 17:38:38 -07:00
if len ( req . GetGrantStrings ()) == 0 {
2020-07-16 22:18:27 -04:00
badFields [ "grant_strings" ] = "Must be non-empty."
2020-07-10 17:42:47 -07:00
}
2020-10-02 16:45:32 -07:00
for _ , v := range req . GetGrantStrings () {
if len ( v ) == 0 {
badFields [ "grant_strings" ] = "Grant strings must not be empty."
break
}
2024-09-17 10:07:43 -04:00
if _ , err := perms . Parse ( ctx , perms . GrantTuple { RoleScopeId : req . GetId (), GrantScopeId : "p_anything" , Grant : v }); err != nil {
2020-10-02 16:45:32 -07:00
badFields [ "grant_strings" ] = fmt . Sprintf ( "Improperly formatted grant %q." , v )
break
}
2022-09-15 09:20:08 -04:00
// NOTE: we don't do a deprecation check here because it's fine to allow people to remove deprecated grants.
2020-10-02 16:45:32 -07:00
}
2020-07-10 17:42:47 -07:00
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
2023-12-15 11:49:59 -08:00
2024-01-23 14:33:09 -05:00
// grantScopeRequest allows us to reuse a few request types in a common way for
// grant scope add/set/remove operations
type grantScopeRequest interface {
GetId () string
GetVersion () uint32
GetGrantScopeIds () [] string
}
func validateRoleGrantScopesRequest ( ctx context . Context , req grantScopeRequest ) error {
badFields := map [ string ] string {}
if ! handlers . ValidId ( handlers . Id ( req . GetId ()), globals . RolePrefix ) {
badFields [ "id" ] = "Incorrectly formatted identifier."
}
if req . GetVersion () == 0 {
badFields [ "version" ] = "Required field."
}
if len ( req . GetGrantScopeIds ()) == 0 {
// This is actually okay for Set because they could be setting to null,
// e.g. removing all grant scope ids
if _ , ok := req .( * pbs . SetRoleGrantScopesRequest ); ! ok {
badFields [ "grant_scope_ids" ] = "Must be non-empty."
}
}
for _ , v := range req . GetGrantScopeIds () {
if len ( v ) == 0 {
badFields [ "grant_scope_ids" ] = "Grant scope IDs must not be empty."
break
}
switch {
case v == scope . Global . String (),
v == globals . GrantScopeThis ,
v == globals . GrantScopeChildren ,
v == globals . GrantScopeDescendants :
case globals . ResourceInfoFromPrefix ( v ). Type == resource . Scope :
if ! handlers . ValidId ( handlers . Id ( v ), globals . ProjectPrefix ) &&
! handlers . ValidId ( handlers . Id ( v ), globals . OrgPrefix ) {
badFields [ "grant_scope_ids" ] = fmt . Sprintf ( "Incorrectly formatted identifier %q." , v )
break
}
default :
badFields [ "grant_scope_ids" ] = fmt . Sprintf ( "Unknown value %q." , v )
break
}
}
if len ( badFields ) > 0 {
return handlers . InvalidArgumentErrorf ( "Errors in provided fields." , badFields )
}
return nil
}
// validateRoleGrantScopesHierarchy is the companion to the domain-side logic to
// validate scopes. It doesn't do all of the same checking but will allow for
// better error messages when possible. We perform this check after
// authentication to limit the possibility of an anonymous user causing DB load
// due to this lookup, which is not a cheap one.
func validateRoleGrantScopesHierarchy ( ctx context . Context , repo * iam . Repository , roleId string , grantScopes [] string ) error {
const op = "service.(Service).validateRoleGrantScopesHierarchy"
// We want to ensure that the values being passed in make sense to whatever
// extent we can right now, so we can provide nice errors back instead of DB
// errors.
role , _ , _ , _ , err := repo . LookupRole ( ctx , roleId )
if err != nil {
return errors . Wrap ( ctx , err , op )
}
switch {
case role . ScopeId == scope . Global . String ():
// Nothing, any grant scope is allowed for global
case strings . HasPrefix ( role . ScopeId , scope . Project . Prefix ()):
// In this case only "this" or the same project scope is allowed
for _ , grantScope := range grantScopes {
switch grantScope {
case globals . GrantScopeThis , role . ScopeId :
default :
return handlers . InvalidArgumentErrorf (
"Invalid grant scope." ,
map [ string ] string {
2024-06-17 12:09:33 -07:00
"grant_scope_ids" : `Project scopes can only have their own scope ID or "this" as a grant scope ID.` ,
2024-01-23 14:33:09 -05:00
})
}
}
case strings . HasPrefix ( role . ScopeId , scope . Org . Prefix ()):
// Orgs can have "this", its own scope, a project scope, or "children"
for _ , grantScope := range grantScopes {
switch {
case grantScope == role . ScopeId ,
grantScope == globals . GrantScopeThis ,
grantScope == globals . GrantScopeChildren ,
strings . HasPrefix ( grantScope , scope . Project . Prefix ()):
default :
return handlers . InvalidArgumentErrorf (
"Invalid grant scope." ,
map [ string ] string {
2024-06-17 12:09:33 -07:00
"grant_scope_ids" : fmt . Sprintf ( "Grant scope ID %q is not valid to set on an organization role." , grantScope ),
2024-01-23 14:33:09 -05:00
})
}
}
default :
// Should never happen
return handlers . InvalidArgumentErrorf (
"Improperly formatted identifier." ,
map [ string ] string {
2024-06-17 12:09:33 -07:00
"grant_scope_ids" : `Unknown scope prefix type.` ,
2024-01-23 14:33:09 -05:00
})
}
return nil
}
2023-12-15 11:49:59 -08:00
func newOutputOpts ( ctx context . Context , item * iam . Role , scopeInfoMap map [ string ] * scopes . ScopeInfo , authResults auth . VerifyResults ) ([] handlers . Option , bool ) {
res := perms . Resource {
Type : resource . Role ,
}
res . Id = item . GetPublicId ()
res . ScopeId = item . GetScopeId ()
2025-01-24 10:23:33 -08:00
res . ParentScopeId = scopeInfoMap [ item . GetScopeId ()]. GetParentScopeId ()
2023-12-15 11:49:59 -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
}