Add configurable policy files
Configurable policy files are added for services. Change-Id: I53549fe80798bdd2e6ed3f92f548ed16393a269f
This commit is contained in:
parent
cdfe0330d3
commit
1e4f72ca2b
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Editable policy files are supported. Add policy yaml file name and path options
|
||||
to the configuration file. Users can edit and modify the policy as needed.
|
@ -28,6 +28,7 @@ from skyline_apiserver.client.utils import generate_session, get_access, get_sys
|
||||
from skyline_apiserver.config import CONF
|
||||
from skyline_apiserver.log import LOG
|
||||
from skyline_apiserver.policy import ENFORCER, UserContext
|
||||
from skyline_apiserver.types import constants
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@ -111,11 +112,27 @@ async def list_policies(
|
||||
# user_context as is.
|
||||
LOG.debug("Keystone is not reachable. No privilege to access system scope.")
|
||||
target = _generate_target(profile)
|
||||
|
||||
results = []
|
||||
services = constants.SUPPORTED_SERVICE_EPS.keys()
|
||||
for service in services:
|
||||
try:
|
||||
enforcer = ENFORCER[service]
|
||||
result = [
|
||||
{"rule": rule, "allowed": ENFORCER.authorize(rule, target, user_context)}
|
||||
for rule in ENFORCER.rules
|
||||
{
|
||||
"rule": f"{service}:{rule}",
|
||||
"allowed": enforcer.authorize(rule, target, user_context),
|
||||
}
|
||||
for rule in enforcer.rules
|
||||
]
|
||||
return schemas.Policies(**{"policies": result})
|
||||
results.extend(result)
|
||||
except Exception:
|
||||
msg = "An error occurred when calling %(service)s enforcer." % {
|
||||
"service": str(service)
|
||||
}
|
||||
LOG.warning(msg)
|
||||
|
||||
return schemas.Policies(**{"policies": results})
|
||||
|
||||
|
||||
@router.post(
|
||||
@ -159,10 +176,14 @@ async def check_policies(
|
||||
target = _generate_target(profile)
|
||||
target.update(policy_rules.target if policy_rules.target else {})
|
||||
try:
|
||||
result = [
|
||||
{"rule": rule, "allowed": ENFORCER.authorize(rule, target, user_context)}
|
||||
for rule in policy_rules.rules
|
||||
]
|
||||
result = []
|
||||
for policy_rule in policy_rules.rules:
|
||||
service = policy_rule.split(":", 1)[0]
|
||||
rule = policy_rule.split(":", 1)[1]
|
||||
enforcer = ENFORCER[service]
|
||||
result.append(
|
||||
{"rule": policy_rule, "allowed": enforcer.authorize(rule, target, user_context)}
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
|
@ -125,6 +125,21 @@ cafile = Opt(
|
||||
default="",
|
||||
)
|
||||
|
||||
policy_file_suffix = Opt(
|
||||
name="policy_file_suffix",
|
||||
description="policy file suffix",
|
||||
schema=StrictStr,
|
||||
default="policy.yaml",
|
||||
)
|
||||
|
||||
policy_file_path = Opt(
|
||||
name="policy_file_path",
|
||||
description="A path to policy file",
|
||||
schema=StrictStr,
|
||||
default="/etc/skyline/policy",
|
||||
)
|
||||
|
||||
|
||||
GROUP_NAME = __name__.split(".")[-1]
|
||||
ALL_OPTS = (
|
||||
debug,
|
||||
@ -142,6 +157,8 @@ ALL_OPTS = (
|
||||
prometheus_enable_basic_auth,
|
||||
prometheus_basic_auth_user,
|
||||
prometheus_basic_auth_password,
|
||||
policy_file_suffix,
|
||||
policy_file_path,
|
||||
)
|
||||
|
||||
__all__ = ("GROUP_NAME", "ALL_OPTS")
|
||||
|
@ -19,27 +19,20 @@ from oslo_policy import _parser
|
||||
from .base import Enforcer, UserContext
|
||||
from .manager import get_service_rules
|
||||
|
||||
ENFORCER = Enforcer()
|
||||
ENFORCER = {}
|
||||
|
||||
|
||||
def setup() -> None:
|
||||
service_rules = get_service_rules()
|
||||
all_api_rules = []
|
||||
for service, rules in service_rules.items():
|
||||
api_rules = []
|
||||
for rule in rules:
|
||||
# Update rule name with prefix service.
|
||||
rule.name = f"{service}:{rule.name}"
|
||||
# Update check
|
||||
rule.check_str = rule.check_str.replace("rule:", f"rule:{service}:")
|
||||
rule.check = _parser.parse_rule(rule.check_str)
|
||||
# Update basic check
|
||||
rule.basic_check_str = rule.basic_check_str.replace("rule:", f"rule:{service}:")
|
||||
rule.basic_check = _parser.parse_rule(rule.basic_check_str)
|
||||
api_rules.append(rule)
|
||||
all_api_rules.extend(api_rules)
|
||||
|
||||
ENFORCER.register_rules(all_api_rules)
|
||||
enforcer = Enforcer(service=service)
|
||||
enforcer.register_rules(api_rules)
|
||||
ENFORCER[service] = enforcer
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
@ -15,14 +15,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import MutableMapping
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterator, List, Union
|
||||
|
||||
import attr
|
||||
from immutables import Map
|
||||
from keystoneauth1.access.access import AccessInfoV3
|
||||
from oslo_policy._checks import _check
|
||||
from oslo_policy import _cache_handler, _checks, policy
|
||||
|
||||
from skyline_apiserver.config import CONF
|
||||
from skyline_apiserver.log import LOG
|
||||
|
||||
from .manager.base import APIRule, Rule
|
||||
|
||||
@ -94,7 +96,23 @@ class UserContext(MutableMapping):
|
||||
|
||||
@attr.s(kw_only=True, repr=True, frozen=False, slots=True, auto_attribs=True)
|
||||
class Enforcer:
|
||||
service: str = attr.ib(repr=True, init=True)
|
||||
rules: Map = attr.ib(factory=Map, repr=True, init=False)
|
||||
file_rules: Dict[str, Any] = attr.ib(default={}, repr=True, init=True)
|
||||
_file_cache: Dict[str, Any] = attr.ib(default={}, repr=True, init=True)
|
||||
|
||||
def load_rules(self) -> None:
|
||||
path = Path(CONF.default.policy_file_path).joinpath(
|
||||
str(self.service + "_" + CONF.default.policy_file_suffix)
|
||||
)
|
||||
if path.exists():
|
||||
reloaded, data = _cache_handler.read_cached_file(
|
||||
self._file_cache, path, force_reload=False
|
||||
)
|
||||
if reloaded or not self.file_rules:
|
||||
self.file_rules = policy.Rules.load(data)
|
||||
else:
|
||||
self.file_rules = {}
|
||||
|
||||
def register_rules(self, rules: List[Union[Rule, APIRule]]) -> None:
|
||||
rule_map = {}
|
||||
@ -107,16 +125,25 @@ class Enforcer:
|
||||
self.rules = Map(rule_map)
|
||||
|
||||
def authorize(self, rule: str, target: Dict[str, Any], context: UserContext) -> bool:
|
||||
result = False
|
||||
do_check = self.rules.get(rule)
|
||||
if do_check is None:
|
||||
raise ValueError(f"Policy {rule} not registered.")
|
||||
try:
|
||||
self.load_rules()
|
||||
except Exception:
|
||||
LOG.debug(f"Failed to load {self.service} rules.")
|
||||
|
||||
result = _check(
|
||||
do_check = self.file_rules.get(rule) or self.rules.get(rule)
|
||||
if do_check is None:
|
||||
LOG.debug(f"Policy {rule} not registered.")
|
||||
return False
|
||||
|
||||
try:
|
||||
result = _checks._check(
|
||||
rule=do_check,
|
||||
target=target,
|
||||
creds=context,
|
||||
enforcer=self,
|
||||
current_rule=rule,
|
||||
)
|
||||
except Exception:
|
||||
result = False
|
||||
|
||||
return result
|
||||
|
Loading…
Reference in New Issue
Block a user