Skip to content

Commit

Permalink
Move Bolt DB access to a separate, centralized package.
Browse files Browse the repository at this point in the history
  • Loading branch information
nathan-osman committed May 3, 2022
1 parent b93b2df commit 91667b4
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 92 deletions.
6 changes: 6 additions & 0 deletions db/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package db

// Config provides the configuration for the Bolt database.
type Config struct {
StorageDir string
}
32 changes: 32 additions & 0 deletions db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package db

import (
"path"

bolt "go.etcd.io/bbolt"
)

const databaseName = "i5.db"

// DB provides a centralized entrypoint to the Bolt database.
type DB struct {
db *bolt.DB
}

// New creates and initializes the database.
func New(cfg *Config) (*DB, error) {
d, err := bolt.Open(
path.Join(cfg.StorageDir, databaseName),
0600,
nil,
)
if err != nil {
return nil, err
}
return &DB{d}, nil
}

// Close frees all database resources.
func (d *DB) Close() {
d.db.Close()
}
49 changes: 49 additions & 0 deletions db/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package db

import (
"errors"

bolt "go.etcd.io/bbolt"
"golang.org/x/crypto/bcrypt"
)

var (
usersBucket = []byte("Users")

errInvalidUsernamePassword = errors.New("invalid username or password")
)

// CreateUser creates a new user in the database.
func (d *DB) CreateUser(username, password string) error {
h, err := bcrypt.GenerateFromPassword([]byte(password), 0)
if err != nil {
return err
}
return d.db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket(usersBucket)
if err != nil {
return err
}
return b.Put([]byte(username), h)
})
}

