@ -5,6 +5,7 @@ import (
"fmt"
"sort"
"strings"
"unicode"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/errors"
@ -46,7 +47,7 @@ type Grant struct {
actions map [ action . Type ] bool
// The set of output fields granted
OutputFields OutputFields Map
OutputFields * OutputFields
// This is used as a temporary staging area before validating permissions to
// allow the same validation code across grant string formats
@ -104,11 +105,12 @@ func (g Grant) clone() *Grant {
ret . actions [ action ] = true
}
}
if g. OutputFields != nil {
ret. OutputFields = make ( OutputFieldsMap , len ( g . Outp utFields) )
for k, v := range g . Outp utFields {
ret. OutputFields [ k ] = v
if outFields, hasSetFields := g . OutputFields . Fields ( ) ; hasSetFields {
fieldsToAdd := make ( [ ] string , 0 , len ( o utFields) )
for _, v := range o utFields {
fieldsToAdd = append ( fieldsToAdd , v )
}
ret . OutputFields = ret . OutputFields . AddFields ( fieldsToAdd )
}
return ret
}
@ -134,8 +136,8 @@ func (g Grant) CanonicalString() string {
builder = append ( builder , fmt . Sprintf ( "actions=%s" , strings . Join ( actions , "," ) ) )
}
if len ( g . OutputFields ) > 0 {
builder = append ( builder , fmt . Sprintf ( "output_fields=%s" , strings . Join ( g. OutputFields . Fields ( ) , "," ) ) )
if outFields , hasSetFields := g . OutputFields . Fields ( ) ; hasSetFields {
builder = append ( builder , fmt . Sprintf ( "output_fields=%s" , strings . Join ( outFields , "," ) ) )
}
return strings . Join ( builder , ";" )
@ -159,8 +161,8 @@ func (g Grant) MarshalJSON() ([]byte, error) {
sort . Strings ( actions )
res [ "actions" ] = actions
}
if len ( g . OutputFields ) > 0 {
res [ "output_fields" ] = g. OutputFields . Fields ( )
if outFields , hasSetFields := g . OutputFields . Fields ( ) ; hasSetFields {
res [ "output_fields" ] = outFields
}
b , err := json . Marshal ( res )
if err != nil {
@ -222,17 +224,22 @@ func (g *Grant) unmarshalJSON(data []byte) error {
}
// We do the make here because we detect later if the field was set but
// no values given
g . OutputFields = make ( OutputFieldsMap , len ( interfaceOutputFields ) )
if len ( interfaceOutputFields ) > 0 {
switch len ( interfaceOutputFields ) {
case 0 :
// JSON was set but no fields defined, add an empty array
g . OutputFields = g . OutputFields . AddFields ( [ ] string { } )
default :
fields := make ( [ ] string , 0 , len ( interfaceOutputFields ) )
for _ , v := range interfaceOutputFields {
field , ok := v . ( string )
switch {
case ! ok :
return errors . NewDeprecated ( errors . InvalidParameter , op , fmt . Sprintf ( "unable to interpret %v in output_fields array as string" , v ) )
default :
g. OutputFields [ field ] = true
fields = append ( fields , field )
}
}
g . OutputFields = g . OutputFields . AddFields ( fields )
}
}
return nil
@ -250,7 +257,7 @@ func (g *Grant) unmarshalText(grantString string) error {
return errors . NewDeprecated ( errors . InvalidParameter , op , fmt . Sprintf ( "segment %q not formatted correctly, wrong number of equal signs" , segment ) )
case len ( kv [ 0 ] ) == 0 :
return errors . NewDeprecated ( errors . InvalidParameter , op , fmt . Sprintf ( "segment %q not formatted correctly, missing key" , segment ) )
case len ( kv [ 1 ] ) == 0 :
case len ( kv [ 1 ] ) == 0 && kv [ 0 ] != "output_fields" :
return errors . NewDeprecated ( errors . InvalidParameter , op , fmt . Sprintf ( "segment %q not formatted correctly, missing value" , segment ) )
}
@ -278,7 +285,12 @@ func (g *Grant) unmarshalText(grantString string) error {
}
case "output_fields" :
g . OutputFields = g . OutputFields . AddFields ( strings . Split ( kv [ 1 ] , "," ) )
switch len ( kv [ 1 ] ) {
case 0 :
g . OutputFields = g . OutputFields . AddFields ( [ ] string { } )
default :
g . OutputFields = g . OutputFields . AddFields ( strings . Split ( kv [ 1 ] , "," ) )
}
}
}
@ -300,9 +312,10 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) {
if scopeId == "" {
return Grant { } , errors . NewDeprecated ( errors . InvalidParameter , op , "missing scope id" )
}
grantString = strings . ToValidUTF8 ( grantString , string ( unicode . ReplacementChar ) )
grant := Grant {
scope : Scope { Id : s copeId} ,
scope : Scope { Id : s trings. ToValidUTF8 ( s copeId, string ( unicode . ReplacementChar ) ) } ,
}
switch {
case scopeId == scope . Global . String ( ) :
@ -337,11 +350,11 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) {
switch id {
case "user.id" , ".User.Id" :
if opts . withUserId != "" {
grant . id = opts. withUserId
grant . id = strings. ToValidUTF8 ( opts. withUserId , string ( unicode . ReplacementChar ) )
}
case "account.id" , ".Account.Id" :
if opts . withAccountId != "" {
grant . id = opts. withAccountId
grant . id = strings. ToValidUTF8 ( opts. withAccountId , string ( unicode . ReplacementChar ) )
}
default :
return Grant { } , errors . NewDeprecated ( errors . InvalidParameter , op , fmt . Sprintf ( "unknown template %q in grant %q value" , grant . id , "id" ) )
@ -376,7 +389,7 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) {
// get to this point because original parsing should error)
return Grant { } , errors . NewDeprecated ( errors . InvalidParameter , op , "parsed grant string contains no id or type" )
case resource . All :
// "type=*;actions=..." is not supported -- we req i ure you to
// "type=*;actions=..." is not supported -- we req ui re you to
// explicitly set a pin or set the ID to *
return Grant { } , errors . NewDeprecated ( errors . InvalidParameter , op , "parsed grant string contains wildcard type with no id value" )
default :
@ -387,7 +400,7 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) {
switch len ( grant . actions ) {
case 0 :
// It's okay to have no actions if only output fields are being defined
if len ( grant . OutputFields ) == 0 {
if _ , hasSetFields := grant . OutputFields . Fields ( ) ; ! hasSetFields {
return Grant { } , errors . NewDeprecated ( errors . InvalidParameter , op , "parsed grant string contains no actions or output fields" )
}
case 1 :
@ -404,10 +417,6 @@ func Parse(scopeId, grantString string, opt ...Option) (Grant, error) {
}
}
}
// Set but empty output fields...
if grant . OutputFields != nil && len ( grant . OutputFields ) == 0 {
return Grant { } , errors . NewDeprecated ( errors . InvalidParameter , op , "parsed grant string has output_fields set but empty" )
}
// This might be zero if output fields is populated
if len ( grant . actions ) > 0 {
// Create a dummy resource and pass it through Allowed and ensure that
@ -457,7 +466,7 @@ func (g *Grant) parseAndValidateActions() error {
g . actionsBeingParsed = nil
// If there are no actions it's fine if the grant is just used to
// specify output fields
if len ( g . OutputFields ) > 0 {
if _ , hasSetFields := g . OutputFields . Fields ( ) ; hasSetFields {
return nil
}
return errors . NewDeprecated ( errors . InvalidParameter , op , "missing actions" )