功能:完善通用云管工作台、审计与诊断能力
This commit is contained in:
1 parent
5ac692724d
commit
9f05aa20ea
225 files changed
+14266
-141
No files matched your search
@@ -0,0 +1,125 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type administratorSession struct {
|
||||
ID string `json:"-" db:"id"`
|
||||
Key string `json:"key" db:"-"`
|
||||
CreatedAt *time.Time `json:"createdAt" db:"created_at"`
|
||||
ExpiresAt time.Time `json:"expiresAt" db:"expires_at"`
|
||||
ClientIP string `json:"clientIp" db:"client_ip"`
|
||||
UserAgent string `json:"userAgent" db:"user_agent"`
|
||||
Current bool `json:"current" db:"-"`
|
||||
}
|
||||
|
||||
// A management handle must never disclose the JWT's session identifier.
|
||||
func (s *Server) administratorSessionKey(id string) string {
|
||||
mac := hmac.New(sha256.New, []byte(s.Config.JWTSecret))
|
||||
mac.Write([]byte("vdi-admin-session-management\x00" + id))
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
func sessionUserAgent(raw string) string {
|
||||
runes := []rune(strings.Map(func(r rune) rune {
|
||||
if unicode.IsControl(r) {
|
||||
return ' '
|
||||
}
|
||||
return r
|
||||
}, raw))
|
||||
if len(runes) > 500 {
|
||||
runes = runes[:500]
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func (s *Server) administratorSessions(c *gin.Context) {
|
||||
tx, _ := s.accountTransaction(c, true)
|
||||
if tx == nil {
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
var username string
|
||||
if err := tx.GetContext(c.Request.Context(), &username, "SELECT username FROM administrators WHERE id=$1", c.Param("id")); err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
items := []administratorSession{}
|
||||
err := tx.SelectContext(c.Request.Context(), &items, `SELECT x.id,x.expires_at,d.created_at,COALESCE(d.client_ip,'') client_ip,COALESCE(d.user_agent,'') user_agent
|
||||
FROM admin_sessions x LEFT JOIN admin_session_details d ON d.session_id=x.id
|
||||
WHERE x.admin_id=$1 AND x.expires_at>now() ORDER BY x.expires_at DESC,x.id DESC`, c.Param("id"))
|
||||
if err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
for i := range items {
|
||||
items[i].Key = s.administratorSessionKey(items[i].ID)
|
||||
items[i].Current = items[i].ID == c.GetString("sessionId")
|
||||
}
|
||||
list(c, items)
|
||||
}
|
||||
|
||||
func (s *Server) revokeAdministratorSession(c *gin.Context) {
|
||||
key := c.Param("sessionKey")
|
||||
if len(key) != 64 {
|
||||
s.fail(c, 404, "not_found", "登录会话不存在或已失效")
|
||||
return
|
||||
}
|
||||
tx, actor := s.accountTransaction(c, true)
|
||||
if tx == nil {
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
ctx := c.Request.Context()
|
||||
var username string
|
||||
if err := tx.GetContext(ctx, &username, "SELECT username FROM administrators WHERE id=$1 FOR UPDATE", c.Param("id")); err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
var ids []string
|
||||
if err := tx.SelectContext(ctx, &ids, "SELECT id FROM admin_sessions WHERE admin_id=$1 AND expires_at>now()", c.Param("id")); err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
for _, id := range ids {
|
||||
if !hmac.Equal([]byte(s.administratorSessionKey(id)), []byte(key)) {
|
||||
continue
|
||||
}
|
||||
if id == c.GetString("sessionId") {
|
||||
s.fail(c, 409, "current_session_protected", "当前登录请使用退出登录入口")
|
||||
return
|
||||
}
|
||||
result, err := tx.ExecContext(ctx, "DELETE FROM admin_sessions WHERE id=$1 AND admin_id=$2 AND expires_at>now()", id, c.Param("id"))
|
||||
if err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
count, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
break
|
||||
}
|
||||
err = accountAudit(ctx, tx, actor.Username, "revoke_administrator_session", username+":"+key, c.GetString("requestId"))
|
||||
if err == nil {
|
||||
err = tx.Commit()
|
||||
}
|
||||
if err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
c.Status(204)
|
||||
return
|
||||
}
|
||||
s.fail(c, 404, "not_found", "登录会话不存在或已失效")
|
||||
}
|
||||
Reference in new issue
Block a user