130 lines
2.9 KiB
Go
130 lines
2.9 KiB
Go
package bolt
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
bolt "go.etcd.io/bbolt"
|
|
"golang.org/x/xerrors"
|
|
|
|
"source.toby3d.me/website/indieauth/internal/domain"
|
|
"source.toby3d.me/website/indieauth/internal/token"
|
|
)
|
|
|
|
type (
|
|
Token struct {
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
DeletedAt time.Time `json:"deletedAt,omitempty"`
|
|
Scopes []string `json:"scopes"`
|
|
AccessToken string `json:"accessToken"`
|
|
ClientID string `json:"clientId"`
|
|
Me string `json:"me"`
|
|
}
|
|
|
|
boltTokenRepository struct {
|
|
db *bolt.DB
|
|
}
|
|
)
|
|
|
|
func NewBoltTokenRepository(db *bolt.DB) token.Repository {
|
|
return &boltTokenRepository{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (repo *boltTokenRepository) Create(ctx context.Context, accessToken *domain.Token) (err error) {
|
|
t, err := repo.Get(ctx, accessToken.AccessToken)
|
|
if err != nil && !xerrors.Is(err, token.ErrNotExist) {
|
|
return errors.Wrap(err, "cannot get token in database")
|
|
}
|
|
|
|
if t != nil {
|
|
return token.ErrExist
|
|
}
|
|
|
|
if err = repo.db.Update(func(tx *bolt.Tx) error {
|
|
//nolint: exhaustivestruct
|
|
bkt, err := tx.CreateBucketIfNotExists(Token{}.Bucket())
|
|
if err != nil {
|
|
return errors.Wrap(err, "cannot create bucket")
|
|
}
|
|
|
|
token := new(Token)
|
|
token.Populate(accessToken)
|
|
|
|
src, err := json.Marshal(token)
|
|
if err != nil {
|
|
return errors.Wrap(err, "cannot marshal token data")
|
|
}
|
|
|
|
if err = bkt.Put([]byte(token.AccessToken), src); err != nil {
|
|
return errors.Wrap(err, "cannot put token into bucket")
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return errors.Wrap(err, "failed to put token into database")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (repo *boltTokenRepository) Get(ctx context.Context, accessToken string) (*domain.Token, error) {
|
|
result := new(domain.Token)
|
|
|
|
if err := repo.db.View(func(tx *bolt.Tx) (err error) {
|
|
t := new(Token)
|
|
|
|
bkt := tx.Bucket(t.Bucket())
|
|
if bkt == nil {
|
|
return token.ErrNotExist
|
|
}
|
|
|
|
src := bkt.Get([]byte(accessToken))
|
|
if src == nil {
|
|
return token.ErrNotExist
|
|
}
|
|
|
|
if err = t.Bind(src, result); err != nil {
|
|
return errors.Wrap(err, "cannot parse token")
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return nil, errors.Wrap(err, "failed to view token in database")
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (Token) Bucket() []byte { return []byte("tokens") }
|
|
|
|
func (t *Token) Populate(accessToken *domain.Token) {
|
|
t.AccessToken = accessToken.AccessToken
|
|
t.ClientID = accessToken.ClientID
|
|
t.CreatedAt = time.Now().UTC().Round(time.Second)
|
|
t.Me = accessToken.Me
|
|
t.Scopes = make([]string, len(accessToken.Scopes))
|
|
t.UpdatedAt = t.CreatedAt
|
|
|
|
for i := range accessToken.Scopes {
|
|
t.Scopes[i] = accessToken.Scopes[i]
|
|
}
|
|
}
|
|
|
|
func (t *Token) Bind(src []byte, accessToken *domain.Token) error {
|
|
if err := json.Unmarshal(src, t); err != nil {
|
|
return errors.Wrap(err, "cannot unmarshal token")
|
|
}
|
|
|
|
accessToken.AccessToken = t.AccessToken
|
|
accessToken.ClientID = t.ClientID
|
|
accessToken.Me = t.Me
|
|
accessToken.Scopes = t.Scopes
|
|
|
|
return nil
|
|
}
|