mirror of
https://github.com/veops/oneterm.git
synced 2025-09-27 03:36:02 +08:00
182 lines
4.1 KiB
Go
182 lines
4.1 KiB
Go
package acl
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/zlib"
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/veops/oneterm/conf"
|
|
"github.com/veops/oneterm/remote"
|
|
)
|
|
|
|
// SigningAlgorithm provides interfaces to generate and verify signature
|
|
type SigningAlgorithm interface {
|
|
GetSignature(key, value string) []byte
|
|
VerifySignature(key, value string, sig []byte) bool
|
|
}
|
|
|
|
// HMACAlgorithm provides signature generation using HMACs.
|
|
type HMACAlgorithm struct {
|
|
DigestMethod func() hash.Hash
|
|
}
|
|
|
|
// GetSignature returns the signature for the given key and value.
|
|
func (a *HMACAlgorithm) GetSignature(key, value string) []byte {
|
|
//a.DigestMethod().Reset()
|
|
h := hmac.New(a.DigestMethod, []byte(key))
|
|
h.Write([]byte(value))
|
|
return h.Sum(nil)
|
|
}
|
|
|
|
// VerifySignature verifies the given signature matches the expected signature.
|
|
func (a *HMACAlgorithm) VerifySignature(key, value string, sig []byte) bool {
|
|
eq := subtle.ConstantTimeCompare(sig, []byte(a.GetSignature(key, value)))
|
|
return eq == 1
|
|
}
|
|
|
|
type Signature struct {
|
|
SecretKey string
|
|
Sep string
|
|
Salt string
|
|
KeyDerivation string
|
|
DigestMethod func() hash.Hash
|
|
Algorithm SigningAlgorithm
|
|
}
|
|
|
|
// Unsign the given string.
|
|
func (s *Signature) Unsign(signed string) (content []byte, err error) {
|
|
if !strings.Contains(signed, s.Sep) {
|
|
err = fmt.Errorf("no %s found in value", s.Sep)
|
|
return
|
|
}
|
|
|
|
li := strings.LastIndex(signed, s.Sep)
|
|
value, sig := signed[:li], signed[li+len(s.Sep):]
|
|
|
|
if ok, _ := s.Verify(value, sig); ok {
|
|
//c, err := base64Decode(strings.Split(strings.Trim(value, "."), ".")[0])
|
|
var c []byte
|
|
c, err = base64.RawURLEncoding.DecodeString(strings.Split(strings.Trim(value, "."), ".")[0])
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var r io.ReadCloser
|
|
r, err = zlib.NewReader(bytes.NewReader(c))
|
|
if err != nil {
|
|
return
|
|
}
|
|
return io.ReadAll(r)
|
|
}
|
|
err = fmt.Errorf("signature %s does not match", sig)
|
|
return
|
|
}
|
|
|
|
func (s *Signature) Verify(value, sig string) (bool, error) {
|
|
key, err := s.DeriveKey()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
signed, err := base64.RawURLEncoding.DecodeString(sig)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return s.Algorithm.VerifySignature(key, value, signed), nil
|
|
}
|
|
|
|
func (s *Signature) DeriveKey() (string, error) {
|
|
var key string
|
|
var err error
|
|
|
|
switch s.KeyDerivation {
|
|
case "hmac":
|
|
h := hmac.New(sha1.New, []byte(s.SecretKey))
|
|
h.Write([]byte(s.Salt))
|
|
|
|
key = string(h.Sum(nil))
|
|
case "none":
|
|
key = s.SecretKey
|
|
default:
|
|
key, err = "", errors.New("unknown key derivation method")
|
|
}
|
|
|
|
return key, err
|
|
}
|
|
|
|
func NewSignature(secret, salt, sep, derivation string, digest func() hash.Hash, algo SigningAlgorithm) *Signature {
|
|
if salt == "" {
|
|
salt = "itsdangerous.Signer"
|
|
}
|
|
if sep == "" {
|
|
sep = "."
|
|
}
|
|
if derivation == "" {
|
|
derivation = "hmac"
|
|
}
|
|
if digest == nil {
|
|
digest = sha1.New
|
|
}
|
|
if algo == nil {
|
|
algo = &HMACAlgorithm{DigestMethod: digest}
|
|
}
|
|
|
|
return &Signature{
|
|
SecretKey: secret,
|
|
Salt: salt,
|
|
Sep: sep,
|
|
KeyDerivation: derivation,
|
|
DigestMethod: digest,
|
|
Algorithm: algo,
|
|
}
|
|
}
|
|
|
|
func AuthWithKey(path string, originData map[string]any) (sess *Session, err error) {
|
|
body := map[string]any{
|
|
"path": path,
|
|
"key": originData["_key"],
|
|
"secret": originData["_secret"],
|
|
"need_parentRoles": true,
|
|
"app_id": "oneterm",
|
|
}
|
|
payload := make(map[string]any)
|
|
for k, v := range originData {
|
|
rv := reflect.ValueOf(v)
|
|
if rv.Kind() == reflect.Map || rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array {
|
|
continue
|
|
}
|
|
payload[k] = v
|
|
}
|
|
body["payload"] = payload
|
|
url := fmt.Sprintf("%s%s", conf.Cfg.Auth.Acl.Url, "/acl/auth_with_key")
|
|
data := &AuthWithKeyResp{}
|
|
resp, err := remote.RC.R().
|
|
SetBody(body).
|
|
SetResult(data).
|
|
Post(url)
|
|
if err = remote.HandleErr(err, resp, nil); err == nil {
|
|
sess = &Session{
|
|
Uid: data.User.UID,
|
|
Acl: Acl{
|
|
Uid: data.User.UID,
|
|
UserName: data.User.Username,
|
|
Rid: data.User.Rid,
|
|
NickName: data.User.Name,
|
|
ParentRoles: data.User.ParentRoles,
|
|
},
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|