Skip to content

Commit 59e1471

Browse files
author
nuwandi-wickramasinghe_zse
committed
test opaauthorizerequest filter with S3 decsion log update
1 parent 5049a17 commit 59e1471

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed

filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http/httptest"
88
"strings"
99
"testing"
10+
"time"
1011

1112
opasdktest "github.com/open-policy-agent/opa/v1/sdk/test"
1213
"github.com/stretchr/testify/assert"
@@ -986,6 +987,275 @@ func TestAuthorizeRequestFilter(t *testing.T) {
986987
}
987988
}
988989

990+
func TestAuthorizeRequestFilterWithS3DecisionLogPlugin(t *testing.T) {
991+
for _, ti := range []struct {
992+
msg string
993+
filterName string
994+
extraeskipBefore string
995+
extraeskipAfter string
996+
regoQuery string
997+
requestPath string
998+
requestMethod string
999+
body string
1000+
contextExtensions string
1001+
expectedBody string
1002+
expectedHeaders http.Header
1003+
expectedStatus int
1004+
backendHeaders http.Header
1005+
removeHeaders http.Header
1006+
discoveryPath string
1007+
discoveryBundle string
1008+
}{
1009+
{
1010+
msg: "Allow Requests for log upload success",
1011+
filterName: "opaAuthorizeRequest",
1012+
regoQuery: "envoy/authz/allow",
1013+
requestPath: "/allow",
1014+
requestMethod: "GET",
1015+
contextExtensions: "",
1016+
expectedStatus: http.StatusOK,
1017+
expectedBody: "Welcome!",
1018+
expectedHeaders: make(http.Header),
1019+
backendHeaders: make(http.Header),
1020+
removeHeaders: make(http.Header),
1021+
discoveryPath: "logs-success",
1022+
},
1023+
{
1024+
msg: "Deny Requests for log upload success",
1025+
filterName: "opaAuthorizeRequest",
1026+
regoQuery: "envoy/authz/allow",
1027+
requestPath: "/not-allow",
1028+
requestMethod: "GET",
1029+
contextExtensions: "",
1030+
expectedStatus: http.StatusForbidden,
1031+
expectedBody: "",
1032+
expectedHeaders: make(http.Header),
1033+
backendHeaders: make(http.Header),
1034+
removeHeaders: make(http.Header),
1035+
discoveryPath: "logs-success",
1036+
},
1037+
{
1038+
msg: "Allow Requests for log upload timeout",
1039+
filterName: "opaAuthorizeRequest",
1040+
regoQuery: "envoy/authz/allow",
1041+
requestPath: "/allow",
1042+
requestMethod: "GET",
1043+
contextExtensions: "",
1044+
expectedStatus: http.StatusOK,
1045+
expectedBody: "Welcome!",
1046+
expectedHeaders: make(http.Header),
1047+
backendHeaders: make(http.Header),
1048+
removeHeaders: make(http.Header),
1049+
discoveryPath: "logs-timeout",
1050+
},
1051+
{
1052+
msg: "Allow Requests for log upload forbidden",
1053+
filterName: "opaAuthorizeRequest",
1054+
regoQuery: "envoy/authz/allow",
1055+
requestPath: "/allow",
1056+
requestMethod: "GET",
1057+
contextExtensions: "",
1058+
expectedStatus: http.StatusOK,
1059+
expectedBody: "Welcome!",
1060+
expectedHeaders: make(http.Header),
1061+
backendHeaders: make(http.Header),
1062+
removeHeaders: make(http.Header),
1063+
discoveryPath: "logs-forbidden",
1064+
},
1065+
{
1066+
msg: "Allow Requests for log upload path not found",
1067+
filterName: "opaAuthorizeRequest",
1068+
regoQuery: "envoy/authz/allow",
1069+
requestPath: "/allow",
1070+
requestMethod: "GET",
1071+
contextExtensions: "",
1072+
expectedStatus: http.StatusOK,
1073+
expectedBody: "Welcome!",
1074+
expectedHeaders: make(http.Header),
1075+
backendHeaders: make(http.Header),
1076+
removeHeaders: make(http.Header),
1077+
discoveryPath: "logs-notfound",
1078+
},
1079+
{
1080+
msg: "Allow Requests for log upload throws 5xx",
1081+
filterName: "opaAuthorizeRequest",
1082+
regoQuery: "envoy/authz/allow",
1083+
requestPath: "/allow",
1084+
requestMethod: "GET",
1085+
contextExtensions: "",
1086+
expectedStatus: http.StatusOK,
1087+
expectedBody: "Welcome!",
1088+
expectedHeaders: make(http.Header),
1089+
backendHeaders: make(http.Header),
1090+
removeHeaders: make(http.Header),
1091+
discoveryPath: "logs-5xx",
1092+
},
1093+
} {
1094+
t.Run(ti.msg, func(t *testing.T) {
1095+
t.Logf("Running test for %v", ti)
1096+
clientServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1097+
w.Write([]byte("Welcome!"))
1098+
assert.True(t, isHeadersPresent(t, ti.backendHeaders, r.Header), "Enriched request header is absent.")
1099+
assert.True(t, isHeadersAbsent(t, ti.removeHeaders, r.Header), "Unwanted HTTP Headers present.")
1100+
1101+
body, err := io.ReadAll(r.Body)
1102+
if err != nil {
1103+
t.Fatal(err)
1104+
}
1105+
assert.Equal(t, ti.body, string(body))
1106+
}))
1107+
defer clientServer.Close()
1108+
1109+
logUploadCount := 0
1110+
s3Server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1111+
if strings.Contains(r.URL.Path, "logs-success") {
1112+
logUploadCount++
1113+
w.WriteHeader(http.StatusOK)
1114+
} else if strings.Contains(r.URL.Path, "logs-forbidden") {
1115+
logUploadCount++
1116+
w.WriteHeader(http.StatusForbidden)
1117+
} else if strings.Contains(r.URL.Path, "logs-timeout") {
1118+
logUploadCount++
1119+
time.Sleep(5 * time.Second)
1120+
w.WriteHeader(http.StatusOK)
1121+
} else if strings.Contains(r.URL.Path, "logs-5xx") {
1122+
logUploadCount++
1123+
w.WriteHeader(http.StatusInternalServerError)
1124+
} else {
1125+
logUploadCount++
1126+
w.WriteHeader(http.StatusNotFound)
1127+
}
1128+
}))
1129+
defer s3Server.Close()
1130+
1131+
opaControlPlane := opasdktest.MustNewServer(
1132+
opasdktest.MockBundle("/bundles/test", map[string]string{
1133+
"main.rego": `
1134+
package envoy.authz
1135+
1136+
default allow = false
1137+
1138+
allow if {
1139+
input.parsed_path == [ "allow" ]
1140+
input.parsed_query == {}
1141+
}
1142+
`,
1143+
}),
1144+
opasdktest.MockBundle("/bundles/discovery-eopa", map[string]string{
1145+
"data.json": fmt.Sprintf(`{
1146+
"discovery": {
1147+
"bundles": {
1148+
"bundles/test": {
1149+
"persist": false,
1150+
"resource": "bundles/test",
1151+
"service": "test"
1152+
}
1153+
},
1154+
"decision_logs": {
1155+
"plugin": "eopa_dl"
1156+
},
1157+
"plugins": {
1158+
"eopa_dl": {
1159+
"buffer": {
1160+
"type": "memory",
1161+
"max_bytes": 50000000
1162+
},
1163+
"output": {
1164+
"type": "s3",
1165+
"bucket": %q,
1166+
"endpoint": %q,
1167+
"force_path": true,
1168+
"region": "eu-central-1",
1169+
"access_key_id": "myid",
1170+
"access_secret": "mysecret",
1171+
"timeout": "2s",
1172+
"batching": {
1173+
"at_bytes": 10000000,
1174+
"at_period": "1s"
1175+
}
1176+
}
1177+
}
1178+
}}
1179+
}`, ti.discoveryPath, s3Server.URL)}),
1180+
)
1181+
1182+
config := []byte(fmt.Sprintf(`{
1183+
"services": {
1184+
"test": {
1185+
"url": %q,
1186+
"response_header_timeout_seconds": 1
1187+
}
1188+
},
1189+
"labels": {
1190+
"environment": "envValue"
1191+
},
1192+
"discovery": {
1193+
"name": "discovery",
1194+
"resource": %q,
1195+
"service": "test"
1196+
}
1197+
}`, opaControlPlane.URL(), "/bundles/discovery-eopa"))
1198+
1199+
fr := make(filters.Registry)
1200+
1201+
envoyMetaDataConfig := []byte(`{
1202+
"filter_metadata": {
1203+
"envoy.filters.http.header_to_metadata": {
1204+
"policy_type": "ingress"
1205+
}
1206+
}
1207+
}`)
1208+
1209+
opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0)
1210+
opts = append(opts,
1211+
openpolicyagent.WithConfigTemplate(config),
1212+
openpolicyagent.WithEnvoyMetadataBytes(envoyMetaDataConfig),
1213+
)
1214+
opaFactory, err := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(tracingtest.NewTracer()),
1215+
openpolicyagent.WithOpenPolicyAgentInstanceConfig(opts...), openpolicyagent.WithEnableEopaPlugins(true))
1216+
assert.NoError(t, err)
1217+
1218+
ftSpec := NewOpaAuthorizeRequestSpec(opaFactory)
1219+
fr.Register(ftSpec)
1220+
ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory)
1221+
fr.Register(ftSpec)
1222+
fr.Register(builtin.NewSetPath())
1223+
1224+
r := eskip.MustParse(fmt.Sprintf(`* -> %s %s("%s", "%s") %s -> "%s"`, ti.extraeskipBefore, ti.filterName, "test", ti.contextExtensions, ti.extraeskipAfter, clientServer.URL))
1225+
1226+
proxy := proxytest.New(fr, r...)
1227+
1228+
var bodyReader io.Reader
1229+
if ti.body != "" {
1230+
bodyReader = strings.NewReader(ti.body)
1231+
}
1232+
1233+
req, err := http.NewRequest(ti.requestMethod, proxy.URL+ti.requestPath, bodyReader)
1234+
assert.NoError(t, err)
1235+
1236+
rsp, err := proxy.Client().Do(req)
1237+
assert.NoError(t, err)
1238+
1239+
assert.Equal(t, ti.expectedStatus, rsp.StatusCode, "HTTP status does not match")
1240+
1241+
assert.True(t, isHeadersPresent(t, ti.expectedHeaders, rsp.Header), "HTTP Headers do not match")
1242+
defer rsp.Body.Close()
1243+
body, err := io.ReadAll(rsp.Body)
1244+
assert.NoError(t, err)
1245+
assert.Equal(t, ti.expectedBody, string(body), "HTTP Body does not match")
1246+
1247+
time.Sleep(2 * time.Second) // wait for async decision log to be sent
1248+
assert.True(t, logUploadCount >= 1, "Decision log upload was not attempted")
1249+
1250+
// Simulate a second request while decision log batching/upload is in progress
1251+
proxy.Client().Timeout = 20 * time.Millisecond
1252+
rsp, err = proxy.Client().Do(req)
1253+
assert.NoError(t, err)
1254+
assert.Equal(t, ti.expectedStatus, rsp.StatusCode, "HTTP status does not match")
1255+
})
1256+
}
1257+
}
1258+
9891259
func TestCreateFilterArguments(t *testing.T) {
9901260
opaRegistry, err := openpolicyagent.NewOpenPolicyAgentRegistry(
9911261
openpolicyagent.WithOpenPolicyAgentInstanceConfig(openpolicyagent.WithConfigTemplate([]byte(""))))

0 commit comments

Comments
 (0)