mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-12-03 01:48:05 +00:00
feat: Add debug logging for RBAC access control decisions
Refactor is_action_allowed() to track decision outcome, matched rule index, and reason. Add structured debug log output for troubleshooting access control. Signed-off-by: Derek Higgins <derekh@redhat.com>
This commit is contained in:
parent
e243892ef0
commit
61022245f0
1 changed files with 56 additions and 25 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from llama_stack.core.datatypes import User
|
from llama_stack.core.datatypes import User
|
||||||
|
from llama_stack.log import get_logger
|
||||||
|
|
||||||
from .conditions import (
|
from .conditions import (
|
||||||
Condition,
|
Condition,
|
||||||
|
|
@ -19,6 +20,8 @@ from .datatypes import (
|
||||||
Scope,
|
Scope,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger = get_logger(name=__name__, category="core::auth")
|
||||||
|
|
||||||
|
|
||||||
def matches_resource(resource_scope: str, actual_resource: str) -> bool:
|
def matches_resource(resource_scope: str, actual_resource: str) -> bool:
|
||||||
if resource_scope == actual_resource:
|
if resource_scope == actual_resource:
|
||||||
|
|
@ -74,35 +77,63 @@ def is_action_allowed(
|
||||||
resource: ProtectedResource,
|
resource: ProtectedResource,
|
||||||
user: User | None,
|
user: User | None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
qualified_resource_id = f"{resource.type}::{resource.identifier}"
|
||||||
|
decision = False
|
||||||
|
reason = ""
|
||||||
|
index = -1
|
||||||
|
|
||||||
# If user is not set, assume authentication is not enabled
|
# If user is not set, assume authentication is not enabled
|
||||||
if not user:
|
if not user:
|
||||||
return True
|
decision = True
|
||||||
|
reason = "no auth"
|
||||||
|
else:
|
||||||
|
if not len(policy):
|
||||||
|
policy = default_policy()
|
||||||
|
|
||||||
if not len(policy):
|
for index, rule in enumerate(policy): # noqa: B007
|
||||||
policy = default_policy()
|
if rule.forbid and matches_scope(rule.forbid, action, qualified_resource_id, user.principal):
|
||||||
|
if rule.when:
|
||||||
|
if matches_conditions(parse_conditions(as_list(rule.when)), resource, user):
|
||||||
|
decision = False
|
||||||
|
reason = rule.description or ""
|
||||||
|
break
|
||||||
|
elif rule.unless:
|
||||||
|
if not matches_conditions(parse_conditions(as_list(rule.unless)), resource, user):
|
||||||
|
decision = False
|
||||||
|
reason = rule.description or ""
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
decision = False
|
||||||
|
reason = rule.description or ""
|
||||||
|
break
|
||||||
|
elif rule.permit and matches_scope(rule.permit, action, qualified_resource_id, user.principal):
|
||||||
|
if rule.when:
|
||||||
|
if matches_conditions(parse_conditions(as_list(rule.when)), resource, user):
|
||||||
|
decision = True
|
||||||
|
reason = rule.description or ""
|
||||||
|
break
|
||||||
|
elif rule.unless:
|
||||||
|
if not matches_conditions(parse_conditions(as_list(rule.unless)), resource, user):
|
||||||
|
decision = True
|
||||||
|
reason = rule.description or ""
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
decision = True
|
||||||
|
reason = rule.description or ""
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
reason = "no matching rule"
|
||||||
|
index = -1
|
||||||
|
|
||||||
qualified_resource_id = f"{resource.type}::{resource.identifier}"
|
# print apprived or denied
|
||||||
for rule in policy:
|
decision_str = "APPROVED" if decision else "DENIED"
|
||||||
if rule.forbid and matches_scope(rule.forbid, action, qualified_resource_id, user.principal):
|
user_str = user.principal if user else "none"
|
||||||
if rule.when:
|
logger.debug(
|
||||||
if matches_conditions(parse_conditions(as_list(rule.when)), resource, user):
|
f"AUTHZ,decision={decision_str},user={user_str},"
|
||||||
return False
|
f"resource_id={qualified_resource_id},action={action},"
|
||||||
elif rule.unless:
|
f"rule_index={index},reason={reason!r}"
|
||||||
if not matches_conditions(parse_conditions(as_list(rule.unless)), resource, user):
|
)
|
||||||
return False
|
return decision
|
||||||
else:
|
|
||||||
return False
|
|
||||||
elif rule.permit and matches_scope(rule.permit, action, qualified_resource_id, user.principal):
|
|
||||||
if rule.when:
|
|
||||||
if matches_conditions(parse_conditions(as_list(rule.when)), resource, user):
|
|
||||||
return True
|
|
||||||
elif rule.unless:
|
|
||||||
if not matches_conditions(parse_conditions(as_list(rule.unless)), resource, user):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
# assume access is denied unless we find a rule that permits access
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class AccessDeniedError(RuntimeError):
|
class AccessDeniedError(RuntimeError):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue