168 lines
4.0 KiB
Go
168 lines
4.0 KiB
Go
package session
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"math/rand"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
type Authorization struct {
|
|
Account primitive.ObjectID `bson:"a" json:"a"`
|
|
invalidated string
|
|
|
|
// by authorization provider
|
|
Platform string `bson:"p" json:"p"`
|
|
Uid string `bson:"u" json:"u"`
|
|
Alias string `bson:"al" json:"al"`
|
|
CreatedTime primitive.DateTime `bson:"ct" json:"ct"`
|
|
}
|
|
|
|
func (auth *Authorization) ToStrings() []string {
|
|
ct, _ := auth.CreatedTime.MarshalJSON()
|
|
return []string{
|
|
"a", auth.Account.Hex(),
|
|
"p", auth.Platform,
|
|
"u", auth.Uid,
|
|
"al", auth.Alias,
|
|
"inv", auth.invalidated,
|
|
"ct", string(ct),
|
|
}
|
|
}
|
|
|
|
func (auth *Authorization) Valid() bool {
|
|
return len(auth.invalidated) == 0 && !auth.Account.IsZero()
|
|
}
|
|
|
|
func MakeAuthrizationFromStringMap(src map[string]string) Authorization {
|
|
accid, _ := primitive.ObjectIDFromHex(src["a"])
|
|
var datetime primitive.DateTime
|
|
datetime.UnmarshalJSON([]byte(src["ct"]))
|
|
return Authorization{
|
|
Account: accid,
|
|
Platform: src["p"],
|
|
Uid: src["u"],
|
|
Alias: src["al"],
|
|
invalidated: src["inv"],
|
|
CreatedTime: datetime,
|
|
}
|
|
}
|
|
|
|
type Provider interface {
|
|
New(*Authorization) (string, error)
|
|
RevokeAll(primitive.ObjectID) error
|
|
Query(string) (Authorization, error)
|
|
Touch(string) (bool, error)
|
|
}
|
|
|
|
type Consumer interface {
|
|
Query(string) Authorization
|
|
Touch(string) (Authorization, error)
|
|
IsRevoked(primitive.ObjectID) bool
|
|
Revoke(string)
|
|
RegisterOnSessionInvalidated(func(primitive.ObjectID))
|
|
}
|
|
|
|
type storagekey string
|
|
type publickey string
|
|
|
|
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
func make_storagekey(acc primitive.ObjectID) storagekey {
|
|
bs := [4]byte{}
|
|
binary.LittleEndian.PutUint32(bs[:], r.Uint32())
|
|
|
|
return storagekey(acc.Hex() + hex.EncodeToString(bs[2:]))
|
|
}
|
|
|
|
func storagekey_to_publickey(sk storagekey) publickey {
|
|
bs, _ := hex.DecodeString(string(sk))
|
|
|
|
acc := bs[:12]
|
|
cs := bs[12:]
|
|
|
|
encoded := [14]byte{}
|
|
for i, v := range acc[:] {
|
|
encoded[i] = (v ^ cs[0]) ^ cs[1]
|
|
}
|
|
encoded[12] = cs[0]
|
|
encoded[13] = cs[1]
|
|
|
|
return publickey(hex.EncodeToString(encoded[:]))
|
|
}
|
|
|
|
func publickey_to_storagekey(pk publickey) storagekey {
|
|
bs, _ := hex.DecodeString(string(pk))
|
|
|
|
acc := bs[:12]
|
|
cs := bs[12:]
|
|
|
|
decoded := [14]byte{}
|
|
for i, v := range acc[:] {
|
|
decoded[i] = (v ^ cs[1]) ^ cs[0]
|
|
}
|
|
decoded[12] = cs[0]
|
|
decoded[13] = cs[1]
|
|
|
|
return storagekey(hex.EncodeToString(decoded[:]))
|
|
}
|
|
|
|
type SessionConfig struct {
|
|
SessionTTL int64 `json:"session_ttl"`
|
|
SessionStorage string `json:"session_storage"`
|
|
}
|
|
|
|
var errInvalidScheme = errors.New("storageAddr is not valid scheme")
|
|
var errSessionStorageMissing = errors.New("session_storageis missing")
|
|
|
|
func NewConsumer(ctx context.Context, storageAddr string, ttl time.Duration) (Consumer, error) {
|
|
if strings.HasPrefix(storageAddr, "mongodb") {
|
|
return newConsumerWithMongo(ctx, storageAddr, ttl)
|
|
}
|
|
|
|
if strings.HasPrefix(storageAddr, "redis") {
|
|
return newConsumerWithRedis(ctx, storageAddr, ttl)
|
|
}
|
|
|
|
return nil, errInvalidScheme
|
|
}
|
|
|
|
func NewConsumerWithConfig(ctx context.Context, cfg SessionConfig) (Consumer, error) {
|
|
if len(cfg.SessionStorage) == 0 {
|
|
return nil, errSessionStorageMissing
|
|
}
|
|
|
|
if cfg.SessionTTL == 0 {
|
|
cfg.SessionTTL = 3600
|
|
}
|
|
return NewConsumer(ctx, cfg.SessionStorage, time.Duration(cfg.SessionTTL)*time.Second)
|
|
}
|
|
|
|
func NewProvider(ctx context.Context, storageAddr string, ttl time.Duration) (Provider, error) {
|
|
if strings.HasPrefix(storageAddr, "mongodb") {
|
|
return newProviderWithMongo(ctx, storageAddr, ttl)
|
|
}
|
|
|
|
if strings.HasPrefix(storageAddr, "redis") {
|
|
return newProviderWithRedis(ctx, storageAddr, ttl)
|
|
}
|
|
|
|
return nil, errInvalidScheme
|
|
}
|
|
|
|
func NewProviderWithConfig(ctx context.Context, cfg SessionConfig) (Provider, error) {
|
|
if len(cfg.SessionStorage) == 0 {
|
|
return nil, errSessionStorageMissing
|
|
}
|
|
|
|
if cfg.SessionTTL == 0 {
|
|
cfg.SessionTTL = 3600
|
|
}
|
|
return NewProvider(ctx, cfg.SessionStorage, time.Duration(cfg.SessionTTL)*time.Second)
|
|
}
|