From 9e09246a298d03964958708abfab96c62d41fbb5 Mon Sep 17 00:00:00 2001 From: simon987 Date: Fri, 1 Feb 2019 21:21:14 -0500 Subject: [PATCH] some work on auth/sessions --- api/auth.go | 53 ++++++++++++++++ api/git.go | 4 +- api/log.go | 4 +- api/main.go | 17 +++-- api/project.go | 2 +- api/task.go | 2 +- api/worker.go | 2 +- config/config.go | 20 +++--- main/main.go | 6 +- schema.sql | 16 ++++- storage/auth.go | 121 ++++++++++++++++++++++++++++++++++++ storage/database.go | 2 +- storage/log.go | 2 +- test/api_git_test.go | 4 +- test/api_index_test.go | 2 +- test/api_log_test.go | 4 +- test/api_project_test.go | 2 +- test/api_task_bench_test.go | 26 +------- test/api_task_test.go | 4 +- test/api_worker_test.go | 4 +- test/common.go | 4 +- test/config.yml | 10 ++- test/main_test.go | 4 +- test/schema.sql | 16 ++++- 24 files changed, 265 insertions(+), 66 deletions(-) create mode 100644 api/auth.go create mode 100644 storage/auth.go diff --git a/api/auth.go b/api/auth.go new file mode 100644 index 0000000..f420be6 --- /dev/null +++ b/api/auth.go @@ -0,0 +1,53 @@ +package api + +import ( + "encoding/json" + "github.com/Sirupsen/logrus" + "github.com/kataras/go-sessions" + "github.com/simon987/task_tracker/storage" +) + +type LoginRequest struct { + Username []byte + Password []byte +} + +type LoginResponse struct { + Ok bool + Message string + Manager *storage.Manager +} + +func (api *WebAPI) Login(r *Request) { + + req := &LoginRequest{} + err := json.Unmarshal(r.Ctx.Request.Body(), req) + + if err != nil { + r.Json(LoginResponse{ + Ok: false, + Message: "Could not parse request", + }, 400) + return + } + + manager, err := api.Database.ValidateCredentials(req.Username, req.Password) + if err != nil { + logrus.WithError(err).WithFields(logrus.Fields{ + "username": string(manager.Username), + }).Warning("Login attempt") + + r.Json(LoginResponse{ + Ok: false, + Message: "Invalid username/password", + }, 403) + return + } + + sess := sessions.StartFasthttp(r.Ctx) + sess.Set("manager", manager) + + logrus.WithFields(logrus.Fields{ + "username": string(manager.Username), + }).Info("Logged in") +} diff --git a/api/git.go b/api/git.go index a7e85d3..866ea3a 100644 --- a/api/git.go +++ b/api/git.go @@ -7,10 +7,10 @@ import ( "encoding/json" "fmt" "github.com/Sirupsen/logrus" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "github.com/valyala/fasthttp" "hash" - "src/task_tracker/config" - "src/task_tracker/storage" "strings" ) diff --git a/api/log.go b/api/log.go index e4d3430..612158d 100644 --- a/api/log.go +++ b/api/log.go @@ -4,9 +4,9 @@ import ( "encoding/json" "errors" "github.com/Sirupsen/logrus" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "github.com/valyala/fasthttp" - "src/task_tracker/config" - "src/task_tracker/storage" "time" ) diff --git a/api/main.go b/api/main.go index 64f0787..bc29b39 100644 --- a/api/main.go +++ b/api/main.go @@ -4,15 +4,17 @@ import ( "fmt" "github.com/Sirupsen/logrus" "github.com/buaazp/fasthttprouter" + "github.com/kataras/go-sessions" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "github.com/valyala/fasthttp" - "src/task_tracker/config" - "src/task_tracker/storage" ) type WebAPI struct { - server *fasthttp.Server - router *fasthttprouter.Router - Database *storage.Database + server *fasthttp.Server + router *fasthttprouter.Router + Database *storage.Database + SessionConfig *sessions.Config } type Info struct { @@ -36,6 +38,11 @@ func New() *WebAPI { api.router = &fasthttprouter.Router{} + api.SessionConfig = &sessions.Config{ + Cookie: config.Cfg.SessionCookieName, + Expires: config.Cfg.SessionCookieExpiration, + } + api.server = &fasthttp.Server{ Handler: api.router.Handler, Name: info.Name, diff --git a/api/project.go b/api/project.go index 6a7c4a6..84b7779 100644 --- a/api/project.go +++ b/api/project.go @@ -3,7 +3,7 @@ package api import ( "encoding/json" "github.com/Sirupsen/logrus" - "src/task_tracker/storage" + "github.com/simon987/task_tracker/storage" "strconv" ) diff --git a/api/task.go b/api/task.go index 2f56456..910e8e5 100644 --- a/api/task.go +++ b/api/task.go @@ -9,7 +9,7 @@ import ( "errors" "github.com/Sirupsen/logrus" "github.com/dchest/siphash" - "src/task_tracker/storage" + "github.com/simon987/task_tracker/storage" "strconv" ) diff --git a/api/worker.go b/api/worker.go index 2df4f2f..d0f2a1b 100644 --- a/api/worker.go +++ b/api/worker.go @@ -3,8 +3,8 @@ package api import ( "encoding/json" "github.com/Sirupsen/logrus" + "github.com/simon987/task_tracker/storage" "math/rand" - "src/task_tracker/storage" "strconv" "time" ) diff --git a/config/config.go b/config/config.go index 15ca657..3bf4d69 100644 --- a/config/config.go +++ b/config/config.go @@ -3,16 +3,19 @@ package config import ( "github.com/Sirupsen/logrus" "github.com/spf13/viper" + "time" ) var Cfg struct { - ServerAddr string - DbConnStr string - WebHookSecret []byte - WebHookHash string - WebHookSigHeader string - LogLevel logrus.Level - DbLogLevels []logrus.Level + ServerAddr string + DbConnStr string + WebHookSecret []byte + WebHookHash string + WebHookSigHeader string + LogLevel logrus.Level + DbLogLevels []logrus.Level + SessionCookieName string + SessionCookieExpiration time.Duration } func SetupConfig() { @@ -35,4 +38,7 @@ func SetupConfig() { newLevel, _ := logrus.ParseLevel(level) Cfg.DbLogLevels = append(Cfg.DbLogLevels, newLevel) } + Cfg.SessionCookieName = viper.GetString("session.cookie_name") + Cfg.SessionCookieExpiration, err = time.ParseDuration(viper.GetString("session.expiration")) + } diff --git a/main/main.go b/main/main.go index 7f68007..0730f96 100644 --- a/main/main.go +++ b/main/main.go @@ -1,10 +1,10 @@ package main import ( + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "math/rand" - "src/task_tracker/api" - "src/task_tracker/config" - "src/task_tracker/storage" "time" ) diff --git a/schema.sql b/schema.sql index 4f79cab..a239aa5 100755 --- a/schema.sql +++ b/schema.sql @@ -1,5 +1,5 @@ DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry, - worker_has_access_to_project; + worker_has_access_to_project, manager, manager_has_role_on_project; DROP TYPE IF EXISTS status; DROP TYPE IF EXISTS log_level; @@ -63,3 +63,17 @@ CREATE TABLE log_entry timestamp INTEGER ); +CREATE TABLE manager +( + id SERIAL PRIMARY KEY, + username TEXT, + password TEXT, + website_admin BOOLEAN +); + +CREATE TABLE manager_has_role_on_project +( + manager INTEGER REFERENCES manager (id), + role SMALLINT, + project INTEGER REFERENCES project (id) +); \ No newline at end of file diff --git a/storage/auth.go b/storage/auth.go new file mode 100644 index 0000000..efcef74 --- /dev/null +++ b/storage/auth.go @@ -0,0 +1,121 @@ +package storage + +import ( + "bytes" + "crypto" + "errors" + "github.com/Sirupsen/logrus" +) + +type ManagerRole int + +const ( + ROLE_READ ManagerRole = 1 + ROLE_EDIT ManagerRole = 2 + ROLE_MANAGE_ACCESS ManagerRole = 3 +) + +type Manager struct { + Id int `json:"id"` + Username string `json:"username"` + WebsiteAdmin bool `json:"website_admin"` +} + +func (database *Database) ValidateCredentials(username []byte, password []byte) (*Manager, error) { + + db := database.getDB() + + row := db.QueryRow(`SELECT id, password, website_admin FROM manager WHERE username=$1`, + username) + + manager := &Manager{} + var passwordHash []byte + err := row.Scan(&manager.Id, &passwordHash, &manager.WebsiteAdmin) + if err != nil { + logrus.WithError(err).WithFields(logrus.Fields{ + "username": username, + }).Warning("Database.ValidateCredentials: user not found") + + return nil, errors.New("username not found") + } + + hash := crypto.SHA512.New() + hash.Write([]byte(password)) + hash.Write([]byte(username)) + + if bytes.Compare(passwordHash, hash.Sum(nil)) != 0 { + logrus.WithFields(logrus.Fields{ + "username": username, + }).Warning("Database.ValidateCredentials: password does not match") + + return nil, errors.New("password does not match") + } + + manager.Username = string(username) + + return manager, nil +} + +func (database *Database) SaveManager(manager *Manager, password []byte) error { + + db := database.getDB() + + hash := crypto.SHA512.New() + hash.Write(password) + hashedPassword := hash.Sum(nil) + + row := db.QueryRow(`INSERT INTO manager (username, password, website_admin) + VALUES ($1,$2,$3) RETURNING ID`, + manager.Username, hashedPassword, manager.WebsiteAdmin) + + err := row.Scan(manager.Id) + if err != nil { + logrus.WithError(err).WithFields(logrus.Fields{ + "username": manager, + }).Warning("Database.SaveManager INSERT error") + + return err + } + + logrus.WithFields(logrus.Fields{ + "manager": manager, + }).Info("Database.SaveManager INSERT") + + return nil +} + +func (database *Database) UpdateManager(manager *Manager) { + + db := database.getDB() + + res, err := db.Exec(`UPDATE manager SET website_admin=$1 WHERE id=$2`, + manager.WebsiteAdmin, manager.Id) + handleErr(err) + + rowsAffected, _ := res.RowsAffected() + + logrus.WithError(err).WithFields(logrus.Fields{ + "rowsAffected": rowsAffected, + "manager": manager, + }).Warning("Database.UpdateManager UPDATE") +} + +func (database *Database) UpdateManagerPassword(id int64, newPassword []byte) { + + hash := crypto.SHA512.New() + hash.Write(newPassword) + hashedPassword := hash.Sum(nil) + + db := database.getDB() + + res, err := db.Exec(`UPDATE manager SET password=$1 WHERE id=$2`, + hashedPassword, id) + handleErr(err) + + rowsAffected, _ := res.RowsAffected() + + logrus.WithError(err).WithFields(logrus.Fields{ + "rowsAffected": rowsAffected, + "id": id, + }).Warning("Database.UpdateManagerPassword UPDATE") +} diff --git a/storage/database.go b/storage/database.go index 24c90d7..ba14894 100644 --- a/storage/database.go +++ b/storage/database.go @@ -4,9 +4,9 @@ import ( "database/sql" "github.com/Sirupsen/logrus" _ "github.com/lib/pq" + "github.com/simon987/task_tracker/config" "io/ioutil" "os" - "src/task_tracker/config" ) type Database struct { diff --git a/storage/log.go b/storage/log.go index 38e0cb3..5be2106 100644 --- a/storage/log.go +++ b/storage/log.go @@ -3,7 +3,7 @@ package storage import ( "encoding/json" "github.com/Sirupsen/logrus" - "src/task_tracker/config" + "github.com/simon987/task_tracker/config" ) type LogEntry struct { diff --git a/test/api_git_test.go b/test/api_git_test.go index e8942ff..3eb6730 100644 --- a/test/api_git_test.go +++ b/test/api_git_test.go @@ -5,9 +5,9 @@ import ( "crypto" "crypto/hmac" "encoding/hex" + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/config" "net/http" - "src/task_tracker/api" - "src/task_tracker/config" "testing" ) diff --git a/test/api_index_test.go b/test/api_index_test.go index ac5a4d2..720e294 100644 --- a/test/api_index_test.go +++ b/test/api_index_test.go @@ -2,8 +2,8 @@ package test import ( "encoding/json" + "github.com/simon987/task_tracker/api" "io/ioutil" - "src/task_tracker/api" "testing" ) diff --git a/test/api_log_test.go b/test/api_log_test.go index 2680475..1d63078 100644 --- a/test/api_log_test.go +++ b/test/api_log_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "fmt" "github.com/Sirupsen/logrus" + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/storage" "io/ioutil" - "src/task_tracker/api" - "src/task_tracker/storage" "testing" "time" ) diff --git a/test/api_project_test.go b/test/api_project_test.go index 6ccbbcf..1d42a19 100644 --- a/test/api_project_test.go +++ b/test/api_project_test.go @@ -3,9 +3,9 @@ package test import ( "encoding/json" "fmt" + "github.com/simon987/task_tracker/api" "io/ioutil" "net/http" - "src/task_tracker/api" "testing" ) diff --git a/test/api_task_bench_test.go b/test/api_task_bench_test.go index da6d91b..5fe23c9 100644 --- a/test/api_task_bench_test.go +++ b/test/api_task_bench_test.go @@ -1,9 +1,9 @@ package test import ( - "src/task_tracker/api" - "src/task_tracker/config" - "src/task_tracker/storage" + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "strconv" "testing" ) @@ -50,23 +50,3 @@ func BenchmarkCreateTask(b *testing.B) { _ = db.SaveTask(&storage.Task{}, project, 0) } } - -func TestTest(t *testing.T) { - - config.SetupConfig() - db := storage.Database{} - - project, _ := db.SaveProject(&storage.Project{ - Priority: 1, - Id: 1, - Version: "bmcreatetask", - Public: true, - Motd: "bmcreatetask", - Name: "BenchmarkCreateTask", - GitRepo: "benchmark_test", - }) - - for i := 0; i < 1000000; i++ { - _ = db.SaveTask(&storage.Task{}, project, 0) - } -} diff --git a/test/api_task_test.go b/test/api_task_test.go index e359995..125b982 100644 --- a/test/api_task_test.go +++ b/test/api_task_test.go @@ -3,9 +3,9 @@ package test import ( "encoding/json" "fmt" + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/storage" "io/ioutil" - "src/task_tracker/api" - "src/task_tracker/storage" "testing" ) diff --git a/test/api_worker_test.go b/test/api_worker_test.go index 781eea0..3756525 100644 --- a/test/api_worker_test.go +++ b/test/api_worker_test.go @@ -3,10 +3,10 @@ package test import ( "encoding/json" "fmt" + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/storage" "io/ioutil" "net/http" - "src/task_tracker/api" - "src/task_tracker/storage" "testing" ) diff --git a/test/common.go b/test/common.go index bd26d30..211edba 100644 --- a/test/common.go +++ b/test/common.go @@ -7,11 +7,11 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/simon987/task_tracker/config" + "github.com/simon987/task_tracker/storage" "io" "io/ioutil" "net/http" - "src/task_tracker/config" - "src/task_tracker/storage" "strconv" ) diff --git a/test/config.yml b/test/config.yml index b25f76b..a9a6c48 100644 --- a/test/config.yml +++ b/test/config.yml @@ -3,8 +3,8 @@ server: database: conn_str: "user=task_tracker dbname=task_tracker_test sslmode=disable" - # log_levels: ["debug", "error"] - log_levels: ["debug", "error", "trace", "info", "warning"] + log_levels: ["debug", "error"] +# log_levels: ["debug", "error", "trace", "info", "warning"] git: webhook_secret: "very_secret_secret" @@ -12,4 +12,8 @@ git: webhook_sig_header: "X-Hub-Signature" log: - level: "trace" \ No newline at end of file + level: "trace" + +session: + cookie_name: "tt" + expiration: "25m" \ No newline at end of file diff --git a/test/main_test.go b/test/main_test.go index 4ec356b..a47bd06 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -1,8 +1,8 @@ package test import ( - "src/task_tracker/api" - "src/task_tracker/config" + "github.com/simon987/task_tracker/api" + "github.com/simon987/task_tracker/config" "testing" "time" ) diff --git a/test/schema.sql b/test/schema.sql index 4f79cab..a239aa5 100755 --- a/test/schema.sql +++ b/test/schema.sql @@ -1,5 +1,5 @@ DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry, - worker_has_access_to_project; + worker_has_access_to_project, manager, manager_has_role_on_project; DROP TYPE IF EXISTS status; DROP TYPE IF EXISTS log_level; @@ -63,3 +63,17 @@ CREATE TABLE log_entry timestamp INTEGER ); +CREATE TABLE manager +( + id SERIAL PRIMARY KEY, + username TEXT, + password TEXT, + website_admin BOOLEAN +); + +CREATE TABLE manager_has_role_on_project +( + manager INTEGER REFERENCES manager (id), + role SMALLINT, + project INTEGER REFERENCES project (id) +); \ No newline at end of file