You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
84 lines
1.9 KiB
84 lines
1.9 KiB
3 years ago
|
// Package session implements session storage.
|
||
|
package session
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
|
||
|
"github.com/go-faster/errors"
|
||
|
|
||
|
"github.com/gotd/td/tg"
|
||
|
)
|
||
|
|
||
|
// Data of session.
|
||
|
type Data struct {
|
||
|
Config tg.Config
|
||
|
DC int
|
||
|
Addr string
|
||
|
AuthKey []byte
|
||
|
AuthKeyID []byte
|
||
|
Salt int64
|
||
|
}
|
||
|
|
||
|
// Storage is secure persistent storage for client session.
|
||
|
//
|
||
|
// NB: Implementation security is important, attacker can abuse it not only for
|
||
|
// connecting as authenticated user or bot, but even decrypting previous
|
||
|
// messages in some situations.
|
||
|
type Storage interface {
|
||
|
LoadSession(ctx context.Context) ([]byte, error)
|
||
|
StoreSession(ctx context.Context, data []byte) error
|
||
|
}
|
||
|
|
||
|
// ErrNotFound means that session is not found in storage.
|
||
|
var ErrNotFound = errors.New("session storage: not found")
|
||
|
|
||
|
// Loader wraps Storage implementing Data (un-)marshaling.
|
||
|
type Loader struct {
|
||
|
Storage Storage
|
||
|
}
|
||
|
|
||
|
type jsonData struct {
|
||
|
Version int
|
||
|
Data Data
|
||
|
}
|
||
|
|
||
|
const latestVersion = 1
|
||
|
|
||
|
// Load loads Data from Storage.
|
||
|
func (l *Loader) Load(ctx context.Context) (*Data, error) {
|
||
|
buf, err := l.Storage.LoadSession(ctx)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "load")
|
||
|
}
|
||
|
if len(buf) == 0 {
|
||
|
return nil, ErrNotFound
|
||
|
}
|
||
|
|
||
|
var v jsonData
|
||
|
if err := json.Unmarshal(buf, &v); err != nil {
|
||
|
return nil, errors.Wrap(err, "unmarshal")
|
||
|
}
|
||
|
if v.Version != latestVersion {
|
||
|
// HACK(ernado): backward compatibility super shenanigan.
|
||
|
return nil, errors.Wrapf(ErrNotFound, "version mismatch (%d != %d)", v.Version, latestVersion)
|
||
|
}
|
||
|
return &v.Data, err
|
||
|
}
|
||
|
|
||
|
// Save saves Data to Storage.
|
||
|
func (l *Loader) Save(ctx context.Context, data *Data) error {
|
||
|
v := jsonData{
|
||
|
Version: latestVersion,
|
||
|
Data: *data,
|
||
|
}
|
||
|
buf, err := json.Marshal(v)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "marshal")
|
||
|
}
|
||
|
if err := l.Storage.StoreSession(ctx, buf); err != nil {
|
||
|
return errors.Wrap(err, "store")
|
||
|
}
|
||
|
return nil
|
||
|
}
|