115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package identityprovider
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
"vpn-platform/backend/internal/nginxgateway"
|
|
"vpn-platform/backend/internal/store"
|
|
)
|
|
|
|
func (p *Provider) Status(c *gin.Context) {
|
|
cfg, err := current()
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
c.JSON(500, gin.H{"error": "读取身份配置失败"})
|
|
return
|
|
}
|
|
base, tlsError := issuer()
|
|
if tlsError != nil {
|
|
base = ""
|
|
}
|
|
issuerRoute, _ := nginxgateway.AuthRouteReady(0)
|
|
callback, vpnCallback := "", ""
|
|
if base != "" {
|
|
callback, vpnCallback = base+"/feishu/callback", strings.TrimSuffix(base, "/id")+"/oauth2/callback"
|
|
}
|
|
c.JSON(200, gin.H{
|
|
"enabled": cfg.Enabled, "app_id": cfg.AppID, "app_secret_configured": cfg.AppSecret != "",
|
|
"tenant_key": cfg.TenantKey, "client_id": cfg.ClientID, "client_secret_configured": cfg.ClientSecret != "",
|
|
"issuer": base, "feishu_callback": callback, "vpn_callback": vpnCallback,
|
|
"tls_ready": tlsError == nil, "gateway_ready": issuerRoute,
|
|
})
|
|
}
|
|
|
|
func (p *Provider) Configure(c *gin.Context) {
|
|
if c.Request.TLS == nil {
|
|
peer, _, err := net.SplitHostPort(c.Request.RemoteAddr)
|
|
if err != nil || net.ParseIP(peer) == nil || !net.ParseIP(peer).IsLoopback() || c.GetHeader("X-Forwarded-Proto") != "https" {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "请通过可信 HTTPS 管理入口保存飞书凭据"})
|
|
return
|
|
}
|
|
}
|
|
var req struct {
|
|
Enabled bool `json:"enabled"`
|
|
AppID string `json:"app_id"`
|
|
AppSecret string `json:"app_secret"`
|
|
TenantKey string `json:"tenant_key"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(400, gin.H{"error": "配置格式错误"})
|
|
return
|
|
}
|
|
req.AppID = strings.TrimSpace(req.AppID)
|
|
req.TenantKey = strings.TrimSpace(req.TenantKey)
|
|
if len(req.AppID) > 128 || strings.ContainsAny(req.AppID, " \r\n\t") || (req.Enabled && (req.AppID == "" || !validTenant(req.TenantKey))) {
|
|
c.JSON(400, gin.H{"error": "请输入有效的飞书 App ID 和租户标识"})
|
|
return
|
|
}
|
|
if req.Enabled {
|
|
if _, err := issuer(); err != nil {
|
|
c.JSON(400, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
}
|
|
cfg, err := current()
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
c.JSON(500, gin.H{"error": "读取身份配置失败"})
|
|
return
|
|
}
|
|
if req.AppSecret != "" {
|
|
if len(req.AppSecret) > 512 {
|
|
c.JSON(400, gin.H{"error": "密钥过长"})
|
|
return
|
|
}
|
|
cfg.AppSecret, err = store.EncryptSecret(req.AppSecret)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "密钥保存失败"})
|
|
return
|
|
}
|
|
}
|
|
if req.Enabled && cfg.AppSecret == "" {
|
|
c.JSON(400, gin.H{"error": "飞书 App Secret 未配置"})
|
|
return
|
|
}
|
|
var newClientSecret string
|
|
if cfg.ClientID == "" {
|
|
newClientSecret, err = randomToken()
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "凭据生成失败"})
|
|
return
|
|
}
|
|
cfg.ClientID = "opsx-vpn"
|
|
cfg.ClientSecret, err = store.EncryptSecret(newClientSecret)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "凭据保存失败"})
|
|
return
|
|
}
|
|
}
|
|
if req.Enabled {
|
|
if _, _, err := signingKey(); err != nil {
|
|
c.JSON(503, gin.H{"error": "签名密钥初始化失败"})
|
|
return
|
|
}
|
|
}
|
|
cfg.ID, cfg.AppID, cfg.TenantKey, cfg.Enabled = 1, req.AppID, req.TenantKey, req.Enabled
|
|
if err := store.DB.Save(&cfg).Error; err != nil {
|
|
c.JSON(500, gin.H{"error": "身份配置保存失败"})
|
|
return
|
|
}
|
|
store.Audit(c.GetString("username"), "identity_provider_configured", "feishu", "")
|
|
c.JSON(http.StatusOK, gin.H{"ok": true, "client_id": cfg.ClientID})
|
|
}
|