PLT-2258 Unified login screen and related APIs (#2820)

* Unified login screen and related APIs

* Refactored login API call to be less convoluted

* Removed LDAP login prompt from invite process

* Fixed existing LDAP users being able to log in if LDAP was configured, but disabled

* Gofmt

* Future proofed login API

* Updated login APIs based on feedback

* Added additional auditing to login API

* Actually removed loginById
Этот коммит содержится в:
Harrison Healey
2016-05-03 14:10:36 -04:00
коммит произвёл Christopher Speller
родитель e76a30bca0
Коммит 87989b8afd
36 изменённых файлов: 630 добавлений и 1049 удалений

Просмотреть файл

@@ -200,19 +200,19 @@ func (me *TestHelper) CreatePost(client *model.Client, channel *model.Channel) *
func (me *TestHelper) LoginBasic() { func (me *TestHelper) LoginBasic() {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
me.BasicClient.Must(me.BasicClient.LoginByEmail(me.BasicTeam.Name, me.BasicUser.Email, me.BasicUser.Password)) me.BasicClient.Must(me.BasicClient.Login(me.BasicUser.Email, me.BasicUser.Password))
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }
func (me *TestHelper) LoginBasic2() { func (me *TestHelper) LoginBasic2() {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
me.BasicClient.Must(me.BasicClient.LoginByEmail(me.BasicTeam.Name, me.BasicUser2.Email, me.BasicUser2.Password)) me.BasicClient.Must(me.BasicClient.Login(me.BasicUser2.Email, me.BasicUser2.Password))
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }
func (me *TestHelper) LoginSystemAdmin() { func (me *TestHelper) LoginSystemAdmin() {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
me.SystemAdminClient.Must(me.SystemAdminClient.LoginByEmail(me.SystemAdminTeam.Name, me.SystemAdminUser.Email, me.SystemAdminUser.Password)) me.SystemAdminClient.Must(me.SystemAdminClient.Login(me.SystemAdminUser.Email, me.SystemAdminUser.Password))
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }

Просмотреть файл

@@ -7,6 +7,8 @@ import (
"github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
"net/http"
) )
func checkPasswordAndAllCriteria(user *model.User, password string, mfaToken string) *model.AppError { func checkPasswordAndAllCriteria(user *model.User, password string, mfaToken string) *model.AppError {
@@ -37,6 +39,32 @@ func checkUserPassword(user *model.User, password string) *model.AppError {
} }
} }
func checkLdapUserPasswordAndAllCriteria(ldapId, password, mfaToken string) (*model.User, *model.AppError) {
ldapInterface := einterfaces.GetLdapInterface()
if ldapInterface == nil {
err := model.NewLocAppError("doLdapAuthentication", "api.user.login_ldap.not_available.app_error", nil, "")
err.StatusCode = http.StatusNotImplemented
return nil, err
}
var user *model.User
if ldapUser, err := ldapInterface.DoLogin(ldapId, password); err != nil {
err.StatusCode = http.StatusUnauthorized
return nil, err
} else {
user = ldapUser
}
if err := checkUserAdditionalAuthenticationCriteria(user, mfaToken); err != nil {
err.StatusCode = http.StatusUnauthorized
return user, err
}
// user successfully authenticated
return user, nil
}
func checkUserAdditionalAuthenticationCriteria(user *model.User, mfaToken string) *model.AppError { func checkUserAdditionalAuthenticationCriteria(user *model.User, mfaToken string) *model.AppError {
if err := checkUserMfa(user, mfaToken); err != nil { if err := checkUserMfa(user, mfaToken); err != nil {
return err return err
@@ -97,3 +125,32 @@ func checkUserNotDisabled(user *model.User) *model.AppError {
} }
return nil return nil
} }
func authenticateUser(user *model.User, password, mfaToken string) (*model.User, *model.AppError) {
ldapAvailable := *utils.Cfg.LdapSettings.Enable && einterfaces.GetLdapInterface() != nil
if user.AuthService == model.USER_AUTH_SERVICE_LDAP {
if !ldapAvailable {
err := model.NewLocAppError("login", "api.user.login_ldap.not_available.app_error", nil, "")
err.StatusCode = http.StatusNotImplemented
return user, err
} else if ldapUser, err := checkLdapUserPasswordAndAllCriteria(user.AuthData, password, mfaToken); err != nil {
err.StatusCode = http.StatusUnauthorized
return user, err
} else {
// slightly redundant to get the user again, but we need to get it from the LDAP server
return ldapUser, nil
}
} else if user.AuthService != "" {
err := model.NewLocAppError("login", "api.user.login.use_auth_service.app_error", map[string]interface{}{"AuthService": user.AuthService}, "")
err.StatusCode = http.StatusBadRequest
return user, err
} else {
if err := checkPasswordAndAllCriteria(user, password, mfaToken); err != nil {
err.StatusCode = http.StatusUnauthorized
return user, err
} else {
return user, nil
}
}
}

Просмотреть файл

@@ -131,7 +131,7 @@ func BenchmarkJoinChannel(b *testing.B) {
user = th.BasicClient.Must(th.BasicClient.CreateUser(user, "")).Data.(*model.User) user = th.BasicClient.Must(th.BasicClient.CreateUser(user, "")).Data.(*model.User)
LinkUserToTeam(user, th.BasicTeam) LinkUserToTeam(user, th.BasicTeam)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
th.BasicClient.LoginByEmail(th.BasicTeam.Name, user.Email, "pwd") th.BasicClient.Login(user.Email, "pwd")
// Benchmark Start // Benchmark Start
b.ResetTimer() b.ResetTimer()

Просмотреть файл

@@ -169,7 +169,7 @@ func TestUpdateChannel(t *testing.T) {
} }
} }
Client.LoginByEmail(team.Name, user2.Email, user2.Password) Client.Login(user2.Email, user2.Password)
if _, err := Client.UpdateChannel(upChannel1); err == nil { if _, err := Client.UpdateChannel(upChannel1); err == nil {
t.Fatal("Standard User should have failed to update") t.Fatal("Standard User should have failed to update")
@@ -432,7 +432,7 @@ func TestJoinChannelById(t *testing.T) {
user3 := th.CreateUser(th.BasicClient) user3 := th.CreateUser(th.BasicClient)
LinkUserToTeam(user3, team) LinkUserToTeam(user3, team)
Client.LoginByEmail(team.Name, user3.Email, "pwd") Client.Login(user3.Email, "pwd")
if _, err := Client.JoinChannel(rchannel.Id); err == nil { if _, err := Client.JoinChannel(rchannel.Id); err == nil {
t.Fatal("shoudn't be able to join direct channel") t.Fatal("shoudn't be able to join direct channel")
@@ -462,7 +462,7 @@ func TestJoinChannelByName(t *testing.T) {
user3 := th.CreateUser(th.BasicClient) user3 := th.CreateUser(th.BasicClient)
LinkUserToTeam(user3, team) LinkUserToTeam(user3, team)
Client.LoginByEmail(team.Name, user3.Email, "pwd") Client.Login(user3.Email, "pwd")
if _, err := Client.JoinChannelByName(rchannel.Name); err == nil { if _, err := Client.JoinChannelByName(rchannel.Name); err == nil {
t.Fatal("shoudn't be able to join direct channel") t.Fatal("shoudn't be able to join direct channel")
@@ -518,7 +518,7 @@ func TestDeleteChannel(t *testing.T) {
Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id) Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)
Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") Client.Login(userTeamAdmin.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -541,7 +541,7 @@ func TestDeleteChannel(t *testing.T) {
userStd := th.CreateUser(th.BasicClient) userStd := th.CreateUser(th.BasicClient)
LinkUserToTeam(userStd, team) LinkUserToTeam(userStd, team)
Client.LoginByEmail(team.Name, userStd.Email, userStd.Password) Client.Login(userStd.Email, userStd.Password)
if _, err := Client.JoinChannel(channel1.Id); err == nil { if _, err := Client.JoinChannel(channel1.Id); err == nil {
t.Fatal("should have failed to join deleted channel") t.Fatal("should have failed to join deleted channel")
@@ -606,7 +606,7 @@ func TestGetChannelExtraInfo(t *testing.T) {
Client2.SetTeamId(team.Id) Client2.SetTeamId(team.Id)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client2.LoginByEmail(team.Name, user2.Email, "pwd") Client2.Login(user2.Email, "pwd")
Client2.Must(Client2.JoinChannel(channel1.Id)) Client2.Must(Client2.JoinChannel(channel1.Id))
if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil { if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, -1, currentEtag); err != nil {
@@ -775,7 +775,7 @@ func TestRemoveChannelMember(t *testing.T) {
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
Client.LoginByEmail(team.Name, userStd.Email, userStd.Password) Client.Login(userStd.Email, userStd.Password)
if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil { if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil {
t.Fatal("Should have errored, user not channel admin") t.Fatal("Should have errored, user not channel admin")

Просмотреть файл

@@ -164,7 +164,7 @@ func (me *LoadTestProvider) SetupCommand(c *Context, channelId string, message s
if err := CreateBasicUser(client); err != nil { if err := CreateBasicUser(client); err != nil {
return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} }
client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD) client.Login(BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
environment, err := CreateTestEnvironmentWithTeams( environment, err := CreateTestEnvironmentWithTeams(
client, client,
utils.Range{numTeams, numTeams}, utils.Range{numTeams, numTeams},

Просмотреть файл

@@ -217,7 +217,7 @@ func TestGetFile(t *testing.T) {
data := model.MapToJson(newProps) data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.FileSettings.PublicLinkSalt)) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.FileSettings.PublicLinkSalt))
Client.LoginByEmail(team2.Name, user2.Email, "pwd") Client.Login(user2.Email, "pwd")
Client.SetTeamId(team2.Id) Client.SetTeamId(team2.Id)
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, false); downErr != nil { if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, false); downErr != nil {

Просмотреть файл

@@ -91,7 +91,7 @@ func TestCreatePost(t *testing.T) {
t.Fatal("Should have been forbidden") t.Fatal("Should have been forbidden")
} }
Client.LoginByEmail(team2.Name, user3.Email, user3.Password) Client.Login(user3.Email, user3.Password)
Client.SetTeamId(team2.Id) Client.SetTeamId(team2.Id)
channel3 := th.CreateChannel(Client, team2) channel3 := th.CreateChannel(Client, team2)
@@ -512,7 +512,7 @@ func TestSearchPostsFromUser(t *testing.T) {
t.Fatalf("wrong number of posts returned %v", len(result.Order)) t.Fatalf("wrong number of posts returned %v", len(result.Order))
} }
Client.LoginByEmail(team.Name, user3.Email, user3.Password) Client.Login(user3.Email, user3.Password)
// wait for the join/leave messages to be created for user3 since they're done asynchronously // wait for the join/leave messages to be created for user3 since they're done asynchronously
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -741,7 +741,7 @@ func TestGetOutOfChannelMentions(t *testing.T) {
user4 := th.CreateUser(Client) user4 := th.CreateUser(Client)
LinkUserToTeam(user4, team2) LinkUserToTeam(user4, team2)
Client.Must(Client.LoginByEmail(team2.Name, user4.Email, user4.Password)) Client.Must(Client.Login(user4.Email, user4.Password))
Client.SetTeamId(team2.Id) Client.SetTeamId(team2.Id)
channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id} channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}

Просмотреть файл

@@ -90,7 +90,7 @@ func TestCreateTeam(t *testing.T) {
LinkUserToTeam(user, rteam.Data.(*model.Team)) LinkUserToTeam(user, rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(rteam.Data.(*model.Team).Id) Client.SetTeamId(rteam.Data.(*model.Team).Id)
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
@@ -209,7 +209,7 @@ func TestAddUserToTeamFromInvite(t *testing.T) {
user2 := th.CreateUser(th.BasicClient) user2 := th.CreateUser(th.BasicClient)
Client.Must(Client.Logout()) Client.Must(Client.Logout())
Client.Must(Client.LoginByEmail("", user2.Email, user2.Password)) Client.Must(Client.Login(user2.Email, user2.Password))
if result, err := th.BasicClient.AddUserToTeamFromInvite("", "", rteam.InviteId); err != nil { if result, err := th.BasicClient.AddUserToTeamFromInvite("", "", rteam.InviteId); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -234,7 +234,7 @@ func TestGetAllTeams(t *testing.T) {
LinkUserToTeam(user, team) LinkUserToTeam(user, team)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if r1, err := Client.GetAllTeams(); err != nil { if r1, err := Client.GetAllTeams(); err != nil {
@@ -254,7 +254,7 @@ func TestGetAllTeams(t *testing.T) {
c.IpAddress = "cmd_line" c.IpAddress = "cmd_line"
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if r1, err := Client.GetAllTeams(); err != nil { if r1, err := Client.GetAllTeams(); err != nil {
@@ -283,7 +283,7 @@ func TestGetAllTeamListings(t *testing.T) {
LinkUserToTeam(user, team) LinkUserToTeam(user, team)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if r1, err := Client.GetAllTeamListings(); err != nil { if r1, err := Client.GetAllTeamListings(); err != nil {
@@ -303,7 +303,7 @@ func TestGetAllTeamListings(t *testing.T) {
c.IpAddress = "cmd_line" c.IpAddress = "cmd_line"
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if r1, err := Client.GetAllTeams(); err != nil { if r1, err := Client.GetAllTeams(); err != nil {
@@ -332,7 +332,7 @@ func TestTeamPermDelete(t *testing.T) {
LinkUserToTeam(user1, team) LinkUserToTeam(user1, team)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Name, user1.Email, "pwd") Client.Login(user1.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -375,7 +375,7 @@ func TestInviteMembers(t *testing.T) {
LinkUserToTeam(user, team) LinkUserToTeam(user, team)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
invite := make(map[string]string) invite := make(map[string]string)
@@ -413,7 +413,7 @@ func TestUpdateTeamDisplayName(t *testing.T) {
LinkUserToTeam(user2, team) LinkUserToTeam(user2, team)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Login(user2.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
vteam := &model.Team{DisplayName: team.DisplayName, Name: team.Name, Email: team.Email, Type: team.Type} vteam := &model.Team{DisplayName: team.DisplayName, Name: team.Name, Email: team.Email, Type: team.Type}
@@ -422,7 +422,7 @@ func TestUpdateTeamDisplayName(t *testing.T) {
t.Fatal("Should have errored, not admin") t.Fatal("Should have errored, not admin")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
vteam.DisplayName = "" vteam.DisplayName = ""
if _, err := Client.UpdateTeam(vteam); err == nil { if _, err := Client.UpdateTeam(vteam); err == nil {
@@ -474,7 +474,7 @@ func TestGetMyTeam(t *testing.T) {
LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if result, err := Client.GetMyTeam(""); err != nil { if result, err := Client.GetMyTeam(""); err != nil {

Просмотреть файл

@@ -45,7 +45,6 @@ func InitUser() {
BaseRoutes.Users.Handle("/reset_password", ApiAppHandler(resetPassword)).Methods("POST") BaseRoutes.Users.Handle("/reset_password", ApiAppHandler(resetPassword)).Methods("POST")
BaseRoutes.Users.Handle("/login", ApiAppHandler(login)).Methods("POST") BaseRoutes.Users.Handle("/login", ApiAppHandler(login)).Methods("POST")
BaseRoutes.Users.Handle("/logout", ApiAppHandler(logout)).Methods("POST") BaseRoutes.Users.Handle("/logout", ApiAppHandler(logout)).Methods("POST")
BaseRoutes.Users.Handle("/login_ldap", ApiAppHandler(loginLdap)).Methods("POST")
BaseRoutes.Users.Handle("/revoke_session", ApiUserRequired(revokeSession)).Methods("POST") BaseRoutes.Users.Handle("/revoke_session", ApiUserRequired(revokeSession)).Methods("POST")
BaseRoutes.Users.Handle("/attach_device", ApiUserRequired(attachDeviceId)).Methods("POST") BaseRoutes.Users.Handle("/attach_device", ApiUserRequired(attachDeviceId)).Methods("POST")
BaseRoutes.Users.Handle("/verify_email", ApiAppHandler(verifyEmail)).Methods("POST") BaseRoutes.Users.Handle("/verify_email", ApiAppHandler(verifyEmail)).Methods("POST")
@@ -333,7 +332,7 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service
} }
} }
Login(c, w, r, ruser, "") doLogin(c, w, r, ruser, "")
if c.Err != nil { if c.Err != nil {
return nil return nil
} }
@@ -431,114 +430,86 @@ func SendVerifyEmailAndForget(c *Context, userId, userEmail, siteURL string) {
func login(c *Context, w http.ResponseWriter, r *http.Request) { func login(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body) props := model.MapFromJson(r.Body)
if len(props["password"]) == 0 { id := props["id"]
loginId := props["login_id"]
password := props["password"]
mfaToken := props["token"]
deviceId := props["device_id"]
if len(password) == 0 {
c.Err = model.NewLocAppError("login", "api.user.login.blank_pwd.app_error", nil, "") c.Err = model.NewLocAppError("login", "api.user.login.blank_pwd.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest c.Err.StatusCode = http.StatusBadRequest
return return
} }
var user *model.User var user *model.User
if len(props["id"]) != 0 { var err *model.AppError
user = LoginById(c, w, r, props["id"], props["password"], props["token"], props["device_id"])
} else if len(props["email"]) != 0 { if len(id) != 0 {
user = LoginByEmail(c, w, r, props["email"], props["name"], props["password"], props["token"], props["device_id"]) c.LogAuditWithUserId(id, "attempt")
} else if len(props["username"]) != 0 {
user = LoginByUsername(c, w, r, props["username"], props["name"], props["password"], props["token"], props["device_id"]) if result := <-Srv.Store.User().Get(id); result.Err != nil {
} else { c.LogAuditWithUserId(user.Id, "failure")
c.Err = model.NewLocAppError("login", "api.user.login.not_provided.app_error", nil, "") c.Err = result.Err
c.Err.StatusCode = http.StatusBadRequest c.Err.StatusCode = http.StatusBadRequest
return return
} else {
user = result.Data.(*model.User)
} }
} else {
c.LogAudit("attempt")
if c.Err != nil { if user, err = getUserForLogin(loginId); err != nil {
c.LogAudit("failure")
c.Err = err
return return
} }
if user != nil { c.LogAuditWithUserId(user.Id, "attempt")
user.Sanitize(map[string]bool{})
} else {
user = &model.User{}
} }
// and then authenticate them
if user, err = authenticateUser(user, password, mfaToken); err != nil {
c.LogAuditWithUserId(user.Id, "failure")
c.Err = err
return
}
c.LogAuditWithUserId(user.Id, "success")
doLogin(c, w, r, user, deviceId)
user.Sanitize(map[string]bool{})
w.Write([]byte(user.ToJson())) w.Write([]byte(user.ToJson()))
} }
func doUserPasswordAuthenticationAndLogin(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, password string, mfaToken string, deviceId string) bool { func getUserForLogin(loginId string) (*model.User, *model.AppError) {
c.LogAuditWithUserId(user.Id, "attempt") ldapAvailable := *utils.Cfg.LdapSettings.Enable && einterfaces.GetLdapInterface() != nil
if err := checkPasswordAndAllCriteria(user, password, mfaToken); err != nil {
c.LogAuditWithUserId(user.Id, "fail") if result := <-Srv.Store.User().GetForLogin(
c.Err = err loginId,
c.Err.StatusCode = http.StatusUnauthorized *utils.Cfg.EmailSettings.EnableSignInWithUsername,
return false *utils.Cfg.EmailSettings.EnableSignInWithEmail,
ldapAvailable,
); 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 { } else {
Login(c, w, r, user, deviceId) return ldapUser, nil
c.LogAuditWithUserId(user.Id, "success")
return true
} }
}
func LoginById(c *Context, w http.ResponseWriter, r *http.Request, userId, password, mfaToken, deviceId string) *model.User {
if result := <-Srv.Store.User().Get(userId); result.Err != nil {
c.Err = result.Err
return nil
} else { } else {
user := result.Data.(*model.User) return result.Data.(*model.User), nil
if len(user.AuthData) != 0 {
c.Err = model.NewLocAppError("LoginById", "api.user.login_by_email.sign_in.app_error",
map[string]interface{}{"AuthService": user.AuthService}, "")
return nil
} }
if doUserPasswordAuthenticationAndLogin(c, w, r, user, password, mfaToken, deviceId) {
return user
}
}
return nil
}
func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, name, password, mfaToken, deviceId string) *model.User {
if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil {
c.Err = result.Err
c.Err.StatusCode = http.StatusUnauthorized
return nil
} else {
user := result.Data.(*model.User)
if len(user.AuthData) != 0 {
c.Err = model.NewLocAppError("LoginByEmail", "api.user.login_by_email.sign_in.app_error",
map[string]interface{}{"AuthService": user.AuthService}, "")
return nil
}
if doUserPasswordAuthenticationAndLogin(c, w, r, user, password, mfaToken, deviceId) {
return user
}
}
return nil
}
func LoginByUsername(c *Context, w http.ResponseWriter, r *http.Request, username, name, password, mfaToken, deviceId string) *model.User {
if result := <-Srv.Store.User().GetByUsername(username); result.Err != nil {
c.Err = result.Err
c.Err.StatusCode = http.StatusUnauthorized
return nil
} else {
user := result.Data.(*model.User)
if len(user.AuthData) != 0 {
c.Err = model.NewLocAppError("LoginByUsername", "api.user.login_by_email.sign_in.app_error",
map[string]interface{}{"AuthService": user.AuthService}, "")
return nil
}
if doUserPasswordAuthenticationAndLogin(c, w, r, user, password, mfaToken, deviceId) {
return user
}
}
return nil
} }
func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader) *model.User { func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader) *model.User {
@@ -570,80 +541,13 @@ func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service st
return nil return nil
} else { } else {
user = result.Data.(*model.User) user = result.Data.(*model.User)
Login(c, w, r, user, "") doLogin(c, w, r, user, "")
return user return user
} }
} }
func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if !*utils.Cfg.LdapSettings.Enable {
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
props := model.MapFromJson(r.Body)
password := props["password"]
id := props["id"]
mfaToken := props["token"]
if len(password) == 0 {
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.blank_pwd.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
if len(id) == 0 {
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.need_id.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
ldapInterface := einterfaces.GetLdapInterface()
if ldapInterface == nil {
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.not_available.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
user, err := ldapInterface.DoLogin(id, password)
if err != nil {
if user != nil {
c.LogAuditWithUserId(user.Id, "attempt")
c.LogAuditWithUserId(user.Id, "fail")
} else {
c.LogAudit("attempt")
c.LogAudit("fail")
}
c.Err = err
c.Err.StatusCode = http.StatusUnauthorized
return
}
c.LogAuditWithUserId(user.Id, "attempt")
if err = checkUserAdditionalAuthenticationCriteria(user, mfaToken); err != nil {
c.LogAuditWithUserId(user.Id, "fail")
c.Err = err
c.Err.StatusCode = http.StatusUnauthorized
return
}
// User is authenticated at this point
Login(c, w, r, user, props["device_id"])
c.LogAuditWithUserId(user.Id, "success")
if user != nil {
user.Sanitize(map[string]bool{})
} else {
user = &model.User{}
}
w.Write([]byte(user.ToJson()))
}
// User MUST be authenticated completely before calling Login // User MUST be authenticated completely before calling Login
func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) { func doLogin(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) {
session := &model.Session{UserId: user.Id, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false} session := &model.Session{UserId: user.Id, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false}
@@ -2371,28 +2275,20 @@ func checkMfa(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body) props := model.MapFromJson(r.Body)
method := props["method"]
if method != model.USER_AUTH_SERVICE_EMAIL &&
method != model.USER_AUTH_SERVICE_USERNAME &&
method != model.USER_AUTH_SERVICE_LDAP {
c.SetInvalidParam("checkMfa", "method")
return
}
loginId := props["login_id"] loginId := props["login_id"]
if len(loginId) == 0 { if len(loginId) == 0 {
c.SetInvalidParam("checkMfa", "login_id") c.SetInvalidParam("checkMfa", "login_id")
return return
} }
var uchan store.StoreChannel // we don't need to worry about contacting the ldap server to get this user because
if method == model.USER_AUTH_SERVICE_EMAIL { // only users already in the system could have MFA enabled
uchan = Srv.Store.User().GetByEmail(loginId) uchan := Srv.Store.User().GetForLogin(
} else if method == model.USER_AUTH_SERVICE_USERNAME { loginId,
uchan = Srv.Store.User().GetByUsername(loginId) *utils.Cfg.EmailSettings.EnableSignInWithUsername,
} else if method == model.USER_AUTH_SERVICE_LDAP { *utils.Cfg.EmailSettings.EnableSignInWithEmail,
uchan = Srv.Store.User().GetByAuth(loginId, model.USER_AUTH_SERVICE_LDAP) *utils.Cfg.LdapSettings.Enable,
} )
rdata := map[string]string{} rdata := map[string]string{}
if result := <-uchan; result.Err != nil { if result := <-uchan; result.Err != nil {

Просмотреть файл

@@ -84,6 +84,19 @@ func TestLogin(t *testing.T) {
th := Setup() th := Setup()
Client := th.CreateClient() Client := th.CreateClient()
enableSignInWithEmail := *utils.Cfg.EmailSettings.EnableSignInWithEmail
enableSignInWithUsername := *utils.Cfg.EmailSettings.EnableSignInWithUsername
enableLdap := *utils.Cfg.LdapSettings.Enable
defer func() {
*utils.Cfg.EmailSettings.EnableSignInWithEmail = enableSignInWithEmail
*utils.Cfg.EmailSettings.EnableSignInWithUsername = enableSignInWithUsername
*utils.Cfg.LdapSettings.Enable = enableLdap
}()
*utils.Cfg.EmailSettings.EnableSignInWithEmail = false
*utils.Cfg.EmailSettings.EnableSignInWithUsername = false
*utils.Cfg.LdapSettings.Enable = false
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team) rteam, _ := Client.CreateTeam(&team)
@@ -100,7 +113,12 @@ func TestLogin(t *testing.T) {
} }
} }
if result, err := Client.LoginByEmail(team.Name, user.Email, user.Password); err != nil { if _, err := Client.Login(user.Email, user.Password); err == nil {
t.Fatal("shouldn't be able to log in by email when disabled")
}
*utils.Cfg.EmailSettings.EnableSignInWithEmail = true
if result, err := Client.Login(user.Email, user.Password); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
if result.Data.(*model.User).Email != user.Email { if result.Data.(*model.User).Email != user.Email {
@@ -108,7 +126,12 @@ func TestLogin(t *testing.T) {
} }
} }
if result, err := Client.LoginByUsername(team.Name, user.Username, user.Password); err != nil { if _, err := Client.Login(user.Username, user.Password); err == nil {
t.Fatal("shouldn't be able to log in by username when disabled")
}
*utils.Cfg.EmailSettings.EnableSignInWithUsername = true
if result, err := Client.Login(user.Username, user.Password); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
if result.Data.(*model.User).Email != user.Email { if result.Data.(*model.User).Email != user.Email {
@@ -116,19 +139,19 @@ func TestLogin(t *testing.T) {
} }
} }
if _, err := Client.LoginByEmail(team.Name, user.Email, user.Password+"invalid"); err == nil { if _, err := Client.Login(user.Email, user.Password+"invalid"); err == nil {
t.Fatal("Invalid Password") t.Fatal("Invalid Password")
} }
if _, err := Client.LoginByUsername(team.Name, user.Username, user.Password+"invalid"); err == nil { if _, err := Client.Login(user.Username, user.Password+"invalid"); err == nil {
t.Fatal("Invalid Password") t.Fatal("Invalid Password")
} }
if _, err := Client.LoginByEmail(team.Name, "", user.Password); err == nil { if _, err := Client.Login("", user.Password); err == nil {
t.Fatal("should have failed") t.Fatal("should have failed")
} }
if _, err := Client.LoginByUsername(team.Name, "", user.Password); err == nil { if _, err := Client.Login("", user.Password); err == nil {
t.Fatal("should have failed") t.Fatal("should have failed")
} }
@@ -160,22 +183,35 @@ func TestLogin(t *testing.T) {
ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash) ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash)
if _, err := Client.LoginByEmail(team2.Name, ruser2.Data.(*model.User).Email, user2.Password); err != nil { if _, err := Client.Login(ruser2.Data.(*model.User).Email, user2.Password); err != nil {
t.Fatal("From verfied hash") t.Fatal("From verfied hash")
} }
Client.AuthToken = authToken Client.AuthToken = authToken
user3 := &model.User{
Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com",
Nickname: "Corey Hulen",
Username: "corey" + model.NewId(),
Password: "pwd",
AuthService: model.USER_AUTH_SERVICE_LDAP,
}
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id))
if _, err := Client.Login(user3.Id, user3.Password); err == nil {
t.Fatal("LDAP user should not be able to log in with LDAP disabled")
}
} }
func TestLoginWithDeviceId(t *testing.T) { func TestLoginWithDeviceId(t *testing.T) {
th := Setup().InitBasic() th := Setup().InitBasic()
Client := th.BasicClient Client := th.BasicClient
team := th.BasicTeam
user := th.BasicUser user := th.BasicUser
Client.Must(Client.Logout()) Client.Must(Client.Logout())
deviceId := model.NewId() deviceId := model.NewId()
if result, err := Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId); err != nil { if result, err := Client.LoginWithDevice(user.Email, user.Password, deviceId); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
ruser := result.Data.(*model.User) ruser := result.Data.(*model.User)
@@ -184,7 +220,7 @@ func TestLoginWithDeviceId(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} else { } else {
sessions := ssresult.Data.([]*model.Session) sessions := ssresult.Data.([]*model.Session)
if _, err := Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId); err != nil { if _, err := Client.LoginWithDevice(user.Email, user.Password, deviceId); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -198,13 +234,12 @@ func TestLoginWithDeviceId(t *testing.T) {
func TestSessions(t *testing.T) { func TestSessions(t *testing.T) {
th := Setup().InitBasic() th := Setup().InitBasic()
Client := th.BasicClient Client := th.BasicClient
team := th.BasicTeam
user := th.BasicUser user := th.BasicUser
Client.Must(Client.Logout()) Client.Must(Client.Logout())
deviceId := model.NewId() deviceId := model.NewId()
Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId) Client.LoginWithDevice(user.Email, user.Password, deviceId)
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
r1, err := Client.GetSessions(user.Id) r1, err := Client.GetSessions(user.Id)
if err != nil { if err != nil {
@@ -269,7 +304,7 @@ func TestGetUser(t *testing.T) {
LinkUserToTeam(ruser3.Data.(*model.User), rteam2.Data.(*model.Team)) LinkUserToTeam(ruser3.Data.(*model.User), rteam2.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser3.Data.(*model.User).Id)) store.Must(Srv.Store.User().VerifyEmail(ruser3.Data.(*model.User).Id))
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
rId := ruser.Data.(*model.User).Id rId := ruser.Data.(*model.User).Id
if result, err := Client.GetUser(rId, ""); err != nil { if result, err := Client.GetUser(rId, ""); err != nil {
@@ -333,7 +368,7 @@ func TestGetUser(t *testing.T) {
c.IpAddress = "cmd_line" c.IpAddress = "cmd_line"
UpdateRoles(c, ruser.Data.(*model.User), model.ROLE_SYSTEM_ADMIN) UpdateRoles(c, ruser.Data.(*model.User), model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
if _, err := Client.GetProfiles(rteam2.Data.(*model.Team).Id, ""); err != nil { if _, err := Client.GetProfiles(rteam2.Data.(*model.Team).Id, ""); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -374,7 +409,7 @@ func TestGetAudits(t *testing.T) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -427,7 +462,7 @@ func TestUserCreateImage(t *testing.T) {
LinkUserToTeam(user, team) LinkUserToTeam(user, team)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.DoApiGet("/users/"+user.Id+"/image", "", "") Client.DoApiGet("/users/"+user.Id+"/image", "", "")
@@ -471,7 +506,7 @@ func TestUserUploadProfileImage(t *testing.T) {
t.Fatal("Should have errored") t.Fatal("Should have errored")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr == nil { if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr == nil {
@@ -575,7 +610,7 @@ func TestUserUpdate(t *testing.T) {
t.Fatal("Should have errored") t.Fatal("Should have errored")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -615,7 +650,7 @@ func TestUserUpdate(t *testing.T) {
LinkUserToTeam(user2, team) LinkUserToTeam(user2, team)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Login(user2.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
user.Nickname = "Tim Timmy" user.Nickname = "Tim Timmy"
@@ -642,7 +677,7 @@ func TestUserUpdatePassword(t *testing.T) {
t.Fatal("Should have errored") t.Fatal("Should have errored")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
if _, err := Client.UpdateUserPassword("123", "pwd", "newpwd"); err == nil { if _, err := Client.UpdateUserPassword("123", "pwd", "newpwd"); err == nil {
t.Fatal("Should have errored") t.Fatal("Should have errored")
@@ -673,7 +708,7 @@ func TestUserUpdatePassword(t *testing.T) {
t.Fatal("LastPasswordUpdate should have changed") t.Fatal("LastPasswordUpdate should have changed")
} }
if _, err := Client.LoginByEmail(team.Name, user.Email, "newpwd"); err != nil { if _, err := Client.Login(user.Email, "newpwd"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -681,7 +716,7 @@ func TestUserUpdatePassword(t *testing.T) {
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
LinkUserToTeam(user2, team) LinkUserToTeam(user2, team)
Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Login(user2.Email, "pwd")
if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil { if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil {
t.Fatal("Should have errored") t.Fatal("Should have errored")
@@ -713,7 +748,7 @@ func TestUserUpdateRoles(t *testing.T) {
t.Fatal("Should have errored, not logged in") t.Fatal("Should have errored, not logged in")
} }
Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Login(user2.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if _, err := Client.UpdateUserRoles(data); err == nil { if _, err := Client.UpdateUserRoles(data); err == nil {
@@ -728,7 +763,7 @@ func TestUserUpdateRoles(t *testing.T) {
LinkUserToTeam(user3, team2) LinkUserToTeam(user3, team2)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) store.Must(Srv.Store.User().VerifyEmail(user3.Id))
Client.LoginByEmail(team2.Name, user3.Email, "pwd") Client.Login(user3.Email, "pwd")
Client.SetTeamId(team2.Id) Client.SetTeamId(team2.Id)
data["user_id"] = user2.Id data["user_id"] = user2.Id
@@ -737,7 +772,7 @@ func TestUserUpdateRoles(t *testing.T) {
t.Fatal("Should have errored, wrong team") t.Fatal("Should have errored, wrong team")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
data["user_id"] = "junk" data["user_id"] = "junk"
data["new_roles"] = "admin" data["new_roles"] = "admin"
@@ -771,7 +806,7 @@ func TestUserUpdateDeviceId(t *testing.T) {
LinkUserToTeam(user, team) LinkUserToTeam(user, team)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
deviceId := model.PUSH_NOTIFY_APPLE + ":1234567890" deviceId := model.PUSH_NOTIFY_APPLE + ":1234567890"
@@ -811,7 +846,7 @@ func TestUserUpdateActive(t *testing.T) {
t.Fatal("Should have errored, not logged in") t.Fatal("Should have errored, not logged in")
} }
Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Login(user2.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if _, err := Client.UpdateActive(user.Id, false); err == nil { if _, err := Client.UpdateActive(user.Id, false); err == nil {
@@ -828,14 +863,14 @@ func TestUserUpdateActive(t *testing.T) {
LinkUserToTeam(user2, team2) LinkUserToTeam(user2, team2)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) store.Must(Srv.Store.User().VerifyEmail(user3.Id))
Client.LoginByEmail(team2.Name, user3.Email, "pwd") Client.Login(user3.Email, "pwd")
Client.SetTeamId(team2.Id) Client.SetTeamId(team2.Id)
if _, err := Client.UpdateActive(user.Id, false); err == nil { if _, err := Client.UpdateActive(user.Id, false); err == nil {
t.Fatal("Should have errored, not yourself") t.Fatal("Should have errored, not yourself")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if _, err := Client.UpdateActive("junk", false); err == nil { if _, err := Client.UpdateActive("junk", false); err == nil {
@@ -859,7 +894,7 @@ func TestUserPermDelete(t *testing.T) {
LinkUserToTeam(user1, team) LinkUserToTeam(user1, team)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Name, user1.Email, "pwd") Client.Login(user1.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -1013,7 +1048,7 @@ func TestUserUpdateNotify(t *testing.T) {
t.Fatal("Should have errored - not logged in") t.Fatal("Should have errored - not logged in")
} }
Client.LoginByEmail(team.Name, user.Email, "pwd") Client.Login(user.Email, "pwd")
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
if result, err := Client.UpdateUserNotify(data); err != nil { if result, err := Client.UpdateUserNotify(data); err != nil {
@@ -1109,7 +1144,7 @@ func TestStatuses(t *testing.T) {
LinkUserToTeam(ruser2, rteam.Data.(*model.Team)) LinkUserToTeam(ruser2, rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser2.Id)) store.Must(Srv.Store.User().VerifyEmail(ruser2.Id))
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
Client.SetTeamId(team.Id) Client.SetTeamId(team.Id)
userIds := []string{ruser2.Id} userIds := []string{ruser2.Id}
@@ -1207,7 +1242,7 @@ func TestOAuthToEmail(t *testing.T) {
t.Fatal("should have failed - not logged in") t.Fatal("should have failed - not logged in")
} }
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
if _, err := Client.OAuthToEmail(m); err == nil { if _, err := Client.OAuthToEmail(m); err == nil {
t.Fatal("should have failed - empty data") t.Fatal("should have failed - empty data")
@@ -1248,7 +1283,7 @@ func TestLDAPToEmail(t *testing.T) {
LinkUserToTeam(ruser, rteam.Data.(*model.Team)) LinkUserToTeam(ruser, rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
m := map[string]string{} m := map[string]string{}
if _, err := Client.LDAPToEmail(m); err == nil { if _, err := Client.LDAPToEmail(m); err == nil {
@@ -1301,7 +1336,7 @@ func TestEmailToLDAP(t *testing.T) {
LinkUserToTeam(ruser, rteam.Data.(*model.Team)) LinkUserToTeam(ruser, rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
m := map[string]string{} m := map[string]string{}
if _, err := Client.EmailToLDAP(m); err == nil { if _, err := Client.EmailToLDAP(m); err == nil {
@@ -1438,7 +1473,7 @@ func TestGenerateMfaQrCode(t *testing.T) {
t.Fatal("should have failed - not logged in") t.Fatal("should have failed - not logged in")
} }
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
if _, err := Client.GenerateMfaQrCode(); err == nil { if _, err := Client.GenerateMfaQrCode(); err == nil {
t.Fatal("should have failed - not licensed") t.Fatal("should have failed - not licensed")
@@ -1476,7 +1511,7 @@ func TestUpdateMfa(t *testing.T) {
t.Fatal("should have failed - not logged in") t.Fatal("should have failed - not logged in")
} }
Client.LoginByEmail(team.Name, user.Email, user.Password) Client.Login(user.Email, user.Password)
if _, err := Client.UpdateMfa(true, ""); err == nil { if _, err := Client.UpdateMfa(true, ""); err == nil {
t.Fatal("should have failed - no token") t.Fatal("should have failed - no token")
@@ -1509,7 +1544,7 @@ func TestCheckMfa(t *testing.T) {
LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
if result, err := Client.CheckMfa(model.USER_AUTH_SERVICE_EMAIL, user.Email); err != nil { if result, err := Client.CheckMfa(user.Email); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
resp := result.Data.(map[string]string) resp := result.Data.(map[string]string)

Просмотреть файл

@@ -1560,7 +1560,7 @@
"translation": "Revoking sessionId=%v for userId=%v re-login with same device Id" "translation": "Revoking sessionId=%v for userId=%v re-login with same device Id"
}, },
{ {
"id": "api.user.login_by_email.sign_in.app_error", "id": "api.user.login.use_auth_service.app_error",
"translation": "Please sign in using {{.AuthService}}" "translation": "Please sign in using {{.AuthService}}"
}, },
{ {
@@ -3443,6 +3443,14 @@
"id": "store.sql_user.get_for_export.app_error", "id": "store.sql_user.get_for_export.app_error",
"translation": "We encountered an error while finding user profiles" "translation": "We encountered an error while finding user profiles"
}, },
{
"id": "store.sql_user.get_for_login.app_error",
"translation": "We couldn't find an existing account matching your credentials. This team may require an invite from the team owner to join."
},
{
"id": "store.sql_user.get_for_login.multiple_users",
"translation": "We found multiple users matching your credentials and were unable to log you in. Please contact an administrator."
},
{ {
"id": "store.sql_user.get_profiles.app_error", "id": "store.sql_user.get_profiles.app_error",
"translation": "We encountered an error while finding user profiles" "translation": "We encountered an error while finding user profiles"

Просмотреть файл

@@ -1560,7 +1560,7 @@
"translation": "Revocando sessionId=%v para el userId=%v vuelve a iniciar la sesión el el mismo dispositivo" "translation": "Revocando sessionId=%v para el userId=%v vuelve a iniciar la sesión el el mismo dispositivo"
}, },
{ {
"id": "api.user.login_by_email.sign_in.app_error", "id": "api.user.login.use_auth_service.app_error",
"translation": "Por favor inicia sesión usando {{.AuthService}}" "translation": "Por favor inicia sesión usando {{.AuthService}}"
}, },
{ {

Просмотреть файл

@@ -1428,7 +1428,7 @@
"translation": "Session sessionId=%v de l'utilisateur userId=%v révoquée re-connexion avec le même identifiant" "translation": "Session sessionId=%v de l'utilisateur userId=%v révoquée re-connexion avec le même identifiant"
}, },
{ {
"id": "api.user.login_by_email.sign_in.app_error", "id": "api.user.login.use_auth_service.app_error",
"translation": "Veuillez vous connecter en utilisant {{.AuthService}}" "translation": "Veuillez vous connecter en utilisant {{.AuthService}}"
}, },
{ {

Просмотреть файл

@@ -1564,7 +1564,7 @@
"translation": "userId=%vのsessionId=%vを無効化しました。同じデバイスIDでログインし直してください" "translation": "userId=%vのsessionId=%vを無効化しました。同じデバイスIDでログインし直してください"
}, },
{ {
"id": "api.user.login_by_email.sign_in.app_error", "id": "api.user.login.use_auth_service.app_error",
"translation": "{{.AuthService}}を使ってサインインしてください" "translation": "{{.AuthService}}を使ってサインインしてください"
}, },
{ {

Просмотреть файл

@@ -1520,7 +1520,7 @@
"translation": "Revogada sessionid=%v para userid=%v logue novamente com o mesmo id de dispositivo" "translation": "Revogada sessionid=%v para userid=%v logue novamente com o mesmo id de dispositivo"
}, },
{ {
"id": "api.user.login_by_email.sign_in.app_error", "id": "api.user.login.use_auth_service.app_error",
"translation": "Por favor logue usando {{.AuthService}}" "translation": "Por favor logue usando {{.AuthService}}"
}, },
{ {

Просмотреть файл

@@ -355,51 +355,21 @@ func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
return c.login(m) return c.login(m)
} }
func (c *Client) LoginByEmail(name string, email string, password string) (*Result, *AppError) { func (c *Client) Login(loginId string, password string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["name"] = name m["login_id"] = loginId
m["email"] = email
m["password"] = password m["password"] = password
return c.login(m) return c.login(m)
} }
func (c *Client) LoginByUsername(name string, username string, password string) (*Result, *AppError) { func (c *Client) LoginWithDevice(loginId string, password string, deviceId string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["name"] = name m["login_id"] = loginId
m["username"] = username
m["password"] = password
return c.login(m)
}
func (c *Client) LoginByEmailWithDevice(name string, email string, password string, deviceId string) (*Result, *AppError) {
m := make(map[string]string)
m["name"] = name
m["email"] = email
m["password"] = password m["password"] = password
m["device_id"] = deviceId m["device_id"] = deviceId
return c.login(m) return c.login(m)
} }
func (c *Client) LoginByLdap(userid string, password string, mfatoken string) (*Result, *AppError) {
m := make(map[string]string)
m["id"] = userid
m["password"] = password
m["token"] = mfatoken
if r, err := c.DoApiPost("/users/login_ldap", MapToJson(m)); err != nil {
return nil, err
} else {
c.AuthToken = r.Header.Get(HEADER_TOKEN)
c.AuthType = HEADER_BEARER
sessionToken := getCookie(SESSION_COOKIE_TOKEN, r)
if c.AuthToken != sessionToken.Value {
NewLocAppError("/users/login_ldap", "model.client.login.app_error", nil, "")
}
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
}
}
func (c *Client) login(m map[string]string) (*Result, *AppError) { func (c *Client) login(m map[string]string) (*Result, *AppError) {
if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil { if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil {
return nil, err return nil, err
@@ -430,9 +400,8 @@ func (c *Client) Logout() (*Result, *AppError) {
} }
} }
func (c *Client) CheckMfa(method, loginId string) (*Result, *AppError) { func (c *Client) CheckMfa(loginId string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
m["method"] = method
m["login_id"] = loginId m["login_id"] = loginId
if r, err := c.DoApiPost("/users/mfa", MapToJson(m)); err != nil { if r, err := c.DoApiPost("/users/mfa", MapToJson(m)); err != nil {

Просмотреть файл

@@ -716,6 +716,47 @@ func (us SqlUserStore) GetByUsername(username string) StoreChannel {
return storeChannel return storeChannel
} }
func (us SqlUserStore) GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled bool) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
params := map[string]interface{}{
"LoginId": loginId,
"AllowSignInWithUsername": allowSignInWithUsername,
"AllowSignInWithEmail": allowSignInWithEmail,
"LdapEnabled": ldapEnabled,
}
users := []*model.User{}
if _, err := us.GetReplica().Select(
&users,
`SELECT
*
FROM
Users
WHERE
(:AllowSignInWithUsername AND Username = :LoginId)
OR (:AllowSignInWithEmail AND Email = :LoginId)
OR (:LdapEnabled AND AuthService = '`+model.USER_AUTH_SERVICE_LDAP+`' AND AuthData = :LoginId)`,
params); err != nil {
result.Err = model.NewLocAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, err.Error())
} else if len(users) == 1 {
result.Data = users[0]
} else if len(users) > 1 {
result.Err = model.NewLocAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.multiple_users", nil, "")
} else {
result.Err = model.NewLocAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, "")
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (us SqlUserStore) VerifyEmail(userId string) StoreChannel { func (us SqlUserStore) VerifyEmail(userId string) StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)

Просмотреть файл

@@ -469,6 +469,82 @@ func TestUserStoreGetByUsername(t *testing.T) {
} }
} }
func TestUserStoreGetForLogin(t *testing.T) {
Setup()
u1 := &model.User{
Email: model.NewId(),
Username: model.NewId(),
AuthService: model.USER_AUTH_SERVICE_GITLAB,
AuthData: model.NewId(),
}
Must(store.User().Save(u1))
u2 := &model.User{
Email: model.NewId(),
Username: model.NewId(),
AuthService: model.USER_AUTH_SERVICE_LDAP,
AuthData: model.NewId(),
}
Must(store.User().Save(u2))
if result := <-store.User().GetForLogin(u1.Username, true, true, true); result.Err != nil {
t.Fatal("Should have gotten user by username", result.Err)
} else if result.Data.(*model.User).Id != u1.Id {
t.Fatal("Should have gotten user1 by username")
}
if result := <-store.User().GetForLogin(u1.Email, true, true, true); result.Err != nil {
t.Fatal("Should have gotten user by email", result.Err)
} else if result.Data.(*model.User).Id != u1.Id {
t.Fatal("Should have gotten user1 by email")
}
if result := <-store.User().GetForLogin(u2.AuthData, true, true, true); result.Err != nil {
t.Fatal("Should have gotten user by LDAP AuthData", result.Err)
} else if result.Data.(*model.User).Id != u2.Id {
t.Fatal("Should have gotten user2 by LDAP AuthData")
}
// prevent getting user by AuthData when they're not an LDAP user
if result := <-store.User().GetForLogin(u1.AuthData, true, true, true); result.Err == nil {
t.Fatal("Should not have gotten user by non-LDAP AuthData")
}
// prevent getting user when different login methods are disabled
if result := <-store.User().GetForLogin(u1.Username, false, true, true); result.Err == nil {
t.Fatal("Should have failed to get user1 by username")
}
if result := <-store.User().GetForLogin(u1.Email, true, false, true); result.Err == nil {
t.Fatal("Should have failed to get user1 by email")
}
if result := <-store.User().GetForLogin(u2.AuthData, true, true, false); result.Err == nil {
t.Fatal("Should have failed to get user3 by LDAP AuthData")
}
// test a special case where two users will have conflicting login information so we throw a special error
u3 := &model.User{
Email: model.NewId(),
Username: model.NewId(),
AuthService: model.USER_AUTH_SERVICE_LDAP,
AuthData: model.NewId(),
}
Must(store.User().Save(u3))
u4 := &model.User{
Email: model.NewId(),
Username: model.NewId(),
AuthService: model.USER_AUTH_SERVICE_LDAP,
AuthData: u3.Username,
}
Must(store.User().Save(u4))
if err := (<-store.User().GetForLogin(u3.Username, true, true, true)).Err; err == nil {
t.Fatal("Should have failed to get users with conflicting login information")
}
}
func TestUserStoreUpdatePassword(t *testing.T) { func TestUserStoreUpdatePassword(t *testing.T) {
Setup() Setup()

Просмотреть файл

@@ -137,6 +137,7 @@ type UserStore interface {
GetByEmail(email string) StoreChannel GetByEmail(email string) StoreChannel
GetByAuth(authData string, authService string) StoreChannel GetByAuth(authData string, authService string) StoreChannel
GetByUsername(username string) StoreChannel GetByUsername(username string) StoreChannel
GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled bool) StoreChannel
VerifyEmail(userId string) StoreChannel VerifyEmail(userId string) StoreChannel
GetEtagForProfiles(teamId string) StoreChannel GetEtagForProfiles(teamId string) StoreChannel
GetEtagForDirectProfiles(userId string) StoreChannel GetEtagForDirectProfiles(userId string) StoreChannel

Просмотреть файл

@@ -204,7 +204,7 @@ func TestIncomingWebhook(t *testing.T) {
c.RequestId = model.NewId() c.RequestId = model.NewId()
c.IpAddress = "cmd_line" c.IpAddress = "cmd_line"
api.UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) api.UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
ApiClient.LoginByEmail(team.Name, user.Email, "pwd") ApiClient.Login(user.Email, "pwd")
ApiClient.SetTeamId(team.Id) ApiClient.SetTeamId(team.Id)
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}

Просмотреть файл

@@ -746,7 +746,19 @@ export default class Client {
end(this.handleResponse.bind(this, 'getMe', success, error)); end(this.handleResponse.bind(this, 'getMe', success, error));
} }
login = (email, username, password, mfaToken, success, error) => { login = (loginId, password, mfaToken, success, error) => {
this.doLogin({login_id: loginId, password, token: mfaToken}, success, error);
this.track('api', 'api_users_login', '', 'login_id', loginId);
}
loginById = (id, password, mfaToken, success, error) => {
this.doLogin({id, password, token: mfaToken}, success, error);
this.track('api', 'api_users_login', '', 'id', id);
}
doLogin = (outgoingData, success, error) => {
var outer = this; // eslint-disable-line consistent-this var outer = this; // eslint-disable-line consistent-this
request. request.
@@ -754,7 +766,7 @@ export default class Client {
set(this.defaultHeaders). set(this.defaultHeaders).
type('application/json'). type('application/json').
accept('application/json'). accept('application/json').
send({email, password, username, token: mfaToken}). send(outgoingData).
end(this.handleResponse.bind( end(this.handleResponse.bind(
this, this,
'login', 'login',
@@ -773,39 +785,6 @@ export default class Client {
}, },
error error
)); ));
this.track('api', 'api_users_login', '', 'email', email);
}
loginByLdap = (ldapId, password, mfaToken, success, error) => {
var outer = this; // eslint-disable-line consistent-this
request.
post(`${this.getUsersRoute()}/login_ldap`).
set(this.defaultHeaders).
type('application/json').
accept('application/json').
send({id: ldapId, password, token: mfaToken}).
end(this.handleResponse.bind(
this,
'loginByLdap',
(data, res) => {
if (res && res.header) {
outer.token = res.header[HEADER_TOKEN];
if (outer.useToken) {
outer.defaultHeaders[HEADER_AUTH] = `${HEADER_BEARER} ${outer.token}`;
}
}
if (success) {
success(data, res);
}
},
error
));
this.track('api', 'api_users_loginLdap', '', 'email', ldapId);
} }
logout = (success, error) => { logout = (success, error) => {
@@ -819,10 +798,10 @@ export default class Client {
this.track('api', 'api_users_logout'); this.track('api', 'api_users_logout');
} }
checkMfa = (method, loginId, success, error) => { checkMfa = (loginId, success, error) => {
var data = {}; const data = {
data.method = method; login_id: loginId
data.login_id = loginId; };
request. request.
post(`${this.getUsersRoute()}/mfa`). post(`${this.getUsersRoute()}/mfa`).

Просмотреть файл

@@ -1,121 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react';
export default class LoginEmail extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: props.serverError
};
}
componentWillReceiveProps(nextProps) {
this.setState({serverError: nextProps.serverError});
}
handleSubmit(e) {
e.preventDefault();
var state = {};
const email = this.refs.email.value.trim();
if (!email) {
state.serverError = Utils.localizeMessage('login_email.emailReq', 'An email is required');
this.setState(state);
return;
}
const password = this.refs.password.value.trim();
if (!password) {
state.serverError = Utils.localizeMessage('login_email.pwdReq', 'A password is required');
this.setState(state);
return;
}
state.serverError = '';
this.setState(state);
this.props.submit(Constants.EMAIL_SERVICE, email, password);
}
render() {
let serverError;
let errorClass = '';
if (this.state.serverError) {
serverError = <label className='control-label'>{this.state.serverError}</label>;
errorClass = ' has-error';
}
let priorEmail = UserStore.getLastEmail();
let focusEmail = false;
let focusPassword = false;
if (priorEmail === '') {
focusEmail = true;
} else {
focusPassword = true;
}
const emailParam = Utils.getUrlParameter('email');
if (emailParam) {
priorEmail = decodeURIComponent(emailParam);
}
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
<div className={'form-group' + errorClass}>
{serverError}
</div>
<div className={'form-group' + errorClass}>
<input
autoFocus={focusEmail}
type='email'
className='form-control'
name='email'
defaultValue={priorEmail}
ref='email'
placeholder={Utils.localizeMessage('login_email.email', 'Email')}
spellCheck='false'
/>
</div>
<div className={'form-group' + errorClass}>
<input
autoFocus={focusPassword}
type='password'
className='form-control'
name='password'
ref='password'
placeholder={Utils.localizeMessage('login_email.pwd', 'Password')}
spellCheck='false'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary'
>
<FormattedMessage
id='login_email.signin'
defaultMessage='Sign in'
/>
</button>
</div>
</div>
</form>
);
}
}
LoginEmail.defaultProps = {
};
LoginEmail.propTypes = {
submit: React.PropTypes.func.isRequired,
serverError: React.PropTypes.string
};

Просмотреть файл

@@ -1,115 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react';
export default class LoginLdap extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: props.serverError
};
}
componentWillReceiveProps(nextProps) {
this.setState({serverError: nextProps.serverError});
}
handleSubmit(e) {
e.preventDefault();
const state = {};
const id = this.refs.id.value.trim();
if (!id) {
state.serverError = Utils.localizeMessage('login_ldap.idlReq', 'An LDAP ID is required');
this.setState(state);
return;
}
const password = this.refs.password.value.trim();
if (!password) {
state.serverError = Utils.localizeMessage('login_ldap.pwdReq', 'An LDAP password is required');
this.setState(state);
return;
}
state.serverError = '';
this.setState(state);
this.props.submit(Constants.LDAP_SERVICE, id, password);
}
render() {
let serverError;
let errorClass = '';
if (this.state.serverError) {
serverError = <label className='control-label'>{this.state.serverError}</label>;
errorClass = ' has-error';
}
let loginPlaceholder;
if (global.window.mm_config.LdapLoginFieldName) {
loginPlaceholder = global.window.mm_config.LdapLoginFieldName;
} else {
loginPlaceholder = Utils.localizeMessage('login_ldap.username', 'LDAP Username');
}
let passwordPlaceholder;
if (global.window.mm_config.LdapPasswordFieldName) {
passwordPlaceholder = global.window.mm_config.LdapPasswordFieldName;
} else {
passwordPlaceholder = Utils.localizeMessage('login_ldap.pwd', 'LDAP Password');
}
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
<div className={'form-group' + errorClass}>
{serverError}
</div>
<div className={'form-group' + errorClass}>
<input
autoFocus={true}
className='form-control'
ref='id'
placeholder={loginPlaceholder}
spellCheck='false'
/>
</div>
<div className={'form-group' + errorClass}>
<input
type='password'
className='form-control'
ref='password'
placeholder={passwordPlaceholder}
spellCheck='false'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary'
>
<FormattedMessage
id='login_ldap.signin'
defaultMessage='Sign in'
/>
</button>
</div>
</div>
</form>
);
}
}
LoginLdap.defaultProps = {
};
LoginLdap.propTypes = {
serverError: React.PropTypes.string,
submit: React.PropTypes.func.isRequired
};

Просмотреть файл

@@ -31,7 +31,7 @@ export default class LoginMfa extends React.Component {
state.serverError = ''; state.serverError = '';
this.setState(state); this.setState(state);
this.props.submit(this.props.method, this.props.loginId, this.props.password, token); this.props.submit(this.props.loginId, this.props.password, token);
} }
render() { render() {
let serverError; let serverError;
@@ -85,7 +85,6 @@ LoginMfa.defaultProps = {
}; };
LoginMfa.propTypes = { LoginMfa.propTypes = {
method: React.PropTypes.string.isRequired,
loginId: React.PropTypes.string.isRequired, loginId: React.PropTypes.string.isRequired,
password: React.PropTypes.string.isRequired, password: React.PropTypes.string.isRequired,
submit: React.PropTypes.func.isRequired submit: React.PropTypes.func.isRequired

Просмотреть файл

@@ -1,121 +0,0 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react';
export default class LoginUsername extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
serverError: props.serverError
};
}
componentWillReceiveProps(nextProps) {
this.setState({serverError: nextProps.serverError});
}
handleSubmit(e) {
e.preventDefault();
const state = {};
const username = this.refs.username.value.trim();
if (!username) {
state.serverError = Utils.localizeMessage('login_username.usernameReq', 'A username is required');
this.setState(state);
return;
}
const password = this.refs.password.value.trim();
if (!password) {
state.serverError = Utils.localizeMessage('login_username.pwdReq', 'A password is required');
this.setState(state);
return;
}
state.serverError = '';
this.setState(state);
this.props.submit(Constants.USERNAME_SERVICE, username, password);
}
render() {
let serverError;
let errorClass = '';
if (this.state.serverError) {
serverError = <label className='control-label'>{this.state.serverError}</label>;
errorClass = ' has-error';
}
let priorUsername = UserStore.getLastUsername();
let focusUsername = false;
let focusPassword = false;
if (priorUsername === '') {
focusUsername = true;
} else {
focusPassword = true;
}
const emailParam = Utils.getUrlParameter('email');
if (emailParam) {
priorUsername = decodeURIComponent(emailParam);
}
return (
<form onSubmit={this.handleSubmit}>
<div className='signup__email-container'>
<div className={'form-group' + errorClass}>
{serverError}
</div>
<div className={'form-group' + errorClass}>
<input
autoFocus={focusUsername}
type='username'
className='form-control'
name='username'
defaultValue={priorUsername}
ref='username'
placeholder={Utils.localizeMessage('login_username.username', 'Username')}
spellCheck='false'
/>
</div>
<div className={'form-group' + errorClass}>
<input
autoFocus={focusPassword}
type='password'
className='form-control'
name='password'
ref='password'
placeholder={Utils.localizeMessage('login_username.pwd', 'Password')}
spellCheck='false'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary'
>
<FormattedMessage
id='login_username.signin'
defaultMessage='Sign in'
/>
</button>
</div>
</div>
</form>
);
}
}
LoginUsername.defaultProps = {
};
LoginUsername.propTypes = {
serverError: React.PropTypes.string,
submit: React.PropTypes.func.isRequired
};

Просмотреть файл

@@ -1,13 +1,11 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
import LoginEmail from './components/login_email.jsx';
import LoginUsername from './components/login_username.jsx';
import LoginLdap from './components/login_ldap.jsx';
import LoginMfa from './components/login_mfa.jsx'; import LoginMfa from './components/login_mfa.jsx';
import ErrorBar from 'components/error_bar.jsx'; import ErrorBar from 'components/error_bar.jsx';
import FormError from 'components/form_error.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx'; import * as GlobalActions from 'action_creators/global_actions.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
import Client from 'utils/web_client.jsx'; import Client from 'utils/web_client.jsx';
@@ -30,10 +28,16 @@ export default class Login extends React.Component {
this.submit = this.submit.bind(this); this.submit = this.submit.bind(this);
this.finishSignin = this.finishSignin.bind(this); this.finishSignin = this.finishSignin.bind(this);
const state = {}; this.handleLoginIdChange = this.handleLoginIdChange.bind(this);
state.showMfa = false; this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.state = state;
this.state = {
loginId: '', // the browser will set a default for this
password: '',
showMfa: false
};
} }
componentDidMount() { componentDidMount() {
document.title = global.window.mm_config.SiteName; document.title = global.window.mm_config.SiteName;
@@ -41,31 +45,97 @@ export default class Login extends React.Component {
browserHistory.push('/select_team'); browserHistory.push('/select_team');
} }
} }
preSubmit(method, loginId, password) {
if (global.window.mm_config.EnableMultifactorAuthentication !== 'true') { preSubmit(e) {
this.submit(method, loginId, password, ''); e.preventDefault();
const loginId = this.state.loginId.trim();
if (!loginId) {
const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
const usernameSigninEnabled = global.window.mm_config.EnableSignInWithUsername === 'true';
const emailSigninEnabled = global.window.mm_config.EnableSignInWithEmail === 'true';
this.setState({
serverError: (
<FormattedMessage
id='login.loginIdRequired'
defaultMessage='A {type} is required'
values={{
type: this.createLoginPlaceholder(emailSigninEnabled, usernameSigninEnabled, ldapEnabled)
}}
/>
)
});
return; return;
} }
Client.checkMfa(method, loginId, const password = this.state.password.trim();
if (!password) {
this.setState({
serverError: (
<FormattedMessage
id='login.passwordRequired'
defaultMessage='A password is required'
/>
)
});
return;
}
if (global.window.mm_config.EnableMultifactorAuthentication !== 'true') {
this.submit(loginId, password, '');
return;
}
Client.checkMfa(
loginId,
(data) => { (data) => {
if (data.mfa_required === 'true') { if (data.mfa_required === 'true') {
this.setState({showMfa: true, method, loginId, password}); this.setState({showMfa: true});
} else { } else {
this.submit(method, loginId, password, ''); this.submit(loginId, password, '');
} }
}, },
(err) => { (err) => {
if (method === Constants.EMAIL_SERVICE) { this.setState({serverError: err.message});
this.setState({serverEmailError: err.message}); }
} else if (method === Constants.USERNAME_SERVICE) { );
this.setState({serverUsernameError: err.message}); }
} else if (method === Constants.LDAP_SERVICE) {
this.setState({serverLdapError: err.message}); submit(loginId, password, token) {
this.setState({showMfa: false, serverError: null});
Client.webLogin(
loginId,
password,
token,
() => {
this.finishSignin();
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
browserHistory.push('/should_verify_email?&email=' + encodeURIComponent(loginId));
return;
} else if (err.id === 'store.sql_user.get_for_login.app_error' ||
err.id === 'ent.ldap.do_login.user_not_registered.app_error' ||
err.id === 'ent.ldap.do_login.user_filtered.app_error') {
this.setState({
serverError: (
<FormattedMessage
id='login.userNotFound'
defaultMessage="We couldn't find an existing account matching your login credentials."
/>
)
});
} else {
this.setState({serverError: err.message});
} }
} }
); );
} }
finishSignin() { finishSignin() {
GlobalActions.emitInitialLoad( GlobalActions.emitInitialLoad(
() => { () => {
@@ -74,72 +144,18 @@ export default class Login extends React.Component {
); );
} }
submit(method, loginId, password, token) { handleLoginIdChange(e) {
this.setState({showMfa: false, serverEmailError: null, serverUsernameError: null, serverLdapError: null}); this.setState({
loginId: e.target.value
});
}
if (method === Constants.EMAIL_SERVICE) { handlePasswordChange(e) {
Client.webLogin( this.setState({
loginId, password: e.target.value
null, });
password,
token,
() => {
UserStore.setLastEmail(loginId);
this.finishSignin();
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
browserHistory.push('/should_verify_email?&email=' + encodeURIComponent(loginId));
return;
} }
this.setState({serverEmailError: err.message});
}
);
} else if (method === Constants.USERNAME_SERVICE) {
Client.webLogin(
null,
loginId,
password,
token,
() => {
UserStore.setLastUsername(loginId);
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
this.finishSignin();
}
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
this.setState({serverUsernameError: Utils.localizeMessage('login_username.verifyEmailError', 'Please verify your email address. Check your inbox for an email.')});
} else if (err.id === 'store.sql_user.get_by_username.app_error') {
this.setState({serverUsernameError: Utils.localizeMessage('login_username.userNotFoundError', 'We couldn\'t find an existing account matching your username for this team.')});
} else {
this.setState({serverUsernameError: err.message});
}
}
);
} else if (method === Constants.LDAP_SERVICE) {
Client.loginByLdap(
loginId,
password,
token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
this.finishSignin();
}
},
(err) => {
this.setState({serverLdapError: err.message});
}
);
}
}
createCustomLogin() { createCustomLogin() {
if (global.window.mm_license.IsLicensed === 'true' && if (global.window.mm_license.IsLicensed === 'true' &&
global.window.mm_license.CustomBrand === 'true' && global.window.mm_license.CustomBrand === 'true' &&
@@ -158,6 +174,36 @@ export default class Login extends React.Component {
return null; return null;
} }
createLoginPlaceholder(emailSigninEnabled, usernameSigninEnabled, ldapEnabled) {
const loginPlaceholders = [];
if (emailSigninEnabled) {
loginPlaceholders.push(Utils.localizeMessage('login.email', 'Email'));
}
if (usernameSigninEnabled) {
loginPlaceholders.push(Utils.localizeMessage('login.username', 'Username'));
}
if (ldapEnabled) {
if (global.window.mm_config.LdapLoginFieldName) {
loginPlaceholders.push(global.window.mm_config.LdapLoginFieldName);
} else {
loginPlaceholders.push(Utils.localizeMessage('login.ldap_username', 'LDAP Username'));
}
}
if (loginPlaceholders.length >= 2) {
return loginPlaceholders.slice(0, loginPlaceholders.length - 1).join(', ') +
Utils.localizeMessage('login.placeholderOr', ' or ') +
loginPlaceholders[loginPlaceholders.length - 1];
} else if (loginPlaceholders.length === 1) {
return loginPlaceholders[0];
}
return '';
}
createLoginOptions() { createLoginOptions() {
const extraParam = Utils.getUrlParameter('extra'); const extraParam = Utils.getUrlParameter('extra');
let extraBox = ''; let extraBox = '';
@@ -248,77 +294,53 @@ export default class Login extends React.Component {
); );
} }
let emailLogin; let login = null;
if (emailSigninEnabled) { if (emailSigninEnabled || usernameSigninEnabled || ldapEnabled) {
emailLogin = ( let errorClass = '';
<LoginEmail if (this.state.serverError) {
serverError={this.state.serverEmailError} errorClass = ' has-error';
submit={this.preSubmit} }
/>
);
if (oauthLogins.length > 0) { login = (
emailLogin = ( <form onSubmit={this.preSubmit}>
<div> <div className='signup__email-container'>
<div className='or__container'> <FormError error={this.state.serverError}/>
<div className={'form-group' + errorClass}>
<input
className='form-control'
name='loginId'
value={this.state.loginId}
onChange={this.handleLoginIdChange}
placeholder={this.createLoginPlaceholder(emailSigninEnabled, usernameSigninEnabled, ldapEnabled)}
spellCheck='false'
/>
</div>
<div className={'form-group' + errorClass}>
<input
type='password'
className='form-control'
name='password'
value={this.state.password}
onChange={this.handlePasswordChange}
placeholder={Utils.localizeMessage('login.password', 'Password')}
spellCheck='false'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary'
>
<FormattedMessage <FormattedMessage
id='login.or' id='login.signIn'
defaultMessage='or' defaultMessage='Sign in'
/> />
</button>
</div> </div>
{emailLogin}
</div> </div>
</form>
); );
} }
}
let usernameLogin;
if (usernameSigninEnabled) {
usernameLogin = (
<LoginUsername
serverError={this.state.serverUsernameError}
submit={this.preSubmit}
/>
);
if (emailSigninEnabled || oauthLogins.length > 0) {
usernameLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
{usernameLogin}
</div>
);
}
}
let ldapLogin;
if (ldapEnabled) {
ldapLogin = (
<LoginLdap
serverError={this.state.serverLdapError}
submit={this.preSubmit}
/>
);
if (emailSigninEnabled || usernameSigninEnabled || oauthLogins.length > 0) {
ldapLogin = (
<div>
<div className='or__container'>
<FormattedMessage
id='login.or'
defaultMessage='or'
/>
</div>
{ldapLogin}
</div>
);
}
}
const userSignUp = ( const userSignUp = (
<div> <div>
@@ -358,14 +380,13 @@ export default class Login extends React.Component {
<div> <div>
{extraBox} {extraBox}
{oauthLogins} {oauthLogins}
{emailLogin} {login}
{usernameLogin}
{ldapLogin}
{userSignUp} {userSignUp}
{forgotPassword} {forgotPassword}
</div> </div>
); );
} }
render() { render() {
let content; let content;
let customContent; let customContent;
@@ -373,7 +394,6 @@ export default class Login extends React.Component {
if (this.state.showMfa) { if (this.state.showMfa) {
content = ( content = (
<LoginMfa <LoginMfa
method={this.state.method}
loginId={this.state.loginId} loginId={this.state.loginId}
password={this.state.password} password={this.state.password}
submit={this.submit} submit={this.submit}

Просмотреть файл

@@ -2,7 +2,6 @@
// See License.txt for license information. // See License.txt for license information.
import LoadingScreen from 'components/loading_screen.jsx'; import LoadingScreen from 'components/loading_screen.jsx';
import LoginLdap from 'components/login/components/login_ldap.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx'; import * as GlobalActions from 'action_creators/global_actions.jsx';
import BrowserStore from 'stores/browser_store.jsx'; import BrowserStore from 'stores/browser_store.jsx';
@@ -25,7 +24,6 @@ class SignupUserComplete extends React.Component {
super(props); super(props);
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
this.handleLdapSignup = this.handleLdapSignup.bind(this);
this.state = { this.state = {
data: '', data: '',
@@ -150,26 +148,6 @@ class SignupUserComplete extends React.Component {
}); });
} }
handleLdapSignup(method, loginId, password, token) {
Client.loginByLdap(loginId, password, token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
GlobalActions.emitInitialLoad(
() => {
browserHistory.push('/select_team');
}
);
}
},
(err) => {
this.setState({serverError: err.message});
}
);
}
handleSubmit(e) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
@@ -268,15 +246,13 @@ class SignupUserComplete extends React.Component {
this.state.data, this.state.data,
this.state.hash, this.state.hash,
this.state.inviteId, this.state.inviteId,
() => { (data) => {
Client.track('signup', 'signup_user_02_complete'); Client.track('signup', 'signup_user_02_complete');
Client.login( Client.loginById(
user.email, data.id,
null,
user.password, user.password,
'', '',
() => { () => {
UserStore.setLastEmail(user.email);
if (this.state.hash > 0) { if (this.state.hash > 0) {
BrowserStore.setGlobalItem(this.state.hash, JSON.stringify({usedBefore: true})); BrowserStore.setGlobalItem(this.state.hash, JSON.stringify({usedBefore: true}));
} }
@@ -455,21 +431,6 @@ class SignupUserComplete extends React.Component {
); );
} }
let ldapSignup;
if (global.window.mm_config.EnableLdap === 'true' && global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.LDAP) {
ldapSignup = (
<div className='inner__content'>
<h5><strong>
<FormattedMessage
id='signup_user_completed.withLdap'
defaultMessage='With your LDAP credentials'
/>
</strong></h5>
<LoginLdap submit={this.handleLdapSignup}/>
</div>
);
}
let emailSignup; let emailSignup;
if (global.window.mm_config.EnableSignUpWithEmail === 'true') { if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
emailSignup = ( emailSignup = (
@@ -533,7 +494,7 @@ class SignupUserComplete extends React.Component {
); );
} }
if (signupMessage.length > 0 && (emailSignup || ldapSignup)) { if (signupMessage.length > 0 && emailSignup) {
signupMessage = ( signupMessage = (
<div> <div>
{signupMessage} {signupMessage}
@@ -547,21 +508,7 @@ class SignupUserComplete extends React.Component {
); );
} }
if (ldapSignup && emailSignup) { if (signupMessage.length === 0 && !emailSignup) {
ldapSignup = (
<div>
{ldapSignup}
<div className='or__container'>
<FormattedMessage
id='signup_user_completed.or'
defaultMessage='or'
/>
</div>
</div>
);
}
if (signupMessage.length === 0 && !emailSignup && !ldapSignup) {
emailSignup = ( emailSignup = (
<div> <div>
<FormattedMessage <FormattedMessage
@@ -586,7 +533,6 @@ class SignupUserComplete extends React.Component {
if (this.state.noOpenServerError) { if (this.state.noOpenServerError) {
signupMessage = null; signupMessage = null;
ldapSignup = null;
emailSignup = null; emailSignup = null;
terms = null; terms = null;
} }
@@ -620,7 +566,6 @@ class SignupUserComplete extends React.Component {
/> />
</h4> </h4>
{signupMessage} {signupMessage}
{ldapSignup}
{emailSignup} {emailSignup}
{serverError} {serverError}
{terms} {terms}

Просмотреть файл

@@ -953,41 +953,29 @@
"login.changed": " Sign-in method changed successfully", "login.changed": " Sign-in method changed successfully",
"login.create": "Create one now", "login.create": "Create one now",
"login.createTeam": "Create a new team", "login.createTeam": "Create a new team",
"login.email": "Email",
"login.find": "Find your other teams", "login.find": "Find your other teams",
"login.forgot": "I forgot my password", "login.forgot": "I forgot my password",
"login.loginIdRequired": "A {type} is required",
"login.gitlab": "with GitLab", "login.gitlab": "with GitLab",
"login.google": "with Google Apps", "login.google": "with Google Apps",
"login.ldapUsername": "LDAP Username",
"login.noAccount": "Don't have an account? ", "login.noAccount": "Don't have an account? ",
"login.on": "on {siteName}", "login.on": "on {siteName}",
"login.or": "or", "login.or": "or",
"login.password": "Password",
"login.passwordChanged": " Password updated successfully", "login.passwordChanged": " Password updated successfully",
"login.passwordRequired": "A password is required",
"login.session_expired": " Your session has expired. Please login again.", "login.session_expired": " Your session has expired. Please login again.",
"login.signIn": "Sign in",
"login.signTo": "Sign in to:", "login.signTo": "Sign in to:",
"login.username": "Username",
"login.userNotFound": "We couldn't find an existing account matching your login credentials.",
"login.verified": " Email Verified", "login.verified": " Email Verified",
"login_email.badTeam": "Bad team name",
"login_email.email": "Email",
"login_email.emailReq": "An email is required",
"login_email.pwd": "Password",
"login_email.pwdReq": "A password is required",
"login_email.signin": "Sign in",
"login_ldap.badTeam": "Bad team name",
"login_ldap.idlReq": "An LDAP ID is required",
"login_ldap.pwd": "LDAP Password",
"login_ldap.pwdReq": "An LDAP password is required",
"login_ldap.signin": "Sign in",
"login_ldap.username": "LDAP Username",
"login_mfa.enterToken": "To complete the sign in process, please enter a token from your smartphone's authenticator", "login_mfa.enterToken": "To complete the sign in process, please enter a token from your smartphone's authenticator",
"login_mfa.submit": "Submit", "login_mfa.submit": "Submit",
"login_mfa.token": "MFA Token", "login_mfa.token": "MFA Token",
"login_mfa.tokenReq": "Please enter an MFA token", "login_mfa.tokenReq": "Please enter an MFA token",
"login_username.badTeam": "Bad team name",
"login_username.pwd": "Password",
"login_username.pwdReq": "A password is required",
"login_username.signin": "Sign in",
"login_username.userNotFoundError": "We couldn't find an existing account matching your username for this team.",
"login_username.username": "Username",
"login_username.usernameReq": "A username is required",
"login_username.verifyEmailError": "Please verify your email address. Check your inbox for an email.",
"member_item.makeAdmin": "Make Admin", "member_item.makeAdmin": "Make Admin",
"member_item.member": "Member", "member_item.member": "Member",
"member_list.noUsersAdd": "No users to add.", "member_list.noUsersAdd": "No users to add.",

Просмотреть файл

@@ -953,41 +953,27 @@
"login.changed": " Cambiado el método de inicio de sesión satisfactoriamente", "login.changed": " Cambiado el método de inicio de sesión satisfactoriamente",
"login.create": "Crea una ahora", "login.create": "Crea una ahora",
"login.createTeam": "Crear un nuevo equipo", "login.createTeam": "Crear un nuevo equipo",
"login.email": "Correo electrónico",
"login.find": "Encuentra tus otros equipos", "login.find": "Encuentra tus otros equipos",
"login.forgot": "Olvide mi contraseña", "login.forgot": "Olvide mi contraseña",
"login.gitlab": "con GitLab", "login.gitlab": "con GitLab",
"login.google": "con Google Apps", "login.google": "con Google Apps",
"login.ldapUsername": "Usuario LDAP",
"login.noAccount": "¿No tienes una cuenta? ", "login.noAccount": "¿No tienes una cuenta? ",
"login.on": "en {siteName}", "login.on": "en {siteName}",
"login.or": "o", "login.or": "o",
"login.password": "Contraseña",
"login.passwordChanged": " La contraseña ha sido actualizada", "login.passwordChanged": " La contraseña ha sido actualizada",
"login.passwordRequired": "La contraseña es obligatoria",
"login.session_expired": " Tu sesión ha expirado. Por favor inicia sesión nuevamente.", "login.session_expired": " Tu sesión ha expirado. Por favor inicia sesión nuevamente.",
"login.signIn": "Entrar",
"login.signTo": "Ingresar a:", "login.signTo": "Ingresar a:",
"login.username": "Nombre de usuario",
"login.verified": " Correo electrónico Verificado", "login.verified": " Correo electrónico Verificado",
"login_email.badTeam": "Nombre de Equipo inválido",
"login_email.email": "Correo electrónico",
"login_email.emailReq": "El correo electrónico es obligatorio",
"login_email.pwd": "Contraseña",
"login_email.pwdReq": "La contraseña es obligatoria",
"login_email.signin": "Entrar",
"login_ldap.badTeam": "Nombre de Equipo inválido",
"login_ldap.idlReq": "El ID de LDAP es obligatorio",
"login_ldap.pwd": "Contraseña LDAP",
"login_ldap.pwdReq": "La contraseña LDAP es obligatoria",
"login_ldap.signin": "Entrar",
"login_ldap.username": "Usuario LDAP",
"login_mfa.enterToken": "Para completar el proceso de inicio de sesión, por favor ingresa el token provisto por el autenticador de tu teléfono inteligente", "login_mfa.enterToken": "Para completar el proceso de inicio de sesión, por favor ingresa el token provisto por el autenticador de tu teléfono inteligente",
"login_mfa.submit": "Enviar", "login_mfa.submit": "Enviar",
"login_mfa.token": "Token AMF", "login_mfa.token": "Token AMF",
"login_mfa.tokenReq": "Por favor ingresa un token AMF", "login_mfa.tokenReq": "Por favor ingresa un token AMF",
"login_username.badTeam": "Mal nombre de equipo",
"login_username.pwd": "Contraseña",
"login_username.pwdReq": "La contraseña es obligatoria",
"login_username.signin": "Ingresar",
"login_username.userNotFoundError": "No encontramos una cuenta existente que coincida con tu nombre de usuario en este equipo.",
"login_username.username": "Nombre de usuario",
"login_username.usernameReq": "El nombre de usuario es obligatorio",
"login_username.verifyEmailError": "Por favor válida tu dirección de correo electrónico. Te hemos enviado un correo, revisa tu bandeja de entrada.",
"member_item.makeAdmin": "Convertir en Admin de Equipo", "member_item.makeAdmin": "Convertir en Admin de Equipo",
"member_item.member": "Miembro", "member_item.member": "Miembro",
"member_list.noUsersAdd": "No hay usuarios que agregar.", "member_list.noUsersAdd": "No hay usuarios que agregar.",

Просмотреть файл

@@ -818,36 +818,22 @@
"login.changed": "Méthode de connexion changée", "login.changed": "Méthode de connexion changée",
"login.create": "Créer maintenant", "login.create": "Créer maintenant",
"login.createTeam": "Créer une nouvelle équipe", "login.createTeam": "Créer une nouvelle équipe",
"login.email": "Adresse électronique",
"login.find": "Trouver vos autres équipes", "login.find": "Trouver vos autres équipes",
"login.forgot": "Mot de passe perdu", "login.forgot": "Mot de passe perdu",
"login.gitlab": "avec GitLab", "login.gitlab": "avec GitLab",
"login.google": "avec Google Apps", "login.google": "avec Google Apps",
"login.ldapUsername": "Utilisateur LDAP",
"login.noAccount": "Pas de compte utilisateur ?", "login.noAccount": "Pas de compte utilisateur ?",
"login.on": "sur {siteName}", "login.on": "sur {siteName}",
"login.or": "ou", "login.or": "ou",
"login.password": "Mot de passe",
"login.passwordRequired": "Le mot de passe est obligatoire",
"login.session_expired": "Votre session a expiré. Merci de vous reconnecter.", "login.session_expired": "Votre session a expiré. Merci de vous reconnecter.",
"login.signIn": "Connexion",
"login.signTo": "Se connecter à :", "login.signTo": "Se connecter à :",
"login.username": "Nom d'utilisateur",
"login.verified": "Adresse électronique vérifiée", "login.verified": "Adresse électronique vérifiée",
"login_email.badTeam": "Erreur dans le nom de l'équipe",
"login_email.email": "Adresse électronique",
"login_email.emailReq": "Une adresse électronique est obligatoire",
"login_email.pwd": "Mot de passe",
"login_email.pwdReq": "Le mot de passe est obligatoire",
"login_email.signin": "Connexion",
"login_ldap.badTeam": "Erreur dans le nom de l'équipe",
"login_ldap.idlReq": "Un identifiant LDAP est obligatoire",
"login_ldap.pwd": "Mot de passe LDAP",
"login_ldap.pwdReq": "Un mot de passe LDAP est obligatoire",
"login_ldap.signin": "Connexion",
"login_ldap.username": "Utilisateur LDAP",
"login_username.badTeam": "Erreur dans le nom de l'équipe",
"login_username.pwd": "Mot de passe",
"login_username.pwdReq": "Mot de passe obligatoire",
"login_username.signin": "Connexion",
"login_username.userNotFoundError": "Impossible de trouver un compte utilisateur à ce nom dans cette équipe.",
"login_username.username": "Nom d'utilisateur",
"login_username.usernameReq": "Le nom d'utilisateur est obligatoire",
"login_username.verifyEmailError": "Vérifiez votre adresse électronique. Cherchez le courriel dans votre boîte de réception.",
"member_item.makeAdmin": "Passer Administrateur", "member_item.makeAdmin": "Passer Administrateur",
"member_item.member": "Membre", "member_item.member": "Membre",
"member_list.noUsersAdd": "Aucun utilisateur à ajouter.", "member_list.noUsersAdd": "Aucun utilisateur à ajouter.",

Просмотреть файл

@@ -915,40 +915,26 @@
"login.changed": "サインイン方法が変更されました", "login.changed": "サインイン方法が変更されました",
"login.create": "ただいま作成しています", "login.create": "ただいま作成しています",
"login.createTeam": "新しいチームを作成する", "login.createTeam": "新しいチームを作成する",
"login.email": "電子メールアドレス",
"login.find": "参加している他のチームを探す", "login.find": "参加している他のチームを探す",
"login.forgot": "パスワードを忘れました", "login.forgot": "パスワードを忘れました",
"login.gitlab": "GitLabでログインする", "login.gitlab": "GitLabでログインする",
"login.google": "Google Appsでログインする", "login.google": "Google Appsでログインする",
"login.ldapUsername": "LDAPユーザー名",
"login.noAccount": "アカウントを持っていますか? ", "login.noAccount": "アカウントを持っていますか? ",
"login.on": "{siteName}にて", "login.on": "{siteName}にて",
"login.or": "または", "login.or": "または",
"login.password": "パスワード",
"login.passwordRequired": "パスワードは必須です",
"login.session_expired": " あなたのセッションは有効期限が切れました。再度ログインしてください。", "login.session_expired": " あなたのセッションは有効期限が切れました。再度ログインしてください。",
"login.signIn": "利用登録",
"login.signTo": "利用登録先: ", "login.signTo": "利用登録先: ",
"login.username": "LDAPユーザー名",
"login.verified": " 電子メールアドレスが確認されました", "login.verified": " 電子メールアドレスが確認されました",
"login_email.badTeam": "無効なチーム名です",
"login_email.email": "電子メールアドレス",
"login_email.emailReq": "電子メールアドレスは必須です",
"login_email.pwd": "パスワード",
"login_email.pwdReq": "パスワードは必須です",
"login_email.signin": "利用登録",
"login_ldap.badTeam": "無効なチーム名です",
"login_ldap.idlReq": "LDAP IDは必須です",
"login_ldap.pwd": "LDAPパスワード",
"login_ldap.pwdReq": "LDAPパスワードは必須です",
"login_ldap.signin": "利用登録",
"login_ldap.username": "LDAPユーザー名",
"login_mfa.enterToken": "利用登録を完了するには、スマートフォンのauthenticatorからのトークンを入力してください。", "login_mfa.enterToken": "利用登録を完了するには、スマートフォンのauthenticatorからのトークンを入力してください。",
"login_mfa.submit": "送信する", "login_mfa.submit": "送信する",
"login_mfa.token": "MFAトークン", "login_mfa.token": "MFAトークン",
"login_mfa.tokenReq": "MFAトークンを入力してください", "login_mfa.tokenReq": "MFAトークンを入力してください",
"login_username.badTeam": "無効なチーム名です",
"login_username.pwd": "パスワード",
"login_username.pwdReq": "パスワードは必須です",
"login_username.signin": "利用登録",
"login_username.userNotFoundError": "このチームにはあなたのユーザー名に合致するアカウントはありません。",
"login_username.username": "ユーザー名",
"login_username.usernameReq": "ユーザー名は必須です",
"login_username.verifyEmailError": "電子メールアドレスを確認します。電子メールの受信ボックスを見てみてください。",
"member_item.makeAdmin": "管理者にする", "member_item.makeAdmin": "管理者にする",
"member_item.member": "メンバー", "member_item.member": "メンバー",
"member_list.noUsersAdd": "ユーザーは追加されません。", "member_list.noUsersAdd": "ユーザーは追加されません。",

Просмотреть файл

@@ -911,40 +911,26 @@
"login.changed": " Método de login alterada com sucesso", "login.changed": " Método de login alterada com sucesso",
"login.create": "Crie um agora", "login.create": "Crie um agora",
"login.createTeam": "Criar uma nova equipe", "login.createTeam": "Criar uma nova equipe",
"login.email": "E-mail",
"login.find": "Encontre suas outras equipes", "login.find": "Encontre suas outras equipes",
"login.forgot": "Eu esqueci a minha senha", "login.forgot": "Eu esqueci a minha senha",
"login.gitlab": "com GitLab", "login.gitlab": "com GitLab",
"login.google": "com Google Apps", "login.google": "com Google Apps",
"login.ldapUsername": "Usuário LDAP",
"login.noAccount": "Não tem uma conta? ", "login.noAccount": "Não tem uma conta? ",
"login.on": "no {siteName}", "login.on": "no {siteName}",
"login.or": "ou", "login.or": "ou",
"login.password": "Senha",
"login.passwordRequired": "É necessário uma senha",
"login.session_expired": " Sua sessão expirou. Por favor faça login novamente.", "login.session_expired": " Sua sessão expirou. Por favor faça login novamente.",
"login.signIn": "Login",
"login.signTo": "Login em:", "login.signTo": "Login em:",
"login.username": "Usuário",
"login.verified": " Email Verificado", "login.verified": " Email Verificado",
"login_email.badTeam": "Nome ruim de equipe",
"login_email.email": "E-mail",
"login_email.emailReq": "Um email é necessário",
"login_email.pwd": "Senha",
"login_email.pwdReq": "É necessário uma senha",
"login_email.signin": "Login",
"login_ldap.badTeam": "Nome de equipe ruim",
"login_ldap.idlReq": "Um ID LDAP é necessário",
"login_ldap.pwd": "Senha LDAP",
"login_ldap.pwdReq": "É necessário uma senha do LDAP",
"login_ldap.signin": "Login",
"login_ldap.username": "Usuário LDAP",
"login_mfa.enterToken": "Para completar o login em processo, por favor entre um token do seu autenticador no smartphone", "login_mfa.enterToken": "Para completar o login em processo, por favor entre um token do seu autenticador no smartphone",
"login_mfa.submit": "Enviar", "login_mfa.submit": "Enviar",
"login_mfa.token": "Token MFA", "login_mfa.token": "Token MFA",
"login_mfa.tokenReq": "Por favor entre um token MFA", "login_mfa.tokenReq": "Por favor entre um token MFA",
"login_username.badTeam": "Nome de equipe ruim",
"login_username.pwd": "Senha",
"login_username.pwdReq": "É necessário uma senha",
"login_username.signin": "Login",
"login_username.userNotFoundError": "Não foi possível encontrar uma conta existente que corresponda ao seu usuário para esta equipe.",
"login_username.username": "Usuário",
"login_username.usernameReq": "Um nome de usuário é necessário",
"login_username.verifyEmailError": "Por favor verifique seu endereço de email. Verifique por um email na sua caixa de entrada.",
"member_item.makeAdmin": "Tornar Admin", "member_item.makeAdmin": "Tornar Admin",
"member_item.member": "Membro", "member_item.member": "Membro",
"member_list.noUsersAdd": "Nenhum usuário para adicionar.", "member_list.noUsersAdd": "Nenhum usuário para adicionar.",

Просмотреть файл

@@ -6,7 +6,6 @@ import EventEmitter from 'events';
import Constants from 'utils/constants.jsx'; import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes; const ActionTypes = Constants.ActionTypes;
import BrowserStore from './browser_store.jsx';
const CHANGE_EVENT = 'change'; const CHANGE_EVENT = 'change';
const CHANGE_EVENT_SESSIONS = 'change_sessions'; const CHANGE_EVENT_SESSIONS = 'change_sessions';
@@ -97,22 +96,6 @@ class UserStoreClass extends EventEmitter {
return null; return null;
} }
getLastEmail() {
return BrowserStore.getGlobalItem('last_email', '');
}
setLastEmail(email) {
BrowserStore.setGlobalItem('last_email', email);
}
getLastUsername() {
return BrowserStore.getGlobalItem('last_username', '');
}
setLastUsername(username) {
BrowserStore.setGlobalItem('last_username', username);
}
hasProfile(userId) { hasProfile(userId) {
return this.getProfiles()[userId] != null; return this.getProfiles()[userId] != null;
} }

Просмотреть файл

@@ -66,7 +66,33 @@ describe('Client.User', function() {
function() { function() {
client.login( client.login(
user.email, user.email,
user.password,
null, null,
function(data) {
assert.equal(data.id.length > 0, true);
assert.equal(data.email, user.email);
done();
},
function(err) {
done(new Error(err.message));
}
);
},
function(err) {
done(new Error(err.message));
}
);
});
it('loginById', function(done) {
var client = TestHelper.createClient();
var user = TestHelper.fakeUser();
client.createUser(
user,
function(newUser) {
assert.equal(user.email, newUser.email);
client.loginById(
newUser.id,
user.password, user.password,
null, null,
function(data) { function(data) {
@@ -86,40 +112,13 @@ describe('Client.User', function() {
}); });
it('loginByUsername', function(done) { it('loginByUsername', function(done) {
var client = TestHelper.createClient();
var user = TestHelper.fakeUser();
client.createUser(
user,
function() {
client.login(
null,
user.username,
user.password,
null,
function(data) {
assert.equal(data.id.length > 0, true);
assert.equal(data.email, user.email);
done();
},
function(err) {
done(new Error(err.message));
}
);
},
function(err) {
done(new Error(err.message));
}
);
});
it('loginByLdap', function(done) {
var client = TestHelper.createClient(); var client = TestHelper.createClient();
client.enableLogErrorsToConsole(false); // Disabling since this unit test causes an error client.enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
var user = TestHelper.fakeUser(); var user = TestHelper.fakeUser();
client.createUser( client.createUser(
user, user,
function() { function() {
client.loginByLdap( client.login(
user.username, user.username,
user.password, user.password,
null, null,
@@ -127,7 +126,8 @@ describe('Client.User', function() {
done(new Error()); done(new Error());
}, },
function(err) { function(err) {
assert.equal(err.id, 'api.user.login_ldap.disabled.app_error'); // should error out because logging in by username is disabled by default
assert.equal(err.id, 'store.sql_user.get_for_login.app_error');
done(); done();
} }
); );
@@ -372,7 +372,6 @@ describe('Client.User', function() {
it('checkMfa', function(done) { it('checkMfa', function(done) {
TestHelper.initBasic(() => { TestHelper.initBasic(() => {
TestHelper.basicClient().checkMfa( TestHelper.basicClient().checkMfa(
'email',
TestHelper.generateId(), TestHelper.generateId(),
function(data) { function(data) {
assert.equal(data.mfa_required, 'false'); assert.equal(data.mfa_required, 'false');

Просмотреть файл

@@ -128,7 +128,6 @@ class TestHelperClass {
outer.basicClient().setTeamId(outer.basict.id); outer.basicClient().setTeamId(outer.basict.id);
outer.basicClient().login( outer.basicClient().login(
rteamSignup.user.email, rteamSignup.user.email,
null,
password, password,
null, null,
function() { function() {

Просмотреть файл

@@ -39,14 +39,13 @@ class WebClientClass extends Client {
// not sure why but super.login doesn't work if using an () => arrow functions. // not sure why but super.login doesn't work if using an () => arrow functions.
// I think this might be a webpack issue. // I think this might be a webpack issue.
webLogin(email, username, password, token, success, error) { webLogin(loginId, password, token, success, error) {
this.login( this.login(
email, loginId,
username,
password, password,
token, token,
(data) => { (data) => {
this.track('api', 'api_users_login_success', '', 'email', data.email); this.track('api', 'api_users_login_success', '', 'login_id', loginId);
BrowserStore.signalLogin(); BrowserStore.signalLogin();
if (success) { if (success) {
@@ -54,7 +53,7 @@ class WebClientClass extends Client {
} }
}, },
(err) => { (err) => {
this.track('api', 'api_users_login_fail', name, 'email', email); this.track('api', 'api_users_login_fail', name, 'login_id', loginId);
if (error) { if (error) {
error(err); error(err);
} }