from __future__ import annotations
from typing import Any
from oslo_config import cfg
from oslo_context import context
from oslo_log import log
from oslo_utils import timeutils
CONF = cfg.CONF
LOG = log.getLogger(__name__)
class RequestContext(context.RequestContext):
def __init__(
self,
user_id=None,
project_id=None,
is_admin=False,
project_name=None,
timestamp=None,
user_domain_id=None,
project_domain_id=None,
**kwargs,
):
kwargs.setdefault("user_id", user_id)
kwargs.setdefault("project_id", project_id)
kwargs.setdefault("user_domain_id", user_domain_id)
kwargs.setdefault("project_domain_id", project_domain_id)
super(RequestContext, self).__init__(**kwargs)
self.project_name = project_name
if not timestamp:
timestamp = timeutils.utcnow()
elif isinstance(timestamp, str):
timestamp = timeutils.parse_isotime(timestamp)
self.timestamp = timestamp
self.is_admin = is_admin
if not self.is_admin and "admin" in self.roles:
self.is_admin = True
elif self.is_admin and "admin" not in self.roles:
self.roles.append("admin")
def to_dict(self):
result = super(RequestContext, self).to_dict()
result["user_id"] = self.user_id
result["project_id"] = self.project_id
result["project_name"] = self.project_name
result["domain_id"] = self.domain_id
result["timestamp"] = self.timestamp.isoformat()
result["request_id"] = self.request_id
return result
@classmethod
def from_dict(cls, values: dict[str, Any], **kwargs: Any) -> RequestContext:
return cls(
user_id=values.get("user_id"),
project_id=values.get("project_id"),
project_name=values.get("project_name"),
domain_id=values.get("domain_id"),
read_deleted=values.get("read_deleted"),
remote_address=values.get("remote_address"),
timestamp=values.get("timestamp"),
quota_class=values.get("quota_class"),
service_catalog=values.get("service_catalog"),
request_id=values.get("request_id"),
global_request_id=values.get("global_request_id"),
is_admin=values.get("is_admin"),
roles=values.get("roles"),
auth_token=values.get("auth_token"),
user_domain_id=values.get("user_domain"),
project_domain_id=values.get("project_domain"),
user_domain=values.get("user_domain"),
project_domain=values.get("project_domain"),
**kwargs,
)
def authorize(self, action, target=None, target_obj=None, fatal=True):
"""Verify that the given action is valid on the target in this context.
:param action: string representing the action to be checked.
:param target: dictionary representing the object of the action
for object creation this should be a dictionary representing the
location of the object e.g. ``{'project_id': context.project_id}``.
If None, then this default target will be considered:
{'project_id': self.project_id, 'user_id': self.user_id}
:param target_obj: dictionary representing the object which will be
used to update target.
:param fatal: if False, will return False when an
exception.PolicyNotAuthorized occurs.
:raises skyline_apiserver.exception.NotAuthorized: if verification fails and fatal
is True.
:return: returns a non-False value (not necessarily "True") if
authorized and False if not authorized and fatal is False.
"""
if target is None:
target = {"project_id": self.project_id, "user_id": self.user_id}
target.update(target_obj or {})
if self.is_admin:
return True
if target.get("project_id") == self.project_id:
return True
if fatal:
from fastapi import HTTPException, status
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Action {action} not authorized",
)
return False
def to_policy_values(self):
policy = super(RequestContext, self).to_policy_values()
policy["is_admin"] = self.is_admin
return policy
@property
def is_system_reader(self):
return "reader" in self.roles
@property
def is_system_reader_with_no_admin(self):
if not self.is_admin:
return self.is_system_reader
return False