mirror of
https://github.com/simon987/task_tracker.git
synced 2025-04-19 10:16:41 +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"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
11
api/main.go
11
api/main.go
@ -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,
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
@ -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"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
16
schema.sql
16
schema.sql
@ -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
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"
|
"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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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"
|
@ -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"
|
||||||
)
|
)
|
||||||
|
@ -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)
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user