diff --git a/internal/webserver/Webserver.go b/internal/webserver/Webserver.go index 6014b57..c532c70 100644 --- a/internal/webserver/Webserver.go +++ b/internal/webserver/Webserver.go @@ -355,10 +355,9 @@ func showLogin(w http.ResponseWriter, r *http.Request) { pw := r.Form.Get("password") failedLogin := false if pw != "" && user != "" { - if authentication.IsCorrectUsernameAndPassword(user, pw) { - isOauth := configuration.Get().Authentication.Method == authentication.OAuth2 - interval := configuration.Get().Authentication.OAuthRecheckInterval - sessionmanager.CreateSession(w, isOauth, interval) + retrievedUser, validCredentials := authentication.IsCorrectUsernameAndPassword(user, pw) + if validCredentials { + sessionmanager.CreateSession(w, false, 0, retrievedUser.Id) redirect(w, "admin") return } diff --git a/internal/webserver/api/Api.go b/internal/webserver/api/Api.go index 85b7ff2..f8e9d5c 100644 --- a/internal/webserver/api/Api.go +++ b/internal/webserver/api/Api.go @@ -64,9 +64,9 @@ func Process(w http.ResponseWriter, r *http.Request, maxMemory int) { case "/user/create": addUser(w, request) case "/user/changeRank": - changeUserRank(w, request) + changeUserRank(w, request, user) case "/user/modify": - modifyUserPermission(w, request) + modifyUserPermission(w, request, user) case "/user/delete": deleteUser(w, request, user) default: @@ -570,15 +570,19 @@ func outputFileInfo(w http.ResponseWriter, file models.File) { _, _ = w.Write(result) } -func modifyUserPermission(w http.ResponseWriter, request apiRequest) { - user, ok := isValidUserForEditing(w, request) +func modifyUserPermission(w http.ResponseWriter, request apiRequest, user models.User) { + userEdit, ok := isValidUserForEditing(w, request) if !ok { return } - if user.UserLevel == models.UserLevelSuperAdmin { + if userEdit.UserLevel == models.UserLevelSuperAdmin { sendError(w, http.StatusBadRequest, "Cannot modify super admin") return } + if user.Id == userEdit.Id { + sendError(w, http.StatusBadRequest, "Cannot modify yourself") + return + } reqPermission := request.usermodInfo.permission addPerm := request.usermodInfo.grantPermission validPermissions := []uint16{models.UserPermReplaceUploads, @@ -592,44 +596,48 @@ func modifyUserPermission(w http.ResponseWriter, request apiRequest) { } if addPerm { - if !user.HasPermission(reqPermission) { - user.SetPermission(reqPermission) - database.SaveUser(user, false) - updateApiKeyPermsOnUserPermChange(user.Id, reqPermission, true) + if !userEdit.HasPermission(reqPermission) { + userEdit.SetPermission(reqPermission) + database.SaveUser(userEdit, false) + updateApiKeyPermsOnUserPermChange(userEdit.Id, reqPermission, true) } return } - if user.HasPermission(reqPermission) { - user.RemovePermission(reqPermission) - database.SaveUser(user, false) - updateApiKeyPermsOnUserPermChange(user.Id, reqPermission, false) + if userEdit.HasPermission(reqPermission) { + userEdit.RemovePermission(reqPermission) + database.SaveUser(userEdit, false) + updateApiKeyPermsOnUserPermChange(userEdit.Id, reqPermission, false) } } -func changeUserRank(w http.ResponseWriter, request apiRequest) { - user, ok := isValidUserForEditing(w, request) +func changeUserRank(w http.ResponseWriter, request apiRequest, user models.User) { + userEdit, ok := isValidUserForEditing(w, request) if !ok { return } - if user.UserLevel == models.UserLevelSuperAdmin { + if user.Id == userEdit.Id { + sendError(w, http.StatusBadRequest, "Cannot modify yourself") + return + } + if userEdit.UserLevel == models.UserLevelSuperAdmin { sendError(w, http.StatusBadRequest, "Cannot modify super admin") return } switch request.usermodInfo.newRank { case "ADMIN": - user.UserLevel = models.UserLevelAdmin - user.Permissions = models.UserPermissionAll - updateApiKeyPermsOnUserPermChange(user.Id, models.UserPermReplaceUploads, true) - updateApiKeyPermsOnUserPermChange(user.Id, models.UserPermManageUsers, true) + userEdit.UserLevel = models.UserLevelAdmin + userEdit.Permissions = models.UserPermissionAll + updateApiKeyPermsOnUserPermChange(userEdit.Id, models.UserPermReplaceUploads, true) + updateApiKeyPermsOnUserPermChange(userEdit.Id, models.UserPermManageUsers, true) case "USER": - user.UserLevel = models.UserLevelUser - user.Permissions = models.UserPermissionNone - updateApiKeyPermsOnUserPermChange(user.Id, models.UserPermReplaceUploads, false) - updateApiKeyPermsOnUserPermChange(user.Id, models.UserPermManageUsers, false) + userEdit.UserLevel = models.UserLevelUser + userEdit.Permissions = models.UserPermissionNone + updateApiKeyPermsOnUserPermChange(userEdit.Id, models.UserPermReplaceUploads, false) + updateApiKeyPermsOnUserPermChange(userEdit.Id, models.UserPermManageUsers, false) default: sendError(w, http.StatusBadRequest, "invalid rank sent") } - database.SaveUser(user, false) + database.SaveUser(userEdit, false) } func updateApiKeyPermsOnUserPermChange(userId int, userPerm uint16, isNewlyGranted bool) { @@ -667,6 +675,10 @@ func deleteUser(w http.ResponseWriter, request apiRequest, user models.User) { sendError(w, http.StatusBadRequest, "Cannot delete super admin") return } + if user.Id == userToDelete.Id { + sendError(w, http.StatusBadRequest, "Cannot delete yourself") + return + } database.DeleteUser(userToDelete.Id) for _, file := range database.GetAllMetadata() { if file.UserId == userToDelete.Id { diff --git a/internal/webserver/authentication/Authentication.go b/internal/webserver/authentication/Authentication.go index 9f22040..b62d766 100644 --- a/internal/webserver/authentication/Authentication.go +++ b/internal/webserver/authentication/Authentication.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "github.com/forceu/gokapi/internal/configuration" + "github.com/forceu/gokapi/internal/configuration/database" "github.com/forceu/gokapi/internal/helper" "github.com/forceu/gokapi/internal/models" "github.com/forceu/gokapi/internal/webserver/authentication/sessionmanager" @@ -100,9 +101,10 @@ func IsAuthenticated(w http.ResponseWriter, r *http.Request) (bool, int) { return true, userId } case Header: - if isGrantedHeader(r) { - return true, 0 // TODO - } // TODO + userId, ok := isGrantedHeader(r) + if ok { + return true, userId + } case Disabled: return true, 0 } @@ -110,18 +112,24 @@ func IsAuthenticated(w http.ResponseWriter, r *http.Request) (bool, int) { } // isGrantedHeader returns true if the user was authenticated by a proxy header if enabled -func isGrantedHeader(r *http.Request) bool { +func isGrantedHeader(r *http.Request) (int, bool) { if authSettings.HeaderKey == "" { - return false + return -1, false } - value := r.Header.Get(authSettings.HeaderKey) - if value == "" { - return false + userName := r.Header.Get(authSettings.HeaderKey) + if userName == "" { + + return -1, false } if len(authSettings.HeaderUsers) == 0 { - return true + user := getOrCreateUser(userName, userName) + return user.Id, true + } + if isUserInArray(userName, authSettings.HeaderUsers) { + user := getOrCreateUser(userName, userName) + return user.Id, true } - return isUserInArray(value, authSettings.HeaderUsers) + return -1, false } func isUserInArray(userEntered string, allowedUsers []string) bool { @@ -264,7 +272,11 @@ func CheckOauthUserAndRedirect(userInfo OAuthUserInfo, w http.ResponseWriter) er } } if isValidOauthUser(userInfo, username, groups) { - sessionmanager.CreateSession(w, authSettings.Method == OAuth2, authSettings.OAuthRecheckInterval) + if userInfo.Email == "" { + userInfo.Email = username + } + user := getOrCreateUser(username, userInfo.Email) + sessionmanager.CreateSession(w, true, authSettings.OAuthRecheckInterval, user.Id) redirect(w, "admin") return nil } @@ -272,6 +284,23 @@ func CheckOauthUserAndRedirect(userInfo OAuthUserInfo, w http.ResponseWriter) er return nil } +func getOrCreateUser(username, email string) models.User { + user, ok := database.GetUserByEmail(email) + if !ok { + user = models.User{ + Name: username, + Email: username, + UserLevel: models.UserLevelUser, + } + database.SaveUser(user, true) + user, ok = database.GetUserByEmail(email) + if !ok { + panic("unable to read new user") + } + } + return user +} + func isValidOauthUser(userInfo OAuthUserInfo, username string, groups []string) bool { if userInfo.Subject == "" { return false @@ -293,9 +322,15 @@ func isGrantedSession(w http.ResponseWriter, r *http.Request) (int, bool) { } // IsCorrectUsernameAndPassword checks if a provided username and password is correct -func IsCorrectUsernameAndPassword(username, password string) bool { - return IsEqualStringConstantTime(username, authSettings.Username) && - IsEqualStringConstantTime(configuration.HashPasswordCustomSalt(password, authSettings.SaltAdmin), authSettings.Password) +func IsCorrectUsernameAndPassword(userEmail, password string) (models.User, bool) { + user, ok := database.GetUserByEmail(userEmail) + if !ok { + return models.User{}, false + } + if IsEqualStringConstantTime(configuration.HashPasswordCustomSalt(password, authSettings.SaltAdmin), user.Password) { + return user, true + } + return models.User{}, false } // IsEqualStringConstantTime uses ConstantTimeCompare to prevent timing attack. diff --git a/internal/webserver/authentication/sessionmanager/SessionManager.go b/internal/webserver/authentication/sessionmanager/SessionManager.go index 37e90da..fde3d1d 100644 --- a/internal/webserver/authentication/sessionmanager/SessionManager.go +++ b/internal/webserver/authentication/sessionmanager/SessionManager.go @@ -42,7 +42,7 @@ func useSession(w http.ResponseWriter, id string, session models.Session, isOaut return false } if session.RenewAt < time.Now().Unix() { - CreateSession(w, isOauth, OAuthRecheckInterval) + CreateSession(w, isOauth, OAuthRecheckInterval, session.UserId) database.DeleteSession(id) } go database.UpdateUserLastOnline(session.UserId) @@ -51,7 +51,7 @@ func useSession(w http.ResponseWriter, id string, session models.Session, isOaut // CreateSession creates a new session - called after login with correct username / password // If sessions parameter is nil, it will be loaded from config -func CreateSession(w http.ResponseWriter, isOauth bool, OAuthRecheckInterval int) { +func CreateSession(w http.ResponseWriter, isOauth bool, OAuthRecheckInterval int, userId int) { timeExpiry := time.Now().Add(cookieLifeAdmin) if isOauth { timeExpiry = time.Now().Add(time.Duration(OAuthRecheckInterval) * time.Hour) @@ -61,6 +61,7 @@ func CreateSession(w http.ResponseWriter, isOauth bool, OAuthRecheckInterval int database.SaveSession(sessionString, models.Session{ RenewAt: time.Now().Add(12 * time.Hour).Unix(), ValidUntil: timeExpiry.Unix(), + UserId: userId, }) writeSessionCookie(w, sessionString, timeExpiry) } diff --git a/internal/webserver/web/templates/html_users.tmpl b/internal/webserver/web/templates/html_users.tmpl index 8e5fcf4..1b066ae 100644 --- a/internal/webserver/web/templates/html_users.tmpl +++ b/internal/webserver/web/templates/html_users.tmpl @@ -41,33 +41,33 @@ {{ .UploadCount }} - + - + - + - + - + - + - + - + {{if gt .User.UserLevel 1}} - {{ else }} - {{ end }} - + {{ end }}