From 9b67a580dad87ea2e0007ac24d7ecc00d852f123 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 12 Mar 2024 20:44:40 +0100 Subject: [PATCH 1/4] Gluecode for database/handlers --- backend/internal/database/db.go | 2 ++ backend/internal/handlers/global_state.go | 33 ++++++++++++++++++----- backend/internal/types/project.go | 21 +++++++++++++++ backend/internal/types/users.go | 5 ++++ 4 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 backend/internal/types/project.go diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 2b3cced..5a88873 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -12,7 +12,9 @@ import ( // Interface for the database type Database interface { + // Insert a new user into the database, password should be hashed before calling AddUser(username string, password string) error + RemoveUser(username string) error PromoteToAdmin(username string) error GetUserId(username string) (int, error) diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 689759b..9c42133 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -11,11 +11,11 @@ import ( // The actual interface that we will use type GlobalState interface { - Register(c *fiber.Ctx) error // To register a new user - UserDelete(c *fiber.Ctx) error // To delete a user - Login(c *fiber.Ctx) error // To get the token - LoginRenew(c *fiber.Ctx) error // To renew the token - // CreateProject(c *fiber.Ctx) error // To create a new project + Register(c *fiber.Ctx) error // To register a new user + UserDelete(c *fiber.Ctx) error // To delete a user + Login(c *fiber.Ctx) error // To get the token + LoginRenew(c *fiber.Ctx) error // To renew the token + CreateProject(c *fiber.Ctx) error // To create a new project // GetProjects(c *fiber.Ctx) error // To get all projects // GetProject(c *fiber.Ctx) error // To get a specific project // UpdateProject(c *fiber.Ctx) error // To update a project @@ -58,7 +58,7 @@ type GState struct { // @Failure 500 {string} string "Internal server error" // @Router /api/register [post] func (gs *GState) Register(c *fiber.Ctx) error { - u := new(types.User) + u := new(types.NewUser) if err := c.BodyParser(u); err != nil { return c.Status(400).SendString(err.Error()) } @@ -142,3 +142,24 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { } return c.JSON(fiber.Map{"token": t}) } + +// CreateProject is a simple handler that creates a new project +func (gs *GState) CreateProject(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + + p := new(types.NewProject) + if err := c.BodyParser(p); err != nil { + return c.Status(400).SendString(err.Error()) + } + + // Get the username from the token and set it as the owner of the project + // This is ugly but + claims := user.Claims.(jwt.MapClaims) + p.Owner = claims["name"].(string) + + if err := gs.Db.AddProject(p.Name, p.Description, p.Owner); err != nil { + return c.Status(500).SendString(err.Error()) + } + + return c.Status(200).SendString("Project added") +} diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go new file mode 100644 index 0000000..cabf6c6 --- /dev/null +++ b/backend/internal/types/project.go @@ -0,0 +1,21 @@ +package types + +import ( + "time" +) + +// Project is a struct that holds the information about a project +type Project struct { + ID int `json:"id" db:"id"` + Name string `json:"name" db:"name"` + Description string `json:"description" db:"description"` + Owner string `json:"owner" db:"owner"` + Created time.Time `json:"created" db:"created"` +} + +// As it arrives from the client +type NewProject struct { + Name string `json:"name"` + Description string `json:"description"` + Owner string `json:"owner"` +} diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go index fa735d7..233ec71 100644 --- a/backend/internal/types/users.go +++ b/backend/internal/types/users.go @@ -16,6 +16,11 @@ func (u *User) ToPublicUser() (*PublicUser, error) { }, nil } +type NewUser struct { + Username string `json:"username"` + Password string `json:"password"` +} + // PublicUser represents a user that is safe to send over the API (no password) type PublicUser struct { UserId string `json:"userId"` From 736cebe036dfadd9a90dd093b8ed25291ea901fa Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Tue, 12 Mar 2024 20:44:54 +0100 Subject: [PATCH 2/4] Sql comments and salts table --- .../internal/database/migrations/0010_users.sql | 5 +++++ .../internal/database/migrations/0070_salts.sql | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 backend/internal/database/migrations/0070_salts.sql diff --git a/backend/internal/database/migrations/0010_users.sql b/backend/internal/database/migrations/0010_users.sql index 7cb23fd..5c9d329 100644 --- a/backend/internal/database/migrations/0010_users.sql +++ b/backend/internal/database/migrations/0010_users.sql @@ -1,3 +1,7 @@ +-- Id is a surrogate key for in ternal use +-- userId is what is used for external id +-- username is what is used for login +-- password is the hashed password CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, userId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE, @@ -5,5 +9,6 @@ CREATE TABLE IF NOT EXISTS users ( password VARCHAR(255) NOT NULL ); +-- Users are commonly searched by username and userId CREATE INDEX IF NOT EXISTS users_username_index ON users (username); CREATE INDEX IF NOT EXISTS users_userId_index ON users (userId); \ No newline at end of file diff --git a/backend/internal/database/migrations/0070_salts.sql b/backend/internal/database/migrations/0070_salts.sql new file mode 100644 index 0000000..9fb0588 --- /dev/null +++ b/backend/internal/database/migrations/0070_salts.sql @@ -0,0 +1,15 @@ +-- It is unclear weather this table will be used + +-- Create the table to store hash salts +CREATE TABLE salts ( + id INTEGER PRIMARY KEY, + salt TEXT NOT NULL +); + +-- Create a trigger to automatically generate a salt when inserting a new user record +CREATE TRIGGER generate_salt_trigger +AFTER INSERT ON users +BEGIN + INSERT INTO salts (salt) VALUES (randomblob(16)); + UPDATE users SET salt_id = (SELECT last_insert_rowid()) WHERE id = new.id; +END; From c4632104a8f9c84c71dba8019fdbeec8c1a3e30b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 13 Mar 2024 11:46:17 +0100 Subject: [PATCH 3/4] Commenting out trigger related to salts --- backend/internal/database/migrations/0070_salts.sql | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/internal/database/migrations/0070_salts.sql b/backend/internal/database/migrations/0070_salts.sql index 9fb0588..b84dfac 100644 --- a/backend/internal/database/migrations/0070_salts.sql +++ b/backend/internal/database/migrations/0070_salts.sql @@ -6,10 +6,11 @@ CREATE TABLE salts ( salt TEXT NOT NULL ); +-- Commented out for now, no time for good practices, which is atrocious -- Create a trigger to automatically generate a salt when inserting a new user record -CREATE TRIGGER generate_salt_trigger -AFTER INSERT ON users -BEGIN - INSERT INTO salts (salt) VALUES (randomblob(16)); - UPDATE users SET salt_id = (SELECT last_insert_rowid()) WHERE id = new.id; -END; +-- CREATE TRIGGER generate_salt_trigger +-- AFTER INSERT ON users +-- BEGIN +-- INSERT INTO salts (salt) VALUES (randomblob(16)); +-- UPDATE users SET salt_id = (SELECT last_insert_rowid()) WHERE id = new.id; +-- END; From 25fdf3bb9b105a343d50c3cef62139e3d2496cdc Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 13 Mar 2024 12:29:15 +0100 Subject: [PATCH 4/4] Nuke dead code --- backend/internal/model/timereport.go | 19 ---- backend/internal/model/user.go | 139 --------------------------- 2 files changed, 158 deletions(-) delete mode 100644 backend/internal/model/timereport.go delete mode 100644 backend/internal/model/user.go diff --git a/backend/internal/model/timereport.go b/backend/internal/model/timereport.go deleted file mode 100644 index d48c5fb..0000000 --- a/backend/internal/model/timereport.go +++ /dev/null @@ -1,19 +0,0 @@ -package model - -type TimeReport struct { - reportId string - projectName string - userName string - userRole string - reportDate string - timeWorked uint64 - isSigned bool - reportStatus string // Example "draft", "signed", "unsigned" -} - -type Project struct { - timeReports []TimeReport - projectName string - projectmembers map[string]User - projectRoles map[string]User -} diff --git a/backend/internal/model/user.go b/backend/internal/model/user.go deleted file mode 100644 index c125a60..0000000 --- a/backend/internal/model/user.go +++ /dev/null @@ -1,139 +0,0 @@ -package model - -import ( - "errors" -) - -type Account struct { - fullName string - userName string -} - -type Administrator struct { - projects map[string]Project - Account - ProjectMember // comp -} - -// Administrator reciever functions -func (administrator Administrator) addUser(project *Project, user *User) error { - // WIP - return errors.New("WIP") -} - -func (administrator Administrator) removerUser(project *Project, user *User) error { - // WIP - return errors.New("WIP") -} - -func (administrator Administrator) deleteProject(project *Project) error { - // WIP - return errors.New("WIP") -} - -func (administrator Administrator) changeUserRole(project *Project, user *User) error { - // WIP - return errors.New("WIP") -} - -func (administrator *Administrator) login() error { - // WIP - return errors.New("WIP") -} - -func (administrator *Administrator) logout() error { - // WIP - return errors.New("WIP") -} - -type ProjectManager struct { - managedProjects map[string]Project // projekt som förvaltas av projektledaren - totalTime uint64 // total totalt tid arbetat av projektledaren - Account - ProjectMember // comp -} - -// ProjectManager reciever functions -func (projectManager ProjectManager) signReport(timeReport *TimeReport, user User) error { - // WIP - return errors.New("WIP") -} - -func (projectManager ProjectManager) unsignReport(timeReport *TimeReport, user User) error { - // WIP - return errors.New("WIP") -} - -func (projectManager ProjectManager) getAllReports(project *Project) ([]TimeReport, error) { - // WIP - return project.timeReports, errors.New("WIP") -} - -func (projectManager ProjectManager) assignRole(user *User, project *Project, newRole string) error { - // WIP - return errors.New("WIP") -} - -func (projectManager ProjectManager) removeMember(project *Project, user *User) error { - // WIP - return errors.New("WIP") -} - -func (projectManager ProjectManager) getTotalTime(project *Project) (uint64, error) { - // WIP - return 0, errors.New("WIP") -} - -func (projectManager *ProjectManager) login() error { - // WIP - return errors.New("WIP") -} - -func (projectManager *ProjectManager) logout() error { - // WIP - return errors.New("WIP") -} - -type ProjectMember struct { - timereports []TimeReport - role string // ????? - Account // comp -} - -// User reciever functions - -// function used to create a time report, returning a *TimeReport is questionable -func (ProjectMember *ProjectMember) createTimeReport(Project *Project) (*TimeReport, error) { - // WIP - return &TimeReport{}, errors.New("WIP") -} - -func (ProjectMember ProjectMember) getTimeReport(timereports *[]TimeReport) (*TimeReport, error) { - // WIP - return &TimeReport{}, errors.New("WIP") -} - -func (ProjectMember *ProjectMember) editTimeReport(timereport *TimeReport) { - // timereport.editReport() - // WIP -} - -func (projectUser ProjectMember) deleteTimeReport(timeReport *TimeReport) error { // Ska bara project manager kunna göra detta? fråga ledarna! - // WIP - return errors.New("WIP") -} - -func (projectUser *ProjectMember) login() error { - // WIP - return errors.New("WIP") -} - -func (projectUser *ProjectMember) logout() error { - // WIP - return errors.New("WIP") -} - -type User interface { - login() - logout() -}