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

// 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
}