mirror of https://github.com/hashicorp/boundary
Multiple grant scope IDs (#4251)
Co-authored-by: Timothy Messier <tim.messier@gmail.com> Co-authored-by: Todd <github@quaddi.com>pull/4270/head
parent
659df2797c
commit
f5dd457b34
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
@ -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, ¤tRoleGrantScopes, "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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
Loading…
Reference in new issue