Remove useless code (#4416)
parent
da81233d61
commit
dd94931116
@ -1,59 +0,0 @@
|
|||||||
from flask import current_app, redirect
|
|
||||||
from flask_restful import Resource, reqparse
|
|
||||||
|
|
||||||
from controllers.console import api
|
|
||||||
from controllers.console.setup import setup_required
|
|
||||||
from services.enterprise.enterprise_sso_service import EnterpriseSSOService
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseSSOSamlLogin(Resource):
|
|
||||||
|
|
||||||
@setup_required
|
|
||||||
def get(self):
|
|
||||||
return EnterpriseSSOService.get_sso_saml_login()
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseSSOSamlAcs(Resource):
|
|
||||||
|
|
||||||
@setup_required
|
|
||||||
def post(self):
|
|
||||||
parser = reqparse.RequestParser()
|
|
||||||
parser.add_argument('SAMLResponse', type=str, required=True, location='form')
|
|
||||||
args = parser.parse_args()
|
|
||||||
saml_response = args['SAMLResponse']
|
|
||||||
|
|
||||||
try:
|
|
||||||
token = EnterpriseSSOService.post_sso_saml_acs(saml_response)
|
|
||||||
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}/signin?console_token={token}')
|
|
||||||
except Exception as e:
|
|
||||||
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}/signin?message={str(e)}')
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseSSOOidcLogin(Resource):
|
|
||||||
|
|
||||||
@setup_required
|
|
||||||
def get(self):
|
|
||||||
return EnterpriseSSOService.get_sso_oidc_login()
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseSSOOidcCallback(Resource):
|
|
||||||
|
|
||||||
@setup_required
|
|
||||||
def get(self):
|
|
||||||
parser = reqparse.RequestParser()
|
|
||||||
parser.add_argument('state', type=str, required=True, location='args')
|
|
||||||
parser.add_argument('code', type=str, required=True, location='args')
|
|
||||||
parser.add_argument('oidc-state', type=str, required=True, location='cookies')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
try:
|
|
||||||
token = EnterpriseSSOService.get_sso_oidc_callback(args)
|
|
||||||
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}/signin?console_token={token}')
|
|
||||||
except Exception as e:
|
|
||||||
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}/signin?message={str(e)}')
|
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(EnterpriseSSOSamlLogin, '/enterprise/sso/saml/login')
|
|
||||||
api.add_resource(EnterpriseSSOSamlAcs, '/enterprise/sso/saml/acs')
|
|
||||||
api.add_resource(EnterpriseSSOOidcLogin, '/enterprise/sso/oidc/login')
|
|
||||||
api.add_resource(EnterpriseSSOOidcCallback, '/enterprise/sso/oidc/callback')
|
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
from flask_restful import Resource
|
||||||
|
|
||||||
|
from controllers.web import api
|
||||||
|
from services.feature_service import FeatureService
|
||||||
|
|
||||||
|
|
||||||
|
class SystemFeatureApi(Resource):
|
||||||
|
def get(self):
|
||||||
|
return FeatureService.get_system_features().dict()
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(SystemFeatureApi, '/system-features')
|
||||||
@ -1,28 +0,0 @@
|
|||||||
from flask import current_app
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from services.enterprise.enterprise_service import EnterpriseService
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseFeatureModel(BaseModel):
|
|
||||||
sso_enforced_for_signin: bool = False
|
|
||||||
sso_enforced_for_signin_protocol: str = ''
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseFeatureService:
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_enterprise_features(cls) -> EnterpriseFeatureModel:
|
|
||||||
features = EnterpriseFeatureModel()
|
|
||||||
|
|
||||||
if current_app.config['ENTERPRISE_ENABLED']:
|
|
||||||
cls._fulfill_params_from_enterprise(features)
|
|
||||||
|
|
||||||
return features
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _fulfill_params_from_enterprise(cls, features):
|
|
||||||
enterprise_info = EnterpriseService.get_info()
|
|
||||||
|
|
||||||
features.sso_enforced_for_signin = enterprise_info['sso_enforced_for_signin']
|
|
||||||
features.sso_enforced_for_signin_protocol = enterprise_info['sso_enforced_for_signin_protocol']
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from models.account import Account, AccountStatus
|
|
||||||
from services.account_service import AccountService, TenantService
|
|
||||||
from services.enterprise.base import EnterpriseRequest
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class EnterpriseSSOService:
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_sso_saml_login(cls) -> str:
|
|
||||||
return EnterpriseRequest.send_request('GET', '/sso/saml/login')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def post_sso_saml_acs(cls, saml_response: str) -> str:
|
|
||||||
response = EnterpriseRequest.send_request('POST', '/sso/saml/acs', json={'SAMLResponse': saml_response})
|
|
||||||
if 'email' not in response or response['email'] is None:
|
|
||||||
logger.exception(response)
|
|
||||||
raise Exception('Saml response is invalid')
|
|
||||||
|
|
||||||
return cls.login_with_email(response.get('email'))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_sso_oidc_login(cls):
|
|
||||||
return EnterpriseRequest.send_request('GET', '/sso/oidc/login')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_sso_oidc_callback(cls, args: dict):
|
|
||||||
state_from_query = args['state']
|
|
||||||
code_from_query = args['code']
|
|
||||||
state_from_cookies = args['oidc-state']
|
|
||||||
|
|
||||||
if state_from_cookies != state_from_query:
|
|
||||||
raise Exception('invalid state or code')
|
|
||||||
|
|
||||||
response = EnterpriseRequest.send_request('GET', '/sso/oidc/callback', params={'code': code_from_query})
|
|
||||||
if 'email' not in response or response['email'] is None:
|
|
||||||
logger.exception(response)
|
|
||||||
raise Exception('OIDC response is invalid')
|
|
||||||
|
|
||||||
return cls.login_with_email(response.get('email'))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def login_with_email(cls, email: str) -> str:
|
|
||||||
account = Account.query.filter_by(email=email).first()
|
|
||||||
if account is None:
|
|
||||||
raise Exception('account not found, please contact system admin to invite you to join in a workspace')
|
|
||||||
|
|
||||||
if account.status == AccountStatus.BANNED:
|
|
||||||
raise Exception('account is banned, please contact system admin')
|
|
||||||
|
|
||||||
tenants = TenantService.get_join_tenants(account)
|
|
||||||
if len(tenants) == 0:
|
|
||||||
raise Exception("workspace not found, please contact system admin to invite you to join in a workspace")
|
|
||||||
|
|
||||||
token = AccountService.get_account_jwt_token(account)
|
|
||||||
|
|
||||||
return token
|
|
||||||
@ -0,0 +1,147 @@
|
|||||||
|
'use client'
|
||||||
|
import cn from 'classnames'
|
||||||
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { fetchSystemFeatures, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share'
|
||||||
|
import LogoSite from '@/app/components/base/logo/logo-site'
|
||||||
|
import { setAccessToken } from '@/app/components/share/utils'
|
||||||
|
|
||||||
|
const WebSSOForm: FC = () => {
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
|
const redirectUrl = searchParams.get('redirect_url')
|
||||||
|
const tokenFromUrl = searchParams.get('web_sso_token')
|
||||||
|
const message = searchParams.get('message')
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [protocal, setProtocal] = useState('')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchFeaturesAndSetToken = async () => {
|
||||||
|
await fetchSystemFeatures().then((res) => {
|
||||||
|
setProtocal(res.sso_enforced_for_web_protocol)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Callback from SSO, process token and redirect
|
||||||
|
if (tokenFromUrl && redirectUrl) {
|
||||||
|
const appCode = redirectUrl.split('/').pop()
|
||||||
|
if (!appCode) {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message: 'redirect url is invalid. App code is not found.',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await setAccessToken(appCode, tokenFromUrl)
|
||||||
|
router.push(redirectUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchFeaturesAndSetToken()
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleSSOLogin = () => {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
if (!redirectUrl) {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message: 'redirect url is not found.',
|
||||||
|
})
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const appCode = redirectUrl.split('/').pop()
|
||||||
|
if (!appCode) {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message: 'redirect url is invalid. App code is not found.',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocal === 'saml') {
|
||||||
|
fetchWebSAMLSSOUrl(appCode, redirectUrl).then((res) => {
|
||||||
|
router.push(res.url)
|
||||||
|
}).finally(() => {
|
||||||
|
setIsLoading(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if (protocal === 'oidc') {
|
||||||
|
fetchWebOIDCSSOUrl(appCode, redirectUrl).then((res) => {
|
||||||
|
router.push(res.url)
|
||||||
|
}).finally(() => {
|
||||||
|
setIsLoading(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message: 'sso protocal is not supported.',
|
||||||
|
})
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn(
|
||||||
|
'flex w-full min-h-screen',
|
||||||
|
'sm:p-4 lg:p-8',
|
||||||
|
'gap-x-20',
|
||||||
|
'justify-center lg:justify-start',
|
||||||
|
)}>
|
||||||
|
<div className={
|
||||||
|
cn(
|
||||||
|
'flex w-full flex-col bg-white shadow rounded-2xl shrink-0',
|
||||||
|
'space-between',
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
<div className='flex items-center justify-between p-6 w-full'>
|
||||||
|
<LogoSite />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={
|
||||||
|
cn(
|
||||||
|
'flex flex-col items-center w-full grow items-center justify-center',
|
||||||
|
'px-6',
|
||||||
|
'md:px-[108px]',
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
<div className='flex flex-col md:w-[400px]'>
|
||||||
|
<div className="w-full mx-auto">
|
||||||
|
<h2 className="text-[32px] font-bold text-gray-900">{t('login.pageTitle')}</h2>
|
||||||
|
</div>
|
||||||
|
<div className="w-full mx-auto mt-10">
|
||||||
|
<Button
|
||||||
|
tabIndex={0}
|
||||||
|
type='primary'
|
||||||
|
onClick={() => { handleSSOLogin() }}
|
||||||
|
disabled={isLoading}
|
||||||
|
className="w-full !fone-medium !text-sm"
|
||||||
|
>{t('login.sso')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(WebSSOForm)
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import { get } from './base'
|
|
||||||
import type { EnterpriseFeatures } from '@/types/enterprise'
|
|
||||||
|
|
||||||
export const getEnterpriseFeatures = () => {
|
|
||||||
return get<EnterpriseFeatures>('/enterprise-features')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSAMLSSOUrl = () => {
|
|
||||||
return get<{ url: string }>('/enterprise/sso/saml/login')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getOIDCSSOUrl = () => {
|
|
||||||
return get<{ url: string; state: string }>('/enterprise/sso/oidc/login')
|
|
||||||
}
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { get } from './base'
|
||||||
|
|
||||||
|
export const getUserSAMLSSOUrl = () => {
|
||||||
|
return get<{ url: string }>('/enterprise/sso/saml/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserOIDCSSOUrl = () => {
|
||||||
|
return get<{ url: string; state: string }>('/enterprise/sso/oidc/login')
|
||||||
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
export type EnterpriseFeatures = {
|
|
||||||
sso_enforced_for_signin: boolean
|
|
||||||
sso_enforced_for_signin_protocol: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultEnterpriseFeatures: EnterpriseFeatures = {
|
|
||||||
sso_enforced_for_signin: false,
|
|
||||||
sso_enforced_for_signin_protocol: '',
|
|
||||||
}
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
export type SystemFeatures = {
|
||||||
|
sso_enforced_for_signin: boolean
|
||||||
|
sso_enforced_for_signin_protocol: string
|
||||||
|
sso_enforced_for_web: boolean
|
||||||
|
sso_enforced_for_web_protocol: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultSystemFeatures: SystemFeatures = {
|
||||||
|
sso_enforced_for_signin: false,
|
||||||
|
sso_enforced_for_signin_protocol: '',
|
||||||
|
sso_enforced_for_web: false,
|
||||||
|
sso_enforced_for_web_protocol: '',
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue