mirror of
				https://github.com/simon987/task_tracker.git
				synced 2025-10-30 23:46:52 +00:00 
			
		
		
		
	some work on auth/sessions
This commit is contained in:
		
							parent
							
								
									d3188c512d
								
							
						
					
					
						commit
						9e09246a29
					
				
							
								
								
									
										53
									
								
								api/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								api/auth.go
									
									
									
									
									
										Normal 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") | ||||
| } | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										11
									
								
								api/main.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								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 | ||||
| 	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, | ||||
|  | ||||
| @ -3,7 +3,7 @@ package api | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"src/task_tracker/storage" | ||||
| 	"github.com/simon987/task_tracker/storage" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
|  | ||||
| @ -3,6 +3,7 @@ package config | ||||
| import ( | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| var Cfg struct { | ||||
| @ -13,6 +14,8 @@ var Cfg struct { | ||||
| 	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")) | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										16
									
								
								schema.sql
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								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) | ||||
| ); | ||||
							
								
								
									
										121
									
								
								storage/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								storage/auth.go
									
									
									
									
									
										Normal 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") | ||||
| } | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -2,8 +2,8 @@ package test | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"github.com/simon987/task_tracker/api" | ||||
| 	"io/ioutil" | ||||
| 	"src/task_tracker/api" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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" | ||||
| @ -13,3 +13,7 @@ git: | ||||
| 
 | ||||
| log: | ||||
|   level: "trace" | ||||
| 
 | ||||
| session: | ||||
|   cookie_name: "tt" | ||||
|   expiration: "25m" | ||||
| @ -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" | ||||
| ) | ||||
|  | ||||
| @ -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) | ||||
| ); | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user