Skip to content

Commit

Permalink
Add option to enable http redirects. (#925)
Browse files Browse the repository at this point in the history
Fixes #921

Signed-off-by: Ashutosh Narkar <[email protected]>
  • Loading branch information
ashutosh-narkar authored Sep 4, 2018
1 parent b4e9362 commit 51d5d11
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 5 deletions.
2 changes: 1 addition & 1 deletion docs/book/language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ evaluation query will always return the same value.
### HTTP
| Built-in | Inputs | Description |
| ------- |--------|-------------|
| <span class="opa-keep-it-together">``http.send(request, output)``</span> | 1 | ``http.send`` executes a HTTP request and returns the response.``request`` is an object containing keys ``method``, ``url`` and optionally ``body``. For example, ``http.send({"method": "get", "url": "http://www.openpolicyagent.org/"}, output)``. ``output`` is an object containing keys ``status``, ``status_code`` and ``body`` which represent the HTTP status, status code and response body respectively. Sample output, ``{"status": "200 OK", "status_code": 200, "body": null``}|
| <span class="opa-keep-it-together">``http.send(request, output)``</span> | 1 | ``http.send`` executes a HTTP request and returns the response.``request`` is an object containing keys ``method``, ``url`` and optionally ``body`` and ``enable_redirect``. For example, ``http.send({"method": "get", "url": "http://www.openpolicyagent.org/"}, output)``. ``output`` is an object containing keys ``status``, ``status_code`` and ``body`` which represent the HTTP status, status code and response body respectively. Sample output, ``{"status": "200 OK", "status_code": 200, "body": null``}. By default, http redirects are not enabled. To enable, set ``enable_redirect`` to ``true``.|

### Rego
| Built-in | Inputs | Description |
Expand Down
29 changes: 25 additions & 4 deletions topdown/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package topdown
import (
"bytes"
"encoding/json"
"fmt"
"strconv"

"net/http"
"os"
Expand All @@ -19,7 +21,7 @@ import (

const defaultHTTPRequestTimeout = time.Second * 5

var allowedKeys = ast.NewSet(ast.StringTerm("method"), ast.StringTerm("url"), ast.StringTerm("body"))
var allowedKeys = ast.NewSet(ast.StringTerm("method"), ast.StringTerm("url"), ast.StringTerm("body"), ast.StringTerm("enable_redirect"))
var requiredKeys = ast.NewSet(ast.StringTerm("method"), ast.StringTerm("url"))

var client *http.Client
Expand Down Expand Up @@ -50,8 +52,13 @@ func createHTTPClient() {
if timeoutDuration != "" {
timeout, _ = time.ParseDuration(timeoutDuration)
}

// create a http client with redirects disabled
client = &http.Client{
Timeout: timeout,
CheckRedirect: func(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
},
}
}

Expand Down Expand Up @@ -82,20 +89,27 @@ func executeHTTPRequest(bctx BuiltinContext, obj ast.Object) (ast.Value, error)
var url string
var method string
var body *bytes.Buffer
var enableRedirect bool
for _, val := range obj.Keys() {
key, err := ast.JSON(val.Value)
if err != nil {
return nil, err
}
key = key.(string)

if key == "method" {
switch key {
case "method":
method = obj.Get(val).String()
method = strings.Trim(method, "\"")
} else if key == "url" {
case "url":
url = obj.Get(val).String()
url = strings.Trim(url, "\"")
} else {
case "enable_redirect":
enableRedirect, err = strconv.ParseBool(obj.Get(val).String())
if err != nil {
return nil, err
}
case "body":
bodyVal := obj.Get(val).Value
bodyValInterface, err := ast.JSON(bodyVal)
if err != nil {
Expand All @@ -107,9 +121,16 @@ func executeHTTPRequest(bctx BuiltinContext, obj ast.Object) (ast.Value, error)
return nil, err
}
body = bytes.NewBuffer(bodyValBytes)
default:
return nil, fmt.Errorf("Invalid Key %v", key)
}
}

// check if redirects are enabled
if enableRedirect {
client.CheckRedirect = nil
}

if body == nil {
body = bytes.NewBufferString("")
}
Expand Down
47 changes: 47 additions & 0 deletions topdown/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,50 @@ func TestInvalidKeyError(t *testing.T) {
runTopDownTestCase(t, data, tc.note, tc.rules, tc.expected)
}
}

// TestHTTPRedirectDisable tests redirects are not enabled by default
func TestHTTPRedirectDisable(t *testing.T) {

// expected result
expectedResult := make(map[string]interface{})
expectedResult["status"] = "301 Moved Permanently"
expectedResult["status_code"] = http.StatusMovedPermanently
expectedResult["body"] = nil

resultObj, err := ast.InterfaceToValue(expectedResult)
if err != nil {
panic(err)
}

var testURL = "http://google.com"
data := loadSmallTestData()
rule := []string{fmt.Sprintf(
`p = x { http.send({"method": "get", "url": "%s"}, x) }`, testURL)}

// run the test
runTopDownTestCase(t, data, "http.send", rule, resultObj.String())

}

// TestHTTPRedirectEnable tests redirects are enabled
func TestHTTPRedirectEnable(t *testing.T) {

// expected result
expectedResult := make(map[string]interface{})
expectedResult["status"] = "200 OK"
expectedResult["status_code"] = http.StatusOK
expectedResult["body"] = nil

resultObj, err := ast.InterfaceToValue(expectedResult)
if err != nil {
panic(err)
}

var testURL = "http://google.com"
data := loadSmallTestData()
rule := []string{fmt.Sprintf(
`p = x { http.send({"method": "get", "url": "%s", "enable_redirect": true}, x) }`, testURL)}

// run the test
runTopDownTestCase(t, data, "http.send", rule, resultObj.String())
}

0 comments on commit 51d5d11

Please sign in to comment.