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
This commit is contained in:
zhu.boxiang 2022-07-15 15:21:24 +08:00
parent 4d6f784384
commit 08c6265d1e
7 changed files with 63 additions and 11 deletions

View File

@ -2207,6 +2207,14 @@
"type": "string"
},
"description": "Policies rules list"
},
"target": {
"title": "Target",
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Policies targets"
}
}
},

View File

@ -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

View File

@ -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)}

View File

@ -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():

View File

@ -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", []))

View File

@ -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")

View File

@ -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