Multiple grant scope IDs (#4251)

Co-authored-by: Timothy Messier <tim.messier@gmail.com>
Co-authored-by: Todd <github@quaddi.com>
pull/4270/head
Jeff Mitchell 2 years ago committed by GitHub
parent 659df2797c
commit f5dd457b34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -15,3 +15,9 @@ issues:
- linters:
- staticcheck
text: "SA1019: pbs.AuthorizedWorkerList is deprecated:"
- linters:
- staticcheck
text: "SA1019: item.GetGrantScopeId is deprecated:"
- linters:
- staticcheck
text: "SA1019: out.GrantScopeId is deprecated:"

@ -15,9 +15,18 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
affects existing addresses (not just creation/updating via the API) so any
existing addresses containing a port will not be able to be used as part of a
target's session authorization call.
* The `grant_scope_id` field on roles is now deprecated in favor of the multiple
grant scope support.
### New and Improved
* Multiple grant scopes in roles: Roles now support multiple grant scopes, along
with the special values `this`, `children` (global/org only) to apply to all
direct children of a scope, and `descendants` (global only) to apply to all
descendants of a scope. These use the new actions `add-grant-scopes`,
`set-grant-scopes`, and `remove-grant-scopes` on roles. For now the
`grant_scope_id` field on roles will continue to be able to be set, which will
set a single grant scope, but this capability is now deprecated.
* Policies (Enterprise and HCP Boundary only): This release introduces Policies, a
Boundary resource that represents a Governance Policy to enforce. The first
implementation targets Storage Policies, which enables administrators to automate

@ -157,6 +157,7 @@ protobuild:
@protoc-go-inject-tag -input=./internal/iam/store/role.pb.go
@protoc-go-inject-tag -input=./internal/iam/store/principal_role.pb.go
@protoc-go-inject-tag -input=./internal/iam/store/role_grant.pb.go
@protoc-go-inject-tag -input=./internal/iam/store/role_grant_scope.pb.go
@protoc-go-inject-tag -input=./internal/iam/store/user.pb.go
@protoc-go-inject-tag -input=./internal/iam/store/scope.pb.go
@protoc-go-inject-tag -input=./internal/iam/store/group.pb.go

@ -27,6 +27,7 @@ type Role struct {
UpdatedTime time.Time `json:"updated_time,omitempty"`
Version uint32 `json:"version,omitempty"`
GrantScopeId string `json:"grant_scope_id,omitempty"`
GrantScopeIds []string `json:"grant_scope_ids,omitempty"`
PrincipalIds []string `json:"principal_ids,omitempty"`
Principals []*Principal `json:"principals,omitempty"`
GrantStrings []string `json:"grant_strings,omitempty"`
@ -448,6 +449,76 @@ func (c *Client) List(ctx context.Context, scopeId string, opt ...Option) (*Role
return target, nil
}
func (c *Client) AddGrantScopes(ctx context.Context, id string, version uint32, grantScopeIds []string, opt ...Option) (*RoleUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into AddGrantScopes request")
}
if len(grantScopeIds) == 0 {
return nil, errors.New("empty grantScopeIds passed into AddGrantScopes request")
}
if c.client == nil {
return nil, errors.New("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into AddGrantScopes request")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
opts.postMap["grant_scope_ids"] = grantScopeIds
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:add-grant-scopes", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating AddGrantScopes request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during AddGrantScopes call: %w", err)
}
target := new(RoleUpdateResult)
target.Item = new(Role)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding AddGrantScopes response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) AddGrants(ctx context.Context, id string, version uint32, grantStrings []string, opt ...Option) (*RoleUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into AddGrants request")
@ -588,6 +659,72 @@ func (c *Client) AddPrincipals(ctx context.Context, id string, version uint32, p
return target, nil
}
func (c *Client) SetGrantScopes(ctx context.Context, id string, version uint32, grantScopeIds []string, opt ...Option) (*RoleUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into SetGrantScopes request")
}
if c.client == nil {
return nil, errors.New("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into SetGrantScopes request")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
opts.postMap["grant_scope_ids"] = grantScopeIds
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:set-grant-scopes", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating SetGrantScopes request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during SetGrantScopes call: %w", err)
}
target := new(RoleUpdateResult)
target.Item = new(Role)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding SetGrantScopes response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) SetGrants(ctx context.Context, id string, version uint32, grantStrings []string, opt ...Option) (*RoleUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into SetGrants request")
@ -720,6 +857,76 @@ func (c *Client) SetPrincipals(ctx context.Context, id string, version uint32, p
return target, nil
}
func (c *Client) RemoveGrantScopes(ctx context.Context, id string, version uint32, grantScopeIds []string, opt ...Option) (*RoleUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into RemoveGrantScopes request")
}
if len(grantScopeIds) == 0 {
return nil, errors.New("empty grantScopeIds passed into RemoveGrantScopes request")
}
if c.client == nil {
return nil, errors.New("nil client")
}
opts, apiOpts := getOpts(opt...)
if version == 0 {
if !opts.withAutomaticVersioning {
return nil, errors.New("zero version number passed into RemoveGrantScopes request")
}
existingTarget, existingErr := c.Read(ctx, id, append([]Option{WithSkipCurlOutput(true)}, opt...)...)
if existingErr != nil {
if api.AsServerError(existingErr) != nil {
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
}
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
}
if existingTarget == nil {
return nil, errors.New("nil resource response found when performing initial check-and-set read")
}
if existingTarget.Item == nil {
return nil, errors.New("nil resource found when performing initial check-and-set read")
}
version = existingTarget.Item.Version
}
opts.postMap["version"] = version
opts.postMap["grant_scope_ids"] = grantScopeIds
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("roles/%s:remove-grant-scopes", url.PathEscape(id)), opts.postMap, apiOpts...)
if err != nil {
return nil, fmt.Errorf("error creating RemoveGrantScopes request: %w", err)
}
if len(opts.queryMap) > 0 {
q := url.Values{}
for k, v := range opts.queryMap {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("error performing client request during RemoveGrantScopes call: %w", err)
}
target := new(RoleUpdateResult)
target.Item = new(Role)
apiErr, err := resp.Decode(target.Item)
if err != nil {
return nil, fmt.Errorf("error decoding RemoveGrantScopes response: %w", err)
}
if apiErr != nil {
return nil, apiErr
}
target.response = resp
return target, nil
}
func (c *Client) RemoveGrants(ctx context.Context, id string, version uint32, grantStrings []string, opt ...Option) (*RoleUpdateResult, error) {
if id == "" {
return nil, fmt.Errorf("empty id value passed into RemoveGrants request")

@ -32,6 +32,7 @@ const (
PrincipalIdsField = "principal_ids"
PrincipalsField = "principals"
GrantScopeIdField = "grant_scope_id"
GrantScopeIdsField = "grant_scope_ids"
GrantsField = "grants"
GrantStringsField = "grant_strings"
PrimaryAuthMethodIdField = "primary_auth_method_id"

@ -14,6 +14,10 @@ const (
RecoveryUserId = "u_recovery"
MinimumSupportedPostgresVersion = "12"
GrantScopeThis = "this"
GrantScopeChildren = "children"
GrantScopeDescendants = "descendants"
)
type (

@ -316,6 +316,10 @@ var inputStructs = []*structInfo{
SliceType: "[]string",
VarName: "grantStrings",
},
"GrantScopes": {
SliceType: "[]string",
VarName: "grantScopeIds",
},
},
pluralResourceName: "roles",
versionEnabled: true,

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/api/authtokens"
"github.com/hashicorp/boundary/api/roles"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
@ -48,6 +49,26 @@ func TestFetchActionSetForId(t *testing.T) {
return tc.AuthTokenRepo(), nil
}
// Delete the global default role so it doesn't interfere with the
// permissions we're testing here
rolesClient := roles.NewClient(tc.Client())
rolesResp, err := rolesClient.List(tc.Context(), scope.Global.String())
require.NoError(t, err)
require.NotNil(t, rolesResp)
assert.Len(t, rolesResp.GetItems(), 3)
var adminRoleId string
for _, item := range rolesResp.GetItems() {
if strings.Contains(item.Name, "Authenticated User") ||
strings.Contains(item.Name, "Login") {
_, err := rolesClient.Delete(tc.Context(), item.Id)
require.NoError(t, err)
} else {
adminRoleId = item.Id
}
}
_, err = rolesClient.Delete(tc.Context(), adminRoleId)
require.NoError(t, err)
orgRole := iam.TestRole(t, conn, org.GetPublicId())
iam.TestUserRole(t, conn, orgRole.PublicId, token.UserId)
iam.TestRoleGrant(t, conn, orgRole.PublicId, "ids=ttcp_foo;actions=read,update")
@ -132,6 +153,21 @@ func TestRecursiveListingDifferentOutputFields(t *testing.T) {
token := tc.Token()
client.SetToken(token.Token)
// Delete the global default role so it doesn't interfere with the
// permissions we're testing here
rolesClient := roles.NewClient(tc.Client())
rolesResp, err := rolesClient.List(tc.Context(), scope.Global.String())
require.NoError(err)
require.NotNil(rolesResp)
assert.Len(rolesResp.GetItems(), 3)
for _, item := range rolesResp.GetItems() {
if strings.Contains(item.Name, "Authenticated User") ||
strings.Contains(item.Name, "Login") {
_, err := rolesClient.Delete(tc.Context(), item.Id)
require.NoError(err)
}
}
// Set some global permissions so we can read the auth method there. Here we
// will expect the defaults.
globalRole := iam.TestRole(t, conn, scope.Global.String())
@ -146,7 +182,7 @@ func TestRecursiveListingDifferentOutputFields(t *testing.T) {
orgRole := iam.TestRole(t, conn, org.GetPublicId())
iam.TestUserRole(t, conn, orgRole.PublicId, token.UserId)
// The first and second will actually not take effect for output
// grantsbecause it's a list and output fields are scoped by action. So we
// grants because it's a list and output fields are scoped by action. So we
// expect only name and scope_id for the auth methods in the scope, using
// two patterns below. However, since you need an action on the resource for
// list to return anything, those grants allow us to list the items, while

@ -114,11 +114,19 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
return err
}
if _, err := b.CreateInitialLoginRole(ctx); err != nil {
if c != nil {
err = errors.Join(err, c())
if !opts.withSkipDefaultRoleCreation {
if _, err := b.CreateInitialLoginRole(ctx); err != nil {
if c != nil {
err = errors.Join(err, c())
}
return err
}
if _, err := b.CreateInitialAuthenticatedUserRole(ctx, WithAuthUserTargetAuthorizeSessionGrant(true)); err != nil {
if c != nil {
err = errors.Join(err, c())
}
return err
}
return err
}
if opts.withSkipAuthMethodCreation {
@ -151,7 +159,10 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
return nil
}
if _, _, err := b.CreateInitialScopes(ctx); err != nil {
if _, _, err := b.CreateInitialScopes(ctx, WithIamOptions(
iam.WithSkipAdminRoleCreation(true),
iam.WithSkipDefaultRoleCreation(true),
)); err != nil {
return err
}

@ -45,26 +45,84 @@ func (b *Server) CreateInitialLoginRole(ctx context.Context) (*iam.Role, error)
pr, err := iam.NewRole(ctx,
scope.Global.String(),
iam.WithName("Login and Default Grants"),
iam.WithDescription(`Role created for login capability, account self-management, and other default grants for users of the global scope at its creation time`),
iam.WithName("Login Grants"),
iam.WithDescription(`Role created for login capability for unauthenticated users`),
)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
}
role, err := iamRepo.CreateRole(ctx, pr)
role, _, _, _, err := iamRepo.CreateRole(ctx, pr)
if err != nil {
return nil, fmt.Errorf("error creating role for default generated grants: %w", err)
}
if _, err := iamRepo.AddRoleGrants(ctx, role.PublicId, role.Version, []string{
"ids=*;type=scope;actions=list,no-op",
"ids=*;type=auth-method;actions=authenticate,list",
"ids={{.Account.Id}};actions=read,change-password",
"ids=*;type=auth-token;actions=list,read:self,delete:self",
"ids=*;type=auth-method;actions=list,authenticate",
"ids=*;type=auth-token;actions=read:self,delete:self",
}); err != nil {
return nil, fmt.Errorf("error creating grant for default generated grants: %w", err)
return nil, fmt.Errorf("error creating grant for initial login grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(ctx, role.PublicId, role.Version+1, []string{globals.AnonymousUserId}); err != nil {
return nil, fmt.Errorf("error adding principal to role for initial login grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(ctx, role.PublicId, role.Version+1, []string{globals.AnonymousUserId}, nil); err != nil {
return nil, fmt.Errorf("error adding principal to role for default generated grants: %w", err)
if _, _, err := iamRepo.SetRoleGrantScopes(ctx, role.PublicId, role.Version+2, []string{"this", "descendants"}); err != nil {
return nil, fmt.Errorf("error adding scope grants to role for initial login grants: %w", err)
}
return role, nil
}
func (b *Server) CreateInitialAuthenticatedUserRole(ctx context.Context, opt ...Option) (*iam.Role, error) {
rw := db.New(b.Database)
kmsCache, err := kms.New(ctx, rw, rw)
if err != nil {
return nil, fmt.Errorf("error creating kms cache: %w", err)
}
if err := kmsCache.AddExternalWrappers(
b.Context,
kms.WithRootWrapper(b.RootKms),
); err != nil {
return nil, fmt.Errorf("error adding config keys to kms: %w", err)
}
iamRepo, err := iam.NewRepository(ctx, rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("unable to create repo for initial login role: %w", err)
}
pr, err := iam.NewRole(ctx,
scope.Global.String(),
iam.WithName("Authenticated User Grants"),
iam.WithDescription(`Role created for account self-management, and other initial authenticated user grants`),
)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
}
role, _, _, _, err := iamRepo.CreateRole(ctx, pr)
if err != nil {
return nil, fmt.Errorf("error creating role for default generated grants: %w", err)
}
grants := []string{
"ids=*;type=scope;actions=read",
"ids=*;type=auth-token;actions=list",
"ids={{.Account.Id}};actions=read,change-password",
"ids=*;type=session;actions=list,read:self,cancel:self",
}
opts := GetOpts(opt...)
if opts.withAuthUserTargetAuthorizeSessionGrant {
grants = append(grants, "ids=*;type=target;actions=list,read,authorize-session")
} else {
grants = append(grants, "ids=*;type=target;actions=list,read")
}
if _, err := iamRepo.AddRoleGrants(ctx, role.PublicId, role.Version, grants); err != nil {
return nil, fmt.Errorf("error creating grant for initial authenticated user grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(ctx, role.PublicId, role.Version+1, []string{globals.AnyAuthenticatedUserId}); err != nil {
return nil, fmt.Errorf("error adding principal to role for initial authenticated user grants: %w", err)
}
if _, _, err := iamRepo.SetRoleGrantScopes(ctx, role.PublicId, role.Version+2, []string{"this", "descendants"}); err != nil {
return nil, fmt.Errorf("error adding scope grants to role for initial authenticated user grants: %w", err)
}
return role, nil
@ -201,21 +259,24 @@ func (b *Server) CreateInitialPasswordAuthMethod(ctx context.Context) (*password
pr, err := iam.NewRole(ctx,
scope.Global.String(),
iam.WithName("Administration"),
iam.WithDescription(fmt.Sprintf(`Provides admin grants within the "%s" scope to the initial user`, scope.Global.String())),
iam.WithDescription("Provides admin grants within all scopes to the initial admin user"),
)
if err != nil {
return nil, fmt.Errorf("error creating in memory role for generated grants: %w", err)
}
defPermsRole, err := iamRepo.CreateRole(ctx, pr)
adminRole, _, _, _, err := iamRepo.CreateRole(ctx, pr)
if err != nil {
return nil, fmt.Errorf("error creating role for default generated grants: %w", err)
}
if _, err := iamRepo.AddRoleGrants(ctx, defPermsRole.PublicId, defPermsRole.Version, []string{"ids=*;type=*;actions=*"}); err != nil {
if _, err := iamRepo.AddRoleGrants(ctx, adminRole.PublicId, adminRole.Version, []string{"ids=*;type=*;actions=*"}); err != nil {
return nil, fmt.Errorf("error creating grant for default generated grants: %w", err)
}
if _, err := iamRepo.AddPrincipalRoles(ctx, defPermsRole.PublicId, defPermsRole.Version+1, []string{u.GetPublicId()}, nil); err != nil {
if _, err := iamRepo.AddPrincipalRoles(ctx, adminRole.PublicId, adminRole.Version+1, []string{u.GetPublicId()}, nil); err != nil {
return nil, fmt.Errorf("error adding principal to role for default generated grants: %w", err)
}
if _, _, err := iamRepo.SetRoleGrantScopes(ctx, adminRole.PublicId, adminRole.Version+2, []string{"this", "descendants"}); err != nil {
return nil, fmt.Errorf("error adding scope grants to role for default generated grants: %w", err)
}
return u, nil
}
@ -256,7 +317,7 @@ func (b *Server) CreateInitialPasswordAuthMethod(ctx context.Context) (*password
return am, u, nil
}
func (b *Server) CreateInitialScopes(ctx context.Context) (*iam.Scope, *iam.Scope, error) {
func (b *Server) CreateInitialScopes(ctx context.Context, opt ...Option) (*iam.Scope, *iam.Scope, error) {
rw := db.New(b.Database)
kmsCache, err := kms.New(ctx, rw, rw)
@ -270,6 +331,8 @@ func (b *Server) CreateInitialScopes(ctx context.Context) (*iam.Scope, *iam.Scop
return nil, nil, fmt.Errorf("error adding config keys to kms: %w", err)
}
opts := GetOpts(opt...)
iamRepo, err := iam.NewRepository(ctx, rw, rw, kmsCache)
if err != nil {
return nil, nil, fmt.Errorf("error creating scopes repository: %w", err)
@ -282,17 +345,20 @@ func (b *Server) CreateInitialScopes(ctx context.Context) (*iam.Scope, *iam.Scop
return nil, nil, fmt.Errorf("error generating initial org id: %w", err)
}
}
opts := []iam.Option{
iamOpts := []iam.Option{
iam.WithName("Generated org scope"),
iam.WithDescription("Provides an initial org scope in Boundary"),
iam.WithRandomReader(b.SecureRandomReader),
iam.WithPublicId(b.DevOrgId),
}
orgScope, err := iam.NewOrg(ctx, opts...)
if len(opts.withIamOptions) > 0 {
iamOpts = append(iamOpts, opts.withIamOptions...)
}
orgScope, err := iam.NewOrg(ctx, iamOpts...)
if err != nil {
return nil, nil, fmt.Errorf("error creating new in memory org scope: %w", err)
}
orgScope, err = iamRepo.CreateScope(ctx, orgScope, b.DevUserId, opts...)
orgScope, err = iamRepo.CreateScope(ctx, orgScope, b.DevUserId, iamOpts...)
if err != nil {
return nil, nil, fmt.Errorf("error saving org scope to the db: %w", err)
}
@ -305,17 +371,20 @@ func (b *Server) CreateInitialScopes(ctx context.Context) (*iam.Scope, *iam.Scop
return nil, nil, fmt.Errorf("error generating initial project id: %w", err)
}
}
opts = []iam.Option{
iamOpts = []iam.Option{
iam.WithName("Generated project scope"),
iam.WithDescription("Provides an initial project scope in Boundary"),
iam.WithRandomReader(b.SecureRandomReader),
iam.WithPublicId(b.DevProjectId),
}
projScope, err := iam.NewProject(ctx, b.DevOrgId, opts...)
if len(opts.withIamOptions) > 0 {
iamOpts = append(iamOpts, opts.withIamOptions...)
}
projScope, err := iam.NewProject(ctx, b.DevOrgId, iamOpts...)
if err != nil {
return nil, nil, fmt.Errorf("error creating new in memory project scope: %w", err)
}
projScope, err = iamRepo.CreateScope(ctx, projScope, b.DevUserId, opts...)
projScope, err = iamRepo.CreateScope(ctx, projScope, b.DevUserId, iamOpts...)
if err != nil {
return nil, nil, fmt.Errorf("error saving project scope to the db: %w", err)
}
@ -472,17 +541,6 @@ func (b *Server) CreateInitialTargetWithAddress(ctx context.Context) (target.Tar
b.InfoKeys = append(b.InfoKeys, "generated target with address id")
b.Info["generated target with address id"] = b.DevTargetId
if b.DevUnprivilegedUserId != "" {
iamRepo, err := iam.NewRepository(ctx, rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("failed to create iam repository: %w", err)
}
err = unprivilegedDevUserRoleSetup(ctx, iamRepo, b.DevUnprivilegedUserId, b.DevProjectId, b.DevTargetId)
if err != nil {
return nil, fmt.Errorf("failed to set up unprivileged dev user: %w", err)
}
}
return tt, nil
}
@ -540,17 +598,6 @@ func (b *Server) CreateInitialTargetWithHostSources(ctx context.Context) (target
b.InfoKeys = append(b.InfoKeys, "generated target with host source id")
b.Info["generated target with host source id"] = b.DevSecondaryTargetId
if b.DevUnprivilegedUserId != "" {
iamRepo, err := iam.NewRepository(ctx, rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
if err != nil {
return nil, fmt.Errorf("failed to create iam repository: %w", err)
}
err = unprivilegedDevUserRoleSetup(ctx, iamRepo, b.DevUnprivilegedUserId, b.DevProjectId, b.DevSecondaryTargetId)
if err != nil {
return nil, fmt.Errorf("failed to set up unprivileged dev user: %w", err)
}
}
return tt, nil
}
@ -626,83 +673,3 @@ func (b *Server) RegisterPlugin(ctx context.Context, name string, hostClient plg
return plg, nil
}
// unprivilegedDevUserRoleSetup adds dev user to the role that grants
// list/read:self/cancel:self on sessions and read:self/delete:self/list on
// tokens. It also creates a role with an `authorize-session` grant for the
// provided targetId.
func unprivilegedDevUserRoleSetup(ctx context.Context, repo *iam.Repository, userId, projectId, targetId string) error {
noopFilter := func(ctx context.Context, item *iam.Role) (bool, error) {
return true, nil
}
roles, err := iam.ListRoles(ctx, []byte("dummy grant"), globals.DefaultMaxPageSize, noopFilter, repo, []string{projectId})
if err != nil {
return fmt.Errorf("failed to list existing roles for project id %q: %w", projectId, err)
}
// Look for default grants role to set unprivileged user as a principal.
defaultRoleIdx := -1
for i, r := range roles.Items {
// Hacky, I know, but saves a DB trip to look up other
// characteristics like "if any principals are currently attached".
// No matter what we pick here it's a bit heuristical.
if r.Name == "Default Grants" {
defaultRoleIdx = i
break
}
}
if defaultRoleIdx == -1 {
return fmt.Errorf("couldn't find default grants role for project id %q", projectId)
}
defaultRole := roles.Items[defaultRoleIdx]
// This function may be called more than once for the same boundary
// deployment (eg: if we're creating more than one target), so we need to
// check if the unprivileged user is not already a principal for the default
// role in this project, as attempting to add an existing principal is an
// error.
principals, err := repo.ListPrincipalRoles(ctx, defaultRole.GetPublicId())
if err != nil {
return fmt.Errorf("failed to list principals for default project role: %w", err)
}
found := false
for _, p := range principals {
if p.PrincipalId == userId {
found = true
}
}
if !found {
_, err = repo.AddPrincipalRoles(ctx, defaultRole.GetPublicId(), defaultRole.GetVersion(), []string{userId})
if err != nil {
return fmt.Errorf("failed to add %q as principal for role id %q", userId, defaultRole.GetPublicId())
}
defaultRole.Version++ // The above call increments the role version in the database, so we must also track that with our state.
}
// Create a new role for the "authorize-session" grant and add the
// unprivileged user as a principal.
asRole, err := iam.NewRole(ctx,
projectId,
iam.WithName(fmt.Sprintf("Session authorization for %s", targetId)),
iam.WithDescription(fmt.Sprintf("Provides grants within the dev project scope to allow the initial unprivileged user to authorize sessions against %s", targetId)),
)
if err != nil {
return fmt.Errorf("failed to create role object: %w", err)
}
asRole, err = repo.CreateRole(ctx, asRole)
if err != nil {
return fmt.Errorf("failed to create role for unprivileged user: %w", err)
}
if _, err := repo.AddPrincipalRoles(ctx, asRole.GetPublicId(), asRole.GetVersion(), []string{userId}, nil); err != nil {
return fmt.Errorf("failed to add unprivileged user as principal to new role: %w", err)
}
asRole.Version++
_, err = repo.AddRoleGrants(ctx, asRole.GetPublicId(), asRole.GetVersion(), []string{fmt.Sprintf("ids=%s;actions=authorize-session", targetId)})
if err != nil {
return fmt.Errorf("failed to add authorize-session grant for unprivileged user: %w", err)
}
return nil
}

@ -5,6 +5,7 @@ package base
import (
"github.com/hashicorp/boundary/internal/event"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/sdk/pbs/plugin"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
)
@ -25,28 +26,31 @@ type Option func(*Options)
// Options - how Options are represented.
type Options struct {
withNoTokenScope bool
withNoTokenValue bool
withSkipDatabaseDestruction bool
withSkipAuthMethodCreation bool
withSkipOidcAuthMethodCreation bool
withSkipLdapAuthMethodCreation bool
withSkipScopesCreation bool
withSkipHostResourcesCreation bool
withSkipTargetCreation bool
withContainerImage string
withDialect string
withDatabaseTemplate string
withEventerConfig *event.EventerConfig
withEventFlags *EventFlags
withEventWrapper wrapping.Wrapper
withAttributeFieldPrefix string
withStatusCode int
withHostPlugin func() (string, plugin.HostPluginServiceClient)
withEventGating bool
withImplicitId string
WithSkipScopeIdFlag bool
WithInterceptedToken *string
withNoTokenScope bool
withNoTokenValue bool
withSkipDefaultRoleCreation bool
withSkipDatabaseDestruction bool
withSkipAuthMethodCreation bool
withSkipOidcAuthMethodCreation bool
withSkipLdapAuthMethodCreation bool
withSkipScopesCreation bool
withSkipHostResourcesCreation bool
withSkipTargetCreation bool
withContainerImage string
withDialect string
withDatabaseTemplate string
withEventerConfig *event.EventerConfig
withEventFlags *EventFlags
withEventWrapper wrapping.Wrapper
withAttributeFieldPrefix string
withStatusCode int
withHostPlugin func() (string, plugin.HostPluginServiceClient)
withEventGating bool
withImplicitId string
WithSkipScopeIdFlag bool
WithInterceptedToken *string
withAuthUserTargetAuthorizeSessionGrant bool
withIamOptions []iam.Option
}
func getDefaultOptions() Options {
@ -81,6 +85,14 @@ func WithNoTokenValue() Option {
}
}
// WithSkipDefaultRoleCreation tells the command not to instantiate the default
// global role
func WithSkipDefaultRoleCreation() Option {
return func(o *Options) {
o.withSkipDefaultRoleCreation = true
}
}
// WithSkipAuthMethodCreation tells the command not to instantiate any auth
// method on first run.
func WithSkipAuthMethodCreation() Option {
@ -227,3 +239,20 @@ func WithInterceptedToken(s *string) Option {
o.WithInterceptedToken = s
}
}
// WithAuthUserTargetAuthorizeSessionGrant indicates that we should add an
// authorize-session grant to the global authenticated user role. This is the
// default for dev mode.
func WithAuthUserTargetAuthorizeSessionGrant(with bool) Option {
return func(o *Options) {
o.withAuthUserTargetAuthorizeSessionGrant = with
}
}
// WithIamOptions provides options to pass through to the IAM package when
// creating initial resources
func WithIamOptions(with ...iam.Option) Option {
return func(o *Options) {
o.withIamOptions = with
}
}

@ -7,6 +7,7 @@ import (
"testing"
"github.com/hashicorp/boundary/internal/event"
"github.com/hashicorp/boundary/internal/iam"
"github.com/stretchr/testify/assert"
)
@ -71,6 +72,13 @@ func Test_GetOpts(t *testing.T) {
testOpts.withNoTokenValue = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipDefaultRoleCreation", func(t *testing.T) {
assert := assert.New(t)
opts := GetOpts(WithSkipDefaultRoleCreation())
testOpts := getDefaultOptions()
testOpts.withSkipDefaultRoleCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipAuthMethodCreation", func(t *testing.T) {
assert := assert.New(t)
opts := GetOpts(WithSkipAuthMethodCreation())
@ -151,4 +159,20 @@ func Test_GetOpts(t *testing.T) {
testOpts.WithInterceptedToken = &s
assert.Equal(opts, testOpts)
})
t.Run("WithAuthUserTargetAuthorizeSessionGrant", func(t *testing.T) {
assert := assert.New(t)
opts := getDefaultOptions()
assert.False(opts.withAuthUserTargetAuthorizeSessionGrant)
opts = GetOpts(WithAuthUserTargetAuthorizeSessionGrant(true))
assert.True(opts.withAuthUserTargetAuthorizeSessionGrant)
})
t.Run("WithIamOptions", func(t *testing.T) {
assert := assert.New(t)
testOpts := getDefaultOptions()
assert.Len(testOpts.withIamOptions, 0)
opts := GetOpts(WithIamOptions(iam.WithSkipAdminRoleCreation(true)))
assert.Len(opts.withIamOptions, 1)
})
}

@ -895,6 +895,21 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Command: base.NewCommand(ui, opts...),
Func: "remove-grants",
}),
"roles add-grant-scopes": clientCacheWrapper(
&rolescmd.Command{
Command: base.NewCommand(ui, opts...),
Func: "add-grant-scopes",
}),
"roles set-grant-scopes": clientCacheWrapper(
&rolescmd.Command{
Command: base.NewCommand(ui, opts...),
Func: "set-grant-scopes",
}),
"roles remove-grant-scopes": clientCacheWrapper(
&rolescmd.Command{
Command: base.NewCommand(ui, opts...),
Func: "remove-grant-scopes",
}),
"scopes": func() (cli.Command, error) {
return &scopescmd.Command{

@ -71,6 +71,13 @@ func (c *Command) Flags() *base.FlagSets {
}
func (c *Command) Run(args []string) int {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
client, err := c.Client(base.WithNoTokenScope(), base.WithNoTokenValue())
if c.WrapperCleanupFunc != nil {
defer func() {
@ -84,13 +91,6 @@ func (c *Command) Run(args []string) int {
return base.CommandCliError
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
// Lookup the primary auth method ID in the global scope
aClient := authmethods.NewClient(client)
pri, err := getPrimaryAuthMethodId(c.Context, aClient, scope.Global.String(), "")

@ -107,7 +107,7 @@ type RoleInfo struct {
Name string `json:"name"`
}
func generateInitialRoleTableOutput(in *RoleInfo) string {
func generateInitialLoginRoleTableOutput(in *RoleInfo) string {
nonAttributeMap := map[string]any{
"Role ID": in.RoleId,
"Name": in.Name,
@ -129,6 +129,28 @@ func generateInitialRoleTableOutput(in *RoleInfo) string {
return base.WrapForHelpText(ret)
}
func generateInitialAuthenticatedUserRoleOutput(in *RoleInfo) string {
nonAttributeMap := map[string]any{
"Role ID": in.RoleId,
"Name": in.Name,
}
maxLength := 0
for k := range nonAttributeMap {
if len(k) > maxLength {
maxLength = len(k)
}
}
ret := []string{
"",
"Initial authenticated user role information:",
base.WrapMap(2, maxLength+2, nonAttributeMap),
}
return base.WrapForHelpText(ret)
}
type AuthInfo struct {
AuthMethodId string `json:"auth_method_id"`
AuthMethodName string `json:"auth_method_name"`

@ -37,16 +37,17 @@ type InitCommand struct {
// deferred function on the Run method.
configWrapperCleanupFunc func() error
flagConfig []string
flagConfigKms string
flagLogLevel string
flagLogFormat string
flagMigrationUrl string
flagSkipInitialLoginRoleCreation bool
flagSkipAuthMethodCreation bool
flagSkipScopesCreation bool
flagSkipHostResourcesCreation bool
flagSkipTargetCreation bool
flagConfig []string
flagConfigKms string
flagLogLevel string
flagLogFormat string
flagMigrationUrl string
flagSkipInitialLoginRoleCreation bool
flagSkipInitialAuthenticatedUserRoleCreation bool
flagSkipAuthMethodCreation bool
flagSkipScopesCreation bool
flagSkipHostResourcesCreation bool
flagSkipTargetCreation bool
}
func (c *InitCommand) Synopsis() string {
@ -124,31 +125,37 @@ func (c *InitCommand) Flags() *base.FlagSets {
f.BoolVar(&base.BoolVar{
Name: "skip-initial-login-role-creation",
Target: &c.flagSkipInitialLoginRoleCreation,
Usage: "If not set, a default role allowing necessary grants for logging in will not be created as part of initialization. If set, the recovery KMS will be needed to perform any actions.",
Usage: "If set, a role providing necessary grants for logging in will not be created as part of initialization. If set, the recovery KMS will be needed to perform any actions.",
})
f.BoolVar(&base.BoolVar{
Name: "skip-initial-authenticated-user-role-creation",
Target: &c.flagSkipInitialAuthenticatedUserRoleCreation,
Usage: "If set, a role providing initial grants for any authenticated user will not be created as part of initialization.",
})
f.BoolVar(&base.BoolVar{
Name: "skip-auth-method-creation",
Target: &c.flagSkipAuthMethodCreation,
Usage: "If not set, an auth method will not be created as part of initialization. If set, the recovery KMS will be needed to perform any actions.",
Usage: "If set, an auth method will not be created as part of initialization. If set, the recovery KMS will be needed to perform any actions.",
})
f.BoolVar(&base.BoolVar{
Name: "skip-scopes-creation",
Target: &c.flagSkipScopesCreation,
Usage: "If not set, scopes will not be created as part of initialization.",
Usage: "If set, scopes will not be created as part of initialization.",
})
f.BoolVar(&base.BoolVar{
Name: "skip-host-resources-creation",
Target: &c.flagSkipHostResourcesCreation,
Usage: "If not set, host resources (host catalog, host set, host) will not be created as part of initialization.",
Usage: "If set, host resources (host catalog, host set, host) will not be created as part of initialization.",
})
f.BoolVar(&base.BoolVar{
Name: "skip-target-creation",
Target: &c.flagSkipTargetCreation,
Usage: "If not set, a target will not be created as part of initialization.",
Usage: "If set, a target will not be created as part of initialization.",
})
f.StringVar(&base.StringVar{
@ -330,11 +337,32 @@ func (c *InitCommand) Run(args []string) (retCode int) {
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(generateInitialRoleTableOutput(roleInfo))
c.UI.Output(generateInitialLoginRoleTableOutput(roleInfo))
case "json":
jsonMap["login_role"] = roleInfo
}
if c.flagSkipInitialAuthenticatedUserRoleCreation {
return base.CommandSuccess
}
role, err = c.CreateInitialAuthenticatedUserRole(c.Context)
if err != nil {
c.UI.Error(fmt.Errorf("Error creating initial global-scoped authenticated user role: %w", err).Error())
return base.CommandCliError
}
roleInfo = &RoleInfo{
RoleId: role.PublicId,
Name: role.Name,
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(generateInitialAuthenticatedUserRoleOutput(roleInfo))
case "json":
jsonMap["authenticated_user_role"] = roleInfo
}
if c.flagSkipAuthMethodCreation {
return base.CommandSuccess
}

@ -79,7 +79,7 @@ type Command struct {
flagTargetDefaultPort int
flagTargetSessionMaxSeconds int
flagTargetSessionConnectionLimit int
flagControllerAPIListenAddr string
flagControllerApiListenAddr string
flagControllerClusterListenAddr string
flagControllerPublicClusterAddr string
flagControllerOnly bool
@ -102,6 +102,8 @@ type Command struct {
flagCreateLoopbackPlugin bool
flagPluginExecutionDir string
flagSkipPlugins bool
flagSkipOidcAuthMethodCreation bool
flagSkipLdapAuthMethodCreation bool
flagWorkerDnsServer string
flagWorkerAuthMethod string
flagWorkerAuthStorageDir string
@ -202,7 +204,7 @@ func (c *Command) Flags() *base.FlagSets {
f.StringVar(&base.StringVar{
Name: "api-listen-address",
Target: &c.flagControllerAPIListenAddr,
Target: &c.flagControllerApiListenAddr,
EnvVar: "BOUNDARY_DEV_CONTROLLER_API_LISTEN_ADDRESS",
Usage: "Address to bind to for controller \"api\" purpose. If this begins with a forward slash, it will be assumed to be a Unix domain socket path.",
})
@ -381,6 +383,16 @@ func (c *Command) Flags() *base.FlagSets {
Usage: "Skip loading compiled-in plugins. This does not prevent loopback plugins from being loaded if enabled.",
Hidden: true,
})
f.BoolVar(&base.BoolVar{
Name: "skip-oidc-auth-method-creation",
Target: &c.flagSkipOidcAuthMethodCreation,
Usage: "Skip creating a test OIDC auth method. This is useful if e.g. running a Unix API listener.",
})
f.BoolVar(&base.BoolVar{
Name: "skip-ldap-auth-method-creation",
Target: &c.flagSkipLdapAuthMethodCreation,
Usage: "Skip creating a test LDAP auth method. This is useful if e.g. running a Unix API listener.",
})
f.StringVar(&base.StringVar{
Name: "worker-dns-server",
Target: &c.flagWorkerDnsServer,
@ -594,8 +606,8 @@ func (c *Command) Run(args []string) int {
}
switch l.Purpose[0] {
case "api":
if c.flagControllerAPIListenAddr != "" {
l.Address = c.flagControllerAPIListenAddr
if c.flagControllerApiListenAddr != "" {
l.Address = c.flagControllerApiListenAddr
}
if strings.HasPrefix(l.Address, "/") {
l.Type = "unix"
@ -750,6 +762,12 @@ func (c *Command) Run(args []string) int {
}
var opts []base.Option
if c.flagSkipOidcAuthMethodCreation {
opts = append(opts, base.WithSkipOidcAuthMethodCreation())
}
if c.flagSkipLdapAuthMethodCreation {
opts = append(opts, base.WithSkipLdapAuthMethodCreation())
}
if c.flagCreateLoopbackPlugin {
c.DevLoopbackPluginId = "pl_1234567890"
c.EnabledPlugins = append(c.EnabledPlugins, base.EnabledPluginLoopback)

@ -26,41 +26,42 @@ func init() {
}
type extraCmdVars struct {
flagGrantScopeId string
flagPrincipals []string
flagGrants []string
flagGrantScopeIds []string
flagPrincipals []string
flagGrants []string
}
func extraActionsFlagsMapFuncImpl() map[string][]string {
return map[string][]string{
"create": {"grant-scope-id"},
"update": {"grant-scope-id"},
"add-principals": {"id", "principal", "version"},
"set-principals": {"id", "principal", "version"},
"remove-principals": {"id", "principal", "version"},
"add-grants": {"id", "grant", "version"},
"set-grants": {"id", "grant", "version"},
"remove-grants": {"id", "grant", "version"},
"create": {"grant-scope-id"},
"update": {"grant-scope-id"},
"add-principals": {"id", "principal", "version"},
"set-principals": {"id", "principal", "version"},
"remove-principals": {"id", "principal", "version"},
"add-grants": {"id", "grant", "version"},
"set-grants": {"id", "grant", "version"},
"remove-grants": {"id", "grant", "version"},
"add-grant-scopes": {"id", "grant-scope-id", "version"},
"set-grant-scopes": {"id", "grant-scope-id", "version"},
"remove-grant-scopes": {"id", "grant-scope-id", "version"},
}
}
func extraSynopsisFuncImpl(c *Command) string {
switch c.Func {
case "add-principals", "set-principals", "remove-principals":
return c.principalsGrantsSynopsisFunc(c.Func, true)
return c.principalsGrantsSynopsisFunc(c.Func, "principals (users, groups)")
case "add-grants", "set-grants", "remove-grants":
return c.principalsGrantsSynopsisFunc(c.Func, false)
return c.principalsGrantsSynopsisFunc(c.Func, "grants")
case "add-grant-scopes":
return c.principalsGrantsSynopsisFunc(c.Func, "grant scopes")
}
return ""
}
func (c *Command) principalsGrantsSynopsisFunc(inFunc string, principals bool) string {
func (c *Command) principalsGrantsSynopsisFunc(inFunc, switchStr string) string {
var in string
switchStr := "principals (users, groups)"
if !principals {
switchStr = "grants"
}
switch {
case strings.HasPrefix(inFunc, "add"):
in = fmt.Sprintf("Add %s to", switchStr)
@ -141,6 +142,39 @@ func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
"",
})
case "add-grant-scopes":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary roles add-grant-scopes [options] [args]",
"",
` Adds grant scopes to a role given its ID. The "grant-scope-id" flag can be specified multiple times. Example:`,
"",
` $ boundary roles add-grant-scopes -id o_1234567890 -grant-scope-id "this" -grant-scope-id "children"`,
"",
"",
})
case "set-grant-scopes":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary roles set-grant-scopes [options] [args]",
"",
` Sets the complete set of grant scopes on a role given its ID. The "grant-scope-id" flag can be specified multiple times. Example:`,
"",
` $ boundary roles set-grant-scopes -id o_1234567890 -grant-scope-id "this" -grant-scope-id "children"`,
"",
"",
})
case "remove-grant-scopes":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary roles remove-grant-scopes [options] [args]",
"",
` Removes grant scopes from a role given its ID. The "grant-scope-id" flags can be specified multiple times. Example:`,
"",
` $ boundary roles remove-grant-scopes -id o_1234567890 -grant-scope-id "this" -grant-scope-id "children"`,
"",
"",
})
default:
helpStr = helpMap["base"]()
}
@ -151,10 +185,10 @@ func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) {
for _, name := range flagsMap[c.Func] {
switch name {
case "grant-scope-id":
f.StringVar(&base.StringVar{
f.StringSliceVar(&base.StringSliceVar{
Name: "grant-scope-id",
Target: &c.flagGrantScopeId,
Usage: "The scope ID for grants set on the role",
Target: &c.flagGrantScopeIds,
Usage: "The scope IDs to inherit grants set on the role",
})
case "principal":
f.StringSliceVar(&base.StringSliceVar{
@ -173,15 +207,21 @@ func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) {
}
func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]roles.Option) bool {
switch c.flagGrantScopeId {
case "":
case "null":
*opts = append(*opts, roles.DefaultGrantScopeId())
default:
*opts = append(*opts, roles.WithGrantScopeId(c.flagGrantScopeId))
}
switch c.Func {
case "create", "update":
switch len(c.flagGrantScopeIds) {
case 0:
case 1:
if c.flagGrantScopeIds[0] == "null" {
*opts = append(*opts, roles.DefaultGrantScopeId())
} else {
*opts = append(*opts, roles.WithGrantScopeId(c.flagGrantScopeIds[0]))
}
default:
c.UI.Error("-grant-scope-id cannot be specified multiple times when using the deprecated create/update grant_scope_id mechanism")
return false
}
case "add-principals", "remove-principals":
if len(c.flagPrincipals) == 0 {
c.UI.Error("No principals supplied via -principal")
@ -194,6 +234,12 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]roles.Opti
return false
}
case "add-grant-scopes", "remove-grant-scopes":
if len(c.flagGrantScopeIds) == 0 {
c.UI.Error("No grant scope IDs supplied via -grant-scope-id")
return false
}
case "set-principals":
switch len(c.flagPrincipals) {
case 0:
@ -215,6 +261,17 @@ func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]roles.Opti
c.flagGrants = nil
}
}
case "set-grant-scopes":
switch len(c.flagGrantScopeIds) {
case 0:
c.UI.Error("No grant scope IDs supplied via -grant-scope-id")
return false
case 1:
if c.flagGrantScopeIds[0] == "null" {
c.flagGrantScopeIds = nil
}
}
}
if len(c.flagGrants) > 0 {
@ -277,6 +334,24 @@ func executeExtraActionsImpl(c *Command, origResp *api.Response, origItem *roles
return nil, nil, nil, err
}
return result.GetResponse(), result.GetItem(), nil, err
case "add-grant-scopes":
result, err := roleClient.AddGrantScopes(c.Context, c.FlagId, version, c.flagGrantScopeIds, opts...)
if err != nil {
return nil, nil, nil, err
}
return result.GetResponse(), result.GetItem(), nil, err
case "set-grant-scopes":
result, err := roleClient.SetGrantScopes(c.Context, c.FlagId, version, c.flagGrantScopeIds, opts...)
if err != nil {
return nil, nil, nil, err
}
return result.GetResponse(), result.GetItem(), nil, err
case "remove-grant-scopes":
result, err := roleClient.RemoveGrantScopes(c.Context, c.FlagId, version, c.flagGrantScopeIds, opts...)
if err != nil {
return nil, nil, nil, err
}
return result.GetResponse(), result.GetItem(), nil, err
}
return origResp, origItem, origItems, origError
}
@ -407,6 +482,17 @@ func printItemTable(item *roles.Role, resp *api.Response) string {
fmt.Sprintf(" %s", grant.Canonical),
)
}
if len(item.GrantScopeIds) > 0 {
ret = append(ret,
"",
fmt.Sprintf(" Grant Scope IDs: %s", ""),
)
}
for _, grantScope := range item.GrantScopeIds {
ret = append(ret,
fmt.Sprintf(" ID: %s", grantScope),
)
}
return base.WrapForHelpText(ret)
}

@ -269,6 +269,30 @@ func (c *Command) Run(args []string) int {
version = uint32(c.FlagVersion)
}
case "add-grant-scopes":
switch c.FlagVersion {
case 0:
opts = append(opts, roles.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
case "remove-grant-scopes":
switch c.FlagVersion {
case 0:
opts = append(opts, roles.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
case "set-grant-scopes":
switch c.FlagVersion {
case 0:
opts = append(opts, roles.WithAutomaticVersioning(true))
default:
version = uint32(c.FlagVersion)
}
}
if ok := extraFlagsHandlingFunc(c, f, &opts); !ok {

@ -567,7 +567,7 @@ var inputStructs = map[string][]*cmdInfo{
Container: "Scope",
HasName: true,
HasDescription: true,
VersionedActions: []string{"update", "add-grants", "remove-grants", "set-grants", "add-principals", "remove-principals", "set-principals"},
VersionedActions: []string{"update", "add-grants", "remove-grants", "set-grants", "add-principals", "remove-principals", "set-principals", "add-grant-scopes", "remove-grant-scopes", "set-grant-scopes"},
},
},
"scopes": {

@ -6,6 +6,8 @@ package roles
import (
"context"
"fmt"
"sort"
"strings"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
@ -47,6 +49,9 @@ var (
action.AddGrants,
action.SetGrants,
action.RemoveGrants,
action.AddGrantScopes,
action.SetGrantScopes,
action.RemoveGrantScopes,
)
// CollectionActions contains the set of actions that can be performed on
@ -144,7 +149,7 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
if !ok {
return false, nil
}
pbItem, err := toProto(ctx, item, nil, nil, outputOpts...)
pbItem, err := toProto(ctx, item, nil, nil, nil, outputOpts...)
if err != nil {
return false, err
}
@ -209,7 +214,7 @@ func (s Service) ListRoles(ctx context.Context, req *pbs.ListRolesRequest) (*pbs
if !ok {
continue
}
item, err := toProto(ctx, item, nil, nil, outputOpts...)
item, err := toProto(ctx, item, nil, nil, nil, outputOpts...)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
@ -248,7 +253,7 @@ func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.Get
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.getFromRepo(ctx, req.GetId())
r, prs, rgs, grantScopes, err := s.getFromRepo(ctx, req.GetId())
if err != nil {
return nil, err
}
@ -267,7 +272,7 @@ func (s Service) GetRole(ctx context.Context, req *pbs.GetRoleRequest) (*pbs.Get
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -286,7 +291,7 @@ func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*p
if authResults.Error != nil {
return nil, authResults.Error
}
r, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
r, prs, rgs, grantScopes, err := s.createInRepo(ctx, authResults.Scope.GetId(), req.GetItem())
if err != nil {
return nil, err
}
@ -305,7 +310,7 @@ func (s Service) CreateRole(ctx context.Context, req *pbs.CreateRoleRequest) (*p
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, nil, nil, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -324,7 +329,7 @@ func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*p
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
r, prs, rgs, grantScopes, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
if err != nil {
return nil, err
}
@ -343,7 +348,7 @@ func (s Service) UpdateRole(ctx context.Context, req *pbs.UpdateRoleRequest) (*p
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -378,7 +383,7 @@ func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipa
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.addPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
r, prs, rgs, grantScopes, err := s.addPrincipalsInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
if err != nil {
return nil, err
}
@ -397,7 +402,7 @@ func (s Service) AddRolePrincipals(ctx context.Context, req *pbs.AddRolePrincipa
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -416,7 +421,7 @@ func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipa
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.setPrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
r, prs, rgs, grantScopes, err := s.setPrincipalsInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
if err != nil {
return nil, err
}
@ -435,7 +440,7 @@ func (s Service) SetRolePrincipals(ctx context.Context, req *pbs.SetRolePrincipa
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -454,7 +459,7 @@ func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePr
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.removePrinciplesInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
r, prs, rgs, grantScopes, err := s.removePrincipalsInRepo(ctx, req.GetId(), req.GetPrincipalIds(), req.GetVersion())
if err != nil {
return nil, err
}
@ -473,7 +478,7 @@ func (s Service) RemoveRolePrincipals(ctx context.Context, req *pbs.RemoveRolePr
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -492,7 +497,7 @@ func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsReques
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.addGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
r, prs, rgs, grantScopes, err := s.addGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
if err != nil {
return nil, err
}
@ -511,7 +516,7 @@ func (s Service) AddRoleGrants(ctx context.Context, req *pbs.AddRoleGrantsReques
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -530,7 +535,7 @@ func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsReques
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.setGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
r, prs, rgs, grantScopes, err := s.setGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
if err != nil {
return nil, err
}
@ -549,7 +554,7 @@ func (s Service) SetRoleGrants(ctx context.Context, req *pbs.SetRoleGrantsReques
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -568,7 +573,7 @@ func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrants
if authResults.Error != nil {
return nil, authResults.Error
}
r, prs, rgs, err := s.removeGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
r, prs, rgs, grantScopes, err := s.removeGrantsInRepo(ctx, req.GetId(), req.GetGrantStrings(), req.GetVersion())
if err != nil {
return nil, err
}
@ -587,7 +592,7 @@ func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrants
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, r.GetPublicId(), IdActions).Strings()))
}
item, err := toProto(ctx, r, prs, rgs, outputOpts...)
item, err := toProto(ctx, r, prs, rgs, grantScopes, outputOpts...)
if err != nil {
return nil, err
}
@ -595,25 +600,142 @@ func (s Service) RemoveRoleGrants(ctx context.Context, req *pbs.RemoveRoleGrants
return &pbs.RemoveRoleGrantsResponse{Item: item}, nil
}
func (s Service) getFromRepo(ctx context.Context, id string) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
// 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) {
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
out, pr, roleGrants, err := repo.LookupRole(ctx, id)
out, pr, roleGrants, roleGrantScopes, err := repo.LookupRole(ctx, id)
if err != nil {
if errors.IsNotFoundError(err) {
return nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
return nil, nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
}
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
if out == nil {
return nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
return nil, nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist.", id)
}
return out, pr, roleGrants, nil
return out, pr, roleGrants, roleGrantScopes, nil
}
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Role) (*iam.Role, error) {
func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Role) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, []*iam.RoleGrantScope, error) {
const op = "roles.(Service).createInRepo"
var opts []iam.Option
if item.GetName() != nil {
@ -627,23 +749,23 @@ func (s Service) createInRepo(ctx context.Context, scopeId string, item *pb.Role
}
u, err := iam.NewRole(ctx, scopeId, opts...)
if err != nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build role for creation: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build role for creation: %v.", err)
}
repo, err := s.repoFn()
if err != nil {
return nil, err
return nil, nil, nil, nil, err
}
out, err := repo.CreateRole(ctx, u)
out, pr, roleGrants, roleGrantScopes, err := repo.CreateRole(ctx, u, opts...)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
return nil, nil, nil, nil, errors.Wrap(ctx, err, op)
}
if out == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create role but no error returned from repository.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to create role but no error returned from repository.")
}
return out, nil
return out, pr, roleGrants, roleGrantScopes, nil
}
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Role) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []string, item *pb.Role) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, []*iam.RoleGrantScope, error) {
const op = "roles.(Service).updateInRepo"
var opts []iam.Option
if desc := item.GetDescription(); desc != nil {
@ -652,32 +774,54 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st
if name := item.GetName(); name != nil {
opts = append(opts, iam.WithName(name.GetValue()))
}
if grantScopeId := item.GetGrantScopeId(); grantScopeId != nil {
opts = append(opts, iam.WithGrantScopeId(grantScopeId.GetValue()))
}
version := item.GetVersion()
u, err := iam.NewRole(ctx, scopeId, opts...)
if err != nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build role for update: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build role for update: %v.", err)
}
u.PublicId = id
dbMask := maskManager.Translate(mask)
if len(dbMask) == 0 {
return nil, nil, nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid fields provided in the update mask."})
}
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
out, pr, gr, rowsUpdated, err := repo.UpdateRole(ctx, u, version, dbMask)
// This has to be separate from Translate since the field only exists on the
// API side now.
var hasGrantScopeIdMask bool
if grantScopeId := item.GetGrantScopeId(); grantScopeId != nil {
// Have to manually check the masks, which could be concatenated strings or slices
for _, v := range mask {
if strutil.StrListContains(strings.Split(v, ","), "grant_scope_id") {
opts = append(opts, iam.WithGrantScopeId(grantScopeId.GetValue()))
hasGrantScopeIdMask = true
break
}
}
if hasGrantScopeIdMask {
if err := validateRoleGrantScopesHierarchy(ctx, repo, id, []string{item.GetGrantScopeId().GetValue()}); err != nil {
return nil, nil, nil, nil, err
}
}
}
dbMask := maskManager.Translate(mask)
if len(dbMask) == 0 && !hasGrantScopeIdMask {
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...)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op)
return nil, nil, nil, nil, errors.Wrap(ctx, err, op)
}
if rowsUpdated == 0 {
return nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist or incorrect version provided.", id)
// 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.
if rowsUpdated == 0 && !hasGrantScopeIdMask {
return nil, nil, nil, nil, handlers.NotFoundErrorf("Role %q doesn't exist or incorrect version provided.", id)
}
return out, pr, gr, nil
return out, pr, gr, grantScopes, nil
}
func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
@ -696,95 +840,95 @@ func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) {
return rows > 0, nil
}
func (s Service) addPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
func (s Service) addPrincipalsInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, []*iam.RoleGrantScope, error) {
const op = "roles.(Service).addPrincpleInRepo"
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
_, err = repo.AddPrincipalRoles(ctx, roleId, version, strutil.RemoveDuplicates(principalIds, false))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add principals to role: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add principals to role: %v.", err)
}
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
out, pr, roleGrants, grantScopes, err := repo.LookupRole(ctx, roleId)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after adding principals"))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after adding principals"))
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding principals to it.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding principals to it.")
}
return out, pr, roleGrants, nil
return out, pr, roleGrants, grantScopes, nil
}
func (s Service) setPrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
const op = "roles.(Service).setPrinciplesInRepo"
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"
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
_, _, err = repo.SetPrincipalRoles(ctx, roleId, version, strutil.RemoveDuplicates(principalIds, false))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set principals on role: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set principals on role: %v.", err)
}
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
out, pr, roleGrants, grantScopes, err := repo.LookupRole(ctx, roleId)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after setting principals"))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after setting principals"))
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting principals for it.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting principals for it.")
}
return out, pr, roleGrants, nil
return out, pr, roleGrants, grantScopes, nil
}
func (s Service) removePrinciplesInRepo(ctx context.Context, roleId string, principalIds []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
const op = "roles.(Service).removePrinciplesInRepo"
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"
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
_, err = repo.DeletePrincipalRoles(ctx, roleId, version, strutil.RemoveDuplicates(principalIds, false))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove principals from role: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove principals from role: %v.", err)
}
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
out, pr, roleGrants, grantScopes, err := repo.LookupRole(ctx, roleId)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after removing principals"))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after removing principals"))
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing principals from it.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing principals from it.")
}
return out, pr, roleGrants, nil
return out, pr, roleGrants, grantScopes, nil
}
func (s Service) addGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
func (s Service) addGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, []*iam.RoleGrantScope, error) {
const op = "service.(Service).addGrantsInRepo"
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
_, err = repo.AddRoleGrants(ctx, roleId, version, strutil.RemoveDuplicates(grants, false))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add grants to role: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to add grants to role: %v.", err)
}
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
out, pr, roleGrants, grantScopes, err := repo.LookupRole(ctx, roleId)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after adding grants"))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after adding grants"))
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding grants to it.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding grants to it.")
}
return out, pr, roleGrants, nil
return out, pr, roleGrants, grantScopes, nil
}
func (s Service) setGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
func (s Service) setGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, []*iam.RoleGrantScope, error) {
const op = "roles.(Service).setGrantsInRepo"
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
// If no grant was provided, we clear the grants.
if grants == nil {
@ -793,37 +937,115 @@ func (s Service) setGrantsInRepo(ctx context.Context, roleId string, grants []st
_, _, err = repo.SetRoleGrants(ctx, roleId, version, strutil.RemoveDuplicates(grants, false))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set grants on role: %v.", err)
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to set grants on role: %v.", err)
}
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
out, pr, roleGrants, grantScopes, err := repo.LookupRole(ctx, roleId)
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after setting grants"))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after setting grants"))
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting grants on it.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after setting grants on it.")
}
return out, pr, roleGrants, nil
return out, pr, roleGrants, grantScopes, nil
}
func (s Service) removeGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, error) {
func (s Service) removeGrantsInRepo(ctx context.Context, roleId string, grants []string, version uint32) (*iam.Role, []*iam.PrincipalRole, []*iam.RoleGrant, []*iam.RoleGrantScope, error) {
const op = "roles.(Service).removeGrantsInRepo"
repo, err := s.repoFn()
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
_, err = repo.DeleteRoleGrants(ctx, roleId, version, strutil.RemoveDuplicates(grants, false))
if err != nil {
// TODO: Figure out a way to surface more helpful error info beyond the Internal error.
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to remove grants from role: %v", err)
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)
}
out, pr, roleGrants, err := repo.LookupRole(ctx, roleId)
out, pr, roleGrants, grantScopes, err := repo.LookupRole(ctx, req.GetId())
if err != nil {
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("uable to look up role after removing grant"))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to look up role after adding grant scopes"))
}
if out == nil {
return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after removing grants from it.")
return nil, nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to lookup role after adding grant scopes to it.")
}
return out, pr, roleGrants, nil
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
}
func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.VerifyResults {
@ -849,7 +1071,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
return res
}
default:
r, _, _, err := repo.LookupRole(ctx, id)
r, _, _, _, err := repo.LookupRole(ctx, id)
if err != nil {
res.Error = err
return res
@ -865,7 +1087,7 @@ func (s Service) authResult(ctx context.Context, id string, a action.Type) auth.
return auth.Verify(ctx, opts...)
}
func toProto(ctx context.Context, in *iam.Role, principals []*iam.PrincipalRole, grants []*iam.RoleGrant, opt ...handlers.Option) (*pb.Role, error) {
func toProto(ctx context.Context, in *iam.Role, principals []*iam.PrincipalRole, grants []*iam.RoleGrant, grantScopes []*iam.RoleGrantScope, opt ...handlers.Option) (*pb.Role, error) {
opts := handlers.GetOpts(opt...)
if opts.WithOutputFields == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building role proto")
@ -900,14 +1122,20 @@ func toProto(ctx context.Context, in *iam.Role, principals []*iam.PrincipalRole,
if outputFields.Has(globals.AuthorizedActionsField) {
out.AuthorizedActions = opts.WithAuthorizedActions
}
if outputFields.Has(globals.GrantScopeIdField) && in.GetGrantScopeId() != "" {
out.GrantScopeId = &wrapperspb.StringValue{Value: in.GetGrantScopeId()}
}
if outputFields.Has(globals.PrincipalIdsField) {
for _, p := range principals {
out.PrincipalIds = append(out.PrincipalIds, p.GetPrincipalId())
}
}
if outputFields.Has(globals.GrantScopeIdsField) {
for _, gs := range grantScopes {
out.GrantScopeIds = append(out.GrantScopeIds, gs.GetScopeIdOrSpecial())
}
sort.Strings(out.GrantScopeIds)
}
if outputFields.Has(globals.GrantScopeIdField) && len(grantScopes) == 1 {
out.GrantScopeId = &wrapperspb.StringValue{Value: grantScopes[0].ScopeIdOrSpecial}
}
if outputFields.Has(globals.PrincipalsField) {
for _, p := range principals {
principal := &pb.Principal{
@ -925,7 +1153,7 @@ func toProto(ctx context.Context, in *iam.Role, principals []*iam.PrincipalRole,
}
if outputFields.Has(globals.GrantsField) {
for _, g := range grants {
parsed, err := perms.Parse(ctx, in.GetGrantScopeId(), g.GetRawGrant())
parsed, err := perms.Parse(ctx, in.GetScopeId(), g.GetRawGrant())
if err != nil {
// This should never happen as we validate on the way in, but let's
// return what we can since we are still returning the raw grant
@ -971,9 +1199,15 @@ func validateCreateRequest(req *pbs.CreateRoleRequest) error {
scope.Global.String() != item.GetScopeId() {
badFields["scope_id"] = "This field is missing or improperly formatted."
}
if item.GetGrantScopeId() != nil && handlers.ValidId(handlers.Id(item.GetScopeId()), scope.Project.Prefix()) {
if item.GetGrantScopeId().GetValue() != item.GetScopeId() {
badFields["grant_scope_id"] = "When the role is in a project scope this value must be that project's scope ID."
if item.GetGrantScopeId() != nil {
switch {
case handlers.ValidId(handlers.Id(item.GetScopeId()), scope.Project.Prefix()):
switch item.GetGrantScopeId().GetValue() {
case item.GetScopeId(),
globals.GrantScopeThis:
default:
badFields["grant_scope_id"] = "When the role is in a project scope this value must be that project's scope ID or \"this\"."
}
}
}
if item.GetPrincipals() != nil {
@ -1001,9 +1235,15 @@ func validateUpdateRequest(req *pbs.UpdateRoleRequest) error {
if req.GetItem().GetGrantStrings() != nil {
badFields["grant_strings"] = "This is a read only field and cannot be specified in an update request."
}
if req.GetItem().GetGrantScopeId() != nil && handlers.ValidId(handlers.Id(req.GetItem().GetScopeId()), scope.Project.Prefix()) {
if req.GetItem().GetGrantScopeId().GetValue() != req.GetItem().GetScopeId() {
badFields["grant_scope_id"] = "When the role is in a project scope this value must be that project's scope ID"
if item := req.GetItem(); item != nil {
switch {
case handlers.ValidId(handlers.Id(item.GetScopeId()), scope.Project.Prefix()):
switch item.GetGrantScopeId().GetValue() {
case item.GetScopeId(),
globals.GrantScopeThis:
default:
badFields["grant_scope_id"] = "When the role is in a project scope this value must be that project's scope ID or \"this\"."
}
}
}
return badFields
@ -1224,6 +1464,113 @@ func validateRemoveRoleGrantsRequest(ctx context.Context, req *pbs.RemoveRoleGra
return nil
}
// 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{
"grant_scope_id": `Project scopes can only have their own scope ID or "this" as a grant scope ID.`,
})
}
}
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{
"grant_scope_id": fmt.Sprintf("Grant scope ID %q is not valid to set on an organization role.", grantScope),
})
}
}
default:
// Should never happen
return handlers.InvalidArgumentErrorf(
"Improperly formatted identifier.",
map[string]string{
"grant_scope_id": `Unknown scope prefix type.`,
})
}
return nil
}
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,

@ -63,7 +63,7 @@ func Test_newRateLimiterConfig(t *testing.T) {
ratelimit.DefaultLimiterMaxQuotas(),
false,
&rateLimiterConfig{
maxSize: 316158,
maxSize: 322161,
configs: nil,
disabled: false,
limits: defaultLimits,

@ -2031,6 +2031,32 @@
"unlimited": false
}
],
"add-grant-scopes": [
{
"action": "add-grant-scopes",
"limit": 30000,
"per": "ip-address",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "add-grant-scopes",
"limit": 3000,
"per": "auth-token",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "add-grant-scopes",
"limit": 30000,
"per": "total",
"period": "30s",
"resource": "role",
"unlimited": false
}
],
"add-principals": [
{
"action": "add-principals",
@ -2213,6 +2239,32 @@
"unlimited": false
}
],
"remove-grant-scopes": [
{
"action": "remove-grant-scopes",
"limit": 30000,
"per": "total",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "remove-grant-scopes",
"limit": 30000,
"per": "ip-address",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "remove-grant-scopes",
"limit": 3000,
"per": "auth-token",
"period": "30s",
"resource": "role",
"unlimited": false
}
],
"remove-principals": [
{
"action": "remove-principals",
@ -2265,6 +2317,32 @@
"unlimited": false
}
],
"set-grant-scopes": [
{
"action": "set-grant-scopes",
"limit": 30000,
"per": "ip-address",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "set-grant-scopes",
"limit": 3000,
"per": "auth-token",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "set-grant-scopes",
"limit": 30000,
"per": "total",
"period": "30s",
"resource": "role",
"unlimited": false
}
],
"set-principals": [
{
"action": "set-principals",
@ -3997,7 +4075,7 @@
]
}
},
"max_size": 316158,
"max_size": 322161,
"msg": "controller api rate limiter"
},
"op": "controller.(rateLimiterConfig).writeSysEvent",

@ -2031,6 +2031,32 @@
"unlimited": false
}
],
"add-grant-scopes": [
{
"action": "add-grant-scopes",
"limit": 30000,
"per": "ip-address",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "add-grant-scopes",
"limit": 3000,
"per": "auth-token",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "add-grant-scopes",
"limit": 30000,
"per": "total",
"period": "30s",
"resource": "role",
"unlimited": false
}
],
"add-principals": [
{
"action": "add-principals",
@ -2213,6 +2239,32 @@
"unlimited": false
}
],
"remove-grant-scopes": [
{
"action": "remove-grant-scopes",
"limit": 30000,
"per": "total",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "remove-grant-scopes",
"limit": 30000,
"per": "ip-address",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "remove-grant-scopes",
"limit": 3000,
"per": "auth-token",
"period": "30s",
"resource": "role",
"unlimited": false
}
],
"remove-principals": [
{
"action": "remove-principals",
@ -2265,6 +2317,32 @@
"unlimited": false
}
],
"set-grant-scopes": [
{
"action": "set-grant-scopes",
"limit": 30000,
"per": "ip-address",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "set-grant-scopes",
"limit": 3000,
"per": "auth-token",
"period": "30s",
"resource": "role",
"unlimited": false
},
{
"action": "set-grant-scopes",
"limit": 30000,
"per": "total",
"period": "30s",
"resource": "role",
"unlimited": false
}
],
"set-principals": [
{
"action": "set-principals",

@ -2031,6 +2031,32 @@
"unlimited": false
}
],
"add-grant-scopes": [
{
"action": "add-grant-scopes",
"limit": 100,
"per": "ip-address",
"period": "1m0s",
"resource": "role",
"unlimited": false
},
{
"action": "add-grant-scopes",
"limit": 100,
"per": "auth-token",
"period": "1m0s",
"resource": "role",
"unlimited": false
},
{
"action": "add-grant-scopes",
"limit": 100,
"per": "total",
"period": "1m0s",
"resource": "role",
"unlimited": false
}
],
"add-principals": [
{
"action": "add-principals",
@ -2213,6 +2239,32 @@
"unlimited": false
}
],
"remove-grant-scopes": [
{
"action": "remove-grant-scopes",
"limit": 100,
"per": "total",
"period": "1m0s",
"resource": "role",
"unlimited": false
},
{
"action": "remove-grant-scopes",
"limit": 100,
"per": "ip-address",
"period": "1m0s",
"resource": "role",
"unlimited": false
},
{
"action": "remove-grant-scopes",
"limit": 100,
"per": "auth-token",
"period": "1m0s",
"resource": "role",
"unlimited": false
}
],
"remove-principals": [
{
"action": "remove-principals",
@ -2265,6 +2317,32 @@
"unlimited": false
}
],
"set-grant-scopes": [
{
"action": "set-grant-scopes",
"limit": 100,
"per": "ip-address",
"period": "1m0s",
"resource": "role",
"unlimited": false
},
{
"action": "set-grant-scopes",
"limit": 100,
"per": "auth-token",
"period": "1m0s",
"resource": "role",
"unlimited": false
},
{
"action": "set-grant-scopes",
"limit": 100,
"per": "total",
"period": "1m0s",
"resource": "role",
"unlimited": false
}
],
"set-principals": [
{
"action": "set-principals",
@ -3997,7 +4075,7 @@
]
}
},
"max_size": 316158,
"max_size": 322161,
"msg": "controller api rate limiter"
},
"op": "controller.(rateLimiterConfig).writeSysEvent",

@ -370,8 +370,8 @@ type TestControllerOpts struct {
// DefaultPassword is the password used when creating the default accounts.
DefaultPassword string
// DisableInitialLoginRoleCreation can be set true to disable creating the
// global scope login role automatically.
// DisableInitialLoginRoleCreation can be set true to disable creating the global
// scope default role automatically.
DisableInitialLoginRoleCreation bool
// DisableAuthMethodCreation can be set true to disable creating an auth
@ -424,6 +424,11 @@ type TestControllerOpts struct {
// eventer.
EnableEventing bool
// EventerConfig allows specifying a custom event config. You must not run
// the test in parallel (no calls to t.Parallel) since the this option
// relies on modifying the system wide default eventer.
EventerConfig *event.EventerConfig
// DisableAuthorizationFailures will still cause authz checks to be
// performed but they won't cause 403 Forbidden. Useful for API-level
// testing to avoid a lot of faff.
@ -661,12 +666,15 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
opts.Config.Controller.Scheduler.JobRunIntervalDuration = opts.SchedulerRunJobInterval
opts.Config.Controller.ApiRateLimiterMaxQuotas = ratelimit.DefaultLimiterMaxQuotas()
if opts.EnableEventing {
opts.Config.Eventing = &event.EventerConfig{
AuditEnabled: true,
ObservationsEnabled: true,
SysEventsEnabled: true,
ErrorEventsDisabled: true,
if opts.EnableEventing || opts.EventerConfig != nil {
opts.Config.Eventing = opts.EventerConfig
if opts.Config.Eventing == nil {
opts.Config.Eventing = &event.EventerConfig{
AuditEnabled: true,
ObservationsEnabled: true,
SysEventsEnabled: true,
ErrorEventsDisabled: true,
}
}
}
serverName, err := os.Hostname()
@ -757,6 +765,9 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
if _, err := tc.b.CreateInitialLoginRole(ctx); err != nil {
t.Fatal(err)
}
if _, err := tc.b.CreateInitialAuthenticatedUserRole(ctx); err != nil {
t.Fatal(err)
}
if !opts.DisableAuthMethodCreation {
if _, _, err := tc.b.CreateInitialPasswordAuthMethod(ctx); err != nil {
t.Fatal(err)
@ -791,6 +802,9 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
}
} else if !opts.DisableDatabaseCreation {
var createOpts []base.Option
if opts.DisableInitialLoginRoleCreation {
createOpts = append(createOpts, base.WithSkipDefaultRoleCreation())
}
if opts.DisableAuthMethodCreation {
createOpts = append(createOpts, base.WithSkipAuthMethodCreation())
}

@ -223,6 +223,8 @@ begin
end;
$$ language plpgsql;
-- Dropped in 83/01_iam_role_grant_scope since we moved to multiple scopes per
-- role
create or replace function grant_scope_id_valid() returns trigger
as $$
declare parent_scope_id text;

@ -0,0 +1,210 @@
-- Copyright (c) HashiCorp, Inc.
-- SPDX-License-Identifier: MPL-2.0
begin;
-- This table is a join table to map roles to the various scopes the role
-- grants apply to. role_id is a foreign key, but scope_id_or_special is not a
-- foreign key to scopes because we support specific special values which are
-- checked for in the constraint. We never query by scope_id_or_special so the
-- ordering of the primary key should be correct.
create table iam_role_grant_scope (
create_time wt_timestamp,
role_id wt_role_id not null -- pk
references iam_role(public_id)
on delete cascade
on update cascade,
scope_id_or_special text not null -- pk
constraint scope_id_or_special_is_valid
check (
length(trim(scope_id_or_special)) = 12
or
scope_id_or_special in ('global', 'this', 'children', 'descendants')
),
primary key(role_id, scope_id_or_special)
);
comment on table iam_role_grant_scope is
'table to map roles to the scopes they grant access to';
insert into oplog_ticket (name, version)
values
('iam_role_grant_scope', 1);
create trigger default_create_time_column before insert on iam_role_grant_scope
for each row execute procedure default_create_time();
-- iam_immutable_role_grant_scope() ensures that grant scopes assigned to
-- roles are immutable.
create function iam_immutable_role_grant_scope() returns trigger
as $$
begin
raise exception 'role grant scopes are immutable';
end;
$$ language plpgsql;
create trigger immutable_role_grant_scope before update on iam_role_grant_scope
for each row execute procedure iam_immutable_role_grant_scope();
-- cascade_role_grant_scope_deletion() ensures that grant scopes entries are
-- deleted when scopes are deleted
create function cascade_role_grant_scope_deletion() returns trigger
as $$
begin
delete from iam_role_grant_scope where scope_id_or_special = old.public_id;
return old;
end;
$$ language plpgsql;
-- Create a trigger to ensure scope deletion cascades, since we're not using wt_scope_id
create trigger cascade_deletion_iam_scope_to_iam_role_grant_scope after delete on iam_scope
for each row execute procedure cascade_role_grant_scope_deletion();
-- role_grant_scope_id_or_special_valid ensures that a given grant scope ID is for a
-- scope that exists or one of our known values
create function role_grant_scope_id_or_special_valid() returns trigger
as $$
declare new_scope_type text;
declare role_scope_id text;
declare parent_scope_id text;
declare role_scope_type text;
declare validated_scope_id text;
declare existing_scope_id_or_special text;
begin
-- We want to make a few checks based on the role's actual scope so select it
select ir.scope_id from iam_role ir where ir.public_id = new.role_id into role_scope_id;
-- It's always allowed to have a scope_id_or_special of "this" but don't
-- allow it as well as the role's explicit scope ID
if new.scope_id_or_special = 'this' then
select rgs.scope_id_or_special
from iam_role_grant_scope rgs
where rgs.role_id = new.role_id and rgs.scope_id_or_special = role_scope_id
into existing_scope_id_or_special;
if existing_scope_id_or_special is not null then
raise exception 'invalid to specify both "this" and a role''s actual scope id as a grant scope';
end if;
return new;
end if;
-- Fetch the scope id for the role
select ir.scope_id from iam_role ir where ir.public_id = new.role_id into role_scope_id;
-- It's always allowed to have the scope_id_or_special be the same as the role's
if new.scope_id_or_special = role_scope_id then
select rgs.scope_id_or_special
from iam_role_grant_scope rgs
where rgs.role_id = new.role_id and rgs.scope_id_or_special = 'this'
into existing_scope_id_or_special;
if existing_scope_id_or_special is not null then
raise exception 'invalid to specify both "this" and a role''s actual scope id as a grant scope';
end if;
return new;
end if;
-- A note about the above: this technically allows us to have grant scope
-- IDs defined on projects. The original grant_scope_id logic allowed this
-- in the domain layer so long as it referred only to its own scope.
-- Accordingly we keep this behavior, even though we could choose to
-- disallow this at the API layer.
-- At this point we've covered the same-as-role-scope and "this" cases
-- above. Now, fetch the type of the role's scope, then check two situations
-- that are either always or never allowed.
select isc.type from iam_scope isc where isc.public_id = role_scope_id into role_scope_type;
-- Always allowed, because any scope is a child of global, and we've handled
-- same-scope-id case above; however we have to check the scope is actually
-- valid/known. Distinction check is used in the final case because if it's
-- not known it's null.
if role_scope_type = 'global' then
case
when new.scope_id_or_special = 'children' then
select rgs.scope_id_or_special
from iam_role_grant_scope rgs
where rgs.role_id = new.role_id and rgs.scope_id_or_special = 'descendants'
into existing_scope_id_or_special;
if existing_scope_id_or_special is not null then
raise exception 'invalid to specify both "children" and "descendants"as a grant scope';
end if;
when new.scope_id_or_special = 'descendants' then
select rgs.scope_id_or_special
from iam_role_grant_scope rgs
where rgs.role_id = new.role_id and rgs.scope_id_or_special = 'children'
into existing_scope_id_or_special;
if existing_scope_id_or_special is not null then
raise exception 'invalid to specify both "children" and "descendants"as a grant scope';
end if;
else
select isc.public_id from iam_scope isc where isc.public_id = new.scope_id_or_special into validated_scope_id;
if validated_scope_id is distinct from new.scope_id_or_special then
raise exception 'invalid grant scope id';
end if;
end case;
return new;
end if;
-- Never allowed, because projects don't have child scopes (and we've
-- already allowed same-scope-id above)
if role_scope_type = 'project' then
raise exception 'invalid to set a grant scope ID to non-same scope_id_or_special when role scope type is project';
end if;
-- Ensure that what remains really is org
if role_scope_type != 'org' then
raise exception 'unknown scope type';
end if;
-- If it's "children" then allow it
if new.scope_id_or_special = 'children' then
return new;
end if;
-- Make "descendants" an error for orgs
if new.scope_id_or_special = 'descendants' then
raise exception 'invalid to specify "descendants" as a grant scope when the role''s scope ID is not "global"';
end if;
-- We are now dealing with a bare scope ID and need to ensure that it's a
-- child project of the role's org scope. Look up the parent scope ID for
-- the grant scope ID given in the row. Allow iff the grant scope ID's
-- parent matches the role's scope ID. We know that the role is in an org
-- scope, so the only acceptable possibility here is that the new scope ID
-- is a project and its parent scope is this org's.
-- Ensure it exists
select isc.public_id from iam_scope isc where isc.public_id = new.scope_id_or_special into validated_scope_id;
if validated_scope_id is distinct from new.scope_id_or_special then
raise exception 'invalid grant scope id';
end if;
-- Ensure it's a project
select isc.type from iam_scope isc where isc.public_id = new.scope_id_or_special into new_scope_type;
if new_scope_type != 'project' then
raise exception 'expected grant scope id scope type to be project';
end if;
-- Ensure that the parent of the project is the role's org scope
select isc.parent_id from iam_scope isc where isc.public_id = new.scope_id_or_special into parent_scope_id;
if parent_scope_id != role_scope_id then
raise exception 'grant scope id is not a child project of the role''s org scope';
end if;
return new;
end;
$$ language plpgsql;
comment on function role_grant_scope_id_or_special_valid() is
'function used to ensure grant scope ids are valid';
create trigger ensure_role_grant_scope_id_or_special_valid before insert or update on iam_role_grant_scope
for each row execute procedure role_grant_scope_id_or_special_valid();
-- Now perform migrations:
-- First, copy current grant scope ID values from existing roles to the new
-- table and set the grant scope ID value on each role to the scope ID
insert into iam_role_grant_scope(role_id, scope_id_or_special)
select public_id as role_id, grant_scope_id as scope_id_or_special from iam_role;
-- Drop the now-unnecessary trigger and function from 0/06_iam
drop trigger ensure_grant_scope_id_valid on iam_role;
drop function grant_scope_id_valid;
-- Remove the column from iam_role
alter table iam_role drop column grant_scope_id;
commit;

@ -106,19 +106,33 @@ begin;
('g___cg-group', 'u_______cora');
insert into iam_role
(scope_id, grant_scope_id, public_id, name)
values
('p____bcolors', 'p____bcolors', 'r_pp_bc__mix', 'Color Mixer'),
('p____rcolors', 'p____rcolors', 'r_pp_rc__mix', 'Color Mixer'),
('p____gcolors', 'p____gcolors', 'r_pp_gc__mix', 'Color Mixer'),
('o_____colors', 'p____bcolors', 'r_op_bc__art', 'Blue Color Artist'),
('o_____colors', 'p____rcolors', 'r_op_rc__art', 'Red Color Artist'),
('o_____colors', 'p____gcolors', 'r_op_gc__art', 'Green Color Artist'),
('o_____colors', 'o_____colors', 'r_oo_____art', 'Color Artist'),
('global', 'o_____colors', 'r_go____name', 'Color Namer'),
('global', 'p____bcolors', 'r_gp____spec', 'Blue Color Inspector'),
('global', 'global', 'r_gg_____buy', 'Purchaser'),
('global', 'global', 'r_gg____shop', 'Shopper');
(scope_id, public_id, name)
values
('p____bcolors', 'r_pp_bc__mix', 'Color Mixer'),
('p____rcolors', 'r_pp_rc__mix', 'Color Mixer'),
('p____gcolors', 'r_pp_gc__mix', 'Color Mixer'),
('o_____colors', 'r_op_bc__art', 'Blue Color Artist'),
('o_____colors', 'r_op_rc__art', 'Red Color Artist'),
('o_____colors', 'r_op_gc__art', 'Green Color Artist'),
('o_____colors', 'r_oo_____art', 'Color Artist'),
('global', 'r_go____name', 'Color Namer'),
('global', 'r_gp____spec', 'Blue Color Inspector'),
('global', 'r_gg_____buy', 'Purchaser'),
('global', 'r_gg____shop', 'Shopper');
insert into iam_role_grant_scope
(role_id, scope_id_or_special)
values
('r_pp_bc__mix', 'this'),
('r_pp_rc__mix', 'p____rcolors'),
('r_pp_gc__mix', 'this'),
('r_op_bc__art', 'p____bcolors'),
('r_op_rc__art', 'p____rcolors'),
('r_op_gc__art', 'p____gcolors'),
('r_go____name', 'o_____colors'),
('r_gp____spec', 'p____bcolors'),
('r_gg_____buy', 'global'),
('r_gg____shop', 'global');
insert into iam_role_grant
(role_id, canonical_grant, raw_grant)

@ -55,14 +55,22 @@ begin;
('g___ws-group', 'u_____waylon');
insert into iam_role
(scope_id, grant_scope_id, public_id, name)
values
-- ('global', 'global', 'r_gg_____buy', 'Purchaser'),
-- ('global', 'global', 'r_gg____shop', 'Shopper'),
('p____bwidget', 'p____bwidget', 'r_pp_bw__bld', 'Widget Builder'),
('p____swidget', 'p____swidget', 'r_pp_sw__bld', 'Widget Builder'),
('o_____widget', 'p____swidget', 'r_op_sw__eng', 'Small Widget Engineer'),
('o_____widget', 'o_____widget', 'r_oo_____eng', 'Widget Engineer');
(scope_id, public_id, name)
values
-- ('global', 'r_gg_____buy', 'Purchaser'),
-- ('global', 'r_gg____shop', 'Shopper'),
('p____bwidget', 'r_pp_bw__bld', 'Widget Builder'),
('p____swidget', 'r_pp_sw__bld', 'Widget Builder'),
('o_____widget', 'r_op_sw__eng', 'Small Widget Engineer'),
('o_____widget', 'r_oo_____eng', 'Widget Engineer');
insert into iam_role_grant_scope
(role_id, scope_id_or_special)
values
('r_pp_bw__bld', 'p____bwidget'),
('r_pp_sw__bld', 'this'),
('r_op_sw__eng', 'p____swidget'),
('r_oo_____eng', 'o_____widget');
insert into iam_role_grant
(role_id, canonical_grant, raw_grant)

@ -107,9 +107,14 @@ begin;
-- Roles
insert into iam_role
(scope_id, grant_scope_id, public_id, name)
(scope_id, public_id, name)
values
('p______tacos', 'p______tacos', 'r_pp___tacos', 'Tacos');
('p______tacos', 'r_pp___tacos', 'Tacos');
insert into iam_role_grant_scope
(role_id, scope_id_or_special )
values
('r_pp___tacos', 'p______tacos');
insert into iam_role_grant
(role_id, canonical_grant, raw_grant)

@ -2570,6 +2570,54 @@
]
}
},
"/v1/roles/{id}:add-grant-scopes": {
"post": {
"summary": "Adds grant scopes to a Role",
"operationId": "RoleService_AddRoleGrantScopes",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.roles.v1.Role"
}
}
},
"parameters": [
{
"name": "id",
"description": "",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"grant_scope_ids": {
"type": "array",
"items": {
"type": "string"
},
"title": ""
}
}
}
}
],
"tags": [
"controller.api.services.v1.RoleService"
]
}
},
"/v1/roles/{id}:add-grants": {
"post": {
"summary": "Adds grants to a Role",
@ -2666,6 +2714,54 @@
]
}
},
"/v1/roles/{id}:remove-grant-scopes": {
"post": {
"summary": "Removes grant scopes from a Role.",
"operationId": "RoleService_RemoveRoleGrantScopes",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.roles.v1.Role"
}
}
},
"parameters": [
{
"name": "id",
"description": "",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"grant_scope_ids": {
"type": "array",
"items": {
"type": "string"
},
"title": ""
}
}
}
}
],
"tags": [
"controller.api.services.v1.RoleService"
]
}
},
"/v1/roles/{id}:remove-grants": {
"post": {
"summary": "Removes grants from a Role.",
@ -2762,6 +2858,54 @@
]
}
},
"/v1/roles/{id}:set-grant-scopes": {
"post": {
"summary": "Set grant scopes for a Role, removing any grant scopes that are not specified in the request.",
"operationId": "RoleService_SetRoleGrantScopes",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/controller.api.resources.roles.v1.Role"
}
}
},
"parameters": [
{
"name": "id",
"description": "",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"format": "int64",
"description": "Version is used to ensure this resource has not changed.\nThe mutation will fail if the version does not match the latest known good version."
},
"grant_scope_ids": {
"type": "array",
"items": {
"type": "string"
},
"title": ""
}
}
}
}
],
"tags": [
"controller.api.services.v1.RoleService"
]
}
},
"/v1/roles/{id}:set-grants": {
"post": {
"summary": "Set grants for a Role, removing any grants that are not specified in the request.",
@ -5944,7 +6088,15 @@
},
"grant_scope_id": {
"type": "string",
"description": "The Scope the grants will apply to. If the Role is at the global scope, this can be an org or project. If the Role is at an org scope, this can be a project within the org. It is invalid for this to be anything other than the Role's scope when the Role's scope is a project."
"description": "The Scope the grants will apply to. If the Role is at the global scope,\nthis can be an org or project. If the Role is at an org scope, this can be\na project within the org. It is invalid for this to be anything other than\nthe Role's scope when the Role's scope is a project.\n\nDeprecated: Use \"grant_scope_ids\" instead."
},
"grant_scope_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Output only. The IDs of Scopes the grants will apply to. This can include\nthe role's own scope ID, or \"this\" for the same behavior; specific IDs of\nscopes that are children of the role's scope; the value \"children\" to match\nall direct child scopes of the role's scope; or the value \"descendants\" to\nmatch all descendant scopes (e.g. child scopes, children of child scopes;\nonly valid at \"global\" scope since it is the only one with children of\nchildren).",
"readOnly": true
},
"principal_ids": {
"type": "array",
@ -7583,6 +7735,14 @@
}
}
},
"controller.api.services.v1.AddRoleGrantScopesResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.roles.v1.Role"
}
}
},
"controller.api.services.v1.AddRoleGrantsResponse": {
"type": "object",
"properties": {
@ -8995,6 +9155,14 @@
}
}
},
"controller.api.services.v1.RemoveRoleGrantScopesResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.roles.v1.Role"
}
}
},
"controller.api.services.v1.RemoveRoleGrantsResponse": {
"type": "object",
"properties": {
@ -9083,6 +9251,14 @@
}
}
},
"controller.api.services.v1.SetRoleGrantScopesResponse": {
"type": "object",
"properties": {
"item": {
"$ref": "#/definitions/controller.api.resources.roles.v1.Role"
}
}
},
"controller.api.services.v1.SetRoleGrantsResponse": {
"type": "object",
"properties": {

@ -1285,6 +1285,342 @@ func (x *RemoveRoleGrantsResponse) GetItem() *roles.Role {
return nil
}
type AddRoleGrantScopesRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
// Version is used to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"`
GrantScopeIds []string `protobuf:"bytes,3,rep,name=grant_scope_ids,proto3" json:"grant_scope_ids,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *AddRoleGrantScopesRequest) Reset() {
*x = AddRoleGrantScopesRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddRoleGrantScopesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddRoleGrantScopesRequest) ProtoMessage() {}
func (x *AddRoleGrantScopesRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AddRoleGrantScopesRequest.ProtoReflect.Descriptor instead.
func (*AddRoleGrantScopesRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{22}
}
func (x *AddRoleGrantScopesRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *AddRoleGrantScopesRequest) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *AddRoleGrantScopesRequest) GetGrantScopeIds() []string {
if x != nil {
return x.GrantScopeIds
}
return nil
}
type AddRoleGrantScopesResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *roles.Role `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *AddRoleGrantScopesResponse) Reset() {
*x = AddRoleGrantScopesResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddRoleGrantScopesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddRoleGrantScopesResponse) ProtoMessage() {}
func (x *AddRoleGrantScopesResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AddRoleGrantScopesResponse.ProtoReflect.Descriptor instead.
func (*AddRoleGrantScopesResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{23}
}
func (x *AddRoleGrantScopesResponse) GetItem() *roles.Role {
if x != nil {
return x.Item
}
return nil
}
type SetRoleGrantScopesRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
// Version is used to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"`
GrantScopeIds []string `protobuf:"bytes,3,rep,name=grant_scope_ids,proto3" json:"grant_scope_ids,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *SetRoleGrantScopesRequest) Reset() {
*x = SetRoleGrantScopesRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SetRoleGrantScopesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetRoleGrantScopesRequest) ProtoMessage() {}
func (x *SetRoleGrantScopesRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetRoleGrantScopesRequest.ProtoReflect.Descriptor instead.
func (*SetRoleGrantScopesRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{24}
}
func (x *SetRoleGrantScopesRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *SetRoleGrantScopesRequest) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *SetRoleGrantScopesRequest) GetGrantScopeIds() []string {
if x != nil {
return x.GrantScopeIds
}
return nil
}
type SetRoleGrantScopesResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *roles.Role `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *SetRoleGrantScopesResponse) Reset() {
*x = SetRoleGrantScopesResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SetRoleGrantScopesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetRoleGrantScopesResponse) ProtoMessage() {}
func (x *SetRoleGrantScopesResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetRoleGrantScopesResponse.ProtoReflect.Descriptor instead.
func (*SetRoleGrantScopesResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{25}
}
func (x *SetRoleGrantScopesResponse) GetItem() *roles.Role {
if x != nil {
return x.Item
}
return nil
}
type RemoveRoleGrantScopesRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" class:"public"` // @gotags: `class:"public"`
// Version is used to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"`
GrantScopeIds []string `protobuf:"bytes,3,rep,name=grant_scope_ids,proto3" json:"grant_scope_ids,omitempty" class:"public"` // @gotags: `class:"public"`
}
func (x *RemoveRoleGrantScopesRequest) Reset() {
*x = RemoveRoleGrantScopesRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RemoveRoleGrantScopesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RemoveRoleGrantScopesRequest) ProtoMessage() {}
func (x *RemoveRoleGrantScopesRequest) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[26]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RemoveRoleGrantScopesRequest.ProtoReflect.Descriptor instead.
func (*RemoveRoleGrantScopesRequest) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{26}
}
func (x *RemoveRoleGrantScopesRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *RemoveRoleGrantScopesRequest) GetVersion() uint32 {
if x != nil {
return x.Version
}
return 0
}
func (x *RemoveRoleGrantScopesRequest) GetGrantScopeIds() []string {
if x != nil {
return x.GrantScopeIds
}
return nil
}
type RemoveRoleGrantScopesResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Item *roles.Role `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
}
func (x *RemoveRoleGrantScopesResponse) Reset() {
*x = RemoveRoleGrantScopesResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RemoveRoleGrantScopesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RemoveRoleGrantScopesResponse) ProtoMessage() {}
func (x *RemoveRoleGrantScopesResponse) ProtoReflect() protoreflect.Message {
mi := &file_controller_api_services_v1_role_service_proto_msgTypes[27]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RemoveRoleGrantScopesResponse.ProtoReflect.Descriptor instead.
func (*RemoveRoleGrantScopesResponse) Descriptor() ([]byte, []int) {
return file_controller_api_services_v1_role_service_proto_rawDescGZIP(), []int{27}
}
func (x *RemoveRoleGrantScopesResponse) GetItem() *roles.Role {
if x != nil {
return x.Item
}
return nil
}
var File_controller_api_services_v1_role_service_proto protoreflect.FileDescriptor
var file_controller_api_services_v1_role_service_proto_rawDesc = []byte{
@ -1441,150 +1777,235 @@ var file_controller_api_services_v1_role_service_proto_rawDesc = []byte{
0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52,
0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x98, 0x11, 0x0a, 0x0b, 0x52, 0x6f,
0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x07, 0x47, 0x65,
0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47,
0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34,
0x92, 0x41, 0x15, 0x12, 0x13, 0x47, 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x62, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f,
0x7b, 0x69, 0x64, 0x7d, 0x12, 0x90, 0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c,
0x65, 0x73, 0x12, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e,
0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x26, 0x92, 0x41, 0x12, 0x12, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20,
0x52, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x76,
0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0xa5, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x92, 0x41, 0x18, 0x12, 0x16, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c,
0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12,
0xa3, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d,
0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6f, 0x0a, 0x19, 0x41, 0x64, 0x64,
0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x28, 0x0a, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f,
0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74,
0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x1a, 0x41, 0x64,
0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x6f, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65,
0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f,
0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c,
0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f,
0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x22, 0x72, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47,
0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x67,
0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70,
0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0x5c, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52,
0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e,
0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x69,
0x74, 0x65, 0x6d, 0x32, 0xf5, 0x16, 0x0a, 0x0b, 0x52, 0x6f, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12,
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x92, 0x41, 0x15, 0x12, 0x13, 0x47,
0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c,
0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x0e,
0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x90,
0x01, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f,
0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6c, 0x65,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x92, 0x41, 0x12, 0x12, 0x10,
0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x2e,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0b, 0x12, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65,
0x73, 0x12, 0xa5, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65,
0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x38, 0x92, 0x41, 0x18, 0x12, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x17, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x09,
0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0xa3, 0x01, 0x0a, 0x0a, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x1c, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x32,
0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12,
0x97, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d,
0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x92,
0x41, 0x11, 0x12, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, 0x6f,
0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x32, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73,
0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x97, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x73, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x2a,
0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12,
0xd8, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x92,
0x41, 0x11, 0x12, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x52, 0x6f,
0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x2a, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x72,
0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xd8, 0x01, 0x0a, 0x11, 0x41, 0x64,
0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12,
0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64,
0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69,
0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65,
0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x56, 0x92, 0x41, 0x25, 0x12, 0x23, 0x41, 0x64, 0x64, 0x73, 0x20, 0x55, 0x73,
0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70,
0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x28, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x1d, 0x2f, 0x76, 0x31,
0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d,
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x97, 0x02, 0x0a, 0x11, 0x53,
0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73,
0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x92, 0x41,
0x25, 0x12, 0x23, 0x41, 0x64, 0x64, 0x73, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e,
0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61,
0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x3a, 0x01, 0x2a, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73,
0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69,
0x70, 0x61, 0x6c, 0x73, 0x12, 0x97, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65,
0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50,
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65,
0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x94, 0x01,
0x92, 0x41, 0x63, 0x12, 0x61, 0x53, 0x65, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61,
0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20,
0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67,
0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x20,
0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65,
0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x3a, 0x01, 0x2a, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73,
0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69,
0x70, 0x61, 0x6c, 0x73, 0x12, 0xf7, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52,
0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x37, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72,
0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x6c, 0x92, 0x41, 0x38, 0x12, 0x36, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x55, 0x73, 0x65,
0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73,
0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x2b, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x20, 0x2f, 0x76,
0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0xba,
0x01, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73,
0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64,
0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e,
0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, 0x64, 0x64, 0x73,
0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c,
0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d,
0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0xf7, 0x01, 0x0a, 0x0d,
0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x94, 0x01, 0x92, 0x41, 0x63, 0x12, 0x61, 0x53,
0x65, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20,
0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65,
0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70,
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61,
0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64,
0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22,
0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a,
0x73, 0x65, 0x74, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0xf7,
0x01, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69,
0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x37, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50,
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x38, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65,
0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61,
0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6c, 0x92, 0x41, 0x38, 0x12,
0x36, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65,
0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x55, 0x73, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64,
0x2f, 0x6f, 0x72, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20,
0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x3a, 0x01, 0x2a,
0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65,
0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x70, 0x72,
0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0xba, 0x01, 0x0a, 0x0d, 0x41, 0x64, 0x64,
0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47,
0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c,
0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x44, 0x92, 0x41, 0x17, 0x12, 0x15, 0x41, 0x64, 0x64, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74,
0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x24, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f,
0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67,
0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0xf7, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c,
0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e,
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72,
0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x80, 0x01, 0x92,
0x41, 0x53, 0x12, 0x51, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x66,
0x6f, 0x72, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76,
0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x74,
0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63,
0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x62, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f,
0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12,
0xcc, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72,
0x61, 0x6e, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e,
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c,
0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x4d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x67, 0x72,
0x61, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65,
0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x22, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d,
0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0xd5,
0x01, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53,
0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x35, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53,
0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x6f, 0x6c,
0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x41, 0x64, 0x64, 0x73, 0x20,
0x67, 0x72, 0x61, 0x6e, 0x74, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20,
0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x3a, 0x01, 0x2a, 0x62,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73,
0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x61, 0x64, 0x64, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x2d,
0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x98, 0x02, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x52, 0x6f,
0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x35, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f,
0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74,
0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x80, 0x01, 0x92, 0x41, 0x53, 0x12, 0x51, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72,
0x61, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c,
0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76,
0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63,
0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x92, 0x01, 0x92,
0x41, 0x5f, 0x12, 0x5d, 0x53, 0x65, 0x74, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x20, 0x73, 0x63,
0x6f, 0x70, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2c,
0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x67, 0x72,
0x61, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f,
0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74,
0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x24, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f,
0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67,
0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0xcc, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f,
0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x92, 0x41, 0x1d, 0x12, 0x1b, 0x52, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x73, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20,
0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x3a, 0x01, 0x2a,
0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65,
0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67, 0x72,
0x61, 0x6e, 0x74, 0x73, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67,
0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x6e, 0x74, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65,
0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x3a, 0x01, 0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d,
0x3a, 0x73, 0x65, 0x74, 0x2d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x73, 0x12, 0xe7, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65,
0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x38, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52,
0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61,
0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x59, 0x92, 0x41, 0x23, 0x12, 0x21, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x67,
0x72, 0x61, 0x6e, 0x74, 0x20, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d,
0x20, 0x61, 0x20, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x3a, 0x01,
0x2a, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x6c,
0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x2d, 0x67,
0x72, 0x61, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x42, 0x4d, 0x5a, 0x4b, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
@ -1599,74 +2020,89 @@ func file_controller_api_services_v1_role_service_proto_rawDescGZIP() []byte {
return file_controller_api_services_v1_role_service_proto_rawDescData
}
var file_controller_api_services_v1_role_service_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
var file_controller_api_services_v1_role_service_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
var file_controller_api_services_v1_role_service_proto_goTypes = []interface{}{
(*GetRoleRequest)(nil), // 0: controller.api.services.v1.GetRoleRequest
(*GetRoleResponse)(nil), // 1: controller.api.services.v1.GetRoleResponse
(*ListRolesRequest)(nil), // 2: controller.api.services.v1.ListRolesRequest
(*ListRolesResponse)(nil), // 3: controller.api.services.v1.ListRolesResponse
(*CreateRoleRequest)(nil), // 4: controller.api.services.v1.CreateRoleRequest
(*CreateRoleResponse)(nil), // 5: controller.api.services.v1.CreateRoleResponse
(*UpdateRoleRequest)(nil), // 6: controller.api.services.v1.UpdateRoleRequest
(*UpdateRoleResponse)(nil), // 7: controller.api.services.v1.UpdateRoleResponse
(*DeleteRoleRequest)(nil), // 8: controller.api.services.v1.DeleteRoleRequest
(*DeleteRoleResponse)(nil), // 9: controller.api.services.v1.DeleteRoleResponse
(*AddRolePrincipalsRequest)(nil), // 10: controller.api.services.v1.AddRolePrincipalsRequest
(*AddRolePrincipalsResponse)(nil), // 11: controller.api.services.v1.AddRolePrincipalsResponse
(*SetRolePrincipalsRequest)(nil), // 12: controller.api.services.v1.SetRolePrincipalsRequest
(*SetRolePrincipalsResponse)(nil), // 13: controller.api.services.v1.SetRolePrincipalsResponse
(*RemoveRolePrincipalsRequest)(nil), // 14: controller.api.services.v1.RemoveRolePrincipalsRequest
(*RemoveRolePrincipalsResponse)(nil), // 15: controller.api.services.v1.RemoveRolePrincipalsResponse
(*AddRoleGrantsRequest)(nil), // 16: controller.api.services.v1.AddRoleGrantsRequest
(*AddRoleGrantsResponse)(nil), // 17: controller.api.services.v1.AddRoleGrantsResponse
(*SetRoleGrantsRequest)(nil), // 18: controller.api.services.v1.SetRoleGrantsRequest
(*SetRoleGrantsResponse)(nil), // 19: controller.api.services.v1.SetRoleGrantsResponse
(*RemoveRoleGrantsRequest)(nil), // 20: controller.api.services.v1.RemoveRoleGrantsRequest
(*RemoveRoleGrantsResponse)(nil), // 21: controller.api.services.v1.RemoveRoleGrantsResponse
(*roles.Role)(nil), // 22: controller.api.resources.roles.v1.Role
(*fieldmaskpb.FieldMask)(nil), // 23: google.protobuf.FieldMask
(*GetRoleRequest)(nil), // 0: controller.api.services.v1.GetRoleRequest
(*GetRoleResponse)(nil), // 1: controller.api.services.v1.GetRoleResponse
(*ListRolesRequest)(nil), // 2: controller.api.services.v1.ListRolesRequest
(*ListRolesResponse)(nil), // 3: controller.api.services.v1.ListRolesResponse
(*CreateRoleRequest)(nil), // 4: controller.api.services.v1.CreateRoleRequest
(*CreateRoleResponse)(nil), // 5: controller.api.services.v1.CreateRoleResponse
(*UpdateRoleRequest)(nil), // 6: controller.api.services.v1.UpdateRoleRequest
(*UpdateRoleResponse)(nil), // 7: controller.api.services.v1.UpdateRoleResponse
(*DeleteRoleRequest)(nil), // 8: controller.api.services.v1.DeleteRoleRequest
(*DeleteRoleResponse)(nil), // 9: controller.api.services.v1.DeleteRoleResponse
(*AddRolePrincipalsRequest)(nil), // 10: controller.api.services.v1.AddRolePrincipalsRequest
(*AddRolePrincipalsResponse)(nil), // 11: controller.api.services.v1.AddRolePrincipalsResponse
(*SetRolePrincipalsRequest)(nil), // 12: controller.api.services.v1.SetRolePrincipalsRequest
(*SetRolePrincipalsResponse)(nil), // 13: controller.api.services.v1.SetRolePrincipalsResponse
(*RemoveRolePrincipalsRequest)(nil), // 14: controller.api.services.v1.RemoveRolePrincipalsRequest
(*RemoveRolePrincipalsResponse)(nil), // 15: controller.api.services.v1.RemoveRolePrincipalsResponse
(*AddRoleGrantsRequest)(nil), // 16: controller.api.services.v1.AddRoleGrantsRequest
(*AddRoleGrantsResponse)(nil), // 17: controller.api.services.v1.AddRoleGrantsResponse
(*SetRoleGrantsRequest)(nil), // 18: controller.api.services.v1.SetRoleGrantsRequest
(*SetRoleGrantsResponse)(nil), // 19: controller.api.services.v1.SetRoleGrantsResponse
(*RemoveRoleGrantsRequest)(nil), // 20: controller.api.services.v1.RemoveRoleGrantsRequest
(*RemoveRoleGrantsResponse)(nil), // 21: controller.api.services.v1.RemoveRoleGrantsResponse
(*AddRoleGrantScopesRequest)(nil), // 22: controller.api.services.v1.AddRoleGrantScopesRequest
(*AddRoleGrantScopesResponse)(nil), // 23: controller.api.services.v1.AddRoleGrantScopesResponse
(*SetRoleGrantScopesRequest)(nil), // 24: controller.api.services.v1.SetRoleGrantScopesRequest
(*SetRoleGrantScopesResponse)(nil), // 25: controller.api.services.v1.SetRoleGrantScopesResponse
(*RemoveRoleGrantScopesRequest)(nil), // 26: controller.api.services.v1.RemoveRoleGrantScopesRequest
(*RemoveRoleGrantScopesResponse)(nil), // 27: controller.api.services.v1.RemoveRoleGrantScopesResponse
(*roles.Role)(nil), // 28: controller.api.resources.roles.v1.Role
(*fieldmaskpb.FieldMask)(nil), // 29: google.protobuf.FieldMask
}
var file_controller_api_services_v1_role_service_proto_depIdxs = []int32{
22, // 0: controller.api.services.v1.GetRoleResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 1: controller.api.services.v1.ListRolesResponse.items:type_name -> controller.api.resources.roles.v1.Role
22, // 2: controller.api.services.v1.CreateRoleRequest.item:type_name -> controller.api.resources.roles.v1.Role
22, // 3: controller.api.services.v1.CreateRoleResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 4: controller.api.services.v1.UpdateRoleRequest.item:type_name -> controller.api.resources.roles.v1.Role
23, // 5: controller.api.services.v1.UpdateRoleRequest.update_mask:type_name -> google.protobuf.FieldMask
22, // 6: controller.api.services.v1.UpdateRoleResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 7: controller.api.services.v1.AddRolePrincipalsResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 8: controller.api.services.v1.SetRolePrincipalsResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 9: controller.api.services.v1.RemoveRolePrincipalsResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 10: controller.api.services.v1.AddRoleGrantsResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 11: controller.api.services.v1.SetRoleGrantsResponse.item:type_name -> controller.api.resources.roles.v1.Role
22, // 12: controller.api.services.v1.RemoveRoleGrantsResponse.item:type_name -> controller.api.resources.roles.v1.Role
0, // 13: controller.api.services.v1.RoleService.GetRole:input_type -> controller.api.services.v1.GetRoleRequest
2, // 14: controller.api.services.v1.RoleService.ListRoles:input_type -> controller.api.services.v1.ListRolesRequest
4, // 15: controller.api.services.v1.RoleService.CreateRole:input_type -> controller.api.services.v1.CreateRoleRequest
6, // 16: controller.api.services.v1.RoleService.UpdateRole:input_type -> controller.api.services.v1.UpdateRoleRequest
8, // 17: controller.api.services.v1.RoleService.DeleteRole:input_type -> controller.api.services.v1.DeleteRoleRequest
10, // 18: controller.api.services.v1.RoleService.AddRolePrincipals:input_type -> controller.api.services.v1.AddRolePrincipalsRequest
12, // 19: controller.api.services.v1.RoleService.SetRolePrincipals:input_type -> controller.api.services.v1.SetRolePrincipalsRequest
14, // 20: controller.api.services.v1.RoleService.RemoveRolePrincipals:input_type -> controller.api.services.v1.RemoveRolePrincipalsRequest
16, // 21: controller.api.services.v1.RoleService.AddRoleGrants:input_type -> controller.api.services.v1.AddRoleGrantsRequest
18, // 22: controller.api.services.v1.RoleService.SetRoleGrants:input_type -> controller.api.services.v1.SetRoleGrantsRequest
20, // 23: controller.api.services.v1.RoleService.RemoveRoleGrants:input_type -> controller.api.services.v1.RemoveRoleGrantsRequest
1, // 24: controller.api.services.v1.RoleService.GetRole:output_type -> controller.api.services.v1.GetRoleResponse
3, // 25: controller.api.services.v1.RoleService.ListRoles:output_type -> controller.api.services.v1.ListRolesResponse
5, // 26: controller.api.services.v1.RoleService.CreateRole:output_type -> controller.api.services.v1.CreateRoleResponse
7, // 27: controller.api.services.v1.RoleService.UpdateRole:output_type -> controller.api.services.v1.UpdateRoleResponse
9, // 28: controller.api.services.v1.RoleService.DeleteRole:output_type -> controller.api.services.v1.DeleteRoleResponse
11, // 29: controller.api.services.v1.RoleService.AddRolePrincipals:output_type -> controller.api.services.v1.AddRolePrincipalsResponse
13, // 30: controller.api.services.v1.RoleService.SetRolePrincipals:output_type -> controller.api.services.v1.SetRolePrincipalsResponse
15, // 31: controller.api.services.v1.RoleService.RemoveRolePrincipals:output_type -> controller.api.services.v1.RemoveRolePrincipalsResponse
17, // 32: controller.api.services.v1.RoleService.AddRoleGrants:output_type -> controller.api.services.v1.AddRoleGrantsResponse
19, // 33: controller.api.services.v1.RoleService.SetRoleGrants:output_type -> controller.api.services.v1.SetRoleGrantsResponse
21, // 34: controller.api.services.v1.RoleService.RemoveRoleGrants:output_type -> controller.api.services.v1.RemoveRoleGrantsResponse
24, // [24:35] is the sub-list for method output_type
13, // [13:24] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
13, // [13:13] is the sub-list for extension extendee
0, // [0:13] is the sub-list for field type_name
28, // 0: controller.api.services.v1.GetRoleResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 1: controller.api.services.v1.ListRolesResponse.items:type_name -> controller.api.resources.roles.v1.Role
28, // 2: controller.api.services.v1.CreateRoleRequest.item:type_name -> controller.api.resources.roles.v1.Role
28, // 3: controller.api.services.v1.CreateRoleResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 4: controller.api.services.v1.UpdateRoleRequest.item:type_name -> controller.api.resources.roles.v1.Role
29, // 5: controller.api.services.v1.UpdateRoleRequest.update_mask:type_name -> google.protobuf.FieldMask
28, // 6: controller.api.services.v1.UpdateRoleResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 7: controller.api.services.v1.AddRolePrincipalsResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 8: controller.api.services.v1.SetRolePrincipalsResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 9: controller.api.services.v1.RemoveRolePrincipalsResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 10: controller.api.services.v1.AddRoleGrantsResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 11: controller.api.services.v1.SetRoleGrantsResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 12: controller.api.services.v1.RemoveRoleGrantsResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 13: controller.api.services.v1.AddRoleGrantScopesResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 14: controller.api.services.v1.SetRoleGrantScopesResponse.item:type_name -> controller.api.resources.roles.v1.Role
28, // 15: controller.api.services.v1.RemoveRoleGrantScopesResponse.item:type_name -> controller.api.resources.roles.v1.Role
0, // 16: controller.api.services.v1.RoleService.GetRole:input_type -> controller.api.services.v1.GetRoleRequest
2, // 17: controller.api.services.v1.RoleService.ListRoles:input_type -> controller.api.services.v1.ListRolesRequest
4, // 18: controller.api.services.v1.RoleService.CreateRole:input_type -> controller.api.services.v1.CreateRoleRequest
6, // 19: controller.api.services.v1.RoleService.UpdateRole:input_type -> controller.api.services.v1.UpdateRoleRequest
8, // 20: controller.api.services.v1.RoleService.DeleteRole:input_type -> controller.api.services.v1.DeleteRoleRequest
10, // 21: controller.api.services.v1.RoleService.AddRolePrincipals:input_type -> controller.api.services.v1.AddRolePrincipalsRequest
12, // 22: controller.api.services.v1.RoleService.SetRolePrincipals:input_type -> controller.api.services.v1.SetRolePrincipalsRequest
14, // 23: controller.api.services.v1.RoleService.RemoveRolePrincipals:input_type -> controller.api.services.v1.RemoveRolePrincipalsRequest
16, // 24: controller.api.services.v1.RoleService.AddRoleGrants:input_type -> controller.api.services.v1.AddRoleGrantsRequest
18, // 25: controller.api.services.v1.RoleService.SetRoleGrants:input_type -> controller.api.services.v1.SetRoleGrantsRequest
20, // 26: controller.api.services.v1.RoleService.RemoveRoleGrants:input_type -> controller.api.services.v1.RemoveRoleGrantsRequest
22, // 27: controller.api.services.v1.RoleService.AddRoleGrantScopes:input_type -> controller.api.services.v1.AddRoleGrantScopesRequest
24, // 28: controller.api.services.v1.RoleService.SetRoleGrantScopes:input_type -> controller.api.services.v1.SetRoleGrantScopesRequest
26, // 29: controller.api.services.v1.RoleService.RemoveRoleGrantScopes:input_type -> controller.api.services.v1.RemoveRoleGrantScopesRequest
1, // 30: controller.api.services.v1.RoleService.GetRole:output_type -> controller.api.services.v1.GetRoleResponse
3, // 31: controller.api.services.v1.RoleService.ListRoles:output_type -> controller.api.services.v1.ListRolesResponse
5, // 32: controller.api.services.v1.RoleService.CreateRole:output_type -> controller.api.services.v1.CreateRoleResponse
7, // 33: controller.api.services.v1.RoleService.UpdateRole:output_type -> controller.api.services.v1.UpdateRoleResponse
9, // 34: controller.api.services.v1.RoleService.DeleteRole:output_type -> controller.api.services.v1.DeleteRoleResponse
11, // 35: controller.api.services.v1.RoleService.AddRolePrincipals:output_type -> controller.api.services.v1.AddRolePrincipalsResponse
13, // 36: controller.api.services.v1.RoleService.SetRolePrincipals:output_type -> controller.api.services.v1.SetRolePrincipalsResponse
15, // 37: controller.api.services.v1.RoleService.RemoveRolePrincipals:output_type -> controller.api.services.v1.RemoveRolePrincipalsResponse
17, // 38: controller.api.services.v1.RoleService.AddRoleGrants:output_type -> controller.api.services.v1.AddRoleGrantsResponse
19, // 39: controller.api.services.v1.RoleService.SetRoleGrants:output_type -> controller.api.services.v1.SetRoleGrantsResponse
21, // 40: controller.api.services.v1.RoleService.RemoveRoleGrants:output_type -> controller.api.services.v1.RemoveRoleGrantsResponse
23, // 41: controller.api.services.v1.RoleService.AddRoleGrantScopes:output_type -> controller.api.services.v1.AddRoleGrantScopesResponse
25, // 42: controller.api.services.v1.RoleService.SetRoleGrantScopes:output_type -> controller.api.services.v1.SetRoleGrantScopesResponse
27, // 43: controller.api.services.v1.RoleService.RemoveRoleGrantScopes:output_type -> controller.api.services.v1.RemoveRoleGrantScopesResponse
30, // [30:44] is the sub-list for method output_type
16, // [16:30] is the sub-list for method input_type
16, // [16:16] is the sub-list for extension type_name
16, // [16:16] is the sub-list for extension extendee
0, // [0:16] is the sub-list for field type_name
}
func init() { file_controller_api_services_v1_role_service_proto_init() }
@ -1939,6 +2375,78 @@ func file_controller_api_services_v1_role_service_proto_init() {
return nil
}
}
file_controller_api_services_v1_role_service_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddRoleGrantScopesRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_role_service_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddRoleGrantScopesResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_role_service_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetRoleGrantScopesRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_role_service_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetRoleGrantScopesResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_role_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RemoveRoleGrantScopesRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_controller_api_services_v1_role_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RemoveRoleGrantScopesResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -1946,7 +2454,7 @@ func file_controller_api_services_v1_role_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_api_services_v1_role_service_proto_rawDesc,
NumEnums: 0,
NumMessages: 22,
NumMessages: 28,
NumExtensions: 0,
NumServices: 1,
},

@ -713,6 +713,210 @@ func local_request_RoleService_RemoveRoleGrants_0(ctx context.Context, marshaler
}
func request_RoleService_AddRoleGrantScopes_0(ctx context.Context, marshaler runtime.Marshaler, client RoleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddRoleGrantScopesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.AddRoleGrantScopes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_RoleService_AddRoleGrantScopes_0(ctx context.Context, marshaler runtime.Marshaler, server RoleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AddRoleGrantScopesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.AddRoleGrantScopes(ctx, &protoReq)
return msg, metadata, err
}
func request_RoleService_SetRoleGrantScopes_0(ctx context.Context, marshaler runtime.Marshaler, client RoleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SetRoleGrantScopesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.SetRoleGrantScopes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_RoleService_SetRoleGrantScopes_0(ctx context.Context, marshaler runtime.Marshaler, server RoleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SetRoleGrantScopesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.SetRoleGrantScopes(ctx, &protoReq)
return msg, metadata, err
}
func request_RoleService_RemoveRoleGrantScopes_0(ctx context.Context, marshaler runtime.Marshaler, client RoleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RemoveRoleGrantScopesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.RemoveRoleGrantScopes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_RoleService_RemoveRoleGrantScopes_0(ctx context.Context, marshaler runtime.Marshaler, server RoleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RemoveRoleGrantScopesRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := server.RemoveRoleGrantScopes(ctx, &protoReq)
return msg, metadata, err
}
// RegisterRoleServiceHandlerServer registers the http handlers for service RoleService to "mux".
// UnaryRPC :call RoleServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -994,6 +1198,81 @@ func RegisterRoleServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
})
mux.Handle("POST", pattern_RoleService_AddRoleGrantScopes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.RoleService/AddRoleGrantScopes", runtime.WithHTTPPathPattern("/v1/roles/{id}:add-grant-scopes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_RoleService_AddRoleGrantScopes_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_RoleService_AddRoleGrantScopes_0(annotatedContext, mux, outboundMarshaler, w, req, response_RoleService_AddRoleGrantScopes_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_RoleService_SetRoleGrantScopes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.RoleService/SetRoleGrantScopes", runtime.WithHTTPPathPattern("/v1/roles/{id}:set-grant-scopes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_RoleService_SetRoleGrantScopes_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_RoleService_SetRoleGrantScopes_0(annotatedContext, mux, outboundMarshaler, w, req, response_RoleService_SetRoleGrantScopes_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_RoleService_RemoveRoleGrantScopes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/controller.api.services.v1.RoleService/RemoveRoleGrantScopes", runtime.WithHTTPPathPattern("/v1/roles/{id}:remove-grant-scopes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_RoleService_RemoveRoleGrantScopes_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_RoleService_RemoveRoleGrantScopes_0(annotatedContext, mux, outboundMarshaler, w, req, response_RoleService_RemoveRoleGrantScopes_0{resp}, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -1277,6 +1556,72 @@ func RegisterRoleServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
})
mux.Handle("POST", pattern_RoleService_AddRoleGrantScopes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.RoleService/AddRoleGrantScopes", runtime.WithHTTPPathPattern("/v1/roles/{id}:add-grant-scopes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_RoleService_AddRoleGrantScopes_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_RoleService_AddRoleGrantScopes_0(annotatedContext, mux, outboundMarshaler, w, req, response_RoleService_AddRoleGrantScopes_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_RoleService_SetRoleGrantScopes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.RoleService/SetRoleGrantScopes", runtime.WithHTTPPathPattern("/v1/roles/{id}:set-grant-scopes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_RoleService_SetRoleGrantScopes_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_RoleService_SetRoleGrantScopes_0(annotatedContext, mux, outboundMarshaler, w, req, response_RoleService_SetRoleGrantScopes_0{resp}, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_RoleService_RemoveRoleGrantScopes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/controller.api.services.v1.RoleService/RemoveRoleGrantScopes", runtime.WithHTTPPathPattern("/v1/roles/{id}:remove-grant-scopes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_RoleService_RemoveRoleGrantScopes_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_RoleService_RemoveRoleGrantScopes_0(annotatedContext, mux, outboundMarshaler, w, req, response_RoleService_RemoveRoleGrantScopes_0{resp}, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -1361,6 +1706,33 @@ func (m response_RoleService_RemoveRoleGrants_0) XXX_ResponseBody() interface{}
return response.Item
}
type response_RoleService_AddRoleGrantScopes_0 struct {
proto.Message
}
func (m response_RoleService_AddRoleGrantScopes_0) XXX_ResponseBody() interface{} {
response := m.Message.(*AddRoleGrantScopesResponse)
return response.Item
}
type response_RoleService_SetRoleGrantScopes_0 struct {
proto.Message
}
func (m response_RoleService_SetRoleGrantScopes_0) XXX_ResponseBody() interface{} {
response := m.Message.(*SetRoleGrantScopesResponse)
return response.Item
}
type response_RoleService_RemoveRoleGrantScopes_0 struct {
proto.Message
}
func (m response_RoleService_RemoveRoleGrantScopes_0) XXX_ResponseBody() interface{} {
response := m.Message.(*RemoveRoleGrantScopesResponse)
return response.Item
}
var (
pattern_RoleService_GetRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, ""))
@ -1383,6 +1755,12 @@ var (
pattern_RoleService_SetRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "set-grants"))
pattern_RoleService_RemoveRoleGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "remove-grants"))
pattern_RoleService_AddRoleGrantScopes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "add-grant-scopes"))
pattern_RoleService_SetRoleGrantScopes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "set-grant-scopes"))
pattern_RoleService_RemoveRoleGrantScopes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "roles", "id"}, "remove-grant-scopes"))
)
var (
@ -1407,4 +1785,10 @@ var (
forward_RoleService_SetRoleGrants_0 = runtime.ForwardResponseMessage
forward_RoleService_RemoveRoleGrants_0 = runtime.ForwardResponseMessage
forward_RoleService_AddRoleGrantScopes_0 = runtime.ForwardResponseMessage
forward_RoleService_SetRoleGrantScopes_0 = runtime.ForwardResponseMessage
forward_RoleService_RemoveRoleGrantScopes_0 = runtime.ForwardResponseMessage
)

@ -22,17 +22,20 @@ import (
const _ = grpc.SupportPackageIsVersion7
const (
RoleService_GetRole_FullMethodName = "/controller.api.services.v1.RoleService/GetRole"
RoleService_ListRoles_FullMethodName = "/controller.api.services.v1.RoleService/ListRoles"
RoleService_CreateRole_FullMethodName = "/controller.api.services.v1.RoleService/CreateRole"
RoleService_UpdateRole_FullMethodName = "/controller.api.services.v1.RoleService/UpdateRole"
RoleService_DeleteRole_FullMethodName = "/controller.api.services.v1.RoleService/DeleteRole"
RoleService_AddRolePrincipals_FullMethodName = "/controller.api.services.v1.RoleService/AddRolePrincipals"
RoleService_SetRolePrincipals_FullMethodName = "/controller.api.services.v1.RoleService/SetRolePrincipals"
RoleService_RemoveRolePrincipals_FullMethodName = "/controller.api.services.v1.RoleService/RemoveRolePrincipals"
RoleService_AddRoleGrants_FullMethodName = "/controller.api.services.v1.RoleService/AddRoleGrants"
RoleService_SetRoleGrants_FullMethodName = "/controller.api.services.v1.RoleService/SetRoleGrants"
RoleService_RemoveRoleGrants_FullMethodName = "/controller.api.services.v1.RoleService/RemoveRoleGrants"
RoleService_GetRole_FullMethodName = "/controller.api.services.v1.RoleService/GetRole"
RoleService_ListRoles_FullMethodName = "/controller.api.services.v1.RoleService/ListRoles"
RoleService_CreateRole_FullMethodName = "/controller.api.services.v1.RoleService/CreateRole"
RoleService_UpdateRole_FullMethodName = "/controller.api.services.v1.RoleService/UpdateRole"
RoleService_DeleteRole_FullMethodName = "/controller.api.services.v1.RoleService/DeleteRole"
RoleService_AddRolePrincipals_FullMethodName = "/controller.api.services.v1.RoleService/AddRolePrincipals"
RoleService_SetRolePrincipals_FullMethodName = "/controller.api.services.v1.RoleService/SetRolePrincipals"
RoleService_RemoveRolePrincipals_FullMethodName = "/controller.api.services.v1.RoleService/RemoveRolePrincipals"
RoleService_AddRoleGrants_FullMethodName = "/controller.api.services.v1.RoleService/AddRoleGrants"
RoleService_SetRoleGrants_FullMethodName = "/controller.api.services.v1.RoleService/SetRoleGrants"
RoleService_RemoveRoleGrants_FullMethodName = "/controller.api.services.v1.RoleService/RemoveRoleGrants"
RoleService_AddRoleGrantScopes_FullMethodName = "/controller.api.services.v1.RoleService/AddRoleGrantScopes"
RoleService_SetRoleGrantScopes_FullMethodName = "/controller.api.services.v1.RoleService/SetRoleGrantScopes"
RoleService_RemoveRoleGrantScopes_FullMethodName = "/controller.api.services.v1.RoleService/RemoveRoleGrantScopes"
)
// RoleServiceClient is the client API for RoleService service.
@ -100,6 +103,22 @@ type RoleServiceClient interface {
// grants will be removed. If missing, malformed, or references a non-existing
// resource, an error is returned.
RemoveRoleGrants(ctx context.Context, in *RemoveRoleGrantsRequest, opts ...grpc.CallOption) (*RemoveRoleGrantsResponse, error)
// AddRoleGrantScopes adds grants scopes to a Role. The provided request must
// include the Role id which the grant scopes will be added to. An error is
// returned if the provided id is malformed or references a non-existing
// resource.
AddRoleGrantScopes(ctx context.Context, in *AddRoleGrantScopesRequest, opts ...grpc.CallOption) (*AddRoleGrantScopesResponse, error)
// SetRoleGrants sets the Role's grant scopes. Any existing grant scopes on
// the Role are deleted if they are not included in this request. The provided
// request must include the Role ID on which the grants will be set. If
// missing, malformed, or referencing a non-existing resource, an error is
// returned.
SetRoleGrantScopes(ctx context.Context, in *SetRoleGrantScopesRequest, opts ...grpc.CallOption) (*SetRoleGrantScopesResponse, error)
// RemoveRoleGrantScopes removes the grant scopes from the specified Role. The
// provided request must include the Role IDs from which the grants will be
// removed. If missing, malformed, or references a non-existing resource, an
// error is returned.
RemoveRoleGrantScopes(ctx context.Context, in *RemoveRoleGrantScopesRequest, opts ...grpc.CallOption) (*RemoveRoleGrantScopesResponse, error)
}
type roleServiceClient struct {
@ -209,6 +228,33 @@ func (c *roleServiceClient) RemoveRoleGrants(ctx context.Context, in *RemoveRole
return out, nil
}
func (c *roleServiceClient) AddRoleGrantScopes(ctx context.Context, in *AddRoleGrantScopesRequest, opts ...grpc.CallOption) (*AddRoleGrantScopesResponse, error) {
out := new(AddRoleGrantScopesResponse)
err := c.cc.Invoke(ctx, RoleService_AddRoleGrantScopes_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *roleServiceClient) SetRoleGrantScopes(ctx context.Context, in *SetRoleGrantScopesRequest, opts ...grpc.CallOption) (*SetRoleGrantScopesResponse, error) {
out := new(SetRoleGrantScopesResponse)
err := c.cc.Invoke(ctx, RoleService_SetRoleGrantScopes_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *roleServiceClient) RemoveRoleGrantScopes(ctx context.Context, in *RemoveRoleGrantScopesRequest, opts ...grpc.CallOption) (*RemoveRoleGrantScopesResponse, error) {
out := new(RemoveRoleGrantScopesResponse)
err := c.cc.Invoke(ctx, RoleService_RemoveRoleGrantScopes_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RoleServiceServer is the server API for RoleService service.
// All implementations must embed UnimplementedRoleServiceServer
// for forward compatibility
@ -274,6 +320,22 @@ type RoleServiceServer interface {
// grants will be removed. If missing, malformed, or references a non-existing
// resource, an error is returned.
RemoveRoleGrants(context.Context, *RemoveRoleGrantsRequest) (*RemoveRoleGrantsResponse, error)
// AddRoleGrantScopes adds grants scopes to a Role. The provided request must
// include the Role id which the grant scopes will be added to. An error is
// returned if the provided id is malformed or references a non-existing
// resource.
AddRoleGrantScopes(context.Context, *AddRoleGrantScopesRequest) (*AddRoleGrantScopesResponse, error)
// SetRoleGrants sets the Role's grant scopes. Any existing grant scopes on
// the Role are deleted if they are not included in this request. The provided
// request must include the Role ID on which the grants will be set. If
// missing, malformed, or referencing a non-existing resource, an error is
// returned.
SetRoleGrantScopes(context.Context, *SetRoleGrantScopesRequest) (*SetRoleGrantScopesResponse, error)
// RemoveRoleGrantScopes removes the grant scopes from the specified Role. The
// provided request must include the Role IDs from which the grants will be
// removed. If missing, malformed, or references a non-existing resource, an
// error is returned.
RemoveRoleGrantScopes(context.Context, *RemoveRoleGrantScopesRequest) (*RemoveRoleGrantScopesResponse, error)
mustEmbedUnimplementedRoleServiceServer()
}
@ -314,6 +376,15 @@ func (UnimplementedRoleServiceServer) SetRoleGrants(context.Context, *SetRoleGra
func (UnimplementedRoleServiceServer) RemoveRoleGrants(context.Context, *RemoveRoleGrantsRequest) (*RemoveRoleGrantsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveRoleGrants not implemented")
}
func (UnimplementedRoleServiceServer) AddRoleGrantScopes(context.Context, *AddRoleGrantScopesRequest) (*AddRoleGrantScopesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddRoleGrantScopes not implemented")
}
func (UnimplementedRoleServiceServer) SetRoleGrantScopes(context.Context, *SetRoleGrantScopesRequest) (*SetRoleGrantScopesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetRoleGrantScopes not implemented")
}
func (UnimplementedRoleServiceServer) RemoveRoleGrantScopes(context.Context, *RemoveRoleGrantScopesRequest) (*RemoveRoleGrantScopesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveRoleGrantScopes not implemented")
}
func (UnimplementedRoleServiceServer) mustEmbedUnimplementedRoleServiceServer() {}
// UnsafeRoleServiceServer may be embedded to opt out of forward compatibility for this service.
@ -525,6 +596,60 @@ func _RoleService_RemoveRoleGrants_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _RoleService_AddRoleGrantScopes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddRoleGrantScopesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RoleServiceServer).AddRoleGrantScopes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RoleService_AddRoleGrantScopes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RoleServiceServer).AddRoleGrantScopes(ctx, req.(*AddRoleGrantScopesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _RoleService_SetRoleGrantScopes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetRoleGrantScopesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RoleServiceServer).SetRoleGrantScopes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RoleService_SetRoleGrantScopes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RoleServiceServer).SetRoleGrantScopes(ctx, req.(*SetRoleGrantScopesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _RoleService_RemoveRoleGrantScopes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveRoleGrantScopesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RoleServiceServer).RemoveRoleGrantScopes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RoleService_RemoveRoleGrantScopes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RoleServiceServer).RemoveRoleGrantScopes(ctx, req.(*RemoveRoleGrantScopesRequest))
}
return interceptor(ctx, in, info, handler)
}
// RoleService_ServiceDesc is the grpc.ServiceDesc for RoleService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -576,6 +701,18 @@ var RoleService_ServiceDesc = grpc.ServiceDesc{
MethodName: "RemoveRoleGrants",
Handler: _RoleService_RemoveRoleGrants_Handler,
},
{
MethodName: "AddRoleGrantScopes",
Handler: _RoleService_AddRoleGrantScopes_Handler,
},
{
MethodName: "SetRoleGrantScopes",
Handler: _RoleService_SetRoleGrantScopes_Handler,
},
{
MethodName: "RemoveRoleGrantScopes",
Handler: _RoleService_RemoveRoleGrantScopes_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "controller/api/services/v1/role_service.proto",

@ -78,7 +78,7 @@ func (g *Group) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType,
return nil
}
func (u *Group) validScopeTypes() []scope.Type {
func (g *Group) validScopeTypes() []scope.Type {
return []scope.Type{scope.Global, scope.Org, scope.Project}
}

@ -6,6 +6,7 @@ package iam
import (
"io"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/pagination"
)
@ -13,7 +14,9 @@ import (
func getOpts(opt ...Option) options {
opts := getDefaultOptions()
for _, o := range opt {
o(&opts)
if o != nil {
o(&opts)
}
}
return opts
}
@ -27,7 +30,8 @@ type options struct {
withName string
withDescription string
withLimit int
withGrantScopeId string
withGrantScopeId *string
withGrantScopeIds []string
withSkipVetForWrite bool
withDisassociate bool
withSkipAdminRoleCreation bool
@ -36,6 +40,8 @@ type options struct {
withRandomReader io.Reader
withAccountIds []string
withPrimaryAuthMethodId string
withReader db.Reader
withWriter db.Writer
withStartPageAfterItem pagination.Item
}
@ -45,7 +51,6 @@ func getDefaultOptions() options {
withName: "",
withDescription: "",
withLimit: 0,
withGrantScopeId: "",
withSkipVetForWrite: false,
}
}
@ -84,7 +89,20 @@ func WithLimit(limit int) Option {
// roles.
func WithGrantScopeId(id string) Option {
return func(o *options) {
o.withGrantScopeId = id
if o.withGrantScopeId == nil {
o.withGrantScopeId = new(string)
}
*o.withGrantScopeId = id
}
}
// WithGrantScopeIds provides an option to specify the scope ID for grants in
// roles. In most tests this is likely the option to use, however, for tests
// that call repo functions instead of test functions the other option is still
// correct to specify a grant scope at creation time.
func WithGrantScopeIds(ids []string) Option {
return func(o *options) {
o.withGrantScopeIds = ids
}
}
@ -150,6 +168,18 @@ func WithPrimaryAuthMethodId(id string) Option {
}
}
// WithReaderWriter allows the caller to pass an inflight transaction to be used
// for all database operations. If WithReaderWriter(...) is used, then the
// caller is responsible for managing the transaction. The purpose of the
// WithReaderWriter(...) option is to allow the caller to create the scope and
// all of its keys in the same transaction.
func WithReaderWriter(r db.Reader, w db.Writer) Option {
return func(o *options) {
o.withReader = r
o.withWriter = w
}
}
// WithStartPageAfterItem is used to paginate over the results.
// The next page will start after the provided item.
func WithStartPageAfterItem(item pagination.Item) Option {

@ -63,9 +63,11 @@ func Test_GetOpts(t *testing.T) {
})
t.Run("WithGrantScopeId", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithGrantScopeId("o_1234"))
str := "o_1234"
opts := getOpts(WithGrantScopeId(str))
testOpts := getDefaultOptions()
testOpts.withGrantScopeId = "o_1234"
testOpts.withGrantScopeId = &str
assert.Equal(opts, testOpts)
})
t.Run("WithDisassociate", func(t *testing.T) {

@ -112,70 +112,131 @@ const (
order by action, member_id;
`
grantsQuery = `
with
users (id) as (
select public_id
from iam_user
%s -- anonUser || authUser
),
user_groups (id) as (
select group_id
from iam_group_member_user
where member_id in (select id from users)
),
user_accounts (id) as (
select public_id
from auth_account
where iam_user_id in (select id from users)
),
user_managed_groups (id) as (
select managed_group_id
from auth_managed_group_member_account
where member_id in (select id from user_accounts)
),
managed_group_roles (role_id) as (
select role_id
from iam_managed_group_role
where principal_id in (select id from user_managed_groups)
),
group_roles (role_id) as (
select role_id
from iam_group_role
where principal_id in (select id from user_groups)
),
user_roles (role_id) as (
select role_id
from iam_user_role
where principal_id in (select id from users)
),
user_group_roles (role_id) as (
select role_id
from group_roles
union
select role_id
from user_roles
union
select role_id
from managed_group_roles
),
roles (role_id, grant_scope_id) as (
select iam_role.public_id,
iam_role.grant_scope_id
from iam_role
where public_id in (select role_id from user_group_roles)
),
final (role_id, role_scope, role_grant) as (
select roles.role_id,
roles.grant_scope_id,
iam_role_grant.canonical_grant
from roles
inner join iam_role_grant
on roles.role_id = iam_role_grant.role_id
-- only retrieves roles with a grant! there can be roles that don't have grants (it's a valid state in boundary)
)
select role_id as role_id, role_scope as scope_id, role_grant as grant from final;
`
grantsForUserQuery = `
with
users (id) as (
select public_id
from iam_user
%s -- anonUser || authUser
),
user_groups (id) as (
select group_id
from iam_group_member_user
where member_id in (select id from users)
),
user_accounts (id) as (
select public_id
from auth_account
where iam_user_id in (select id from users)
),
user_managed_groups (id) as (
select managed_group_id
from auth_managed_group_member_account
where member_id in (select id from user_accounts)
),
managed_group_roles (role_id) as (
select role_id
from iam_managed_group_role
where principal_id in (select id from user_managed_groups)
),
group_roles (role_id) as (
select role_id
from iam_group_role
where principal_id in (select id from user_groups)
),
user_roles (role_id) as (
select role_id
from iam_user_role
where principal_id in (select id from users)
),
user_group_roles (role_id) as (
select role_id
from group_roles
union
select role_id
from user_roles
union
select role_id
from managed_group_roles
),
roles (role_id, role_scope_id) as (
select iam_role.public_id,
iam_role.scope_id
from iam_role
where public_id in (select role_id from user_group_roles)
),
role_grant_scopes (role_id, role_scope_id, grant_scope_id) as (
select roles.role_id,
roles.role_scope_id,
iam_role_grant_scope.scope_id_or_special
from roles
inner join iam_role_grant_scope
on roles.role_id = iam_role_grant_scope.role_id
),
-- For all role_ids with a special scope_id of 'descendants', we want to
-- perform a cartesian product to pair the role_id with all non-global scopes.
descendant_grant_scopes (role_id, grant_scope_id) as (
select role_grant_scopes.role_id as role_id,
iam_scope.public_id as grant_scope_id
from role_grant_scopes,
iam_scope
where iam_scope.public_id != 'global'
and role_grant_scopes.grant_scope_id = 'descendants'
),
children_grant_scopes (role_id, grant_scope_id) as (
select role_grant_scopes.role_id as role_id,
iam_scope.public_id as grant_scope_id
from role_grant_scopes
join iam_scope
on iam_scope.parent_id = role_grant_scopes.role_scope_id
where role_grant_scopes.grant_scope_id = 'children'
),
this_grant_scopes (role_id, grant_scope_id) as (
select role_grant_scopes.role_id as role_id,
role_grant_scopes.role_scope_id as grant_scope_id
from role_grant_scopes
where role_grant_scopes.grant_scope_id = 'this'
),
direct_grant_scopes (role_id, grant_scope_id) as (
select role_grant_scopes.role_id as role_id,
role_grant_scopes.grant_scope_id as grant_scope_id
from role_grant_scopes
where role_grant_scopes.grant_scope_id not in ('descendants', 'children', 'this')
),
grant_scopes (role_id, grant_scope_id) as (
select
role_id as role_id,
grant_scope_id as grant_scope_id
from descendant_grant_scopes
union
select
role_id as role_id,
grant_scope_id as grant_scope_id
from children_grant_scopes
union
select
role_id as role_id,
grant_scope_id as grant_scope_id
from this_grant_scopes
union
select
role_id as role_id,
grant_scope_id as grant_scope_id
from direct_grant_scopes
),
final (role_id, grant_scope_id, canonical_grant) as (
select grant_scopes.role_id,
grant_scopes.grant_scope_id,
iam_role_grant.canonical_grant
from grant_scopes
join iam_role_grant
on grant_scopes.role_id = iam_role_grant.role_id
)
select role_id as role_id,
grant_scope_id as scope_id,
canonical_grant as grant
from final;
`
estimateCountRoles = `
select reltuples::bigint as estimate from pg_class where oid in ('iam_role'::regclass)

@ -55,6 +55,8 @@ func NewRepository(ctx context.Context, r db.Reader, w db.Writer, kms *kms.Kms,
// list will return a listing of resources and honor the WithLimit option or the
// repo defaultLimit
//
// Supported options: WithLimit, WithReaderWriter
func (r *Repository) list(ctx context.Context, resources any, where string, args []any, opt ...Option) error {
opts := getOpts(opt...)
limit := r.defaultLimit
@ -62,11 +64,17 @@ func (r *Repository) list(ctx context.Context, resources any, where string, args
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
return r.reader.SearchWhere(ctx, resources, where, args, db.WithLimit(limit))
reader := r.reader
if opts.withReader != nil {
reader = opts.withReader
}
return reader.SearchWhere(ctx, resources, where, args, db.WithLimit(limit))
}
// create will create a new iam resource in the db repository with an oplog entry
func (r *Repository) create(ctx context.Context, resource Resource, _ ...Option) (Resource, error) {
//
// Supported options: WithReaderWriter
func (r *Repository) create(ctx context.Context, resource Resource, opt ...Option) (Resource, error) {
const op = "iam.(Repository).create"
if resource == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing resource")
@ -90,24 +98,20 @@ func (r *Repository) create(ctx context.Context, resource Resource, _ ...Option)
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var returnedResource any
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
returnedResource = resourceCloner.Clone()
err := w.Create(
ctx,
returnedResource,
db.WithOplog(oplogWrapper, metadata),
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
returnedResource := resourceCloner.Clone()
opts := getOpts(opt...)
if opts.withWriter != nil {
err = opts.withWriter.Create(ctx, returnedResource, db.WithOplog(oplogWrapper, metadata))
} else {
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
return w.Create(ctx, returnedResource, db.WithOplog(oplogWrapper, metadata))
},
)
}
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
@ -115,6 +119,8 @@ func (r *Repository) create(ctx context.Context, resource Resource, _ ...Option)
}
// update will update an iam resource in the db repository with an oplog entry
//
// Supported options: WithReaderWriter
func (r *Repository) update(ctx context.Context, resource Resource, version uint32, fieldMaskPaths []string, setToNullPaths []string, opt ...Option) (Resource, int, error) {
const op = "iam.(Repository).update"
if version == 0 {
@ -141,6 +147,18 @@ func (r *Repository) update(ctx context.Context, resource Resource, version uint
dbOpts = append(dbOpts, db.WithSkipVetForWrite(true))
}
reader := r.reader
writer := r.writer
needFreshReaderWriter := true
if opts.withReader != nil && opts.withWriter != nil {
reader = opts.withReader
writer = opts.withWriter
if !writer.IsTx(ctx) {
return nil, db.NoRowsAffected, errors.New(ctx, errors.Internal, op, "writer is not in transaction")
}
needFreshReaderWriter = false
}
var scope *Scope
switch t := resource.(type) {
case *Scope:
@ -159,29 +177,35 @@ func (r *Repository) update(ctx context.Context, resource Resource, version uint
var rowsUpdated int
var returnedResource any
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
returnedResource = resourceCloner.Clone()
rowsUpdated, err = w.Update(
ctx,
returnedResource,
fieldMaskPaths,
setToNullPaths,
dbOpts...,
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated > 1 {
// return err, which will result in a rollback of the update
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
}
return nil
},
)
txFunc := func(rdr db.Reader, wtr db.Writer) error {
returnedResource = resourceCloner.Clone()
rowsUpdated, err = wtr.Update(
ctx,
returnedResource,
fieldMaskPaths,
setToNullPaths,
dbOpts...,
)
if err != nil {
return errors.Wrap(ctx, err, op)
}
if rowsUpdated > 1 {
// return err, which will result in a rollback of the update
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
}
return nil
}
if !needFreshReaderWriter {
err = txFunc(reader, writer)
} else {
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
txFunc,
)
}
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
}

@ -0,0 +1,385 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package iam
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/oplog"
)
// AddRoleGrantScopes will add role grant scopes associated with the role ID in
// the repository. No options are currently supported. Zero is not a valid value
// for the WithVersion option and will return an error.
func (r *Repository) AddRoleGrantScopes(ctx context.Context, roleId string, roleVersion uint32, grantScopes []string, _ ...Option) ([]*RoleGrantScope, error) {
const op = "iam.(Repository).AddRoleGrantScopes"
if roleId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role id")
}
if len(grantScopes) == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing grant scopes")
}
if roleVersion == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
role := allocRole()
role.PublicId = roleId
scope, err := role.GetScope(ctx, r.reader)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId)))
}
role.ScopeId = scope.PublicId
// Find existing grant scopes
roleGrantScopes := []*RoleGrantScope{}
if err := r.reader.SearchWhere(ctx, &roleGrantScopes, "role_id = ?", []any{roleId}); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to search for grant scopes"))
}
found := map[string]*RoleGrantScope{}
for _, rgs := range roleGrantScopes {
found[rgs.ScopeIdOrSpecial] = rgs
}
// Check incoming grant scopes to see if they exist so we don't try to add
// again and cause an integrity error
addRoleGrantScopes := make([]any, 0, len(grantScopes))
for _, grantScope := range grantScopes {
if _, ok := found[grantScope]; !ok {
addRoleGrantScopes = append(addRoleGrantScopes, grantScope)
}
}
newRoleGrantScopes := make([]any, 0, len(addRoleGrantScopes))
for _, grantScope := range grantScopes {
roleGrantScope, err := NewRoleGrantScope(ctx, role.GetPublicId(), grantScope)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory role grant scope"))
}
newRoleGrantScopes = append(newRoleGrantScopes, roleGrantScope)
}
oplogWrapper, err := r.kms.GetWrapper(ctx, role.GetScopeId(), kms.KeyPurposeOplog)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
msgs := make([]*oplog.Message, 0, 2)
roleTicket, err := w.GetTicket(ctx, &role)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
// We need to update the role version as that's the aggregate
updatedRole := allocRole()
updatedRole.PublicId = role.GetPublicId()
updatedRole.Version = uint32(roleVersion + 1)
var roleOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update role version"))
}
if rowsUpdated != 1 {
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated))
}
msgs = append(msgs, &roleOplogMsg)
roleGrantScopesOplogMsgs := make([]*oplog.Message, 0, len(newRoleGrantScopes))
if err := w.CreateItems(ctx, newRoleGrantScopes, db.NewOplogMsgs(&roleGrantScopesOplogMsgs)); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add grants"))
}
msgs = append(msgs, roleGrantScopesOplogMsgs...)
metadata := oplog.Metadata{
"op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()},
"scope-id": []string{scope.PublicId},
"scope-type": []string{scope.Type},
"resource-public-id": []string{role.PublicId},
}
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
}
return nil
},
)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
roleGrantScopes = make([]*RoleGrantScope, 0, len(newRoleGrantScopes))
for _, grantScope := range newRoleGrantScopes {
roleGrantScopes = append(roleGrantScopes, grantScope.(*RoleGrantScope))
}
return roleGrantScopes, nil
}
// DeleteRoleGrantScopes will delete role grant scopes associated with the role ID in
// the repository. No options are currently supported. Zero is not a valid value
// for the WithVersion option and will return an error.
func (r *Repository) DeleteRoleGrantScopes(ctx context.Context, roleId string, roleVersion uint32, grantScopes []string, _ ...Option) (int, error) {
const op = "iam.(Repository).DeleteRoleGrantScopes"
if roleId == "" {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role id")
}
if len(grantScopes) == 0 {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing grant scopes")
}
if roleVersion == 0 {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
role := allocRole()
role.PublicId = roleId
scope, err := role.GetScope(ctx, r.reader)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId)))
}
role.ScopeId = scope.PublicId
oplogWrapper, err := r.kms.GetWrapper(ctx, role.GetScopeId(), kms.KeyPurposeOplog)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var totalRowsDeleted int
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, w db.Writer) error {
msgs := make([]*oplog.Message, 0, 2)
roleTicket, err := w.GetTicket(ctx, &role)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
// We need to update the role version as that's the aggregate
updatedRole := allocRole()
updatedRole.PublicId = role.GetPublicId()
updatedRole.Version = uint32(roleVersion + 1)
var roleOplogMsg oplog.Message
rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update role version"))
}
if rowsUpdated != 1 {
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated))
}
msgs = append(msgs, &roleOplogMsg)
deleteRoleGrantScopes := make([]any, 0, len(grantScopes))
for _, grantScope := range grantScopes {
roleGrantScope, err := NewRoleGrantScope(ctx, roleId, grantScope)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory role grant scope"))
}
deleteRoleGrantScopes = append(deleteRoleGrantScopes, roleGrantScope)
}
if len(deleteRoleGrantScopes) == 0 {
return nil
}
roleGrantScopesOplogMsgs := make([]*oplog.Message, 0, len(deleteRoleGrantScopes))
rowsDeleted, err := w.DeleteItems(ctx, deleteRoleGrantScopes, db.NewOplogMsgs(&roleGrantScopesOplogMsgs))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add grants"))
}
totalRowsDeleted = rowsDeleted
msgs = append(msgs, roleGrantScopesOplogMsgs...)
metadata := oplog.Metadata{
"op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()},
"scope-id": []string{scope.PublicId},
"scope-type": []string{scope.Type},
"resource-public-id": []string{role.PublicId},
}
if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
}
return nil
},
)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op)
}
return totalRowsDeleted, nil
}
// SetRoleGrantScopes sets grant scopes on a role (roleId). The role's current
// db version
// must match the roleVersion or an error will be returned. Zero is not a valid
// value for the WithVersion option and will return an error.
func (r *Repository) SetRoleGrantScopes(ctx context.Context, roleId string, roleVersion uint32, grantScopes []string, opt ...Option) ([]*RoleGrantScope, int, error) {
const op = "iam.(Repository).SetRoleGrantScopes"
if roleId == "" {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role id")
}
if roleVersion == 0 {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
}
// Explicitly set to zero clears, but treat nil as a mistake
if grantScopes == nil {
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing grants")
}
reader := r.reader
writer := r.writer
needFreshReaderWriter := true
opts := getOpts(opt...)
if opts.withReader != nil && opts.withWriter != nil {
reader = opts.withReader
writer = opts.withWriter
needFreshReaderWriter = false
}
role := allocRole()
role.PublicId = roleId
// NOTE: Set calculation can safely take place out of the transaction since
// we are using roleVersion to ensure that we end up operating on the same
// set of data from this query to the final set in the transaction function
// Find existing grant scopes
roleGrantScopes := []*RoleGrantScope{}
if err := reader.SearchWhere(ctx, &roleGrantScopes, "role_id = ?", []any{roleId}); err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to search for grant scopes"))
}
found := map[string]*RoleGrantScope{}
for _, rgs := range roleGrantScopes {
found[rgs.ScopeIdOrSpecial] = rgs
}
// Check incoming grant scopes to see if they exist and if so act appropriately
currentRoleGrantScopes := make([]*RoleGrantScope, 0, len(grantScopes)+len(found))
addRoleGrantScopes := make([]any, 0, len(grantScopes))
deleteRoleGrantScopes := make([]any, 0, len(grantScopes))
for _, grantScope := range grantScopes {
rgs, ok := found[grantScope]
if ok {
// If we have an exact match, do nothing, we want to keep
// it, but remove from found
currentRoleGrantScopes = append(currentRoleGrantScopes, rgs)
delete(found, grantScope)
continue
}
// Not found, so add
rgs, err := NewRoleGrantScope(ctx, roleId, grantScope)
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory role grant"))
}
addRoleGrantScopes = append(addRoleGrantScopes, rgs)
currentRoleGrantScopes = append(currentRoleGrantScopes, rgs)
}
if len(found) > 0 {
for _, rgs := range found {
deleteRoleGrantScopes = append(deleteRoleGrantScopes, rgs)
}
}
if len(addRoleGrantScopes) == 0 && len(deleteRoleGrantScopes) == 0 {
return currentRoleGrantScopes, db.NoRowsAffected, nil
}
scope, err := role.GetScope(ctx, reader)
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId)))
}
oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog)
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
}
var totalRowsDeleted int
currentRoleGrantScopes = currentRoleGrantScopes[:0]
txFunc := func(rdr db.Reader, wtr db.Writer) error {
msgs := make([]*oplog.Message, 0, 2)
roleTicket, err := wtr.GetTicket(ctx, &role)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
}
updatedRole := allocRole()
updatedRole.PublicId = roleId
updatedRole.Version = roleVersion + 1
var roleOplogMsg oplog.Message
rowsUpdated, err := wtr.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to update role version"))
}
if rowsUpdated != 1 {
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated))
}
msgs = append(msgs, &roleOplogMsg)
// Anything we didn't take out of found needs to be removed. This needs
// to come before writing in new ones because otherwise we may hit some
// validation issues.
if len(deleteRoleGrantScopes) > 0 {
roleGrantScopeOplogMsgs := make([]*oplog.Message, 0, len(deleteRoleGrantScopes))
rowsDeleted, err := wtr.DeleteItems(ctx, deleteRoleGrantScopes, db.NewOplogMsgs(&roleGrantScopeOplogMsgs))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to delete role grant scope"))
}
if rowsDeleted != len(deleteRoleGrantScopes) {
return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("role grant scope deleted %d did not match request for %d", rowsDeleted, len(deleteRoleGrantScopes)))
}
totalRowsDeleted = rowsDeleted
msgs = append(msgs, roleGrantScopeOplogMsgs...)
}
// Write the new ones in
if len(addRoleGrantScopes) > 0 {
roleGrantScopeOplogMsgs := make([]*oplog.Message, 0, len(addRoleGrantScopes))
if err := wtr.CreateItems(ctx, addRoleGrantScopes, db.NewOplogMsgs(&roleGrantScopeOplogMsgs)); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add grant scope during set"))
}
msgs = append(msgs, roleGrantScopeOplogMsgs...)
}
metadata := oplog.Metadata{
"op-type": []string{oplog.OpType_OP_TYPE_DELETE.String(), oplog.OpType_OP_TYPE_CREATE.String()},
"scope-id": []string{scope.PublicId},
"scope-type": []string{scope.Type},
"resource-public-id": []string{roleId},
}
if err := wtr.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog"))
}
if err := r.list(ctx, &currentRoleGrantScopes, "role_id = ?", []any{roleId}, opt...); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve current role grant scopes after set"))
}
return nil
}
if !needFreshReaderWriter {
err = txFunc(reader, writer)
} else {
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
txFunc,
)
}
if err != nil {
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
}
return currentRoleGrantScopes, totalRowsDeleted, nil
}

@ -135,7 +135,7 @@ func TestRepository_AddPrincipalRoles(t *testing.T) {
var userIds, groupIds []string
for _, roleId := range []string{orgRole.PublicId, projRole.PublicId} {
origRole, _, _, err := repo.LookupRole(context.Background(), roleId)
origRole, _, _, _, err := repo.LookupRole(context.Background(), roleId)
require.NoError(err)
if tt.args.wantUserIds {
@ -187,7 +187,7 @@ func TestRepository_AddPrincipalRoles(t *testing.T) {
assert.Equal(gotPrincipal[principalId].GetType(), r.GetType())
}
r, _, _, err := repo.LookupRole(context.Background(), roleId)
r, _, _, _, err := repo.LookupRole(context.Background(), roleId)
require.NoError(err)
assert.Equal(tt.args.roleVersion+1, r.Version)
assert.Equal(origRole.Version, r.Version-1)
@ -646,7 +646,7 @@ func TestRepository_SetPrincipalRoles(t *testing.T) {
if tt.args.addToOrigGrps {
tt.args.groupIds = append(tt.args.groupIds, origGrps...)
}
origRole, _, _, err := repo.LookupRole(context.Background(), tt.args.role.PublicId)
origRole, _, _, _, err := repo.LookupRole(context.Background(), tt.args.role.PublicId)
require.NoError(err)
principalIds := append(tt.args.userIds, tt.args.groupIds...)
@ -671,7 +671,7 @@ func TestRepository_SetPrincipalRoles(t *testing.T) {
sort.Strings(gotIds)
assert.Equal(wantIds, gotIds)
r, _, _, err := repo.LookupRole(context.Background(), tt.args.role.PublicId)
r, _, _, _, err := repo.LookupRole(context.Background(), tt.args.role.PublicId)
require.NoError(err)
if tt.name != "no change" {
assert.Equalf(tt.args.roleVersion+1, r.Version, "%s unexpected version: %d/%d", tt.name, tt.args.roleVersion+1, r.Version)

@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
@ -18,34 +19,70 @@ import (
// CreateRole will create a role in the repository and return the written
// role. No options are currently supported.
func (r *Repository) CreateRole(ctx context.Context, role *Role, _ ...Option) (*Role, error) {
func (r *Repository) CreateRole(ctx context.Context, role *Role, opt ...Option) (*Role, []*PrincipalRole, []*RoleGrant, []*RoleGrantScope, error) {
const op = "iam.(Repository).CreateRole"
if role == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role")
return nil, nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing role")
}
if role.Role == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role store")
return nil, nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing role store")
}
if role.PublicId != "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
return nil, nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
}
if role.ScopeId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
return nil, nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
id, err := newRoleId(ctx)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
return nil, nil, nil, nil, errors.Wrap(ctx, err, op)
}
c := role.Clone().(*Role)
c.PublicId = id
resource, err := r.create(ctx, c)
opts := getOpts(opt...)
var initialScope string
switch {
case opts.withGrantScopeId == nil:
initialScope = globals.GrantScopeThis
case *opts.withGrantScopeId == "":
initialScope = globals.GrantScopeThis
default:
initialScope = *opts.withGrantScopeId
}
var resource Resource
var pr []*PrincipalRole
var rg []*RoleGrant
var grantScopes []*RoleGrantScope
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(reader db.Reader, writer db.Writer) error {
resource, err = r.create(ctx, c, WithReaderWriter(reader, writer))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("while creating role"))
}
_, _, err = r.SetRoleGrantScopes(ctx, id, resource.(*Role).Version, []string{initialScope}, WithReaderWriter(reader, writer))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("while setting grant scopes"))
}
// Do a fresh lookup to get all return values
resource, pr, rg, grantScopes, err = r.LookupRole(ctx, resource.(*Role).PublicId, WithReaderWriter(reader, writer))
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
})
if err != nil {
if errors.IsUniqueError(err) {
return nil, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("role %s already exists in scope %s", role.Name, role.ScopeId))
return nil, nil, nil, nil, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("role %s already exists in scope %s", role.Name, role.ScopeId))
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", c.PublicId)))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", c.PublicId)))
}
return resource.(*Role), nil
return resource.(*Role), pr, rg, grantScopes, nil
}
// UpdateRole will update a role in the repository and return the written role.
@ -54,43 +91,56 @@ func (r *Repository) CreateRole(ctx context.Context, role *Role, _ ...Option) (*
// included in fieldMask. Name, Description, and GrantScopeId are the only
// updatable fields, If no updatable fields are included in the fieldMaskPaths,
// then an error is returned.
func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, fieldMaskPaths []string, _ ...Option) (*Role, []*PrincipalRole, []*RoleGrant, int, error) {
func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, fieldMaskPaths []string, opt ...Option) (*Role, []*PrincipalRole, []*RoleGrant, []*RoleGrantScope, int, error) {
const op = "iam.(Repository).UpdateRole"
if role == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role")
return nil, nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role")
}
if role.Role == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role store")
return nil, nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role store")
}
if role.PublicId == "" {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
return nil, nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
}
for _, f := range fieldMaskPaths {
switch {
case strings.EqualFold("name", f):
case strings.EqualFold("description", f):
case strings.EqualFold("grantscopeid", f):
default:
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f))
return nil, nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f))
}
}
opts := getOpts(opt...)
var grantScopeIdToSet []string
if opts.withGrantScopeId != nil {
// If the value is empty, they're trying to clear it (e.g. a
// null field), which is represented by an empty slice in
// SetRoleGrantScopes
grantScopeIdToSet = make([]string, 0, 1)
if *opts.withGrantScopeId != "" {
grantScopeIdToSet = append(grantScopeIdToSet, *opts.withGrantScopeId)
}
}
var dbMask, nullFields []string
dbMask, nullFields = dbw.BuildUpdatePaths(
map[string]any{
"name": role.Name,
"description": role.Description,
"GrantScopeId": role.GrantScopeId,
"name": role.Name,
"description": role.Description,
},
fieldMaskPaths,
nil,
)
if len(dbMask) == 0 && len(nullFields) == 0 {
return nil, nil, nil, db.NoRowsAffected, errors.E(ctx, errors.WithCode(errors.EmptyFieldMask), errors.WithOp(op))
if len(dbMask) == 0 && len(nullFields) == 0 && grantScopeIdToSet == nil {
return nil, nil, nil, nil, db.NoRowsAffected, errors.E(ctx, errors.WithCode(errors.EmptyFieldMask), errors.WithOp(op))
}
var resource Resource
var rowsUpdated int
var pr []*PrincipalRole
var rg []*RoleGrant
var grantScopes []*RoleGrantScope
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
@ -98,19 +148,23 @@ func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32,
func(read db.Reader, w db.Writer) error {
var err error
c := role.Clone().(*Role)
resource, rowsUpdated, err = r.update(ctx, c, version, dbMask, nullFields)
if err != nil {
return errors.Wrap(ctx, err, op)
resource = c // If we don't have dbMask or nullFields, we'll return this
if len(dbMask) > 0 || len(nullFields) > 0 {
resource, rowsUpdated, err = r.update(ctx, c, version, dbMask, nullFields, WithReaderWriter(read, w))
if err != nil {
return errors.Wrap(ctx, err, op)
}
version = resource.(*Role).Version
}
repo, err := NewRepository(ctx, read, w, r.kms)
if err != nil {
return errors.Wrap(ctx, err, op)
}
pr, err = repo.ListPrincipalRoles(ctx, role.PublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
if grantScopeIdToSet != nil {
_, _, err = r.SetRoleGrantScopes(ctx, role.PublicId, version, grantScopeIdToSet, WithReaderWriter(read, w))
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("while setting grant scopes"))
}
}
rg, err = repo.ListRoleGrants(ctx, role.PublicId)
// Do a fresh lookup since version may have gone up by 1 or 2 based
// on grant scope id
resource, pr, rg, grantScopes, err = r.LookupRole(ctx, role.PublicId, WithReaderWriter(read, w))
if err != nil {
return errors.Wrap(ctx, err, op)
}
@ -119,54 +173,73 @@ func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32,
)
if err != nil {
if errors.IsUniqueError(err) {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("role %s already exists in org %s", role.Name, role.ScopeId))
return nil, nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("role %s already exists in org %s", role.Name, role.ScopeId))
}
return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", role.PublicId)))
return nil, nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", role.PublicId)))
}
return resource.(*Role), pr, rg, rowsUpdated, nil
return resource.(*Role), pr, rg, grantScopes, rowsUpdated, nil
}
// LookupRole will look up a role in the repository. If the role is not
// found, it will return nil, nil.
func (r *Repository) LookupRole(ctx context.Context, withPublicId string, _ ...Option) (*Role, []*PrincipalRole, []*RoleGrant, error) {
//
// Supported options: WithReaderWriter
func (r *Repository) LookupRole(ctx context.Context, withPublicId string, opt ...Option) (*Role, []*PrincipalRole, []*RoleGrant, []*RoleGrantScope, error) {
const op = "iam.(Repository).LookupRole"
if withPublicId == "" {
return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
return nil, nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
}
opts := getOpts(opt...)
role := allocRole()
role.PublicId = withPublicId
var pr []*PrincipalRole
var rg []*RoleGrant
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
if err := read.LookupByPublicId(ctx, &role); err != nil {
return errors.Wrap(ctx, err, op)
}
repo, err := NewRepository(ctx, read, w, r.kms)
if err != nil {
return errors.Wrap(ctx, err, op)
}
pr, err = repo.ListPrincipalRoles(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
rg, err = repo.ListRoleGrants(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
var rgs []*RoleGrantScope
lookupFunc := func(read db.Reader, w db.Writer) error {
if err := read.LookupByPublicId(ctx, &role); err != nil {
return errors.Wrap(ctx, err, op)
}
repo, err := NewRepository(ctx, read, w, r.kms)
if err != nil {
return errors.Wrap(ctx, err, op)
}
pr, err = repo.ListPrincipalRoles(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
rg, err = repo.ListRoleGrants(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
rgs, err = repo.ListRoleGrantScopes(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
}
var err error
if opts.withReader != nil && opts.withWriter != nil {
if !opts.withWriter.IsTx(ctx) {
return nil, nil, nil, nil, errors.New(ctx, errors.Internal, op, "writer is not in transaction")
}
err = lookupFunc(opts.withReader, opts.withWriter)
} else {
_, err = r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
lookupFunc,
)
}
if err != nil {
if errors.IsNotFoundError(err) {
return nil, nil, nil, nil
return nil, nil, nil, nil, nil
}
return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", withPublicId)))
return nil, nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", withPublicId)))
}
return &role, pr, rg, nil
return &role, pr, rg, rgs, nil
}
// DeleteRole will delete a role from the repository.

@ -389,6 +389,20 @@ func (r *Repository) ListRoleGrants(ctx context.Context, roleId string, opt ...O
return roleGrants, nil
}
// ListRoleGrantScopes returns the grant scopes for the roleId and supports the WithLimit
// option.
func (r *Repository) ListRoleGrantScopes(ctx context.Context, roleId string, opt ...Option) ([]*RoleGrantScope, error) {
const op = "iam.(Repository).ListRoleGrantScopes"
if roleId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role id")
}
var roleGrantScopes []*RoleGrantScope
if err := r.list(ctx, &roleGrantScopes, "role_id = ?", []any{roleId}, opt...); err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to lookup role grant scopes"))
}
return roleGrantScopes, nil
}
func (r *Repository) GrantsForUser(ctx context.Context, userId string, _ ...Option) ([]perms.GrantTuple, error) {
const op = "iam.(Repository).GrantsForUser"
if userId == "" {
@ -403,9 +417,9 @@ func (r *Repository) GrantsForUser(ctx context.Context, userId string, _ ...Opti
var query string
switch userId {
case globals.AnonymousUserId:
query = fmt.Sprintf(grantsQuery, anonUser)
query = fmt.Sprintf(grantsForUserQuery, anonUser)
default:
query = fmt.Sprintf(grantsQuery, authUser)
query = fmt.Sprintf(grantsForUserQuery, authUser)
}
var grants []perms.GrantTuple
@ -415,11 +429,9 @@ func (r *Repository) GrantsForUser(ctx context.Context, userId string, _ ...Opti
}
defer rows.Close()
for rows.Next() {
var g perms.GrantTuple
if err := r.reader.ScanRows(ctx, rows, &g); err != nil {
if err := r.reader.ScanRows(ctx, rows, &grants); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
grants = append(grants, g)
}
if err := rows.Err(); err != nil {
return nil, errors.Wrap(ctx, err, op)

@ -10,6 +10,7 @@ import (
mathrand "math/rand"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/ldap"
"github.com/hashicorp/boundary/internal/auth/ldap/store"
"github.com/hashicorp/boundary/internal/auth/oidc"
@ -23,6 +24,43 @@ import (
)
func TestGrantsForUser(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
user := iam.TestUser(t, iamRepo, "global")
org1, proj1 := iam.TestScopes(
t,
iamRepo,
iam.WithSkipAdminRoleCreation(true),
iam.WithSkipDefaultRoleCreation(true),
)
org2, proj2 := iam.TestScopes(
t,
iamRepo,
iam.WithSkipAdminRoleCreation(true),
iam.WithSkipDefaultRoleCreation(true),
)
t.Log("org1", org1.GetPublicId(), "proj1", proj1.GetPublicId(), "org2", org2.GetPublicId(), "proj2", proj2.GetPublicId())
org1Proj1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithGrantScopeId(proj1.PublicId))
org2Proj2Role := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithGrantScopeIds([]string{globals.GrantScopeThis, globals.GrantScopeChildren}))
globalRole := iam.TestRole(t, conn, scope.Global.String(), iam.WithGrantScopeIds([]string{globals.GrantScopeDescendants}))
iam.TestUserRole(t, conn, org1Proj1Role.PublicId, user.PublicId)
iam.TestUserRole(t, conn, org2Proj2Role.PublicId, user.PublicId)
iam.TestUserRole(t, conn, globalRole.PublicId, user.PublicId)
iam.TestRoleGrant(t, conn, org1Proj1Role.PublicId, "id=*;type=*;actions=read")
iam.TestRoleGrant(t, conn, org2Proj2Role.PublicId, "id=*;type=*;actions=create")
iam.TestRoleGrant(t, conn, org2Proj2Role.PublicId, "id=*;type=*;actions=list,no-op")
iam.TestRoleGrant(t, conn, globalRole.PublicId, "id=*;type=auth-method;actions=update")
iam.TestRoleGrant(t, conn, globalRole.PublicId, "id=*;type=credential-store;actions=list,no-op")
// time.Sleep(10000 * time.Second)
// ctx := context.Background()
// grantTuples, err := iamRepo.GrantsForUser(ctx, user.PublicId)
// require.NoError(t, err)
// t.Log(pretty.Sprint(grantTuples))
}
func TestGrantsForUserRandomized(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)

@ -151,11 +151,11 @@ func TestRepository_CreateRole(t *testing.T) {
if tt.wantDup {
dup, err := NewRole(ctx, org.PublicId, tt.args.opt...)
assert.NoError(err)
dup, err = repo.CreateRole(context.Background(), dup, tt.args.opt...)
dup, _, _, _, err = repo.CreateRole(context.Background(), dup, tt.args.opt...)
assert.NoError(err)
assert.NotNil(dup)
}
grp, err := repo.CreateRole(context.Background(), tt.args.role, tt.args.opt...)
grp, _, _, _, err := repo.CreateRole(context.Background(), tt.args.role, tt.args.opt...)
if tt.wantErr {
assert.Error(err)
assert.Nil(grp)
@ -167,7 +167,7 @@ func TestRepository_CreateRole(t *testing.T) {
assert.NotNil(grp.CreateTime)
assert.NotNil(grp.UpdateTime)
foundGrp, _, _, err := repo.LookupRole(context.Background(), grp.PublicId)
foundGrp, _, _, _, err := repo.LookupRole(context.Background(), grp.PublicId)
assert.NoError(err)
assert.True(proto.Equal(foundGrp, grp))
@ -395,7 +395,7 @@ func TestRepository_UpdateRole(t *testing.T) {
_ = TestUserRole(t, conn, r.GetPublicId(), u.GetPublicId())
_ = TestRoleGrant(t, conn, r.GetPublicId(), "ids=*;type=*;actions=*")
r.Name = tt.args.name
_, _, _, _, err := repo.UpdateRole(context.Background(), r, r.Version, tt.args.fieldMaskPaths, tt.args.opt...)
_, _, _, _, _, err := repo.UpdateRole(context.Background(), r, r.Version, tt.args.fieldMaskPaths, tt.args.opt...)
assert.NoError(err)
}
@ -427,7 +427,7 @@ func TestRepository_UpdateRole(t *testing.T) {
updateRole.Name = tt.args.name
updateRole.Description = tt.args.description
roleAfterUpdate, principals, grants, updatedRows, err := repo.UpdateRole(context.Background(), &updateRole, r.Version, tt.args.fieldMaskPaths, tt.args.opt...)
roleAfterUpdate, principals, grants, _, updatedRows, err := repo.UpdateRole(context.Background(), &updateRole, r.Version, tt.args.fieldMaskPaths, tt.args.opt...)
if tt.wantErr {
assert.Error(err)
assert.True(errors.Match(errors.T(tt.wantIsError), err))
@ -450,7 +450,7 @@ func TestRepository_UpdateRole(t *testing.T) {
default:
assert.NotEqual(r.UpdateTime, roleAfterUpdate.UpdateTime)
}
foundRole, _, _, err := repo.LookupRole(context.Background(), r.PublicId)
foundRole, _, _, _, err := repo.LookupRole(context.Background(), r.PublicId)
assert.NoError(err)
assert.True(proto.Equal(roleAfterUpdate, foundRole))
underlyingDB, err := conn.SqlDB(ctx)
@ -544,7 +544,7 @@ func TestRepository_DeleteRole(t *testing.T) {
}
assert.NoError(err)
assert.Equal(tt.wantRowsDeleted, deletedRows)
foundRole, _, _, err := repo.LookupRole(ctx, tt.args.role.PublicId)
foundRole, _, _, _, err := repo.LookupRole(ctx, tt.args.role.PublicId)
assert.NoError(err)
assert.Nil(foundRole)

@ -209,7 +209,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
adminRole = adminRoleRaw.(*Role)
msgs := make([]*oplog.Message, 0, 3)
msgs := make([]*oplog.Message, 0, 4)
roleTicket, err := w.GetTicket(ctx, adminRole)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
@ -237,6 +237,16 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
}
msgs = append(msgs, roleGrantOplogMsgs...)
roleGrantScope, err := NewRoleGrantScope(ctx, adminRolePublicId, globals.GrantScopeThis)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory role grant scope"))
}
roleGrantScopeOplogMsgs := make([]*oplog.Message, 0, 1)
if err := w.CreateItems(ctx, []any{roleGrantScope}, db.NewOplogMsgs(&roleGrantScopeOplogMsgs)); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add grant scope"))
}
msgs = append(msgs, roleGrantScopeOplogMsgs...)
rolePrincipal, err := NewUserRole(ctx, adminRolePublicId, userId)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory role user"))
@ -272,7 +282,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
defaultRole = defaultRoleRaw.(*Role)
msgs := make([]*oplog.Message, 0, 6)
msgs := make([]*oplog.Message, 0, 7)
roleTicket, err := w.GetTicket(ctx, defaultRole)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket"))
@ -360,6 +370,16 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o
msgs = append(msgs, roleUserOplogMsgs...)
}
roleGrantScope, err := NewRoleGrantScope(ctx, defaultRolePublicId, globals.GrantScopeThis)
if err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory role grant scope"))
}
roleGrantScopeOplogMsgs := make([]*oplog.Message, 0, 1)
if err := w.CreateItems(ctx, []any{roleGrantScope}, db.NewOplogMsgs(&roleGrantScopeOplogMsgs)); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add grant scope"))
}
msgs = append(msgs, roleGrantScopeOplogMsgs...)
metadata := oplog.Metadata{
"op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()},
"scope-id": []string{s.PublicId},

@ -703,7 +703,7 @@ func TestRepository_ListUsers_Multiple_Scopes(t *testing.T) {
})
const numPerScope = 10
var total int = 3 // anon, auth, recovery
total := 3 // anon, auth, recovery
for i := 0; i < numPerScope; i++ {
iam.TestUser(t, repo, "global")
total++

@ -34,7 +34,7 @@ var (
)
// NewRole creates a new in memory role with a scope (project/org)
// allowed options include: withDescripion, WithName, withGrantScopeId.
// allowed options include: withDescripion, WithName.
func NewRole(ctx context.Context, scopeId string, opt ...Option) (*Role, error) {
const op = "iam.NewRole"
if scopeId == "" {
@ -43,10 +43,9 @@ func NewRole(ctx context.Context, scopeId string, opt ...Option) (*Role, error)
opts := getOpts(opt...)
r := &Role{
Role: &store.Role{
ScopeId: scopeId,
Name: opts.withName,
Description: opts.withDescription,
GrantScopeId: opts.withGrantScopeId,
ScopeId: scopeId,
Name: opts.withName,
Description: opts.withDescription,
},
}
return r, nil
@ -59,8 +58,8 @@ func allocRole() Role {
}
// Clone creates a clone of the Role.
func (r *Role) Clone() any {
cp := proto.Clone(r.Role)
func (role *Role) Clone() any {
cp := proto.Clone(role.Role)
return &Role{
Role: cp.(*store.Role),
}
@ -78,7 +77,7 @@ func (role *Role) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType
return nil
}
func (u *Role) validScopeTypes() []scope.Type {
func (role *Role) validScopeTypes() []scope.Type {
return []scope.Type{scope.Global, scope.Org, scope.Project}
}
@ -103,9 +102,9 @@ func (*Role) Actions() map[string]action.Type {
}
// TableName returns the tablename to override the default gorm table name.
func (r *Role) TableName() string {
if r.tableName != "" {
return r.tableName
func (role *Role) TableName() string {
if role.tableName != "" {
return role.tableName
}
return defaultRoleTableName
}
@ -113,8 +112,8 @@ func (r *Role) TableName() string {
// SetTableName sets the tablename and satisfies the ReplayableMessage
// interface. If the caller attempts to set the name to "" the name will be
// reset to the default name.
func (r *Role) SetTableName(n string) {
r.tableName = n
func (role *Role) SetTableName(n string) {
role.tableName = n
}
type deletedRole struct {

@ -0,0 +1,111 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package iam
import (
"context"
"fmt"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam/store"
"github.com/hashicorp/boundary/internal/types/resource"
"github.com/hashicorp/boundary/internal/types/scope"
"google.golang.org/protobuf/proto"
)
const defaultRoleGrantScopeTable = "iam_role_grant_scope"
// RoleGrantScope defines the grant scopes that are assigned to a role
type RoleGrantScope struct {
*store.RoleGrantScope
tableName string `gorm:"-"`
}
// ensure that RoleGrantScope implements the interfaces of: Cloneable and db.VetForWriter
var (
_ Cloneable = (*RoleGrantScope)(nil)
_ db.VetForWriter = (*RoleGrantScope)(nil)
)
// NewRoleGrantScope creates a new in memory role grant scope
func NewRoleGrantScope(ctx context.Context, roleId string, grantScope string, _ ...Option) (*RoleGrantScope, error) {
const op = "iam.NewRoleGrantScope"
if roleId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role id")
}
if grantScope == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing grant scope")
}
switch {
case grantScope == scope.Global.String(),
grantScope == globals.GrantScopeThis,
grantScope == globals.GrantScopeChildren,
grantScope == globals.GrantScopeDescendants:
case globals.ResourceInfoFromPrefix(grantScope).Type == resource.Scope:
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown grant scope id %q", grantScope))
}
rgs := &RoleGrantScope{
RoleGrantScope: &store.RoleGrantScope{
RoleId: roleId,
ScopeIdOrSpecial: grantScope,
},
}
return rgs, nil
}
func allocRoleGrantScope() RoleGrantScope {
return RoleGrantScope{
RoleGrantScope: &store.RoleGrantScope{},
}
}
// Clone creates a clone of the RoleGrantScope
func (g *RoleGrantScope) Clone() any {
cp := proto.Clone(g.RoleGrantScope)
return &RoleGrantScope{
RoleGrantScope: cp.(*store.RoleGrantScope),
}
}
// VetForWrite implements db.VetForWrite() interface
func (g *RoleGrantScope) VetForWrite(ctx context.Context, _ db.Reader, _ db.OpType, _ ...db.Option) error {
const op = "iam.(RoleGrantScope).VetForWrite"
if g.RoleId == "" {
return errors.New(ctx, errors.InvalidParameter, op, "missing role id")
}
if g.ScopeIdOrSpecial == "" {
return errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
switch {
case g.ScopeIdOrSpecial == scope.Global.String(),
g.ScopeIdOrSpecial == globals.GrantScopeThis,
g.ScopeIdOrSpecial == globals.GrantScopeChildren,
g.ScopeIdOrSpecial == globals.GrantScopeDescendants:
case globals.ResourceInfoFromPrefix(g.ScopeIdOrSpecial).Type == resource.Scope:
default:
return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown grant scope id %q", g.ScopeIdOrSpecial))
}
return nil
}
// TableName returns the tablename to override the default gorm table name
func (g *RoleGrantScope) TableName() string {
if g.tableName != "" {
return g.tableName
}
return defaultRoleGrantScopeTable
}
// SetTableName sets the tablename and satisfies the ReplayableMessage
// interface. If the caller attempts to set the name to "" the name will be
// reset to the default name.
func (g *RoleGrantScope) SetTableName(n string) {
g.tableName = n
}

@ -219,7 +219,6 @@ func Test_RoleUpdate(t *testing.T) {
repo := TestRepo(t, conn, wrapper)
id := testId(t)
org, proj := TestScopes(t, repo)
org2, proj2 := TestScopes(t, repo)
rw := db.New(conn)
type args struct {
name string
@ -227,7 +226,6 @@ func Test_RoleUpdate(t *testing.T) {
fieldMaskPaths []string
nullPaths []string
scopeId string
grantScopeId string
scopeIdOverride string
opts []db.Option
}
@ -325,80 +323,6 @@ func Test_RoleUpdate(t *testing.T) {
wantErr: false,
wantRowsUpdate: 1,
},
{
name: "set grant scope in project with same scope",
args: args{
name: "set grant scope in project with same scope",
fieldMaskPaths: []string{"Name", "GrantScopeId"},
scopeId: proj.PublicId,
grantScopeId: proj.PublicId,
},
wantRowsUpdate: 1,
},
{
name: "set grant scope in org with same scope",
args: args{
name: "set grant scope in org with same scope",
fieldMaskPaths: []string{"Name", "GrantScopeId"},
scopeId: org2.PublicId,
grantScopeId: org2.PublicId,
},
wantRowsUpdate: 1,
},
{
name: "set grant scope in project with different scope",
args: args{
name: "set grant scope in project with different scope",
fieldMaskPaths: []string{"GrantScopeId"},
scopeId: proj.PublicId,
grantScopeId: proj2.PublicId,
},
wantErr: true,
wantErrMsg: "db.Update: invalid to set grant_scope_id to non-same scope_id when role scope type is project: integrity violation: error #1104",
},
{
name: "set grant scope in org",
args: args{
name: "set grant scope in org",
fieldMaskPaths: []string{"Name", "GrantScopeId"},
scopeId: org2.PublicId,
grantScopeId: proj2.PublicId,
},
wantRowsUpdate: 1,
},
{
name: "set grant scope in external project",
args: args{
name: "set grant scope in external project",
fieldMaskPaths: []string{"GrantScopeId"},
scopeId: org.PublicId,
grantScopeId: proj2.PublicId,
},
wantErr: true,
wantErrMsg: "db.Update: grant_scope_id is not a child project of the role scope: integrity violation: error #1104",
},
{
name: "set grant scope in global",
args: args{
name: "set grant scope in global",
fieldMaskPaths: []string{"GrantScopeId"},
scopeId: org.PublicId,
grantScopeId: "global",
},
wantErr: true,
wantErrMsg: "db.Update: grant_scope_id is not a child project of the role scope: integrity violation: error #1104",
},
{
name: "set grant scope to parent",
args: args{
name: "set grant scope to parent",
fieldMaskPaths: []string{"GrantScopeId"},
scopeId: proj2.PublicId,
grantScopeId: org2.PublicId,
},
wantErr: true,
wantErrMsg: "db.Update: invalid to set grant_scope_id to non-same scope_id when role scope type is project: integrity violation: error #1104",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -424,7 +348,6 @@ func Test_RoleUpdate(t *testing.T) {
}
updateRole.Name = tt.args.name
updateRole.Description = tt.args.description
updateRole.GrantScopeId = tt.args.grantScopeId
updatedRows, err := rw.Update(context.Background(), &updateRole, tt.args.fieldMaskPaths, tt.args.nullPaths, tt.args.opts...)
if tt.wantErr {
@ -443,9 +366,6 @@ func Test_RoleUpdate(t *testing.T) {
foundRole.PublicId = role.GetPublicId()
err = rw.LookupByPublicId(context.Background(), &foundRole)
require.NoError(err)
if tt.args.grantScopeId == "" {
updateRole.GrantScopeId = role.ScopeId
}
assert.True(proto.Equal(updateRole, foundRole))
if len(tt.args.nullPaths) != 0 {
underlyingDB, err := conn.SqlDB(ctx)

@ -53,10 +53,6 @@ type Role struct {
// itself and when modifying dependent items like principal roles.
// @inject_tag: `gorm:"default:null"`
Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
// grant_scope_id is used for delegating access; it defines a scope other than
// the role's scope that is used when compiling these grants into an ACL
// @inject_tag: `gorm:"default:null"`
GrantScopeId string `protobuf:"bytes,80,opt,name=grant_scope_id,json=grantScopeId,proto3" json:"grant_scope_id,omitempty" gorm:"default:null"`
}
func (x *Role) Reset() {
@ -140,13 +136,6 @@ func (x *Role) GetVersion() uint32 {
return 0
}
func (x *Role) GetGrantScopeId() string {
if x != nil {
return x.GrantScopeId
}
return ""
}
var File_controller_storage_iam_store_v1_role_proto protoreflect.FileDescriptor
var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
@ -160,7 +149,7 @@ var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa4, 0x03, 0x0a, 0x04, 0x52,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x02, 0x0a, 0x04, 0x52,
0x6f, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64,
0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01,
@ -182,16 +171,11 @@ var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74,
0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x42,
0x22, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x0c, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70,
0x65, 0x49, 0x64, 0x12, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x5f, 0x69, 0x64, 0x52, 0x0c, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x49,
0x64, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61,
0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x61, 0x6d, 0x2f,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x50, 0x10, 0x51, 0x42, 0x38, 0x5a,
0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68,
0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72,
0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

@ -33,14 +33,14 @@ type RoleGrant struct {
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,1,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
// role_id is the ID of the role this is a part of
// @inject_tag: gorm:"primary_key"
// @inject_tag: `gorm:"primary_key"`
RoleId string `protobuf:"bytes,2,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty" gorm:"primary_key"`
// raw_grant is the string grant value as provided by the user
// @inject_tag: `gorm:"default:null"`
RawGrant string `protobuf:"bytes,3,opt,name=raw_grant,json=rawGrant,proto3" json:"raw_grant,omitempty" gorm:"default:null"`
// canonical_grant is the canonical string representation of the grant value.
// We use this as the unique constraint.
// @inject_tag: gorm:"primary_key"
// @inject_tag: `gorm:"primary_key"`
CanonicalGrant string `protobuf:"bytes,4,opt,name=canonical_grant,json=canonicalGrant,proto3" json:"canonical_grant,omitempty" gorm:"primary_key"`
}

@ -0,0 +1,190 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc (unknown)
// source: controller/storage/iam/store/v1/role_grant_scope.proto
package store
import (
timestamp "github.com/hashicorp/boundary/internal/db/timestamp"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type RoleGrantScope struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
CreateTime *timestamp.Timestamp `protobuf:"bytes,1,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
// role_id is the ID of the role this is a part of
// @inject_tag: `gorm:"primary_key"`
RoleId string `protobuf:"bytes,2,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty" gorm:"primary_key"`
// scope_id_or_special is the string grant scope value as provided by the
// user, which may be one of a few special values as well
//
// @inject_tag: `gorm:"primary_key"`
ScopeIdOrSpecial string `protobuf:"bytes,3,opt,name=scope_id_or_special,json=scopeIdOrSpecial,proto3" json:"scope_id_or_special,omitempty" gorm:"primary_key"`
}
func (x *RoleGrantScope) Reset() {
*x = RoleGrantScope{}
if protoimpl.UnsafeEnabled {
mi := &file_controller_storage_iam_store_v1_role_grant_scope_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RoleGrantScope) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RoleGrantScope) ProtoMessage() {}
func (x *RoleGrantScope) ProtoReflect() protoreflect.Message {
mi := &file_controller_storage_iam_store_v1_role_grant_scope_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RoleGrantScope.ProtoReflect.Descriptor instead.
func (*RoleGrantScope) Descriptor() ([]byte, []int) {
return file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescGZIP(), []int{0}
}
func (x *RoleGrantScope) GetCreateTime() *timestamp.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *RoleGrantScope) GetRoleId() string {
if x != nil {
return x.RoleId
}
return ""
}
func (x *RoleGrantScope) GetScopeIdOrSpecial() string {
if x != nil {
return x.ScopeIdOrSpecial
}
return ""
}
var File_controller_storage_iam_store_v1_role_grant_scope_proto protoreflect.FileDescriptor
var file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDesc = []byte{
0x0a, 0x36, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
0x72, 0x61, 0x67, 0x65, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76,
0x31, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x69, 0x61, 0x6d,
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa5, 0x01, 0x0a, 0x0e, 0x52,
0x6f, 0x6c, 0x65, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x4b, 0x0a,
0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f,
0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c,
0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x5f,
0x6f, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x10, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x4f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x69,
0x61, 0x6c, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x61, 0x6d,
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescOnce sync.Once
file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescData = file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDesc
)
func file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescGZIP() []byte {
file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescOnce.Do(func() {
file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescData)
})
return file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDescData
}
var file_controller_storage_iam_store_v1_role_grant_scope_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_controller_storage_iam_store_v1_role_grant_scope_proto_goTypes = []interface{}{
(*RoleGrantScope)(nil), // 0: controller.storage.iam.store.v1.RoleGrantScope
(*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp
}
var file_controller_storage_iam_store_v1_role_grant_scope_proto_depIdxs = []int32{
1, // 0: controller.storage.iam.store.v1.RoleGrantScope.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_controller_storage_iam_store_v1_role_grant_scope_proto_init() }
func file_controller_storage_iam_store_v1_role_grant_scope_proto_init() {
if File_controller_storage_iam_store_v1_role_grant_scope_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_controller_storage_iam_store_v1_role_grant_scope_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RoleGrantScope); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_controller_storage_iam_store_v1_role_grant_scope_proto_goTypes,
DependencyIndexes: file_controller_storage_iam_store_v1_role_grant_scope_proto_depIdxs,
MessageInfos: file_controller_storage_iam_store_v1_role_grant_scope_proto_msgTypes,
}.Build()
File_controller_storage_iam_store_v1_role_grant_scope_proto = out.File
file_controller_storage_iam_store_v1_role_grant_scope_proto_rawDesc = nil
file_controller_storage_iam_store_v1_role_grant_scope_proto_goTypes = nil
file_controller_storage_iam_store_v1_role_grant_scope_proto_depIdxs = nil
}

