chore: remove sso_enforced_for_web_protocol、enable_web_sso_switch_component、sso_enforced_for_web from systemFeatures , instead with webapp_auth

pull/18656/head
NFish 1 year ago
parent 0c492abfc9
commit 7eb52770ad

@ -20,7 +20,7 @@ import cn from '@/utils/classnames'
import { useStore } from '@/app/components/app/store' import { useStore } from '@/app/components/app/store'
import AppSideBar from '@/app/components/app-sidebar' import AppSideBar from '@/app/components/app-sidebar'
import type { NavIcon } from '@/app/components/app-sidebar/navLink' import type { NavIcon } from '@/app/components/app-sidebar/navLink'
import { fetchAppDetail, fetchAppSSO } from '@/service/apps' import { fetchAppDetail } from '@/service/apps'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
@ -32,6 +32,13 @@ export type IAppDetailLayoutProps = {
params: { appId: string } params: { appId: string }
} }
type NavigationType = {
name: string
href: string
icon: NavIcon
selectedIcon: NavIcon
}
const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => { const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const { const {
children, children,
@ -50,12 +57,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
}))) })))
const [isLoadingAppDetail, setIsLoadingAppDetail] = useState(false) const [isLoadingAppDetail, setIsLoadingAppDetail] = useState(false)
const [appDetailRes, setAppDetailRes] = useState<App | null>(null) const [appDetailRes, setAppDetailRes] = useState<App | null>(null)
const [navigation, setNavigation] = useState<Array<{ const [navigation, setNavigation] = useState<Array<NavigationType>>([])
name: string
href: string
icon: NavIcon
selectedIcon: NavIcon
}>>([])
const { systemFeatures } = useGlobalPublicStore() const { systemFeatures } = useGlobalPublicStore()
const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => { const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => {
@ -142,15 +144,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
router.replace(`/app/${appId}/configuration`) router.replace(`/app/${appId}/configuration`)
} }
else { else {
setAppDetail({ ...res, enable_sso: false }) setAppDetail({ ...res })
setNavigation(getNavigations(appId, isCurrentWorkspaceEditor, res.mode)) setNavigation(getNavigations(appId, isCurrentWorkspaceEditor, res.mode) as Array<NavigationType>)
if (systemFeatures.enable_web_sso_switch_component && canIEditApp) {
fetchAppSSO({ appId }).then((ssoRes) => {
setAppDetail({ ...res, enable_sso: ssoRes.enabled })
})
}
} }
}, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, pathname, router, setAppDetail, systemFeatures.enable_web_sso_switch_component]) }, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, pathname, router, setAppDetail])
useUnmount(() => { useUnmount(() => {
setAppDetail() setAppDetail()

@ -8,19 +8,16 @@ import Loading from '@/app/components/base/loading'
import { ToastContext } from '@/app/components/base/toast' import { ToastContext } from '@/app/components/base/toast'
import { import {
fetchAppDetail, fetchAppDetail,
fetchAppSSO,
updateAppSSO,
updateAppSiteAccessToken, updateAppSiteAccessToken,
updateAppSiteConfig, updateAppSiteConfig,
updateAppSiteStatus, updateAppSiteStatus,
} from '@/service/apps' } from '@/service/apps'
import type { App, AppSSO } from '@/types/app' import type { App } from '@/types/app'
import type { UpdateAppSiteCodeResponse } from '@/models/app' import type { UpdateAppSiteCodeResponse } from '@/models/app'
import { asyncRunSafe } from '@/utils' import { asyncRunSafe } from '@/utils'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import type { IAppCardProps } from '@/app/components/app/overview/appCard' import type { IAppCardProps } from '@/app/components/app/overview/appCard'
import { useStore as useAppStore } from '@/app/components/app/store' import { useStore as useAppStore } from '@/app/components/app/store'
import { useGlobalPublicStore } from '@/context/global-public-context'
export type ICardViewProps = { export type ICardViewProps = {
appId: string appId: string
@ -31,19 +28,12 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
const { notify } = useContext(ToastContext) const { notify } = useContext(ToastContext)
const appDetail = useAppStore(state => state.appDetail) const appDetail = useAppStore(state => state.appDetail)
const setAppDetail = useAppStore(state => state.setAppDetail) const setAppDetail = useAppStore(state => state.setAppDetail)
const { systemFeatures } = useGlobalPublicStore()
const updateAppDetail = async () => { const updateAppDetail = async () => {
try { try {
const res = await fetchAppDetail({ url: '/apps', id: appId }) const res = await fetchAppDetail({ url: '/apps', id: appId })
if (systemFeatures.enable_web_sso_switch_component) {
const ssoRes = await fetchAppSSO({ appId })
setAppDetail({ ...res, enable_sso: ssoRes.enabled })
}
else {
setAppDetail({ ...res }) setAppDetail({ ...res })
} }
}
catch (error) { console.error(error) } catch (error) { console.error(error) }
} }
@ -93,16 +83,6 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
if (!err) if (!err)
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
if (systemFeatures.enable_web_sso_switch_component) {
const [sso_err] = await asyncRunSafe<AppSSO>(
updateAppSSO({ id: appId, enabled: Boolean(params.enable_sso) }) as Promise<AppSSO>,
)
if (sso_err) {
handleCallbackResult(sso_err)
return
}
}
handleCallbackResult(err) handleCallbackResult(err)
} }

@ -1,7 +1,7 @@
'use client' 'use client'
import { useRouter, useSearchParams } from 'next/navigation' import { useRouter, useSearchParams } from 'next/navigation'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useEffect } from 'react' import React, { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
@ -9,6 +9,7 @@ import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/
import { setAccessToken } from '@/app/components/share/utils' import { setAccessToken } from '@/app/components/share/utils'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { useGlobalPublicStore } from '@/context/global-public-context' import { useGlobalPublicStore } from '@/context/global-public-context'
import { SSOProtocol } from '@/types/feature'
const WebSSOForm: FC = () => { const WebSSOForm: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
@ -27,15 +28,15 @@ const WebSSOForm: FC = () => {
}) })
} }
const getAppCodeFromRedirectUrl = () => { const getAppCodeFromRedirectUrl = useCallback(() => {
const appCode = redirectUrl?.split('/').pop() const appCode = redirectUrl?.split('/').pop()
if (!appCode) if (!appCode)
return null return null
return appCode return appCode
} }, [redirectUrl])
const processTokenAndRedirect = async () => { const processTokenAndRedirect = useCallback(async () => {
const appCode = getAppCodeFromRedirectUrl() const appCode = getAppCodeFromRedirectUrl()
if (!appCode || !tokenFromUrl || !redirectUrl) { if (!appCode || !tokenFromUrl || !redirectUrl) {
showErrorToast('redirect url or app code or token is invalid.') showErrorToast('redirect url or app code or token is invalid.')
@ -44,7 +45,7 @@ const WebSSOForm: FC = () => {
await setAccessToken(appCode, tokenFromUrl) await setAccessToken(appCode, tokenFromUrl)
router.push(redirectUrl) router.push(redirectUrl)
} }, [getAppCodeFromRedirectUrl, redirectUrl, router, tokenFromUrl])
const handleSSOLogin = async () => { const handleSSOLogin = async () => {
const appCode = getAppCodeFromRedirectUrl() const appCode = getAppCodeFromRedirectUrl()
@ -53,18 +54,18 @@ const WebSSOForm: FC = () => {
return return
} }
switch (systemFeatures.sso_enforced_for_web_protocol) { switch (systemFeatures.webapp_auth.sso_config.protocol) {
case 'saml': { case SSOProtocol.SAML: {
const samlRes = await fetchWebSAMLSSOUrl(appCode, redirectUrl) const samlRes = await fetchWebSAMLSSOUrl(appCode, redirectUrl)
router.push(samlRes.url) router.push(samlRes.url)
break break
} }
case 'oidc': { case SSOProtocol.OIDC: {
const oidcRes = await fetchWebOIDCSSOUrl(appCode, redirectUrl) const oidcRes = await fetchWebOIDCSSOUrl(appCode, redirectUrl)
router.push(oidcRes.url) router.push(oidcRes.url)
break break
} }
case 'oauth2': { case SSOProtocol.OAuth2: {
const oauth2Res = await fetchWebOAuth2SSOUrl(appCode, redirectUrl) const oauth2Res = await fetchWebOAuth2SSOUrl(appCode, redirectUrl)
router.push(oauth2Res.url) router.push(oauth2Res.url)
break break
@ -74,6 +75,14 @@ const WebSSOForm: FC = () => {
} }
} }
const goWebApp = () => {
if (!redirectUrl) {
showErrorToast('redirect url is invalid.')
return
}
router.push(redirectUrl)
}
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
if (message) { if (message) {
@ -88,8 +97,8 @@ const WebSSOForm: FC = () => {
} }
init() init()
}, [message, tokenFromUrl]) // Added dependencies to useEffect }, [message, processTokenAndRedirect, tokenFromUrl])
if (systemFeatures.webapp_auth.enable) {
return ( return (
<div className="flex items-center justify-center h-full"> <div className="flex items-center justify-center h-full">
<div className={cn('flex flex-col items-center w-full grow justify-center', 'px-6', 'md:px-[108px]')}> <div className={cn('flex flex-col items-center w-full grow justify-center', 'px-6', 'md:px-[108px]')}>
@ -97,6 +106,12 @@ const WebSSOForm: FC = () => {
</div> </div>
</div> </div>
) )
}
else {
return <div className="flex items-center justify-center h-full">
<p>Current App is not required for login, you can <span className='text-text-accent cursor-pointer' onClick={goWebApp}>click here</span> continue.</p>
</div>
}
} }
export default React.memo(WebSSOForm) export default React.memo(WebSSOForm)

@ -17,7 +17,7 @@ export type IAppDetailNavProps = {
desc: string desc: string
isExternal?: boolean isExternal?: boolean
icon: string icon: string
icon_background: string icon_background: string | null
navigation: Array<{ navigation: Array<{
name: string name: string
href: string href: string

@ -30,7 +30,7 @@ export default function AccessControl(props: AccessControlProps) {
const specificMembers = useAccessControlStore(s => s.specificMembers) const specificMembers = useAccessControlStore(s => s.specificMembers)
const currentMenu = useAccessControlStore(s => s.currentMenu) const currentMenu = useAccessControlStore(s => s.currentMenu)
const setCurrentMenu = useAccessControlStore(s => s.setCurrentMenu) const setCurrentMenu = useAccessControlStore(s => s.setCurrentMenu)
const hideTip = systemFeatures.enable_web_sso_switch_component && systemFeatures.sso_enforced_for_web const hideTip = systemFeatures
useEffect(() => { useEffect(() => {
setAppId(app.id) setAppId(app.id)

@ -20,7 +20,7 @@ export default function SpecificGroupsOrMembers() {
const setSpecificMembers = useAccessControlStore(s => s.setSpecificMembers) const setSpecificMembers = useAccessControlStore(s => s.setSpecificMembers)
const { t } = useTranslation() const { t } = useTranslation()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
const hideTip = systemFeatures.enable_web_sso_switch_component && systemFeatures.sso_enforced_for_web const hideTip = systemFeatures.webapp_auth.enable
const { isPending, data } = useAppWhiteListSubjects(appId, Boolean(appId) && currentMenu === AccessMode.SPECIFIC_GROUPS_MEMBERS) const { isPending, data } = useAppWhiteListSubjects(appId, Boolean(appId) && currentMenu === AccessMode.SPECIFIC_GROUPS_MEMBERS)
useEffect(() => { useEffect(() => {

@ -1,5 +1,5 @@
import type { LangFuseConfig, LangSmithConfig, OpikConfig, TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type' import type { LangFuseConfig, LangSmithConfig, OpikConfig, TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
import type { App, AppSSO, AppTemplate, SiteConfig } from '@/types/app' import type { App, AppTemplate, SiteConfig } from '@/types/app'
/* export type App = { /* export type App = {
id: string id: string
@ -89,8 +89,6 @@ export type DSLImportResponse = {
error: string error: string
} }
export type AppSSOResponse = { enabled: AppSSO['enable_sso'] }
export type AppTemplatesResponse = { export type AppTemplatesResponse = {
data: AppTemplate[] data: AppTemplate[]
} }

@ -1,6 +1,6 @@
import type { Fetcher } from 'swr' import type { Fetcher } from 'swr'
import { del, get, patch, post, put } from './base' import { del, get, patch, post, put } from './base'
import type { ApiKeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppDetailResponse, AppListResponse, AppSSOResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, DSLImportMode, DSLImportResponse, GenerationIntroductionResponse, TracingConfig, TracingStatus, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WorkflowDailyConversationsResponse } from '@/models/app' import type { ApiKeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, DSLImportMode, DSLImportResponse, GenerationIntroductionResponse, TracingConfig, TracingStatus, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WorkflowDailyConversationsResponse } from '@/models/app'
import type { CommonResponse } from '@/models/common' import type { CommonResponse } from '@/models/common'
import type { AppIconType, AppMode, ModelConfig } from '@/types/app' import type { AppIconType, AppMode, ModelConfig } from '@/types/app'
import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type' import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
@ -13,13 +13,6 @@ export const fetchAppDetail = ({ url, id }: { url: string; id: string }) => {
return get<AppDetailResponse>(`${url}/${id}`) return get<AppDetailResponse>(`${url}/${id}`)
} }
export const fetchAppSSO = async ({ appId }: { appId: string }) => {
return get<AppSSOResponse>(`/enterprise/app-setting/sso?appID=${appId}`)
}
export const updateAppSSO = async ({ id, enabled }: { id: string; enabled: boolean }) => {
return post('/enterprise/app-setting/sso', { body: { app_id: id, enabled } })
}
export const fetchAppTemplates: Fetcher<AppTemplatesResponse, { url: string }> = ({ url }) => { export const fetchAppTemplates: Fetcher<AppTemplatesResponse, { url: string }> = ({ url }) => {
return get<AppTemplatesResponse>(url) return get<AppTemplatesResponse>(url)
} }

@ -512,7 +512,7 @@ export const ssePost = (
}).catch(() => { }).catch(() => {
res.json().then((data: any) => { res.json().then((data: any) => {
if (isPublicAPI) { if (isPublicAPI) {
if (data.code === 'web_sso_auth_required') if (data.code === 'web_sso_auth_required' || data.code === 'web_app_access_denied')
requiredWebSSOLogin() requiredWebSSOLogin()
if (data.code === 'unauthorized') { if (data.code === 'unauthorized') {
@ -566,7 +566,7 @@ export const request = async<T>(url: string, options = {}, otherOptions?: IOther
// special code // special code
const { code, message } = errRespData const { code, message } = errRespData
// webapp sso // webapp sso
if (code === 'web_sso_auth_required') { if (code === 'web_sso_auth_required' || code === 'web_app_access_denied') {
requiredWebSSOLogin() requiredWebSSOLogin()
return Promise.reject(err) return Promise.reject(err)
} }

@ -21,9 +21,6 @@ type License = {
export type SystemFeatures = { export type SystemFeatures = {
sso_enforced_for_signin: boolean sso_enforced_for_signin: boolean
sso_enforced_for_signin_protocol: SSOProtocol | '' sso_enforced_for_signin_protocol: SSOProtocol | ''
sso_enforced_for_web: boolean
sso_enforced_for_web_protocol: SSOProtocol | ''
enable_web_sso_switch_component: boolean
enable_email_code_login: boolean enable_email_code_login: boolean
enable_email_password_login: boolean enable_email_password_login: boolean
enable_social_oauth_login: boolean enable_social_oauth_login: boolean
@ -38,14 +35,18 @@ export type SystemFeatures = {
favicon: string favicon: string
application_title: string application_title: string
} }
webapp_auth: {
enable: boolean
allow_sso: boolean
sso_config: {
protocol: SSOProtocol
}
}
} }
export const defaultSystemFeatures: SystemFeatures = { export const defaultSystemFeatures: SystemFeatures = {
sso_enforced_for_signin: false, sso_enforced_for_signin: false,
sso_enforced_for_signin_protocol: '', sso_enforced_for_signin_protocol: '',
sso_enforced_for_web: false,
sso_enforced_for_web_protocol: '',
enable_web_sso_switch_component: false,
enable_email_code_login: false, enable_email_code_login: false,
enable_email_password_login: false, enable_email_password_login: false,
enable_social_oauth_login: false, enable_social_oauth_login: false,
@ -63,4 +64,11 @@ export const defaultSystemFeatures: SystemFeatures = {
favicon: '', favicon: '',
application_title: 'test title', application_title: 'test title',
}, },
webapp_auth: {
enable: false,
allow_sso: false,
sso_config: {
protocol: SSOProtocol.SAML,
},
},
} }

Loading…
Cancel
Save