diff options
author | Joram Wilander <jwawilander@gmail.com> | 2017-01-13 15:17:50 -0500 |
---|---|---|
committer | Harrison Healey <harrisonmhealey@gmail.com> | 2017-01-13 15:17:50 -0500 |
commit | 0e2b321e6f5ab5983bc3428aa455dac7012c36ee (patch) | |
tree | c3919a284cf13ddcb66a6f482b1c9bbd9f34fc9b /app/user.go | |
parent | 97558f6a6ec4c53fa69035fb430ead209d9c222d (diff) | |
download | chat-0e2b321e6f5ab5983bc3428aa455dac7012c36ee.tar.gz chat-0e2b321e6f5ab5983bc3428aa455dac7012c36ee.tar.bz2 chat-0e2b321e6f5ab5983bc3428aa455dac7012c36ee.zip |
Refactor and migrate more functions out of api into app package (#5063)
Diffstat (limited to 'app/user.go')
-rw-r--r-- | app/user.go | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/app/user.go b/app/user.go index 5acd9dcaa..909c8cca9 100644 --- a/app/user.go +++ b/app/user.go @@ -4,11 +4,105 @@ package app import ( + "bytes" + "fmt" + "hash/fnv" + "image" + "image/color" + "image/draw" + _ "image/gif" + _ "image/jpeg" + "image/png" + "io" + "io/ioutil" + "net/http" + "strconv" + "strings" + l4g "github.com/alecthomas/log4go" + "github.com/golang/freetype" + "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) +func CreateUserWithHash(user *model.User, hash string, data string) (*model.User, *model.AppError) { + props := model.MapFromJson(strings.NewReader(data)) + + if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) { + return nil, model.NewLocAppError("CreateUserWithHash", "api.user.create_user.signup_link_invalid.app_error", nil, "") + } + + if t, err := strconv.ParseInt(props["time"], 10, 64); err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours + return nil, model.NewLocAppError("CreateUserWithHash", "api.user.create_user.signup_link_expired.app_error", nil, "") + } + + teamId := props["id"] + + var team *model.Team + if result := <-Srv.Store.Team().Get(teamId); result.Err != nil { + return nil, result.Err + } else { + team = result.Data.(*model.Team) + } + + user.Email = props["email"] + user.EmailVerified = true + + var ruser *model.User + var err *model.AppError + if ruser, err = CreateUser(user); err != nil { + return nil, err + } + + if err := JoinUserToTeam(team, ruser); err != nil { + return nil, err + } + + AddDirectChannels(team.Id, ruser) + + return ruser, nil +} + +func CreateUserWithInviteId(user *model.User, inviteId string) (*model.User, *model.AppError) { + var team *model.Team + if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil { + return nil, result.Err + } else { + team = result.Data.(*model.Team) + } + + var ruser *model.User + var err *model.AppError + if ruser, err = CreateUser(user); err != nil { + return nil, err + } + + if err := JoinUserToTeam(team, ruser); err != nil { + return nil, err + } + + AddDirectChannels(team.Id, ruser) + + return ruser, nil +} + +func IsFirstUserAccount() bool { + if SessionCacheLength() == 0 { + if cr := <-Srv.Store.User().GetTotalUsersCount(); cr.Err != nil { + l4g.Error(cr.Err) + return false + } else { + count := cr.Data.(int64) + if count <= 0 { + return true + } + } + } + + return false +} + func CreateUser(user *model.User) (*model.User, *model.AppError) { user.Roles = model.ROLE_SYSTEM_USER.Id @@ -58,3 +152,330 @@ func CreateUser(user *model.User) (*model.User, *model.AppError) { return ruser, nil } } + +func CreateOAuthUser(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) { + var user *model.User + provider := einterfaces.GetOauthProvider(service) + if provider == nil { + return nil, model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.not_available.app_error", map[string]interface{}{"Service": strings.Title(service)}, "") + } else { + user = provider.GetUserFromJson(userData) + } + + if user == nil { + return nil, model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.create.app_error", map[string]interface{}{"Service": service}, "") + } + + suchan := Srv.Store.User().GetByAuth(user.AuthData, service) + euchan := Srv.Store.User().GetByEmail(user.Email) + + found := true + count := 0 + for found { + if found = IsUsernameTaken(user.Username); found { + user.Username = user.Username + strconv.Itoa(count) + count += 1 + } + } + + if result := <-suchan; result.Err == nil { + return nil, model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.already_used.app_error", map[string]interface{}{"Service": service}, "email="+user.Email) + } + + if result := <-euchan; result.Err == nil { + authService := result.Data.(*model.User).AuthService + if authService == "" { + return nil, model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.already_attached.app_error", + map[string]interface{}{"Service": service, "Auth": model.USER_AUTH_SERVICE_EMAIL}, "email="+user.Email) + } else { + return nil, model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.already_attached.app_error", + map[string]interface{}{"Service": service, "Auth": authService}, "email="+user.Email) + } + } + + user.EmailVerified = true + + ruser, err := CreateUser(user) + if err != nil { + return nil, err + } + + if len(teamId) > 0 { + err = JoinUserToTeamById(teamId, user) + if err != nil { + return nil, err + } + + err = AddDirectChannels(teamId, user) + if err != nil { + l4g.Error(err.Error()) + } + } + + return ruser, nil +} + +// Check if the username is already used by another user. Return false if the username is invalid. +func IsUsernameTaken(name string) bool { + + if !model.IsValidUsername(name) { + return false + } + + if result := <-Srv.Store.User().GetByUsername(name); result.Err != nil { + return false + } else { + return true + } + + return false +} + +func GetUser(userId string) (*model.User, *model.AppError) { + if result := <-Srv.Store.User().Get(userId); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.User), nil + } +} + +func GetUserByUsername(username string) (*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetByUsername(username); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.User), nil + } +} + +func GetUserByEmail(email string) (*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.User), nil + } +} + +func GetUserByAuth(authData *string, authService string) (*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetByAuth(authData, authService); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(*model.User), nil + } +} + +func GetUserForLogin(loginId string, onlyLdap bool) (*model.User, *model.AppError) { + ldapAvailable := *utils.Cfg.LdapSettings.Enable && einterfaces.GetLdapInterface() != nil && utils.IsLicensed && *utils.License.Features.LDAP + + if result := <-Srv.Store.User().GetForLogin( + loginId, + *utils.Cfg.EmailSettings.EnableSignInWithUsername && !onlyLdap, + *utils.Cfg.EmailSettings.EnableSignInWithEmail && !onlyLdap, + ldapAvailable, + ); result.Err != nil && result.Err.Id == "store.sql_user.get_for_login.multiple_users" { + // don't fall back to LDAP in this case since we already know there's an LDAP user, but that it shouldn't work + result.Err.StatusCode = http.StatusBadRequest + return nil, result.Err + } else if result.Err != nil { + if !ldapAvailable { + // failed to find user and no LDAP server to fall back on + result.Err.StatusCode = http.StatusBadRequest + return nil, result.Err + } + + // fall back to LDAP server to see if we can find a user + if ldapUser, ldapErr := einterfaces.GetLdapInterface().GetUser(loginId); ldapErr != nil { + ldapErr.StatusCode = http.StatusBadRequest + return nil, ldapErr + } else { + return ldapUser, nil + } + } else { + return result.Data.(*model.User), nil + } +} + +func GetUsers(offset int, limit int) (map[string]*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetAllProfiles(offset, limit); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(map[string]*model.User), nil + } +} + +func GetUsersEtag() string { + return (<-Srv.Store.User().GetEtagForAllProfiles()).Data.(string) +} + +func GetUsersInTeam(teamId string, offset int, limit int) (map[string]*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetProfiles(teamId, offset, limit); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(map[string]*model.User), nil + } +} + +func GetUsersInTeamEtag(teamId string) string { + return (<-Srv.Store.User().GetEtagForProfiles(teamId)).Data.(string) +} + +func GetUsersInChannel(channelId string, offset int, limit int) (map[string]*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetProfilesInChannel(channelId, offset, limit, false); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(map[string]*model.User), nil + } +} + +func GetUsersNotInChannel(teamId string, channelId string, offset int, limit int) (map[string]*model.User, *model.AppError) { + if result := <-Srv.Store.User().GetProfilesNotInChannel(teamId, channelId, offset, limit); result.Err != nil { + return nil, result.Err + } else { + return result.Data.(map[string]*model.User), nil + } +} + +func ActivateMfa(userId, token string) *model.AppError { + mfaInterface := einterfaces.GetMfaInterface() + if mfaInterface == nil { + err := model.NewLocAppError("ActivateMfa", "api.user.update_mfa.not_available.app_error", nil, "") + err.StatusCode = http.StatusNotImplemented + return err + } + + var user *model.User + if result := <-Srv.Store.User().Get(userId); result.Err != nil { + return result.Err + } else { + user = result.Data.(*model.User) + } + + if len(user.AuthService) > 0 && user.AuthService != model.USER_AUTH_SERVICE_LDAP { + return model.NewLocAppError("ActivateMfa", "api.user.activate_mfa.email_and_ldap_only.app_error", nil, "") + } + + if err := mfaInterface.Activate(user, token); err != nil { + return err + } + + return nil +} + +func DeactivateMfa(userId string) *model.AppError { + mfaInterface := einterfaces.GetMfaInterface() + if mfaInterface == nil { + err := model.NewLocAppError("DeactivateMfa", "api.user.update_mfa.not_available.app_error", nil, "") + err.StatusCode = http.StatusNotImplemented + return err + } + + if err := mfaInterface.Deactivate(userId); err != nil { + return err + } + + return nil +} + +func CreateProfileImage(username string, userId string) ([]byte, *model.AppError) { + colors := []color.NRGBA{ + {197, 8, 126, 255}, + {227, 207, 18, 255}, + {28, 181, 105, 255}, + {35, 188, 224, 255}, + {116, 49, 196, 255}, + {197, 8, 126, 255}, + {197, 19, 19, 255}, + {250, 134, 6, 255}, + {227, 207, 18, 255}, + {123, 201, 71, 255}, + {28, 181, 105, 255}, + {35, 188, 224, 255}, + {116, 49, 196, 255}, + {197, 8, 126, 255}, + {197, 19, 19, 255}, + {250, 134, 6, 255}, + {227, 207, 18, 255}, + {123, 201, 71, 255}, + {28, 181, 105, 255}, + {35, 188, 224, 255}, + {116, 49, 196, 255}, + {197, 8, 126, 255}, + {197, 19, 19, 255}, + {250, 134, 6, 255}, + {227, 207, 18, 255}, + {123, 201, 71, 255}, + } + + h := fnv.New32a() + h.Write([]byte(userId)) + seed := h.Sum32() + + initial := string(strings.ToUpper(username)[0]) + + fontBytes, err := ioutil.ReadFile(utils.FindDir("fonts") + utils.Cfg.FileSettings.InitialFont) + if err != nil { + return nil, model.NewLocAppError("CreateProfileImage", "api.user.create_profile_image.default_font.app_error", nil, err.Error()) + } + font, err := freetype.ParseFont(fontBytes) + if err != nil { + return nil, model.NewLocAppError("CreateProfileImage", "api.user.create_profile_image.default_font.app_error", nil, err.Error()) + } + + width := int(utils.Cfg.FileSettings.ProfileWidth) + height := int(utils.Cfg.FileSettings.ProfileHeight) + color := colors[int64(seed)%int64(len(colors))] + dstImg := image.NewRGBA(image.Rect(0, 0, width, height)) + srcImg := image.White + draw.Draw(dstImg, dstImg.Bounds(), &image.Uniform{color}, image.ZP, draw.Src) + size := float64((width + height) / 4) + + c := freetype.NewContext() + c.SetFont(font) + c.SetFontSize(size) + c.SetClip(dstImg.Bounds()) + c.SetDst(dstImg) + c.SetSrc(srcImg) + + pt := freetype.Pt(width/6, height*2/3) + _, err = c.DrawString(initial, pt) + if err != nil { + return nil, model.NewLocAppError("CreateProfileImage", "api.user.create_profile_image.initial.app_error", nil, err.Error()) + } + + buf := new(bytes.Buffer) + + if imgErr := png.Encode(buf, dstImg); imgErr != nil { + return nil, model.NewLocAppError("CreateProfileImage", "api.user.create_profile_image.encode.app_error", nil, imgErr.Error()) + } else { + return buf.Bytes(), nil + } +} + +func GetProfileImage(user *model.User) ([]byte, *model.AppError) { + var img []byte + + if len(utils.Cfg.FileSettings.DriverName) == 0 { + var err *model.AppError + if img, err = CreateProfileImage(user.Username, user.Id); err != nil { + return nil, err + } + } else { + path := "users/" + user.Id + "/profile.png" + + if data, err := ReadFile(path); err != nil { + if img, err = CreateProfileImage(user.Username, user.Id); err != nil { + return nil, err + } + + if user.LastPictureUpdate == 0 { + if err := WriteFile(img, path); err != nil { + return nil, err + } + } + + } else { + img = data + } + } + + return img, nil +} |