133 lines
4.1 KiB
Go
133 lines
4.1 KiB
Go
package fileauth
|
||
|
||
import (
|
||
"crypto/hmac"
|
||
"crypto/sha1"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
const algorithmJavaUpload = "fileauth-java-upload"
|
||
|
||
// MarshalPutPolicyUpload 生产 putPolicy:scope=仅 bucket,无 returnBody(与 FileAuth.java HashMap 字段顺序一致)。
|
||
func MarshalPutPolicyUpload(bucket string, deadlineUnix int64) ([]byte, error) {
|
||
bucket = strings.TrimSpace(bucket)
|
||
if bucket == "" {
|
||
return nil, errors.New("empty bucket")
|
||
}
|
||
if deadlineUnix <= 0 {
|
||
return nil, errors.New("invalid deadline")
|
||
}
|
||
scopeJSON, err := json.Marshal(bucket)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return []byte(`{"scope":` + string(scopeJSON) + `,"deadline":` + strconv.FormatInt(deadlineUnix, 10) + `}`), nil
|
||
}
|
||
|
||
// signPolicyJava 与 FileAuth.java sign(byte[]) 一致:HMAC-SHA1(secret, policyJSON) → URL-safe Base64。
|
||
func signPolicyJava(secret string, policyJSON []byte) (string, error) {
|
||
mac := hmac.New(sha1.New, []byte(secret))
|
||
if _, err := mac.Write(policyJSON); err != nil {
|
||
return "", fmt.Errorf("hmac: %w", err)
|
||
}
|
||
return encodeURLSafe(mac.Sum(nil)), nil
|
||
}
|
||
|
||
// BuildUploadTokenFromPolicy 按 FileAuth.java createUploadToken 生成 token。
|
||
func BuildUploadTokenFromPolicy(accessKey, secret string, putPolicyJSON []byte, algorithm string) (UploadTokenParts, error) {
|
||
accessKey = strings.TrimSpace(accessKey)
|
||
secret = strings.TrimSpace(secret)
|
||
if accessKey == "" || secret == "" {
|
||
return UploadTokenParts{}, errors.New("empty key or secret")
|
||
}
|
||
if len(putPolicyJSON) == 0 {
|
||
return UploadTokenParts{}, errors.New("empty putPolicy")
|
||
}
|
||
if algorithm == "" {
|
||
algorithm = algorithmJavaUpload
|
||
}
|
||
|
||
policyJSON := string(putPolicyJSON)
|
||
sign, err := signPolicyJava(secret, putPolicyJSON)
|
||
if err != nil {
|
||
return UploadTokenParts{}, err
|
||
}
|
||
policyB64 := encodeURLSafe(putPolicyJSON)
|
||
token := accessKey + ":" + sign + ":" + policyB64
|
||
|
||
var policy struct {
|
||
Deadline int64 `json:"deadline"`
|
||
}
|
||
_ = json.Unmarshal(putPolicyJSON, &policy)
|
||
|
||
return UploadTokenParts{
|
||
AccessKey: accessKey,
|
||
PolicyJSON: policyJSON,
|
||
EncodedPutPolicy: policyB64,
|
||
SignInput: policyJSON,
|
||
EncodedSign: sign,
|
||
UploadToken: token,
|
||
DeadlineUnix: policy.Deadline,
|
||
Algorithm: algorithm,
|
||
}, nil
|
||
}
|
||
|
||
// BuildUploadTokenPartsForUpload 生产路径:scope=仅 bucket。
|
||
func BuildUploadTokenPartsForUpload(accessKey, secret, bucket string, deadlineUnix int64) (UploadTokenParts, error) {
|
||
accessKey = strings.TrimSpace(accessKey)
|
||
secret = strings.TrimSpace(secret)
|
||
bucket = strings.TrimSpace(bucket)
|
||
if accessKey == "" || secret == "" {
|
||
return UploadTokenParts{}, errors.New("empty key or secret")
|
||
}
|
||
if bucket == "" {
|
||
return UploadTokenParts{}, errors.New("empty bucket")
|
||
}
|
||
if deadlineUnix <= 0 {
|
||
return UploadTokenParts{}, errors.New("invalid deadline")
|
||
}
|
||
jsonPolicy, err := MarshalPutPolicyUpload(bucket, deadlineUnix)
|
||
if err != nil {
|
||
return UploadTokenParts{}, err
|
||
}
|
||
parts, err := BuildUploadTokenFromPolicy(accessKey, secret, jsonPolicy, algorithmJavaUpload)
|
||
if err != nil {
|
||
return UploadTokenParts{}, err
|
||
}
|
||
now := time.Now().Unix()
|
||
expires := deadlineUnix - now
|
||
if expires < 0 {
|
||
expires = 0
|
||
}
|
||
parts.ExpiresSec = expires
|
||
return parts, nil
|
||
}
|
||
|
||
// BuildUploadTokenPartsAtFile filename 不参与 scope(token policy scope=仅 bucket)。
|
||
func BuildUploadTokenPartsAtFile(accessKey, secret, bucket, filename string, deadlineUnix int64) (UploadTokenParts, error) {
|
||
_ = filename
|
||
return BuildUploadTokenPartsForUpload(accessKey, secret, bucket, deadlineUnix)
|
||
}
|
||
|
||
// CreateUploadTokenAtFile 生产上传凭证。
|
||
func CreateUploadTokenAtFile(accessKey, secret, bucket, filename string, deadlineUnix int64) (string, error) {
|
||
parts, err := BuildUploadTokenPartsAtFile(accessKey, secret, bucket, filename, deadlineUnix)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return parts.UploadToken, nil
|
||
}
|
||
|
||
// HMACSignHex 返回 HMAC-SHA1(policyJSON) 原始字节的十六进制(调试用)。
|
||
func HMACSignHex(secret, policyJSON string) string {
|
||
mac := hmac.New(sha1.New, []byte(secret))
|
||
_, _ = mac.Write([]byte(policyJSON))
|
||
return hex.EncodeToString(mac.Sum(nil))
|
||
}
|