skyline-apiserver/skyline_apiserver/api/v1/login.py
zhu.boxiang 8cb60f0833 chore: Move skyline_apiserver out of libs
1. move skyline_apiserver out of libs
2. remove libs folder
3. remove old skyline folder
4. adjust zull, devstack and dockerfile

Change-Id: I27a4babd3df077d1dfc7555f67a6ea618d4b2966
2022-05-18 17:21:00 +08:00

226 lines
7.0 KiB
Python

# Copyright 2021 99cloud
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response, status
from keystoneauth1.identity.v3 import Password
from keystoneauth1.session import Session
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.system import (
get_endpoints,
get_project_scope_token,
get_projects,
)
from skyline_apiserver.client.utils import generate_session, get_system_session
from skyline_apiserver.config import CONF
from skyline_apiserver.core.security import (
generate_profile,
generate_profile_by_token,
parse_access_token,
)
from skyline_apiserver.db import api as db_api
from skyline_apiserver.log import LOG
from skyline_apiserver.types import constants
router = APIRouter()
async def _patch_profile(profile: schemas.Profile, global_request_id: str) -> schemas.Profile:
try:
profile.endpoints = await get_endpoints(region=profile.region)
profile.projects = await get_projects(
global_request_id=global_request_id,
region=profile.region,
user=profile.user.id,
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=str(e),
)
return profile
@router.post(
"/login",
description="Login & get user profile.",
responses={
200: {"model": schemas.Profile},
401: {"model": schemas.common.UnauthorizedMessage},
},
response_model=schemas.Profile,
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def login(
request: Request,
response: Response,
credential: schemas.Credential,
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
try:
auth_url = await utils.get_endpoint(
region=credential.region,
service="keystone",
session=get_system_session(),
)
unscope_auth = Password(
auth_url=auth_url,
user_domain_name=credential.domain,
username=credential.username,
password=credential.password,
reauthenticate=False,
)
session = Session(auth=unscope_auth, verify=False, timeout=constants.DEFAULT_TIMEOUT)
unscope_client = KeystoneClient(
session=session,
endpoint=auth_url,
interface=CONF.openstack.interface_type,
)
project_scope = unscope_client.auth.projects()
# we must get the project_scope with enabled project
project_scope = [scope for scope in project_scope if scope.enabled]
if not project_scope:
raise Exception("You are not authorized for any projects or domains.")
project_scope_token = await get_project_scope_token(
keystone_token=session.get_token(),
region=credential.region,
project_id=project_scope[0].id,
)
profile = await generate_profile(
keystone_token=project_scope_token,
region=credential.region,
)
profile = await _patch_profile(profile, x_openstack_request_id)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=str(e),
)
else:
response.set_cookie(CONF.default.session_name, profile.toJWTPayload())
return profile
@router.get(
"/profile",
description="Get user profile.",
responses={
200: {"model": schemas.Profile},
401: {"model": schemas.common.UnauthorizedMessage},
},
response_model=schemas.Profile,
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def get_profile(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
return await _patch_profile(profile, x_openstack_request_id)
@router.post(
"/logout",
description="Log out.",
responses={
200: {"model": schemas.common.OK},
},
response_model=schemas.common.OK,
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def logout(
response: Response,
request: Request,
payload: str = Depends(deps.getJWTPayload),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
if payload:
try:
token = parse_access_token(payload)
profile = await generate_profile_by_token(token)
session = await generate_session(profile)
await revoke_token(profile, session, x_openstack_request_id, token.keystone_token)
await db_api.revoke_token(profile.uuid, profile.exp)
except Exception as e:
LOG.debug(str(e))
response.delete_cookie(CONF.default.session_name)
return schemas.common.OK(message="Logout OK")
@router.post(
"/switch_project/{project_id}",
description="Switch project.",
responses={
200: {"model": schemas.Profile},
401: {"model": schemas.common.UnauthorizedMessage},
},
response_model=schemas.Profile,
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def switch_project(
project_id: str,
response: Response,
profile: schemas.Profile = Depends(deps.get_profile),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
try:
project_scope_token = await get_project_scope_token(
keystone_token=profile.keystone_token,
region=profile.region,
project_id=project_id,
)
profile = await generate_profile(
keystone_token=project_scope_token,
region=profile.region,
uuid_value=profile.uuid,
)
profile = await _patch_profile(profile, x_openstack_request_id)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=str(e),
)
else:
response.set_cookie(CONF.default.session_name, profile.toJWTPayload())
return profile