mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-06 23:51:15 +08:00
7
go.mod
7
go.mod
@@ -31,20 +31,20 @@ require (
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/gomarkdown/markdown v0.0.0-20260614204949-e08cff860f76
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/infiniflow/infinity-go-sdk v0.0.0-00010101000000-000000000000
|
||||
github.com/iromli/go-itsdangerous v0.0.0-20220223194502-9c8bef8dac6a
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kaptinlin/jsonrepair v0.4.8
|
||||
github.com/larksuite/oapi-sdk-go/v3 v3.9.9
|
||||
github.com/ledongthuc/pdf v0.0.0-20250511090121-5959a4027728
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/minio/minio-go/v7 v7.0.99
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nats-io/nats-server/v2 v2.14.3
|
||||
github.com/nats-io/nats.go v1.52.0
|
||||
github.com/nikolalohinski/gonja v1.5.3
|
||||
github.com/open-dingtalk/dingtalk-stream-sdk-go v0.0.5
|
||||
github.com/peterh/liner v1.2.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkoukk/tiktoken-go v0.1.8
|
||||
github.com/redis/go-redis/v9 v9.18.0
|
||||
github.com/signintech/gopdf v0.36.1
|
||||
@@ -145,7 +145,6 @@ require (
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.17 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.23.0 // indirect
|
||||
github.com/goph/emperror v0.17.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
@@ -160,6 +159,7 @@ require (
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/highwayhash v1.0.4 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.8.2 // indirect
|
||||
@@ -169,7 +169,6 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/phpdave11/gofpdi v1.0.14-0.20211212211723-1f10f9844311 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.1 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -311,8 +311,6 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/infiniflow/infinity/go v0.0.0-20260723093510-ceb4bc518010 h1:TKHL99MrpO2d+q6zRUSPRWsxTXo+pojFr3teiK8HLWw=
|
||||
github.com/infiniflow/infinity/go v0.0.0-20260723093510-ceb4bc518010/go.mod h1:hw3z5AwNFsGy1cdrE0Mfjot2y9jqVHTxBufUx9VzZ+0=
|
||||
github.com/iromli/go-itsdangerous v0.0.0-20220223194502-9c8bef8dac6a h1:Inib12UR9HAfBubrGNraPjKt/Cu8xPbTJbC50+0wP5U=
|
||||
github.com/iromli/go-itsdangerous v0.0.0-20220223194502-9c8bef8dac6a/go.mod h1:8N0Hlye5Lzw+H/yHWpZMkT0QLA+iOHG7KLdvAm95DZg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
|
||||
@@ -17,21 +17,28 @@
|
||||
package utility
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/iromli/go-itsdangerous"
|
||||
)
|
||||
|
||||
const accessTokenExpireSeconds = 86400
|
||||
|
||||
// ExtractAccessToken extract access token from authorization header
|
||||
// This is equivalent to: str(jwt.loads(authorization)) in Python
|
||||
// Uses github.com/iromli/go-itsdangerous for itsdangerous compatibility
|
||||
func ExtractAccessToken(authorization, secretKey string) (string, error) {
|
||||
if authorization == "" {
|
||||
return "", errors.New("empty authorization")
|
||||
@@ -40,37 +47,19 @@ func ExtractAccessToken(authorization, secretKey string) (string, error) {
|
||||
// Strip "Bearer " prefix if present
|
||||
token := strings.TrimPrefix(authorization, "Bearer ")
|
||||
|
||||
// Create URLSafeTimedSerializer with correct configuration
|
||||
// Matching Python itsdangerous configuration:
|
||||
// - salt: "itsdangerous"
|
||||
// - key_derivation: "django-concat"
|
||||
// - digest_method: sha1
|
||||
algo := &itsdangerous.HMACAlgorithm{DigestMethod: sha1.New}
|
||||
signer := itsdangerous.NewTimestampSignature(
|
||||
secretKey,
|
||||
"itsdangerous",
|
||||
".",
|
||||
"django-concat",
|
||||
sha1.New,
|
||||
algo,
|
||||
)
|
||||
|
||||
// Unsign the token (verifies signature and extracts payload)
|
||||
encodedValue, err := signer.Unsign(token, 0)
|
||||
encodedValue, err := unsignAccessToken(token, secretKey, accessTokenExpireSeconds)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decode token: %w", err)
|
||||
}
|
||||
|
||||
// Base64 decode the payload
|
||||
jsonValue, err := urlSafeB64Decode(encodedValue)
|
||||
jsonValue, err := decodePayload(encodedValue)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decode payload: %w", err)
|
||||
}
|
||||
|
||||
// Parse JSON string (remove surrounding quotes)
|
||||
value := string(jsonValue)
|
||||
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
|
||||
value = value[1 : len(value)-1]
|
||||
var value string
|
||||
if err := json.Unmarshal(jsonValue, &value); err != nil {
|
||||
return "", fmt.Errorf("failed to parse payload: %w", err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
@@ -78,55 +67,129 @@ func ExtractAccessToken(authorization, secretKey string) (string, error) {
|
||||
|
||||
// DumpAccessToken creates an authorization token from access token
|
||||
// This is equivalent to: jwt.dumps(access_token) in Python
|
||||
// Uses github.com/iromli/go-itsdangerous for itsdangerous compatibility
|
||||
func DumpAccessToken(accessToken, secretKey string) (string, error) {
|
||||
if accessToken == "" {
|
||||
return "", errors.New("empty access token")
|
||||
}
|
||||
|
||||
// Create URLSafeTimedSerializer with correct configuration
|
||||
// Matching Python itsdangerous configuration:
|
||||
// - salt: "itsdangerous"
|
||||
// - key_derivation: "django-concat"
|
||||
// - digest_method: sha1
|
||||
algo := &itsdangerous.HMACAlgorithm{DigestMethod: sha1.New}
|
||||
signer := itsdangerous.NewTimestampSignature(
|
||||
secretKey,
|
||||
"itsdangerous",
|
||||
".",
|
||||
"django-concat",
|
||||
sha1.New,
|
||||
algo,
|
||||
)
|
||||
|
||||
// Encode the access token as JSON string (add surrounding quotes)
|
||||
jsonValue := fmt.Sprintf("\"%s\"", accessToken)
|
||||
encodedValue := urlSafeB64Encode([]byte(jsonValue))
|
||||
|
||||
// Sign the token (creates signature)
|
||||
token, err := signer.Sign(encodedValue)
|
||||
jsonValue, err := json.Marshal(accessToken)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sign token: %w", err)
|
||||
return "", fmt.Errorf("failed to encode payload: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
return signAccessToken(encodePayload(jsonValue), secretKey), nil
|
||||
}
|
||||
|
||||
// urlSafeB64Decode URL-safe base64 decode
|
||||
func urlSafeB64Decode(s string) ([]byte, error) {
|
||||
// Add padding if needed
|
||||
padding := 4 - len(s)%4
|
||||
if padding != 4 {
|
||||
s += strings.Repeat("=", padding)
|
||||
}
|
||||
return base64.URLEncoding.DecodeString(s)
|
||||
return base64.RawURLEncoding.DecodeString(s)
|
||||
}
|
||||
|
||||
// urlSafeB64Encode URL-safe base64 encode (without padding)
|
||||
func urlSafeB64Encode(data []byte) string {
|
||||
encoded := base64.URLEncoding.EncodeToString(data)
|
||||
// Remove padding
|
||||
return strings.TrimRight(encoded, "=")
|
||||
return base64.RawURLEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
func encodePayload(jsonValue []byte) string {
|
||||
var buf bytes.Buffer
|
||||
zw := zlib.NewWriter(&buf)
|
||||
_, _ = zw.Write(jsonValue)
|
||||
_ = zw.Close()
|
||||
|
||||
compressed := buf.Bytes()
|
||||
if len(compressed) < len(jsonValue)-1 {
|
||||
return "." + urlSafeB64Encode(compressed)
|
||||
}
|
||||
return urlSafeB64Encode(jsonValue)
|
||||
}
|
||||
|
||||
func decodePayload(payload string) ([]byte, error) {
|
||||
decompress := false
|
||||
if strings.HasPrefix(payload, ".") {
|
||||
payload = payload[1:]
|
||||
decompress = true
|
||||
}
|
||||
|
||||
jsonValue, err := urlSafeB64Decode(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !decompress {
|
||||
return jsonValue, nil
|
||||
}
|
||||
|
||||
zr, err := zlib.NewReader(bytes.NewReader(jsonValue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer zr.Close()
|
||||
return io.ReadAll(zr)
|
||||
}
|
||||
|
||||
func signAccessToken(payload, secretKey string) string {
|
||||
value := payload + "." + urlSafeB64Encode(intToBytes(time.Now().Unix()))
|
||||
return value + "." + getAccessTokenSignature(value, secretKey)
|
||||
}
|
||||
|
||||
func unsignAccessToken(token, secretKey string, maxAge int64) (string, error) {
|
||||
token = strings.TrimSpace(token)
|
||||
|
||||
li := strings.LastIndex(token, ".")
|
||||
if li < 0 {
|
||||
return "", errors.New("signature missing")
|
||||
}
|
||||
value := token[:li]
|
||||
sig := token[li+1:]
|
||||
if !hmac.Equal([]byte(sig), []byte(getAccessTokenSignature(value, secretKey))) {
|
||||
return "", errors.New("signature does not match")
|
||||
}
|
||||
|
||||
li = strings.LastIndex(value, ".")
|
||||
if li < 0 {
|
||||
return "", errors.New("timestamp missing")
|
||||
}
|
||||
payload := value[:li]
|
||||
tsValue := value[li+1:]
|
||||
tsBytes, err := urlSafeB64Decode(tsValue)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("malformed timestamp: %w", err)
|
||||
}
|
||||
timestamp := bytesToInt(tsBytes)
|
||||
if maxAge > 0 {
|
||||
age := time.Now().Unix() - timestamp
|
||||
if age > maxAge {
|
||||
return "", fmt.Errorf("signature age %d > %d seconds", age, maxAge)
|
||||
}
|
||||
if age < 0 {
|
||||
return "", fmt.Errorf("signature age %d < 0 seconds", age)
|
||||
}
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func getAccessTokenSignature(value, secretKey string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte("itsdangerous" + "signer" + secretKey))
|
||||
key := h.Sum(nil)
|
||||
|
||||
mac := hmac.New(sha1.New, key)
|
||||
mac.Write([]byte(value))
|
||||
return urlSafeB64Encode(mac.Sum(nil))
|
||||
}
|
||||
|
||||
func intToBytes(num int64) []byte {
|
||||
var buf [8]byte
|
||||
binary.BigEndian.PutUint64(buf[:], uint64(num))
|
||||
return bytes.TrimLeft(buf[:], "\x00")
|
||||
}
|
||||
|
||||
func bytesToInt(data []byte) int64 {
|
||||
if len(data) > 8 {
|
||||
data = data[len(data)-8:]
|
||||
}
|
||||
var buf [8]byte
|
||||
copy(buf[8-len(data):], data)
|
||||
return int64(binary.BigEndian.Uint64(buf[:]))
|
||||
}
|
||||
|
||||
// GenerateSecretKey generates a 32-byte hex string (equivalent to Python's secrets.token_hex(32))
|
||||
|
||||
Reference in New Issue
Block a user