re-uploading work
This commit is contained in:
524
tests/e2e/tier3/test_t3_10_rbac.py
Normal file
524
tests/e2e/tier3/test_t3_10_rbac.py
Normal file
@@ -0,0 +1,524 @@
|
||||
"""
|
||||
T3.10: RBAC Permission Checks Test
|
||||
|
||||
Tests that role-based access control (RBAC) is enforced across all API endpoints.
|
||||
Users with different roles should have different levels of access.
|
||||
|
||||
Priority: MEDIUM
|
||||
Duration: ~20 seconds
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from helpers.client import AttuneClient
|
||||
from helpers.fixtures import unique_ref
|
||||
|
||||
|
||||
@pytest.mark.tier3
|
||||
@pytest.mark.security
|
||||
@pytest.mark.rbac
|
||||
def test_viewer_role_permissions(client: AttuneClient):
|
||||
"""
|
||||
Test that viewer role can only read resources, not create/update/delete.
|
||||
|
||||
Note: This test assumes RBAC is implemented. If not yet implemented,
|
||||
this test will document the expected behavior.
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("T3.10a: Viewer Role Permission Test")
|
||||
print("=" * 80)
|
||||
|
||||
# Step 1: Create a viewer user
|
||||
print("\n[STEP 1] Creating viewer user...")
|
||||
viewer_username = f"viewer_{unique_ref()}"
|
||||
viewer_email = f"{viewer_username}@example.com"
|
||||
viewer_password = "viewer_password_123"
|
||||
|
||||
# Register viewer (using admin client)
|
||||
try:
|
||||
viewer_reg = client.register(
|
||||
username=viewer_username,
|
||||
email=viewer_email,
|
||||
password=viewer_password,
|
||||
role="viewer", # Request viewer role
|
||||
)
|
||||
print(f"✓ Viewer user created: {viewer_username}")
|
||||
except Exception as e:
|
||||
print(f"⚠ Viewer registration failed: {e}")
|
||||
print(" Note: RBAC may not be fully implemented yet")
|
||||
pytest.skip("RBAC registration not available")
|
||||
|
||||
# Login as viewer
|
||||
viewer_client = AttuneClient(base_url=client.base_url)
|
||||
try:
|
||||
viewer_client.login(username=viewer_username, password=viewer_password)
|
||||
print(f"✓ Viewer logged in")
|
||||
except Exception as e:
|
||||
print(f"⚠ Viewer login failed: {e}")
|
||||
pytest.skip("Could not login as viewer")
|
||||
|
||||
# Step 2: Test READ operations (should succeed)
|
||||
print("\n[STEP 2] Testing READ operations (should succeed)...")
|
||||
|
||||
read_tests = []
|
||||
|
||||
# Test listing packs
|
||||
try:
|
||||
packs = viewer_client.list_packs()
|
||||
print(f"✓ Viewer can list packs: {len(packs)} packs visible")
|
||||
read_tests.append(("list_packs", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Viewer cannot list packs: {e}")
|
||||
read_tests.append(("list_packs", False))
|
||||
|
||||
# Test listing actions
|
||||
try:
|
||||
actions = viewer_client.list_actions()
|
||||
print(f"✓ Viewer can list actions: {len(actions)} actions visible")
|
||||
read_tests.append(("list_actions", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Viewer cannot list actions: {e}")
|
||||
read_tests.append(("list_actions", False))
|
||||
|
||||
# Test listing rules
|
||||
try:
|
||||
rules = viewer_client.list_rules()
|
||||
print(f"✓ Viewer can list rules: {len(rules)} rules visible")
|
||||
read_tests.append(("list_rules", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Viewer cannot list rules: {e}")
|
||||
read_tests.append(("list_rules", False))
|
||||
|
||||
# Step 3: Test CREATE operations (should fail)
|
||||
print("\n[STEP 3] Testing CREATE operations (should fail with 403)...")
|
||||
|
||||
create_tests = []
|
||||
|
||||
# Test creating pack
|
||||
try:
|
||||
pack_data = {
|
||||
"ref": f"test_pack_{unique_ref()}",
|
||||
"name": "Test Pack",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
pack_response = viewer_client.create_pack(pack_data)
|
||||
print(f"✗ SECURITY VIOLATION: Viewer created pack: {pack_response.get('ref')}")
|
||||
create_tests.append(("create_pack", False)) # Should have failed
|
||||
except Exception as e:
|
||||
if (
|
||||
"403" in str(e)
|
||||
or "forbidden" in str(e).lower()
|
||||
or "permission" in str(e).lower()
|
||||
):
|
||||
print(f"✓ Viewer blocked from creating pack (403 Forbidden)")
|
||||
create_tests.append(("create_pack", True))
|
||||
else:
|
||||
print(f"⚠ Viewer create pack failed with unexpected error: {e}")
|
||||
create_tests.append(("create_pack", False))
|
||||
|
||||
# Test creating action
|
||||
try:
|
||||
action_data = {
|
||||
"ref": f"test_action_{unique_ref()}",
|
||||
"name": "Test Action",
|
||||
"runner_type": "python",
|
||||
"entry_point": "main.py",
|
||||
"pack": "core",
|
||||
}
|
||||
action_response = viewer_client.create_action(action_data)
|
||||
print(
|
||||
f"✗ SECURITY VIOLATION: Viewer created action: {action_response.get('ref')}"
|
||||
)
|
||||
create_tests.append(("create_action", False))
|
||||
except Exception as e:
|
||||
if (
|
||||
"403" in str(e)
|
||||
or "forbidden" in str(e).lower()
|
||||
or "permission" in str(e).lower()
|
||||
):
|
||||
print(f"✓ Viewer blocked from creating action (403 Forbidden)")
|
||||
create_tests.append(("create_action", True))
|
||||
else:
|
||||
print(f"⚠ Viewer create action failed: {e}")
|
||||
create_tests.append(("create_action", False))
|
||||
|
||||
# Test creating rule
|
||||
try:
|
||||
rule_data = {
|
||||
"name": f"Test Rule {unique_ref()}",
|
||||
"trigger": "core.timer.interval",
|
||||
"action": "core.echo",
|
||||
"enabled": True,
|
||||
}
|
||||
rule_response = viewer_client.create_rule(rule_data)
|
||||
print(f"✗ SECURITY VIOLATION: Viewer created rule: {rule_response.get('id')}")
|
||||
create_tests.append(("create_rule", False))
|
||||
except Exception as e:
|
||||
if (
|
||||
"403" in str(e)
|
||||
or "forbidden" in str(e).lower()
|
||||
or "permission" in str(e).lower()
|
||||
):
|
||||
print(f"✓ Viewer blocked from creating rule (403 Forbidden)")
|
||||
create_tests.append(("create_rule", True))
|
||||
else:
|
||||
print(f"⚠ Viewer create rule failed: {e}")
|
||||
create_tests.append(("create_rule", False))
|
||||
|
||||
# Step 4: Test EXECUTE operations (should fail)
|
||||
print("\n[STEP 4] Testing EXECUTE operations (should fail with 403)...")
|
||||
|
||||
execute_tests = []
|
||||
|
||||
# Test executing action
|
||||
try:
|
||||
exec_data = {"action": "core.echo", "parameters": {"message": "test"}}
|
||||
exec_response = viewer_client.execute_action(exec_data)
|
||||
print(
|
||||
f"✗ SECURITY VIOLATION: Viewer executed action: {exec_response.get('id')}"
|
||||
)
|
||||
execute_tests.append(("execute_action", False))
|
||||
except Exception as e:
|
||||
if (
|
||||
"403" in str(e)
|
||||
or "forbidden" in str(e).lower()
|
||||
or "permission" in str(e).lower()
|
||||
):
|
||||
print(f"✓ Viewer blocked from executing action (403 Forbidden)")
|
||||
execute_tests.append(("execute_action", True))
|
||||
else:
|
||||
print(f"⚠ Viewer execute failed: {e}")
|
||||
execute_tests.append(("execute_action", False))
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 80)
|
||||
print("VIEWER ROLE TEST SUMMARY")
|
||||
print("=" * 80)
|
||||
print(f"User: {viewer_username} (role: viewer)")
|
||||
print("\nREAD Permissions (should succeed):")
|
||||
for operation, passed in read_tests:
|
||||
status = "✓" if passed else "✗"
|
||||
print(f" {status} {operation}: {'PASS' if passed else 'FAIL'}")
|
||||
|
||||
print("\nCREATE Permissions (should fail):")
|
||||
for operation, blocked in create_tests:
|
||||
status = "✓" if blocked else "✗"
|
||||
print(
|
||||
f" {status} {operation}: {'BLOCKED' if blocked else 'ALLOWED (VIOLATION)'}"
|
||||
)
|
||||
|
||||
print("\nEXECUTE Permissions (should fail):")
|
||||
for operation, blocked in execute_tests:
|
||||
status = "✓" if blocked else "✗"
|
||||
print(
|
||||
f" {status} {operation}: {'BLOCKED' if blocked else 'ALLOWED (VIOLATION)'}"
|
||||
)
|
||||
|
||||
# Check results
|
||||
all_read_passed = all(passed for _, passed in read_tests)
|
||||
all_create_blocked = all(blocked for _, blocked in create_tests)
|
||||
all_execute_blocked = all(blocked for _, blocked in execute_tests)
|
||||
|
||||
if all_read_passed and all_create_blocked and all_execute_blocked:
|
||||
print("\n✅ VIEWER ROLE PERMISSIONS CORRECT!")
|
||||
else:
|
||||
print("\n⚠️ RBAC ISSUES DETECTED:")
|
||||
if not all_read_passed:
|
||||
print(" - Viewer cannot read some resources")
|
||||
if not all_create_blocked:
|
||||
print(" - Viewer can create resources (SECURITY ISSUE)")
|
||||
if not all_execute_blocked:
|
||||
print(" - Viewer can execute actions (SECURITY ISSUE)")
|
||||
|
||||
print("=" * 80)
|
||||
|
||||
# Note: We may skip assertions if RBAC not fully implemented
|
||||
if not create_tests and not execute_tests:
|
||||
pytest.skip("RBAC not fully implemented yet")
|
||||
|
||||
|
||||
@pytest.mark.tier3
|
||||
@pytest.mark.security
|
||||
@pytest.mark.rbac
|
||||
def test_admin_role_permissions(client: AttuneClient):
|
||||
"""
|
||||
Test that admin role has full access to all resources.
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("T3.10b: Admin Role Permission Test")
|
||||
print("=" * 80)
|
||||
|
||||
# The default client is typically admin
|
||||
print("\n[STEP 1] Testing admin permissions (using default client)...")
|
||||
|
||||
operations = []
|
||||
|
||||
# Test create pack
|
||||
try:
|
||||
pack_data = {
|
||||
"ref": f"admin_test_pack_{unique_ref()}",
|
||||
"name": "Admin Test Pack",
|
||||
"version": "1.0.0",
|
||||
"description": "Testing admin permissions",
|
||||
}
|
||||
pack_response = client.create_pack(pack_data)
|
||||
print(f"✓ Admin can create pack: {pack_response['ref']}")
|
||||
operations.append(("create_pack", True))
|
||||
|
||||
# Clean up
|
||||
client.delete_pack(pack_response["ref"])
|
||||
print(f"✓ Admin can delete pack")
|
||||
operations.append(("delete_pack", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Admin cannot create/delete pack: {e}")
|
||||
operations.append(("create_pack", False))
|
||||
operations.append(("delete_pack", False))
|
||||
|
||||
# Test create action
|
||||
try:
|
||||
action_data = {
|
||||
"ref": f"admin_test_action_{unique_ref()}",
|
||||
"name": "Admin Test Action",
|
||||
"runner_type": "python",
|
||||
"entry_point": "main.py",
|
||||
"pack": "core",
|
||||
"enabled": True,
|
||||
}
|
||||
action_response = client.create_action(action_data)
|
||||
print(f"✓ Admin can create action: {action_response['ref']}")
|
||||
operations.append(("create_action", True))
|
||||
|
||||
# Clean up
|
||||
client.delete_action(action_response["ref"])
|
||||
print(f"✓ Admin can delete action")
|
||||
operations.append(("delete_action", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Admin cannot create/delete action: {e}")
|
||||
operations.append(("create_action", False))
|
||||
|
||||
# Test execute action
|
||||
try:
|
||||
exec_data = {"action": "core.echo", "parameters": {"message": "admin test"}}
|
||||
exec_response = client.execute_action(exec_data)
|
||||
print(f"✓ Admin can execute action: execution {exec_response['id']}")
|
||||
operations.append(("execute_action", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Admin cannot execute action: {e}")
|
||||
operations.append(("execute_action", False))
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 80)
|
||||
print("ADMIN ROLE TEST SUMMARY")
|
||||
print("=" * 80)
|
||||
print("Admin Operations:")
|
||||
for operation, passed in operations:
|
||||
status = "✓" if passed else "✗"
|
||||
print(f" {status} {operation}: {'PASS' if passed else 'FAIL'}")
|
||||
|
||||
all_passed = all(passed for _, passed in operations)
|
||||
if all_passed:
|
||||
print("\n✅ ADMIN HAS FULL ACCESS!")
|
||||
else:
|
||||
print("\n⚠️ ADMIN MISSING SOME PERMISSIONS")
|
||||
|
||||
print("=" * 80)
|
||||
|
||||
assert all_passed, "Admin should have full permissions"
|
||||
|
||||
|
||||
@pytest.mark.tier3
|
||||
@pytest.mark.security
|
||||
@pytest.mark.rbac
|
||||
def test_executor_role_permissions(client: AttuneClient):
|
||||
"""
|
||||
Test that executor role can execute actions but not create resources.
|
||||
|
||||
Executor role is for service accounts or CI/CD systems that only need
|
||||
to trigger executions, not manage infrastructure.
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("T3.10c: Executor Role Permission Test")
|
||||
print("=" * 80)
|
||||
|
||||
# Step 1: Create executor user
|
||||
print("\n[STEP 1] Creating executor user...")
|
||||
executor_username = f"executor_{unique_ref()}"
|
||||
executor_email = f"{executor_username}@example.com"
|
||||
executor_password = "executor_password_123"
|
||||
|
||||
try:
|
||||
executor_reg = client.register(
|
||||
username=executor_username,
|
||||
email=executor_email,
|
||||
password=executor_password,
|
||||
role="executor",
|
||||
)
|
||||
print(f"✓ Executor user created: {executor_username}")
|
||||
except Exception as e:
|
||||
print(f"⚠ Executor registration not available: {e}")
|
||||
pytest.skip("Executor role not implemented yet")
|
||||
|
||||
# Login as executor
|
||||
executor_client = AttuneClient(base_url=client.base_url)
|
||||
try:
|
||||
executor_client.login(username=executor_username, password=executor_password)
|
||||
print(f"✓ Executor logged in")
|
||||
except Exception as e:
|
||||
print(f"⚠ Executor login failed: {e}")
|
||||
pytest.skip("Could not login as executor")
|
||||
|
||||
# Step 2: Test EXECUTE permissions (should succeed)
|
||||
print("\n[STEP 2] Testing EXECUTE permissions (should succeed)...")
|
||||
|
||||
execute_tests = []
|
||||
|
||||
try:
|
||||
exec_data = {"action": "core.echo", "parameters": {"message": "executor test"}}
|
||||
exec_response = executor_client.execute_action(exec_data)
|
||||
print(f"✓ Executor can execute action: execution {exec_response['id']}")
|
||||
execute_tests.append(("execute_action", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Executor cannot execute action: {e}")
|
||||
execute_tests.append(("execute_action", False))
|
||||
|
||||
# Step 3: Test CREATE permissions (should fail)
|
||||
print("\n[STEP 3] Testing CREATE permissions (should fail)...")
|
||||
|
||||
create_tests = []
|
||||
|
||||
# Try to create pack (should fail)
|
||||
try:
|
||||
pack_data = {
|
||||
"ref": f"exec_test_pack_{unique_ref()}",
|
||||
"name": "Executor Test Pack",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
pack_response = executor_client.create_pack(pack_data)
|
||||
print(f"✗ VIOLATION: Executor created pack: {pack_response['ref']}")
|
||||
create_tests.append(("create_pack", False))
|
||||
except Exception as e:
|
||||
if "403" in str(e) or "forbidden" in str(e).lower():
|
||||
print(f"✓ Executor blocked from creating pack")
|
||||
create_tests.append(("create_pack", True))
|
||||
else:
|
||||
print(f"⚠ Unexpected error: {e}")
|
||||
create_tests.append(("create_pack", False))
|
||||
|
||||
# Step 4: Test READ permissions (should succeed)
|
||||
print("\n[STEP 4] Testing READ permissions (should succeed)...")
|
||||
|
||||
read_tests = []
|
||||
|
||||
try:
|
||||
actions = executor_client.list_actions()
|
||||
print(f"✓ Executor can list actions: {len(actions)} visible")
|
||||
read_tests.append(("list_actions", True))
|
||||
except Exception as e:
|
||||
print(f"✗ Executor cannot list actions: {e}")
|
||||
read_tests.append(("list_actions", False))
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 80)
|
||||
print("EXECUTOR ROLE TEST SUMMARY")
|
||||
print("=" * 80)
|
||||
print(f"User: {executor_username} (role: executor)")
|
||||
print("\nEXECUTE Permissions (should succeed):")
|
||||
for operation, passed in execute_tests:
|
||||
status = "✓" if passed else "✗"
|
||||
print(f" {status} {operation}: {'PASS' if passed else 'FAIL'}")
|
||||
|
||||
print("\nCREATE Permissions (should fail):")
|
||||
for operation, blocked in create_tests:
|
||||
status = "✓" if blocked else "✗"
|
||||
print(
|
||||
f" {status} {operation}: {'BLOCKED' if blocked else 'ALLOWED (VIOLATION)'}"
|
||||
)
|
||||
|
||||
print("\nREAD Permissions (should succeed):")
|
||||
for operation, passed in read_tests:
|
||||
status = "✓" if passed else "✗"
|
||||
print(f" {status} {operation}: {'PASS' if passed else 'FAIL'}")
|
||||
|
||||
all_execute_ok = all(passed for _, passed in execute_tests)
|
||||
all_create_blocked = all(blocked for _, blocked in create_tests)
|
||||
all_read_ok = all(passed for _, passed in read_tests)
|
||||
|
||||
if all_execute_ok and all_create_blocked and all_read_ok:
|
||||
print("\n✅ EXECUTOR ROLE PERMISSIONS CORRECT!")
|
||||
else:
|
||||
print("\n⚠️ EXECUTOR ROLE ISSUES DETECTED")
|
||||
|
||||
print("=" * 80)
|
||||
|
||||
|
||||
@pytest.mark.tier3
|
||||
@pytest.mark.security
|
||||
@pytest.mark.rbac
|
||||
def test_role_permissions_summary():
|
||||
"""
|
||||
Summary test documenting the expected RBAC permission matrix.
|
||||
|
||||
This is a documentation test that doesn't execute API calls,
|
||||
but serves as a reference for the expected permission model.
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("T3.10d: RBAC Permission Matrix Reference")
|
||||
print("=" * 80)
|
||||
|
||||
permission_matrix = {
|
||||
"admin": {
|
||||
"packs": ["create", "read", "update", "delete"],
|
||||
"actions": ["create", "read", "update", "delete", "execute"],
|
||||
"rules": ["create", "read", "update", "delete"],
|
||||
"triggers": ["create", "read", "update", "delete"],
|
||||
"executions": ["read", "cancel"],
|
||||
"datastore": ["read", "write", "delete"],
|
||||
"secrets": ["create", "read", "update", "delete"],
|
||||
"users": ["create", "read", "update", "delete"],
|
||||
},
|
||||
"editor": {
|
||||
"packs": ["create", "read", "update"],
|
||||
"actions": ["create", "read", "update", "execute"],
|
||||
"rules": ["create", "read", "update"],
|
||||
"triggers": ["create", "read", "update"],
|
||||
"executions": ["read", "execute", "cancel"],
|
||||
"datastore": ["read", "write"],
|
||||
"secrets": ["read", "update"],
|
||||
"users": ["read"],
|
||||
},
|
||||
"executor": {
|
||||
"packs": ["read"],
|
||||
"actions": ["read", "execute"],
|
||||
"rules": ["read"],
|
||||
"triggers": ["read"],
|
||||
"executions": ["read", "execute"],
|
||||
"datastore": ["read"],
|
||||
"secrets": ["read"],
|
||||
"users": [],
|
||||
},
|
||||
"viewer": {
|
||||
"packs": ["read"],
|
||||
"actions": ["read"],
|
||||
"rules": ["read"],
|
||||
"triggers": ["read"],
|
||||
"executions": ["read"],
|
||||
"datastore": ["read"],
|
||||
"secrets": [],
|
||||
"users": [],
|
||||
},
|
||||
}
|
||||
|
||||
print("\nExpected Permission Matrix:\n")
|
||||
|
||||
for role, permissions in permission_matrix.items():
|
||||
print(f"{role.upper()} Role:")
|
||||
for resource, ops in permissions.items():
|
||||
ops_str = ", ".join(ops) if ops else "none"
|
||||
print(f" - {resource}: {ops_str}")
|
||||
print()
|
||||
|
||||
print("=" * 80)
|
||||
print("📋 This matrix defines the expected RBAC behavior")
|
||||
print("=" * 80)
|
||||
|
||||
# This test always passes - it's documentation
|
||||
assert True
|
||||
Reference in New Issue
Block a user