From 4f0f3845e4d93191df3ddea1bd03d72e4076ea8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20V=C3=A9lez=20Vidal?= Date: Fri, 1 Sep 2023 14:25:07 +0200 Subject: [PATCH] MM-54182 - add correct information to session table from mobile devices (#24353) * MM-54182 - add correct information to session table from mobile devices * improve comments around helper function --------- Co-authored-by: Mattermost Build --- server/channels/api4/user.go | 6 ++++-- server/channels/app/user_agent.go | 12 +++++++++++- server/channels/app/user_agent_test.go | 7 ++++++- server/channels/utils/utils.go | 19 ++++++++++++++++++ server/channels/utils/utils_test.go | 27 ++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/server/channels/api4/user.go b/server/channels/api4/user.go index 053de727ee..7f20e105a1 100644 --- a/server/channels/api4/user.go +++ b/server/channels/api4/user.go @@ -1950,7 +1950,8 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAuditWithUserId(user.Id, "authenticated") - err = c.App.DoLogin(c.AppContext, w, r, user, deviceId, false, false, false) + isMobileDevice := utils.IsMobileRequest(r) + err = c.App.DoLogin(c.AppContext, w, r, user, deviceId, isMobileDevice, false, false) if err != nil { c.Err = err return @@ -2044,7 +2045,8 @@ func loginCWS(c *Context, w http.ResponseWriter, r *http.Request) { } audit.AddEventParameterAuditable(auditRec, "user", user) c.LogAuditWithUserId(user.Id, "authenticated") - err = c.App.DoLogin(c.AppContext, w, r, user, "", false, false, false) + isMobileDevice := utils.IsMobileRequest(r) + err = c.App.DoLogin(c.AppContext, w, r, user, "", isMobileDevice, false, false) if err != nil { c.LogErrorByCode(err) http.Redirect(w, r, *c.App.Config().ServiceSettings.SiteURL, http.StatusFound) diff --git a/server/channels/app/user_agent.go b/server/channels/app/user_agent.go index 301edd807a..42d56c7f05 100644 --- a/server/channels/app/user_agent.go +++ b/server/channels/app/user_agent.go @@ -84,6 +84,11 @@ func getOSName(ua *uasurfer.UserAgent) string { } func getBrowserVersion(ua *uasurfer.UserAgent, userAgentString string) string { + if index := strings.Index(userAgentString, "Mattermost Mobile/"); index != -1 { + afterVersion := userAgentString[index+len("Mattermost Mobile/"):] + return strings.Fields(afterVersion)[0] + } + if index := strings.Index(userAgentString, "Mattermost/"); index != -1 { afterVersion := userAgentString[index+len("Mattermost/"):] return strings.Fields(afterVersion)[0] @@ -123,10 +128,15 @@ var browserNames = map[uasurfer.BrowserName]string{ func getBrowserName(ua *uasurfer.UserAgent, userAgentString string) string { browser := ua.Browser.Name - if strings.Contains(userAgentString, "Mattermost") { + if strings.Contains(userAgentString, "Electron") || + (strings.Contains(userAgentString, "Mattermost") && !strings.Contains(userAgentString, "Mattermost Mobile")) { return "Desktop App" } + if strings.Contains(userAgentString, "Mattermost Mobile") { + return "Mobile App" + } + if strings.Contains(userAgentString, "mmctl") { return "mmctl" } diff --git a/server/channels/app/user_agent_test.go b/server/channels/app/user_agent_test.go index aa730d50fe..1a40b4dfd9 100644 --- a/server/channels/app/user_agent_test.go +++ b/server/channels/app/user_agent_test.go @@ -33,6 +33,7 @@ var testUserAgents = []testUserAgent{ {"Safari 9", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38"}, {"Safari 8", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12"}, {"Safari Mobile", "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1"}, + {"Mobile App", "Mattermost Mobile/2.7.0+482 (Android; 13; sdk_gphone64_arm64)"}, } func TestGetPlatformName(t *testing.T) { @@ -53,6 +54,7 @@ func TestGetPlatformName(t *testing.T) { "Macintosh", "Macintosh", "iPhone", + "Linux", } for i, userAgent := range testUserAgents { @@ -83,6 +85,7 @@ func TestGetOSName(t *testing.T) { "Mac OS", "Mac OS", "iOS", + "Android", } for i, userAgent := range testUserAgents { @@ -103,7 +106,7 @@ func TestGetBrowserName(t *testing.T) { "Chrome", "mmctl", "Desktop App", - "Chrome", + "Desktop App", "Edge", "Internet Explorer", "Internet Explorer", @@ -113,6 +116,7 @@ func TestGetBrowserName(t *testing.T) { "Safari", "Safari", "Safari", + "Mobile App", } for i, userAgent := range testUserAgents { @@ -143,6 +147,7 @@ func TestGetBrowserVersion(t *testing.T) { "11.0", "8.0.7", "9.0", + "2.7.0+482", } for i, userAgent := range testUserAgents { diff --git a/server/channels/utils/utils.go b/server/channels/utils/utils.go index 7e0cd20e1c..ede1c4b249 100644 --- a/server/channels/utils/utils.go +++ b/server/channels/utils/utils.go @@ -208,6 +208,25 @@ func IsValidMobileAuthRedirectURL(config *model.Config, redirectURL string) bool return false } +// This will only return TRUE if the request comes from a mobile running the Mobile App. +// If the request comes from a mobile using the browser, it will return FALSE. +func IsMobileRequest(r *http.Request) bool { + userAgent := r.UserAgent() + if userAgent == "" { + return false + } + + // Check if the User-Agent contain keywords found in mobile devices running the mobile App + mobileKeywords := []string{"Mobile", "Android", "iOS", "iPhone", "iPad"} + for _, keyword := range mobileKeywords { + if strings.Contains(userAgent, keyword) { + return true + } + } + + return false +} + // RoundOffToZeroes converts all digits to 0 except the 1st one. // Special case: If there is only 1 digit, then returns 0. func RoundOffToZeroes(n float64) int64 { diff --git a/server/channels/utils/utils_test.go b/server/channels/utils/utils_test.go index 0c9784f8d6..6a531d3a82 100644 --- a/server/channels/utils/utils_test.go +++ b/server/channels/utils/utils_test.go @@ -5,6 +5,7 @@ package utils import ( "net/http" + "net/http/httptest" "testing" "github.com/stretchr/testify/assert" @@ -279,6 +280,32 @@ func TestRoundOffToZeroes(t *testing.T) { } } +func TestIsMobileRequest(t *testing.T) { + testCases := []struct { + userAgent string + expected bool + }{ + // Test cases with mobile devices + {"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)", true}, + {"Mozilla/5.0 (Android 12; Mobile)", true}, + {"Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.99 Mobile Safari/537.36", true}, + + // Test cases with NO mobile devices + {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", false}, + {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", false}, + } + + for _, tc := range testCases { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("User-Agent", tc.userAgent) + + result := IsMobileRequest(req) + if result != tc.expected { + t.Errorf("User-Agent: %s, expected: %v, got: %v", tc.userAgent, tc.expected, result) + } + } +} + func TestRoundOffToZeroesResolution(t *testing.T) { messageGranularity := 3 storageGranularity := 8