some work on auth/sessions

This commit is contained in:
simon987 2019-02-01 21:21:14 -05:00
parent d3188c512d
commit 9e09246a29
24 changed files with 265 additions and 66 deletions

53
api/auth.go Normal file
View File

@ -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")
}

View File

@ -7,10 +7,10 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/simon987/task_tracker/config"
"github.com/simon987/task_tracker/storage"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
"hash" "hash"
"src/task_tracker/config"
"src/task_tracker/storage"
"strings" "strings"
) )

View File

@ -4,9 +4,9 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/simon987/task_tracker/config"
"github.com/simon987/task_tracker/storage"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
"src/task_tracker/config"
"src/task_tracker/storage"
"time" "time"
) )

View File

@ -4,15 +4,17 @@ import (
"fmt" "fmt"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/buaazp/fasthttprouter" "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" "github.com/valyala/fasthttp"
"src/task_tracker/config"
"src/task_tracker/storage"
) )
type WebAPI struct { type WebAPI struct {
server *fasthttp.Server server *fasthttp.Server
router *fasthttprouter.Router router *fasthttprouter.Router
Database *storage.Database Database *storage.Database
SessionConfig *sessions.Config
} }
type Info struct { type Info struct {
@ -36,6 +38,11 @@ func New() *WebAPI {
api.router = &fasthttprouter.Router{} api.router = &fasthttprouter.Router{}
api.SessionConfig = &sessions.Config{
Cookie: config.Cfg.SessionCookieName,
Expires: config.Cfg.SessionCookieExpiration,
}
api.server = &fasthttp.Server{ api.server = &fasthttp.Server{
Handler: api.router.Handler, Handler: api.router.Handler,
Name: info.Name, Name: info.Name,

View File

@ -3,7 +3,7 @@ package api
import ( import (
"encoding/json" "encoding/json"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"src/task_tracker/storage" "github.com/simon987/task_tracker/storage"
"strconv" "strconv"
) )

View File

@ -9,7 +9,7 @@ import (
"errors" "errors"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/dchest/siphash" "github.com/dchest/siphash"
"src/task_tracker/storage" "github.com/simon987/task_tracker/storage"
"strconv" "strconv"
) )

View File

@ -3,8 +3,8 @@ package api
import ( import (
"encoding/json" "encoding/json"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/simon987/task_tracker/storage"
"math/rand" "math/rand"
"src/task_tracker/storage"
"strconv" "strconv"
"time" "time"
) )

View File

@ -3,6 +3,7 @@ package config
import ( import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/spf13/viper" "github.com/spf13/viper"
"time"
) )
var Cfg struct { var Cfg struct {
@ -13,6 +14,8 @@ var Cfg struct {
WebHookSigHeader string WebHookSigHeader string
LogLevel logrus.Level LogLevel logrus.Level
DbLogLevels []logrus.Level DbLogLevels []logrus.Level
SessionCookieName string
SessionCookieExpiration time.Duration
} }
func SetupConfig() { func SetupConfig() {
@ -35,4 +38,7 @@ func SetupConfig() {
newLevel, _ := logrus.ParseLevel(level) newLevel, _ := logrus.ParseLevel(level)
Cfg.DbLogLevels = append(Cfg.DbLogLevels, newLevel) Cfg.DbLogLevels = append(Cfg.DbLogLevels, newLevel)
} }
Cfg.SessionCookieName = viper.GetString("session.cookie_name")
Cfg.SessionCookieExpiration, err = time.ParseDuration(viper.GetString("session.expiration"))
} }

View File

@ -1,10 +1,10 @@
package main package main
import ( import (
"github.com/simon987/task_tracker/api"
"github.com/simon987/task_tracker/config"
"github.com/simon987/task_tracker/storage"
"math/rand" "math/rand"
"src/task_tracker/api"
"src/task_tracker/config"
"src/task_tracker/storage"
"time" "time"
) )

View File

@ -1,5 +1,5 @@
DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry, 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 status;
DROP TYPE IF EXISTS log_level; DROP TYPE IF EXISTS log_level;
@ -63,3 +63,17 @@ CREATE TABLE log_entry
timestamp INTEGER 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)
);

121
storage/auth.go Normal file
View File

@ -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")
}

