From bcdcbd5049043187a49534e0bac0169a6f76e9e3 Mon Sep 17 00:00:00 2001 From: bagas Date: Sat, 21 Sep 2024 11:26:25 +0700 Subject: [PATCH] Properly handle error and improve error messages --- handler/auth/google/callback/callback.go | 14 +++- handler/auth/google/setup/setup.go | 11 ++- handler/auth/totp/totp.go | 84 ++++----------------- handler/file/delete/delete.go | 3 + handler/file/download/download.go | 18 +---- handler/file/file.go | 7 +- handler/file/query/query.go | 6 +- handler/file/rename/rename.go | 8 +- handler/file/upload/upload.go | 9 ++- handler/file/visibility/visibility.go | 3 +- handler/forgotPassword/forgotPassword.go | 36 ++++----- handler/forgotPassword/verify/verify.go | 32 +++++++- handler/index/index.go | 1 + handler/logout/logout.go | 17 ++--- handler/signin/signin.go | 84 ++++----------------- handler/signup/signup.go | 11 ++- handler/signup/verify/verify.go | 6 +- handler/user/ResetPassword/ResetPassword.go | 25 +++++- handler/user/session/terminate/terminate.go | 19 ++++- handler/user/totp/setup.go | 17 ++++- handler/user/user.go | 6 +- service/service.go | 5 +- session/session.go | 5 -- types/types.go | 2 +- utils/utils.go | 63 ++++++++++++++-- 25 files changed, 258 insertions(+), 234 deletions(-) diff --git a/handler/auth/google/callback/callback.go b/handler/auth/google/callback/callback.go index 064affa..ed949b2 100644 --- a/handler/auth/google/callback/callback.go +++ b/handler/auth/google/callback/callback.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/fossyy/filekeeper/app" googleOauthSetupHandler "github.com/fossyy/filekeeper/handler/auth/google/setup" - signinHandler "github.com/fossyy/filekeeper/handler/signin" "github.com/fossyy/filekeeper/session" "github.com/fossyy/filekeeper/types" "github.com/fossyy/filekeeper/utils" @@ -49,7 +48,7 @@ func GET(w http.ResponseWriter, r *http.Request) { _, err := app.Server.Cache.GetCache(r.Context(), "CsrfTokens:"+r.URL.Query().Get("state")) if err != nil { if errors.Is(err, redis.Nil) { - w.WriteHeader(http.StatusUnauthorized) + http.Redirect(w, r, fmt.Sprintf("/signin?error=%s", "csrf_token_error"), http.StatusFound) return } w.WriteHeader(http.StatusInternalServerError) @@ -155,7 +154,7 @@ func GET(w http.ResponseWriter, r *http.Request) { } userAgent := r.Header.Get("User-Agent") - browserInfo, osInfo := signinHandler.ParseUserAgent(userAgent) + browserInfo, osInfo := utils.ParseUserAgent(userAgent) sessionInfo := session.SessionInfo{ SessionID: storeSession.ID, @@ -168,17 +167,24 @@ func GET(w http.ResponseWriter, r *http.Request) { } storeSession.Save(w) - session.AddSessionInfo(oauthUser.Email, &sessionInfo) + err = session.AddSessionInfo(oauthUser.Email, &sessionInfo) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } cookie, err := r.Cookie("redirect") if errors.Is(err, http.ErrNoCookie) { http.Redirect(w, r, "/", http.StatusSeeOther) return } + http.SetCookie(w, &http.Cookie{ Name: "redirect", MaxAge: -1, }) + http.Redirect(w, r, cookie.Value, http.StatusSeeOther) return } diff --git a/handler/auth/google/setup/setup.go b/handler/auth/google/setup/setup.go index f49ca72..44ee283 100644 --- a/handler/auth/google/setup/setup.go +++ b/handler/auth/google/setup/setup.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "github.com/fossyy/filekeeper/app" - signinHandler "github.com/fossyy/filekeeper/handler/signin" "github.com/fossyy/filekeeper/session" "github.com/fossyy/filekeeper/types" "github.com/fossyy/filekeeper/types/models" @@ -121,11 +120,12 @@ func POST(w http.ResponseWriter, r *http.Request) { }) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } userAgent := r.Header.Get("User-Agent") - browserInfo, osInfo := signinHandler.ParseUserAgent(userAgent) + browserInfo, osInfo := utils.ParseUserAgent(userAgent) sessionInfo := session.SessionInfo{ SessionID: storeSession.ID, @@ -138,7 +138,12 @@ func POST(w http.ResponseWriter, r *http.Request) { } storeSession.Save(w) - session.AddSessionInfo(unregisteredUser.Email, &sessionInfo) + err = session.AddSessionInfo(unregisteredUser.Email, &sessionInfo) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } http.Redirect(w, r, "/user", http.StatusSeeOther) return diff --git a/handler/auth/totp/totp.go b/handler/auth/totp/totp.go index 29cecc1..2508c48 100644 --- a/handler/auth/totp/totp.go +++ b/handler/auth/totp/totp.go @@ -2,13 +2,13 @@ package totpHandler import ( "errors" + "github.com/fossyy/filekeeper/app" "github.com/fossyy/filekeeper/session" "github.com/fossyy/filekeeper/types" "github.com/fossyy/filekeeper/utils" "github.com/fossyy/filekeeper/view/client/totp" "github.com/xlzd/gotp" "net/http" - "strings" "time" ) @@ -31,7 +31,12 @@ func GET(w http.ResponseWriter, r *http.Request) { } func POST(w http.ResponseWriter, r *http.Request) { - r.ParseForm() + err := r.ParseForm() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } code := r.Form.Get("code") _, user, key := session.GetSession(r) totp := gotp.NewDefaultTOTP(user.Totp) @@ -46,13 +51,12 @@ func POST(w http.ResponseWriter, r *http.Request) { }) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } - if err != nil { - return - } + userAgent := r.Header.Get("User-Agent") - browserInfo, osInfo := ParseUserAgent(userAgent) + browserInfo, osInfo := utils.ParseUserAgent(userAgent) sessionInfo := session.SessionInfo{ SessionID: storeSession.ID, @@ -65,7 +69,12 @@ func POST(w http.ResponseWriter, r *http.Request) { } storeSession.Save(w) - session.AddSessionInfo(user.Email, &sessionInfo) + err = session.AddSessionInfo(user.Email, &sessionInfo) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } cookie, err := r.Cookie("redirect") if errors.Is(err, http.ErrNoCookie) { @@ -91,64 +100,3 @@ func POST(w http.ResponseWriter, r *http.Request) { } } - -func ParseUserAgent(userAgent string) (map[string]string, map[string]string) { - browserInfo := make(map[string]string) - osInfo := make(map[string]string) - if strings.Contains(userAgent, "Firefox") { - browserInfo["browser"] = "Firefox" - parts := strings.Split(userAgent, "Firefox/") - if len(parts) > 1 { - version := strings.Split(parts[1], " ")[0] - browserInfo["version"] = version - } - } else if strings.Contains(userAgent, "Chrome") { - browserInfo["browser"] = "Chrome" - parts := strings.Split(userAgent, "Chrome/") - if len(parts) > 1 { - version := strings.Split(parts[1], " ")[0] - browserInfo["version"] = version - } - } else { - browserInfo["browser"] = "Unknown" - browserInfo["version"] = "Unknown" - } - - if strings.Contains(userAgent, "Windows") { - osInfo["os"] = "Windows" - parts := strings.Split(userAgent, "Windows ") - if len(parts) > 1 { - version := strings.Split(parts[1], ";")[0] - osInfo["version"] = version - } - } else if strings.Contains(userAgent, "Macintosh") { - osInfo["os"] = "Mac OS" - parts := strings.Split(userAgent, "Mac OS X ") - if len(parts) > 1 { - version := strings.Split(parts[1], ";")[0] - osInfo["version"] = version - } - } else if strings.Contains(userAgent, "Linux") { - osInfo["os"] = "Linux" - osInfo["version"] = "Unknown" - } else if strings.Contains(userAgent, "Android") { - osInfo["os"] = "Android" - parts := strings.Split(userAgent, "Android ") - if len(parts) > 1 { - version := strings.Split(parts[1], ";")[0] - osInfo["version"] = version - } - } else if strings.Contains(userAgent, "iPhone") || strings.Contains(userAgent, "iPad") || strings.Contains(userAgent, "iPod") { - osInfo["os"] = "iOS" - parts := strings.Split(userAgent, "OS ") - if len(parts) > 1 { - version := strings.Split(parts[1], " ")[0] - osInfo["version"] = version - } - } else { - osInfo["os"] = "Unknown" - osInfo["version"] = "Unknown" - } - - return browserInfo, osInfo -} diff --git a/handler/file/delete/delete.go b/handler/file/delete/delete.go index 076077b..82ececd 100644 --- a/handler/file/delete/delete.go +++ b/handler/file/delete/delete.go @@ -15,6 +15,7 @@ func DELETE(w http.ResponseWriter, r *http.Request) { file, err := app.Server.Database.GetFile(fileID) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -26,12 +27,14 @@ func DELETE(w http.ResponseWriter, r *http.Request) { err = app.Server.Database.DeleteFile(fileID) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } err = app.Server.Storage.Delete(r.Context(), fmt.Sprintf("%s/%s", file.OwnerID.String(), file.ID.String())) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } diff --git a/handler/file/download/download.go b/handler/file/download/download.go index 5bb5233..9aa073d 100644 --- a/handler/file/download/download.go +++ b/handler/file/download/download.go @@ -15,7 +15,7 @@ func GET(w http.ResponseWriter, r *http.Request) { fileID := r.PathValue("id") file, err := app.Server.Database.GetFile(fileID) if err != nil { - http.Error(w, "Internal Server Error", http.StatusInternalServerError) + w.WriteHeader(http.StatusInternalServerError) app.Server.Logger.Error(err.Error()) return } @@ -31,17 +31,6 @@ func GET(w http.ResponseWriter, r *http.Request) { } } - //uploadDir := "uploads" - //currentDir, _ := os.Getwd() - //basePath := filepath.Join(currentDir, uploadDir) - //saveFolder := filepath.Join(basePath, file.OwnerID.String(), file.ID.String()) - // - //if filepath.Dir(saveFolder) != filepath.Join(basePath, file.OwnerID.String()) { - // http.Error(w, "Invalid Path", http.StatusInternalServerError) - // app.Server.Logger.Error("invalid path") - // return - //} - rangeHeader := r.Header.Get("Range") if rangeHeader != "" { rangeParts := strings.Split(strings.TrimPrefix(rangeHeader, "bytes="), "-") @@ -94,7 +83,7 @@ func sendFileChunk(w http.ResponseWriter, file *models.File, start, end int64) { chunkKey := fmt.Sprintf("%s/%s/chunk_%d", file.OwnerID.String(), file.ID.String(), i) chunkData, err := app.Server.Storage.Get(context.TODO(), chunkKey) if err != nil { - http.Error(w, fmt.Sprintf("Error retrieving chunk: %v", err), http.StatusInternalServerError) + w.WriteHeader(http.StatusInternalServerError) app.Server.Logger.Error(err.Error()) return } @@ -112,7 +101,7 @@ func sendFileChunk(w http.ResponseWriter, file *models.File, start, end int64) { _, err = w.Write(dataToSend) if err != nil { - http.Error(w, fmt.Sprintf("Error writing chunk: %v", err), http.StatusInternalServerError) + w.WriteHeader(http.StatusInternalServerError) app.Server.Logger.Error(err.Error()) return } @@ -120,7 +109,6 @@ func sendFileChunk(w http.ResponseWriter, file *models.File, start, end int64) { if i == int64(file.TotalChunk)-1 { err := app.Server.Database.IncrementDownloadCount(file.ID.String()) if err != nil { - http.Error(w, fmt.Sprintf("Error updating download count: %v", err), http.StatusInternalServerError) app.Server.Logger.Error(err.Error()) return } diff --git a/handler/file/file.go b/handler/file/file.go index f92856e..29e1b60 100644 --- a/handler/file/file.go +++ b/handler/file/file.go @@ -15,8 +15,8 @@ func GET(w http.ResponseWriter, r *http.Request) { userSession := r.Context().Value("user").(types.User) files, err := app.Server.Database.GetFiles(userSession.UserID.String(), "", types.All) if err != nil { - app.Server.Logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } var filesData []types.FileData @@ -46,13 +46,14 @@ func GET(w http.ResponseWriter, r *http.Request) { allowance, err := app.Server.Database.GetAllowance(userSession.UserID) if err != nil { - app.Server.Logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } usage, err := app.Server.Service.GetUserStorageUsage(userSession.UserID.String()) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -71,8 +72,8 @@ func GET(w http.ResponseWriter, r *http.Request) { err = component.Render(r.Context(), w) if err != nil { - fmt.Println(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } } diff --git a/handler/file/query/query.go b/handler/file/query/query.go index 8e3d098..e45e291 100644 --- a/handler/file/query/query.go +++ b/handler/file/query/query.go @@ -26,8 +26,8 @@ func GET(w http.ResponseWriter, r *http.Request) { files, err := app.Server.Database.GetFiles(userSession.UserID.String(), query, fileStatus) if err != nil { - app.Server.Logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -38,8 +38,8 @@ func GET(w http.ResponseWriter, r *http.Request) { existingChunks, err := app.Server.Storage.ListObjects(r.Context(), prefix) if err != nil { - app.Server.Logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -61,10 +61,12 @@ func GET(w http.ResponseWriter, r *http.Request) { err := component.Render(r.Context(), w) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } return } w.WriteHeader(http.StatusForbidden) + return } diff --git a/handler/file/rename/rename.go b/handler/file/rename/rename.go index d811ae6..63469db 100644 --- a/handler/file/rename/rename.go +++ b/handler/file/rename/rename.go @@ -18,6 +18,7 @@ func PATCH(w http.ResponseWriter, r *http.Request) { file, err := app.Server.Database.GetFile(fileID) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -34,6 +35,7 @@ func PATCH(w http.ResponseWriter, r *http.Request) { newFile, err := app.Server.Database.RenameFile(fileID, newName) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -41,8 +43,8 @@ func PATCH(w http.ResponseWriter, r *http.Request) { existingChunks, err := app.Server.Storage.ListObjects(r.Context(), prefix) if err != nil { - app.Server.Logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -62,8 +64,8 @@ func PATCH(w http.ResponseWriter, r *http.Request) { err = component.Render(r.Context(), w) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } - fileView.JustFile(fileData) - w.WriteHeader(http.StatusOK) + return } diff --git a/handler/file/upload/upload.go b/handler/file/upload/upload.go index 544c29d..d947175 100644 --- a/handler/file/upload/upload.go +++ b/handler/file/upload/upload.go @@ -12,6 +12,7 @@ func POST(w http.ResponseWriter, r *http.Request) { fileID := r.PathValue("id") if err := r.ParseMultipartForm(32 << 20); err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -25,28 +26,30 @@ func POST(w http.ResponseWriter, r *http.Request) { rawIndex := r.FormValue("index") index, err := strconv.Atoi(rawIndex) if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } fileByte, _, err := r.FormFile("chunk") if err != nil { - app.Server.Logger.Error("error getting upload info: " + err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error("error getting upload info: " + err.Error()) return } defer fileByte.Close() buffer, err := io.ReadAll(fileByte) if err != nil { - app.Server.Logger.Error("error copying byte to file dst: " + err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error("error copying byte to file dst: " + err.Error()) return } err = app.Server.Storage.Add(r.Context(), fmt.Sprintf("%s/%s/chunk_%d", file.OwnerID.String(), file.ID.String(), index), buffer) if err != nil { - app.Server.Logger.Error("error copying byte to file dst: " + err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error("error copying byte to file dst: " + err.Error()) return } diff --git a/handler/file/visibility/visibility.go b/handler/file/visibility/visibility.go index 903fc86..648e2fb 100644 --- a/handler/file/visibility/visibility.go +++ b/handler/file/visibility/visibility.go @@ -36,8 +36,8 @@ func PUT(w http.ResponseWriter, r *http.Request) { existingChunks, err := app.Server.Storage.ListObjects(r.Context(), prefix) if err != nil { - app.Server.Logger.Error(err.Error()) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -55,6 +55,7 @@ func PUT(w http.ResponseWriter, r *http.Request) { err = component.Render(r.Context(), w) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } } diff --git a/handler/forgotPassword/forgotPassword.go b/handler/forgotPassword/forgotPassword.go index e3eb791..1829a80 100644 --- a/handler/forgotPassword/forgotPassword.go +++ b/handler/forgotPassword/forgotPassword.go @@ -12,7 +12,6 @@ import ( "github.com/google/uuid" "github.com/redis/go-redis/v9" "net/http" - "sync" "time" "github.com/fossyy/filekeeper/types" @@ -22,10 +21,8 @@ import ( ) type ForgotPassword struct { - User *models.User - Code string - mu sync.Mutex - CreateTime time.Time + User *models.User + Code string } func GET(w http.ResponseWriter, r *http.Request) { @@ -39,6 +36,7 @@ func GET(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } + return } func POST(w http.ResponseWriter, r *http.Request) { @@ -52,17 +50,22 @@ func POST(w http.ResponseWriter, r *http.Request) { emailForm := r.Form.Get("email") user, err := app.Server.Service.GetUser(r.Context(), emailForm) - if errors.Is(err, gorm.ErrRecordNotFound) { - component := forgotPasswordView.Main("Filekeeper - Forgot Password Page", types.Message{ - Code: 0, - Message: "Unexpected error has occurred", - }) - err := component.Render(r.Context(), w) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - app.Server.Logger.Error(err.Error()) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + component := forgotPasswordView.Main("Filekeeper - Forgot Password Page", types.Message{ + Code: 0, + Message: "Unexpected error has occurred", + }) + err := component.Render(r.Context(), w) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } return } + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -99,9 +102,8 @@ func verifyForgot(user *models.User) error { if errors.Is(err, redis.Nil) { code = utils.GenerateRandomString(64) userData = &ForgotPassword{ - User: user, - Code: code, - CreateTime: time.Now(), + User: user, + Code: code, } newForgotUser, err := json.Marshal(userData) diff --git a/handler/forgotPassword/verify/verify.go b/handler/forgotPassword/verify/verify.go index c329deb..8a0e937 100644 --- a/handler/forgotPassword/verify/verify.go +++ b/handler/forgotPassword/verify/verify.go @@ -30,6 +30,7 @@ func GET(w http.ResponseWriter, r *http.Request) { return } w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -43,6 +44,7 @@ func GET(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } + return } func POST(w http.ResponseWriter, r *http.Request) { @@ -58,6 +60,7 @@ func POST(w http.ResponseWriter, r *http.Request) { err = json.Unmarshal([]byte(data), &userData) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -98,12 +101,33 @@ func POST(w http.ResponseWriter, r *http.Request) { return } - app.Server.Cache.DeleteCache(r.Context(), "ForgotPasswordCode:"+userData.User.Email) - app.Server.Cache.DeleteCache(r.Context(), "ForgotPassword:"+code) + err = app.Server.Cache.DeleteCache(r.Context(), "ForgotPasswordCode:"+userData.User.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } - session.RemoveAllSessions(userData.User.Email) + err = app.Server.Cache.DeleteCache(r.Context(), "ForgotPassword:"+code) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } - app.Server.Service.DeleteUser(userData.User.Email) + err = session.RemoveAllSessions(userData.User.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } + + err = app.Server.Service.DeleteUser(userData.User.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } component := forgotPasswordView.ChangeSuccess("Filekeeper - Forgot Password Page") err = component.Render(r.Context(), w) diff --git a/handler/index/index.go b/handler/index/index.go index 0a16ef2..67004db 100644 --- a/handler/index/index.go +++ b/handler/index/index.go @@ -16,4 +16,5 @@ func GET(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } + return } diff --git a/handler/logout/logout.go b/handler/logout/logout.go index 19d6dc8..6083729 100644 --- a/handler/logout/logout.go +++ b/handler/logout/logout.go @@ -1,7 +1,6 @@ package logoutHandler import ( - "errors" "github.com/fossyy/filekeeper/app" "github.com/fossyy/filekeeper/types" "net/http" @@ -14,25 +13,23 @@ func GET(w http.ResponseWriter, r *http.Request) { userSession := r.Context().Value("user").(types.User) cookie, err := r.Cookie("Session") if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } storeSession := session.Get(cookie.Value) - if err != nil { - if errors.Is(err, &session.SessionNotFoundError{}) { - storeSession.Destroy(w) - } - w.WriteHeader(http.StatusInternalServerError) - return - } err = storeSession.Delete() if err != nil { - app.Server.Logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } + err = session.RemoveSessionInfo(userSession.Email, cookie.Value) if err != nil { - app.Server.Logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } diff --git a/handler/signin/signin.go b/handler/signin/signin.go index fba76c1..c7fb9ff 100644 --- a/handler/signin/signin.go +++ b/handler/signin/signin.go @@ -9,15 +9,10 @@ import ( "github.com/fossyy/filekeeper/utils" "github.com/fossyy/filekeeper/view/client/signin" "net/http" - "strings" ) var errorMessages = make(map[string]string) -func init() { - -} - func init() { errorMessages = map[string]string{ "redirect_uri_mismatch": "The redirect URI provided does not match the one registered with our service. Please contact the administrator for assistance.", @@ -36,7 +31,7 @@ func init() { "login_required": "You need to log in again to proceed. Please try logging in again.", "account_selection_required": "Please select an account to proceed with the request.", "consent_required": "Consent is required to proceed. Please provide consent to continue.", - "csrf_token_error": "The CSRF token is missing or invalid. Please refresh the page and try again.", + "csrf_token_error": "The CSRF token is missing or invalid. Please try again.", "suspicious_session": "We've detected unusual activity on your account. Please log in again to confirm it's you.", } } @@ -49,7 +44,7 @@ func GET(w http.ResponseWriter, r *http.Request) { message = "Unknown error occurred. Please contact support at bagas@fossy.my.id for assistance." } - component = signinView.Main("Sign in Page", types.Message{ + component = signinView.Main("Filekeeper - Sign in Page", types.Message{ Code: 0, Message: message, }) @@ -66,6 +61,7 @@ func GET(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } + return } func POST(w http.ResponseWriter, r *http.Request) { @@ -95,7 +91,6 @@ func POST(w http.ResponseWriter, r *http.Request) { if email == userData.Email && utils.CheckPasswordHash(password, userData.Password) { if userData.Totp != "" { - storeSession, err := session.Create(types.User{ UserID: userData.UserID, Email: email, @@ -105,6 +100,7 @@ func POST(w http.ResponseWriter, r *http.Request) { }) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } storeSession.Save(w) @@ -120,10 +116,11 @@ func POST(w http.ResponseWriter, r *http.Request) { }) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } userAgent := r.Header.Get("User-Agent") - browserInfo, osInfo := ParseUserAgent(userAgent) + browserInfo, osInfo := utils.ParseUserAgent(userAgent) sessionInfo := session.SessionInfo{ SessionID: storeSession.ID, @@ -136,7 +133,12 @@ func POST(w http.ResponseWriter, r *http.Request) { } storeSession.Save(w) - session.AddSessionInfo(email, &sessionInfo) + err = session.AddSessionInfo(email, &sessionInfo) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } cookie, err := r.Cookie("redirect") if errors.Is(err, http.ErrNoCookie) { @@ -160,65 +162,5 @@ func POST(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } -} - -func ParseUserAgent(userAgent string) (map[string]string, map[string]string) { - browserInfo := make(map[string]string) - osInfo := make(map[string]string) - if strings.Contains(userAgent, "Firefox") { - browserInfo["browser"] = "Firefox" - parts := strings.Split(userAgent, "Firefox/") - if len(parts) > 1 { - version := strings.Split(parts[1], " ")[0] - browserInfo["version"] = version - } - } else if strings.Contains(userAgent, "Chrome") { - browserInfo["browser"] = "Chrome" - parts := strings.Split(userAgent, "Chrome/") - if len(parts) > 1 { - version := strings.Split(parts[1], " ")[0] - browserInfo["version"] = version - } - } else { - browserInfo["browser"] = "Unknown" - browserInfo["version"] = "Unknown" - } - - if strings.Contains(userAgent, "Windows") { - osInfo["os"] = "Windows" - parts := strings.Split(userAgent, "Windows ") - if len(parts) > 1 { - version := strings.Split(parts[1], ";")[0] - osInfo["version"] = version - } - } else if strings.Contains(userAgent, "Macintosh") { - osInfo["os"] = "Mac OS" - parts := strings.Split(userAgent, "Mac OS X ") - if len(parts) > 1 { - version := strings.Split(parts[1], ";")[0] - osInfo["version"] = version - } - } else if strings.Contains(userAgent, "Linux") { - osInfo["os"] = "Linux" - osInfo["version"] = "Unknown" - } else if strings.Contains(userAgent, "Android") { - osInfo["os"] = "Android" - parts := strings.Split(userAgent, "Android ") - if len(parts) > 1 { - version := strings.Split(parts[1], ";")[0] - osInfo["version"] = version - } - } else if strings.Contains(userAgent, "iPhone") || strings.Contains(userAgent, "iPad") || strings.Contains(userAgent, "iPod") { - osInfo["os"] = "iOS" - parts := strings.Split(userAgent, "OS ") - if len(parts) > 1 { - version := strings.Split(parts[1], " ")[0] - osInfo["version"] = version - } - } else { - osInfo["os"] = "Unknown" - osInfo["version"] = "Unknown" - } - - return browserInfo, osInfo + return } diff --git a/handler/signup/signup.go b/handler/signup/signup.go index eb13beb..c603b2b 100644 --- a/handler/signup/signup.go +++ b/handler/signup/signup.go @@ -20,9 +20,8 @@ import ( ) type UnverifiedUser struct { - User *models.User - Code string - CreateTime time.Time + User *models.User + Code string } func GET(w http.ResponseWriter, r *http.Request) { @@ -36,6 +35,7 @@ func GET(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } + return } func POST(w http.ResponseWriter, r *http.Request) { @@ -123,9 +123,8 @@ func verifyEmail(user *models.User) error { } unverifiedUser := UnverifiedUser{ - User: user, - Code: code, - CreateTime: time.Now(), + User: user, + Code: code, } newUnverifiedUser, err := json.Marshal(unverifiedUser) if err != nil { diff --git a/handler/signup/verify/verify.go b/handler/signup/verify/verify.go index 7eea7c7..a155cd4 100644 --- a/handler/signup/verify/verify.go +++ b/handler/signup/verify/verify.go @@ -15,7 +15,6 @@ import ( func GET(w http.ResponseWriter, r *http.Request) { code := r.PathValue("code") - userDataStr, err := app.Server.Cache.GetCache(context.Background(), "UnverifiedUser:"+code) if err != nil { if errors.Is(err, redis.Nil) { @@ -53,11 +52,15 @@ func GET(w http.ResponseWriter, r *http.Request) { err = app.Server.Cache.DeleteCache(context.Background(), "UnverifiedUser:"+code) if err != nil { app.Server.Logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return } err = app.Server.Cache.DeleteCache(context.Background(), "VerificationCode:"+unverifiedUser.User.Email) if err != nil { app.Server.Logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return } component := signupView.VerifySuccess("Filekeeper - Verify Page") @@ -67,4 +70,5 @@ func GET(w http.ResponseWriter, r *http.Request) { app.Server.Logger.Error(err.Error()) return } + return } diff --git a/handler/user/ResetPassword/ResetPassword.go b/handler/user/ResetPassword/ResetPassword.go index 5d76aa7..7fbec6a 100644 --- a/handler/user/ResetPassword/ResetPassword.go +++ b/handler/user/ResetPassword/ResetPassword.go @@ -9,19 +9,26 @@ import ( ) func POST(w http.ResponseWriter, r *http.Request) { - r.ParseForm() + err := r.ParseForm() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } userSession := r.Context().Value("user").(types.User) currentPassword := r.Form.Get("currentPassword") password := r.Form.Get("password") user, err := app.Server.Service.GetUser(r.Context(), userSession.Email) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } hashPassword, err := utils.HashPassword(password) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -33,11 +40,23 @@ func POST(w http.ResponseWriter, r *http.Request) { err = app.Server.Database.UpdateUserPassword(user.Email, hashPassword) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } - session.RemoveAllSessions(userSession.Email) - app.Server.Service.DeleteUser(userSession.Email) + err = session.RemoveAllSessions(userSession.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } + + err = app.Server.Service.DeleteUser(userSession.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } http.Redirect(w, r, "/signin", http.StatusSeeOther) return diff --git a/handler/user/session/terminate/terminate.go b/handler/user/session/terminate/terminate.go index bd2b3e9..66d2d04 100644 --- a/handler/user/session/terminate/terminate.go +++ b/handler/user/session/terminate/terminate.go @@ -1,6 +1,7 @@ package userSessionTerminateHandler import ( + "github.com/fossyy/filekeeper/app" "github.com/fossyy/filekeeper/session" "github.com/fossyy/filekeeper/types" "github.com/fossyy/filekeeper/view/client/user" @@ -16,11 +17,24 @@ func DELETE(w http.ResponseWriter, r *http.Request) { return } - otherSession.Delete() - session.RemoveSessionInfo(mySession.Email, otherSession.ID) + err := otherSession.Delete() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } + + err = session.RemoveSessionInfo(mySession.Email, otherSession.ID) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } + sessions, err := session.GetSessions(mySession.Email) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } component := userView.SessionTable(sessions) @@ -30,4 +44,5 @@ func DELETE(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } + return } diff --git a/handler/user/totp/setup.go b/handler/user/totp/setup.go index e20b1ba..5278bd6 100644 --- a/handler/user/totp/setup.go +++ b/handler/user/totp/setup.go @@ -37,8 +37,8 @@ func GET(w http.ResponseWriter, r *http.Request) { uri := totp.ProvisioningUri(userSession.Email, "filekeeper") base64Str, err := generateQRCode(uri) if err != nil { - fmt.Printf("%v\n", err) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -56,6 +56,7 @@ func GET(w http.ResponseWriter, r *http.Request) { } if err := component.Render(r.Context(), w); err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } } @@ -63,6 +64,7 @@ func GET(w http.ResponseWriter, r *http.Request) { func POST(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } @@ -74,17 +76,23 @@ func POST(w http.ResponseWriter, r *http.Request) { base64Str, err := generateQRCode(uri) if err != nil { - fmt.Printf("%v\n", err) w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } var component templ.Component if totp.Verify(code, time.Now().Unix()) { if err := app.Server.Database.InitializeTotp(userSession.Email, secret); err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } + err := app.Server.Service.DeleteUser(userSession.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } - app.Server.Service.DeleteUser(userSession.Email) if r.Header.Get("hx-request") == "true" { component = userTotpSetupView.MainContent("Filekeeper - 2FA Setup Page", base64Str, secret, userSession, types.Message{ Code: 1, @@ -99,6 +107,7 @@ func POST(w http.ResponseWriter, r *http.Request) { if err := component.Render(r.Context(), w); err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } return @@ -116,7 +125,9 @@ func POST(w http.ResponseWriter, r *http.Request) { } if err := component.Render(r.Context(), w); err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } + return } } diff --git a/handler/user/user.go b/handler/user/user.go index bf7c306..6fb050d 100644 --- a/handler/user/user.go +++ b/handler/user/user.go @@ -55,7 +55,8 @@ func GET(w http.ResponseWriter, r *http.Request) { if r.Header.Get("upgrade") == "websocket" { upgrade, err := upgrader.Upgrade(w, r, nil) if err != nil { - panic(err) + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } handlerWS(upgrade, userSession) @@ -64,18 +65,21 @@ func GET(w http.ResponseWriter, r *http.Request) { sessions, err := session.GetSessions(userSession.Email) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } allowance, err := app.Server.Database.GetAllowance(userSession.UserID) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } usage, err := app.Server.Service.GetUserStorageUsage(userSession.UserID.String()) if err != nil { w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) return } diff --git a/service/service.go b/service/service.go index 73e96ad..282fbf3 100644 --- a/service/service.go +++ b/service/service.go @@ -59,11 +59,12 @@ func (r *Service) GetUser(ctx context.Context, email string) (*models.User, erro return &user, nil } -func (r *Service) DeleteUser(email string) { +func (r *Service) DeleteUser(email string) error { err := r.cache.DeleteCache(context.Background(), "UserCache:"+email) if err != nil { - return + return err } + return nil } func (r *Service) GetUserStorageUsage(ownerID string) (uint64, error) { diff --git a/session/session.go b/session/session.go index 45d385f..7803bf8 100644 --- a/session/session.go +++ b/session/session.go @@ -31,7 +31,6 @@ type SessionInfo struct { } type UserStatus string -type SessionNotFoundError struct{} const ( Authorized UserStatus = "authorized" @@ -40,10 +39,6 @@ const ( Suspicious UserStatus = "suspicious" ) -func (e *SessionNotFoundError) Error() string { - return "session not found" -} - func Get(id string) *Session { return &Session{ID: id} } diff --git a/types/types.go b/types/types.go index aed2357..0199028 100644 --- a/types/types.go +++ b/types/types.go @@ -88,7 +88,7 @@ type CachingServer interface { type Services interface { GetUser(ctx context.Context, email string) (*models.User, error) - DeleteUser(email string) + DeleteUser(email string) error GetFile(id string) (*models.File, error) GetUserFile(name, ownerID string) (*FileWithDetail, error) GetUserStorageUsage(ownerID string) (uint64, error) diff --git a/utils/utils.go b/utils/utils.go index ad60e52..95a1b47 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -145,12 +145,63 @@ func GenerateCSRFToken() (string, error) { return csrfToken, nil } -func SanitizeFilename(filename string) string { - invalidChars := []string{"\\", "/", ":", "*", "?", "\"", "<", ">", "|"} - - for _, char := range invalidChars { - filename = strings.ReplaceAll(filename, char, "_") +func ParseUserAgent(userAgent string) (map[string]string, map[string]string) { + browserInfo := make(map[string]string) + osInfo := make(map[string]string) + if strings.Contains(userAgent, "Firefox") { + browserInfo["browser"] = "Firefox" + parts := strings.Split(userAgent, "Firefox/") + if len(parts) > 1 { + version := strings.Split(parts[1], " ")[0] + browserInfo["version"] = version + } + } else if strings.Contains(userAgent, "Chrome") { + browserInfo["browser"] = "Chrome" + parts := strings.Split(userAgent, "Chrome/") + if len(parts) > 1 { + version := strings.Split(parts[1], " ")[0] + browserInfo["version"] = version + } + } else { + browserInfo["browser"] = "Unknown" + browserInfo["version"] = "Unknown" } - return filename + if strings.Contains(userAgent, "Windows") { + osInfo["os"] = "Windows" + parts := strings.Split(userAgent, "Windows ") + if len(parts) > 1 { + version := strings.Split(parts[1], ";")[0] + osInfo["version"] = version + } + } else if strings.Contains(userAgent, "Macintosh") { + osInfo["os"] = "Mac OS" + parts := strings.Split(userAgent, "Mac OS X ") + if len(parts) > 1 { + version := strings.Split(parts[1], ";")[0] + osInfo["version"] = version + } + } else if strings.Contains(userAgent, "Linux") { + osInfo["os"] = "Linux" + osInfo["version"] = "Unknown" + } else if strings.Contains(userAgent, "Android") { + osInfo["os"] = "Android" + parts := strings.Split(userAgent, "Android ") + if len(parts) > 1 { + version := strings.Split(parts[1], ";")[0] + osInfo["version"] = version + } + } else if strings.Contains(userAgent, "iPhone") || strings.Contains(userAgent, "iPad") || strings.Contains(userAgent, "iPod") { + osInfo["os"] = "iOS" + parts := strings.Split(userAgent, "OS ") + if len(parts) > 1 { + version := strings.Split(parts[1], " ")[0] + osInfo["version"] = version + } + } else { + osInfo["os"] = "Unknown" + osInfo["version"] = "Unknown" + } + + return browserInfo, osInfo }