feat(backend): unified response helpers, refresh token, security hardening
- new resp package (OK/Page/Fail/BadRequest/.../InternalErr): unified error
envelope {detail, code?}, sanitized 500s (full error logged server-side,
generic message to client - stops DB error leakage), pagination envelope
{items,total,page,page_size}; migrate 200+ inline c.JSON error sites
- refresh token: 7d rotation tokens (SHA-256 hashed at rest), public
POST /auth/refresh with single-use rotation, POST /auth/logout revokes
all user refresh tokens (endpoint previously missing - frontend 404)
- security: gh-ost command line password masked in task status payload;
Instance.MarshalJSON guarantees encrypted passwords never leave the API
(removes reliance on per-handler manual masking); production (DEBUG=false)
with default JWT secret now auto-generates and persists a random secret
- safe.Go recover wrapper for background goroutines (instance probe,
gh-ost async)
- delete dead service layer (zero callers)
- fix pre-existing go vet unkeyed bson.E literals
This commit is contained in:
1 parent
d5b8b8116c
commit
16f1f7d65f
26 files changed
+916
-963
No files matched your search
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"sql-platform/internal/handler/resp"
|
||||
"sql-platform/internal/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -46,7 +47,7 @@ func GetLDAPConfig(c *gin.Context) {
|
||||
func UpdateLDAPConfig(c *gin.Context) {
|
||||
var input map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
|
||||
resp.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,11 +85,23 @@ func UpdateLDAPConfig(c *gin.Context) {
|
||||
if len(configs) == 0 {
|
||||
// Create new config
|
||||
cfg := model.LDAPConfig{
|
||||
LDAPHost: getStr("ldap_host"),
|
||||
AdminDN: getStr("admin_dn"),
|
||||
BaseDN: getStr("base_dn"),
|
||||
SearchFilter: func() *string { s := getStr("search_filter"); if s != "" { return &s }; return nil }(),
|
||||
UserMapping: func() *string { s := userMappingStr; if s != "" { return &s }; return nil }(),
|
||||
LDAPHost: getStr("ldap_host"),
|
||||
AdminDN: getStr("admin_dn"),
|
||||
BaseDN: getStr("base_dn"),
|
||||
SearchFilter: func() *string {
|
||||
s := getStr("search_filter")
|
||||
if s != "" {
|
||||
return &s
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
UserMapping: func() *string {
|
||||
s := userMappingStr
|
||||
if s != "" {
|
||||
return &s
|
||||
}
|
||||
return nil
|
||||
}(),
|
||||
}
|
||||
if port := getInt("ldap_port"); port > 0 {
|
||||
cfg.LDAPPort = &port
|
||||
@@ -149,13 +162,13 @@ func TestLDAPConnection(c *gin.Context) {
|
||||
TestPassword string `json:"test_password"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
|
||||
resp.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var cfg model.LDAPConfig
|
||||
if err := model.DB.Order("id").First(&cfg).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"detail": "LDAP 配置不存在,请先保存配置"})
|
||||
resp.NotFound(c, "LDAP 配置不存在,请先保存配置")
|
||||
return
|
||||
}
|
||||
if cfg.LDAPHost == "" {
|
||||
@@ -342,17 +355,17 @@ func ListLDAPUsers(c *gin.Context) {
|
||||
}
|
||||
|
||||
type LdapUser struct {
|
||||
DN string `json:"dn"`
|
||||
UID string `json:"uid"`
|
||||
CN string `json:"cn"`
|
||||
DN string `json:"dn"`
|
||||
UID string `json:"uid"`
|
||||
CN string `json:"cn"`
|
||||
Mail string `json:"mail"`
|
||||
}
|
||||
var users []LdapUser
|
||||
for _, e := range sr.Entries {
|
||||
users = append(users, LdapUser{
|
||||
DN: e.DN,
|
||||
UID: e.GetAttributeValue("uid"),
|
||||
CN: e.GetAttributeValue("cn"),
|
||||
DN: e.DN,
|
||||
UID: e.GetAttributeValue("uid"),
|
||||
CN: e.GetAttributeValue("cn"),
|
||||
Mail: e.GetAttributeValue("mail"),
|
||||
})
|
||||
}
|
||||
@@ -367,26 +380,26 @@ func CreateLDAPOU(c *gin.Context) {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil || req.OuName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "缺少 ou_name 参数"})
|
||||
resp.BadRequest(c, "缺少 ou_name 参数")
|
||||
return
|
||||
}
|
||||
|
||||
var cfg model.LDAPConfig
|
||||
if err := model.DB.Order("id").First(&cfg).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 配置不存在"})
|
||||
resp.BadRequest(c, "LDAP 配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := dialLDAP(&cfg)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 连接失败: " + err.Error()})
|
||||
resp.BadRequest(c, "LDAP 连接失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
adminPw, _ := decryptPassword(cfg.AdminPassword)
|
||||
if err := conn.Bind(cfg.AdminDN, adminPw); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 管理员绑定失败"})
|
||||
resp.BadRequest(c, "LDAP 管理员绑定失败")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -399,7 +412,7 @@ func CreateLDAPOU(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := conn.Add(addReq); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "创建 OU 失败: " + err.Error()})
|
||||
resp.BadRequest(c, "创建 OU 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -416,13 +429,13 @@ func CreateLDAPUser(c *gin.Context) {
|
||||
OuDn string `json:"ou_dn"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil || req.Username == "" || req.Password == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "缺少 username 或 password 参数"})
|
||||
resp.BadRequest(c, "缺少 username 或 password 参数")
|
||||
return
|
||||
}
|
||||
|
||||
var cfg model.LDAPConfig
|
||||
if err := model.DB.Order("id").First(&cfg).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 配置不存在"})
|
||||
resp.BadRequest(c, "LDAP 配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -434,14 +447,14 @@ func CreateLDAPUser(c *gin.Context) {
|
||||
|
||||
conn, err := dialLDAP(&cfg)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 连接失败: " + err.Error()})
|
||||
resp.BadRequest(c, "LDAP 连接失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
adminPw, _ := decryptPassword(cfg.AdminPassword)
|
||||
if err := conn.Bind(cfg.AdminDN, adminPw); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 管理员绑定失败"})
|
||||
resp.BadRequest(c, "LDAP 管理员绑定失败")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -475,7 +488,7 @@ func CreateLDAPUser(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := conn.Add(addReq); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "创建用户失败: " + err.Error()})
|
||||
resp.BadRequest(c, "创建用户失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -496,20 +509,20 @@ func BatchImportLDAP(c *gin.Context) {
|
||||
|
||||
var cfg model.LDAPConfig
|
||||
if err := model.DB.Order("id").First(&cfg).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 配置不存在"})
|
||||
resp.BadRequest(c, "LDAP 配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := dialLDAP(&cfg)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 连接失败: " + err.Error()})
|
||||
resp.BadRequest(c, "LDAP 连接失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
adminPw, _ := decryptPassword(cfg.AdminPassword)
|
||||
if err := conn.Bind(cfg.AdminDN, adminPw); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 管理员绑定失败"})
|
||||
resp.BadRequest(c, "LDAP 管理员绑定失败")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -534,7 +547,7 @@ func BatchImportLDAP(c *gin.Context) {
|
||||
[]string{"uid", "cn", "mail"}, nil,
|
||||
))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 搜索失败: " + err.Error()})
|
||||
resp.BadRequest(c, "LDAP 搜索失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -551,9 +564,14 @@ func BatchImportLDAP(c *gin.Context) {
|
||||
if len(req.UserIDs) > 0 {
|
||||
found := false
|
||||
for _, id := range req.UserIDs {
|
||||
if id == uid { found = true; break }
|
||||
if id == uid {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
if !found { continue }
|
||||
}
|
||||
|
||||
cn := entry.GetAttributeValue("cn")
|
||||
@@ -564,8 +582,12 @@ func BatchImportLDAP(c *gin.Context) {
|
||||
if err == nil {
|
||||
// Update existing user
|
||||
updates := map[string]interface{}{}
|
||||
if cn != "" && cn != existing.DisplayName { updates["display_name"] = cn }
|
||||
if mail != "" && (existing.Email == nil || *existing.Email != mail) { updates["email"] = mail }
|
||||
if cn != "" && cn != existing.DisplayName {
|
||||
updates["display_name"] = cn
|
||||
}
|
||||
if mail != "" && (existing.Email == nil || *existing.Email != mail) {
|
||||
updates["email"] = mail
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
model.DB.Model(&existing).Updates(updates)
|
||||
}
|
||||
@@ -582,7 +604,9 @@ func BatchImportLDAP(c *gin.Context) {
|
||||
IsActive: &isActive,
|
||||
HashedPassword: "-",
|
||||
}
|
||||
if mail != "" { newUser.Email = &mail }
|
||||
if mail != "" {
|
||||
newUser.Email = &mail
|
||||
}
|
||||
if err := model.DB.Create(&newUser).Error; err != nil {
|
||||
results = append(results, map[string]string{"uid": uid, "status": "error: " + err.Error()})
|
||||
skipped++
|
||||
@@ -606,31 +630,31 @@ func DeleteLDAPUser(c *gin.Context) {
|
||||
DN string `json:"dn" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "缺少 dn 参数"})
|
||||
resp.BadRequest(c, "缺少 dn 参数")
|
||||
return
|
||||
}
|
||||
|
||||
var cfg model.LDAPConfig
|
||||
if err := model.DB.Order("id").First(&cfg).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 配置不存在"})
|
||||
resp.BadRequest(c, "LDAP 配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := dialLDAP(&cfg)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 连接失败: " + err.Error()})
|
||||
resp.BadRequest(c, "LDAP 连接失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
adminPw, _ := decryptPassword(cfg.AdminPassword)
|
||||
if err := conn.Bind(cfg.AdminDN, adminPw); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "LDAP 管理员绑定失败"})
|
||||
resp.BadRequest(c, "LDAP 管理员绑定失败")
|
||||
return
|
||||
}
|
||||
|
||||
if err := conn.Del(ldap.NewDelRequest(req.DN, nil)); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"detail": "删除用户失败: " + err.Error()})
|
||||
resp.BadRequest(c, "删除用户失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user