Skip to content

Commit

Permalink
topdown: Add urlquery.decode_object builtin
Browse files Browse the repository at this point in the history
This builtin is the reverse of the encode_object builtin and
makes it easier to use the URI query parameters in policies

Fixes open-policy-agent#2647

Signed-off-by: Frederic <frederic.vanreet@icloud.com>
  • Loading branch information
GBrawl committed Aug 30, 2020
1 parent dd78d44 commit fbbd605
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 0 deletions.
12 changes: 12 additions & 0 deletions ast/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ var DefaultBuiltins = [...]*Builtin{
URLQueryDecode,
URLQueryEncode,
URLQueryEncodeObject,
URLQueryDecodeObject,
YAMLMarshal,
YAMLUnmarshal,

Expand Down Expand Up @@ -1283,6 +1284,17 @@ var URLQueryEncodeObject = &Builtin{
),
}

// URLQueryDecodeObject decodes the given URL query string into an object.
var URLQueryDecodeObject = &Builtin{
Name: "urlquery.decode_object",
Decl: types.NewFunction(
types.Args(types.S),
types.NewObject(nil, types.NewDynamicProperty(
types.S,
types.NewArray(nil, types.S))),
),
}

// YAMLMarshal serializes the input term.
var YAMLMarshal = &Builtin{
Name: "yaml.marshal",
Expand Down
25 changes: 25 additions & 0 deletions capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -3043,6 +3043,31 @@
"type": "function"
}
},
{
"name": "urlquery.decode_object",
"decl": {
"args": [
{
"type": "string"
}
],
"result": {
"dynamic": {
"key": {
"type": "string"
},
"value": {
"dynamic": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"type": "function"
}
},
{
"name": "urlquery.encode",
"decl": {
Expand Down
1 change: 1 addition & 0 deletions docs/content/policy-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ The following table shows examples of how ``glob.match`` works:
| <span class="opa-keep-it-together">``output := urlquery.encode(string)``</span> | ``output`` is ``string`` serialized to a URL query parameter encoded string |
| <span class="opa-keep-it-together">``output := urlquery.encode_object(object)``</span> | ``output`` is ``object`` serialized to a URL query parameter encoded string |
| <span class="opa-keep-it-together">``output := urlquery.decode(string)``</span> | ``output`` is ``string`` deserialized from a URL query parameter encoded string |
| <span class="opa-keep-it-together">``output := urlquery.decode_object(string)``</span> | ``output`` is ``object`` deserialized from a URL query parameter string |
| <span class="opa-keep-it-together">``output := json.marshal(x)``</span> | ``output`` is ``x`` serialized to a JSON string |
| <span class="opa-keep-it-together">``output := json.unmarshal(string)``</span> | ``output`` is ``string`` deserialized to a term from a JSON encoded string |
| <span class="opa-keep-it-together">``output := yaml.marshal(x)``</span> | ``output`` is ``x`` serialized to a YAML string |
Expand Down
34 changes: 34 additions & 0 deletions test/cases/testdata/urlbuiltins/test-urlbuiltins-1076.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
cases:
- modules:
- |
package decode_object
p = x {
x = urlquery.decode_object("a=value_a1&b=value_b&a=value_a2")
}
note: urlbuiltins/decode_object multiple
query: data.decode_object.p = x
want_result:
- x: {"a": ["value_a1", "value_a2"], "b": ["value_b"]}
- modules:
- |
package decode_object
p = x {
x = urlquery.decode_object("a=value_a1&b")
}
note: urlbuiltins/decode_object empty parameter
query: data.decode_object.p = x
want_result:
- x: {"a": ["value_a1"], "b": [""]}
- modules:
- |
package decode_object
p = x {
x = urlquery.decode_object("")
}
note: urlbuiltins/decode_object empty string
query: data.decode_object.p = x
want_result:
- x: {}
24 changes: 24 additions & 0 deletions topdown/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,29 @@ func builtinURLQueryEncodeObject(a ast.Value) (ast.Value, error) {
return ast.String(query.Encode()), nil
}

func builtinURLQueryDecodeObject(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
query, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
}

queryParams, err := url.ParseQuery(string(query))
if err != nil {
return err
}

queryObject := ast.NewObject()
for k, v := range queryParams {
paramsArray := make([]*ast.Term, len(v))
for i, param := range v {
paramsArray[i] = ast.StringTerm(param)
}
queryObject.Insert(ast.StringTerm(k), ast.ArrayTerm(paramsArray...))
}

return iter(ast.NewTerm(queryObject))
}

func builtinYAMLMarshal(a ast.Value) (ast.Value, error) {

asJSON, err := ast.JSON(a)
Expand Down Expand Up @@ -212,6 +235,7 @@ func init() {
RegisterFunctionalBuiltin1(ast.URLQueryDecode.Name, builtinURLQueryDecode)
RegisterFunctionalBuiltin1(ast.URLQueryEncode.Name, builtinURLQueryEncode)
RegisterFunctionalBuiltin1(ast.URLQueryEncodeObject.Name, builtinURLQueryEncodeObject)
RegisterBuiltinFunc(ast.URLQueryDecodeObject.Name, builtinURLQueryDecodeObject)
RegisterFunctionalBuiltin1(ast.YAMLMarshal.Name, builtinYAMLMarshal)
RegisterFunctionalBuiltin1(ast.YAMLUnmarshal.Name, builtinYAMLUnmarshal)
}

0 comments on commit fbbd605

Please sign in to comment.