Fetch Feishu tenant identity from app credentials
This commit is contained in:
1 parent
0528bc4b9a
commit
eb3861bbe8
7 files changed
+316
-11
No files matched your search
@@ -575,7 +575,7 @@
|
||||
},
|
||||
"identity": {
|
||||
"tab": "Feishu identity", "title": "Built-in Feishu identity service", "description": "Issue OIDC identities for VPN using your existing HTTPS domain. Feishu app credentials stay upstream; this platform creates separate VPN client credentials.",
|
||||
"appId": "Feishu App ID", "appSecret": "Feishu App Secret", "secretPlaceholder": "Leave blank to retain the secret; use a rotated secret for new setup", "tenant": "Allowed Feishu tenant_key", "tenantPlaceholder": "Verify the enterprise tenant_key from Feishu user info", "enabled": "Enable built-in issuer", "httpsOnly": "Enter app credentials only through the HTTPS management site. Switch to its secure address.", "openHttps": "Open HTTPS management site",
|
||||
"appId": "Feishu App ID", "appSecret": "Feishu App Secret", "secretPlaceholder": "Leave blank to retain the secret; use a rotated secret for new setup", "tenant": "Allowed Feishu tenant_key", "tenantPlaceholder": "Fetch automatically or enter manually", "fetchTenant": "Fetch tenant ID", "tenantFetched": "Feishu tenant ID retrieved", "tenantFailed": "Could not fetch Feishu tenant", "enabled": "Enable built-in issuer", "httpsOnly": "Enter app credentials only through the HTTPS management site. Switch to its secure address.", "openHttps": "Open HTTPS management site",
|
||||
"httpsRequired": "Configure and activate your public HTTPS site and certificate first.", "gatewayPending": "The identity provider is saved but Nginx does not yet route /id/; sync the gateway.",
|
||||
"addresses": "Addresses and client credentials", "issuer": "OIDC issuer", "feishuCallback": "Callback registered in Feishu", "vpnCallback": "VPN client callback", "clientId": "VPN Client ID",
|
||||
"oneTimeSecret": "New VPN Client Secret is displayed only once. Store it safely; rotation of active tunnel credentials requires separate planning.", "routeHint": "Sync identity routes first. After starting the one OpenVPN OIDC tunnel using this domain, sync again to route /oauth2/ to its local auth component.",
|
||||
|
||||
@@ -575,7 +575,7 @@
|
||||
},
|
||||
"identity": {
|
||||
"tab": "飞书身份", "title": "内置飞书身份服务", "description": "使用现有 HTTPS 域名签发 VPN 所需 OIDC 身份令牌。飞书 App 凭据仅用于飞书上游,VPN 客户端凭据由本平台生成。",
|
||||
"appId": "飞书 App ID", "appSecret": "飞书 App Secret", "secretPlaceholder": "留空保持当前密钥;新配置请输入轮换后的密钥", "tenant": "允许的飞书 tenant_key", "tenantPlaceholder": "从飞书用户信息确认企业 tenant_key", "enabled": "启用内置发行者", "httpsOnly": "应用密钥只能通过 HTTPS 管理入口填写;请切换到安全地址。", "openHttps": "打开 HTTPS 管理入口",
|
||||
"appId": "飞书 App ID", "appSecret": "飞书 App Secret", "secretPlaceholder": "留空保持当前密钥;新配置请输入轮换后的密钥", "tenant": "允许的飞书 tenant_key", "tenantPlaceholder": "可自动获取,也可手动填写", "fetchTenant": "自动获取企业标识", "tenantFetched": "已读取飞书企业标识", "tenantFailed": "读取飞书企业信息失败", "enabled": "启用内置发行者", "httpsOnly": "应用密钥只能通过 HTTPS 管理入口填写;请切换到安全地址。", "openHttps": "打开 HTTPS 管理入口",
|
||||
"httpsRequired": "请先完成公网域名 HTTPS 证书和站点配置,再启用身份服务。", "gatewayPending": "身份服务已保存,但 Nginx 尚未转发 /id/;请同步公网入口。",
|
||||
"addresses": "接入地址与客户端凭据", "issuer": "OIDC 发行者", "feishuCallback": "登记到飞书应用的回调", "vpnCallback": "VPN 客户端回调", "clientId": "VPN Client ID",
|
||||
"oneTimeSecret": "新 VPN Client Secret 只显示这一次,请立即妥善保存;现有专线的密钥轮换需另行安排。", "routeHint": "先同步身份路由;启动唯一一条使用当前域名的 OpenVPN OIDC 专线后,再同步一次以转发 /oauth2/ 到本机认证组件。",
|
||||
|
||||
@@ -45,9 +45,13 @@
|
||||
<el-alert v-if="!identitySecure" type="warning" :closable="false" :title="$t('identity.httpsOnly')" style="margin-bottom:16px" />
|
||||
<el-button v-if="!identitySecure && identityHttpsUrl" type="primary" tag="a" :href="identityHttpsUrl" style="margin-bottom:16px">{{ $t('identity.openHttps') }}</el-button>
|
||||
<el-form v-if="identitySecure" 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.appId')"><el-input v-model.trim="identityForm.app_id" placeholder="cli_..." maxlength="128" @change="onIdentityAppChange" /></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.tenant')">
|
||||
<el-input v-model.trim="identityForm.tenant_key" :placeholder="$t('identity.tenantPlaceholder')" maxlength="128" />
|
||||
<el-button :loading="tenantLoading" :disabled="!identityForm.app_id" style="margin-top:8px" @click="fetchIdentityTenant">{{ $t('identity.fetchTenant') }}</el-button>
|
||||
<span v-if="tenantName" class="tls-hint" style="margin-left:10px">{{ tenantName }}</span>
|
||||
</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>
|
||||
@@ -206,12 +210,35 @@ const identityHttpsUrl = computed(() => identityStatus.value?.issuer ? `${identi
|
||||
const identityLoading = ref(false)
|
||||
const identitySaving = ref(false)
|
||||
const identityGatewayLoading = ref(false)
|
||||
const tenantLoading = ref(false)
|
||||
const tenantName = ref('')
|
||||
function onIdentityAppChange() {
|
||||
if (identityForm.value.app_id !== identityStatus.value?.app_id) {
|
||||
identityForm.value.tenant_key = ''
|
||||
tenantName.value = ''
|
||||
}
|
||||
}
|
||||
async function fetchIdentityTenant() {
|
||||
if (!identitySecure) return ElMessage.warning(t('identity.httpsOnly'))
|
||||
tenantLoading.value = true
|
||||
try {
|
||||
const appId = identityForm.value.app_id
|
||||
const { data } = await api.post('/system/identity/tenant', { app_id: appId, app_secret: identityForm.value.app_secret })
|
||||
if (identityForm.value.app_id === appId) {
|
||||
identityForm.value.tenant_key = data.tenant_key
|
||||
tenantName.value = data.name || ''
|
||||
ElMessage.success(t('identity.tenantFetched'))
|
||||
}
|
||||
} catch (e) { ElMessage.error(e.response?.data?.error || t('identity.tenantFailed')) }
|
||||
finally { tenantLoading.value = 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 || '' }
|
||||
tenantName.value = ''
|
||||
} catch (e) { ElMessage.error(e.response?.data?.error || t('identity.loadFailed')) }
|
||||
finally { identityLoading.value = false }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user