feat: Add default_project_id
1. add default_project_id into profile to return 2. if user has default_project_id, then we will login into this project as default. Change-Id: I147f7866163ae4d102e83f7c28bbf0077f463974
This commit is contained in:
parent
99f557e19c
commit
235f7fb286
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add ``default_project_id`` into profile to return.
|
||||
- |
|
||||
If user has default_project_id, then we will login into this project as default.
|
@ -15,7 +15,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import PurePath
|
||||
from typing import Any, List, Optional, Tuple
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Header, HTTPException, Request, Response, status
|
||||
from fastapi.responses import RedirectResponse
|
||||
@ -26,7 +26,7 @@ from keystoneclient.client import Client as KeystoneClient
|
||||
from skyline_apiserver import schemas
|
||||
from skyline_apiserver.api import deps
|
||||
from skyline_apiserver.client import utils
|
||||
from skyline_apiserver.client.openstack.keystone import revoke_token
|
||||
from skyline_apiserver.client.openstack.keystone import get_token_data, get_user, revoke_token
|
||||
from skyline_apiserver.client.openstack.system import (
|
||||
get_endpoints,
|
||||
get_project_scope_token,
|
||||
@ -46,6 +46,19 @@ from skyline_apiserver.types import constants
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
async def _get_default_project_id(
|
||||
session: Session, region: str, user_id: Optional[str] = None
|
||||
) -> Union[str, None]:
|
||||
if not user_id:
|
||||
token = session.get_token()
|
||||
token_data = await get_token_data(token, region, session)
|
||||
_user_id = token_data["token"]["user"]["id"]
|
||||
else:
|
||||
_user_id = user_id
|
||||
user = await get_user(_user_id, region, session)
|
||||
return getattr(user, "default_project_id", None)
|
||||
|
||||
|
||||
async def _get_projects_and_unscope_token(
|
||||
region: str,
|
||||
domain: Optional[str] = None,
|
||||
@ -53,7 +66,7 @@ async def _get_projects_and_unscope_token(
|
||||
password: Optional[str] = None,
|
||||
token: Optional[str] = None,
|
||||
project_enabled: bool = False,
|
||||
) -> Tuple[List[Any], str]:
|
||||
) -> Tuple[List[Any], str, Union[str, None]]:
|
||||
auth_url = await utils.get_endpoint(
|
||||
region=region,
|
||||
service="keystone",
|
||||
@ -78,6 +91,9 @@ async def _get_projects_and_unscope_token(
|
||||
session = Session(
|
||||
auth=unscope_auth, verify=CONF.default.cafile, timeout=constants.DEFAULT_TIMEOUT
|
||||
)
|
||||
|
||||
default_project_id = await _get_default_project_id(session, region)
|
||||
|
||||
unscope_client = KeystoneClient(
|
||||
session=session,
|
||||
endpoint=auth_url,
|
||||
@ -93,7 +109,7 @@ async def _get_projects_and_unscope_token(
|
||||
if not project_scope:
|
||||
raise Exception("You are not authorized for any projects or domains.")
|
||||
|
||||
return project_scope, unscope_token
|
||||
return project_scope, unscope_token, default_project_id
|
||||
|
||||
|
||||
async def _patch_profile(profile: schemas.Profile, global_request_id: str) -> schemas.Profile:
|
||||
@ -107,9 +123,13 @@ async def _patch_profile(profile: schemas.Profile, global_request_id: str) -> sc
|
||||
)
|
||||
|
||||
if not projects:
|
||||
projects, _ = await _get_projects_and_unscope_token(
|
||||
projects, _, default_project_id = await _get_projects_and_unscope_token(
|
||||
region=profile.region, token=profile.keystone_token
|
||||
)
|
||||
else:
|
||||
default_project_id = await _get_default_project_id(
|
||||
get_system_session(), profile.region, user_id=profile.user.id
|
||||
)
|
||||
|
||||
profile.projects = {
|
||||
i.id: {
|
||||
@ -121,6 +141,8 @@ async def _patch_profile(profile: schemas.Profile, global_request_id: str) -> sc
|
||||
for i in projects
|
||||
}
|
||||
|
||||
profile.default_project_id = default_project_id
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@ -151,7 +173,7 @@ async def login(
|
||||
),
|
||||
) -> schemas.Profile:
|
||||
try:
|
||||
project_scope, unscope_token = await _get_projects_and_unscope_token(
|
||||
project_scope, unscope_token, default_project_id = await _get_projects_and_unscope_token(
|
||||
region=credential.region,
|
||||
domain=credential.domain,
|
||||
username=credential.username,
|
||||
@ -162,7 +184,7 @@ async def login(
|
||||
project_scope_token = await get_project_scope_token(
|
||||
keystone_token=unscope_token,
|
||||
region=credential.region,
|
||||
project_id=project_scope[0].id,
|
||||
project_id=default_project_id or project_scope[0].id,
|
||||
)
|
||||
|
||||
profile = await generate_profile(
|
||||
@ -248,7 +270,7 @@ async def websso(
|
||||
),
|
||||
) -> RedirectResponse:
|
||||
try:
|
||||
project_scope, _ = await _get_projects_and_unscope_token(
|
||||
project_scope, _, default_project_id = await _get_projects_and_unscope_token(
|
||||
region=CONF.openstack.sso_region,
|
||||
token=token,
|
||||
project_enabled=True,
|
||||
@ -257,7 +279,7 @@ async def websso(
|
||||
project_scope_token = await get_project_scope_token(
|
||||
keystone_token=token,
|
||||
region=CONF.openstack.sso_region,
|
||||
project_id=project_scope[0].id,
|
||||
project_id=default_project_id or project_scope[0].id,
|
||||
)
|
||||
|
||||
profile = await generate_profile(
|
||||
|
@ -17,7 +17,7 @@ from __future__ import annotations
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from keystoneauth1.exceptions.http import Unauthorized
|
||||
from keystoneauth1.exceptions.http import NotFound, Unauthorized
|
||||
from keystoneauth1.session import Session
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
|
||||
@ -82,3 +82,43 @@ async def revoke_token(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e),
|
||||
)
|
||||
|
||||
|
||||
async def get_token_data(token: str, region: str, session: Session) -> Any:
|
||||
try:
|
||||
kc = await utils.keystone_client(
|
||||
session=session,
|
||||
region=region,
|
||||
)
|
||||
return await run_in_threadpool(kc.tokens.get_token_data, token)
|
||||
except Unauthorized as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=str(e),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e),
|
||||
)
|
||||
|
||||
|
||||
async def get_user(id: str, region: str, session: Session) -> Any:
|
||||
try:
|
||||
kc = await utils.keystone_client(session=session, region=region)
|
||||
return await run_in_threadpool(kc.users.get, id)
|
||||
except Unauthorized as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=str(e),
|
||||
)
|
||||
except NotFound as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(e),
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(e),
|
||||
)
|
||||
|
@ -94,6 +94,7 @@ class Profile(PayloadBase):
|
||||
base_domains: Optional[List[str]] = Field(None, description="User base domains")
|
||||
endpoints: Optional[Dict[str, Any]] = Field(None, description="Keystone endpoints")
|
||||
projects: Optional[Dict[str, Any]] = Field(None, description="User projects")
|
||||
default_project_id: Optional[str] = Field(None, description="User default project ID")
|
||||
version: str = Field(..., description="Version")
|
||||
|
||||
def toPayLoad(self) -> Payload:
|
||||
|
@ -2757,6 +2757,11 @@
|
||||
"type": "object",
|
||||
"description": "User projects"
|
||||
},
|
||||
"default_project_id": {
|
||||
"title": "Default Project Id",
|
||||
"type": "string",
|
||||
"description": "User default project ID"
|
||||
},
|
||||
"version": {
|
||||
"title": "Version",
|
||||
"type": "string",
|
||||
|
Loading…
x
Reference in New Issue
Block a user