View File

@ -4,9 +4,9 @@ import (
"database/sql" "database/sql"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/simon987/task_tracker/config"
"io/ioutil" "io/ioutil"
"os" "os"
"src/task_tracker/config"
) )
type Database struct { type Database struct {

View File

@ -3,7 +3,7 @@ package storage
import ( import (
"encoding/json" "encoding/json"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"src/task_tracker/config" "github.com/simon987/task_tracker/config"
) )
type LogEntry struct { type LogEntry struct {

View File

@ -5,9 +5,9 @@ import (
"crypto" "crypto"
"crypto/hmac" "crypto/hmac"
"encoding/hex" "encoding/hex"
"github.com/simon987/task_tracker/api"
"github.com/simon987/task_tracker/config"
"net/http" "net/http"
"src/task_tracker/api"
"src/task_tracker/config"
"testing" "testing"
) )

View File

@ -2,8 +2,8 @@ package test
import ( import (
"encoding/json" "encoding/json"
"github.com/simon987/task_tracker/api"
"io/ioutil" "io/ioutil"
"src/task_tracker/api"
"testing" "testing"
) )

View File

@ -4,9 +4,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/simon987/task_tracker/api"
"github.com/simon987/task_tracker/storage"
"io/ioutil" "io/ioutil"
"src/task_tracker/api"
"src/task_tracker/storage"
"testing" "testing"
"time" "time"
) )

View File

@ -3,9 +3,9 @@ package test
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/simon987/task_tracker/api"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"src/task_tracker/api"
"testing" "testing"
) )

View File

@ -1,9 +1,9 @@
package test package test
import ( import (
"src/task_tracker/api" "github.com/simon987/task_tracker/api"
"src/task_tracker/config" "github.com/simon987/task_tracker/config"
"src/task_tracker/storage" "github.com/simon987/task_tracker/storage"
"strconv" "strconv"
"testing" "testing"
) )
@ -50,23 +50,3 @@ func BenchmarkCreateTask(b *testing.B) {
_ = db.SaveTask(&storage.Task{}, project, 0) _ = 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)
}
}

View File

@ -3,9 +3,9 @@ package test
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/simon987/task_tracker/api"
"github.com/simon987/task_tracker/storage"
"io/ioutil" "io/ioutil"
"src/task_tracker/api"
"src/task_tracker/storage"
"testing" "testing"
) )

View File

@ -3,10 +3,10 @@ package test
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/simon987/task_tracker/api"
"github.com/simon987/task_tracker/storage"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"src/task_tracker/api"
"src/task_tracker/storage"
"testing" "testing"
) )

View File

@ -7,11 +7,11 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/simon987/task_tracker/config"
"github.com/simon987/task_tracker/storage"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"src/task_tracker/config"
"src/task_tracker/storage"
"strconv" "strconv"
) )

View File

@ -3,8 +3,8 @@ server:
database: database:
conn_str: "user=task_tracker dbname=task_tracker_test sslmode=disable" conn_str: "user=task_tracker dbname=task_tracker_test sslmode=disable"
# log_levels: ["debug", "error"] log_levels: ["debug", "error"]
log_levels: ["debug", "error", "trace", "info", "warning"] # log_levels: ["debug", "error", "trace", "info", "warning"]
git: git:
webhook_secret: "very_secret_secret" webhook_secret: "very_secret_secret"
@ -13,3 +13,7 @@ git:
log: log:
level: "trace" level: "trace"
session:
cookie_name: "tt"
expiration: "25m"

View File

@ -1,8 +1,8 @@
package test package test
import ( import (
"src/task_tracker/api" "github.com/simon987/task_tracker/api"
"src/task_tracker/config" "github.com/simon987/task_tracker/config"
"testing" "testing"
"time" "time"
) )

View File

@ -1,5 +1,5 @@
DROP TABLE IF EXISTS worker_identity, worker, project, task, log_entry, 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 status;
DROP TYPE IF EXISTS log_level; DROP TYPE IF EXISTS log_level;
@ -63,3 +63,17 @@ CREATE TABLE log_entry
timestamp INTEGER 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)
);