@ -8,6 +8,7 @@ import (
"crypto/rand"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/store"
"github.com/hashicorp/boundary/internal/db"
dbassert "github.com/hashicorp/boundary/internal/db/assert"
@ -185,9 +186,16 @@ func TestUser(t testing.TB, repo *Repository, scopeId string, opt ...Option) *Us
return user
}
// TestRole creates a role suitable for testing.
// TestRole creates a role suitable for testing. It will use a default grant
// scope ID unless WithGrantScopeId is used. To prevent a grant scope from being
// created, pass in a grant scope ID option with the value "testing-none".
func TestRole(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Role {
t.Helper()
opts := getOpts(opt...)
if opts.withGrantScopeId != nil && *opts.withGrantScopeId != "" && len(opts.withGrantScopeIds) > 0 {
require.FailNow(t, "cannot specify both withGrantScopeId and withGrantScopeIds")
}
ctx := context.Background()
require := require.New(t)
rw := db.New(conn)
@ -197,11 +205,30 @@ func TestRole(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Role {
id, err := newRoleId(ctx)
require.NoError(err)
role.PublicId = id
err = rw.Create(ctx, role)
require.NoError(err)
require.NoError(rw.Create(ctx, role))
require.NotEmpty(role.PublicId)
opts := getOpts(opt...)
grantScopeIds := opts.withGrantScopeIds
if len(grantScopeIds) == 0 {
var scpId string
switch {
case opts.withGrantScopeId == nil:
scpId = globals.GrantScopeThis
case *opts.withGrantScopeId == "":
scpId = globals.GrantScopeThis
default:
scpId = *opts.withGrantScopeId
}
grantScopeIds = []string{scpId}
}
for _, gsi := range grantScopeIds {
if gsi == "testing-none" {
continue
}
gs, err := NewRoleGrantScope(ctx, id, gsi)
require.NoError(err)
require.NoError(rw.Create(ctx, gs))
}
require.Equal(opts.withDescription, role.Description)
require.Equal(opts.withName, role.Name)
return role
@ -219,6 +246,17 @@ func TestRoleGrant(t testing.TB, conn *db.DB, roleId, grant string, opt ...Optio
return g
}
func TestRoleGrantScope(t testing.TB, conn *db.DB, roleId, grantScopeId string, opt ...Option) *RoleGrantScope {
t.Helper()
require := require.New(t)
rw := db.New(conn)
gs, err := NewRoleGrantScope(context.Background(), roleId, grantScopeId, opt...)
require.NoError(err)
require.NoError(rw.Create(context.Background(), gs))
return gs
}
// TestGroup creates a group suitable for testing.
func TestGroup(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Group {
t.Helper()

@ -932,7 +932,7 @@ func Test_AnonRestrictions(t *testing.T) {
if i == resource.Controller || i == resource.Worker {
continue
}
for j := action.Type(1); j <= action.ReApplyStoragePolicy; j++ {
for j := action.Type(1); j <= action.RemoveGrantScopes; j++ {
id := "foobar"
prefixes := globals.ResourcePrefixesFromType(resource.Type(i))
if len(prefixes) > 0 {

@ -88,16 +88,27 @@ message Role {
// The mutation will fail if the version does not match the latest known good version.
uint32 version = 80; // @gotags: `class:"public"`
// The Scope the grants will apply to. If the Role is at the global scope, this can be an org or project. If the Role is at an org scope, this can be a project within the org. It is invalid for this to be anything other than the Role's scope when the Role's scope is a project.
// The Scope the grants will apply to. If the Role is at the global scope,
// this can be an org or project. If the Role is at an org scope, this can be
// a project within the org. It is invalid for this to be anything other than
// the Role's scope when the Role's scope is a project.
//
// Deprecated: Use "grant_scope_ids" instead.
google.protobuf.StringValue grant_scope_id = 90 [
json_name = "grant_scope_id",
(custom_options.v1.generate_sdk_option) = true,
(custom_options.v1.mask_mapping) = {
this: "grant_scope_id"
that: "GrantScopeId"
}
deprecated = true,
(custom_options.v1.generate_sdk_option) = true
]; // @gotags: `class:"public" eventstream:"observation"`
// Output only. The IDs of Scopes the grants will apply to. This can include
// the role's own scope ID, or "this" for the same behavior; specific IDs of
// scopes that are children of the role's scope; the value "children" to match
// all direct child scopes of the role's scope; or the value "descendants" to
// match all descendant scopes (e.g. child scopes, children of child scopes;
// only valid at "global" scope since it is the only one with children of
// children).
repeated string grant_scope_ids = 91 [json_name = "grant_scope_ids"]; // @gotags: `class:"public"`
// Output only. The IDs (only) of principals that are assigned to this role.
repeated string principal_ids = 100 [json_name = "principal_ids"]; // @gotags: `class:"public" eventstream:"observation"`

@ -152,6 +152,46 @@ service RoleService {
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Removes grants from a Role."};
}
// AddRoleGrantScopes adds grants scopes to a Role. The provided request must
// include the Role id which the grant scopes will be added to. An error is
// returned if the provided id is malformed or references a non-existing
// resource.
rpc AddRoleGrantScopes(AddRoleGrantScopesRequest) returns (AddRoleGrantScopesResponse) {
option (google.api.http) = {
post: "/v1/roles/{id}:add-grant-scopes"
body: "*"
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Adds grant scopes to a Role"};
}
// SetRoleGrants sets the Role's grant scopes. Any existing grant scopes on
// the Role are deleted if they are not included in this request. The provided
// request must include the Role ID on which the grants will be set. If
// missing, malformed, or referencing a non-existing resource, an error is
// returned.
rpc SetRoleGrantScopes(SetRoleGrantScopesRequest) returns (SetRoleGrantScopesResponse) {
option (google.api.http) = {
post: "/v1/roles/{id}:set-grant-scopes"
body: "*"
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Set grant scopes for a Role, removing any grant scopes that are not specified in the request."};
}
// RemoveRoleGrantScopes removes the grant scopes from the specified Role. The
// provided request must include the Role IDs from which the grants will be
// removed. If missing, malformed, or references a non-existing resource, an
// error is returned.
rpc RemoveRoleGrantScopes(RemoveRoleGrantScopesRequest) returns (RemoveRoleGrantScopesResponse) {
option (google.api.http) = {
post: "/v1/roles/{id}:remove-grant-scopes"
body: "*"
response_body: "item"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Removes grant scopes from a Role."};
}
}
message GetRoleRequest {
@ -305,3 +345,39 @@ message RemoveRoleGrantsRequest {
message RemoveRoleGrantsResponse {
resources.roles.v1.Role item = 1;
}
message AddRoleGrantScopesRequest {
string id = 1; // @gotags: `class:"public"`
// Version is used to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
uint32 version = 2; // @gotags: `class:"public"`
repeated string grant_scope_ids = 3 [json_name = "grant_scope_ids"]; // @gotags: `class:"public"`
}
message AddRoleGrantScopesResponse {
resources.roles.v1.Role item = 1;
}
message SetRoleGrantScopesRequest {
string id = 1; // @gotags: `class:"public"`
// Version is used to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
uint32 version = 2; // @gotags: `class:"public"`
repeated string grant_scope_ids = 3 [json_name = "grant_scope_ids"]; // @gotags: `class:"public"`
}
message SetRoleGrantScopesResponse {
resources.roles.v1.Role item = 1;
}
message RemoveRoleGrantScopesRequest {
string id = 1; // @gotags: `class:"public"`
// Version is used to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
uint32 version = 2; // @gotags: `class:"public"`
repeated string grant_scope_ids = 3 [json_name = "grant_scope_ids"]; // @gotags: `class:"public"`
}
message RemoveRoleGrantScopesResponse {
resources.roles.v1.Role item = 1;
}

@ -47,11 +47,6 @@ message Role {
// @inject_tag: `gorm:"default:null"`
uint32 version = 70;
// grant_scope_id is used for delegating access; it defines a scope other than
// the role's scope that is used when compiling these grants into an ACL
// @inject_tag: `gorm:"default:null"`
string grant_scope_id = 80 [(custom_options.v1.mask_mapping) = {
this: "GrantScopeId"
that: "grant_scope_id"
}];
// Previously grant_scope_id
reserved 80;
}

@ -15,7 +15,7 @@ message RoleGrant {
timestamp.v1.Timestamp create_time = 1;
// role_id is the ID of the role this is a part of
// @inject_tag: gorm:"primary_key"
// @inject_tag: `gorm:"primary_key"`
string role_id = 2;
// raw_grant is the string grant value as provided by the user
@ -24,6 +24,6 @@ message RoleGrant {
// canonical_grant is the canonical string representation of the grant value.
// We use this as the unique constraint.
// @inject_tag: gorm:"primary_key"
// @inject_tag: `gorm:"primary_key"`
string canonical_grant = 4;
}

@ -0,0 +1,26 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
syntax = "proto3";
package controller.storage.iam.store.v1;
import "controller/storage/timestamp/v1/timestamp.proto";
option go_package = "github.com/hashicorp/boundary/internal/iam/store;store";
message RoleGrantScope {
// create_time from the RDBMS
// @inject_tag: `gorm:"default:current_timestamp"`
timestamp.v1.Timestamp create_time = 1;
// role_id is the ID of the role this is a part of
// @inject_tag: `gorm:"primary_key"`
string role_id = 2;
// scope_id_or_special is the string grant scope value as provided by the
// user, which may be one of a few special values as well
//
// @inject_tag: `gorm:"primary_key"`
string scope_id_or_special = 3;
}

@ -147,18 +147,23 @@ func TestCrud(t *testing.T) {
hcClient := hostcatalogs.NewClient(client)
hc, err := hcClient.Create(tc.Context(), "static", proj.GetPublicId(), hostcatalogs.WithName("foo"))
require.NoError(err)
checkCatalog("create", hc.Item, err, "foo", 1)
hc, err = hcClient.Read(tc.Context(), hc.Item.Id)
require.NoError(err)
checkCatalog("read", hc.Item, err, "foo", 1)
hc, err = hcClient.Update(tc.Context(), hc.Item.Id, hc.Item.Version, hostcatalogs.WithName("foo"))
require.NoError(err)
checkCatalog("update", hc.Item, err, "foo", 1)
hc, err = hcClient.Update(tc.Context(), hc.Item.Id, hc.Item.Version, hostcatalogs.WithName("bar"))
require.NoError(err)
checkCatalog("update", hc.Item, err, "bar", 2)
hc, err = hcClient.Update(tc.Context(), hc.Item.Id, hc.Item.Version, hostcatalogs.DefaultName())
require.NoError(err)
checkCatalog("update", hc.Item, err, "", 3)
_, err = hcClient.Delete(tc.Context(), hc.Item.Id)

@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/sdk/pbs/controller/api"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/roles"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
@ -53,6 +54,10 @@ func TestRoles(t *testing.T) {
UpdatedTime: pbNow,
Version: 0,
GrantScopeId: &wrapperspb.StringValue{Value: "grant-scope-id"},
GrantScopeIds: []string{
globals.GrantScopeThis,
"grant-scope-id",
},
PrincipalIds: []string{
"principal-id",
},
@ -99,6 +104,10 @@ func TestRoles(t *testing.T) {
UpdatedTime: pbNow,
Version: 0,
GrantScopeId: &wrapperspb.StringValue{Value: "grant-scope-id"},
GrantScopeIds: []string{
globals.GrantScopeThis,
"grant-scope-id",
},
PrincipalIds: []string{
"principal-id",
},

@ -69,7 +69,7 @@ func TestCustom(t *testing.T) {
require.NotNil(g)
rc := roles.NewClient(client)
var version uint32 = 1
var version uint32 = 2
r, err := rc.Create(tc.Context(), tt.scopeId, roles.WithName("foo"))
require.NoError(err)
@ -241,16 +241,16 @@ func TestCrud(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
roleClient := roles.NewClient(client)
g, err := roleClient.Create(tc.Context(), tt.scopeId, roles.WithName("foo"))
checkRole("create", g.Item, err, "foo", 1)
checkRole("create", g.Item, err, "foo", 2)
g, err = roleClient.Read(tc.Context(), g.Item.Id)
checkRole("read", g.Item, err, "foo", 1)
checkRole("read", g.Item, err, "foo", 2)
g, err = roleClient.Update(tc.Context(), g.Item.Id, g.Item.Version, roles.WithName("bar"))
checkRole("update", g.Item, err, "bar", 2)
checkRole("update", g.Item, err, "bar", 3)
g, err = roleClient.Update(tc.Context(), g.Item.Id, g.Item.Version, roles.DefaultName())
checkRole("update", g.Item, err, "", 3)
checkRole("update", g.Item, err, "", 4)
_, err = roleClient.Delete(tc.Context(), g.Item.Id)
require.NoError(err)

@ -32,9 +32,9 @@ load _target_host_sources
[ "$status" -eq 0 ]
}
@test "boundary/target: unpriv user can not read default target" {
@test "boundary/target: unpriv user can read default target" {
run read_target $DEFAULT_TARGET
[ "$status" -eq 1 ]
[ "$status" -eq 0 ]
}
@test "boundary/login: login back in as admin user" {

@ -27,6 +27,14 @@ func TestListAnonymousRecursing(t *testing.T) {
rolesClient := rolesapi.NewClient(client)
orgScopeId := "o_1234567890"
// Create a custom role in org scope
customRole, err := rolesClient.Create(tc.Context(), orgScopeId)
require.NoError(err)
customRole, err = rolesClient.AddPrincipals(tc.Context(), customRole.Item.Id, customRole.Item.Version, []string{"u_anon"})
require.NoError(err)
_, err = rolesClient.AddGrants(tc.Context(), customRole.Item.Id, customRole.Item.Version, []string{"id=*;type=auth-method;actions=list,authenticate"})
require.NoError(err)
// Create an auth method in org scope for the test
am, err := amClient.Create(tc.Context(), "password", orgScopeId)
require.NoError(err)
@ -49,18 +57,19 @@ func TestListAnonymousRecursing(t *testing.T) {
rl, err := rolesClient.List(tc.Context(), scope.Global.String())
require.NoError(err)
require.NotNil(rl)
require.Len(rl.GetItems(), 2)
require.Len(rl.GetItems(), 3)
// Find the non-admin one and delete that first
adminIdx := 0
defaultIdx := 1
roles := rl.GetItems()
if strings.Contains(roles[0].Name, "Default") {
adminIdx, defaultIdx = 1, 0
adminRoleId := ""
for _, role := range rl.GetItems() {
if strings.Contains(role.Name, "Admin") {
adminRoleId = role.Id
} else {
_, err = rolesClient.Delete(tc.Context(), role.Id)
require.NoError(err)
}
}
_, err = rolesClient.Delete(tc.Context(), roles[defaultIdx].Id)
require.NoError(err)
_, err = rolesClient.Delete(tc.Context(), roles[adminIdx].Id)
_, err = rolesClient.Delete(tc.Context(), adminRoleId)
require.NoError(err)
// Make sure we can't list in global

@ -4,6 +4,7 @@
package cluster
import (
"bytes"
"context"
"os"
"path"
@ -15,6 +16,7 @@ import (
"github.com/hashicorp/boundary/internal/cmd/config"
"github.com/hashicorp/boundary/internal/daemon/controller"
"github.com/hashicorp/boundary/internal/daemon/worker"
"github.com/hashicorp/boundary/internal/event"
"github.com/hashicorp/boundary/internal/tests/helper"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
@ -22,6 +24,7 @@ import (
func TestUnixListener(t *testing.T) {
require := require.New(t)
buf := new(bytes.Buffer)
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
})
@ -52,6 +55,23 @@ func TestUnixListener(t *testing.T) {
Config: conf,
Logger: logger.Named("c1"),
DisableOidcAuthMethodCreation: true,
EventerConfig: &event.EventerConfig{
ObservationsEnabled: true,
SysEventsEnabled: true,
Sinks: []*event.SinkConfig{
{
Name: "output",
Type: event.WriterSink,
EventTypes: []event.Type{
event.EveryType,
},
WriterConfig: &event.WriterSinkTypeConfig{
Writer: buf,
},
Format: event.TextHclogSinkFormat,
},
},
},
})
defer c1.Shutdown()
@ -80,6 +100,23 @@ func TestUnixListener(t *testing.T) {
Config: conf,
Logger: logger.Named("c1"),
DisableOidcAuthMethodCreation: true,
EventerConfig: &event.EventerConfig{
ObservationsEnabled: true,
SysEventsEnabled: true,
Sinks: []*event.SinkConfig{
{
Name: "output",
Type: event.WriterSink,
EventTypes: []event.Type{
event.EveryType,
},
WriterConfig: &event.WriterSinkTypeConfig{
Writer: buf,
},
Format: event.TextHclogSinkFormat,
},
},
},
})
defer c1.Shutdown()

@ -71,6 +71,9 @@ const (
AttachStoragePolicy Type = 57
DetachStoragePolicy Type = 58
ReApplyStoragePolicy Type = 59
AddGrantScopes Type = 60
SetGrantScopes Type = 61
RemoveGrantScopes Type = 62
// When adding new actions, be sure to update:
//
@ -138,6 +141,9 @@ var Map = map[string]Type{
AttachStoragePolicy.String(): AttachStoragePolicy,
DetachStoragePolicy.String(): DetachStoragePolicy,
ReApplyStoragePolicy.String(): ReApplyStoragePolicy,
AddGrantScopes.String(): AddGrantScopes,
SetGrantScopes.String(): SetGrantScopes,
RemoveGrantScopes.String(): RemoveGrantScopes,
}
var DeprecatedMap = map[string]Type{
@ -211,6 +217,9 @@ func (a Type) String() string {
"attach-storage-policy",
"detach-storage-policy",
"reapply-storage-policy",
"add-grant-scopes",
"set-grant-scopes",
"remove-grant-scopes",
}[a]
}

@ -261,8 +261,23 @@ type Role struct {
// Version is used in mutation requests, after the initial creation, to ensure this resource has not changed.
// The mutation will fail if the version does not match the latest known good version.
Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"`
// The Scope the grants will apply to. If the Role is at the global scope, this can be an org or project. If the Role is at an org scope, this can be a project within the org. It is invalid for this to be anything other than the Role's scope when the Role's scope is a project.
// The Scope the grants will apply to. If the Role is at the global scope,
// this can be an org or project. If the Role is at an org scope, this can be
// a project within the org. It is invalid for this to be anything other than
// the Role's scope when the Role's scope is a project.
//
// Deprecated: Use "grant_scope_ids" instead.
//
// Deprecated: Marked as deprecated in controller/api/resources/roles/v1/role.proto.
GrantScopeId *wrapperspb.StringValue `protobuf:"bytes,90,opt,name=grant_scope_id,proto3" json:"grant_scope_id,omitempty" class:"public" eventstream:"observation"` // @gotags: `class:"public" eventstream:"observation"`
// Output only. The IDs of Scopes the grants will apply to. This can include
// the role's own scope ID, or "this" for the same behavior; specific IDs of
// scopes that are children of the role's scope; the value "children" to match
// all direct child scopes of the role's scope; or the value "descendants" to
// match all descendant scopes (e.g. child scopes, children of child scopes;
// only valid at "global" scope since it is the only one with children of
// children).
GrantScopeIds []string `protobuf:"bytes,91,rep,name=grant_scope_ids,proto3" json:"grant_scope_ids,omitempty" class:"public"` // @gotags: `class:"public"`
// Output only. The IDs (only) of principals that are assigned to this role.
PrincipalIds []string `protobuf:"bytes,100,rep,name=principal_ids,proto3" json:"principal_ids,omitempty" class:"public" eventstream:"observation"` // @gotags: `class:"public" eventstream:"observation"`
// Output only. The principals that are assigned to this role.
@ -363,6 +378,7 @@ func (x *Role) GetVersion() uint32 {
return 0
}
// Deprecated: Marked as deprecated in controller/api/resources/roles/v1/role.proto.
func (x *Role) GetGrantScopeId() *wrapperspb.StringValue {
if x != nil {
return x.GrantScopeId
@ -370,6 +386,13 @@ func (x *Role) GetGrantScopeId() *wrapperspb.StringValue {
return nil
}
func (x *Role) GetGrantScopeIds() []string {
if x != nil {
return x.GrantScopeIds
}
return nil
}
func (x *Role) GetPrincipalIds() []string {
if x != nil {
return x.PrincipalIds
@ -441,7 +464,7 @@ var file_controller_api_resources_roles_v1_role_proto_rawDesc = []byte{
0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72,
0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x4a, 0x73, 0x6f,
0x6e, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0xb9, 0x06, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65,
0x6e, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0xc3, 0x06, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x05,
@ -469,36 +492,37 @@ var file_controller_api_resources_roles_v1_role_proto_rawDesc = []byte{
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x6c, 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74,
0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x26, 0xa0,
0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1e, 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73,
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x0c, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x53, 0x63,
0x6f, 0x70, 0x65, 0x49, 0x64, 0x52, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70,
0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72,
0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x0a, 0x70,
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73,
0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x0a, 0x70,
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x72, 0x61,
0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x12,
0x41, 0x0a, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x82, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e,
0x74, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3b, 0x72, 0x6f, 0x6c, 0x65,
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x06, 0xa0,
0xda, 0x29, 0x01, 0x18, 0x01, 0x52, 0x0e, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f,
0x70, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73,
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x5b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f,
0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x12,
0x24, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x73,
0x18, 0x64, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61,
0x6c, 0x5f, 0x69, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70,
0x61, 0x6c, 0x73, 0x18, 0x6e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72,
0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70,
0x61, 0x6c, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x73, 0x18, 0x78, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x6e,
0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x67, 0x72, 0x61,
0x6e, 0x74, 0x73, 0x18, 0x82, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47,
0x72, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x12,
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x4c, 0x5a,
0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68,
0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73,
0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f,
0x72, 0x6f, 0x6c, 0x65, 0x73, 0x3b, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (

Loading…
Cancel
Save