From 79eb59ad46f15c6d7eccf3021e3ce116fb309618 Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Sun, 17 Mar 2024 14:13:35 +0100 Subject: [PATCH 01/13] button fix --- frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx b/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx index 2af1145..bbafd6a 100644 --- a/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx +++ b/frontend/src/Pages/ProjectManagerPages/PMProjectMembers.tsx @@ -14,6 +14,7 @@ function PMProjectMembers(): JSX.Element { onClick={(): void => { return; }} + type={"button"} /> @@ -22,6 +23,7 @@ function PMProjectMembers(): JSX.Element { onClick={(): void => { return; }} + type={"button"} /> From 141e5c8bb6120220f77f0d394c3d0b1b27999cda Mon Sep 17 00:00:00 2001 From: pavel Hamawand Date: Sun, 17 Mar 2024 16:21:36 +0100 Subject: [PATCH 02/13] minor fix --- frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx index 85c80df..ca84770 100644 --- a/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx +++ b/frontend/src/Pages/UserPages/UserNewTimeReportPage.tsx @@ -1,7 +1,7 @@ import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; import NewTimeReport from "../../Components/TimeReport"; -import BackButton from "../../Components/BackButton"; +import { Link } from "react-router-dom"; function UserNewTimeReportPage(): JSX.Element { const content = ( From e7e79ced139eab82840c02f9485e0dfaebc26a58 Mon Sep 17 00:00:00 2001 From: Peter KW Date: Sun, 17 Mar 2024 19:16:57 +0100 Subject: [PATCH 03/13] Now checks if user is in database with api.login and sets proper authority level based on name for now --- frontend/src/Components/LoginCheck.tsx | 74 ++++++++++++++++---------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/frontend/src/Components/LoginCheck.tsx b/frontend/src/Components/LoginCheck.tsx index ccf761d..3658cbf 100644 --- a/frontend/src/Components/LoginCheck.tsx +++ b/frontend/src/Components/LoginCheck.tsx @@ -1,34 +1,54 @@ -import { NewUser } from "../Types/Users"; +import { NewUser } from "../Types/goTypes"; +import { api, APIResponse } from "../API/API"; +import { Dispatch, SetStateAction } from "react"; -function LoginCheck(props: { username: string; password: string }): number { - //Example users for testing without backend, remove when using backend - const admin: NewUser = { - userName: "admin", - password: "123", - }; - const pmanager: NewUser = { - userName: "pmanager", - password: "123", - }; +/* + * Checks if user is in database with api.login and then sets proper authority level + * TODO: change so that it checks for user type (admin, user, pm) somehow instead + **/ +function LoginCheck(props: { + username: string; + password: string; + setAuthority: Dispatch>; +}): number { const user: NewUser = { - userName: "user", - password: "123", + username: props.username, + password: props.password, }; + api + .login(user) + .then((response: APIResponse) => { + if (response.success) { + if (response.data !== undefined) { + const token = response.data; + //TODO: change so that it checks for user type (admin, user, pm) instead + if (token !== "" && props.username === "admin") { + props.setAuthority((prevAuth) => { + prevAuth = 1; + return prevAuth; + }); + } else if (token !== "" && props.username === "pm") { + props.setAuthority((prevAuth) => { + prevAuth = 2; + return prevAuth; + }); + } else if (token !== "" && props.username === "user") { + props.setAuthority((prevAuth) => { + prevAuth = 3; + return prevAuth; + }); + } + } else { + console.error("Token was undefined"); + } + } else { + console.error("Token could not be fetched"); + } + }) + .catch((error) => { + console.error("An error occurred during login:", error); + }); - //TODO: Compare with db instead when finished - if (props.username === admin.userName && props.password === admin.password) { - return 1; - } else if ( - props.username === pmanager.userName && - props.password === pmanager.password - ) { - return 2; - } else if ( - props.username === user.userName && - props.password === user.password - ) { - return 3; - } return 0; } From b999e47f9424c71acf26b9e770635f053a82099e Mon Sep 17 00:00:00 2001 From: Peter KW Date: Sun, 17 Mar 2024 19:17:34 +0100 Subject: [PATCH 04/13] Changes to handlesubmit --- frontend/src/Pages/LoginPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/Pages/LoginPage.tsx b/frontend/src/Pages/LoginPage.tsx index f58f3e0..1ff8c9c 100644 --- a/frontend/src/Pages/LoginPage.tsx +++ b/frontend/src/Pages/LoginPage.tsx @@ -15,9 +15,10 @@ function LoginPage(props: { and if so, redirect to correct page */ function handleSubmit(event: FormEvent): void { event.preventDefault(); - props.setAuthority((prevAuth) => { - prevAuth = LoginCheck({ username: username, password: password }); - return prevAuth; + LoginCheck({ + username: username, + password: password, + setAuthority: props.setAuthority, }); } From 197712592313f7f3af2c62ba0bba236ea73cbabe Mon Sep 17 00:00:00 2001 From: Peter KW Date: Sun, 17 Mar 2024 19:18:03 +0100 Subject: [PATCH 05/13] Now exporting APIResponse interface --- frontend/src/API/API.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index bd6518b..6dfbc1e 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -7,7 +7,7 @@ import { } from "../Types/goTypes"; // This type of pattern should be hard to misuse -interface APIResponse { +export interface APIResponse { success: boolean; message?: string; data?: T; From 9240d5e0521203031339766fda169662b2761520 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 17 Mar 2024 19:24:13 +0100 Subject: [PATCH 06/13] Verbose debug printing in login endpoint --- backend/internal/handlers/handlers_user_related.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index e90abd0..c1c8abe 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -57,9 +57,11 @@ func (gs *GState) Login(c *fiber.Ctx) error { // The body type is identical to a NewUser u := new(types.NewUser) if err := c.BodyParser(u); err != nil { + println("Error parsing body") return c.Status(400).SendString(err.Error()) } + println("Username:", u.Username) if !gs.Db.CheckUser(u.Username, u.Password) { println("User not found") return c.SendStatus(fiber.StatusUnauthorized) @@ -74,13 +76,16 @@ func (gs *GState) Login(c *fiber.Ctx) error { // Create token token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + println("Token created for user:", u.Username) // Generate encoded token and send it as response. t, err := token.SignedString([]byte("secret")) if err != nil { + println("Error signing token") return c.SendStatus(fiber.StatusInternalServerError) } + println("Successfully signed token for user:", u.Username) return c.JSON(fiber.Map{"token": t}) } From 402b0ac08b189faad3b94a41abdd29b85d6e5749 Mon Sep 17 00:00:00 2001 From: al8763be Date: Sun, 17 Mar 2024 19:28:03 +0100 Subject: [PATCH 07/13] Update API.test.ts --- frontend/src/API/API.test.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/src/API/API.test.ts b/frontend/src/API/API.test.ts index e0a93f6..dbae706 100644 --- a/frontend/src/API/API.test.ts +++ b/frontend/src/API/API.test.ts @@ -8,9 +8,8 @@ describe("API", () => { username: "lol", // Add the username property password: "lol", }; - const response = await api.registerUser(user); - + console.log(response.message); expect(response.success).toBe(true); expect(response.data).toHaveProperty("userId"); }); @@ -24,7 +23,7 @@ describe("API", () => { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t"; const response = await api.createProject(project, token); - + console.log(response.message); expect(response.success).toBe(true); expect(response.data).toHaveProperty("projectId"); }); @@ -34,7 +33,7 @@ describe("API", () => { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t"; const response = await api.renewToken(refreshToken); - + console.log(response.message); expect(response.success).toBe(true); expect(response.data).toHaveProperty("accessToken"); expect(response.data).toHaveProperty("refreshToken"); @@ -45,7 +44,7 @@ describe("API", () => { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t"; const username = "rrgumdzpmc"; const response = await api.getUserProjects(username, token); - + console.log(response.message); expect(response.success).toBe(true); expect(response.data).toHaveProperty("projects"); }); @@ -65,7 +64,7 @@ describe("API", () => { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTcxMDk0MDIwMywibmFtZSI6InJyZ3VtZHpwbWMifQ.V9NHoYMYV61t"; const response = await api.submitWeeklyReport(report, token); - + console.log(response.message); expect(response.success).toBe(true); expect(response.data).toHaveProperty( "message", @@ -80,7 +79,7 @@ describe("API", () => { }; const response = await api.login(user); - + console.log(response.message); expect(response.success).toBe(true); expect(response.data).toHaveProperty("accessToken"); expect(response.data).toHaveProperty("refreshToken"); From 3683552af8cb1e766056dabc55c24ec5f40a7acb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 17 Mar 2024 19:33:13 +0100 Subject: [PATCH 08/13] Verbose debug printing in register endpoint --- backend/internal/handlers/handlers_user_related.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index c1c8abe..0619ea5 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -22,13 +22,16 @@ import ( func (gs *GState) Register(c *fiber.Ctx) error { u := new(types.NewUser) if err := c.BodyParser(u); err != nil { + println("Error parsing body") return c.Status(400).SendString(err.Error()) } + println("Adding user:", u.Username) if err := gs.Db.AddUser(u.Username, u.Password); err != nil { return c.Status(500).SendString(err.Error()) } + println("User added:", u.Username) return c.Status(200).SendString("User added") } From b93df693d253998b2cd16010d1a51cda013955af Mon Sep 17 00:00:00 2001 From: dDogge <> Date: Sun, 17 Mar 2024 19:56:16 +0100 Subject: [PATCH 09/13] Fixed weekly_report --- backend/internal/database/migrations/0035_weekly_report.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/database/migrations/0035_weekly_report.sql b/backend/internal/database/migrations/0035_weekly_report.sql index 366d932..81f2eee 100644 --- a/backend/internal/database/migrations/0035_weekly_report.sql +++ b/backend/internal/database/migrations/0035_weekly_report.sql @@ -1,4 +1,4 @@ -CREATE TABLE weekly_reports ( +CREATE TABLE weekly_reports IF NOT EXISTS( report_id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, project_id INTEGER NOT NULL, From 8a34fc07fae73d457b213a7f05cf756e27895870 Mon Sep 17 00:00:00 2001 From: dDogge <> Date: Sun, 17 Mar 2024 19:58:44 +0100 Subject: [PATCH 10/13] Weekly_report fixed for real this time --- backend/internal/database/migrations/0035_weekly_report.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/database/migrations/0035_weekly_report.sql b/backend/internal/database/migrations/0035_weekly_report.sql index 81f2eee..30876bd 100644 --- a/backend/internal/database/migrations/0035_weekly_report.sql +++ b/backend/internal/database/migrations/0035_weekly_report.sql @@ -1,4 +1,4 @@ -CREATE TABLE weekly_reports IF NOT EXISTS( +CREATE TABLE IF NOT EXISTS weekly_reports( report_id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, project_id INTEGER NOT NULL, From e03727613d25761f526d5a501c8af3bed408e7cb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sun, 17 Mar 2024 20:04:29 +0100 Subject: [PATCH 11/13] Extremely important formatting --- backend/internal/database/migrations/0035_weekly_report.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/database/migrations/0035_weekly_report.sql b/backend/internal/database/migrations/0035_weekly_report.sql index 30876bd..8f76b80 100644 --- a/backend/internal/database/migrations/0035_weekly_report.sql +++ b/backend/internal/database/migrations/0035_weekly_report.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS weekly_reports( +CREATE TABLE IF NOT EXISTS weekly_reports ( report_id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, project_id INTEGER NOT NULL, From 37bbbb6098a9f9e2dcd79fdb0d71f71e3899a769 Mon Sep 17 00:00:00 2001 From: dDogge <> Date: Sun, 17 Mar 2024 20:30:55 +0100 Subject: [PATCH 12/13] Added SignWeeklyReport function and 2 corresponding test, also small change to WeeklyReport.go --- backend/internal/database/db.go | 33 ++++++- backend/internal/database/db_test.go | 126 +++++++++++++++++++++++++ backend/internal/types/WeeklyReport.go | 2 + 3 files changed, 160 insertions(+), 1 deletion(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 7f4a89c..5cbb13f 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -2,6 +2,7 @@ package database import ( "embed" + "errors" "path/filepath" "ttime/internal/types" @@ -30,6 +31,7 @@ type Database interface { GetProject(projectId int) (types.Project, error) GetUserRole(username string, projectname string) (string, error) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) + SignWeeklyReport(reportId int, projectManagerId int) error } // This struct is a wrapper type that holds the database connection @@ -270,7 +272,8 @@ func (d *Db) GetWeeklyReport(username string, projectName string, week int) (typ admin_time, own_work_time, study_time, - testing_time + testing_time, + signed_by FROM weekly_reports WHERE @@ -282,6 +285,34 @@ func (d *Db) GetWeeklyReport(username string, projectName string, week int) (typ return report, err } +// SignWeeklyReport signs a weekly report by updating the signed_by field +// with the provided project manager's ID, but only if the project manager +// is in the same project as the report +func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { + // Retrieve the project ID associated with the report + var reportProjectID int + err := d.Get(&reportProjectID, "SELECT project_id FROM weekly_reports WHERE report_id = ?", reportId) + if err != nil { + return err + } + + // Retrieve the project ID associated with the project manager + var managerProjectID int + err = d.Get(&managerProjectID, "SELECT project_id FROM user_roles WHERE user_id = ? AND p_role = 'project_manager'", projectManagerId) + if err != nil { + return err + } + + // Check if the project manager is in the same project as the report + if reportProjectID != managerProjectID { + return errors.New("project manager doesn't have permission to sign the report") + } + + // Update the signed_by field of the specified report + _, err = d.Exec("UPDATE weekly_reports SET signed_by = ? WHERE report_id = ?", projectManagerId, reportId) + return err +} + // Reads a directory of migration files and applies them to the database. // This will eventually be used on an embedded directory func (d *Db) Migrate() error { diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index f791066..1e8741d 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -1,6 +1,7 @@ package database import ( + "fmt" "testing" ) @@ -410,3 +411,128 @@ func TestGetWeeklyReport(t *testing.T) { } // Check other fields similarly } + +func TestSignWeeklyReport(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add project manager + err = db.AddUser("projectManager", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add a regular user + err = db.AddUser("testuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add project + err = db.AddProject("testproject", "description", "projectManager") + if err != nil { + t.Error("AddProject failed:", err) + } + + // Add both regular users as members to the project + err = db.AddUserToProject("testuser", "testproject", "member") + if err != nil { + t.Error("AddUserToProject failed:", err) + } + + err = db.AddUserToProject("projectManager", "testproject", "project_manager") + if err != nil { + t.Error("AddUserToProject failed:", err) + } + + // Add a weekly report for one of the regular users + err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) + if err != nil { + t.Error("AddWeeklyReport failed:", err) + } + + // Retrieve the added report + report, err := db.GetWeeklyReport("testuser", "testproject", 1) + if err != nil { + t.Error("GetWeeklyReport failed:", err) + } + + // Print project manager's ID + projectManagerID, err := db.GetUserId("projectManager") + if err != nil { + t.Error("GetUserId failed:", err) + } + fmt.Println("Project Manager's ID:", projectManagerID) + + // Sign the report with the project manager + err = db.SignWeeklyReport(report.ReportId, projectManagerID) + if err != nil { + t.Error("SignWeeklyReport failed:", err) + } + + // Retrieve the report again to check if it's signed + signedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) + if err != nil { + t.Error("GetWeeklyReport failed:", err) + } + + // Ensure the report is signed by the project manager + if *signedReport.SignedBy != projectManagerID { + t.Errorf("Expected SignedBy to be %d, got %d", projectManagerID, *signedReport.SignedBy) + } +} + +func TestSignWeeklyReportByNonProjectManager(t *testing.T) { + db, err := setupState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Add project manager + err = db.AddUser("projectManager", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add a regular user + err = db.AddUser("testuser", "password") + if err != nil { + t.Error("AddUser failed:", err) + } + + // Add project + err = db.AddProject("testproject", "description", "projectManager") + if err != nil { + t.Error("AddProject failed:", err) + } + + // Add the regular user as a member to the project + err = db.AddUserToProject("testuser", "testproject", "member") + if err != nil { + t.Error("AddUserToProject failed:", err) + } + + // Add a weekly report for the regular user + err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) + if err != nil { + t.Error("AddWeeklyReport failed:", err) + } + + // Retrieve the added report + report, err := db.GetWeeklyReport("testuser", "testproject", 1) + if err != nil { + t.Error("GetWeeklyReport failed:", err) + } + + anotherManagerID, err := db.GetUserId("projectManager") + if err != nil { + t.Error("GetUserId failed:", err) + } + + err = db.SignWeeklyReport(report.ReportId, anotherManagerID) + if err == nil { + t.Error("Expected SignWeeklyReport to fail with a project manager who is not in the project, but it didn't") + } +} diff --git a/backend/internal/types/WeeklyReport.go b/backend/internal/types/WeeklyReport.go index b704cc8..299395a 100644 --- a/backend/internal/types/WeeklyReport.go +++ b/backend/internal/types/WeeklyReport.go @@ -41,4 +41,6 @@ type WeeklyReport struct { StudyTime int `json:"studyTime" db:"study_time"` // Total time spent on testing TestingTime int `json:"testingTime" db:"testing_time"` + // The project manager who signed it + SignedBy *int `json:"signedBy" db:"signed_by"` } From 40caa2d158add3a64058d7567d7e9d75d88be143 Mon Sep 17 00:00:00 2001 From: dDogge <> Date: Sun, 17 Mar 2024 20:35:48 +0100 Subject: [PATCH 13/13] Changed name to a test in db_test.go --- backend/internal/database/db_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 1e8741d..09de45b 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -484,7 +484,7 @@ func TestSignWeeklyReport(t *testing.T) { } } -func TestSignWeeklyReportByNonProjectManager(t *testing.T) { +func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) { db, err := setupState() if err != nil { t.Error("setupState failed:", err)