diff --git a/.github/actions/rego-lint/action.yml b/.github/actions/rego-lint/action.yml index 020e18c..d9252be 100644 --- a/.github/actions/rego-lint/action.yml +++ b/.github/actions/rego-lint/action.yml @@ -29,12 +29,20 @@ runs: if [ -z "$FILES" ]; then echo "has_files=false" >> "$GITHUB_OUTPUT" else - DIRS=$(printf '%s\n' "$FILES" | while IFS= read -r f; do dirname "$f"; done | sort -u | tr '\n' ' ') + DIRS=$(printf '%s\n' "$FILES" | while IFS= read -r f; do dirname "$f"; done | sort -u) echo "has_files=true" >> "$GITHUB_OUTPUT" - echo "dirs=$DIRS" >> "$GITHUB_OUTPUT" + { + echo "dirs<> "$GITHUB_OUTPUT" fi - name: Lint changed Rego files if: steps.changed.outputs.has_files == 'true' shell: bash - run: regal lint "${{ steps.changed.outputs.dirs }}" + env: + CHANGED_DIRS: ${{ steps.changed.outputs.dirs }} + run: | + mapfile -t dirs <<< "$CHANGED_DIRS" + regal lint "${dirs[@]}" diff --git a/.regal/config.yaml b/.regal/config.yaml new file mode 100644 index 0000000..98ecaff --- /dev/null +++ b/.regal/config.yaml @@ -0,0 +1,18 @@ +project: + roots: + - dev + - prod + - stage + +rules: + idiomatic: + directory-package-mismatch: + level: ignore + no-defined-entrypoint: + level: ignore + imports: + unresolved-reference: + level: ignore + performance: + defer-assignment: + level: ignore diff --git a/dev/shared/authentication.rego b/dev/shared/authentication.rego index 659ff9b..a98b37d 100644 --- a/dev/shared/authentication.rego +++ b/dev/shared/authentication.rego @@ -39,8 +39,7 @@ user_active(user_claims) if { # Helper to get authenticated user claims authenticated_claims := user_claims if { - token := extract_token - valid_token(token) - user_claims := claims(token) + valid_token(extract_token) + user_claims := claims(extract_token) user_active(user_claims) } diff --git a/dev/store-ops/pos/authorization.rego b/dev/store-ops/pos/authorization.rego index 6b1daf4..bb6b08d 100644 --- a/dev/store-ops/pos/authorization.rego +++ b/dev/store-ops/pos/authorization.rego @@ -1,5 +1,5 @@ # METADATA -# title: POS Transaction Authorization +# title: POS Authorization # description: Controls who can perform point-of-sale operations including sales, returns, voids, and manager overrides # related_resources: # - ref: https://wiki.acmecorp.internal/retail-ops/pos-security @@ -102,6 +102,8 @@ _at_assigned_store(user_claims) if { _return_limit_for_role := {"cashier": 50, "shift_lead": 200, "store_manager": 999999, "district_manager": 999999} _within_return_limit(user_claims) if { + is_number(input.request.body.amount) + input.request.body.amount >= 0 limit := _return_limit_for_role[user_claims.role] input.request.body.amount <= limit } @@ -112,6 +114,8 @@ _within_return_limit(user_claims) if { _discount_limit_for_role := {"shift_lead": 15, "store_manager": 25, "district_manager": 50} _within_discount_limit(user_claims) if { + is_number(input.request.body.discount_percent) + input.request.body.discount_percent >= 0 limit := _discount_limit_for_role[user_claims.role] input.request.body.discount_percent <= limit } diff --git a/dev/store-ops/pos/authorization_test.rego b/dev/store-ops/pos/authorization_test.rego index 648d0a6..5fbc726 100644 --- a/dev/store-ops/pos/authorization_test.rego +++ b/dev/store-ops/pos/authorization_test.rego @@ -57,55 +57,51 @@ mock_users := { # ============================================================================= test_cashier_can_sell if { - authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_inactive_cashier_denied if { - not authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-inactive"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-inactive"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_wrong_store_denied if { - not authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + not authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } test_district_mgr_any_store if { - authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-districtmgr"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-districtmgr"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } # ============================================================================= @@ -113,73 +109,111 @@ test_district_mgr_any_store if { # ============================================================================= test_cashier_return_under_limit if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 49.99}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 49.99}, + }} + with data.users as mock_users } test_cashier_return_over_limit_denied if { - not authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 75.00}, - } - } with data.users as mock_users + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 75.00}, + }} + with data.users as mock_users } test_shift_lead_return_higher_limit if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 150.00}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 150.00}, + }} + with data.users as mock_users } test_shift_lead_return_over_limit_denied if { - not authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 250.00}, - } - } with data.users as mock_users + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 250.00}, + }} + with data.users as mock_users } test_store_mgr_return_unlimited if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 5000.00}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 5000.00}, + }} + with data.users as mock_users +} + +# ============================================================================= +# RETURN INPUT VALIDATION TESTS +# ============================================================================= + +test_return_string_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": "49.99"}, + }} + with data.users as mock_users +} + +test_return_null_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": null}, + }} + with data.users as mock_users +} + +test_return_negative_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": -10.00}, + }} + with data.users as mock_users } # ============================================================================= @@ -187,29 +221,27 @@ test_store_mgr_return_unlimited if { # ============================================================================= test_cashier_void_denied if { - not authorization.allow_void with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/void", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_void with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/void", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_void_allowed if { - authorization.allow_void with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/void", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_void with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/void", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -217,45 +249,85 @@ test_shift_lead_void_allowed if { # ============================================================================= test_shift_lead_discount_within_limit if { - authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 10}, - } - } with data.users as mock_users + authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 10}, + }} + with data.users as mock_users } test_shift_lead_discount_over_limit_denied if { - not authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 20}, - } - } with data.users as mock_users + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 20}, + }} + with data.users as mock_users } test_store_mgr_higher_discount if { - authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 25}, - } - } with data.users as mock_users + authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 25}, + }} + with data.users as mock_users +} + +# ============================================================================= +# PRICE OVERRIDE INPUT VALIDATION TESTS +# ============================================================================= + +test_discount_string_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": "10"}, + }} + with data.users as mock_users +} + +test_discount_null_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": null}, + }} + with data.users as mock_users +} + +test_discount_negative_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": -5}, + }} + with data.users as mock_users } # ============================================================================= @@ -263,27 +335,25 @@ test_store_mgr_higher_discount if { # ============================================================================= test_cashier_close_register_denied if { - not authorization.allow_close_register with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/close-register", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_close_register with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/close-register", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_close_register if { - authorization.allow_close_register with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/close-register", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_close_register with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/close-register", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } diff --git a/dev/store-ops/timeclock/authorization_test.rego b/dev/store-ops/timeclock/authorization_test.rego index f015052..89ec270 100644 --- a/dev/store-ops/timeclock/authorization_test.rego +++ b/dev/store-ops/timeclock/authorization_test.rego @@ -35,7 +35,11 @@ mock_users := { "district_mgr": { "token": "token-districtmgr", "role": "district_manager", - "permissions": ["timeclock:punch", "timeclock:view_team", "timeclock:approve", "timeclock:edit", "timeclock:payroll_export"], + "permissions": [ + "timeclock:punch", "timeclock:view_team", + "timeclock:approve", "timeclock:edit", + "timeclock:payroll_export", + ], "department": "store_ops", "active": true, "exp": 9999999999, @@ -48,42 +52,39 @@ mock_users := { # ============================================================================= test_cashier_clock_in if { - authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-in", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-in", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_clock_out if { - authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-out", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-out", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_wrong_store_denied if { - not authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-in", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + not authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-in", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } # ============================================================================= @@ -91,41 +92,36 @@ test_cashier_wrong_store_denied if { # ============================================================================= test_cashier_view_own_timecard if { - authorization.allow_view_own_timecard with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/my-timecard", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - } - } with data.users as mock_users + authorization.allow_view_own_timecard with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/my-timecard", + "headers": {"authorization": "Bearer token-cashier1"}, + }}} + with data.users as mock_users } test_cashier_view_team_denied if { - not authorization.allow_view_team_timecards with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/team", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_view_team_timecards with input as {"request": { + "http": { + "method": "GET", + "path": "/timeclock/team", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_view_team if { - authorization.allow_view_team_timecards with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/team", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_view_team_timecards with input as {"request": { + "http": { + "method": "GET", + "path": "/timeclock/team", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -133,29 +129,27 @@ test_shift_lead_view_team if { # ============================================================================= test_shift_lead_approve_denied if { - not authorization.allow_approve_timecard with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/approve/TC-001", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_approve_timecard with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/approve/TC-001", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_store_mgr_approve if { - authorization.allow_approve_timecard with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/approve/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_approve_timecard with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/approve/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -163,31 +157,29 @@ test_store_mgr_approve if { # ============================================================================= test_store_mgr_edit_with_reason if { - authorization.allow_edit_timecard with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/timeclock/timecard/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"reason": "Employee forgot to clock out - verified by shift lead"}, - } - } with data.users as mock_users + authorization.allow_edit_timecard with input as {"request": { + "http": { + "method": "PATCH", + "path": "/timeclock/timecard/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"reason": "Employee forgot to clock out - verified by shift lead"}, + }} + with data.users as mock_users } test_store_mgr_edit_no_reason_denied if { - not authorization.allow_edit_timecard with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/timeclock/timecard/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"reason": ""}, - } - } with data.users as mock_users + not authorization.allow_edit_timecard with input as {"request": { + "http": { + "method": "PATCH", + "path": "/timeclock/timecard/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"reason": ""}, + }} + with data.users as mock_users } # ============================================================================= @@ -195,25 +187,19 @@ test_store_mgr_edit_no_reason_denied if { # ============================================================================= test_store_mgr_payroll_denied if { - not authorization.allow_payroll_export with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/payroll-export", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - } - } with data.users as mock_users + not authorization.allow_payroll_export with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/payroll-export", + "headers": {"authorization": "Bearer token-storemgr"}, + }}} + with data.users as mock_users } test_district_mgr_payroll_export if { - authorization.allow_payroll_export with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/payroll-export", - "headers": {"authorization": "Bearer token-districtmgr"}, - }, - } - } with data.users as mock_users + authorization.allow_payroll_export with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/payroll-export", + "headers": {"authorization": "Bearer token-districtmgr"}, + }}} + with data.users as mock_users } diff --git a/dev/website/api/authorization.rego b/dev/website/api/authorization.rego index 0eaf4d3..394ec45 100644 --- a/dev/website/api/authorization.rego +++ b/dev/website/api/authorization.rego @@ -1,6 +1,8 @@ # METADATA # title: Website API Authorization -# description: Controls access to the e-commerce REST API — product catalog, orders, customer profiles, and admin operations +# description: >- +# Controls access to the e-commerce REST API — product catalog, +# orders, customer profiles, and admin operations # authors: # - name: Website Engineering package website.api.authorization diff --git a/dev/website/api/authorization_test.rego b/dev/website/api/authorization_test.rego index 0391a99..63204ef 100644 --- a/dev/website/api/authorization_test.rego +++ b/dev/website/api/authorization_test.rego @@ -52,21 +52,18 @@ mock_users := { # ============================================================================= test_anyone_browse_products if { - authorization.allow_public with input as { - "request": {"http": {"method": "GET", "path": "/api/products"}}, - } + authorization.allow_public with input as {"request": {"http": {"method": "GET", "path": "/api/products"}}} } test_anyone_view_single_product if { - authorization.allow_public_product with input as { - "request": {"http": {"method": "GET", "path": "/api/products/SKU-12345"}}, - } + authorization.allow_public_product with input as {"request": {"http": { + "method": "GET", + "path": "/api/products/SKU-12345", + }}} } test_anyone_browse_categories if { - authorization.allow_public with input as { - "request": {"http": {"method": "GET", "path": "/api/categories"}}, - } + authorization.allow_public with input as {"request": {"http": {"method": "GET", "path": "/api/categories"}}} } # ============================================================================= @@ -74,39 +71,30 @@ test_anyone_browse_categories if { # ============================================================================= test_customer_view_own_orders if { - authorization.allow_view_own_orders with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/orders/mine", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_view_own_orders with input as {"request": {"http": { + "method": "GET", + "path": "/api/orders/mine", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_place_order if { - authorization.allow_place_order with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/orders", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_place_order with input as {"request": {"http": { + "method": "POST", + "path": "/api/orders", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_cannot_view_all_orders if { - not authorization.allow_admin_view_orders with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/admin/orders", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_view_orders with input as {"request": {"http": { + "method": "GET", + "path": "/api/admin/orders", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -114,27 +102,21 @@ test_customer_cannot_view_all_orders if { # ============================================================================= test_customer_view_profile if { - authorization.allow_view_profile with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/profile", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_view_profile with input as {"request": {"http": { + "method": "GET", + "path": "/api/profile", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_update_profile if { - authorization.allow_update_profile with input as { - "request": { - "http": { - "method": "PUT", - "path": "/api/profile", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_update_profile with input as {"request": {"http": { + "method": "PUT", + "path": "/api/profile", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -142,39 +124,30 @@ test_customer_update_profile if { # ============================================================================= test_merchandiser_create_product if { - authorization.allow_admin_create_product with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/products", - "headers": {"authorization": "Bearer token-merch"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_create_product with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/products", + "headers": {"authorization": "Bearer token-merch"}, + }}} + with data.users as mock_users } test_customer_create_product_denied if { - not authorization.allow_admin_create_product with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/products", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_create_product with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/products", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_merchandiser_update_product if { - authorization.allow_admin_update_product with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/api/admin/products/SKU-12345", - "headers": {"authorization": "Bearer token-merch"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_update_product with input as {"request": {"http": { + "method": "PATCH", + "path": "/api/admin/products/SKU-12345", + "headers": {"authorization": "Bearer token-merch"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -182,37 +155,28 @@ test_merchandiser_update_product if { # ============================================================================= test_cs_agent_refund_denied if { - not authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-csagent"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-csagent"}, + }}} + with data.users as mock_users } test_cs_lead_refund_allowed if { - authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-cslead"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-cslead"}, + }}} + with data.users as mock_users } test_admin_refund_allowed if { - authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-admin"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-admin"}, + }}} + with data.users as mock_users } diff --git a/dev/website/cyber-monday-rate-limiter/authorization_test.rego b/dev/website/cyber-monday-rate-limiter/authorization_test.rego index f969f63..1f4c615 100644 --- a/dev/website/cyber-monday-rate-limiter/authorization_test.rego +++ b/dev/website/cyber-monday-rate-limiter/authorization_test.rego @@ -36,48 +36,36 @@ mock_users := { # ============================================================================= test_allow_request_with_remaining_quota if { - authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "50", - "x-bot-score": "10", - }, - }, - } - } + authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "50", + "x-bot-score": "10", + }, + }}} } test_deny_request_rate_limited if { - not authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "0", - "x-bot-score": "10", - }, - }, - } - } + not authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "0", + "x-bot-score": "10", + }, + }}} } test_deny_bot_traffic if { - not authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "50", - "x-bot-score": "95", - }, - }, - } - } + not authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "50", + "x-bot-score": "95", + }, + }}} } # ============================================================================= @@ -85,48 +73,39 @@ test_deny_bot_traffic if { # ============================================================================= test_gold_member_checkout_at_peak if { - authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-gold", - "x-queue-depth": "15000", - }, - }, - } - } with data.users as mock_users + authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-gold", + "x-queue-depth": "15000", + }, + }}} + with data.users as mock_users } test_standard_customer_denied_at_peak if { - not authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-standard", - "x-queue-depth": "15000", - }, - }, - } - } with data.users as mock_users + not authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-standard", + "x-queue-depth": "15000", + }, + }}} + with data.users as mock_users } test_standard_customer_allowed_off_peak if { - authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-standard", - "x-queue-depth": "500", - }, - }, - } - } with data.users as mock_users + authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-standard", + "x-queue-depth": "500", + }, + }}} + with data.users as mock_users } # ============================================================================= @@ -134,37 +113,28 @@ test_standard_customer_allowed_off_peak if { # ============================================================================= test_admin_toggle_event_mode if { - authorization.allow_toggle_event_mode with input as { - "request": { - "http": { - "method": "POST", - "path": "/admin/event-mode", - "headers": {"authorization": "Bearer token-platadmin"}, - }, - } - } with data.users as mock_users + authorization.allow_toggle_event_mode with input as {"request": {"http": { + "method": "POST", + "path": "/admin/event-mode", + "headers": {"authorization": "Bearer token-platadmin"}, + }}} + with data.users as mock_users } test_non_admin_toggle_denied if { - not authorization.allow_toggle_event_mode with input as { - "request": { - "http": { - "method": "POST", - "path": "/admin/event-mode", - "headers": {"authorization": "Bearer token-gold"}, - }, - } - } with data.users as mock_users + not authorization.allow_toggle_event_mode with input as {"request": {"http": { + "method": "POST", + "path": "/admin/event-mode", + "headers": {"authorization": "Bearer token-gold"}, + }}} + with data.users as mock_users } test_admin_adjust_limits if { - authorization.allow_adjust_limits with input as { - "request": { - "http": { - "method": "PUT", - "path": "/admin/rate-limits", - "headers": {"authorization": "Bearer token-platadmin"}, - }, - } - } with data.users as mock_users + authorization.allow_adjust_limits with input as {"request": {"http": { + "method": "PUT", + "path": "/admin/rate-limits", + "headers": {"authorization": "Bearer token-platadmin"}, + }}} + with data.users as mock_users } diff --git a/prod/shared/authentication.rego b/prod/shared/authentication.rego index 659ff9b..a98b37d 100644 --- a/prod/shared/authentication.rego +++ b/prod/shared/authentication.rego @@ -39,8 +39,7 @@ user_active(user_claims) if { # Helper to get authenticated user claims authenticated_claims := user_claims if { - token := extract_token - valid_token(token) - user_claims := claims(token) + valid_token(extract_token) + user_claims := claims(extract_token) user_active(user_claims) } diff --git a/prod/store-ops/pos/authorization.rego b/prod/store-ops/pos/authorization.rego index 6b1daf4..1c9a869 100644 --- a/prod/store-ops/pos/authorization.rego +++ b/prod/store-ops/pos/authorization.rego @@ -102,6 +102,8 @@ _at_assigned_store(user_claims) if { _return_limit_for_role := {"cashier": 50, "shift_lead": 200, "store_manager": 999999, "district_manager": 999999} _within_return_limit(user_claims) if { + is_number(input.request.body.amount) + input.request.body.amount >= 0 limit := _return_limit_for_role[user_claims.role] input.request.body.amount <= limit } @@ -112,6 +114,8 @@ _within_return_limit(user_claims) if { _discount_limit_for_role := {"shift_lead": 15, "store_manager": 25, "district_manager": 50} _within_discount_limit(user_claims) if { + is_number(input.request.body.discount_percent) + input.request.body.discount_percent >= 0 limit := _discount_limit_for_role[user_claims.role] input.request.body.discount_percent <= limit } diff --git a/prod/store-ops/pos/authorization_test.rego b/prod/store-ops/pos/authorization_test.rego index 648d0a6..5fbc726 100644 --- a/prod/store-ops/pos/authorization_test.rego +++ b/prod/store-ops/pos/authorization_test.rego @@ -57,55 +57,51 @@ mock_users := { # ============================================================================= test_cashier_can_sell if { - authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_inactive_cashier_denied if { - not authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-inactive"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-inactive"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_wrong_store_denied if { - not authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + not authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } test_district_mgr_any_store if { - authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-districtmgr"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-districtmgr"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } # ============================================================================= @@ -113,73 +109,111 @@ test_district_mgr_any_store if { # ============================================================================= test_cashier_return_under_limit if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 49.99}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 49.99}, + }} + with data.users as mock_users } test_cashier_return_over_limit_denied if { - not authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 75.00}, - } - } with data.users as mock_users + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 75.00}, + }} + with data.users as mock_users } test_shift_lead_return_higher_limit if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 150.00}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 150.00}, + }} + with data.users as mock_users } test_shift_lead_return_over_limit_denied if { - not authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 250.00}, - } - } with data.users as mock_users + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 250.00}, + }} + with data.users as mock_users } test_store_mgr_return_unlimited if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 5000.00}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 5000.00}, + }} + with data.users as mock_users +} + +# ============================================================================= +# RETURN INPUT VALIDATION TESTS +# ============================================================================= + +test_return_string_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": "49.99"}, + }} + with data.users as mock_users +} + +test_return_null_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": null}, + }} + with data.users as mock_users +} + +test_return_negative_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": -10.00}, + }} + with data.users as mock_users } # ============================================================================= @@ -187,29 +221,27 @@ test_store_mgr_return_unlimited if { # ============================================================================= test_cashier_void_denied if { - not authorization.allow_void with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/void", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_void with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/void", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_void_allowed if { - authorization.allow_void with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/void", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_void with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/void", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -217,45 +249,85 @@ test_shift_lead_void_allowed if { # ============================================================================= test_shift_lead_discount_within_limit if { - authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 10}, - } - } with data.users as mock_users + authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 10}, + }} + with data.users as mock_users } test_shift_lead_discount_over_limit_denied if { - not authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 20}, - } - } with data.users as mock_users + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 20}, + }} + with data.users as mock_users } test_store_mgr_higher_discount if { - authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 25}, - } - } with data.users as mock_users + authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 25}, + }} + with data.users as mock_users +} + +# ============================================================================= +# PRICE OVERRIDE INPUT VALIDATION TESTS +# ============================================================================= + +test_discount_string_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": "10"}, + }} + with data.users as mock_users +} + +test_discount_null_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": null}, + }} + with data.users as mock_users +} + +test_discount_negative_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": -5}, + }} + with data.users as mock_users } # ============================================================================= @@ -263,27 +335,25 @@ test_store_mgr_higher_discount if { # ============================================================================= test_cashier_close_register_denied if { - not authorization.allow_close_register with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/close-register", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_close_register with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/close-register", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_close_register if { - authorization.allow_close_register with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/close-register", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_close_register with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/close-register", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } diff --git a/prod/store-ops/timeclock/authorization_test.rego b/prod/store-ops/timeclock/authorization_test.rego index f015052..89ec270 100644 --- a/prod/store-ops/timeclock/authorization_test.rego +++ b/prod/store-ops/timeclock/authorization_test.rego @@ -35,7 +35,11 @@ mock_users := { "district_mgr": { "token": "token-districtmgr", "role": "district_manager", - "permissions": ["timeclock:punch", "timeclock:view_team", "timeclock:approve", "timeclock:edit", "timeclock:payroll_export"], + "permissions": [ + "timeclock:punch", "timeclock:view_team", + "timeclock:approve", "timeclock:edit", + "timeclock:payroll_export", + ], "department": "store_ops", "active": true, "exp": 9999999999, @@ -48,42 +52,39 @@ mock_users := { # ============================================================================= test_cashier_clock_in if { - authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-in", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-in", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_clock_out if { - authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-out", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-out", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_wrong_store_denied if { - not authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-in", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + not authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-in", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } # ============================================================================= @@ -91,41 +92,36 @@ test_cashier_wrong_store_denied if { # ============================================================================= test_cashier_view_own_timecard if { - authorization.allow_view_own_timecard with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/my-timecard", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - } - } with data.users as mock_users + authorization.allow_view_own_timecard with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/my-timecard", + "headers": {"authorization": "Bearer token-cashier1"}, + }}} + with data.users as mock_users } test_cashier_view_team_denied if { - not authorization.allow_view_team_timecards with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/team", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_view_team_timecards with input as {"request": { + "http": { + "method": "GET", + "path": "/timeclock/team", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_view_team if { - authorization.allow_view_team_timecards with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/team", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_view_team_timecards with input as {"request": { + "http": { + "method": "GET", + "path": "/timeclock/team", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -133,29 +129,27 @@ test_shift_lead_view_team if { # ============================================================================= test_shift_lead_approve_denied if { - not authorization.allow_approve_timecard with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/approve/TC-001", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_approve_timecard with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/approve/TC-001", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_store_mgr_approve if { - authorization.allow_approve_timecard with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/approve/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_approve_timecard with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/approve/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -163,31 +157,29 @@ test_store_mgr_approve if { # ============================================================================= test_store_mgr_edit_with_reason if { - authorization.allow_edit_timecard with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/timeclock/timecard/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"reason": "Employee forgot to clock out - verified by shift lead"}, - } - } with data.users as mock_users + authorization.allow_edit_timecard with input as {"request": { + "http": { + "method": "PATCH", + "path": "/timeclock/timecard/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"reason": "Employee forgot to clock out - verified by shift lead"}, + }} + with data.users as mock_users } test_store_mgr_edit_no_reason_denied if { - not authorization.allow_edit_timecard with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/timeclock/timecard/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"reason": ""}, - } - } with data.users as mock_users + not authorization.allow_edit_timecard with input as {"request": { + "http": { + "method": "PATCH", + "path": "/timeclock/timecard/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"reason": ""}, + }} + with data.users as mock_users } # ============================================================================= @@ -195,25 +187,19 @@ test_store_mgr_edit_no_reason_denied if { # ============================================================================= test_store_mgr_payroll_denied if { - not authorization.allow_payroll_export with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/payroll-export", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - } - } with data.users as mock_users + not authorization.allow_payroll_export with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/payroll-export", + "headers": {"authorization": "Bearer token-storemgr"}, + }}} + with data.users as mock_users } test_district_mgr_payroll_export if { - authorization.allow_payroll_export with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/payroll-export", - "headers": {"authorization": "Bearer token-districtmgr"}, - }, - } - } with data.users as mock_users + authorization.allow_payroll_export with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/payroll-export", + "headers": {"authorization": "Bearer token-districtmgr"}, + }}} + with data.users as mock_users } diff --git a/prod/website/api/authorization.rego b/prod/website/api/authorization.rego index 0eaf4d3..394ec45 100644 --- a/prod/website/api/authorization.rego +++ b/prod/website/api/authorization.rego @@ -1,6 +1,8 @@ # METADATA # title: Website API Authorization -# description: Controls access to the e-commerce REST API — product catalog, orders, customer profiles, and admin operations +# description: >- +# Controls access to the e-commerce REST API — product catalog, +# orders, customer profiles, and admin operations # authors: # - name: Website Engineering package website.api.authorization diff --git a/prod/website/api/authorization_test.rego b/prod/website/api/authorization_test.rego index 0391a99..63204ef 100644 --- a/prod/website/api/authorization_test.rego +++ b/prod/website/api/authorization_test.rego @@ -52,21 +52,18 @@ mock_users := { # ============================================================================= test_anyone_browse_products if { - authorization.allow_public with input as { - "request": {"http": {"method": "GET", "path": "/api/products"}}, - } + authorization.allow_public with input as {"request": {"http": {"method": "GET", "path": "/api/products"}}} } test_anyone_view_single_product if { - authorization.allow_public_product with input as { - "request": {"http": {"method": "GET", "path": "/api/products/SKU-12345"}}, - } + authorization.allow_public_product with input as {"request": {"http": { + "method": "GET", + "path": "/api/products/SKU-12345", + }}} } test_anyone_browse_categories if { - authorization.allow_public with input as { - "request": {"http": {"method": "GET", "path": "/api/categories"}}, - } + authorization.allow_public with input as {"request": {"http": {"method": "GET", "path": "/api/categories"}}} } # ============================================================================= @@ -74,39 +71,30 @@ test_anyone_browse_categories if { # ============================================================================= test_customer_view_own_orders if { - authorization.allow_view_own_orders with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/orders/mine", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_view_own_orders with input as {"request": {"http": { + "method": "GET", + "path": "/api/orders/mine", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_place_order if { - authorization.allow_place_order with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/orders", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_place_order with input as {"request": {"http": { + "method": "POST", + "path": "/api/orders", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_cannot_view_all_orders if { - not authorization.allow_admin_view_orders with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/admin/orders", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_view_orders with input as {"request": {"http": { + "method": "GET", + "path": "/api/admin/orders", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -114,27 +102,21 @@ test_customer_cannot_view_all_orders if { # ============================================================================= test_customer_view_profile if { - authorization.allow_view_profile with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/profile", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_view_profile with input as {"request": {"http": { + "method": "GET", + "path": "/api/profile", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_update_profile if { - authorization.allow_update_profile with input as { - "request": { - "http": { - "method": "PUT", - "path": "/api/profile", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_update_profile with input as {"request": {"http": { + "method": "PUT", + "path": "/api/profile", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -142,39 +124,30 @@ test_customer_update_profile if { # ============================================================================= test_merchandiser_create_product if { - authorization.allow_admin_create_product with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/products", - "headers": {"authorization": "Bearer token-merch"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_create_product with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/products", + "headers": {"authorization": "Bearer token-merch"}, + }}} + with data.users as mock_users } test_customer_create_product_denied if { - not authorization.allow_admin_create_product with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/products", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_create_product with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/products", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_merchandiser_update_product if { - authorization.allow_admin_update_product with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/api/admin/products/SKU-12345", - "headers": {"authorization": "Bearer token-merch"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_update_product with input as {"request": {"http": { + "method": "PATCH", + "path": "/api/admin/products/SKU-12345", + "headers": {"authorization": "Bearer token-merch"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -182,37 +155,28 @@ test_merchandiser_update_product if { # ============================================================================= test_cs_agent_refund_denied if { - not authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-csagent"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-csagent"}, + }}} + with data.users as mock_users } test_cs_lead_refund_allowed if { - authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-cslead"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-cslead"}, + }}} + with data.users as mock_users } test_admin_refund_allowed if { - authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-admin"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-admin"}, + }}} + with data.users as mock_users } diff --git a/prod/website/cyber-monday-rate-limiter/authorization_test.rego b/prod/website/cyber-monday-rate-limiter/authorization_test.rego index f969f63..1f4c615 100644 --- a/prod/website/cyber-monday-rate-limiter/authorization_test.rego +++ b/prod/website/cyber-monday-rate-limiter/authorization_test.rego @@ -36,48 +36,36 @@ mock_users := { # ============================================================================= test_allow_request_with_remaining_quota if { - authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "50", - "x-bot-score": "10", - }, - }, - } - } + authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "50", + "x-bot-score": "10", + }, + }}} } test_deny_request_rate_limited if { - not authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "0", - "x-bot-score": "10", - }, - }, - } - } + not authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "0", + "x-bot-score": "10", + }, + }}} } test_deny_bot_traffic if { - not authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "50", - "x-bot-score": "95", - }, - }, - } - } + not authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "50", + "x-bot-score": "95", + }, + }}} } # ============================================================================= @@ -85,48 +73,39 @@ test_deny_bot_traffic if { # ============================================================================= test_gold_member_checkout_at_peak if { - authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-gold", - "x-queue-depth": "15000", - }, - }, - } - } with data.users as mock_users + authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-gold", + "x-queue-depth": "15000", + }, + }}} + with data.users as mock_users } test_standard_customer_denied_at_peak if { - not authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-standard", - "x-queue-depth": "15000", - }, - }, - } - } with data.users as mock_users + not authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-standard", + "x-queue-depth": "15000", + }, + }}} + with data.users as mock_users } test_standard_customer_allowed_off_peak if { - authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-standard", - "x-queue-depth": "500", - }, - }, - } - } with data.users as mock_users + authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-standard", + "x-queue-depth": "500", + }, + }}} + with data.users as mock_users } # ============================================================================= @@ -134,37 +113,28 @@ test_standard_customer_allowed_off_peak if { # ============================================================================= test_admin_toggle_event_mode if { - authorization.allow_toggle_event_mode with input as { - "request": { - "http": { - "method": "POST", - "path": "/admin/event-mode", - "headers": {"authorization": "Bearer token-platadmin"}, - }, - } - } with data.users as mock_users + authorization.allow_toggle_event_mode with input as {"request": {"http": { + "method": "POST", + "path": "/admin/event-mode", + "headers": {"authorization": "Bearer token-platadmin"}, + }}} + with data.users as mock_users } test_non_admin_toggle_denied if { - not authorization.allow_toggle_event_mode with input as { - "request": { - "http": { - "method": "POST", - "path": "/admin/event-mode", - "headers": {"authorization": "Bearer token-gold"}, - }, - } - } with data.users as mock_users + not authorization.allow_toggle_event_mode with input as {"request": {"http": { + "method": "POST", + "path": "/admin/event-mode", + "headers": {"authorization": "Bearer token-gold"}, + }}} + with data.users as mock_users } test_admin_adjust_limits if { - authorization.allow_adjust_limits with input as { - "request": { - "http": { - "method": "PUT", - "path": "/admin/rate-limits", - "headers": {"authorization": "Bearer token-platadmin"}, - }, - } - } with data.users as mock_users + authorization.allow_adjust_limits with input as {"request": {"http": { + "method": "PUT", + "path": "/admin/rate-limits", + "headers": {"authorization": "Bearer token-platadmin"}, + }}} + with data.users as mock_users } diff --git a/stage/shared/authentication.rego b/stage/shared/authentication.rego index 659ff9b..a98b37d 100644 --- a/stage/shared/authentication.rego +++ b/stage/shared/authentication.rego @@ -39,8 +39,7 @@ user_active(user_claims) if { # Helper to get authenticated user claims authenticated_claims := user_claims if { - token := extract_token - valid_token(token) - user_claims := claims(token) + valid_token(extract_token) + user_claims := claims(extract_token) user_active(user_claims) } diff --git a/stage/store-ops/pos/authorization.rego b/stage/store-ops/pos/authorization.rego index 6b1daf4..1c9a869 100644 --- a/stage/store-ops/pos/authorization.rego +++ b/stage/store-ops/pos/authorization.rego @@ -102,6 +102,8 @@ _at_assigned_store(user_claims) if { _return_limit_for_role := {"cashier": 50, "shift_lead": 200, "store_manager": 999999, "district_manager": 999999} _within_return_limit(user_claims) if { + is_number(input.request.body.amount) + input.request.body.amount >= 0 limit := _return_limit_for_role[user_claims.role] input.request.body.amount <= limit } @@ -112,6 +114,8 @@ _within_return_limit(user_claims) if { _discount_limit_for_role := {"shift_lead": 15, "store_manager": 25, "district_manager": 50} _within_discount_limit(user_claims) if { + is_number(input.request.body.discount_percent) + input.request.body.discount_percent >= 0 limit := _discount_limit_for_role[user_claims.role] input.request.body.discount_percent <= limit } diff --git a/stage/store-ops/pos/authorization_test.rego b/stage/store-ops/pos/authorization_test.rego index 648d0a6..5fbc726 100644 --- a/stage/store-ops/pos/authorization_test.rego +++ b/stage/store-ops/pos/authorization_test.rego @@ -57,55 +57,51 @@ mock_users := { # ============================================================================= test_cashier_can_sell if { - authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_inactive_cashier_denied if { - not authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-inactive"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-inactive"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_wrong_store_denied if { - not authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + not authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } test_district_mgr_any_store if { - authorization.allow_sale with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/sale", - "headers": {"authorization": "Bearer token-districtmgr"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + authorization.allow_sale with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/sale", + "headers": {"authorization": "Bearer token-districtmgr"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } # ============================================================================= @@ -113,73 +109,111 @@ test_district_mgr_any_store if { # ============================================================================= test_cashier_return_under_limit if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 49.99}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 49.99}, + }} + with data.users as mock_users } test_cashier_return_over_limit_denied if { - not authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 75.00}, - } - } with data.users as mock_users + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 75.00}, + }} + with data.users as mock_users } test_shift_lead_return_higher_limit if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 150.00}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 150.00}, + }} + with data.users as mock_users } test_shift_lead_return_over_limit_denied if { - not authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 250.00}, - } - } with data.users as mock_users + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 250.00}, + }} + with data.users as mock_users } test_store_mgr_return_unlimited if { - authorization.allow_return with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/return", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"amount": 5000.00}, - } - } with data.users as mock_users + authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": 5000.00}, + }} + with data.users as mock_users +} + +# ============================================================================= +# RETURN INPUT VALIDATION TESTS +# ============================================================================= + +test_return_string_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": "49.99"}, + }} + with data.users as mock_users +} + +test_return_null_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": null}, + }} + with data.users as mock_users +} + +test_return_negative_amount_denied if { + not authorization.allow_return with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/return", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"amount": -10.00}, + }} + with data.users as mock_users } # ============================================================================= @@ -187,29 +221,27 @@ test_store_mgr_return_unlimited if { # ============================================================================= test_cashier_void_denied if { - not authorization.allow_void with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/void", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_void with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/void", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_void_allowed if { - authorization.allow_void with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/void", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_void with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/void", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -217,45 +249,85 @@ test_shift_lead_void_allowed if { # ============================================================================= test_shift_lead_discount_within_limit if { - authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 10}, - } - } with data.users as mock_users + authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 10}, + }} + with data.users as mock_users } test_shift_lead_discount_over_limit_denied if { - not authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 20}, - } - } with data.users as mock_users + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 20}, + }} + with data.users as mock_users } test_store_mgr_higher_discount if { - authorization.allow_price_override with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/price-override", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"discount_percent": 25}, - } - } with data.users as mock_users + authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": 25}, + }} + with data.users as mock_users +} + +# ============================================================================= +# PRICE OVERRIDE INPUT VALIDATION TESTS +# ============================================================================= + +test_discount_string_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": "10"}, + }} + with data.users as mock_users +} + +test_discount_null_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": null}, + }} + with data.users as mock_users +} + +test_discount_negative_value_denied if { + not authorization.allow_price_override with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/price-override", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"discount_percent": -5}, + }} + with data.users as mock_users } # ============================================================================= @@ -263,27 +335,25 @@ test_store_mgr_higher_discount if { # ============================================================================= test_cashier_close_register_denied if { - not authorization.allow_close_register with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/close-register", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_close_register with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/close-register", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_close_register if { - authorization.allow_close_register with input as { - "request": { - "http": { - "method": "POST", - "path": "/pos/close-register", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_close_register with input as {"request": { + "http": { + "method": "POST", + "path": "/pos/close-register", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } diff --git a/stage/store-ops/timeclock/authorization_test.rego b/stage/store-ops/timeclock/authorization_test.rego index f015052..89ec270 100644 --- a/stage/store-ops/timeclock/authorization_test.rego +++ b/stage/store-ops/timeclock/authorization_test.rego @@ -35,7 +35,11 @@ mock_users := { "district_mgr": { "token": "token-districtmgr", "role": "district_manager", - "permissions": ["timeclock:punch", "timeclock:view_team", "timeclock:approve", "timeclock:edit", "timeclock:payroll_export"], + "permissions": [ + "timeclock:punch", "timeclock:view_team", + "timeclock:approve", "timeclock:edit", + "timeclock:payroll_export", + ], "department": "store_ops", "active": true, "exp": 9999999999, @@ -48,42 +52,39 @@ mock_users := { # ============================================================================= test_cashier_clock_in if { - authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-in", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-in", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_clock_out if { - authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-out", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-out", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_cashier_wrong_store_denied if { - not authorization.allow_punch with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/clock-in", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-099"}, - } - } with data.users as mock_users + not authorization.allow_punch with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/clock-in", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-099"}, + }} + with data.users as mock_users } # ============================================================================= @@ -91,41 +92,36 @@ test_cashier_wrong_store_denied if { # ============================================================================= test_cashier_view_own_timecard if { - authorization.allow_view_own_timecard with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/my-timecard", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - } - } with data.users as mock_users + authorization.allow_view_own_timecard with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/my-timecard", + "headers": {"authorization": "Bearer token-cashier1"}, + }}} + with data.users as mock_users } test_cashier_view_team_denied if { - not authorization.allow_view_team_timecards with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/team", - "headers": {"authorization": "Bearer token-cashier1"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_view_team_timecards with input as {"request": { + "http": { + "method": "GET", + "path": "/timeclock/team", + "headers": {"authorization": "Bearer token-cashier1"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_shift_lead_view_team if { - authorization.allow_view_team_timecards with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/team", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_view_team_timecards with input as {"request": { + "http": { + "method": "GET", + "path": "/timeclock/team", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -133,29 +129,27 @@ test_shift_lead_view_team if { # ============================================================================= test_shift_lead_approve_denied if { - not authorization.allow_approve_timecard with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/approve/TC-001", - "headers": {"authorization": "Bearer token-shiftlead"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + not authorization.allow_approve_timecard with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/approve/TC-001", + "headers": {"authorization": "Bearer token-shiftlead"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } test_store_mgr_approve if { - authorization.allow_approve_timecard with input as { - "request": { - "http": { - "method": "POST", - "path": "/timeclock/approve/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - } - } with data.users as mock_users + authorization.allow_approve_timecard with input as {"request": { + "http": { + "method": "POST", + "path": "/timeclock/approve/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + }} + with data.users as mock_users } # ============================================================================= @@ -163,31 +157,29 @@ test_store_mgr_approve if { # ============================================================================= test_store_mgr_edit_with_reason if { - authorization.allow_edit_timecard with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/timeclock/timecard/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"reason": "Employee forgot to clock out - verified by shift lead"}, - } - } with data.users as mock_users + authorization.allow_edit_timecard with input as {"request": { + "http": { + "method": "PATCH", + "path": "/timeclock/timecard/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"reason": "Employee forgot to clock out - verified by shift lead"}, + }} + with data.users as mock_users } test_store_mgr_edit_no_reason_denied if { - not authorization.allow_edit_timecard with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/timeclock/timecard/TC-001", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - "headers": {"x-store-id": "STORE-042"}, - "body": {"reason": ""}, - } - } with data.users as mock_users + not authorization.allow_edit_timecard with input as {"request": { + "http": { + "method": "PATCH", + "path": "/timeclock/timecard/TC-001", + "headers": {"authorization": "Bearer token-storemgr"}, + }, + "headers": {"x-store-id": "STORE-042"}, + "body": {"reason": ""}, + }} + with data.users as mock_users } # ============================================================================= @@ -195,25 +187,19 @@ test_store_mgr_edit_no_reason_denied if { # ============================================================================= test_store_mgr_payroll_denied if { - not authorization.allow_payroll_export with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/payroll-export", - "headers": {"authorization": "Bearer token-storemgr"}, - }, - } - } with data.users as mock_users + not authorization.allow_payroll_export with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/payroll-export", + "headers": {"authorization": "Bearer token-storemgr"}, + }}} + with data.users as mock_users } test_district_mgr_payroll_export if { - authorization.allow_payroll_export with input as { - "request": { - "http": { - "method": "GET", - "path": "/timeclock/payroll-export", - "headers": {"authorization": "Bearer token-districtmgr"}, - }, - } - } with data.users as mock_users + authorization.allow_payroll_export with input as {"request": {"http": { + "method": "GET", + "path": "/timeclock/payroll-export", + "headers": {"authorization": "Bearer token-districtmgr"}, + }}} + with data.users as mock_users } diff --git a/stage/website/api/authorization.rego b/stage/website/api/authorization.rego index 0eaf4d3..394ec45 100644 --- a/stage/website/api/authorization.rego +++ b/stage/website/api/authorization.rego @@ -1,6 +1,8 @@ # METADATA # title: Website API Authorization -# description: Controls access to the e-commerce REST API — product catalog, orders, customer profiles, and admin operations +# description: >- +# Controls access to the e-commerce REST API — product catalog, +# orders, customer profiles, and admin operations # authors: # - name: Website Engineering package website.api.authorization diff --git a/stage/website/api/authorization_test.rego b/stage/website/api/authorization_test.rego index 0391a99..63204ef 100644 --- a/stage/website/api/authorization_test.rego +++ b/stage/website/api/authorization_test.rego @@ -52,21 +52,18 @@ mock_users := { # ============================================================================= test_anyone_browse_products if { - authorization.allow_public with input as { - "request": {"http": {"method": "GET", "path": "/api/products"}}, - } + authorization.allow_public with input as {"request": {"http": {"method": "GET", "path": "/api/products"}}} } test_anyone_view_single_product if { - authorization.allow_public_product with input as { - "request": {"http": {"method": "GET", "path": "/api/products/SKU-12345"}}, - } + authorization.allow_public_product with input as {"request": {"http": { + "method": "GET", + "path": "/api/products/SKU-12345", + }}} } test_anyone_browse_categories if { - authorization.allow_public with input as { - "request": {"http": {"method": "GET", "path": "/api/categories"}}, - } + authorization.allow_public with input as {"request": {"http": {"method": "GET", "path": "/api/categories"}}} } # ============================================================================= @@ -74,39 +71,30 @@ test_anyone_browse_categories if { # ============================================================================= test_customer_view_own_orders if { - authorization.allow_view_own_orders with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/orders/mine", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_view_own_orders with input as {"request": {"http": { + "method": "GET", + "path": "/api/orders/mine", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_place_order if { - authorization.allow_place_order with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/orders", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_place_order with input as {"request": {"http": { + "method": "POST", + "path": "/api/orders", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_cannot_view_all_orders if { - not authorization.allow_admin_view_orders with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/admin/orders", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_view_orders with input as {"request": {"http": { + "method": "GET", + "path": "/api/admin/orders", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -114,27 +102,21 @@ test_customer_cannot_view_all_orders if { # ============================================================================= test_customer_view_profile if { - authorization.allow_view_profile with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/profile", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_view_profile with input as {"request": {"http": { + "method": "GET", + "path": "/api/profile", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_customer_update_profile if { - authorization.allow_update_profile with input as { - "request": { - "http": { - "method": "PUT", - "path": "/api/profile", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + authorization.allow_update_profile with input as {"request": {"http": { + "method": "PUT", + "path": "/api/profile", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -142,39 +124,30 @@ test_customer_update_profile if { # ============================================================================= test_merchandiser_create_product if { - authorization.allow_admin_create_product with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/products", - "headers": {"authorization": "Bearer token-merch"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_create_product with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/products", + "headers": {"authorization": "Bearer token-merch"}, + }}} + with data.users as mock_users } test_customer_create_product_denied if { - not authorization.allow_admin_create_product with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/products", - "headers": {"authorization": "Bearer token-shopper"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_create_product with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/products", + "headers": {"authorization": "Bearer token-shopper"}, + }}} + with data.users as mock_users } test_merchandiser_update_product if { - authorization.allow_admin_update_product with input as { - "request": { - "http": { - "method": "PATCH", - "path": "/api/admin/products/SKU-12345", - "headers": {"authorization": "Bearer token-merch"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_update_product with input as {"request": {"http": { + "method": "PATCH", + "path": "/api/admin/products/SKU-12345", + "headers": {"authorization": "Bearer token-merch"}, + }}} + with data.users as mock_users } # ============================================================================= @@ -182,37 +155,28 @@ test_merchandiser_update_product if { # ============================================================================= test_cs_agent_refund_denied if { - not authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-csagent"}, - }, - } - } with data.users as mock_users + not authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-csagent"}, + }}} + with data.users as mock_users } test_cs_lead_refund_allowed if { - authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-cslead"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-cslead"}, + }}} + with data.users as mock_users } test_admin_refund_allowed if { - authorization.allow_admin_refund with input as { - "request": { - "http": { - "method": "POST", - "path": "/api/admin/orders/ORD-999/refund", - "headers": {"authorization": "Bearer token-admin"}, - }, - } - } with data.users as mock_users + authorization.allow_admin_refund with input as {"request": {"http": { + "method": "POST", + "path": "/api/admin/orders/ORD-999/refund", + "headers": {"authorization": "Bearer token-admin"}, + }}} + with data.users as mock_users } diff --git a/stage/website/cyber-monday-rate-limiter/authorization_test.rego b/stage/website/cyber-monday-rate-limiter/authorization_test.rego index f969f63..1f4c615 100644 --- a/stage/website/cyber-monday-rate-limiter/authorization_test.rego +++ b/stage/website/cyber-monday-rate-limiter/authorization_test.rego @@ -36,48 +36,36 @@ mock_users := { # ============================================================================= test_allow_request_with_remaining_quota if { - authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "50", - "x-bot-score": "10", - }, - }, - } - } + authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "50", + "x-bot-score": "10", + }, + }}} } test_deny_request_rate_limited if { - not authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "0", - "x-bot-score": "10", - }, - }, - } - } + not authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "0", + "x-bot-score": "10", + }, + }}} } test_deny_bot_traffic if { - not authorization.allow_request with input as { - "request": { - "http": { - "method": "GET", - "path": "/api/products", - "headers": { - "x-ratelimit-remaining": "50", - "x-bot-score": "95", - }, - }, - } - } + not authorization.allow_request with input as {"request": {"http": { + "method": "GET", + "path": "/api/products", + "headers": { + "x-ratelimit-remaining": "50", + "x-bot-score": "95", + }, + }}} } # ============================================================================= @@ -85,48 +73,39 @@ test_deny_bot_traffic if { # ============================================================================= test_gold_member_checkout_at_peak if { - authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-gold", - "x-queue-depth": "15000", - }, - }, - } - } with data.users as mock_users + authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-gold", + "x-queue-depth": "15000", + }, + }}} + with data.users as mock_users } test_standard_customer_denied_at_peak if { - not authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-standard", - "x-queue-depth": "15000", - }, - }, - } - } with data.users as mock_users + not authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-standard", + "x-queue-depth": "15000", + }, + }}} + with data.users as mock_users } test_standard_customer_allowed_off_peak if { - authorization.allow_checkout_during_event with input as { - "request": { - "http": { - "method": "POST", - "path": "/basket/checkout", - "headers": { - "authorization": "Bearer token-standard", - "x-queue-depth": "500", - }, - }, - } - } with data.users as mock_users + authorization.allow_checkout_during_event with input as {"request": {"http": { + "method": "POST", + "path": "/basket/checkout", + "headers": { + "authorization": "Bearer token-standard", + "x-queue-depth": "500", + }, + }}} + with data.users as mock_users } # ============================================================================= @@ -134,37 +113,28 @@ test_standard_customer_allowed_off_peak if { # ============================================================================= test_admin_toggle_event_mode if { - authorization.allow_toggle_event_mode with input as { - "request": { - "http": { - "method": "POST", - "path": "/admin/event-mode", - "headers": {"authorization": "Bearer token-platadmin"}, - }, - } - } with data.users as mock_users + authorization.allow_toggle_event_mode with input as {"request": {"http": { + "method": "POST", + "path": "/admin/event-mode", + "headers": {"authorization": "Bearer token-platadmin"}, + }}} + with data.users as mock_users } test_non_admin_toggle_denied if { - not authorization.allow_toggle_event_mode with input as { - "request": { - "http": { - "method": "POST", - "path": "/admin/event-mode", - "headers": {"authorization": "Bearer token-gold"}, - }, - } - } with data.users as mock_users + not authorization.allow_toggle_event_mode with input as {"request": {"http": { + "method": "POST", + "path": "/admin/event-mode", + "headers": {"authorization": "Bearer token-gold"}, + }}} + with data.users as mock_users } test_admin_adjust_limits if { - authorization.allow_adjust_limits with input as { - "request": { - "http": { - "method": "PUT", - "path": "/admin/rate-limits", - "headers": {"authorization": "Bearer token-platadmin"}, - }, - } - } with data.users as mock_users + authorization.allow_adjust_limits with input as {"request": {"http": { + "method": "PUT", + "path": "/admin/rate-limits", + "headers": {"authorization": "Bearer token-platadmin"}, + }}} + with data.users as mock_users }