Skip to content

Commit

Permalink
Improved store. Added data folder. Jwt signing key is now randomly cr…
Browse files Browse the repository at this point in the history
…eated at runtime. Decided to use hclogger as default logger
  • Loading branch information
michelvocks committed Mar 6, 2018
1 parent 1ed0c54 commit a1b0eb4
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ yarn-error.log
selenium-debug.log
test/unit/coverage
test/e2e/reports

# data folder generated by gaia
data/
19 changes: 17 additions & 2 deletions cmd/gaia/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"flag"
"os"

hclog "github.com/hashicorp/go-hclog"
"github.com/kataras/iris"
"github.com/michelvocks/gaia"
"github.com/michelvocks/gaia/handlers"
Expand All @@ -19,6 +21,7 @@ func init() {

// command line arguments
flag.StringVar(&cfg.ListenPort, "port", "8080", "Listen port for gaia")
flag.StringVar(&cfg.DataPath, "datapath", "data", "Path to the data folder")
flag.StringVar(&cfg.Bolt.Path, "dbpath", "gaia.db", "Path to gaia bolt db file")

// Default values
Expand All @@ -29,18 +32,30 @@ func main() {
// Parse command line flgs
flag.Parse()

// Initialize shared logger
cfg.Logger = hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
Output: hclog.DefaultOutput,
Name: "Gaia",
})

// Initialize IRIS
irisInstance = iris.New()

// Initialize store
s := store.NewStore()
err := s.Init(cfg)
if err != nil {
panic(err)
cfg.Logger.Error("cannot initialize store", "error", err.Error())
os.Exit(1)
}

// Initialize handlers
handlers.InitHandlers(irisInstance, s)
err = handlers.InitHandlers(cfg, irisInstance, s)
if err != nil {
cfg.Logger.Error("cannot initialize handlers", "error", err.Error())
os.Exit(1)
}

// Start listen
irisInstance.Run(iris.Addr(":" + cfg.ListenPort))
Expand Down
4 changes: 4 additions & 0 deletions gaia.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package gaia

import (
"os"

hclog "github.com/hashicorp/go-hclog"
)

// User is the user object
Expand All @@ -16,6 +18,8 @@ type User struct {
// Config holds all config options
type Config struct {
ListenPort string
DataPath string
Logger hclog.Logger

Bolt struct {
Path string
Expand Down
8 changes: 3 additions & 5 deletions handlers/User.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package handlers

import (
"log"
"time"

jwt "github.com/dgrijalva/jwt-go"
Expand Down Expand Up @@ -31,7 +30,7 @@ func UserLogin(ctx iris.Context) {
// Authenticate user
user, err := storeService.UserAuth(u)
if err != nil {
log.Printf("error during UserAuth: %s", err)
cfg.Logger.Error("error during UserAuth: %s", err)
ctx.StatusCode(iris.StatusInternalServerError)
return
}
Expand All @@ -55,11 +54,10 @@ func UserLogin(ctx iris.Context) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Sign and get encoded token
b := []byte{'f', '2', 'f', 'f', 's', 'h', 's'}
tokenstring, err := token.SignedString(b)
tokenstring, err := token.SignedString(jwtKey)
if err != nil {
ctx.StatusCode(iris.StatusInternalServerError)
log.Printf("Error signing jwt token: %s", err)
cfg.Logger.Error("error signing jwt token: %s", err)
return
}
user.JwtExpiry = claims.ExpiresAt
Expand Down
24 changes: 23 additions & 1 deletion handlers/handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package handlers

import (
"crypto/rand"

"github.com/kataras/iris"
"github.com/michelvocks/gaia"
"github.com/michelvocks/gaia/store"
)

Expand All @@ -13,13 +16,32 @@ const (
// Use this to talk to the store.
var storeService *store.Store

// cfg is a pointer to the global config
var cfg *gaia.Config

// jwtKey is a random generated key for jwt signing
var jwtKey []byte

// InitHandlers initializes(registers) all handlers
func InitHandlers(i *iris.Application, s *store.Store) {
func InitHandlers(c *gaia.Config, i *iris.Application, s *store.Store) error {
// Set config
cfg = c

// Set store instance
storeService = s

// Generate signing key for jwt
jwtKey = make([]byte, 64)
_, err := rand.Read(jwtKey)
if err != nil {
return err
}
cfg.Logger.Info("jwt signing key generated", "key", jwtKey)

// Define prefix
p := "/api/" + apiVersion + "/"

i.Post(p+"users/login", UserLogin)

return nil
}
45 changes: 37 additions & 8 deletions store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package store
import (
"encoding/json"
"fmt"
"os"

bolt "github.com/coreos/bbolt"
"github.com/michelvocks/gaia"
"golang.org/x/crypto/bcrypt"
)

var (
Expand All @@ -32,10 +34,18 @@ func NewStore() *Store {
return s
}

// UserUpdate takes the given user and saves it
// UserPut takes the given user and saves it
// to the bolt database. User will be overwritten
// if it already exists.
func (s *Store) UserUpdate(u *gaia.User) error {
// It also clears the password field afterwards.
func (s *Store) UserPut(u *gaia.User) error {
// Encrypt password before we save it
hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.MinCost)
if err != nil {
return err
}
u.Password = string(hash)

return s.db.Update(func(tx *bolt.Tx) error {
// Get bucket
b := tx.Bucket(userBucket)
Expand All @@ -46,6 +56,9 @@ func (s *Store) UserUpdate(u *gaia.User) error {
return err
}

// Clear password from origin object
u.Password = ""

// Put user
return b.Put([]byte(u.Username), m)
})
Expand All @@ -65,7 +78,7 @@ func (s *Store) UserAuth(u *gaia.User) (*gaia.User, error) {
}

// Check if password is valid
if user.Password != u.Password {
if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(u.Password)); err != nil {
return nil, nil
}

Expand Down Expand Up @@ -102,16 +115,32 @@ func (s *Store) UserGet(username string) (*gaia.User, error) {
return user, err
}

// Init initalizes the connection to the database.
// Init creates the data folder if not exists,
// generates private key and bolt database.
// This should be called only once per database
// because bolt holds a lock on the database file.
func (s *Store) Init(cfg *gaia.Config) error {
db, err := bolt.Open(cfg.Bolt.Path, cfg.Bolt.Mode, nil)
// Make sure data folder exists
err := os.MkdirAll(cfg.DataPath, 0700)
if err != nil {
return err
}

// Open connection to bolt database
path := cfg.DataPath + string(os.PathSeparator) + cfg.Bolt.Path
db, err := bolt.Open(path, cfg.Bolt.Mode, nil)
if err != nil {
return err
}
s.db = db

// Setup database
return setupDatabase(s)
}

// setupDatabase create all buckets in the db.
// Additionally, it makes sure that the admin user exists.
func setupDatabase(s *Store) error {
// Create bucket if not exists function
var bucketName []byte
c := func(tx *bolt.Tx) error {
Expand All @@ -124,12 +153,12 @@ func (s *Store) Init(cfg *gaia.Config) error {

// Make sure buckets exist
bucketName = userBucket
err = db.Update(c)
err := s.db.Update(c)
if err != nil {
return err
}
bucketName = pipelineBucket
err = db.Update(c)
err = s.db.Update(c)
if err != nil {
return err
}
Expand All @@ -142,7 +171,7 @@ func (s *Store) Init(cfg *gaia.Config) error {

// Create admin user if we cannot find it
if admin == nil {
err = s.UserUpdate(&gaia.User{
err = s.UserPut(&gaia.User{
DisplayName: adminUsername,
Username: adminUsername,
Password: adminPassword,
Expand Down
30 changes: 21 additions & 9 deletions store/store_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package store

import (
"fmt"
"os"
"testing"

Expand All @@ -13,10 +14,19 @@ var config *gaia.Config
func TestMain(m *testing.M) {
store = NewStore()
config = &gaia.Config{}
config.DataPath = "data"
config.Bolt.Path = "test.db"
config.Bolt.Mode = 0600

os.Exit(m.Run())
r := m.Run()

// cleanup
err := os.Remove("data")
if err != nil {
fmt.Printf("cannot remove data folder: %s\n", err.Error())
r = 1
}
os.Exit(r)
}

func TestInit(t *testing.T) {
Expand All @@ -26,7 +36,7 @@ func TestInit(t *testing.T) {
}

// cleanup
err = os.Remove("test.db")
err = os.Remove("data/test.db")
if err != nil {
t.Fatal(err)
}
Expand All @@ -42,7 +52,7 @@ func TestUserGet(t *testing.T) {
u.Username = "testuser"
u.Password = "12345!#+21+"
u.DisplayName = "Test"
err = store.UserUpdate(u)
err = store.UserPut(u)
if err != nil {
t.Fatal(err)
}
Expand All @@ -64,13 +74,13 @@ func TestUserGet(t *testing.T) {
}

// cleanup
err = os.Remove("test.db")
err = os.Remove("data/test.db")
if err != nil {
t.Fatal(err)
}
}

func TestUserUpdate(t *testing.T) {
func TestUserPut(t *testing.T) {
err := store.Init(config)
if err != nil {
t.Fatal(err)
Expand All @@ -80,13 +90,13 @@ func TestUserUpdate(t *testing.T) {
u.Username = "testuser"
u.Password = "12345!#+21+"
u.DisplayName = "Test"
err = store.UserUpdate(u)
err = store.UserPut(u)
if err != nil {
t.Fatal(err)
}

// cleanup
err = os.Remove("test.db")
err = os.Remove("data/test.db")
if err != nil {
t.Fatal(err)
}
Expand All @@ -102,12 +112,14 @@ func TestUserAuth(t *testing.T) {
u.Username = "testuser"
u.Password = "12345!#+21+"
u.DisplayName = "Test"
err = store.UserUpdate(u)
err = store.UserPut(u)
if err != nil {
t.Fatal(err)
return
}

// Password field has been cleared after last UserPut
u.Password = "12345!#+21+"
r, err := store.UserAuth(u)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -140,7 +152,7 @@ func TestUserAuth(t *testing.T) {
}

// cleanup
err = os.Remove("test.db")
err = os.Remove("data/test.db")
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit a1b0eb4

Please sign in to comment.