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 __future__ import annotations
|
||||||
|
|
||||||
from pathlib import PurePath
|
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 import APIRouter, Depends, Form, Header, HTTPException, Request, Response, status
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
@ -26,7 +26,7 @@ from keystoneclient.client import Client as KeystoneClient
|
|||||||
from skyline_apiserver import schemas
|
from skyline_apiserver import schemas
|
||||||
from skyline_apiserver.api import deps
|
from skyline_apiserver.api import deps
|
||||||
from skyline_apiserver.client import utils
|
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 (
|
from skyline_apiserver.client.openstack.system import (
|
||||||
get_endpoints,
|
get_endpoints,
|
||||||
get_project_scope_token,
|
get_project_scope_token,
|
||||||
@ -46,6 +46,19 @@ from skyline_apiserver.types import constants
|
|||||||
router = APIRouter()
|
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(
|
async def _get_projects_and_unscope_token(
|
||||||
region: str,
|
region: str,
|
||||||
domain: Optional[str] = None,
|
domain: Optional[str] = None,
|
||||||
@ -53,7 +66,7 @@ async def _get_projects_and_unscope_token(
|
|||||||
password: Optional[str] = None,
|
password: Optional[str] = None,
|
||||||
token: Optional[str] = None,
|
token: Optional[str] = None,
|
||||||
project_enabled: bool = False,
|
project_enabled: bool = False,
|
||||||
) -> Tuple[List[Any], str]:
|
) -> Tuple[List[Any], str, Union[str, None]]:
|
||||||
auth_url = await utils.get_endpoint(
|
auth_url = await utils.get_endpoint(
|
||||||
region=region,
|
region=region,
|
||||||
service="keystone",
|
service="keystone",
|
||||||
@ -78,6 +91,9 @@ async def _get_projects_and_unscope_token(
|
|||||||
session = Session(
|
session = Session(
|
||||||
auth=unscope_auth, verify=CONF.default.cafile, timeout=constants.DEFAULT_TIMEOUT
|
auth=unscope_auth, verify=CONF.default.cafile, timeout=constants.DEFAULT_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
default_project_id = await _get_default_project_id(session, region)
|
||||||
|
|
||||||
unscope_client = KeystoneClient(
|
unscope_client = KeystoneClient(
|
||||||
session=session,
|
session=session,
|
||||||
endpoint=auth_url,
|
endpoint=auth_url,
|
||||||
@ -93,7 +109,7 @@ async def _get_projects_and_unscope_token(
|
|||||||
if not project_scope:
|
if not project_scope:
|
||||||
raise Exception("You are not authorized for any projects or domains.")
|
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:
|
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:
|
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
|
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 = {
|
profile.projects = {
|
||||||
i.id: {
|
i.id: {
|
||||||
@ -121,6 +141,8 @@ async def _patch_profile(profile: schemas.Profile, global_request_id: str) -> sc
|
|||||||
for i in projects
|
for i in projects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile.default_project_id = default_project_id
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
@ -151,7 +173,7 @@ async def login(
|
|||||||
),
|
),
|
||||||
) -> schemas.Profile:
|
) -> schemas.Profile:
|
||||||
try:
|
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,
|
region=credential.region,
|
||||||
domain=credential.domain,
|
domain=credential.domain,
|
||||||
username=credential.username,
|
username=credential.username,
|
||||||
@ -162,7 +184,7 @@ async def login(
|
|||||||
project_scope_token = await get_project_scope_token(
|
project_scope_token = await get_project_scope_token(
|
||||||
keystone_token=unscope_token,
|
keystone_token=unscope_token,
|
||||||
region=credential.region,
|
region=credential.region,
|
||||||
project_id=project_scope[0].id,
|
project_id=default_project_id or project_scope[0].id,
|
||||||
)
|
)
|
||||||
|
|
||||||
profile = await generate_profile(
|
profile = await generate_profile(
|
||||||
@ -248,7 +270,7 @@ async def websso(
|
|||||||
),
|
),
|
||||||
) -> RedirectResponse:
|
) -> RedirectResponse:
|
||||||
try:
|
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,
|
region=CONF.openstack.sso_region,
|
||||||
token=token,
|
token=token,
|
||||||
project_enabled=True,
|
project_enabled=True,
|
||||||
@ -257,7 +279,7 @@ async def websso(
|
|||||||
project_scope_token = await get_project_scope_token(
|
project_scope_token = await get_project_scope_token(
|
||||||
keystone_token=token,
|
keystone_token=token,
|
||||||
region=CONF.openstack.sso_region,
|
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(
|
profile = await generate_profile(
|
||||||
|
@ -17,7 +17,7 @@ from __future__ import annotations
|
|||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from keystoneauth1.exceptions.http import Unauthorized
|
from keystoneauth1.exceptions.http import NotFound, Unauthorized
|
||||||
from keystoneauth1.session import Session
|
from keystoneauth1.session import Session
|
||||||
from starlette.concurrency import run_in_threadpool
|
from starlette.concurrency import run_in_threadpool
|
||||||
|
|
||||||
@ -82,3 +82,43 @@ async def revoke_token(
|
|||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
detail=str(e),
|
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")
|
base_domains: Optional[List[str]] = Field(None, description="User base domains")
|
||||||
endpoints: Optional[Dict[str, Any]] = Field(None, description="Keystone endpoints")
|
endpoints: Optional[Dict[str, Any]] = Field(None, description="Keystone endpoints")
|
||||||
projects: Optional[Dict[str, Any]] = Field(None, description="User projects")
|
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")
|
version: str = Field(..., description="Version")
|
||||||
|
|
||||||
def toPayLoad(self) -> Payload:
|
def toPayLoad(self) -> Payload:
|
||||||
|
@ -2757,6 +2757,11 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "User projects"
|
"description": "User projects"
|
||||||
},
|
},
|
||||||
|
"default_project_id": {
|
||||||
|
"title": "Default Project Id",
|
||||||
|
"type": "string",
|
||||||
|
"description": "User default project ID"
|
||||||
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"title": "Version",
|
"title": "Version",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
Loading…
Reference in New Issue
Block a user