// LoginUser verifies the specified login credentials.
func (d *DB) LoginUser(username, password string) error {
var hashedPassword []byte
d.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(usersBucket)
if b == nil {
return nil
}
hashedPassword = b.Get([]byte(username))
return nil
})
if hashedPassword == nil {
return errInvalidUsernamePassword
}
if err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(password)); err != nil {
return errInvalidUsernamePassword
}
return nil
}
56 changes: 34 additions & 22 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/howeyc/gopass"
"github.com/nathan-osman/i5/conman"
"github.com/nathan-osman/i5/db"
"github.com/nathan-osman/i5/dbman"
"github.com/nathan-osman/i5/dockmon"
"github.com/nathan-osman/i5/logger"
Expand Down Expand Up @@ -142,6 +143,15 @@ func main() {
Usage: "create a new user account",
Action: func(c *cli.Context) error {

// Attempt to open the Bolt database
d, err := db.New(&db.Config{
StorageDir: c.String("storage-dir"),
})
if err != nil {
return err
}
defer d.Close()

// Prompt for the username
var username string
fmt.Print("Username? ")
Expand All @@ -155,11 +165,7 @@ func main() {
}

// Create the user
if err := status.CreateUser(
c.String("storage-dir"),
username,
string(p),
); err != nil {
if err := d.CreateUser(username, string(p)); err != nil {
return nil
}

Expand All @@ -171,6 +177,15 @@ func main() {
},
Action: func(c *cli.Context) error {

// Attempt to open the Bolt database
d, err := db.New(&db.Config{
StorageDir: c.String("storage-dir"),
})
if err != nil {
return err
}
defer d.Close()

// Check if the status website was enabled
var statusDomain = c.String("status-domain")

Expand All @@ -197,7 +212,7 @@ func main() {
defer dm.Close()

// Create the database manager
d := dbman.NewManager()
dbm := dbman.NewManager()

// Connect to MySQL if requested
if c.Bool("mysql") {
Expand All @@ -210,7 +225,7 @@ func main() {
if err != nil {
return err
}
d.Register(msql)
dbm.Register(msql)
}

// Connect to PostgreSQL if requested
Expand All @@ -224,31 +239,28 @@ func main() {
if err != nil {
return err
}
d.Register(psql)
dbm.Register(psql)
}

// Create the container manager
cm := conman.New(&conman.Config{
EventChan: dm.EventChan,
Dbman: d,
Dbman: dbm,
})
defer cm.Close()

// If a domain name for the internal server was specified, use it
// If a domain name for the status server was specified, use it
if statusDomain != "" {
s, err := status.New(&status.Config{
Key: c.String("status-key"),
Debug: c.Bool("debug"),
Domain: statusDomain,
Insecure: c.Bool("status-insecure"),
StorageDir: c.String("storage-dir"),
Conman: cm,
Dbman: d,
Logger: l,
s := status.New(&status.Config{
Key: c.String("status-key"),
Debug: c.Bool("debug"),
Domain: statusDomain,
Insecure: c.Bool("status-insecure"),
DB: d,
Conman: cm,
Dbman: dbm,
Logger: l,
})
if err != nil {
return err
}
defer s.Close()
cm.Add(s.Container)
}
Expand Down
22 changes: 3 additions & 19 deletions status/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import (

"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
bolt "go.etcd.io/bbolt"
"golang.org/x/crypto/bcrypt"
)

const (
sessionUsername = "username"

errInvalidUsernamePassword = "invalid username or password"
errUnauthorized = "you do not have permission to access that page"
errUnauthorized = "you do not have permission to access that page"
)

type authLoginParams struct {
Expand All @@ -27,21 +24,8 @@ func (s *Status) authLogin(c *gin.Context) {
failure(c, http.StatusBadRequest, err.Error())
return
}
var password []byte
s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(usersBucket)
if b == nil {
return nil
}
password = b.Get([]byte(params.Username))
return nil
})
if password == nil {
failure(c, http.StatusUnauthorized, errInvalidUsernamePassword)
return
}
if err := bcrypt.CompareHashAndPassword(password, []byte(params.Password)); err != nil {
failure(c, http.StatusUnauthorized, errInvalidUsernamePassword)
if err := s.db.CreateUser(params.Username, params.Password); err != nil {
failure(c, http.StatusUnauthorized, err.Error())
return
}
session := sessions.Default(c)
Expand Down
5 changes: 3 additions & 2 deletions status/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package status

import (
"github.com/nathan-osman/i5/conman"
"github.com/nathan-osman/i5/db"
"github.com/nathan-osman/i5/dbman"
"github.com/nathan-osman/i5/logger"
)
Expand All @@ -16,8 +17,8 @@ type Config struct {
Domain string
// Insecure allows insecure connections to the server.
Insecure bool
// StorageDir indicates where the bbolt database should be stored
StorageDir string
// DB is a pointer to a DB instance.
DB *db.DB
// Conman is a pointer to a Conman instance.
Conman *conman.Conman
// Dbman is a pointer to a db.Manager instance.
Expand Down
40 changes: 0 additions & 40 deletions status/db.go

This file was deleted.

14 changes: 5 additions & 9 deletions status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/nathan-osman/i5/conman"
"github.com/nathan-osman/i5/db"
"github.com/nathan-osman/i5/dbman"
"github.com/nathan-osman/i5/dockmon"
"github.com/nathan-osman/i5/logger"
"github.com/nathan-osman/i5/ui"
bolt "go.etcd.io/bbolt"
)

const sessionName = "status"
Expand All @@ -24,23 +24,19 @@ type Status struct {
conman *conman.Conman
dbman *dbman.Manager
logger *logger.Logger
db *bolt.DB
db *db.DB
startup int64
}

// New creates a new status container.
func New(cfg *Config) (*Status, error) {
d, err := openDB(cfg.StorageDir)
if err != nil {
return nil, err
}
func New(cfg *Config) *Status {
var (
r = gin.Default()
s = &Status{
conman: cfg.Conman,
dbman: cfg.Dbman,
logger: cfg.Logger,
db: d,
db: cfg.DB,
startup: time.Now().Unix(),
}
store = cookie.NewStore([]byte(cfg.Key))
Expand Down Expand Up @@ -76,7 +72,7 @@ func New(cfg *Config) (*Status, error) {
Handler: r,
Running: true,
}
return s, nil
return s
}

// Close shuts down the status server.
Expand Down

0 comments on commit 91667b4

Please sign in to comment.