From 08c6265d1ee00aaa248674a519cf7362f2c88528 Mon Sep 17 00:00:00 2001 From: "zhu.boxiang" Date: Fri, 15 Jul 2022 15:21:24 +0800 Subject: [PATCH] feat: Support system scope for policy list and check 1. support system scope for policy list and check 2. add target for check policy api 3. update version of requirements and test-requirements Change-Id: If251d26bdb522b03a8fb94ae0034d8ca44be5b61 --- docs/api/swagger.json | 8 +++++++ requirements.txt | 10 ++++---- skyline_apiserver/api/v1/policy.py | 37 ++++++++++++++++++++++++++--- skyline_apiserver/client/utils.py | 7 ++++++ skyline_apiserver/policy/base.py | 7 +++++- skyline_apiserver/schemas/policy.py | 3 ++- test-requirements.txt | 2 +- 7 files changed, 63 insertions(+), 11 deletions(-) diff --git a/docs/api/swagger.json b/docs/api/swagger.json index 3b4bf90..e5ce3e5 100644 --- a/docs/api/swagger.json +++ b/docs/api/swagger.json @@ -2207,6 +2207,14 @@ "type": "string" }, "description": "Policies rules list" + }, + "target": { + "title": "Target", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Policies targets" } } }, diff --git a/requirements.txt b/requirements.txt index d8e3d51..84c3fce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,10 +23,10 @@ click>=7.1.2,<=8.1.3 # BSD License (3 clause) jinja2>=2.11.3,<=3.1.2 # BSD License (3 clause) h11<0.13,>=0.11 # MIT MarkupSafe>=2.0.1,<=2.1.1 # BSD License (3 clause) -python-keystoneclient>=3.21.0,<=4.5.0 # Apache-2.0 -python-cinderclient>=5.0.2,<=8.3.0 # Apache-2.0 +python-keystoneclient>=3.21.0 # Apache-2.0 +python-cinderclient>=5.0.2 # Apache-2.0 python-glanceclient>=2.17.1 # Apache-2.0 -python-neutronclient>=6.14.1,<=7.8.0 # Apache-2.0 -python-novaclient>=15.1.1,<=18.0.0 # Apache-2.0 -keystoneauth1>=3.17.4,<=4.6.0 # Apache-2.0 +python-neutronclient>=6.14.1 # Apache-2.0 +python-novaclient>=15.1.1 # Apache-2.0 +keystoneauth1>=3.17.4 # Apache-2.0 oslo.policy>=2.3.4 # Apache-2.0 diff --git a/skyline_apiserver/api/v1/policy.py b/skyline_apiserver/api/v1/policy.py index 292ed68..eddec0d 100644 --- a/skyline_apiserver/api/v1/policy.py +++ b/skyline_apiserver/api/v1/policy.py @@ -17,10 +17,12 @@ from __future__ import annotations from typing import Dict from fastapi import APIRouter, Depends, HTTPException, status +from keystoneauth1.exceptions.http import Unauthorized as KeystoneUnauthorized from skyline_apiserver import schemas from skyline_apiserver.api import deps -from skyline_apiserver.client.utils import generate_session, get_access +from skyline_apiserver.client.utils import generate_session, get_access, get_system_scope_access +from skyline_apiserver.log import LOG from skyline_apiserver.policy import ENFORCER, UserContext router = APIRouter() @@ -75,10 +77,24 @@ def _generate_target(profile: schemas.Profile) -> Dict[str, str]: ) async def list_policies( profile: schemas.Profile = Depends(deps.get_profile_update_jwt), -): +) -> schemas.Policies: session = await generate_session(profile) access = await get_access(session) user_context = UserContext(access) + try: + system_scope_access = await get_system_scope_access( + profile.keystone_token, profile.region + ) + user_context["system_scope"] = ( + "all" + if getattr(system_scope_access, "system") + and getattr(system_scope_access, "system", {}).get("all", False) + else user_context["system_scope"] + ) + except KeystoneUnauthorized: + # User is not authorized to access the system scope. So just ignore the + # exception and use the user_context as is. + LOG.debug("Keystone token is invalid. No privilege to access system scope.") target = _generate_target(profile) result = [ {"rule": rule, "allowed": ENFORCER.authorize(rule, target, user_context)} @@ -103,11 +119,26 @@ async def list_policies( async def check_policies( policy_rules: schemas.PoliciesRules, profile: schemas.Profile = Depends(deps.get_profile_update_jwt), -): +) -> schemas.Policies: session = await generate_session(profile) access = await get_access(session) user_context = UserContext(access) + try: + system_scope_access = await get_system_scope_access( + profile.keystone_token, profile.region + ) + user_context["system_scope"] = ( + "all" + if getattr(system_scope_access, "system") + and getattr(system_scope_access, "system", {}).get("all", False) + else user_context["system_scope"] + ) + except KeystoneUnauthorized: + # User is not authorized to access the system scope. So just ignore the + # exception and use the user_context as is. + LOG.debug("Keystone token is invalid. No privilege to access system scope.") 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)} diff --git a/skyline_apiserver/client/utils.py b/skyline_apiserver/client/utils.py index f25a7c9..3a3b74d 100644 --- a/skyline_apiserver/client/utils.py +++ b/skyline_apiserver/client/utils.py @@ -69,6 +69,13 @@ def get_system_session() -> Session: return SESSION +async def get_system_scope_access(keystone_token: str, region: str) -> AccessInfoV3: + auth_url = await get_endpoint(region, "keystone", get_system_session()) + scope_auth = Token(auth_url, keystone_token, system_scope="all") + session = Session(auth=scope_auth, verify=False, timeout=constants.DEFAULT_TIMEOUT) + return await run_in_threadpool(session.auth.get_auth_ref, session) + + async def get_access(session: Session) -> AccessInfoV3: auth = session.auth if auth._needs_reauthenticate(): diff --git a/skyline_apiserver/policy/base.py b/skyline_apiserver/policy/base.py index be9de8e..dc11718 100644 --- a/skyline_apiserver/policy/base.py +++ b/skyline_apiserver/policy/base.py @@ -45,7 +45,12 @@ class UserContext(MutableMapping): self._data.setdefault("domain_name", getattr(access, "domain_name", None)) self._data.setdefault("user_domain_name", getattr(access, "user_domain_name", None)) self._data.setdefault("project_domain_name", getattr(access, "project_domain_name", None)) - self._data.setdefault("system_scope", getattr(access, "system_scope", None)) + self._data.setdefault( + "system_scope", + "all" + if getattr(access, "system") and getattr(access, "system", {}).get("all", False) + else "", + ) self._data.setdefault("role_ids", getattr(access, "role_ids", [])) self._data.setdefault("roles", getattr(access, "role_names", [])) diff --git a/skyline_apiserver/schemas/policy.py b/skyline_apiserver/schemas/policy.py index 3aaa0a4..f971a12 100644 --- a/skyline_apiserver/schemas/policy.py +++ b/skyline_apiserver/schemas/policy.py @@ -14,7 +14,7 @@ from __future__ import annotations -from typing import List +from typing import Dict, List, Optional from pydantic import BaseModel, Field @@ -30,3 +30,4 @@ class Policies(BaseModel): class PoliciesRules(BaseModel): rules: List[str] = Field(..., description="Policies rules list") + target: Optional[Dict[str, str]] = Field(None, description="Policies targets") diff --git a/test-requirements.txt b/test-requirements.txt index 384ed8a..f1187a1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -18,4 +18,4 @@ mimesis<=4.1.3 # MIT asgi-lifespan<=1.0.1 # MIT types-PyYAML<=5.4.10 # Apache-2.0 oslo.log<=5.0.0 # Apache-2.0 -neutron-lib>=2.15.0,<=2.21.0 # Apache-2.0 +neutron-lib>=2.15.0 # Apache-2.0