diff --git a/apps/authz/src/opa/rego/__test__/criteria/spendingLimit_test.rego b/apps/authz/src/opa/rego/__test__/criteria/spendingLimit_test.rego index adb3f4b68..e7c9ec665 100644 --- a/apps/authz/src/opa/rego/__test__/criteria/spendingLimit_test.rego +++ b/apps/authz/src/opa/rego/__test__/criteria/spendingLimit_test.rego @@ -38,6 +38,7 @@ test_checkRollingTimeWindow { test_checkSpendingLimitByAmount { conditions = { "limit": "1000000000000000000", + "operator": operators.greaterThan, "timeWindow": { "type": "rolling", "value": (12 * 60) * 60, @@ -57,6 +58,7 @@ test_checkSpendingLimitByAmount { test_checkSpendingLimitByValue { conditions = { "limit": "900000000000000000", + "operator": operators.greaterThan, "currency": "fiat:usd", "timeWindow": { "type": "rolling", diff --git a/apps/authz/src/opa/rego/lib/criteria/spendingLimit.rego b/apps/authz/src/opa/rego/lib/criteria/spendingLimit.rego index a5e624a0f..88973d3fe 100644 --- a/apps/authz/src/opa/rego/lib/criteria/spendingLimit.rego +++ b/apps/authz/src/opa/rego/lib/criteria/spendingLimit.rego @@ -87,6 +87,28 @@ checkSpendingTimeWindow(timestamp, timeWindow) { timestampNs >= time.now_ns() - secondsToNanoSeconds(timeWindow.value) } +# Check By Operator + +checkSpendingOperator(spendings, operator, limit) { + operator == operators.lessThan + spendings < limit +} + +checkSpendingOperator(spendings, operator, limit) { + operator == operators.lessThanOrEqual + spendings <= limit +} + +checkSpendingOperator(spendings, operator, limit) { + operator == operators.greaterThan + spendings > limit +} + +checkSpendingOperator(spendings, operator, limit) { + operator == operators.greaterThanOrEqual + spendings >= limit +} + # Calculate Spending calculateSpending(transfer, currency) = result { @@ -105,6 +127,8 @@ checkSpendingLimit(params) { conditions = object.union( { "currency": wildcard, + "limit": wildcard, + "operator": wildcard, "timeWindow": { "type": wildcard, "value": wildcard, # in seconds @@ -123,10 +147,12 @@ checkSpendingLimit(params) { params, ) - limit = conditions.limit - currency = conditions.currency + limit = to_number(conditions.limit) + + operator = conditions.operator + timeWindow = conditions.timeWindow filters = conditions.filters @@ -166,5 +192,5 @@ checkSpendingLimit(params) { spending = calculateSpending(transfer, currency) ]) - spendings + amount > to_number(limit) + checkSpendingOperator(spendings + amount, operator, limit) } diff --git a/apps/authz/src/opa/rego/policies/e2e.rego b/apps/authz/src/opa/rego/policies/e2e.rego index 7e3716aec..2b7f59b9e 100644 --- a/apps/authz/src/opa/rego/policies/e2e.rego +++ b/apps/authz/src/opa/rego/policies/e2e.rego @@ -21,6 +21,6 @@ forbid[{"policyId": "exampleForbidPolicy"}] = reason { checkWalletId({"eip155:eoa:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b"}) checkIntentType({"transferNative"}) checkIntentToken({"eip155:137/slip44:966"}) - checkSpendingLimit({"limit": "1000000000000000000", "timeWindow": {"type": "rolling", "value": 43200}, "filters": {"tokens": ["eip155:137/slip44:966"], "users": ["matt@narval.xyz"]}}) + checkSpendingLimit({"limit": "1000000000000000000", "operator": "gt", "timeWindow": {"type": "rolling", "value": 43200}, "filters": {"tokens": ["eip155:137/slip44:966"], "users": ["matt@narval.xyz"]}}) reason = {"type": "forbid", "policyId": "exampleForbidPolicy", "approvalsSatisfied": [], "approvalsMissing": []} } diff --git a/apps/authz/src/opa/rego/policies/spendings.rego b/apps/authz/src/opa/rego/policies/spendings.rego index 0d47c261f..806eb4665 100644 --- a/apps/authz/src/opa/rego/policies/spendings.rego +++ b/apps/authz/src/opa/rego/policies/spendings.rego @@ -20,6 +20,7 @@ forbid[{"policyId": "spendingLimitByRole"}] = reason { checkIntentToken(tokens) checkSpendingLimit({ "limit": limit, + "operator": operators.greaterThan, "currency": currency, "timeWindow": { "type": "rolling", @@ -57,6 +58,7 @@ forbid[{"policyId": "spendingLimitByUser"}] = reason { checkIntentToken(tokens) checkSpendingLimit({ "limit": limit, + "operator": operators.greaterThan, "currency": currency, "timeWindow": { "type": "rolling", @@ -89,6 +91,7 @@ forbid[{"policyId": "spendingLimitByWalletResource"}] = reason { checkWalletId(resources) checkSpendingLimit({ "limit": limit, + "operator": operators.greaterThan, "currency": currency, "timeWindow": { "type": "rolling", @@ -120,6 +123,7 @@ forbid[{"policyId": "spendingLimitByUserGroup"}] = reason { checkIntentType(transferTypes) checkSpendingLimit({ "limit": limit, + "operator": operators.greaterThan, "currency": currency, "timeWindow": { "type": "rolling", @@ -151,6 +155,7 @@ forbid[{"policyId": "spendingLimitByWalletGroup"}] = reason { checkIntentType(transferTypes) checkSpendingLimit({ "limit": limit, + "operator": operators.greaterThan, "currency": currency, "timeWindow": { "type": "rolling", @@ -191,6 +196,7 @@ permit[{"policyId": "spendingLimitWithApprovals"}] = reason { checkIntentToken(tokens) checkSpendingLimit({ "limit": limit, + "operator": operators.greaterThan, "currency": currency, "timeWindow": { "type": "rolling", diff --git a/apps/authz/src/opa/template/mockData.ts b/apps/authz/src/opa/template/mockData.ts index 9d172c6d7..3fd1367ab 100644 --- a/apps/authz/src/opa/template/mockData.ts +++ b/apps/authz/src/opa/template/mockData.ts @@ -98,6 +98,7 @@ export const exampleForbidPolicy: Policy = { criterion: Criterion.CHECK_SPENDING_LIMIT, args: { limit: '1000000000000000000', + operator: ValueOperators.GREATER_THAN, timeWindow: { type: 'rolling', value: 12 * 60 * 60 diff --git a/apps/authz/src/shared/types/policy.type.ts b/apps/authz/src/shared/types/policy.type.ts index 2ea520005..26365580c 100644 --- a/apps/authz/src/shared/types/policy.type.ts +++ b/apps/authz/src/shared/types/policy.type.ts @@ -250,6 +250,10 @@ export class SpendingLimitCondition { @ApiProperty() limit: string + @IsEnum(ValueOperators) + @ApiProperty({ enum: ValueOperators }) + operator: ValueOperators + @IsOptional() @IsEnum(FiatCurrency) @ApiPropertyOptional({ enum: FiatCurrency })