"""
PDP policy evaluation context
"""
import logging
from typing import List, Any
from .provider.base import AttributeProvider
from .provider.request import RequestAttributeProvider
from .request import AccessRequest
LOG = logging.getLogger(__name__)
[docs]class EvaluationContext(object):
"""
Evaluation context class
"""
def __init__(self, request: AccessRequest, providers: List[AttributeProvider] = None):
"""
Initialize evaluation context object
:param request: request object
"""
self._subject_id = request.subject_id
self._resource_id = request.resource_id
self._action_id = request.action_id
self._request_provider = RequestAttributeProvider(request)
self._other_providers = providers or []
# Access control element being evaluated
self._ace = None
# Path of attribute being evaluated
self._attribute_path = None
# Call stack of attribute providers as called by context. The stack
# is used to prevent infinite recursive loops.
self._provider_call_stack = [None]
@property
def subject_id(self) -> str:
"""
Subject identifier being evaluated
"""
return self._subject_id
@property
def resource_id(self) -> str:
"""
Resource identifier being evaluated
"""
return self._resource_id
@property
def action_id(self):
"""
Action identifier being evaluated
"""
return self._action_id
@property
def ace(self) -> str:
"""
Access control element being evaluated
"""
return self._ace
@ace.setter
def ace(self, value: str):
"""
Set access control element to evaluate
"""
self._ace = value
@property
def attribute_path(self) -> str:
"""
Attribute path being evaluated in ObjectPath notation
"""
return self._attribute_path
@attribute_path.setter
def attribute_path(self, path: str):
"""
Set attribute path to evaluate
"""
self._attribute_path = path
@property
def attribute_value(self) -> Any:
"""
Attribute value to evaluate
"""
return self.get_attribute_value(self.ace, self.attribute_path)
[docs] def get_attribute_value(self, ace: str, attribute_path: str):
"""
Get attribute value for given access control element and attribute path
:param ace: access control element
:param attribute_path: attribute path in ObjectPath format
:return: attribute value
"""
rvalue = self._request_provider.get_attribute_value(ace, attribute_path, self)
# If attribute value not found then check other attribute providers
if rvalue is None:
# Providers are checked in order
for provider in self._other_providers:
# To prevent infinite recursion skip provider if already in call stack.
if provider not in self._provider_call_stack:
# Append provider to call-stack
self._provider_call_stack.append(provider)
# Call attribute provider
rvalue = provider.get_attribute_value(ace, attribute_path, self)
# Pop provider from call-stack
self._provider_call_stack.pop()
if rvalue is not None:
# Return attribute value for the very first provider which has the value.
# Other providers are not checked.
return rvalue
return rvalue