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}) }