Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/py-sdk-build-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ jobs:

publish_test:
name: Publish to Test PyPI
if: github.ref != 'refs/heads/main'
if: ${{ github.ref_type == 'branch' && github.ref_name != 'main' }}
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
#environment: pypi
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.3.0"
version = "0.4.0"
edition = "2021"
license = "Apache-2.0"
authors = ["Amit Saxena"]
Expand Down
8 changes: 6 additions & 2 deletions examples/python/core-sdk/basic_refund.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
and blocks the call if the policy denies it.
"""

from actra import Actra
from actra import Actra, ActraPolicyError
from actra.runtime import ActraRuntime


Expand Down Expand Up @@ -121,4 +121,8 @@ def refund(amount: int):


print("\n--- Blocked call ---")
refund(amount=1500)
try:
refund(amount=1500)
except ActraPolicyError as e:
print("Refund blocked by policy")
print("Rule:", e.matched_rule )
116 changes: 116 additions & 0 deletions examples/python/core-sdk/build_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
Build Action Example

This example demonstrates how to use `ActraRuntime.build_action`
when evaluating actions outside of decorators

This pattern is useful for integrations where the application
does not call a protected function, such as:

- API frameworks
- message queues
- MCP servers
- background workers
"""

from actra import Actra, ActraRuntime


# ------------------------------------------------------------
# 1. Schema
# ------------------------------------------------------------

schema_yaml = """
version: 1

actions:
refund:
fields:
amount: number

actor:
fields:
role: string

snapshot:
fields:
fraud_flag: boolean
"""


# ------------------------------------------------------------
# 2. Policy
# ------------------------------------------------------------

policy_yaml = """
version: 1

rules:
- id: block_large_refund
scope:
action: refund
when:
subject:
domain: action
field: amount
operator: greater_than
value:
literal: 1000
effect: block
"""


# ------------------------------------------------------------
# 3. Compile policy
# ------------------------------------------------------------

policy = Actra.from_strings(schema_yaml, policy_yaml)
runtime = ActraRuntime(policy)


# ------------------------------------------------------------
# 4. Register resolvers
# ------------------------------------------------------------

runtime.set_actor_resolver(lambda ctx: {"role": "support"})
runtime.set_snapshot_resolver(lambda ctx: {"fraud_flag": False})


# ------------------------------------------------------------
# 5. Example external input
# ------------------------------------------------------------
# Imagine this comes from an API request or message queue

request_data = {
"amount": 200
}


# ------------------------------------------------------------
# 6. Define a handler signature
# ------------------------------------------------------------
# build_action uses the function signature to determine which
# fields are valid action parameters.
#
# The function is NOT executed. It is only used for introspection.

def fake_handler(amount):
pass


action = runtime.build_action(
func=fake_handler,
action_type="refund",
args=(),
kwargs=request_data,
ctx=None
)


# ------------------------------------------------------------
# 7. Evaluate decision
# ------------------------------------------------------------

decision = runtime.evaluate(action)

print("Decision:", decision)
10 changes: 8 additions & 2 deletions examples/python/core-sdk/custom_action_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
map directly to policy fields.
"""

from actra import Actra
from actra import Actra, ActraPolicyError
from actra.runtime import ActraRuntime


Expand Down Expand Up @@ -79,6 +79,7 @@
def build_refund_action(action_type, args, kwargs, ctx):
"""
Convert application inputs into the policy action object.
Only fields defined in the policy schema should be included
"""

return {
Expand All @@ -104,4 +105,9 @@ def refund(amount: int, currency: str):
refund(amount=200, currency="USD")

print("\nBlocked call")
refund(amount=1500, currency="USD")

try:
refund(amount=1500, currency="USD")
except ActraPolicyError as e:
print("Refund blocked by policy")
print("Rule:", e.matched_rule)
46 changes: 39 additions & 7 deletions examples/python/core-sdk/custom_actor_snapshot_resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@
Custom Actor and Snapshot Resolvers Example

Demonstrates how runtime context can be used to dynamically
resolve actor and system state.
resolve actor identity and external system state.

Resolvers allow applications to supply policy inputs
from runtime context such as:

- authenticated user identity
- request metadata
- system state

"""

from actra import Actra, ActraRuntime
from actra import Actra, ActraRuntime, ActraPolicyError

# ------------------------------------------------------------
# 1. Schema
# ------------------------------------------------------------

