210 lines
4.6 KiB
Go
210 lines
4.6 KiB
Go
|
package database
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"time"
|
||
|
|
||
|
"github.com/Xe/uuid"
|
||
|
"github.com/asdine/storm"
|
||
|
"github.com/brandur/simplebox"
|
||
|
"golang.org/x/net/context"
|
||
|
)
|
||
|
|
||
|
// Database errors
|
||
|
var (
|
||
|
ErrNotImplemented = errors.New("database: not implemented")
|
||
|
ErrInvalidKind = errors.New("database: invalid route kind")
|
||
|
ErrNoSuchToken = errors.New("database: no such token")
|
||
|
ErrCantDecryptCert = errors.New("database: can't decrypt cert")
|
||
|
ErrUnknownCryptMethod = errors.New("database: unknown encryption method")
|
||
|
)
|
||
|
|
||
|
// BoltDBStorage is a backend that uses https://github.com/boltdb/bolt to store
|
||
|
// route data.
|
||
|
type BoltDBStorage struct {
|
||
|
db *storm.DB
|
||
|
sb *simplebox.SimpleBox
|
||
|
}
|
||
|
|
||
|
// NewBoltStorage creates a new Storage instance backed by BoltDB + Storm.
|
||
|
func NewBoltStorage(path string, key *[32]byte) (Storage, error) {
|
||
|
db, err := storm.Open(path)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
b := &BoltDBStorage{
|
||
|
db: db,
|
||
|
sb: simplebox.NewFromSecretKey(key),
|
||
|
}
|
||
|
|
||
|
return Storage(b), nil
|
||
|
}
|
||
|
|
||
|
// interface compliance
|
||
|
var (
|
||
|
_ Storage = &BoltDBStorage{}
|
||
|
)
|
||
|
|
||
|
// GetRoute gets a single route out of the database.
|
||
|
func (b *BoltDBStorage) GetRoute(ctx context.Context, host string) (Route, error) {
|
||
|
r := Route{}
|
||
|
err := b.db.One("Hostname", host, &r)
|
||
|
return r, err
|
||
|
}
|
||
|
|
||
|
// GetAllRoutes gets all routes out of the database.
|
||
|
func (b *BoltDBStorage) GetAllRoutes(ctx context.Context) ([]Route, error) {
|
||
|
rs := []Route{}
|
||
|
err := b.db.All(&rs)
|
||
|
return rs, err
|
||
|
}
|
||
|
|
||
|
// PutRoute creates a new route in the database.
|
||
|
func (b *BoltDBStorage) PutRoute(ctx context.Context, domain, kind string) (Route, error) {
|
||
|
switch kind {
|
||
|
case "tcp", "http":
|
||
|
// empty case, do nothing
|
||
|
default:
|
||
|
return Route{}, ErrInvalidKind
|
||
|
}
|
||
|
|
||
|
r := Route{
|
||
|
ID: uuid.New(),
|
||
|
Creator: ctx.Value("creator").(string),
|
||
|
Hostname: domain,
|
||
|
}
|
||
|
|
||
|
err := b.db.Save(&r)
|
||
|
if err != nil {
|
||
|
return Route{}, err
|
||
|
}
|
||
|
|
||
|
return r, nil
|
||
|
}
|
||
|
|
||
|
// DeleteRoute removes a route from the database.
|
||
|
func (b *BoltDBStorage) DeleteRoute(ctx context.Context, id string) error {
|
||
|
r := Route{}
|
||
|
err := b.db.One("ID", id, &r)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return b.db.DeleteStruct(&r)
|
||
|
}
|
||
|
|
||
|
// GetToken fetches a token from the database. This is mainly used in validation
|
||
|
// of tokens.
|
||
|
func (b *BoltDBStorage) GetToken(ctx context.Context, token string) (Token, error) {
|
||
|
t := Token{}
|
||
|
err := b.db.One("Body", token, &t)
|
||
|
return t, err
|
||
|
}
|
||
|
|
||
|
// GetTokensForOwner fetches all of the tokens owned by a given owner.
|
||
|
func (b *BoltDBStorage) GetTokensForOwner(ctx context.Context, owner string) ([]Token, error) {
|
||
|
ts := []Token{}
|
||
|
err := b.db.Find("Owner", owner, &ts)
|
||
|
return ts, err
|
||
|
}
|
||
|
|
||
|
// PutToken adds a new token to the database.
|
||
|
func (b *BoltDBStorage) PutToken(ctx context.Context, token, owner string, scopes []string) (Token, error) {
|
||
|
t := Token{
|
||
|
ID: uuid.New(),
|
||
|
Body: token,
|
||
|
Owner: owner,
|
||
|
Scopes: scopes,
|
||
|
|
||
|
CreatedAt: time.Now(),
|
||
|
Active: true,
|
||
|
}
|
||
|
|
||
|
err := b.db.Save(&t)
|
||
|
if err != nil {
|
||
|
return Token{}, err
|
||
|
}
|
||
|
|
||
|
return t, nil
|
||
|
}
|
||
|
|
||
|
// DeleteToken removes a token from the database.
|
||
|
func (b *BoltDBStorage) DeleteToken(ctx context.Context, id string) error {
|
||
|
t := Token{}
|
||
|
err := b.db.One("ID", id, &t)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return b.db.DeleteStruct(&t)
|
||
|
}
|
||
|
|
||
|
// DeactivateToken de-activates a token in the database. This should be used
|
||
|
// instead of deletion in many cases.
|
||
|
func (b *BoltDBStorage) DeactivateToken(ctx context.Context, id string) error {
|
||
|
t := Token{}
|
||
|
err := b.db.One("ID", id, &t)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
t.Active = false
|
||
|
|
||
|
return b.db.Save(&t)
|
||
|
}
|
||
|
|
||
|
// GetCert fetches a TLS certificate from the database.
|
||
|
func (b *BoltDBStorage) GetCert(ctx context.Context, key string) ([]byte, error) {
|
||
|
cc := CachedCert{}
|
||
|
err := b.db.One("Key", key, &cc)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var body []byte
|
||
|
|
||
|
switch cc.CryptoLevel {
|
||
|
case CryptoLevelNone:
|
||
|
body = cc.Body
|
||
|
case CryptoLevelSecretbox:
|
||
|
if b.sb == nil {
|
||
|
return nil, ErrCantDecryptCert
|
||
|
}
|
||
|
|
||
|
body, err = b.sb.Decrypt(cc.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return body, nil
|
||
|
}
|
||
|
|
||
|
// PutCert adds a new TLS certificate to the database.
|
||
|
func (b *BoltDBStorage) PutCert(ctx context.Context, key string, data []byte) error {
|
||
|
cc := CachedCert{
|
||
|
Key: key,
|
||
|
CryptoLevel: CryptoLevelNone,
|
||
|
Body: data,
|
||
|
}
|
||
|
|
||
|
if b.sb != nil {
|
||
|
cc.CryptoLevel = CryptoLevelSecretbox
|
||
|
cc.Body = b.sb.Encrypt(data)
|
||
|
}
|
||
|
|
||
|
return b.db.Save(&cc)
|
||
|
}
|
||
|
|
||
|
// DeleteCert removes a certificate from the database.
|
||
|
func (b *BoltDBStorage) DeleteCert(ct context.Context, key string) error {
|
||
|
cc := CachedCert{}
|
||
|
err := b.db.One("Key", key, &cc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return b.db.DeleteStruct(&cc)
|
||
|
}
|