package session import ( "context" "encoding/binary" "encoding/hex" "errors" "math/rand" "strconv" "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 int64 `bson:"ct" json:"ct"` } func (auth *Authorization) ToStrings() []string { return []string{ "a", auth.Account.Hex(), "p", auth.Platform, "u", auth.Uid, "al", auth.Alias, "inv", auth.invalidated, "ct", strconv.FormatInt(auth.CreatedTime, 10), } } 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"]) ct, _ := strconv.ParseInt(src["ct"], 10, 0) return Authorization{ Account: accid, Platform: src["p"], Uid: src["u"], Alias: src["al"], invalidated: src["inv"], CreatedTime: ct, } } 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) }