update user profile
parent
8444724a58
commit
4612764b0b
|
@ -11,6 +11,7 @@ type User struct {
|
|||
Email string
|
||||
Password string
|
||||
LastOnline time.Time
|
||||
UpdatedAt time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package utils
|
|||
const (
|
||||
minUsername = "2"
|
||||
maxUsername = "20"
|
||||
MinUsername = 2
|
||||
MaxUsername = 20
|
||||
minPassword = "6"
|
||||
MinPassword = 6
|
||||
maxPassword = "64"
|
||||
|
@ -18,7 +20,7 @@ const (
|
|||
|
||||
MaxAvatarSize = 5 * 1024 * 1024 // 5 MB
|
||||
|
||||
GroupTaskLockedTime = 3 // seconds - need to be equal with web
|
||||
GroupTaskLockedTime = 3
|
||||
SessionExpiresAtTime = 7 * 24 * 60 * 60 // 1 week
|
||||
)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"janex/admin-dashboard-backend/modules/utils"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/websocket/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
@ -55,6 +56,14 @@ func SendMessageToUser(userId string, ignoreUserSessionId string, sendSocketMess
|
|||
}
|
||||
}
|
||||
|
||||
func SendMessageOnlyToSessionId(sessionId string, sendSocketMessage structs.SendSocketMessage) {
|
||||
for _, client := range cache.GetSocketClients() {
|
||||
if client.SessionId == sessionId {
|
||||
client.SendMessage(sendSocketMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This close all connections that are connected with one session id.
|
||||
// For example when a user has two browser tabs opened
|
||||
func CloseAllUserSessionConnections(sessionId string) {
|
||||
|
@ -65,6 +74,18 @@ func CloseAllUserSessionConnections(sessionId string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Used to close all user connections
|
||||
// For example when a user changed his password
|
||||
func CloseAndDeleteAllUserConnections(userId string) {
|
||||
for _, client := range cache.GetSocketClients() {
|
||||
if client.UserId == userId {
|
||||
client.SendSessionClosedMessage()
|
||||
}
|
||||
}
|
||||
|
||||
database.DB.Where("user_id = ?", userId).Delete(&structs.UserSession{})
|
||||
}
|
||||
|
||||
func GetUserSessions(userId string) []structs.UserSessionSocket {
|
||||
var userSessions []structs.UserSession
|
||||
|
||||
|
@ -168,20 +189,25 @@ func isEmailAvailable(email string) bool {
|
|||
return user.Email == ""
|
||||
}
|
||||
|
||||
func UpdateUserProfile(userId string, changes map[string]interface{}) {
|
||||
log.Debug().Msgf("changes: %v", changes)
|
||||
func UpdateUserProfile(conn *websocket.Conn, changes map[string]interface{}) {
|
||||
sessionId := conn.Locals("sessionId").(string)
|
||||
userId := conn.Locals("userId").(string)
|
||||
|
||||
var user structs.User
|
||||
var updates = make(map[string]interface{})
|
||||
|
||||
// TODO: validate length of username and email
|
||||
var changesResult = make(map[string]uint8)
|
||||
|
||||
if changes["username"] != nil {
|
||||
username := changes["username"].(string)
|
||||
|
||||
if isUsernameAvailable(username) {
|
||||
user.Username = username
|
||||
updates["Username"] = username
|
||||
if isUsernameLengthValid(username) { // only affected if username was manipulated as min and max is provided in web ui
|
||||
if isUsernameAvailable(username) {
|
||||
user.Username = username
|
||||
updates["Username"] = username
|
||||
changesResult["Username"] = 0
|
||||
} else {
|
||||
changesResult["Username"] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,37 +217,71 @@ func UpdateUserProfile(userId string, changes map[string]interface{}) {
|
|||
if isEmailAvailable(email) {
|
||||
user.Email = email
|
||||
updates["Email"] = email
|
||||
changesResult["Email"] = 0
|
||||
} else {
|
||||
changesResult["Email"] = 1
|
||||
}
|
||||
}
|
||||
|
||||
if changes["password"] != nil {
|
||||
log.Debug().Msg("update password")
|
||||
password := changes["password"].(string)
|
||||
if changes["oldPassword"] != nil && changes["newPassword"] != nil {
|
||||
oldPassword := changes["oldPassword"].(string)
|
||||
newPassword := changes["newPassword"].(string)
|
||||
|
||||
decodedPassword, err := base64.StdEncoding.DecodeString(changes["password"].(string))
|
||||
decodedOldPassword, err := base64.StdEncoding.DecodeString(oldPassword)
|
||||
decodedNewPassword, err1 := base64.StdEncoding.DecodeString(newPassword)
|
||||
|
||||
if err != nil {
|
||||
log.Error().Msg("Failed to decode base64 password, err: " + err.Error())
|
||||
}
|
||||
if err == nil && err1 == nil {
|
||||
if utils.IsPasswordLengthValid(string(decodedOldPassword)) { // only affected if username was manipulated as min and max is provided in web ui
|
||||
database.DB.Select("password").First(&user, "id = ?", userId)
|
||||
|
||||
if utils.IsPasswordLengthValid(password) {
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), decodedPassword); err != nil {
|
||||
log.Error().Msg("Incorrect password")
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), decodedOldPassword); err == nil {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(decodedNewPassword), bcrypt.DefaultCost)
|
||||
|
||||
if err == nil {
|
||||
user.Password = string(hashedPassword)
|
||||
} else {
|
||||
log.Error().Msgf("Failed to generate hash password %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
log.Error().Msg("Incorrect password")
|
||||
changesResult["Password"] = 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
log.Error().Msgf("Failed to decode old password %s", err.Error())
|
||||
}
|
||||
if err1 != nil {
|
||||
log.Error().Msgf("Failed to decode new password %s", err1.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: logout all client user sessions
|
||||
}
|
||||
|
||||
log.Debug().Msgf("len %v", len(changes))
|
||||
|
||||
// TODO: dont sent change message if user changed password
|
||||
if len(changes) > 0 {
|
||||
// TODO: update user last updated timestamp
|
||||
user.UpdatedAt = time.Now()
|
||||
|
||||
database.DB.Model(&structs.User{}).Where("id = ?", userId).Updates(user)
|
||||
|
||||
if changes["username"] != nil || changes["email"] != nil {
|
||||
BroadcastMessage(structs.SendSocketMessage{
|
||||
if changes["username"] != nil || changes["email"] != nil || changes["oldPassword"] != nil && changes["newPassword"] != nil {
|
||||
if changes["oldPassword"] != nil && changes["newPassword"] != nil {
|
||||
// user has changed password - logout all his sessions
|
||||
CloseAndDeleteAllUserConnections(userId)
|
||||
} else {
|
||||
SendMessageOnlyToSessionId(sessionId, structs.SendSocketMessage{
|
||||
Cmd: utils.SentCmdUserProfileUpdated,
|
||||
Body: struct {
|
||||
UserId string
|
||||
Changes map[string]interface{}
|
||||
Result map[string]uint8
|
||||
}{
|
||||
UserId: userId,
|
||||
Changes: updates,
|
||||
Result: changesResult,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
BroadcastMessageExceptUserSessionId(sessionId, structs.SendSocketMessage{
|
||||
Cmd: utils.SentCmdUserProfileUpdated,
|
||||
Body: struct {
|
||||
UserId string
|
||||
|
@ -233,5 +293,9 @@ func UpdateUserProfile(userId string, changes map[string]interface{}) {
|
|||
})
|
||||
}
|
||||
}
|
||||
// TODO: sent feedback back to user for ui notification message
|
||||
}
|
||||
|
||||
func isUsernameLengthValid(username string) bool {
|
||||
l := len(username)
|
||||
return l > utils.MinUsername && l < utils.MaxUsername
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ func RunHub() {
|
|||
})
|
||||
break
|
||||
case utils.ReceivedCmdUpdateUserProfile:
|
||||
socketclients.UpdateUserProfile(data.Conn.Locals("userId").(string), receivedMessage.Body["changes"].(map[string]interface{}))
|
||||
socketclients.UpdateUserProfile(data.Conn, receivedMessage.Body["changes"].(map[string]interface{}))
|
||||
break
|
||||
default:
|
||||
log.Error().Msgf("Received unknown message: %v", receivedMessage)
|
||||
|
|
Loading…
Reference in New Issue