Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proxy feature for SAML #96

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/cloudforet/console_api_v2/conf/global_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,6 @@
# OpenCost Settings
OPENCOST_PROMETHEUS_HOST = ""
OPENCOST_PROMETHEUS_EXTERNAL_URL = ""

# SAML Settings
CONSOLE_DOMAIN = ""
67 changes: 64 additions & 3 deletions src/cloudforet/console_api_v2/interface/rest/extension/auth.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import logging

from fastapi import Depends, Header
from fastapi import Depends, Request
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import RedirectResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi_utils.cbv import cbv
from fastapi_utils.inferring_router import InferringRouter
from spaceone.core.error import *
from spaceone.core import config
from spaceone.core.error import ERROR_REQUIRED_PARAMETER
from spaceone.core.fastapi.api import BaseAPI, exception_handler

from cloudforet.console_api_v2.model.auth.request import *
from cloudforet.console_api_v2.manager.cloudforet_manager import CloudforetManager
from cloudforet.console_api_v2.service.auth_service import AuthService
from cloudforet.console_api_v2.service.proxy_service import ProxyService

_LOGGER = logging.getLogger(__name__)
_AUTH_SCHEME = HTTPBasic()
Expand All @@ -33,3 +36,61 @@ async def basic(
auth_service = AuthService()
await run_in_threadpool(auth_service.basic, http_authorization.dict())
return {"status_code": "200"}

@router.post("/saml/{domain_id}")
@exception_handler
async def saml(self, request: Request, domain_id: str):
form_data = await request.form()
credentials = self._extract_credentials(request, dict(form_data))
refresh_token = self._issue_token(credentials, domain_id)
domain_name = self._get_domain_name(domain_id)
return self._redirect_response(domain_name, refresh_token)

@staticmethod
def _extract_credentials(request: Request, form_data: dict) -> dict:
return {
"http_host": request.client.host,
"server_port": request.url.port,
"script_name": request.url.path,
"post_data": form_data,
}

@staticmethod
def _issue_token(credentials: dict, domain_id: str) -> str:
dispatch_params = {
"grpc_method": "identity.Token.issue",
"credentials": credentials,
"auth_type": "EXTERNAL",
"domain_id": domain_id,
}

proxy_service = ProxyService()
response = proxy_service.dispatch_api(dispatch_params)

return response.get("refresh_token")

@staticmethod
def _get_domain_name(domain_id: str) -> str:
cloudforet_mgr = CloudforetManager()
grpc_method = "identity.Domain.get"
dispatch_params = {"domain_id": domain_id}
system_token = config.get_global("TOKEN")

response = cloudforet_mgr.dispatch_api(
grpc_method, dispatch_params, system_token
)

return response.get("name")

@staticmethod
def _redirect_response(domain_name: str, refresh_token: str) -> RedirectResponse:
console_domain: str = config.get_global("CONSOLE_DOMAIN").format(
domain_name=domain_name
)
if refresh_token:
return RedirectResponse(
f"{console_domain}/saml?sso_access_token={refresh_token}",
status_code=302,
)

return RedirectResponse(f"{console_domain}", status_code=302)
Loading