strongparams

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2021 License: BSD-3-Clause Imports: 10 Imported by: 0

README

go-strongparams

RubyOnRails inspired Golang implementation of Strong Parameters

Strong Params

Strong parameters is a really great approach of whitelisting model properties. github.com/gorilla/schema is a really great tool. But, it doesn't support dynamic whitelisting on the struct. Through struct tags it is only possible to define which fields are required.

Let's take a look on the following use case:

type OptionalParams struct {
    OptionalKey1 *string `params:"key1"`
    OptionalKey2 *string `params:"key2"`
}

optionalParams := OptionalParams{}

It is pretty complicated to retrieve the one or the other value from the request query string or post parameters.

This can easily be solved with go-strongparams. 😄

optionalParams := OptionalParams{}
values := url.Values{
    "key1": []string{"value"},
    "key2": []string{"ignoredValue"},
}

Params().Permit("key1").Values(values)(&optionalParams)

output, _ := json.Marshal(values)
fmt.Println(output) // > {"key1": "value", "key2": null}
(*StrongParams) Require(requireKey string) *StrongParams

Require enables defining a single key which defines an entity that needs to be present.

queryRequest := // ?entity[key1]=value1&entity[key2]=value2
Params().Require("entity").Query(queryRequest)(&optionalParams)

// OR

postFormRequest := // entity[key1]=value1
                   // entity[key2]=value2
Params().Require("entity").PostForm(postFormRequest)(&optionalParams)

// OR

values := url.Values{
    "entity[key1]": []string{"value1"},
    "entity[key2]": []string{"value2"},
}
Params().Permit("entity").Values(values)(&optionalParams)
(*StrongParams) RequireOne(requireKey string) *StrongParamsRequireOne

RequireOne enables validating that a key is present and retrieving it.

queryRequest := // ?entity[key1]=1&entity[key2]=2
i, err := Params().RequireOne("entity[key1]").Query(queryRequest)(strconv.Atoi)

// OR

postFormRequest := // entity[key1]=value1
                   // entity[key2]=value2
i, err := Params().RequireOne("entity[key1]").PostForm(postFormRequest)(strconv.Atoi)

// OR

values := url.Values{
    "entity[key1]": []string{"value1"},
    "entity[key2]": []string{"value2"},
}
i, err := Params().RequireOne("entity[key1]").Values(values)(strconv.Atoi)
(*StrongParams) Permit(permitRule string, permitRules... string) *StrongParamsRequiredAndPermitted

Permit enables whitelisting keys.

type EntityHolder struct {
    Entity string `params:"entity"`
}
entity := EntityHolder{}

queryRequest := // ?entity[key1]=value1&entity[field2]=value2
Params().Permit("entity:{key1, key2}").Query(queryRequest)(&entity)

// OR

postFormRequest := // entity[key1]=value1
                   // entity[key2]=value2
Params().Permit("entity:{key1, key2}").PostForm(postFormRequest)(&entity)

// OR

values := url.Values{
    "entity[key1]": []string{"value1"},
    "entity[key2]": []string{"value2"},
}
Params().Permit("entity:{key1, key2}").Values(values)(&entity)
(*StrongParamsRequired) Permit(permitRule string, permitRules... string) *StrongParamsRequiredAndPermitted

Permit enables whitelisting keys.

queryRequest := // ?entity[key1]=value1&entity[field2]=value2
Params().Require("entity").Permit("key1, key2").Query(queryRequest)(&optionalParams)

// OR

postFormRequest := // entity[key1]=value1
                   // entity[key2]=value2
Params().Require("entity").Permit("key1, key2").PostForm(postFormRequest)(&optionalParams)

// OR

values := url.Values{
    "entity[key1]": []string{"value1"},
    "entity[key2]": []string{"value2"},
}
Params().Require("entity").Permit("key1, key2").Values(values)(&optionalParams)
github.com/gorilla/schema dot notation

github.com/gorilla/schema uses a dot notation (eg. entity.0.key) instead of brackets notation (eg. entity[0][key]). go-strongparams helps to overcome this downside. Before passing the url.Values to the schema.Decoder it transforms the bracket notation keys to dot notation keys.

queryRequest := // ?entity[key1]=value1&entity[field2]=value2
Params().Query(queryRequest)(&entity)

// OR

postFormRequest := // entity[key1]=value1
                   // entity[key2]=value2
Params().PostForm(postFormRequest)(&entity)

// OR

values := url.Values{
    "entity[key1]": []string{"value1"},
    "entity[key2]": []string{"value2"},
}
Params().Values(values)(&entity)

More examples in ./test/StrongParams_test.go

schema.Decoder

By default go-strongparams uses the following schema.Decoder configuration.