schema_yaml = """
version: 1
Expand All @@ -25,6 +36,9 @@
fraud_flag: boolean
"""

# ------------------------------------------------------------
# 2. Policy
# ------------------------------------------------------------

policy_yaml = """
version: 1
Expand All @@ -50,31 +64,46 @@
effect: block
"""

# ------------------------------------------------------------
# 3. Compile policy
# ------------------------------------------------------------

policy = Actra.from_strings(schema_yaml, policy_yaml)
runtime = ActraRuntime(policy)


# Example request context
# ------------------------------------------------------------
# 4. Example request context
# ------------------------------------------------------------
class RequestContext:
def __init__(self, role, fraud_flag):
self.role = role
self.fraud_flag = fraud_flag


# Actor resolver
# ------------------------------------------------------------
# 5. Register resolvers
# ------------------------------------------------------------

# Actor resolver extracts identity information

runtime.set_actor_resolver(
lambda ctx: {"role": ctx.role}
)

# Snapshot resolver
# Snapshot resolver extracts system state
runtime.set_snapshot_resolver(
lambda ctx: {"fraud_flag": ctx.fraud_flag}
)

# ------------------------------------------------------------
# 6. Create runtime context
# ------------------------------------------------------------

ctx = RequestContext(role="support", fraud_flag=False)

# ------------------------------------------------------------
# 8. Build action
# ------------------------------------------------------------

action = runtime.build_action(
action_type="refund",
Expand All @@ -83,6 +112,9 @@ def __init__(self, role, fraud_flag):
ctx=ctx
)

decision = runtime.evaluate(action, ctx)
# ------------------------------------------------------------
# 9. Evaluate policy
# ------------------------------------------------------------

decision = runtime.evaluate(action, ctx)
print(decision)
19 changes: 14 additions & 5 deletions examples/python/core-sdk/fields_filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
Field Filtering Example

This example demonstrates how to restrict which parameters
become part of the Actra action object.
become part of the Actra action object

This is useful when functions contain additional arguments
that should not be exposed to the policy engine.
Field filtering is useful when functions contain additional
arguments that should not be exposed to the policy engine

In this example the function accepts both `amount` and
`currency`, but the policy schema only defines the `amount`
field. The `fields` parameter ensures only the allowed fields
are included in the policy action
"""

from actra import Actra
from actra import Actra, ActraPolicyError
from actra.runtime import ActraRuntime


Expand Down Expand Up @@ -85,4 +90,8 @@ def refund(amount: int, currency: str):
refund(amount=200, currency="USD")

print("\nBlocked call")
refund(amount=1500, currency="USD")
try:
refund(amount=1500, currency="USD")
except ActraPolicyError as e:
print("Refund blocked by policy")
print("Rule:", e.matched_rule)
20 changes: 8 additions & 12 deletions examples/python/core-sdk/manual_runtime_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,10 @@
# - message queues
#

action = runtime.build_action(
action_type="refund",
args=(),
kwargs={"amount": 200},
ctx=None
)
action = {
"type": "refund",
"amount": 200
}

# ------------------------------------------------------------
# 7. Evaluate the action
Expand All @@ -107,12 +105,10 @@
# 8. Blocked example
# ------------------------------------------------------------

blocked_action = runtime.build_action(
action_type="refund",
args=(),
kwargs={"amount": 1500},
ctx=None
)
blocked_action = {
"type": "refund",
"amount": 1500
}

blocked_result = runtime.evaluate(blocked_action)

Expand Down
12 changes: 7 additions & 5 deletions examples/python/core-sdk/multiple_runtimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Each runtime can enforce a different policy.
"""

from actra import Actra
from actra import Actra, ActraPolicyError
from actra.runtime import ActraRuntime


Expand Down Expand Up @@ -156,8 +156,9 @@ def admin_refund(amount: int):

try:
support_refund(amount=5000)
except PermissionError as e:
print(e)
except ActraPolicyError as e:
print("Support refund blocked")
print("Rule:",e.matched_rule)


print("\nAdmin runtime")
Expand All @@ -166,5 +167,6 @@ def admin_refund(amount: int):

try:
admin_refund(amount=20000)
except PermissionError as e:
print(e)
except ActraPolicyError as e:
print("Admin refund blocked")
print("Rule:",e.matched_rule)
Loading
Loading