1package cloudflare 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10) 11 12var firewallRulePageOpts = PaginationOptions{ 13 PerPage: 25, 14 Page: 1, 15} 16 17func TestFirewallRules(t *testing.T) { 18 setup() 19 defer teardown() 20 21 handler := func(w http.ResponseWriter, r *http.Request) { 22 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 23 w.Header().Set("content-type", "application/json") 24 fmt.Fprintf(w, `{ 25 "result":[ 26 { 27 "id":"2ae338944d6143383c3cf05a7c80d984", 28 "paused":false, 29 "description":"allow uploads without waf", 30 "action":"bypass", 31 "products": ["waf"], 32 "priority":null, 33 "filter":{ 34 "id":"74217d7bd5ab435e84b1bd473bf4fb9f", 35 "expression":"http.request.uri.path matches \"^/upload$\"", 36 "paused":false, 37 "description":"/upload" 38 } 39 }, 40 { 41 "id":"4ae338944d6143378c3cf05a7c77d983", 42 "paused":false, 43 "description":"allow API traffic without challenge", 44 "action":"allow", 45 "priority":null, 46 "filter":{ 47 "id":"14217d7bd5ab435e84b1bd468bf4fb9f", 48 "expression":"http.request.uri.path matches \"^/api/.*$\"", 49 "paused":false, 50 "description":"/api" 51 } 52 }, 53 { 54 "id":"f2d427378e7542acb295380d352e2ebd", 55 "paused":false, 56 "description":"do not challenge login from office", 57 "action":"allow", 58 "priority":null, 59 "filter":{ 60 "id":"b7ff25282d394be7b945e23c7106ce8a", 61 "expression":"(http.request.uri.path ~ \"^.*/xmlrpc.php$\"", 62 "paused":false, 63 "description":"wordpress xmlrpc" 64 } 65 }, 66 { 67 "id":"cbf4b7a5a2a24e59a03044d6d44ceb09", 68 "paused":false, 69 "description":"challenge login", 70 "action":"challenge", 71 "priority":null, 72 "filter":{ 73 "id":"c218c536b2bd406f958f278cf0fa8c0f", 74 "expression":"(http.request.uri.path ~ \"^.*/wp-login.php$\"", 75 "paused":false, 76 "description":"Login" 77 } 78 }, 79 { 80 "id":"52161eb6af4241bb9d4b32394be72fdf", 81 "paused":false, 82 "description":"JS challenge site", 83 "action":"js_challenge", 84 "priority":null, 85 "filter":{ 86 "id":"f2a64520581a4209aab12187a0081364", 87 "expression":"not http.request.uri.path matches \"^/api/.*$\"", 88 "paused":false, 89 "description":"not /api" 90 } 91 } 92 ], 93 "success":true, 94 "errors":null, 95 "messages":null, 96 "result_info":{ 97 "page":1, 98 "per_page":25, 99 "count":5, 100 "total_count":5 101 } 102 } 103 `) 104 } 105 106 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules", handler) 107 want := []FirewallRule{ 108 { 109 ID: "2ae338944d6143383c3cf05a7c80d984", 110 Paused: false, 111 Description: "allow uploads without waf", 112 Action: "bypass", 113 Priority: nil, 114 Products: []string{"waf"}, 115 Filter: Filter{ 116 ID: "74217d7bd5ab435e84b1bd473bf4fb9f", 117 Expression: "http.request.uri.path matches \"^/upload$\"", 118 Paused: false, 119 Description: "/upload", 120 }, 121 }, 122 { 123 ID: "4ae338944d6143378c3cf05a7c77d983", 124 Paused: false, 125 Description: "allow API traffic without challenge", 126 Action: "allow", 127 Priority: nil, 128 Filter: Filter{ 129 ID: "14217d7bd5ab435e84b1bd468bf4fb9f", 130 Expression: "http.request.uri.path matches \"^/api/.*$\"", 131 Paused: false, 132 Description: "/api", 133 }, 134 }, 135 { 136 ID: "f2d427378e7542acb295380d352e2ebd", 137 Paused: false, 138 Description: "do not challenge login from office", 139 Action: "allow", 140 Priority: nil, 141 Filter: Filter{ 142 ID: "b7ff25282d394be7b945e23c7106ce8a", 143 Expression: "(http.request.uri.path ~ \"^.*/xmlrpc.php$\"", 144 Paused: false, 145 Description: "wordpress xmlrpc", 146 }, 147 }, 148 { 149 ID: "cbf4b7a5a2a24e59a03044d6d44ceb09", 150 Paused: false, 151 Description: "challenge login", 152 Action: "challenge", 153 Priority: nil, 154 Filter: Filter{ 155 ID: "c218c536b2bd406f958f278cf0fa8c0f", 156 Expression: "(http.request.uri.path ~ \"^.*/wp-login.php$\"", 157 Paused: false, 158 Description: "Login", 159 }, 160 }, 161 { 162 ID: "52161eb6af4241bb9d4b32394be72fdf", 163 Paused: false, 164 Description: "JS challenge site", 165 Action: "js_challenge", 166 Priority: nil, 167 Filter: Filter{ 168 ID: "f2a64520581a4209aab12187a0081364", 169 Expression: "not http.request.uri.path matches \"^/api/.*$\"", 170 Paused: false, 171 Description: "not /api", 172 }, 173 }, 174 } 175 176 actual, err := client.FirewallRules(context.Background(), "d56084adb405e0b7e32c52321bf07be6", firewallRulePageOpts) 177 178 if assert.NoError(t, err) { 179 assert.Equal(t, want, actual) 180 } 181} 182 183func TestFirewallRule(t *testing.T) { 184 setup() 185 defer teardown() 186 187 handler := func(w http.ResponseWriter, r *http.Request) { 188 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 189 w.Header().Set("content-type", "application/json") 190 fmt.Fprintf(w, `{ 191 "result":{ 192 "id":"f2d427378e7542acb295380d352e2ebd", 193 "paused":false, 194 "description":"do not challenge login from office", 195 "action":"allow", 196 "priority":null, 197 "filter":{ 198 "id":"b7ff25282d394be7b945e23c7106ce8a", 199 "expression":"ip.src in {127.0.0.1} ~ \"^.*/login.php$\")", 200 "paused":false, 201 "description":"Login from office" 202 } 203 }, 204 "success":true, 205 "errors":null, 206 "messages":null 207 } 208 `) 209 } 210 211 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules/f2d427378e7542acb295380d352e2ebd", handler) 212 want := FirewallRule{ 213 ID: "f2d427378e7542acb295380d352e2ebd", 214 Paused: false, 215 Description: "do not challenge login from office", 216 Action: "allow", 217 Priority: nil, 218 Filter: Filter{ 219 ID: "b7ff25282d394be7b945e23c7106ce8a", 220 Expression: "ip.src in {127.0.0.1} ~ \"^.*/login.php$\")", 221 Paused: false, 222 Description: "Login from office", 223 }, 224 } 225 226 actual, err := client.FirewallRule(context.Background(), "d56084adb405e0b7e32c52321bf07be6", "f2d427378e7542acb295380d352e2ebd") 227 228 if assert.NoError(t, err) { 229 assert.Equal(t, want, actual) 230 } 231} 232 233func TestCreateSingleFirewallRule(t *testing.T) { 234 setup() 235 defer teardown() 236 237 handler := func(w http.ResponseWriter, r *http.Request) { 238 assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) 239 w.Header().Set("content-type", "application/json") 240 fmt.Fprintf(w, `{ 241 "result":[ 242 { 243 "id":"f2d427378e7542acb295380d352e2ebd", 244 "paused":false, 245 "description":"do not challenge login from office", 246 "action":"allow", 247 "priority":null, 248 "filter":{ 249 "id":"b7ff25282d394be7b945e23c7106ce8a", 250 "expression":"ip.src in {127.0.0.0/24}", 251 "paused":false, 252 "description":"Login from office" 253 } 254 } 255 ], 256 "success":true, 257 "errors":null, 258 "messages":null 259 } 260 `) 261 } 262 263 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules", handler) 264 want := []FirewallRule{ 265 { 266 ID: "f2d427378e7542acb295380d352e2ebd", 267 Paused: false, 268 Description: "do not challenge login from office", 269 Action: "allow", 270 Priority: nil, 271 Filter: Filter{ 272 ID: "b7ff25282d394be7b945e23c7106ce8a", 273 Expression: "ip.src in {127.0.0.0/24}", 274 Paused: false, 275 Description: "Login from office", 276 }, 277 }, 278 } 279 280 actual, err := client.CreateFirewallRules(context.Background(), "d56084adb405e0b7e32c52321bf07be6", want) 281 282 if assert.NoError(t, err) { 283 assert.Equal(t, want, actual) 284 } 285} 286 287func TestCreateMultipleFirewallRules(t *testing.T) { 288 setup() 289 defer teardown() 290 291 handler := func(w http.ResponseWriter, r *http.Request) { 292 assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) 293 w.Header().Set("content-type", "application/json") 294 fmt.Fprintf(w, `{ 295 "result":[ 296 { 297 "id":"f2d427378e7542acb295380d352e2ebd", 298 "paused":false, 299 "description":"do not challenge login from office", 300 "action":"allow", 301 "priority":null, 302 "filter":{ 303 "id":"b7ff25282d394be7b945e23c7106ce8a", 304 "expression":"ip.src in {127.0.0.0/24}", 305 "paused":false, 306 "description":"Login from office" 307 } 308 }, 309 { 310 "id":"cbf4b7a5a2a24e59a03044d6d44ceb09", 311 "paused":false, 312 "description":"challenge login", 313 "action":"challenge", 314 "priority":null, 315 "filter":{ 316 "id":"c218c536b2bd406f958f278cf0fa8c0f", 317 "expression":"(http.request.uri.path ~ \"^.*/wp-login.php$\")", 318 "paused":false, 319 "description":"Login" 320 } 321 } 322 ], 323 "success":true, 324 "errors":null, 325 "messages":null 326 } 327 `) 328 } 329 330 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules", handler) 331 want := []FirewallRule{ 332 { 333 ID: "f2d427378e7542acb295380d352e2ebd", 334 Paused: false, 335 Description: "do not challenge login from office", 336 Action: "allow", 337 Priority: nil, 338 Filter: Filter{ 339 ID: "b7ff25282d394be7b945e23c7106ce8a", 340 Expression: "ip.src in {127.0.0.0/24}", 341 Paused: false, 342 Description: "Login from office", 343 }, 344 }, 345 { 346 ID: "cbf4b7a5a2a24e59a03044d6d44ceb09", 347 Paused: false, 348 Description: "challenge login", 349 Action: "challenge", 350 Priority: nil, 351 Filter: Filter{ 352 ID: "c218c536b2bd406f958f278cf0fa8c0f", 353 Expression: "(http.request.uri.path ~ \"^.*/wp-login.php$\")", 354 Paused: false, 355 Description: "Login", 356 }, 357 }, 358 } 359 360 actual, err := client.CreateFirewallRules(context.Background(), "d56084adb405e0b7e32c52321bf07be6", want) 361 362 if assert.NoError(t, err) { 363 assert.Equal(t, want, actual) 364 } 365} 366 367func TestUpdateFirewallRuleWithMissingID(t *testing.T) { 368 setup() 369 defer teardown() 370 371 want := FirewallRule{ 372 ID: "", 373 Paused: false, 374 Description: "challenge site", 375 Action: "challenge", 376 Priority: nil, 377 Filter: Filter{ 378 ID: "f2a64520581a4209aab12187a0081364", 379 Expression: "not http.request.uri.path matches \"^/api/.*$\"", 380 Paused: false, 381 Description: "not /api", 382 }, 383 } 384 385 _, err := client.UpdateFirewallRule(context.Background(), "d56084adb405e0b7e32c52321bf07be6", want) 386 assert.EqualError(t, err, "firewall rule ID cannot be empty") 387} 388 389func TestUpdateSingleFirewallRule(t *testing.T) { 390 setup() 391 defer teardown() 392 393 handler := func(w http.ResponseWriter, r *http.Request) { 394 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 395 w.Header().Set("content-type", "application/json") 396 fmt.Fprintf(w, `{ 397 "result":{ 398 "id":"52161eb6af4241bb9d4b32394be72fdf", 399 "paused":false, 400 "description":"challenge site", 401 "action":"challenge", 402 "priority":null, 403 "filter":{ 404 "id":"f2a64520581a4209aab12187a0081364", 405 "expression":"not http.request.uri.path matches \"^/api/.*$\"", 406 "paused":false, 407 "description":"not /api" 408 } 409 }, 410 "success":true, 411 "errors":null, 412 "messages":null 413 } 414 `) 415 } 416 417 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules/52161eb6af4241bb9d4b32394be72fdf", handler) 418 want := FirewallRule{ 419 ID: "52161eb6af4241bb9d4b32394be72fdf", 420 Paused: false, 421 Description: "challenge site", 422 Action: "challenge", 423 Priority: nil, 424 Filter: Filter{ 425 ID: "f2a64520581a4209aab12187a0081364", 426 Expression: "not http.request.uri.path matches \"^/api/.*$\"", 427 Paused: false, 428 Description: "not /api", 429 }, 430 } 431 432 actual, err := client.UpdateFirewallRule(context.Background(), "d56084adb405e0b7e32c52321bf07be6", want) 433 434 if assert.NoError(t, err) { 435 assert.Equal(t, want, actual) 436 } 437} 438 439func TestUpdateMultipleFirewallRules(t *testing.T) { 440 setup() 441 defer teardown() 442 443 handler := func(w http.ResponseWriter, r *http.Request) { 444 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 445 w.Header().Set("content-type", "application/json") 446 fmt.Fprintf(w, `{ 447 "result":[ 448 { 449 "id":"f2d427378e7542acb295380d352e2ebd", 450 "paused":false, 451 "description":"do not challenge login from office", 452 "action":"allow", 453 "priority":null, 454 "filter":{ 455 "id":"b7ff25282d394be7b945e23c7106ce8a", 456 "expression":"ip.src in {127.0.0.0/24}", 457 "paused":false, 458 "description":"Login from office" 459 } 460 }, 461 { 462 "id":"cbf4b7a5a2a24e59a03044d6d44ceb09", 463 "paused":false, 464 "description":"challenge login", 465 "action":"challenge", 466 "priority":null, 467 "filter":{ 468 "id":"c218c536b2bd406f958f278cf0fa8c0f", 469 "expression":"(http.request.uri.path ~ \"^.*/wp-login.php$\")", 470 "paused":false, 471 "description":"Login" 472 } 473 } 474 ], 475 "success":true, 476 "errors":null, 477 "messages":null 478 } 479 `) 480 } 481 482 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules", handler) 483 want := []FirewallRule{ 484 { 485 ID: "f2d427378e7542acb295380d352e2ebd", 486 Paused: false, 487 Description: "do not challenge login from office", 488 Action: "allow", 489 Priority: nil, 490 Filter: Filter{ 491 ID: "b7ff25282d394be7b945e23c7106ce8a", 492 Expression: "ip.src in {127.0.0.0/24}", 493 Paused: false, 494 Description: "Login from office", 495 }, 496 }, 497 { 498 ID: "cbf4b7a5a2a24e59a03044d6d44ceb09", 499 Paused: false, 500 Description: "challenge login", 501 Action: "challenge", 502 Priority: nil, 503 Filter: Filter{ 504 ID: "c218c536b2bd406f958f278cf0fa8c0f", 505 Expression: "(http.request.uri.path ~ \"^.*/wp-login.php$\")", 506 Paused: false, 507 Description: "Login", 508 }, 509 }, 510 } 511 512 actual, err := client.UpdateFirewallRules(context.Background(), "d56084adb405e0b7e32c52321bf07be6", want) 513 514 if assert.NoError(t, err) { 515 assert.Equal(t, want, actual) 516 } 517} 518 519func TestDeleteSingleFirewallRule(t *testing.T) { 520 setup() 521 defer teardown() 522 523 handler := func(w http.ResponseWriter, r *http.Request) { 524 assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) 525 w.Header().Set("content-type", "application/json") 526 fmt.Fprintf(w, `{ 527 "result": [], 528 "success": true, 529 "errors": null, 530 "messages": null 531 } 532 `) 533 } 534 535 mux.HandleFunc("/zones/d56084adb405e0b7e32c52321bf07be6/firewall/rules/f2d427378e7542acb295380d352e2ebd", handler) 536 537 err := client.DeleteFirewallRule(context.Background(), "d56084adb405e0b7e32c52321bf07be6", "f2d427378e7542acb295380d352e2ebd") 538 assert.NoError(t, err) 539} 540 541func TestDeleteFirewallRuleWithMissingID(t *testing.T) { 542 setup() 543 defer teardown() 544 545 err := client.DeleteFirewallRule(context.Background(), "d56084adb405e0b7e32c52321bf07be6", "") 546 assert.EqualError(t, err, "firewall rule ID cannot be empty") 547} 548