generated from coulomb/repo-seed
enterprise/flex-auth integration layer
This commit is contained in:
@@ -94,7 +94,12 @@ def test_builtin_policy_descriptor_exposes_cli_and_adapter_boundary():
|
||||
assert {capability.id for capability in descriptor.capabilities} >= {
|
||||
"policy",
|
||||
"policy_filter",
|
||||
"identity_claims",
|
||||
"resource_manifest",
|
||||
"decision_log",
|
||||
}
|
||||
assert "mkt policy check" in descriptor.cli["commands"]
|
||||
assert "mkt policy subject" in descriptor.cli["commands"]
|
||||
assert "mkt policy resource-manifest" in descriptor.cli["commands"]
|
||||
assert "IdentityClaimsAdapter" in descriptor.metadata["external_adapters"]
|
||||
assert "RelationshipPolicyAdapter" in descriptor.metadata["external_adapters"]
|
||||
|
||||
@@ -7,11 +7,19 @@ from markitect_tool.cli import main
|
||||
from markitect_tool.policy import (
|
||||
DirectoryGroupResolution,
|
||||
DirectoryGroupResolutionRequest,
|
||||
EnterprisePolicyError,
|
||||
EnterprisePolicyMap,
|
||||
EnterpriseIdentity,
|
||||
EnterprisePolicyMapRequest,
|
||||
FlexAuthResourceManifest,
|
||||
LocalDecisionLogStore,
|
||||
LocalEnterprisePolicyMapper,
|
||||
LocalLabelPolicy,
|
||||
LocalLabelPolicyGateway,
|
||||
NetKingdomIdentityClaimsAdapter,
|
||||
StaticDirectoryGroupResolver,
|
||||
)
|
||||
from markitect_tool.policy.models import PolicyDecision
|
||||
|
||||
|
||||
POLICY_TEXT = """id: example-policy
|
||||
@@ -247,6 +255,203 @@ def test_enterprise_policy_adapter_requests_serialize_cleanly():
|
||||
assert map_request.to_dict()["identity"]["canonical_id"].endswith("#user-123")
|
||||
|
||||
|
||||
def test_netkingdom_claims_adapter_validates_required_claims_and_audience():
|
||||
adapter = NetKingdomIdentityClaimsAdapter(
|
||||
issuer="https://sso.example.test/realms/netkingdom",
|
||||
audiences=["markitect-tool"],
|
||||
)
|
||||
|
||||
identity = adapter.verify(_claims())
|
||||
|
||||
assert identity.canonical_id == "oidc:https://sso.example.test/realms/netkingdom#user-123"
|
||||
assert identity.roles == ["viewer"]
|
||||
assert identity.scopes == ["openid", "profile", "markitect:read"]
|
||||
assert identity.groups == ["/markitect/readers"]
|
||||
assert identity.assurance["mfa"] is True
|
||||
|
||||
|
||||
def test_netkingdom_claims_adapter_rejects_local_issuer_in_production():
|
||||
adapter = NetKingdomIdentityClaimsAdapter(audiences=["markitect-tool"])
|
||||
claims = _claims() | {"iss": "http://localhost:8080/realms/dev"}
|
||||
|
||||
try:
|
||||
adapter.verify(claims, context={"environment": "production"})
|
||||
except EnterprisePolicyError as exc:
|
||||
assert "Local development issuer" in str(exc)
|
||||
else:
|
||||
raise AssertionError("expected local production issuer rejection")
|
||||
|
||||
|
||||
def test_static_group_resolver_reports_group_overage_and_freshness():
|
||||
request = DirectoryGroupResolutionRequest(
|
||||
subject_id="oidc:https://sso.example.test/realms/netkingdom#user-123",
|
||||
issuer="https://sso.example.test/realms/netkingdom",
|
||||
claims={"hasgroups": True},
|
||||
)
|
||||
resolver = StaticDirectoryGroupResolver(
|
||||
groups_by_subject={request.subject_id: ["/markitect/readers"]},
|
||||
refreshed_at="2026-05-04T10:00:00Z",
|
||||
)
|
||||
|
||||
result = resolver.resolve(request)
|
||||
|
||||
assert result.groups == ["/markitect/readers"]
|
||||
assert result.overage is True
|
||||
assert result.refreshed_at == "2026-05-04T10:00:00Z"
|
||||
|
||||
|
||||
def test_local_enterprise_policy_mapper_maps_groups_roles_and_scopes():
|
||||
identity = NetKingdomIdentityClaimsAdapter(
|
||||
issuer="https://sso.example.test/realms/netkingdom",
|
||||
audiences=["markitect-tool"],
|
||||
).verify(_claims())
|
||||
policy_map = EnterprisePolicyMap.from_mapping(_enterprise_policy_map())
|
||||
subject = LocalEnterprisePolicyMapper(policy_map).map_subject(
|
||||
EnterprisePolicyMapRequest(identity=identity)
|
||||
)
|
||||
|
||||
assert subject.allowed_labels == ["public", "internal"]
|
||||
assert subject.trust_zones == ["public", "internal"]
|
||||
assert subject.allowed_actions == ["read", "query", "search"]
|
||||
assert subject.attributes["matched_policy_rules"] == [
|
||||
"group:/markitect/readers",
|
||||
"role:viewer",
|
||||
"scope:markitect:read",
|
||||
]
|
||||
|
||||
|
||||
def test_flex_auth_resource_manifest_serializes_resources():
|
||||
manifest = FlexAuthResourceManifest.from_mapping(
|
||||
{
|
||||
"id": "markitect-example",
|
||||
"system": "markitect-tool",
|
||||
"actions": ["read", "query"],
|
||||
"resources": [
|
||||
{
|
||||
"id": "document:public-note",
|
||||
"type": "document",
|
||||
"path": "examples/policy/public-note.md",
|
||||
"labels": ["public"],
|
||||
"trust_zone": "public",
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
data = manifest.to_dict()
|
||||
|
||||
assert data["id"] == "markitect-example"
|
||||
assert data["resources"][0]["id"] == "document:public-note"
|
||||
|
||||
|
||||
def test_local_decision_log_store_redacts_token_context(tmp_path: Path):
|
||||
store = LocalDecisionLogStore(tmp_path / "decisions.jsonl")
|
||||
decision = PolicyDecision(
|
||||
subject="subject-1",
|
||||
action="query",
|
||||
object_id="document:internal",
|
||||
effect="deny",
|
||||
reason="missing label",
|
||||
)
|
||||
|
||||
audit_id = store.record(decision, context={"token": "secret", "policy_version": "v1"})
|
||||
entry = store.get(decision.decision_id)
|
||||
|
||||
assert audit_id.startswith("audit:")
|
||||
assert entry is not None
|
||||
assert entry["context"]["token"] == "<redacted>"
|
||||
assert entry["context"]["policy_version"] == "v1"
|
||||
|
||||
|
||||
def test_mkt_policy_subject_maps_claims_file_to_subject(tmp_path: Path):
|
||||
claims_file = tmp_path / "claims.yaml"
|
||||
map_file = tmp_path / "policy-map.yaml"
|
||||
claims_file.write_text(yaml_dump(_claims()), encoding="utf-8")
|
||||
map_file.write_text(yaml_dump(_enterprise_policy_map()), encoding="utf-8")
|
||||
|
||||
result = CliRunner().invoke(
|
||||
main,
|
||||
[
|
||||
"policy",
|
||||
"subject",
|
||||
str(claims_file),
|
||||
"--policy-map",
|
||||
str(map_file),
|
||||
"--format",
|
||||
"json",
|
||||
],
|
||||
)
|
||||
data = json.loads(result.output)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert data["subject"]["allowed_labels"] == ["public", "internal"]
|
||||
assert data["subject"]["allowed_actions"] == ["read", "query", "search"]
|
||||
|
||||
|
||||
def test_mkt_policy_resource_manifest_inspects_manifest(tmp_path: Path):
|
||||
manifest_file = tmp_path / "resources.yaml"
|
||||
manifest_file.write_text(
|
||||
yaml_dump(
|
||||
{
|
||||
"id": "manifest-1",
|
||||
"system": "markitect-tool",
|
||||
"actions": ["read"],
|
||||
"resources": [{"id": "document:one", "type": "document"}],
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = CliRunner().invoke(
|
||||
main,
|
||||
["policy", "resource-manifest", str(manifest_file), "--format", "json"],
|
||||
)
|
||||
data = json.loads(result.output)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert data["manifest"]["resources"][0]["id"] == "document:one"
|
||||
|
||||
|
||||
def yaml_dump(value: dict) -> str:
|
||||
import yaml
|
||||
|
||||
return yaml.safe_dump(value, sort_keys=False)
|
||||
|
||||
|
||||
def _claims() -> dict:
|
||||
return {
|
||||
"iss": "https://sso.example.test/realms/netkingdom",
|
||||
"sub": "user-123",
|
||||
"aud": ["markitect-tool"],
|
||||
"exp": 4102444800,
|
||||
"iat": 1767225600,
|
||||
"preferred_username": "ada",
|
||||
"scope": "openid profile markitect:read",
|
||||
"realm_access": {"roles": ["viewer"]},
|
||||
"groups": ["/markitect/readers"],
|
||||
"amr": ["pwd", "otp"],
|
||||
}
|
||||
|
||||
|
||||
def _enterprise_policy_map() -> dict:
|
||||
return {
|
||||
"id": "markitect-enterprise-policy-map",
|
||||
"issuer": "https://sso.example.test/realms/netkingdom",
|
||||
"audiences": ["markitect-tool"],
|
||||
"defaults": {"allowed_labels": ["public"], "trust_zones": ["public"]},
|
||||
"groups": {
|
||||
"/markitect/readers": {
|
||||
"allowed_labels": ["internal"],
|
||||
"trust_zones": ["internal"],
|
||||
"actions": ["read", "query", "search"],
|
||||
}
|
||||
},
|
||||
"roles": {"viewer": {"actions": ["read", "query", "search"]}},
|
||||
"scopes": {"markitect:read": {"actions": ["read", "query", "search"]}},
|
||||
"trust_zones": {"internal": {"required_groups": ["/markitect/readers"]}},
|
||||
}
|
||||
|
||||
|
||||
def _policy_mapping() -> dict:
|
||||
return {
|
||||
"id": "example-policy",
|
||||
|
||||
@@ -83,6 +83,39 @@ def test_load_workflow_file_preserves_standard_sections(tmp_path: Path):
|
||||
assert plan.steps[0]["id"] == "render"
|
||||
|
||||
|
||||
def test_load_workflow_file_preserves_policy_identity_permissions(tmp_path: Path):
|
||||
workflow = tmp_path / "policy.workflow.md"
|
||||
workflow.write_text(
|
||||
"""# Policy Workflow
|
||||
|
||||
```yaml workflow
|
||||
metadata:
|
||||
id: policy-aware
|
||||
inputs:
|
||||
static:
|
||||
value: ok
|
||||
permissions:
|
||||
policy:
|
||||
subject_from_token: examples/policy/netkingdom-claims.yaml
|
||||
policy_map: examples/policy/enterprise-policy-map.yaml
|
||||
required_assurance:
|
||||
mfa: true
|
||||
emergency_justification: INC-123
|
||||
decision_log: .markitect/policy-decisions.jsonl
|
||||
flex_auth:
|
||||
resource_manifest: examples/policy/flex-auth-resource-manifest.yaml
|
||||
```
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
plan = load_workflow_file(workflow)
|
||||
|
||||
assert plan.permissions["policy"]["subject_from_token"] == "examples/policy/netkingdom-claims.yaml"
|
||||
assert plan.permissions["policy"]["required_assurance"]["mfa"] is True
|
||||
assert plan.permissions["flex_auth"]["resource_manifest"].endswith("flex-auth-resource-manifest.yaml")
|
||||
|
||||
|
||||
def test_workflow_runner_collects_sources_and_renders_output(tmp_path: Path):
|
||||
workflow = _write_workflow_fixture(tmp_path)
|
||||
plan = load_workflow_file(workflow)
|
||||
|
||||
Reference in New Issue
Block a user