decoder := schema.NewDecoder()
decoder.SetAliasTag("params")

Tag alias params is used instead of schema.Decoder default schema.

To override the default decoder two methods can be used.

  • Overrides given StrongParams struct's decoder:
    Params().WithDecoder(newDecoder)
    
  • Every new StrongParams struct will have the following decoder:
    WithDecoder(newDecoder).Params()
    
    This way there is no need to define your explicit decoder for every StrongParam separately.

Permitter

The github.com/vellotis/go-strongparams/permitter package holds a simple rule engine that is capable of validating if a specific key is permitted or not.

The rules can have the following elements:

  • Key (KeyLiteral) defines a whitelisted key:

    • string literal of ASCII numbers and letters, eg. someKey
    • single quote ' wrapped string literal of ASCII numbers, letters and spaces, eg. 'some key'
  • Object (ObjectLiteral) defines a whitelisted object containing any nested literals:

    • { KeyLiteral, KeyLiteral } eg.
      { key1, key2 } matches query key1=value1&key2=value2

    • { KeyLiteral:Array, KeyLiteral:ObjectLiteral, KeyLiteral } eg.
      { key1:[], key2:{objKey}, key3} matches query key1[]=value&key2[objKey]=objValue&key3=keyValue

    NOTE! Rule {} doesn't match anything

  • Array (ArrayLiteral) defines a whitelisted array containing any nested literals:

    • [] matches array of string values, eg.
      []=value1&[]value2 or [0]=value1&[1]value2
    • [ KeyLiteral ] eg.
      [ key ] matches query [0][key]=value
    • [ KeyLiteral:Array, KeyLiteral:ObjectLiteral, KeyLiteral ] eg.
      { key1:[], key2:{objKey}, key3 } matches query [0][key1][]=value1&[0][key1][]=value2&[0][key2][objKey]=objValue&[0][key3]=keyValue

Some examples:

ParsePermitted(/*Rule*/).IsPermitted(/*Permitted key*/)
Rule Permitted keys
key key
obj:{key} obj[key]
key1,key2,key3 key1
key2
key3
key1:[sub1,sub2,sub3],key2:{sub4} key1[0][sub1]
key1[0][sub2]
key1[0][sub3]
key2[sub4]
key1:{'sub x1':{sub x2:[key]}} key[sub x1][sub x2][0][key]
key:[] key[]
key[0]
key[1]
key[n] (n = Number >= 0)
key:{} Matches noting

More examples in ./permittable/test/Permittable_test.go

License

BSD licensed. See the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ReturnOfType

type ReturnOfType func(StringParser) (interface{}, error)

ReturnOfType enables parsing and returning the single key from url.Values declared by StrongParams.RequireOne method or returns an error if it fails.

The StringParser parameter shall be a function definition that enables parsing string value to specific required type.

type ReturnTarget

type ReturnTarget func(target interface{}) error

