diff options
author | Jesús Espino <jespinog@gmail.com> | 2018-04-18 22:46:10 +0200 |
---|---|---|
committer | Christopher Speller <crspeller@gmail.com> | 2018-04-18 13:46:10 -0700 |
commit | 0910eae31de8ed7b409654515dbd11f5c86dbf71 (patch) | |
tree | 3d5fb47842693cd2ea1a357994c85d04902773a7 /api4 | |
parent | b13a228b0451098ea32933a36fe64566e366583d (diff) | |
download | chat-0910eae31de8ed7b409654515dbd11f5c86dbf71.tar.gz chat-0910eae31de8ed7b409654515dbd11f5c86dbf71.tar.bz2 chat-0910eae31de8ed7b409654515dbd11f5c86dbf71.zip |
MM-9779: Incorporate a Token into the invitations system (#8604)
* Incorporate a Token into the invitations system
* Adding unit tests
* Fixing some api4 client tests
* Removing unnecesary hash validation
* Change the Hash concept on invitations with tokenId
* Not send invitation if it wasn't able to create the Token
* Fixing some naming problems
* Changing the hash query params received from the client side
* Removed unneded data param in the token usage
Diffstat (limited to 'api4')
-rw-r--r-- | api4/team.go | 7 | ||||
-rw-r--r-- | api4/team_test.go | 56 | ||||
-rw-r--r-- | api4/user.go | 6 | ||||
-rw-r--r-- | api4/user_test.go | 137 |
4 files changed, 101 insertions, 105 deletions
diff --git a/api4/team.go b/api4/team.go index 33cd57fbb..94035a770 100644 --- a/api4/team.go +++ b/api4/team.go @@ -376,15 +376,14 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) { } func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request) { - hash := r.URL.Query().Get("hash") - data := r.URL.Query().Get("data") + tokenId := r.URL.Query().Get("token") inviteId := r.URL.Query().Get("invite_id") var member *model.TeamMember var err *model.AppError - if len(hash) > 0 && len(data) > 0 { - member, err = c.App.AddTeamMemberByHash(c.Session.UserId, hash, data) + if len(tokenId) > 0 { + member, err = c.App.AddTeamMemberByToken(c.Session.UserId, tokenId) } else if len(inviteId) > 0 { member, err = c.App.AddTeamMemberByInviteId(inviteId, c.Session.UserId) } else { diff --git a/api4/team_test.go b/api4/team_test.go index 991dee148..cdf201771 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -13,6 +13,7 @@ import ( "encoding/base64" + "github.com/mattermost/mattermost-server/app" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/utils" "github.com/stretchr/testify/assert" @@ -1361,17 +1362,16 @@ func TestAddTeamMember(t *testing.T) { _, resp = Client.AddTeamMember(team.Id, otherUser.Id) CheckNoError(t, resp) - // by hash and data + // by token Client.Login(otherUser.Email, otherUser.Password) - dataObject := make(map[string]string) - dataObject["time"] = fmt.Sprintf("%v", model.GetMillis()) - dataObject["id"] = team.Id + token := model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": team.Id}), + ) + <-th.App.Srv.Store.Token().Save(token) - data := model.MapToJson(dataObject) - hashed := utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) - - tm, resp = Client.AddTeamMemberFromInvite(hashed, data, "") + tm, resp = Client.AddTeamMemberFromInvite(token.Token, "") CheckNoError(t, resp) if tm == nil { @@ -1386,36 +1386,42 @@ func TestAddTeamMember(t *testing.T) { t.Fatal("team ids should have matched") } - tm, resp = Client.AddTeamMemberFromInvite("junk", data, "") + if result := <-th.App.Srv.Store.Token().GetByToken(token.Token); result.Err == nil { + t.Fatal("The token must be deleted after be used") + } + + tm, resp = Client.AddTeamMemberFromInvite("junk", "") CheckBadRequestStatus(t, resp) if tm != nil { t.Fatal("should have not returned team member") } - _, resp = Client.AddTeamMemberFromInvite(hashed, "junk", "") - CheckBadRequestStatus(t, resp) - - // expired data of more than 50 hours - dataObject["time"] = fmt.Sprintf("%v", model.GetMillis()-1000*60*60*50) - data = model.MapToJson(dataObject) - hashed = utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) + // expired token of more than 50 hours + token = model.NewToken(app.TOKEN_TYPE_TEAM_INVITATION, "") + token.CreateAt = model.GetMillis() - 1000*60*60*50 + <-th.App.Srv.Store.Token().Save(token) - tm, resp = Client.AddTeamMemberFromInvite(hashed, data, "") + tm, resp = Client.AddTeamMemberFromInvite(token.Token, "") CheckBadRequestStatus(t, resp) + th.App.DeleteToken(token) // invalid team id - dataObject["id"] = GenerateTestId() - data = model.MapToJson(dataObject) - hashed = utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) - - tm, resp = Client.AddTeamMemberFromInvite(hashed, data, "") - CheckBadRequestStatus(t, resp) + testId := GenerateTestId() + token = model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": testId}), + ) + <-th.App.Srv.Store.Token().Save(token) + + tm, resp = Client.AddTeamMemberFromInvite(token.Token, "") + CheckNotFoundStatus(t, resp) + th.App.DeleteToken(token) // by invite_id Client.Login(otherUser.Email, otherUser.Password) - tm, resp = Client.AddTeamMemberFromInvite("", "", team.InviteId) + tm, resp = Client.AddTeamMemberFromInvite("", team.InviteId) CheckNoError(t, resp) if tm == nil { @@ -1430,7 +1436,7 @@ func TestAddTeamMember(t *testing.T) { t.Fatal("team ids should have matched") } - tm, resp = Client.AddTeamMemberFromInvite("", "", "junk") + tm, resp = Client.AddTeamMemberFromInvite("", "junk") CheckNotFoundStatus(t, resp) if tm != nil { diff --git a/api4/user.go b/api4/user.go index f5c56656c..e13bf9448 100644 --- a/api4/user.go +++ b/api4/user.go @@ -73,15 +73,15 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - hash := r.URL.Query().Get("h") + tokenId := r.URL.Query().Get("t") inviteId := r.URL.Query().Get("iid") // No permission check required var ruser *model.User var err *model.AppError - if len(hash) > 0 { - ruser, err = c.App.CreateUserWithHash(user, hash, r.URL.Query().Get("d")) + if len(tokenId) > 0 { + ruser, err = c.App.CreateUserWithToken(user, tokenId) } else if len(inviteId) > 0 { ruser, err = c.App.CreateUserWithInviteId(user, inviteId) } else if c.IsSystemAdmin() { diff --git a/api4/user_test.go b/api4/user_test.go index 27219726b..5b4d8890a 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -4,15 +4,14 @@ package api4 import ( - "fmt" "net/http" "strconv" "testing" "time" + "github.com/mattermost/mattermost-server/app" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" - "github.com/mattermost/mattermost-server/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -81,23 +80,20 @@ func TestCreateUser(t *testing.T) { } } -func TestCreateUserWithHash(t *testing.T) { +func TestCreateUserWithToken(t *testing.T) { th := Setup().InitBasic().InitSystemAdmin() defer th.TearDown() Client := th.Client - t.Run("CreateWithHashHappyPath", func(t *testing.T) { + t.Run("CreateWithTokenHappyPath", func(t *testing.T) { user := model.User{Email: th.GenerateTestEmail(), Nickname: "Corey Hulen", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID} - props := make(map[string]string) - props["email"] = user.Email - props["id"] = th.BasicTeam.Id - props["display_name"] = th.BasicTeam.DisplayName - props["name"] = th.BasicTeam.Name - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data := model.MapToJson(props) - hash := utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) - - ruser, resp := Client.CreateUserWithHash(&user, hash, data) + token := model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": user.Email}), + ) + <-th.App.Srv.Store.Token().Save(token) + + ruser, resp := Client.CreateUserWithToken(&user, token.Token) CheckNoError(t, resp) CheckCreatedStatus(t, resp) @@ -110,78 +106,73 @@ func TestCreateUserWithHash(t *testing.T) { t.Fatal("did not clear roles") } CheckUserSanitization(t, ruser) + if result := <-th.App.Srv.Store.Token().GetByToken(token.Token); result.Err == nil { + t.Fatal("The token must be deleted after be used") + } + + if result := <-th.App.Srv.Store.Token().GetByToken(token.Token); result.Err == nil { + t.Fatal("The token must be deleted after be used") + } + + if teams, err := th.App.GetTeamsForUser(ruser.Id); err != nil || len(teams) == 0 { + t.Fatal("The user must have teams") + } else if teams[0].Id != th.BasicTeam.Id { + t.Fatal("The user joined team must be the team provided.") + } }) - t.Run("NoHashAndNoData", func(t *testing.T) { + t.Run("NoToken", func(t *testing.T) { user := model.User{Email: th.GenerateTestEmail(), Nickname: "Corey Hulen", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID} - props := make(map[string]string) - props["email"] = user.Email - props["id"] = th.BasicTeam.Id - props["display_name"] = th.BasicTeam.DisplayName - props["name"] = th.BasicTeam.Name - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data := model.MapToJson(props) - hash := utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) - - _, resp := Client.CreateUserWithHash(&user, "", data) + token := model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": user.Email}), + ) + <-th.App.Srv.Store.Token().Save(token) + defer th.App.DeleteToken(token) + + _, resp := Client.CreateUserWithToken(&user, "") CheckBadRequestStatus(t, resp) - CheckErrorMessage(t, resp, "api.user.create_user.missing_hash_or_data.app_error") - - _, resp = Client.CreateUserWithHash(&user, hash, "") - CheckBadRequestStatus(t, resp) - CheckErrorMessage(t, resp, "api.user.create_user.missing_hash_or_data.app_error") + CheckErrorMessage(t, resp, "api.user.create_user.missing_token.app_error") }) - t.Run("HashExpired", func(t *testing.T) { + t.Run("TokenExpired", func(t *testing.T) { user := model.User{Email: th.GenerateTestEmail(), Nickname: "Corey Hulen", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID} timeNow := time.Now() past49Hours := timeNow.Add(-49*time.Hour).UnixNano() / int64(time.Millisecond) - - props := make(map[string]string) - props["email"] = user.Email - props["id"] = th.BasicTeam.Id - props["display_name"] = th.BasicTeam.DisplayName - props["name"] = th.BasicTeam.Name - props["time"] = fmt.Sprintf("%v", past49Hours) - data := model.MapToJson(props) - hash := utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) - - _, resp := Client.CreateUserWithHash(&user, hash, data) - CheckInternalErrorStatus(t, resp) + token := model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": user.Email}), + ) + token.CreateAt = past49Hours + <-th.App.Srv.Store.Token().Save(token) + defer th.App.DeleteToken(token) + + _, resp := Client.CreateUserWithToken(&user, token.Token) + CheckBadRequestStatus(t, resp) CheckErrorMessage(t, resp, "api.user.create_user.signup_link_expired.app_error") }) - t.Run("WrongHash", func(t *testing.T) { + t.Run("WrongToken", func(t *testing.T) { user := model.User{Email: th.GenerateTestEmail(), Nickname: "Corey Hulen", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID} - props := make(map[string]string) - props["email"] = user.Email - props["id"] = th.BasicTeam.Id - props["display_name"] = th.BasicTeam.DisplayName - props["name"] = th.BasicTeam.Name - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data := model.MapToJson(props) - hash := utils.HashSha256(fmt.Sprintf("%v:%v", data, "WrongHash")) - - _, resp := Client.CreateUserWithHash(&user, hash, data) - CheckInternalErrorStatus(t, resp) + + _, resp := Client.CreateUserWithToken(&user, "wrong") + CheckBadRequestStatus(t, resp) CheckErrorMessage(t, resp, "api.user.create_user.signup_link_invalid.app_error") }) t.Run("EnableUserCreationDisable", func(t *testing.T) { user := model.User{Email: th.GenerateTestEmail(), Nickname: "Corey Hulen", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID} - props := make(map[string]string) - props["email"] = user.Email - props["id"] = th.BasicTeam.Id - props["display_name"] = th.BasicTeam.DisplayName - props["name"] = th.BasicTeam.Name - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data := model.MapToJson(props) - hash := utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) + token := model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": user.Email}), + ) + <-th.App.Srv.Store.Token().Save(token) + defer th.App.DeleteToken(token) th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableUserCreation = false }) - _, resp := Client.CreateUserWithHash(&user, hash, data) + _, resp := Client.CreateUserWithToken(&user, token.Token) CheckNotImplementedStatus(t, resp) CheckErrorMessage(t, resp, "api.user.create_user.signup_email_disabled.app_error") @@ -191,18 +182,15 @@ func TestCreateUserWithHash(t *testing.T) { t.Run("EnableOpenServerDisable", func(t *testing.T) { user := model.User{Email: th.GenerateTestEmail(), Nickname: "Corey Hulen", Password: "hello1", Username: GenerateTestUsername(), Roles: model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID} - props := make(map[string]string) - props["email"] = user.Email - props["id"] = th.BasicTeam.Id - props["display_name"] = th.BasicTeam.DisplayName - props["name"] = th.BasicTeam.Name - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data := model.MapToJson(props) - hash := utils.HashSha256(fmt.Sprintf("%v:%v", data, th.App.Config().EmailSettings.InviteSalt)) + token := model.NewToken( + app.TOKEN_TYPE_TEAM_INVITATION, + model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": user.Email}), + ) + <-th.App.Srv.Store.Token().Save(token) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = false }) - ruser, resp := Client.CreateUserWithHash(&user, hash, data) + ruser, resp := Client.CreateUserWithToken(&user, token.Token) CheckNoError(t, resp) CheckCreatedStatus(t, resp) @@ -215,6 +203,9 @@ func TestCreateUserWithHash(t *testing.T) { t.Fatal("did not clear roles") } CheckUserSanitization(t, ruser) + if result := <-th.App.Srv.Store.Token().GetByToken(token.Token); result.Err == nil { + t.Fatal("The token must be deleted after be used") + } }) } |