* Adding grants related methods to Roles. * Adding grants back after the merge of Principals. * Adding tests for Grant related custom methods. * Improving api handler methods. * Fixing tests and sdk field. * Adding todos for adding principals and grants to List and Update Role responses. * Fix up Set* tests and fix SetPrincipals bug. * Making AddPrincipals and RemovePrincipals similar to the tests for AddGrants and RemoveGrants. * Add JSON grants to output (#187) I also changed canonical_grants to grants_canonical and used grants_json. I believe that when members are simply different formats of other members this is the preferred way to have it in the API. Let me know if you disagree and we can dig in more. * Regenerate the API role resource. Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
110 lines
3.3 KiB
Go
110 lines
3.3 KiB
Go
package iam
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/watchtower/internal/db"
|
|
"github.com/hashicorp/watchtower/internal/iam/store"
|
|
"github.com/hashicorp/watchtower/internal/perms"
|
|
"github.com/hashicorp/watchtower/internal/types/resource"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
const defaultRoleGrantTable = "iam_role_grant"
|
|
|
|
// RoleGrant defines the grants that are assigned to a role
|
|
type RoleGrant struct {
|
|
*store.RoleGrant
|
|
tableName string `gorm:"-"`
|
|
}
|
|
|
|
// ensure that RoleGrant implements the interfaces of: Clonable and db.VetForWriter
|
|
var _ Clonable = (*RoleGrant)(nil)
|
|
var _ db.VetForWriter = (*RoleGrant)(nil)
|
|
|
|
// NewRoleGrant creates a new in memory role grant
|
|
func NewRoleGrant(roleId string, grant string, opt ...Option) (*RoleGrant, error) {
|
|
if roleId == "" {
|
|
return nil, fmt.Errorf("new role grant: role id is not set: %w", db.ErrNilParameter)
|
|
}
|
|
if grant == "" {
|
|
return nil, fmt.Errorf("new role grant: grant is empty: %w", db.ErrNilParameter)
|
|
}
|
|
|
|
// Validate that the grant parses successfully. Note that we fake the scope
|
|
// here to avoid a lookup as the scope is only relevant at actual ACL
|
|
// checking time and we just care that it parses correctly.
|
|
perm, err := perms.Parse("o_abcd1234", "", grant)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("new role grant: error parsing grant string: %w", err)
|
|
}
|
|
rg := &RoleGrant{
|
|
RoleGrant: &store.RoleGrant{
|
|
RoleId: roleId,
|
|
RawGrant: grant,
|
|
CanonicalGrant: perm.CanonicalString(),
|
|
},
|
|
}
|
|
return rg, nil
|
|
}
|
|
|
|
func allocRoleGrant() RoleGrant {
|
|
return RoleGrant{
|
|
RoleGrant: &store.RoleGrant{},
|
|
}
|
|
}
|
|
|
|
// Clone creates a clone of the RoleGrant
|
|
func (g *RoleGrant) Clone() interface{} {
|
|
cp := proto.Clone(g.RoleGrant)
|
|
return &RoleGrant{
|
|
RoleGrant: cp.(*store.RoleGrant),
|
|
}
|
|
}
|
|
|
|
// VetForWrite implements db.VetForWrite() interface
|
|
func (g *RoleGrant) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
|
if g.RawGrant == "" {
|
|
return fmt.Errorf("vet role grant for writing: grant is empty: %w", db.ErrNilParameter)
|
|
}
|
|
|
|
// Validate that the grant parses successfully. Note that we fake the scope
|
|
// here to avoid a lookup as the scope is only relevant at actual ACL
|
|
// checking time and we just care that it parses correctly. We may have
|
|
// already done this in NewRoleGrant, but we re-check and set it here
|
|
// anyways because it should still be part of the vetting process.
|
|
perm, err := perms.Parse("o_abcd1234", "", g.RawGrant)
|
|
if err != nil {
|
|
return fmt.Errorf("vet role grant for writing: error parsing grant string: %w", err)
|
|
}
|
|
canonical := perm.CanonicalString()
|
|
if g.CanonicalGrant != "" && g.CanonicalGrant != canonical {
|
|
return fmt.Errorf("vet role grant for writing: existing canonical grant and derived one do not match: %w", err)
|
|
}
|
|
g.CanonicalGrant = canonical
|
|
|
|
return nil
|
|
}
|
|
|
|
// ResourceType returns the type of the RoleGrant
|
|
func (*RoleGrant) ResourceType() resource.Type { return resource.RoleGrant }
|
|
|
|
// TableName returns the tablename to override the default gorm table name
|
|
func (g *RoleGrant) TableName() string {
|
|
if g.tableName != "" {
|
|
return g.tableName
|
|
}
|
|
return defaultRoleGrantTable
|
|
}
|
|
|
|
// SetTableName sets the tablename and satisfies the ReplayableMessage interface
|
|
func (g *RoleGrant) SetTableName(n string) {
|
|
switch n {
|
|
case "":
|
|
g.tableName = defaultRoleGrantTable
|
|
default:
|
|
g.tableName = n
|
|
}
|
|
}
|