Merge remote-tracking branch 'origin/dev' into gruppPP
This commit is contained in:
commit
99eb5f17b5
9 changed files with 333 additions and 5 deletions
|
@ -47,6 +47,8 @@ type Database interface {
|
|||
GetUserName(id int) (string, error)
|
||||
UnsignWeeklyReport(reportId int, projectManagerId int) error
|
||||
DeleteReport(reportID int) error
|
||||
ChangeProjectName(projectName string, newProjectName string) error
|
||||
ChangeUserPassword(username string, password string) error
|
||||
}
|
||||
|
||||
// This struct is a wrapper type that holds the database connection
|
||||
|
@ -670,3 +672,14 @@ func (d *Db) DeleteReport(reportID int) error {
|
|||
_, err := d.Exec("DELETE FROM weekly_reports WHERE report_id = ?", reportID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangeProjectName is a handler that changes the name of a project
|
||||
func (d *Db) ChangeProjectName(projectName string, newProjectName string) error {
|
||||
_, err := d.Exec("UPDATE projects SET name = ? WHERE name = ?", newProjectName, projectName)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Db) ChangeUserPassword(username string, password string) error {
|
||||
_, err := d.Exec("UPDATE users SET password = ? WHERE username = ?", password, username)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1092,3 +1092,53 @@ func TestDeleteReport(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestChangeProjectName(t *testing.T) {
|
||||
db, err := setupAdvancedState()
|
||||
if err != nil {
|
||||
t.Error("setupState failed:", err)
|
||||
}
|
||||
|
||||
// Promote user to Admin
|
||||
err = db.PromoteToAdmin("demouser")
|
||||
if err != nil {
|
||||
t.Error("PromoteToAdmin failed:", err)
|
||||
}
|
||||
|
||||
// Change project name
|
||||
err = db.ChangeProjectName("projecttest", "newprojectname")
|
||||
if err != nil {
|
||||
t.Error("ChangeProjectName failed:", err)
|
||||
}
|
||||
|
||||
// Check if the project name was changed
|
||||
projects, err := db.GetAllProjects()
|
||||
if err != nil {
|
||||
t.Error("GetAllProjects failed:", err)
|
||||
}
|
||||
if projects[0].Name != "newprojectname" {
|
||||
t.Error("ChangeProjectName failed: expected newprojectname, got", projects[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeUserPassword(t *testing.T) {
|
||||
db, err := setupState()
|
||||
if err != nil {
|
||||
t.Error("setupState failed:", err)
|
||||
}
|
||||
|
||||
// Add a user
|
||||
_ = db.AddUser("testuser", "password")
|
||||
|
||||
// Change user password
|
||||
err = db.ChangeUserPassword("testuser", "newpassword")
|
||||
if err != nil {
|
||||
t.Error("ChangeUserPassword failed:", err)
|
||||
}
|
||||
|
||||
// Check if the password was changed
|
||||
if !db.CheckUser("testuser", "newpassword") {
|
||||
t.Error("ChangeUserPassword failed: password not changed")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
43
backend/internal/handlers/projects/ChangeProjectName.go
Normal file
43
backend/internal/handlers/projects/ChangeProjectName.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package projects
|
||||
|
||||
import (
|
||||
db "ttime/internal/database"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// ChangeProjectName is a handler that changes the name of a project
|
||||
func ChangeProjectName(c *fiber.Ctx) error {
|
||||
|
||||
//check token and get username of current user
|
||||
user := c.Locals("user").(*jwt.Token)
|
||||
claims := user.Claims.(jwt.MapClaims)
|
||||
username := claims["name"].(string)
|
||||
|
||||
// Extract the necessary parameters from the request
|
||||
projectName := c.Params("projectName")
|
||||
newProjectName := c.Query("newProjectName")
|
||||
|
||||
// Check if user is site admin
|
||||
issiteadmin, err := db.GetDb(c).IsSiteAdmin(username)
|
||||
if err != nil {
|
||||
log.Warn("Error checking if siteadmin:", err)
|
||||
return c.Status(500).SendString(err.Error())
|
||||
} else if !issiteadmin {
|
||||
log.Warn("User is not siteadmin")
|
||||
return c.Status(401).SendString("User is not siteadmin")
|
||||
}
|
||||
|
||||
|
||||
// Perform the project name change
|
||||
err = db.GetDb(c).ChangeProjectName(projectName, newProjectName)
|
||||
if err != nil {
|
||||
log.Warn("Error changing project name:", err)
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
// Return a success message
|
||||
return c.Status(200).SendString("Project name changed successfully")
|
||||
}
|
|
@ -16,6 +16,7 @@ func GetStatistics(c *fiber.Ctx) error {
|
|||
|
||||
// Extract project name from query parameters
|
||||
projectName := c.Query("projectName")
|
||||
userNameParam := c.Query("userName")
|
||||
|
||||
log.Info(username, " trying to get statistics for project: ", projectName)
|
||||
|
||||
|
@ -24,18 +25,23 @@ func GetStatistics(c *fiber.Ctx) error {
|
|||
return c.Status(400).SendString("Missing project name")
|
||||
}
|
||||
|
||||
// If the user is not a project manager, they can't view statistics
|
||||
// Check if the user is a project manager
|
||||
pm, err := db.GetDb(c).IsProjectManager(username, projectName)
|
||||
if err != nil {
|
||||
log.Info("Error checking if user is project manager:", err)
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
if !pm {
|
||||
log.Info("Unauthorized access")
|
||||
// Bail if the user is not a PM or checking its own statistics
|
||||
if !pm && userNameParam != "" && userNameParam != username {
|
||||
log.Info("Unauthorized access for user: ", username, "trying to access project: ", projectName, "statistics for user: ", userNameParam)
|
||||
return c.Status(403).SendString("Unauthorized access")
|
||||
}
|
||||
|
||||
if pm && userNameParam != "" {
|
||||
username = userNameParam
|
||||
}
|
||||
|
||||
// Retrieve statistics for the project from the database
|
||||
statistics, err := db.GetDb(c).ReportStatistics(username, projectName)
|
||||
if err != nil {
|
||||
|
|
42
backend/internal/handlers/users/ChangeUserPassword.go
Normal file
42
backend/internal/handlers/users/ChangeUserPassword.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
db "ttime/internal/database"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// ChangeUserPassword is a handler that changes the password of a user
|
||||
func ChangeUserPassword(c *fiber.Ctx) error {
|
||||
|
||||
//Check token and get username of current user
|
||||
user := c.Locals("user").(*jwt.Token)
|
||||
claims := user.Claims.(jwt.MapClaims)
|
||||
admin := claims["name"].(string)
|
||||
|
||||
// Extract the necessary parameters from the request
|
||||
username := c.Params("username")
|
||||
newPassword := c.Query("newPassword")
|
||||
|
||||
// Check if user is site admin
|
||||
issiteadmin, err := db.GetDb(c).IsSiteAdmin(admin)
|
||||
if err != nil {
|
||||
log.Warn("Error checking if siteadmin:", err)
|
||||
return c.Status(500).SendString(err.Error())
|
||||
} else if !issiteadmin {
|
||||
log.Warn("User is not siteadmin")
|
||||
return c.Status(401).SendString("User is not siteadmin")
|
||||
}
|
||||
|
||||
// Perform the password change
|
||||
err = db.GetDb(c).ChangeUserPassword(username, newPassword)
|
||||
if err != nil {
|
||||
log.Warn("Error changing password:", err)
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
// Return a success message
|
||||
return c.Status(200).SendString("Password changed successfully")
|
||||
}
|
|
@ -110,6 +110,7 @@ func main() {
|
|||
api.Post("/promoteToAdmin", users.PromoteToAdmin)
|
||||
api.Put("/changeUserName", users.ChangeUserName)
|
||||
api.Delete("/userdelete/:username", users.UserDelete) // Perhaps just use POST to avoid headaches
|
||||
api.Put("/changeUserPassword/:username", users.ChangeUserPassword)
|
||||
|
||||
// All project related routes
|
||||
// projectGroup := api.Group("/project") // Not currently in use
|
||||
|
@ -125,6 +126,7 @@ func main() {
|
|||
api.Delete("/removeUserFromProject/:projectName", projects.RemoveUserFromProject)
|
||||
api.Delete("/removeProject/:projectName", projects.RemoveProject)
|
||||
api.Delete("/project/:projectID", projects.DeleteProject)
|
||||
api.Put("/ChangeProjectName/:projectName", projects.ChangeProjectName)
|
||||
|
||||
// All report related routes
|
||||
// reportGroup := api.Group("/report") // Not currently in use
|
||||
|
|
|
@ -269,7 +269,32 @@ interface API {
|
|||
getStatistics(
|
||||
projectName: string,
|
||||
token: string,
|
||||
userName?: string,
|
||||
): Promise<APIResponse<Statistics>>;
|
||||
|
||||
/**
|
||||
* Changes the name of a project
|
||||
* @param {string} projectName The name of the project
|
||||
* @param {string} newProjectName The new name of the project
|
||||
* @param {string} token The authentication token
|
||||
*/
|
||||
changeProjectName(
|
||||
projectName: string,
|
||||
newProjectName: string,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>>;
|
||||
|
||||
/**
|
||||
* Changes the password of a user
|
||||
* @param {string} username The username of the user
|
||||
* @param {string} newPassword The new password
|
||||
* @param {string} token The authentication token
|
||||
*/
|
||||
changeUserPassword(
|
||||
username: string,
|
||||
newPassword: string,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>>;
|
||||
}
|
||||
|
||||
/** An instance of the API */
|
||||
|
@ -981,10 +1006,11 @@ export const api: API = {
|
|||
async getStatistics(
|
||||
token: string,
|
||||
projectName: string,
|
||||
userName?: string,
|
||||
): Promise<APIResponse<Statistics>> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/getStatistics/?projectName=${projectName}`,
|
||||
`/api/getStatistics/?projectName=${projectName}&userName=${userName ?? ""}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
@ -1004,4 +1030,58 @@ export const api: API = {
|
|||
return { success: false, message: "Failed to get statistics" };
|
||||
}
|
||||
},
|
||||
|
||||
async changeProjectName(
|
||||
projectName: string,
|
||||
newProjectName: string,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/changeProjectName/${projectName}?newProjectName=${newProjectName}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return { success: false, message: "Failed to change project name" };
|
||||
} else {
|
||||
return { success: true, message: "Project name changed" };
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, message: "Failed to change project name" };
|
||||
}
|
||||
},
|
||||
|
||||
async changeUserPassword(
|
||||
username: string,
|
||||
newPassword: string,
|
||||
token: string,
|
||||
): Promise<APIResponse<string>> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/changePassword/${username}?newPassword=${newPassword}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return { success: false, message: "Failed to change password" };
|
||||
} else {
|
||||
return { success: true, message: "Password changed" };
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, message: "Failed to change password" };
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -37,6 +37,8 @@ promoteToPmPath = base_url + "/api/promoteToPm"
|
|||
unsignReportPath = base_url + "/api/unsignReport"
|
||||
deleteReportPath = base_url + "/api/deleteReport"
|
||||
getStatisticsPath = base_url + "/api/getStatistics"
|
||||
changeProjectNamePath = base_url + "/api/changeProjectName"
|
||||
changeUserPasswordPath = base_url + "/api/changeUserPassword"
|
||||
|
||||
debug_output = False
|
||||
|
||||
|
@ -170,4 +172,20 @@ def getStatistics(token: string, projectName: string):
|
|||
headers = {"Authorization": "Bearer " + token},
|
||||
params={"projectName": projectName}
|
||||
)
|
||||
return response.json()
|
||||
return response.json()
|
||||
|
||||
def changeProjectName(token: string, projectName: string, newProjectName: string):
|
||||
response = requests.put(
|
||||
changeProjectNamePath + "/" + projectName,
|
||||
headers = {"Authorization": "Bearer " + token},
|
||||
params={"newProjectName": newProjectName}
|
||||
)
|
||||
return response
|
||||
|
||||
def changeUserPassword(token: string, username: string, newPassword: string):
|
||||
response = requests.put(
|
||||
changeUserPasswordPath + "/" + username,
|
||||
headers = {"Authorization": "Bearer " + token},
|
||||
params={"newPassword": newPassword}
|
||||
)
|
||||
return response
|
|
@ -666,8 +666,82 @@ def test_get_statistics():
|
|||
assert stats["totalDevelopmentTime"] == 20, "Total development time is not correct"
|
||||
gprint("test_get_statistics successful")
|
||||
|
||||
def test_project_name_change():
|
||||
# Create admin
|
||||
admin_username = randomString()
|
||||
admin_password = randomString()
|
||||
|
||||
project_name = "project" + randomString()
|
||||
|
||||
token = register_and_login(admin_username, admin_password)
|
||||
|
||||
# Promote to admin
|
||||
response = requests.post(
|
||||
promoteToAdminPath,
|
||||
json={"username": admin_username},
|
||||
headers={"Authorization": "Bearer " + token},
|
||||
)
|
||||
|
||||
|
||||
response = create_project(token, project_name)
|
||||
assert response.status_code == 200, "Create project failed"
|
||||
|
||||
response = requests.get(
|
||||
getUserProjectsPath + "/" + admin_username,
|
||||
headers={"Authorization": "Bearer " + token},
|
||||
)
|
||||
|
||||
dprint(response.json())
|
||||
|
||||
new_project_name = "new project name " + randomString()
|
||||
dprint("Changing project name from ", project_name, " to ", new_project_name)
|
||||
response = changeProjectName(token, project_name, new_project_name)
|
||||
|
||||
response = requests.get(
|
||||
getUserProjectsPath + "/" + admin_username,
|
||||
headers={"Authorization": "Bearer " + token},
|
||||
)
|
||||
|
||||
dprint(response.json())
|
||||
|
||||
if (response.json()[0]["name"] != new_project_name):
|
||||
assert False, "Project name change failed"
|
||||
|
||||
|
||||
assert response.status_code == 200, "Project name change failed"
|
||||
gprint("test_projectNameChange successful")
|
||||
|
||||
def test_change_user_password():
|
||||
# Create admin
|
||||
admin_username = randomString()
|
||||
admin_password = randomString()
|
||||
|
||||
user = randomString()
|
||||
password = randomString()
|
||||
|
||||
token = register_and_login(admin_username, admin_password)
|
||||
|
||||
# Promote to admin
|
||||
response = requests.post(
|
||||
promoteToAdminPath,
|
||||
json={"username": admin_username},
|
||||
headers={"Authorization": "Bearer " + token},
|
||||
)
|
||||
|
||||
_ = register_and_login(user, password)
|
||||
|
||||
response = changeUserPassword(token, user, "new_password")
|
||||
assert response.status_code == 200, "Change user password failed"
|
||||
|
||||
response = login(user, "new_password")
|
||||
assert response.status_code == 200, "Login failed with new password"
|
||||
|
||||
gprint("test_change_user_password successful")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_change_user_password()
|
||||
test_project_name_change();
|
||||
test_delete_report()
|
||||
test_unsign_report()
|
||||
test_promote_to_manager()
|
||||
|
|
Loading…
Reference in a new issue