Add opt-in built-in Feishu OIDC issuer for OpenVPN
This commit is contained in:
1 parent
6ca609fde4
commit
63cdda4ae1
23 files changed
+1244
-17
No files matched your search
@@ -37,6 +37,35 @@
|
||||
</el-card>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="auth.isAdmin()" :label="$t('identity.tab')" name="identity">
|
||||
<div class="settings-column settings-column-wide">
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>{{ $t('identity.title') }}</b></template>
|
||||
<el-alert type="info" :closable="false" :title="$t('identity.description')" style="margin-bottom:16px" />
|
||||
<el-form label-position="top" @submit.prevent="saveIdentity">
|
||||
<el-form-item :label="$t('identity.appId')"><el-input v-model.trim="identityForm.app_id" placeholder="cli_..." maxlength="128" /></el-form-item>
|
||||
<el-form-item :label="$t('identity.appSecret')"><el-input v-model="identityForm.app_secret" type="password" show-password autocomplete="new-password" :placeholder="$t('identity.secretPlaceholder')" /></el-form-item>
|
||||
<el-form-item :label="$t('identity.tenant')"><el-input v-model.trim="identityForm.tenant_key" :placeholder="$t('identity.tenantPlaceholder')" maxlength="128" /></el-form-item>
|
||||
<el-form-item :label="$t('identity.enabled')"><el-switch v-model="identityForm.enabled" :disabled="!identityStatus?.tls_ready" /></el-form-item>
|
||||
<el-button type="primary" :loading="identitySaving" @click="saveIdentity">{{ $t('common.save') }}</el-button>
|
||||
<el-button :loading="identityLoading" @click="loadIdentity">{{ $t('tls.refresh') }}</el-button>
|
||||
</el-form>
|
||||
<el-alert v-if="!identityStatus?.tls_ready" type="warning" :closable="false" :title="$t('identity.httpsRequired')" style="margin-top:16px" />
|
||||
<el-alert v-if="identityStatus?.enabled && !identityStatus?.gateway_ready" type="warning" :closable="false" :title="$t('identity.gatewayPending')" style="margin-top:16px" />
|
||||
</el-card>
|
||||
<el-card class="page-card" shadow="never" style="margin-top:16px">
|
||||
<template #header><b>{{ $t('identity.addresses') }}</b></template>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item :label="$t('identity.issuer')">{{ identityStatus?.issuer || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('identity.feishuCallback')">{{ identityStatus?.feishu_callback || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('identity.vpnCallback')">{{ identityStatus?.vpn_callback || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('identity.clientId')">{{ identityStatus?.client_id || '—' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<p class="tls-hint">{{ $t('identity.routeHint') }}</p>
|
||||
<el-button :loading="identityGatewayLoading" :disabled="!identityStatus?.tls_ready" @click="syncIdentityGateway">{{ $t('identity.syncGateway') }}</el-button>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="auth.isAdmin()" :label="$t('settings.gatewayTab')" name="gateway">
|
||||
<div class="settings-column settings-column-wide">
|
||||
<el-card class="page-card" shadow="never" style="margin-bottom:16px">
|
||||
@@ -162,12 +191,44 @@ const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const activeTab = ref('security')
|
||||
const visibleTab = (name) => auth.isAdmin() && ['gateway', 'host'].includes(name) ? name : 'security'
|
||||
const visibleTab = (name) => auth.isAdmin() && ['identity', 'gateway', 'host'].includes(name) ? name : 'security'
|
||||
watch(() => route.query.tab, name => { activeTab.value = visibleTab(name) }, { immediate: true })
|
||||
function changeTab(name) {
|
||||
if (route.query.tab !== name) router.replace({ path: '/settings', query: name === 'security' ? {} : { tab: name } })
|
||||
}
|
||||
const pwd = ref({ old_password: '', new_password: '', confirm: '' })
|
||||
const identityStatus = ref(null)
|
||||
const identityForm = ref({ enabled: false, app_id: '', app_secret: '', tenant_key: '' })
|
||||
const identityLoading = ref(false)
|
||||
const identitySaving = ref(false)
|
||||
const identityGatewayLoading = ref(false)
|
||||
async function loadIdentity() {
|
||||
identityLoading.value = true
|
||||
try {
|
||||
const { data } = await api.get('/system/identity')
|
||||
identityStatus.value = data
|
||||
identityForm.value = { enabled: data.enabled, app_id: data.app_id || '', app_secret: '', tenant_key: data.tenant_key || '' }
|
||||
} catch (e) { ElMessage.error(e.response?.data?.error || t('identity.loadFailed')) }
|
||||
finally { identityLoading.value = false }
|
||||
}
|
||||
async function saveIdentity() {
|
||||
identitySaving.value = true
|
||||
try {
|
||||
await api.put('/system/identity', identityForm.value)
|
||||
await loadIdentity()
|
||||
ElMessage.success(t('identity.saved'))
|
||||
} catch (e) { ElMessage.error(e.response?.data?.error || t('identity.saveFailed')) }
|
||||
finally { identitySaving.value = false }
|
||||
}
|
||||
async function syncIdentityGateway() {
|
||||
identityGatewayLoading.value = true
|
||||
try {
|
||||
await api.post('/system/identity/gateway')
|
||||
await loadIdentity()
|
||||
ElMessage.success(t('identity.gatewaySynced'))
|
||||
} catch (e) { ElMessage.error(e.response?.data?.error || t('identity.gatewayFailed')) }
|
||||
finally { identityGatewayLoading.value = false }
|
||||
}
|
||||
const pwdLoading = ref(false)
|
||||
const mfa = ref({ enabled: false, uri: '', code: '' })
|
||||
const mfaCanvas = ref(null)
|
||||
@@ -365,7 +426,7 @@ async function changePwd() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (auth.isAdmin() && !auth.mustChange) { loadTLS(); loadNginxSetup(); loadHostReadiness() }
|
||||
if (auth.isAdmin() && !auth.mustChange) { loadTLS(); loadNginxSetup(); loadHostReadiness(); loadIdentity() }
|
||||
try {
|
||||
const status = await api.get('/mfa/status')
|
||||
mfa.value.enabled = status.data.enabled
|
||||
|
||||
Reference in new issue
Block a user