ReturnTarget enables just features of schema.Decoder (https://github.com/gorilla/schema) without performing any additional checks. schema.Decoder requires a dot notation of properties eg. "root.0.key" equivalent to query string "root[0][key]". Given method enables using the standard query string brackets format with schema.Decoder. Before passing the url.Values to schema.Decoder the keys are transposed to the required dot notation.

The `target` parameter shall be a pointer to the parsable struct.

type StringParser

type StringParser interface{}

StringParser should be a function of:

func(string) {<any output type>, error}

The interface will be checked by the runner. If it doesn't comply it will panic.

type StrongParams

type StrongParams struct {
	// contains filtered or unexported fields
}

func Params

func Params() *StrongParams

Params declares the *http.Request to be used for the strong parameters mechanism.

**NOTE** The method panics if the passed request parameter is nil.

func (*StrongParams) Permit

func (this *StrongParams) Permit(permitRule string, permitRules ...string) *StrongParamsRequiredAndPermitted

Permit instructs to apply the rules to whitelist the keys in url.Values before decoding it to the target struct.

func (*StrongParams) PostForm

func (this *StrongParams) PostForm(request *http.Request) ReturnTarget

PostForm instructs the mechanism to process http.Request's url.PostForm property's url.Values.

func (*StrongParams) Query

func (this *StrongParams) Query(request *http.Request) ReturnTarget

Query instructs the mechanism to process http.Request's url.URL property url.URL/Query() method returned url.Values.

func (*StrongParams) Require

func (this *StrongParams) Require(requireKey string) *StrongParamsRequired

Require instructs to ensure before processing that the processable url.Values has by `requireKey` parameter provided root key. The chained StrongParamsRequired.Permit rules are applied on the object found behind the parameter `requireKey` defined key.

Go: Params().Require("root").Permit("sub:{key}")
Whitelisted query: root[sub][key]=value

The `requireKey` value cannot be empty. Otherwise an error is produced but not returned until executing ReturnTarget function.

func (*StrongParams) RequireOne

func (this *StrongParams) RequireOne(requireKey string) *StrongParamsRequireOne

RequireOne instructs to ensure presence and parse a single value in url.Values. `requireKey` parameter defines the key to retrieve from the url.Values.

func (*StrongParams) Values

func (this *StrongParams) Values(values url.Values) ReturnTarget

Values instructs the mechanism to process url.Values from `values` parameter.

func (*StrongParams) WithDecoder

func (this *StrongParams) WithDecoder(decoder *schema.Decoder) *StrongParams

StrongParams.WithDecoder instructs the strong-parameters mechanism to use explicit decoder. The new decoder will be used on the returned *StrongParams struct pointer not on the receiver parameter. To use new implicitly defined schema.Decoder, look WithDecoder method instead.

type StrongParamsRequireOne

type StrongParamsRequireOne struct {
	// contains filtered or unexported fields
}

func (*StrongParamsRequireOne) PostForm

func (this *StrongParamsRequireOne) PostForm(request *http.Request) ReturnOfType

PostForm instructs the mechanism to process http.Request's url.PostForm property's url.Values.

func (*StrongParamsRequireOne) Query

func (this *StrongParamsRequireOne) Query(request *http.Request) ReturnOfType

Query instructs the mechanism to process http.Request's url.URL property url.URL/Query() method returned url.Values.

func (*StrongParamsRequireOne) Values

func (this *StrongParamsRequireOne) Values(values url.Values) ReturnOfType

Values instructs the mechanism to process url.Values from `values` parameter.

type StrongParamsRequired

type StrongParamsRequired struct {
	// contains filtered or unexported fields
}

func (*StrongParamsRequired) Permit

func (this *StrongParamsRequired) Permit(permitRule string, permitRules ...string) *StrongParamsRequiredAndPermitted

Permit instructs to apply the rules to whitelist the keys in url.Values before decoding it to the target struct. The chained Permit rules are applied on the object found behind the parameter `requireKey` defined key instructed by StrongParams.Require method.

Go: Params().Require("root").Permit("sub:{key}")
Whitelisted query: root[sub][key]=value

These two use cases are equivalent:

Permit("[key1, key2]")
Permit("key1", "key2")

func (*StrongParamsRequired) PostForm

func (this *StrongParamsRequired) PostForm(request *http.Request) ReturnTarget

PostForm instructs the mechanism to process http.Request's url.PostForm property's url.Values.

func (*StrongParamsRequired) Query

func (this *StrongParamsRequired) Query(request *http.Request) ReturnTarget

Query instructs the mechanism to process http.Request's url.URL property url.URL/Query() method returned url.Values.

func (*StrongParamsRequired) Values

func (this *StrongParamsRequired) Values(values url.Values) ReturnTarget

Values instructs the mechanism to process url.Values from `values` parameter.

type StrongParamsRequiredAndPermitted

type StrongParamsRequiredAndPermitted struct {
	// contains filtered or unexported fields
}

func (*StrongParamsRequiredAndPermitted) PostForm

func (this *StrongParamsRequiredAndPermitted) PostForm(request *http.Request) ReturnTarget

PostForm instructs the mechanism to process http.Request's url.PostForm property's url.Values.

func (*StrongParamsRequiredAndPermitted) Query

Query instructs the mechanism to process http.Request's url.URL property url.URL/Query() method returned url.Values.

func (*StrongParamsRequiredAndPermitted) Values

Values instructs the mechanism to process url.Values from `values` parameter.

type StrongParamsWithDecoder

type StrongParamsWithDecoder struct {
	// contains filtered or unexported fields
}

func WithDecoder

func WithDecoder(decoder *schema.Decoder) (*StrongParamsWithDecoder, error)

WithDecoder declares the decoder to be used for Params mechanism.

paramsWithDecoder, err := WithDecoder(schema.NewDecoder())
// handle error
queryParams := paramsWithDecoder.Params(request).Query()

Returns an error if the passed decoder is a `nil` value.

func WithDecoderSafe

func WithDecoderSafe(decoder *schema.Decoder) *StrongParamsWithDecoder

WithDecoderSafe is an equivalent of WithDecoder but instead of returning an error when the passed decoder is a `nil` value it panics with the same error.

WithDecoder(schema.NewDecoder()).Params(request).Query()

func (*StrongParamsWithDecoder) Params

func (this *StrongParamsWithDecoder) Params() *StrongParams

Params declares the *http.Request to be used for the strong parameters mechanism.

**NOTE** The method panics if the passed request parameter is nil.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL