1
1
package main
2
2
3
3
import (
4
+ "context"
4
5
"encoding/json"
5
6
"fmt"
7
+ "io"
6
8
"net/http"
9
+ "net/http/httptest"
7
10
"os"
8
11
"strings"
9
12
"testing"
10
13
11
- "github.com/caarlos0/env/v11"
12
14
"github.com/rond-authz/rond/core"
13
15
"github.com/rond-authz/rond/internal/config"
14
16
"github.com/rond-authz/rond/internal/testutils"
15
17
"github.com/rond-authz/rond/openapi"
18
+
19
+ "github.com/caarlos0/env/v11"
20
+ "github.com/fredmaggiowski/gowq"
16
21
"github.com/sirupsen/logrus/hooks/test"
17
22
"github.com/stretchr/testify/require"
18
23
"gopkg.in/h2non/gock.v1"
@@ -80,8 +85,187 @@ func BenchmarkStartup(b *testing.B) {
80
85
}
81
86
}
82
87
88
+ func TestStartupAndLoadWithConcurrentRequests (t * testing.T ) {
89
+ log , _ := test .NewNullLogger ()
90
+
91
+ tmpdir , err := os .MkdirTemp ("" , "rond-startup-test-" )
92
+ require .NoError (t , err )
93
+
94
+ policies := []string {`package policies` }
95
+ policies = append (policies , `allow_get {
96
+ verb := input.request.method
97
+ verb == "GET"
98
+ }` )
99
+ policies = append (policies , `allow_post {
100
+ verb := input.request.method
101
+ verb == "POST"
102
+ }` )
103
+ policies = append (policies , generateFilterPolicy ("something" )) // filter_something
104
+ policies = append (policies , generateProjectionPolicy ("data" )) // proj_data
105
+
106
+ oas := & openapi.OpenAPISpec {
107
+ Paths : map [string ]openapi.PathVerbs {
108
+ "/allow-get" : {
109
+ http .MethodGet : {
110
+ PermissionV2 : & core.RondConfig {
111
+ RequestFlow : core.RequestFlow {PolicyName : "allow_get" },
112
+ },
113
+ },
114
+ http .MethodPost : {
115
+ PermissionV2 : & core.RondConfig {
116
+ RequestFlow : core.RequestFlow {PolicyName : "allow_get" },
117
+ },
118
+ },
119
+ },
120
+ "/filter-something" : {
121
+ http .MethodGet : {
122
+ PermissionV2 : & core.RondConfig {
123
+ RequestFlow : core.RequestFlow {PolicyName : "filter_something" , GenerateQuery : true },
124
+ },
125
+ },
126
+ },
127
+ "/project-data" : {
128
+ http .MethodPost : {
129
+ PermissionV2 : & core.RondConfig {
130
+ RequestFlow : core.RequestFlow {PolicyName : "allow_post" },
131
+ ResponseFlow : core.ResponseFlow {PolicyName : "proj_data" },
132
+ },
133
+ },
134
+ },
135
+ },
136
+ }
137
+ oasFileName := writeOAS (t , tmpdir , oas )
138
+ policiesFileName := writePolicies (t , tmpdir , policies )
139
+
140
+ defer gock .Off ()
141
+ defer gock .DisableNetworkingFilters ()
142
+ defer gock .DisableNetworking ()
143
+
144
+ gock .EnableNetworking ()
145
+ gock .NetworkingFilter (func (r * http.Request ) bool {
146
+ if r .URL .Host == "localhost:3050" {
147
+ return false
148
+ }
149
+ if r .URL .Path == "/documentation/json" && r .URL .Host == "localhost:3050" {
150
+ return false
151
+ }
152
+ return true
153
+ })
154
+
155
+ gock .New ("http://localhost:3050" ).
156
+ Persist ().
157
+ Get ("/documentation/json" ).
158
+ Reply (200 ).
159
+ File (oasFileName )
160
+
161
+ gock .New ("http://localhost:3050/" ).
162
+ Persist ().
163
+ Get ("/allow-get" ).
164
+ Reply (200 )
165
+ gock .New ("http://localhost:3050/" ).
166
+ Persist ().
167
+ Get ("/filter-something" ).
168
+ MatchHeader ("acl_rows" , `{"$or":[{"$and":[{"key":{"$eq":42}}]}]}` ).
169
+ Reply (200 )
170
+ gock .New ("http://localhost:3050/" ).
171
+ Persist ().
172
+ Post ("/project-data" ).
173
+ Reply (200 ).
174
+ JSON ([]struct {
175
+ Key string `json:"key"`
176
+ }{
177
+ {"k1" },
178
+ {"k2" },
179
+ })
180
+
181
+ mongoHost := os .Getenv ("MONGO_HOST_CI" )
182
+ if mongoHost == "" {
183
+ mongoHost = testutils .LocalhostMongoDB
184
+ t .Logf ("Connection to localhost MongoDB, on CI env this is a problem!" )
185
+ }
186
+ randomizedDBNamePart := testutils .GetRandomName (10 )
187
+ mongoDBName := fmt .Sprintf ("test-%s" , randomizedDBNamePart )
188
+ envs , err := env.ParseAsWithOptions [config.EnvironmentVariables ](env.Options {
189
+ Environment : map [string ]string {
190
+ "TARGET_SERVICE_HOST" : "localhost:3050" ,
191
+ "TARGET_SERVICE_OAS_PATH" : "/documentation/json" ,
192
+ "OPA_MODULES_DIRECTORY" : policiesFileName ,
193
+ "LOG_LEVEL" : "fatal" ,
194
+ "MONGODB_URL" : fmt .Sprintf ("mongodb://%s/%s" , mongoHost , mongoDBName ),
195
+ "BINDINGS_COLLECTION_NAME" : "bindings" ,
196
+ "ROLES_COLLECTION_NAME" : "roles" ,
197
+ },
198
+ })
199
+ require .NoError (t , err )
200
+
201
+ app , err := setupService (envs , log )
202
+ require .NoError (t , err )
203
+ require .True (t , <- app .sdkBootState .IsReadyChan ())
204
+ defer app .close ()
205
+
206
+ // everything is up and running, now start bombarding the webserver
207
+ type RequestConf struct {
208
+ Verb string
209
+ Path string
210
+ ExpectedStatus int
211
+ ExpectedBody string
212
+ }
213
+ dictionary := []RequestConf {
214
+ {Verb : http .MethodGet , Path : "/allow-get" , ExpectedStatus : http .StatusOK },
215
+ {Verb : http .MethodPost , Path : "/allow-get" , ExpectedStatus : http .StatusForbidden },
216
+ {Verb : http .MethodGet , Path : "/filter-something" , ExpectedStatus : http .StatusOK },
217
+ {Verb : http .MethodPost , Path : "/filter-something" , ExpectedStatus : http .StatusNotFound },
218
+ {Verb : http .MethodPost , Path : "/project-data" , ExpectedBody : `["k1","k2"]` , ExpectedStatus : http .StatusOK },
219
+ {Verb : http .MethodGet , Path : "/project-data" , ExpectedStatus : http .StatusNotFound },
220
+ }
221
+
222
+ queue := gowq.New [RequestConf ](100 )
223
+
224
+ i := 0
225
+ for i < 100_000 {
226
+ d := dictionary [i % len (dictionary )]
227
+ i ++
228
+ queue .Push (func (ctx context.Context ) (RequestConf , error ) {
229
+ w := httptest .NewRecorder ()
230
+
231
+ req := httptest .NewRequest (d .Verb , d .Path , nil )
232
+ app .router .ServeHTTP (w , req )
233
+ require .Equal (t , d .ExpectedStatus , w .Result ().StatusCode )
234
+
235
+ if d .ExpectedBody != "" {
236
+ data , err := io .ReadAll (w .Body )
237
+ require .NoError (t , err )
238
+ require .Equal (t , d .ExpectedBody , string (data ))
239
+ }
240
+
241
+ return d , nil
242
+ })
243
+ }
244
+
245
+ _ , errors := queue .RunAll (context .TODO ())
246
+ require .Len (t , errors , 0 )
247
+ }
248
+
249
+ func writeOAS (t require.TestingT , tmpdir string , oas * openapi.OpenAPISpec ) string {
250
+ oasContent , err := json .Marshal (oas )
251
+ require .NoError (t , err )
252
+
253
+ oasFileName := fmt .Sprintf ("%s/oas.json" , tmpdir )
254
+ err = os .WriteFile (oasFileName , oasContent , 0644 )
255
+ require .NoError (t , err )
256
+ return oasFileName
257
+ }
258
+
259
+ func writePolicies (t require.TestingT , tmpdir string , policies []string ) string {
260
+ policyFileName := fmt .Sprintf ("%s/policies.rego" , tmpdir )
261
+ policiesContent := []byte (strings .Join (policies , "\n " ))
262
+ err := os .WriteFile (policyFileName , policiesContent , 0644 )
263
+ require .NoError (t , err )
264
+ return policyFileName
265
+ }
266
+
83
267
func generateAndSaveConfig (t require.TestingT , tmpdir string , numberOfPaths int ) (string , string ) {
84
- oas := openapi.OpenAPISpec {
268
+ oas := & openapi.OpenAPISpec {
85
269
Paths : make (map [string ]openapi.PathVerbs ),
86
270
}
87
271
policies := []string {"package policies" }
@@ -135,17 +319,8 @@ func generateAndSaveConfig(t require.TestingT, tmpdir string, numberOfPaths int)
135
319
}
136
320
}
137
321
138
- oasContent , err := json .Marshal (oas )
139
- require .NoError (t , err )
140
-
141
- oasFileName := fmt .Sprintf ("%s/oas.json" , tmpdir )
142
- err = os .WriteFile (oasFileName , oasContent , 0644 )
143
- require .NoError (t , err )
144
-
145
- policyFileName := fmt .Sprintf ("%s/policies.rego" , tmpdir )
146
- policiesContent := []byte (strings .Join (policies , "\n " ))
147
- err = os .WriteFile (policyFileName , policiesContent , 0644 )
148
- require .NoError (t , err )
322
+ oasFileName := writeOAS (t , tmpdir , oas )
323
+ policyFileName := writePolicies (t , tmpdir , policies )
149
324
150
325
return oasFileName , policyFileName
151
326
}
@@ -159,11 +334,11 @@ func generateFilterPolicy(name string) string {
159
334
160
335
func generateProjectionPolicy (name string ) string {
161
336
return fmt .Sprintf (`proj_%s [projects] {
162
- projects := [projects_with_envs_filtered |
337
+ projects := [kept |
163
338
project := input.response.body[_]
164
- projects_with_envs_filtered := project
339
+ kept := project.key
165
340
]
166
- }` , name )
341
+ }` , name )
167
342
}
168
343
169
344
func generateAllowPolicy (name string ) string {
0 commit comments