diff --git a/apps/authz/src/opa/rego/__test__/policies/spendings_test.rego b/apps/authz/src/opa/rego/__test__/policies/spendings_test.rego index c385e3ce1..b6160327d 100644 --- a/apps/authz/src/opa/rego/__test__/policies/spendings_test.rego +++ b/apps/authz/src/opa/rego/__test__/policies/spendings_test.rego @@ -163,3 +163,43 @@ test_spendingLimitByWalletGroup { "approvalsMissing": [], } } + +test_permitRuleSpendingLimit { + res = permit[{"policyId": "spendingLimitWithApprovals"}] with input as spendingLimitReq with data.entities as entities + + res == { + "approvalsMissing": [{ + "approvalCount": 2, + "approvalEntityType": "Narval::User", + "countPrincipal": false, + "entityIds": ["test-bob-uid", "test-bar-uid"], + }], + "approvalsSatisfied": [], + "policyId": "spendingLimitWithApprovals", + "type": "permit", + } +} + +test_permitRuleSpendingLimit { + spendingLimitWithApprovalsReq = object.union(request, { + "principal": {"userId": "test-alice-uid"}, + "resource": {"uid": "eip155:eoa:0xddcf208f219a6e6af072f2cfdc615b2c1805f98e"}, "approvals": [ + {"userId": "test-bob-uid"}, + {"userId": "test-bar-uid"}, + ], + }) + + res = permit[{"policyId": "spendingLimitWithApprovals"}] with input as spendingLimitWithApprovalsReq with data.entities as entities + + res == { + "approvalsMissing": [], + "approvalsSatisfied": [{ + "approvalCount": 2, + "approvalEntityType": "Narval::User", + "countPrincipal": false, + "entityIds": ["test-bob-uid", "test-bar-uid"], + }], + "policyId": "spendingLimitWithApprovals", + "type": "permit", + } +} diff --git a/apps/authz/src/opa/rego/policies/spendings.rego b/apps/authz/src/opa/rego/policies/spendings.rego index 078833ca1..0d47c261f 100644 --- a/apps/authz/src/opa/rego/policies/spendings.rego +++ b/apps/authz/src/opa/rego/policies/spendings.rego @@ -166,3 +166,45 @@ forbid[{"policyId": "spendingLimitByWalletGroup"}] = reason { "approvalsMissing": [], } } + +# If Alice transfers >$5k usd value of USDC in a 12 hour rolling window, then require approvals + +permit[{"policyId": "spendingLimitWithApprovals"}] = reason { + transferTypes = {"transferERC20"} + users = {"test-alice-uid"} + tokens = {"eip155:137/erc20:0x2791bca1f2de4661ed88a30c99a7a9449aa84174", "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"} + currency = "fiat:usd" + limit = "5000000000" + approvalsRequired = [{ + "approvalCount": 2, + "countPrincipal": false, + "approvalEntityType": "Narval::User", + "entityIds": ["test-bob-uid", "test-bar-uid"], + }] + + checkResourceIntegrity + checkPrincipal + checkNonceExists + checkAction({"signTransaction"}) + checkPrincipalId(users) + checkIntentType(transferTypes) + checkIntentToken(tokens) + checkSpendingLimit({ + "limit": limit, + "currency": currency, + "timeWindow": { + "type": "rolling", + "value": (12 * 60) * 60, + }, + "filters": {"tokens": tokens}, + }) + + approvals = checkApprovals(approvalsRequired) + + reason = { + "type": "permit", + "policyId": "spendingLimitWithApprovals", + "approvalsSatisfied": approvals.approvalsSatisfied, + "approvalsMissing": approvals.approvalsMissing, + } +}