Go: add audit log framework (#17129)

### Summary

Prepare for audit log

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
Jin Hai
2026-07-20 23:50:45 +08:00
committed by GitHub
parent f45f03a016
commit 3670b047f0
11 changed files with 229 additions and 6 deletions

View File

@@ -20,16 +20,18 @@ import (
"fmt"
"net/http"
"ragflow/internal/common"
"ragflow/internal/engine/clickhouse"
"ragflow/internal/engine/redis"
"ragflow/internal/server"
"ragflow/internal/server/local"
"ragflow/internal/utility"
"strconv"
"time"
"ragflow/internal/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"ragflow/internal/service"
)
// UserHandler user handler
@@ -102,27 +104,60 @@ func (h *UserHandler) Register(c *gin.Context) {
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/users/login [post]
func (h *UserHandler) Login(c *gin.Context) {
startAt := time.Now()
operationLog := &common.OperationLog{
EventTime: startAt,
Operation: "login",
APIPath: c.FullPath(),
HTTPMethod: c.Request.Method,
IPAddress: c.ClientIP(),
}
defer func() {
operationLog.DurationMS = time.Since(startAt).Milliseconds()
clickhouseDriver := clickhouse.GetDriver()
err := clickhouseDriver.SaveOperationLog(operationLog)
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, err.Error())
return
}
}()
var req service.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ResponseWithCodeData(c, common.CodeBadRequest, false, err.Error())
operationLog.ErrorCode = uint16(common.CodeBadRequest)
operationLog.Message = err.Error()
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
operationLog.ResourceName = req.Username
user, code, err := h.userService.Login(&req)
if err != nil {
common.ResponseWithCodeData(c, code, false, err.Error())
operationLog.ErrorCode = uint16(code)
operationLog.Message = err.Error()
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
operationLog.UserID = user.ID
// Sign the access_token using itsdangerous (compatible with Python)
secretKey, err := server.GetSecretKey(redis.Get())
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, fmt.Sprintf("Failed to get secret key: %s", err.Error()))
errMessage := fmt.Sprintf("Failed to get secret key: %s", err.Error())
common.ResponseWithCodeData(c, common.CodeServerError, false, errMessage)
operationLog.ErrorCode = uint16(common.CodeServerError)
operationLog.Message = errMessage
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
authToken, err := utility.DumpAccessToken(*user.AccessToken, secretKey)
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, "Failed to generate auth token")
operationLog.ErrorCode = uint16(common.CodeServerError)
operationLog.Message = "Failed to generate auth token"
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
@@ -149,32 +184,68 @@ func (h *UserHandler) Login(c *gin.Context) {
// @Success 200 {object} map[string]interface{}
// @Router /v1/user/login [post]
func (h *UserHandler) LoginByEmail(c *gin.Context) {
startAt := time.Now()
operationLog := &common.OperationLog{
EventTime: startAt,
Operation: "login",
APIPath: c.FullPath(),
HTTPMethod: c.Request.Method,
IPAddress: c.ClientIP(),
}
defer func() {
operationLog.DurationMS = time.Since(startAt).Milliseconds()
clickhouseDriver := clickhouse.GetDriver()
err := clickhouseDriver.SaveOperationLog(operationLog)
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, err.Error())
return
}
}()
var req service.EmailLoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ResponseWithCodeData(c, common.CodeBadRequest, false, err.Error())
operationLog.ErrorCode = uint16(common.CodeBadRequest)
operationLog.Message = err.Error()
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
operationLog.ResourceName = req.Email
if !local.IsAdminAvailable() {
license := local.GetAdminStatus()
common.ResponseWithCodeData(c, common.CodeAuthenticationError, "No", license.Reason)
operationLog.ErrorCode = uint16(common.CodeAuthenticationError)
operationLog.Message = license.Reason
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
user, code, err := h.userService.LoginByEmail(&req)
if err != nil {
common.ResponseWithCodeData(c, code, false, err.Error())
operationLog.ErrorCode = uint16(code)
operationLog.Message = err.Error()
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
operationLog.UserID = user.ID
secretKey, err := server.GetSecretKey(redis.Get())
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, fmt.Sprintf("Failed to get secret key: %s", err.Error()))
errorMessage := fmt.Sprintf("Failed to get secret key: %s", err.Error())
common.ResponseWithCodeData(c, common.CodeServerError, false, errorMessage)
operationLog.ErrorCode = uint16(common.CodeServerError)
operationLog.Message = errorMessage
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
authToken, err := utility.DumpAccessToken(*user.AccessToken, secretKey)
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, "Failed to generate auth token")
operationLog.ErrorCode = uint16(common.CodeServerError)
operationLog.Message = "Failed to generate auth token"
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
setOAuthAuthCookie(c, authToken)
@@ -224,6 +295,24 @@ func (h *UserHandler) GetUserByID(c *gin.Context) {
// @Success 200 {object} map[string]interface{}
// @Router /v1/user/logout [post]
func (h *UserHandler) Logout(c *gin.Context) {
startAt := time.Now()
operationLog := &common.OperationLog{
EventTime: startAt,
Operation: "logout",
APIPath: c.FullPath(),
HTTPMethod: c.Request.Method,
IPAddress: c.ClientIP(),
}
defer func() {
operationLog.DurationMS = time.Since(startAt).Milliseconds()
clickhouseDriver := clickhouse.GetDriver()
err := clickhouseDriver.SaveOperationLog(operationLog)
if err != nil {
common.ResponseWithCodeData(c, common.CodeServerError, false, err.Error())
return
}
}()
http.SetCookie(c.Writer, &http.Cookie{
Name: oauthAuthCookie,
Value: "",
@@ -237,8 +326,11 @@ func (h *UserHandler) Logout(c *gin.Context) {
// Same as AuthMiddleware@auth.go
token := c.GetHeader("Authorization")
if token == "" {
common.ResponseWithHttpCodeData(c, http.StatusUnauthorized, 401, nil, "Missing Authorization header")
common.ResponseWithHttpCodeData(c, http.StatusUnauthorized, common.CodeUnauthorized, nil, "Missing Authorization header")
c.Abort()
operationLog.ErrorCode = uint16(common.CodeUnauthorized)
operationLog.Message = "Missing Authorization header"
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
@@ -247,13 +339,20 @@ func (h *UserHandler) Logout(c *gin.Context) {
if err != nil {
common.ResponseWithHttpCodeData(c, http.StatusUnauthorized, code, nil, "Invalid access token")
c.Abort()
operationLog.ErrorCode = uint16(code)
operationLog.Message = "Invalid access token"
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}
operationLog.UserID = user.ID
// Logout user
code, err = h.userService.Logout(user)
if err != nil {
common.ResponseWithCodeData(c, code, false, err.Error())
operationLog.ErrorCode = uint16(code)
operationLog.Message = err.Error()
operationLog.DurationMS = time.Since(startAt).Milliseconds()
return
}