summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/admin.go4
-rw-r--r--app/analytics.go2
-rw-r--r--app/app.go2
-rw-r--r--app/apptestlib.go10
-rw-r--r--app/audit.go2
-rw-r--r--app/authentication.go23
-rw-r--r--app/authorization.go2
-rw-r--r--app/authorization_test.go2
-rw-r--r--app/auto_channels.go2
-rw-r--r--app/auto_constants.go2
-rw-r--r--app/auto_environment.go2
-rw-r--r--app/auto_posts.go2
-rw-r--r--app/auto_teams.go2
-rw-r--r--app/auto_users.go2
-rw-r--r--app/brand.go2
-rw-r--r--app/channel.go4
-rw-r--r--app/command.go3
-rw-r--r--app/command_away.go2
-rw-r--r--app/command_echo.go2
-rw-r--r--app/command_expand_collapse.go2
-rw-r--r--app/command_invite_people.go2
-rw-r--r--app/command_join.go2
-rw-r--r--app/command_loadtest.go2
-rw-r--r--app/command_logout.go2
-rw-r--r--app/command_me.go2
-rw-r--r--app/command_msg.go2
-rw-r--r--app/command_offline.go2
-rw-r--r--app/command_online.go2
-rw-r--r--app/command_shortcuts.go2
-rw-r--r--app/command_shrug.go2
-rw-r--r--app/compliance.go2
-rw-r--r--app/diagnostics.go2
-rw-r--r--app/diagnostics_test.go2
-rw-r--r--app/email.go17
-rw-r--r--app/email_batching.go2
-rw-r--r--app/email_batching_test.go2
-rw-r--r--app/email_test.go2
-rw-r--r--app/emoji.go206
-rw-r--r--app/file.go2
-rw-r--r--app/file_test.go2
-rw-r--r--app/import.go2
-rw-r--r--app/import_test.go2
-rw-r--r--app/ldap.go80
-rw-r--r--app/license.go2
-rw-r--r--app/license_test.go2
-rw-r--r--app/login.go2
-rw-r--r--app/notification.go2
-rw-r--r--app/notification_test.go2
-rw-r--r--app/oauth.go649
-rw-r--r--app/oauth_test.go2
-rw-r--r--app/post.go14
-rw-r--r--app/preference.go2
-rw-r--r--app/reaction.go16
-rw-r--r--app/saml.go2
-rw-r--r--app/security_update_check.go2
-rw-r--r--app/server.go2
-rw-r--r--app/session.go2
-rw-r--r--app/session_test.go2
-rw-r--r--app/slackimport.go2
-rw-r--r--app/slackimport_test.go2
-rw-r--r--app/status.go2
-rw-r--r--app/team.go33
-rw-r--r--app/team_test.go2
-rw-r--r--app/user.go6
-rw-r--r--app/user_test.go2
-rw-r--r--app/web_conn.go2
-rw-r--r--app/web_hub.go2
-rw-r--r--app/webhook.go2
-rw-r--r--app/webrtc.go87
-rw-r--r--app/websocket_router.go2
-rw-r--r--app/webtrc.go2
71 files changed, 1170 insertions, 96 deletions
diff --git a/app/admin.go b/app/admin.go
index b86eb5993..103c4617b 100644
--- a/app/admin.go
+++ b/app/admin.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -136,7 +136,6 @@ func SaveConfig(cfg *model.Config) *model.AppError {
return model.NewLocAppError("saveConfig", "ent.cluster.save_config.error", nil, "")
}
- //oldCfg := utils.Cfg
utils.DisableConfigWatch()
utils.SaveConfig(utils.CfgFileName, cfg)
utils.LoadConfig(utils.CfgFileName)
@@ -150,6 +149,7 @@ func SaveConfig(cfg *model.Config) *model.AppError {
}
}
+ // oldCfg := utils.Cfg
// Future feature is to sync the configuration files
// if einterfaces.GetClusterInterface() != nil {
// err := einterfaces.GetClusterInterface().ConfigChanged(cfg, oldCfg, true)
diff --git a/app/analytics.go b/app/analytics.go
index f1146327f..78e1fe7b4 100644
--- a/app/analytics.go
+++ b/app/analytics.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/app.go b/app/app.go
index 8568c7bba..2758add0e 100644
--- a/app/app.go
+++ b/app/app.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/apptestlib.go b/app/apptestlib.go
index 52530f92f..0c7086c64 100644
--- a/app/apptestlib.go
+++ b/app/apptestlib.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -75,6 +75,14 @@ func (me *TestHelper) InitBasic() *TestHelper {
return me
}
+func (me *TestHelper) MakeUsername() string {
+ return "un_" + model.NewId()
+}
+
+func (me *TestHelper) MakeEmail() string {
+ return "success_" + model.NewId() + "@simulator.amazonses.com"
+}
+
func (me *TestHelper) CreateTeam() *model.Team {
id := model.NewId()
team := &model.Team{
diff --git a/app/audit.go b/app/audit.go
index 6b7439d43..fdd152719 100644
--- a/app/audit.go
+++ b/app/audit.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/authentication.go b/app/authentication.go
index 369458527..5e1b4461f 100644
--- a/app/authentication.go
+++ b/app/authentication.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -43,7 +43,7 @@ func checkUserPassword(user *model.User, password string) *model.AppError {
return result.Err
}
- return model.NewLocAppError("checkUserPassword", "api.user.check_user_password.invalid.app_error", nil, "user_id="+user.Id)
+ return model.NewAppError("checkUserPassword", "api.user.check_user_password.invalid.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized)
} else {
if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, 0); result.Err != nil {
return result.Err
@@ -57,8 +57,7 @@ func checkLdapUserPasswordAndAllCriteria(ldapId *string, password string, mfaTok
ldapInterface := einterfaces.GetLdapInterface()
if ldapInterface == nil || ldapId == nil {
- err := model.NewLocAppError("doLdapAuthentication", "api.user.login_ldap.not_available.app_error", nil, "")
- err.StatusCode = http.StatusNotImplemented
+ err := model.NewAppError("doLdapAuthentication", "api.user.login_ldap.not_available.app_error", nil, "", http.StatusNotImplemented)
return nil, err
}
@@ -109,13 +108,13 @@ func CheckUserMfa(user *model.User, token string) *model.AppError {
mfaInterface := einterfaces.GetMfaInterface()
if mfaInterface == nil {
- return model.NewLocAppError("checkUserMfa", "api.user.check_user_mfa.not_available.app_error", nil, "")
+ return model.NewAppError("checkUserMfa", "api.user.check_user_mfa.not_available.app_error", nil, "", http.StatusNotImplemented)
}
if ok, err := mfaInterface.ValidateToken(user.MfaSecret, token); err != nil {
return err
} else if !ok {
- return model.NewLocAppError("checkUserMfa", "api.user.check_user_mfa.bad_code.app_error", nil, "")
+ return model.NewAppError("checkUserMfa", "api.user.check_user_mfa.bad_code.app_error", nil, "", http.StatusUnauthorized)
}
return nil
@@ -123,7 +122,7 @@ func CheckUserMfa(user *model.User, token string) *model.AppError {
func checkUserLoginAttempts(user *model.User) *model.AppError {
if user.FailedAttempts >= utils.Cfg.ServiceSettings.MaximumLoginAttempts {
- return model.NewAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id, http.StatusForbidden)
+ return model.NewAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized)
}
return nil
@@ -131,14 +130,14 @@ func checkUserLoginAttempts(user *model.User) *model.AppError {
func checkEmailVerified(user *model.User) *model.AppError {
if !user.EmailVerified && utils.Cfg.EmailSettings.RequireEmailVerification {
- return model.NewLocAppError("Login", "api.user.login.not_verified.app_error", nil, "user_id="+user.Id)
+ return model.NewAppError("Login", "api.user.login.not_verified.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized)
}
return nil
}
func checkUserNotDisabled(user *model.User) *model.AppError {
if user.DeleteAt > 0 {
- return model.NewLocAppError("Login", "api.user.login.inactive.app_error", nil, "user_id="+user.Id)
+ return model.NewAppError("Login", "api.user.login.inactive.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized)
}
return nil
}
@@ -148,8 +147,7 @@ func authenticateUser(user *model.User, password, mfaToken string) (*model.User,
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
+ err := model.NewAppError("login", "api.user.login_ldap.not_available.app_error", nil, "", http.StatusNotImplemented)
return user, err
} else if ldapUser, err := checkLdapUserPasswordAndAllCriteria(user.AuthData, password, mfaToken); err != nil {
err.StatusCode = http.StatusUnauthorized
@@ -163,8 +161,7 @@ func authenticateUser(user *model.User, password, mfaToken string) (*model.User,
if authService == model.USER_AUTH_SERVICE_SAML {
authService = strings.ToUpper(authService)
}
- err := model.NewLocAppError("login", "api.user.login.use_auth_service.app_error", map[string]interface{}{"AuthService": authService}, "")
- err.StatusCode = http.StatusBadRequest
+ err := model.NewAppError("login", "api.user.login.use_auth_service.app_error", map[string]interface{}{"AuthService": authService}, "", http.StatusBadRequest)
return user, err
} else {
if err := CheckPasswordAndAllCriteria(user, password, mfaToken); err != nil {
diff --git a/app/authorization.go b/app/authorization.go
index 4d36c63e8..9fc2edfb9 100644
--- a/app/authorization.go
+++ b/app/authorization.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/authorization_test.go b/app/authorization_test.go
index 049567483..4d2fdd5a0 100644
--- a/app/authorization_test.go
+++ b/app/authorization_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/auto_channels.go b/app/auto_channels.go
index 3945a5a4f..2f91cf14b 100644
--- a/app/auto_channels.go
+++ b/app/auto_channels.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/auto_constants.go b/app/auto_constants.go
index c8c903e32..2a9438396 100644
--- a/app/auto_constants.go
+++ b/app/auto_constants.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/auto_environment.go b/app/auto_environment.go
index b0a4f54b8..8827ddf87 100644
--- a/app/auto_environment.go
+++ b/app/auto_environment.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/auto_posts.go b/app/auto_posts.go
index b32407539..07d260846 100644
--- a/app/auto_posts.go
+++ b/app/auto_posts.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/auto_teams.go b/app/auto_teams.go
index 6e66f4446..029d33aa1 100644
--- a/app/auto_teams.go
+++ b/app/auto_teams.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/auto_users.go b/app/auto_users.go
index 7a99cc90b..71e20817d 100644
--- a/app/auto_users.go
+++ b/app/auto_users.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/brand.go b/app/brand.go
index 9b3df3145..bb11bd581 100644
--- a/app/brand.go
+++ b/app/brand.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/channel.go b/app/channel.go
index 1c04905c2..17fa02ad3 100644
--- a/app/channel.go
+++ b/app/channel.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -81,7 +81,7 @@ func JoinDefaultChannels(teamId string, user *model.User, channelRole string, us
err = cmResult.Err
}
- if requestor == nil {
+ if requestor == nil {
if err := postJoinChannelMessage(user, offTopic); err != nil {
l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
}
diff --git a/app/command.go b/app/command.go
index 188729ad5..2af934710 100644
--- a/app/command.go
+++ b/app/command.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -312,6 +312,7 @@ func GetCommand(commandId string) (*model.Command, *model.AppError) {
}
if result := <-Srv.Store.Command().Get(commandId); result.Err != nil {
+ result.Err.StatusCode = http.StatusNotFound
return nil, result.Err
} else {
return result.Data.(*model.Command), nil
diff --git a/app/command_away.go b/app/command_away.go
index 55553fa3f..f4150dfeb 100644
--- a/app/command_away.go
+++ b/app/command_away.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_echo.go b/app/command_echo.go
index 40d70e54a..ef70cb609 100644
--- a/app/command_echo.go
+++ b/app/command_echo.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_expand_collapse.go b/app/command_expand_collapse.go
index a4a152c60..314a54b2a 100644
--- a/app/command_expand_collapse.go
+++ b/app/command_expand_collapse.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_invite_people.go b/app/command_invite_people.go
index 0496dadca..6b0ee96b4 100644
--- a/app/command_invite_people.go
+++ b/app/command_invite_people.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_join.go b/app/command_join.go
index 5b19dd7a0..9ad3682a1 100644
--- a/app/command_join.go
+++ b/app/command_join.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_loadtest.go b/app/command_loadtest.go
index d3c7474ae..ad64573fc 100644
--- a/app/command_loadtest.go
+++ b/app/command_loadtest.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_logout.go b/app/command_logout.go
index 1a353056e..cc59d2686 100644
--- a/app/command_logout.go
+++ b/app/command_logout.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_me.go b/app/command_me.go
index bb29ec1e0..be09ac5dd 100644
--- a/app/command_me.go
+++ b/app/command_me.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_msg.go b/app/command_msg.go
index fd4ace61a..977e403d5 100644
--- a/app/command_msg.go
+++ b/app/command_msg.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_offline.go b/app/command_offline.go
index 6e2c125f8..5316ccc5f 100644
--- a/app/command_offline.go
+++ b/app/command_offline.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_online.go b/app/command_online.go
index bd6fbab60..b0becaf14 100644
--- a/app/command_online.go
+++ b/app/command_online.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_shortcuts.go b/app/command_shortcuts.go
index 93e5f0f51..7df5dbaed 100644
--- a/app/command_shortcuts.go
+++ b/app/command_shortcuts.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/command_shrug.go b/app/command_shrug.go
index 12d1039ec..88dbcef8c 100644
--- a/app/command_shrug.go
+++ b/app/command_shrug.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/compliance.go b/app/compliance.go
index 966b9b523..cb1eece70 100644
--- a/app/compliance.go
+++ b/app/compliance.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/diagnostics.go b/app/diagnostics.go
index 2c8211f42..295164c97 100644
--- a/app/diagnostics.go
+++ b/app/diagnostics.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/diagnostics_test.go b/app/diagnostics_test.go
index 21b077bd8..80c12fd2d 100644
--- a/app/diagnostics_test.go
+++ b/app/diagnostics_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/email.go b/app/email.go
index cb2fa213a..235d949be 100644
--- a/app/email.go
+++ b/app/email.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -18,7 +18,7 @@ func SendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL st
subject := T("api.templates.username_change_subject",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
- "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
+ "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL
@@ -40,8 +40,7 @@ func SendEmailChangeVerifyEmail(userId, newUserEmail, locale, siteURL string) *m
subject := T("api.templates.email_change_verify_subject",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
- "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
-
+ "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
bodyPage := utils.NewHTMLTemplate("email_change_verify_body", locale)
bodyPage.Props["SiteURL"] = siteURL
@@ -63,7 +62,7 @@ func SendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *model.App
subject := T("api.templates.email_change_subject",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
- "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
+ "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL
@@ -128,7 +127,7 @@ func SendWelcomeEmail(userId string, email string, verified bool, locale, siteUR
subject := T("api.templates.welcome_subject",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
- "ServerURL": rawUrl.Host})
+ "ServerURL": rawUrl.Host})
bodyPage := utils.NewHTMLTemplate("welcome_body", locale)
bodyPage.Props["SiteURL"] = siteURL
@@ -161,7 +160,7 @@ func SendPasswordChangeEmail(email, method, locale, siteURL string) *model.AppEr
subject := T("api.templates.password_change_subject",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
- "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
+ "TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
bodyPage := utils.NewHTMLTemplate("password_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL
@@ -234,8 +233,8 @@ func SendInviteEmails(team *model.Team, senderName string, invites []string, sit
subject := utils.T("api.templates.invite_subject",
map[string]interface{}{"SenderName": senderName,
- "TeamDisplayName": team.DisplayName,
- "SiteName": utils.ClientCfg["SiteName"]})
+ "TeamDisplayName": team.DisplayName,
+ "SiteName": utils.ClientCfg["SiteName"]})
bodyPage := utils.NewHTMLTemplate("invite_body", model.DEFAULT_LOCALE)
bodyPage.Props["SiteURL"] = siteURL
diff --git a/app/email_batching.go b/app/email_batching.go
index ac91aae08..6d7a376ef 100644
--- a/app/email_batching.go
+++ b/app/email_batching.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/email_batching_test.go b/app/email_batching_test.go
index 23722facd..74fbea5c3 100644
--- a/app/email_batching_test.go
+++ b/app/email_batching_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/email_test.go b/app/email_test.go
index 6d1a6f14a..3f57c54f9 100644
--- a/app/email_test.go
+++ b/app/email_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/emoji.go b/app/emoji.go
new file mode 100644
index 000000000..b0c8418aa
--- /dev/null
+++ b/app/emoji.go
@@ -0,0 +1,206 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "bytes"
+ "image"
+ "image/color/palette"
+ "image/draw"
+ "image/gif"
+ _ "image/jpeg"
+ "image/png"
+ "io"
+ "mime/multipart"
+ "net/http"
+
+ l4g "github.com/alecthomas/log4go"
+
+ "github.com/disintegration/imaging"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+)
+
+const (
+ MaxEmojiFileSize = 1000 * 1024 // 1 MB
+ MaxEmojiWidth = 128
+ MaxEmojiHeight = 128
+)
+
+func CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) {
+ // wipe the emoji id so that existing emojis can't get overwritten
+ emoji.Id = ""
+
+ // do our best to validate the emoji before committing anything to the DB so that we don't have to clean up
+ // orphaned files left over when validation fails later on
+ emoji.PreSave()
+ if err := emoji.IsValid(); err != nil {
+ return nil, err
+ }
+
+ if emoji.CreatorId != sessionUserId {
+ return nil, model.NewAppError("createEmoji", "api.emoji.create.other_user.app_error", nil, "", http.StatusForbidden)
+ }
+
+ if result := <-Srv.Store.Emoji().GetByName(emoji.Name); result.Err == nil && result.Data != nil {
+ return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if imageData := multiPartImageData.File["image"]; len(imageData) == 0 {
+ err := model.NewLocAppError("Context", "api.context.invalid_body_param.app_error", map[string]interface{}{"Name": "createEmoji"}, "")
+ err.StatusCode = http.StatusBadRequest
+ return nil, err
+ } else if err := UploadEmojiImage(emoji.Id, imageData[0]); err != nil {
+ return nil, err
+ }
+
+ if result := <-Srv.Store.Emoji().Save(emoji); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.Emoji), nil
+ }
+}
+
+func GetEmojiList() ([]*model.Emoji, *model.AppError) {
+ if result := <-Srv.Store.Emoji().GetAll(); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.Emoji), nil
+ }
+}
+
+func UploadEmojiImage(id string, imageData *multipart.FileHeader) *model.AppError {
+ file, err := imageData.Open()
+ if err != nil {
+ return model.NewAppError("uploadEmojiImage", "api.emoji.upload.open.app_error", nil, "", http.StatusBadRequest)
+ }
+ defer file.Close()
+
+ buf := bytes.NewBuffer(nil)
+ io.Copy(buf, file)
+
+ // make sure the file is an image and is within the required dimensions
+ if config, _, err := image.DecodeConfig(bytes.NewReader(buf.Bytes())); err != nil {
+ return model.NewAppError("uploadEmojiImage", "api.emoji.upload.image.app_error", nil, "", http.StatusBadRequest)
+ } else if config.Width > MaxEmojiWidth || config.Height > MaxEmojiHeight {
+ data := buf.Bytes()
+ newbuf := bytes.NewBuffer(nil)
+ if info, err := model.GetInfoForBytes(imageData.Filename, data); err != nil {
+ return err
+ } else if info.MimeType == "image/gif" {
+ if gif_data, err := gif.DecodeAll(bytes.NewReader(data)); err != nil {
+ return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.gif_decode_error", nil, "", http.StatusBadRequest)
+ } else {
+ resized_gif := resizeEmojiGif(gif_data)
+ if err := gif.EncodeAll(newbuf, resized_gif); err != nil {
+ return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.gif_encode_error", nil, "", http.StatusBadRequest)
+ }
+ if err := WriteFile(newbuf.Bytes(), getEmojiImagePath(id)); err != nil {
+ return err
+ }
+ }
+ } else {
+ if img, _, err := image.Decode(bytes.NewReader(data)); err != nil {
+ return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.decode_error", nil, "", http.StatusBadRequest)
+ } else {
+ resized_image := resizeEmoji(img, config.Width, config.Height)
+ if err := png.Encode(newbuf, resized_image); err != nil {
+ return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.encode_error", nil, "", http.StatusBadRequest)
+ }
+ if err := WriteFile(newbuf.Bytes(), getEmojiImagePath(id)); err != nil {
+ return err
+ }
+ }
+ }
+ } else {
+ if err := WriteFile(buf.Bytes(), getEmojiImagePath(id)); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func DeleteEmoji(emoji *model.Emoji) *model.AppError {
+ if err := (<-Srv.Store.Emoji().Delete(emoji.Id, model.GetMillis())).Err; err != nil {
+ return err
+ }
+
+ go deleteEmojiImage(emoji.Id)
+ go deleteReactionsForEmoji(emoji.Name)
+ return nil
+}
+
+func GetEmoji(emojiId string) (*model.Emoji, *model.AppError) {
+ if !*utils.Cfg.ServiceSettings.EnableCustomEmoji {
+ return nil, model.NewAppError("deleteEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if len(utils.Cfg.FileSettings.DriverName) == 0 {
+ return nil, model.NewAppError("deleteImage", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.Emoji().Get(emojiId, false); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.Emoji), nil
+ }
+}
+
+func resizeEmojiGif(gifImg *gif.GIF) *gif.GIF {
+ // Create a new RGBA image to hold the incremental frames.
+ firstFrame := gifImg.Image[0].Bounds()
+ b := image.Rect(0, 0, firstFrame.Dx(), firstFrame.Dy())
+ img := image.NewRGBA(b)
+
+ resizedImage := image.Image(nil)
+ // Resize each frame.
+ for index, frame := range gifImg.Image {
+ bounds := frame.Bounds()
+ draw.Draw(img, bounds, frame, bounds.Min, draw.Over)
+ resizedImage = resizeEmoji(img, firstFrame.Dx(), firstFrame.Dy())
+ gifImg.Image[index] = imageToPaletted(resizedImage)
+ }
+ // Set new gif width and height
+ gifImg.Config.Width = resizedImage.Bounds().Dx()
+ gifImg.Config.Height = resizedImage.Bounds().Dy()
+ return gifImg
+}
+
+func getEmojiImagePath(id string) string {
+ return "emoji/" + id + "/image"
+}
+
+func resizeEmoji(img image.Image, width int, height int) image.Image {
+ emojiWidth := float64(width)
+ emojiHeight := float64(height)
+
+ var emoji image.Image
+ if emojiHeight <= MaxEmojiHeight && emojiWidth <= MaxEmojiWidth {
+ emoji = img
+ } else {
+ emoji = imaging.Fit(img, MaxEmojiWidth, MaxEmojiHeight, imaging.Lanczos)
+ }
+ return emoji
+}
+
+func imageToPaletted(img image.Image) *image.Paletted {
+ b := img.Bounds()
+ pm := image.NewPaletted(b, palette.Plan9)
+ draw.FloydSteinberg.Draw(pm, b, img, image.ZP)
+ return pm
+}
+
+func deleteEmojiImage(id string) {
+ if err := MoveFile(getEmojiImagePath(id), "emoji/"+id+"/image_deleted"); err != nil {
+ l4g.Error("Failed to rename image when deleting emoji %v", id)
+ }
+}
+
+func deleteReactionsForEmoji(emojiName string) {
+ if result := <-Srv.Store.Reaction().DeleteAllWithEmojiName(emojiName); result.Err != nil {
+ l4g.Warn(utils.T("api.emoji.delete.delete_reactions.app_error"), emojiName)
+ l4g.Warn(result.Err)
+ }
+}
diff --git a/app/file.go b/app/file.go
index 8c0960fe8..c5e2982d4 100644
--- a/app/file.go
+++ b/app/file.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/file_test.go b/app/file_test.go
index 9df03315e..683b574b8 100644
--- a/app/file_test.go
+++ b/app/file_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/import.go b/app/import.go
index 6bf4e8a89..f92c9b1cc 100644
--- a/app/import.go
+++ b/app/import.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/import_test.go b/app/import_test.go
index e3ed80169..847864977 100644
--- a/app/import_test.go
+++ b/app/import_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/ldap.go b/app/ldap.go
index fe68dfa81..1b823dc47 100644
--- a/app/ldap.go
+++ b/app/ldap.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -18,7 +18,7 @@ func SyncLdap() {
if ldapI := einterfaces.GetLdapInterface(); ldapI != nil {
ldapI.SyncNow()
} else {
- l4g.Error("%v", model.NewLocAppError("ldapSyncNow", "ent.ldap.disabled.app_error", nil, "").Error())
+ l4g.Error("%v", model.NewLocAppError("SyncLdap", "ent.ldap.disabled.app_error", nil, "").Error())
}
}
}()
@@ -31,10 +31,84 @@ func TestLdap() *model.AppError {
return err
}
} else {
- err := model.NewLocAppError("ldapTest", "ent.ldap.disabled.app_error", nil, "")
+ err := model.NewLocAppError("TestLdap", "ent.ldap.disabled.app_error", nil, "")
err.StatusCode = http.StatusNotImplemented
return err
}
return nil
}
+
+func SwitchEmailToLdap(email, password, code, ldapId, ldapPassword string) (string, *model.AppError) {
+ user, err := GetUserByEmail(email)
+ if err != nil {
+ return "", err
+ }
+
+ if err := CheckPasswordAndAllCriteria(user, password, code); err != nil {
+ return "", err
+ }
+
+ if err := RevokeAllSessions(user.Id); err != nil {
+ return "", err
+ }
+
+ ldapInterface := einterfaces.GetLdapInterface()
+ if ldapInterface == nil {
+ return "", model.NewAppError("SwitchEmailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if err := ldapInterface.SwitchToLdap(user.Id, ldapId, ldapPassword); err != nil {
+ return "", err
+ }
+
+ go func() {
+ if err := SendSignInChangeEmail(user.Email, "AD/LDAP", user.Locale, utils.GetSiteURL()); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ return "/login?extra=signin_change", nil
+}
+
+func SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (string, *model.AppError) {
+ user, err := GetUserByEmail(email)
+ if err != nil {
+ return "", err
+ }
+
+ if user.AuthService != model.USER_AUTH_SERVICE_LDAP {
+ return "", model.NewAppError("SwitchLdapToEmail", "api.user.ldap_to_email.not_ldap_account.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ ldapInterface := einterfaces.GetLdapInterface()
+ if ldapInterface == nil || user.AuthData == nil {
+ return "", model.NewAppError("SwitchLdapToEmail", "api.user.ldap_to_email.not_available.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if err := ldapInterface.CheckPassword(*user.AuthData, ldapPassword); err != nil {
+ return "", err
+ }
+
+ if err := CheckUserMfa(user, code); err != nil {
+ return "", err
+ }
+
+ if err := UpdatePassword(user, newPassword); err != nil {
+ return "", err
+ }
+
+ if err := RevokeAllSessions(user.Id); err != nil {
+ return "", err
+ }
+
+ T := utils.GetUserTranslations(user.Locale)
+
+ go func() {
+ if err := SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, utils.GetSiteURL()); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ return "/login?extra=signin_change", nil
+}
diff --git a/app/license.go b/app/license.go
index c41c17fd8..7a00d7fb4 100644
--- a/app/license.go
+++ b/app/license.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/license_test.go b/app/license_test.go
index d7d851589..a7761b204 100644
--- a/app/license_test.go
+++ b/app/license_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/login.go b/app/login.go
index e9bcf1f03..4c7ab8474 100644
--- a/app/login.go
+++ b/app/login.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/notification.go b/app/notification.go
index ef1b70aad..e983f5e8c 100644
--- a/app/notification.go
+++ b/app/notification.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/notification_test.go b/app/notification_test.go
index 3768a95c7..794bb4b37 100644
--- a/app/notification_test.go
+++ b/app/notification_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/oauth.go b/app/oauth.go
index 3e8b0b8d2..260e4ac00 100644
--- a/app/oauth.go
+++ b/app/oauth.go
@@ -1,14 +1,386 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
import (
+ "bytes"
+ "crypto/tls"
+ b64 "encoding/base64"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
)
-func RevokeAccessToken(token string) *model.AppError {
+func CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("CreateOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ secret := model.NewId()
+ app.ClientSecret = secret
+
+ if result := <-Srv.Store.OAuth().SaveApp(app); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.OAuthApp), nil
+ }
+}
+
+func GetOAuthApp(appId string) (*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetApp(appId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.OAuthApp), nil
+ }
+}
+
+func DeleteOAuthApp(appId string) *model.AppError {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return model.NewAppError("DeleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if err := (<-Srv.Store.OAuth().DeleteApp(appId)).Err; err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func GetOAuthApps(page, perPage int) ([]*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthApps", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetApps(page*perPage, perPage); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.OAuthApp), nil
+ }
+}
+
+func GetOAuthAppsByCreator(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthAppsByUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetAppByUser(userId, page*perPage, perPage); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.OAuthApp), nil
+ }
+}
+
+func AllowOAuthAppAccessToUser(userId, responseType, clientId, redirectUri, scope, state string) (string, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if len(scope) == 0 {
+ scope = model.DEFAULT_SCOPE
+ }
+
+ var oauthApp *model.OAuthApp
+ if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
+ return "", result.Err
+ } else {
+ oauthApp = result.Data.(*model.OAuthApp)
+ }
+
+ if !oauthApp.IsValidRedirectURL(redirectUri) {
+ return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if responseType != model.AUTHCODE_RESPONSE_TYPE {
+ return redirectUri + "?error=unsupported_response_type&state=" + state, nil
+ }
+
+ authData := &model.AuthData{UserId: userId, ClientId: clientId, CreateAt: model.GetMillis(), RedirectUri: redirectUri, State: state, Scope: scope}
+ authData.Code = model.HashPassword(fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, userId))
+
+ // this saves the OAuth2 app as authorized
+ authorizedApp := model.Preference{
+ UserId: userId,
+ Category: model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP,
+ Name: clientId,
+ Value: scope,
+ }
+
+ if result := <-Srv.Store.Preference().Save(&model.Preferences{authorizedApp}); result.Err != nil {
+ return redirectUri + "?error=server_error&state=" + state, nil
+ }
+
+ if result := <-Srv.Store.OAuth().SaveAuthData(authData); result.Err != nil {
+ return redirectUri + "?error=server_error&state=" + state, nil
+ }
+
+ return redirectUri + "?code=" + url.QueryEscape(authData.Code) + "&state=" + url.QueryEscape(authData.State), nil
+}
+
+func GetOAuthAccessToken(clientId, grantType, redirectUri, code, secret, refreshToken string) (*model.AccessResponse, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ var oauthApp *model.OAuthApp
+ if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
+ } else {
+ oauthApp = result.Data.(*model.OAuthApp)
+ }
+
+ if oauthApp.ClientSecret != secret {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusForbidden)
+ }
+
+ var user *model.User
+ var accessData *model.AccessData
+ var accessRsp *model.AccessResponse
+ if grantType == model.ACCESS_TOKEN_GRANT_TYPE {
+
+ var authData *model.AuthData
+ if result := <-Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusInternalServerError)
+ } else {
+ authData = result.Data.(*model.AuthData)
+ }
+
+ if authData.IsExpired() {
+ <-Srv.Store.OAuth().RemoveAuthData(authData.Code)
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
+ }
+
+ if authData.RedirectUri != redirectUri {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if !model.ComparePassword(code, fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, authData.UserId)) {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if result := <-Srv.Store.User().Get(authData.UserId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
+ } else {
+ user = result.Data.(*model.User)
+ }
+ if result := <-Srv.Store.OAuth().GetPreviousAccessData(user.Id, clientId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "", http.StatusInternalServerError)
+ } else if result.Data != nil {
+ accessData := result.Data.(*model.AccessData)
+ if accessData.IsExpired() {
+ if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
+ return nil, err
+ } else {
+ accessRsp = access
+ }
+ } else {
+ //return the same token and no need to create a new session
+ accessRsp = &model.AccessResponse{
+ AccessToken: accessData.Token,
+ TokenType: model.ACCESS_TOKEN_TYPE,
+ ExpiresIn: int32((accessData.ExpiresAt - model.GetMillis()) / 1000),
+ }
+ }
+ } else {
+ // create a new session and return new access token
+ var session *model.Session
+ if result, err := newSession(oauthApp.Name, user); err != nil {
+ return nil, err
+ } else {
+ session = result
+ }
+
+ accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt, Scope: authData.Scope}
+
+ if result := <-Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
+ l4g.Error(result.Err)
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
+ }
+
+ accessRsp = &model.AccessResponse{
+ AccessToken: session.Token,
+ TokenType: model.ACCESS_TOKEN_TYPE,
+ RefreshToken: accessData.RefreshToken,
+ ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
+ }
+ }
+
+ <-Srv.Store.OAuth().RemoveAuthData(authData.Code)
+ } else {
+ // when grantType is refresh_token
+ if result := <-Srv.Store.OAuth().GetAccessDataByRefreshToken(refreshToken); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "", http.StatusNotFound)
+ } else {
+ accessData = result.Data.(*model.AccessData)
+ }
+
+ if result := <-Srv.Store.User().Get(accessData.UserId); result.Err != nil {
+ return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
+ return nil, err
+ } else {
+ accessRsp = access
+ }
+ }
+
+ return accessRsp, nil
+}
+
+func newSession(appName string, user *model.User) (*model.Session, *model.AppError) {
+ // set new token an session
+ session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true}
+ session.SetExpireInDays(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays)
+ session.AddProp(model.SESSION_PROP_PLATFORM, appName)
+ session.AddProp(model.SESSION_PROP_OS, "OAuth2")
+ session.AddProp(model.SESSION_PROP_BROWSER, "OAuth2")
+
+ if result := <-Srv.Store.Session().Save(session); result.Err != nil {
+ return nil, model.NewAppError("newSession", "api.oauth.get_access_token.internal_session.app_error", nil, "", http.StatusInternalServerError)
+ } else {
+ session = result.Data.(*model.Session)
+ AddSessionToCache(session)
+ }
+
+ return session, nil
+}
+
+func newSessionUpdateToken(appName string, accessData *model.AccessData, user *model.User) (*model.AccessResponse, *model.AppError) {
+ var session *model.Session
+ <-Srv.Store.Session().Remove(accessData.Token) //remove the previous session
+
+ if result, err := newSession(appName, user); err != nil {
+ return nil, err
+ } else {
+ session = result
+ }
+
+ accessData.Token = session.Token
+ accessData.ExpiresAt = session.ExpiresAt
+ if result := <-Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
+ l4g.Error(result.Err)
+ return nil, model.NewAppError("newSessionUpdateToken", "web.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
+ }
+ accessRsp := &model.AccessResponse{
+ AccessToken: session.Token,
+ TokenType: model.ACCESS_TOKEN_TYPE,
+ ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
+ }
+
+ return accessRsp, nil
+}
+
+func GetOAuthLoginEndpoint(service, teamId, redirectTo, loginHint string) (string, *model.AppError) {
+ stateProps := map[string]string{}
+ stateProps["action"] = model.OAUTH_ACTION_LOGIN
+ if len(teamId) != 0 {
+ stateProps["team_id"] = teamId
+ }
+
+ if len(redirectTo) != 0 {
+ stateProps["redirect_to"] = redirectTo
+ }
+
+ if authUrl, err := GetAuthorizationCode(service, stateProps, loginHint); err != nil {
+ return "", err
+ } else {
+ return authUrl, nil
+ }
+}
+
+func GetOAuthSignupEndpoint(service, teamId string) (string, *model.AppError) {
+ stateProps := map[string]string{}
+ stateProps["action"] = model.OAUTH_ACTION_SIGNUP
+ if len(teamId) != 0 {
+ stateProps["team_id"] = teamId
+ }
+
+ if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil {
+ return "", err
+ } else {
+ return authUrl, nil
+ }
+}
+
+func GetAuthorizedAppsForUser(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("GetAuthorizedAppsForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ if result := <-Srv.Store.OAuth().GetAuthorizedApps(userId, page*perPage, perPage); result.Err != nil {
+ return nil, result.Err
+ } else {
+ apps := result.Data.([]*model.OAuthApp)
+ for k, a := range apps {
+ a.Sanitize()
+ apps[k] = a
+ }
+
+ return apps, nil
+ }
+}
+
+func DeauthorizeOAuthAppForUser(userId, appId string) *model.AppError {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return model.NewAppError("DeauthorizeOAuthAppForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ // revoke app sessions
+ if result := <-Srv.Store.OAuth().GetAccessDataByUserForApp(userId, appId); result.Err != nil {
+ return result.Err
+ } else {
+ accessData := result.Data.([]*model.AccessData)
+
+ for _, a := range accessData {
+ if err := RevokeAccessToken(a.Token); err != nil {
+ return err
+ }
+
+ if rad := <-Srv.Store.OAuth().RemoveAccessData(a.Token); rad.Err != nil {
+ return rad.Err
+ }
+ }
+ }
+
+ // Deauthorize the app
+ if err := (<-Srv.Store.Preference().Delete(userId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, appId)).Err; err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
+ if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
+ return nil, model.NewAppError("RegenerateOAuthAppSecret", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ app.ClientSecret = model.NewId()
+ if update := <-Srv.Store.OAuth().UpdateApp(app); update.Err != nil {
+ return nil, update.Err
+ }
+
+ return app, nil
+}
+
+func RevokeAccessToken(token string) *model.AppError {
session, _ := GetSession(token)
schan := Srv.Store.Session().Remove(token)
@@ -32,3 +404,276 @@ func RevokeAccessToken(token string) *model.AppError {
return nil
}
+
+func CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
+ defer func() {
+ ioutil.ReadAll(body)
+ body.Close()
+ }()
+
+ action := props["action"]
+
+ switch action {
+ case model.OAUTH_ACTION_SIGNUP:
+ return CreateOAuthUser(service, body, teamId)
+ case model.OAUTH_ACTION_LOGIN:
+ return LoginByOAuth(service, body, teamId)
+ case model.OAUTH_ACTION_EMAIL_TO_SSO:
+ return CompleteSwitchWithOAuth(service, body, props["email"])
+ case model.OAUTH_ACTION_SSO_TO_EMAIL:
+ return LoginByOAuth(service, body, teamId)
+ default:
+ return LoginByOAuth(service, body, teamId)
+ }
+}
+
+func LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
+ buf := bytes.Buffer{}
+ buf.ReadFrom(userData)
+
+ authData := ""
+ provider := einterfaces.GetOauthProvider(service)
+ if provider == nil {
+ return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
+ map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
+ } else {
+ authData = provider.GetAuthDataFromJson(bytes.NewReader(buf.Bytes()))
+ }
+
+ if len(authData) == 0 {
+ return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
+ map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
+ }
+
+ user, err := GetUserByAuth(&authData, service)
+ if err != nil {
+ if err.Id == store.MISSING_AUTH_ACCOUNT_ERROR {
+ return CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId)
+ }
+ return nil, err
+ }
+
+ if err = UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
+ return nil, err
+ }
+
+ if len(teamId) > 0 {
+ err = AddUserToTeamByTeamId(teamId, user)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ return user, nil
+}
+
+func CompleteSwitchWithOAuth(service string, userData io.ReadCloser, email string) (*model.User, *model.AppError) {
+ authData := ""
+ ssoEmail := ""
+ provider := einterfaces.GetOauthProvider(service)
+ if provider == nil {
+ return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
+ map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
+ } else {
+ ssoUser := provider.GetUserFromJson(userData)
+ ssoEmail = ssoUser.Email
+
+ if ssoUser.AuthData != nil {
+ authData = *ssoUser.AuthData
+ }
+ }
+
+ if len(authData) == 0 {
+ return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
+ map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
+ }
+
+ if len(email) == 0 {
+ return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ var user *model.User
+ if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil {
+ return nil, result.Err
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if err := RevokeAllSessions(user.Id); err != nil {
+ return nil, err
+ }
+
+ if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, &authData, ssoEmail, true); result.Err != nil {
+ return nil, result.Err
+ }
+
+ go func() {
+ if err := SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, utils.GetSiteURL()); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ return user, nil
+}
+
+func GetAuthorizationCode(service string, props map[string]string, loginHint string) (string, *model.AppError) {
+ sso := utils.Cfg.GetSSOService(service)
+ if sso != nil && !sso.Enable {
+ return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
+ }
+
+ clientId := sso.Id
+ endpoint := sso.AuthEndpoint
+ scope := sso.Scope
+
+ props["hash"] = model.HashPassword(clientId)
+ state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props)))
+
+ redirectUri := utils.GetSiteURL() + "/signup/" + service + "/complete"
+
+ authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state)
+
+ if len(scope) > 0 {
+ authUrl += "&scope=" + utils.UrlEncode(scope)
+ }
+
+ if len(loginHint) > 0 {
+ authUrl += "&login_hint=" + utils.UrlEncode(loginHint)
+ }
+
+ return authUrl, nil
+}
+
+func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
+ sso := utils.Cfg.GetSSOService(service)
+ if sso == nil || !sso.Enable {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service)
+ }
+
+ stateStr := ""
+ if b, err := b64.StdEncoding.DecodeString(state); err != nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error())
+ } else {
+ stateStr = string(b)
+ }
+
+ stateProps := model.MapFromJson(strings.NewReader(stateStr))
+
+ if !model.ComparePassword(stateProps["hash"], sso.Id) {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "")
+ }
+
+ teamId := stateProps["team_id"]
+
+ p := url.Values{}
+ p.Set("client_id", sso.Id)
+ p.Set("client_secret", sso.Secret)
+ p.Set("code", code)
+ p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
+ p.Set("redirect_uri", redirectUri)
+
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections},
+ }
+ client := &http.Client{Transport: tr}
+ req, _ := http.NewRequest("POST", sso.TokenEndpoint, strings.NewReader(p.Encode()))
+
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Accept", "application/json")
+
+ var ar *model.AccessResponse
+ var respBody []byte
+ if resp, err := client.Do(req); err != nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error())
+ } else {
+ ar = model.AccessResponseFromJson(resp.Body)
+ defer func() {
+ ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ }()
+ if ar == nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "")
+ }
+ }
+
+ if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+string(respBody))
+ }
+
+ if len(ar.AccessToken) == 0 {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "")
+ }
+
+ p = url.Values{}
+ p.Set("access_token", ar.AccessToken)
+ req, _ = http.NewRequest("GET", sso.UserApiEndpoint, strings.NewReader(""))
+
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Accept", "application/json")
+ req.Header.Set("Authorization", "Bearer "+ar.AccessToken)
+
+ if resp, err := client.Do(req); err != nil {
+ return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error",
+ map[string]interface{}{"Service": service}, err.Error())
+ } else {
+ return resp.Body, teamId, stateProps, nil
+ }
+
+}
+
+func SwitchEmailToOAuth(email, password, code, service string) (string, *model.AppError) {
+ var user *model.User
+ var err *model.AppError
+ if user, err = GetUserByEmail(email); err != nil {
+ return "", err
+ }
+
+ if err := CheckPasswordAndAllCriteria(user, password, code); err != nil {
+ return "", err
+ }
+
+ stateProps := map[string]string{}
+ stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
+ stateProps["email"] = email
+
+ if service == model.USER_AUTH_SERVICE_SAML {
+ return utils.GetSiteURL() + "/login/sso/saml?action=" + model.OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + email, nil
+ } else {
+ if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil {
+ return "", err
+ } else {
+ return authUrl, nil
+ }
+ }
+}
+
+func SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) {
+ var user *model.User
+ var err *model.AppError
+ if user, err = GetUserByEmail(email); err != nil {
+ return "", err
+ }
+
+ if user.Id != requesterId {
+ return "", model.NewAppError("SwitchOAuthToEmail", "api.user.oauth_to_email.context.app_error", nil, "", http.StatusForbidden)
+ }
+
+ if err := UpdatePassword(user, password); err != nil {
+ return "", err
+ }
+
+ T := utils.GetUserTranslations(user.Locale)
+
+ go func() {
+ if err := SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, utils.GetSiteURL()); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ if err := RevokeAllSessions(requesterId); err != nil {
+ return "", err
+ }
+
+ return "/login?extra=signin_change", nil
+}
diff --git a/app/oauth_test.go b/app/oauth_test.go
index 3ca3a2d4a..9e8fdfc7d 100644
--- a/app/oauth_test.go
+++ b/app/oauth_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/post.go b/app/post.go
index 7f38a9bd2..bf61bafb2 100644
--- a/app/post.go
+++ b/app/post.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -402,6 +402,14 @@ func GetFlaggedPostsForTeam(userId, teamId string, offset int, limit int) (*mode
}
}
+func GetFlaggedPostsForChannel(userId, channelId string, offset int, limit int) (*model.PostList, *model.AppError) {
+ if result := <-Srv.Store.Post().GetFlaggedPostsForChannel(userId, channelId, offset, limit); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.PostList), nil
+ }
+}
+
func GetPermalinkPost(postId string, userId string) (*model.PostList, *model.AppError) {
if result := <-Srv.Store.Post().Get(postId); result.Err != nil {
return nil, result.Err
@@ -559,13 +567,13 @@ func GetOpenGraphMetadata(url string) *opengraph.OpenGraph {
res, err := httpClient.Get(url)
if err != nil {
- l4g.Error(err.Error())
+ l4g.Error("GetOpenGraphMetadata request failed for url=%v with err=%v", url, err.Error())
return og
}
defer CloseBody(res)
if err := og.ProcessHTML(res.Body); err != nil {
- l4g.Error(err.Error())
+ l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", url, err.Error())
}
return og
diff --git a/app/preference.go b/app/preference.go
index ff251fb16..793f1802b 100644
--- a/app/preference.go
+++ b/app/preference.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/reaction.go b/app/reaction.go
new file mode 100644
index 000000000..cc31018ec
--- /dev/null
+++ b/app/reaction.go
@@ -0,0 +1,16 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+func GetReactionsForPost(postId string) ([]*model.Reaction, *model.AppError) {
+ if result := <-Srv.Store.Reaction().GetForPost(postId, true); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.Reaction), nil
+ }
+}
diff --git a/app/saml.go b/app/saml.go
index e2bf4ccb2..8a6e6f16c 100644
--- a/app/saml.go
+++ b/app/saml.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/security_update_check.go b/app/security_update_check.go
index 2b000f2b2..12014bdf3 100644
--- a/app/security_update_check.go
+++ b/app/security_update_check.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/server.go b/app/server.go
index 972c91ea3..a757e184e 100644
--- a/app/server.go
+++ b/app/server.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/session.go b/app/session.go
index 9e4fda4bd..0df643743 100644
--- a/app/session.go
+++ b/app/session.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/session_test.go b/app/session_test.go
index aea31cf86..b3cd9fd57 100644
--- a/app/session_test.go
+++ b/app/session_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/slackimport.go b/app/slackimport.go
index 3e226203b..71f16c874 100644
--- a/app/slackimport.go
+++ b/app/slackimport.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/slackimport_test.go b/app/slackimport_test.go
index 6db6adf2d..87de597ca 100644
--- a/app/slackimport_test.go
+++ b/app/slackimport_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/status.go b/app/status.go
index f6565b7d2..a3b921700 100644
--- a/app/status.go
+++ b/app/status.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/team.go b/app/team.go
index 12c970665..d4e6d6308 100644
--- a/app/team.go
+++ b/app/team.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -6,6 +6,7 @@ package app
import (
"fmt"
"net/http"
+ "net/url"
"strconv"
"strings"
@@ -747,3 +748,33 @@ func GetTeamStats(teamId string) (*model.TeamStats, *model.AppError) {
return stats, nil
}
+
+func GetTeamIdFromQuery(query url.Values) (string, *model.AppError) {
+ hash := query.Get("h")
+ inviteId := query.Get("id")
+
+ if len(hash) > 0 {
+ data := query.Get("d")
+ props := model.MapFromJson(strings.NewReader(data))
+
+ if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
+ return "", model.NewAppError("GetTeamIdFromQuery", "api.oauth.singup_with_oauth.invalid_link.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ t, err := strconv.ParseInt(props["time"], 10, 64)
+ if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
+ return "", model.NewAppError("GetTeamIdFromQuery", "api.oauth.singup_with_oauth.expired_link.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ return props["id"], nil
+ } else if len(inviteId) > 0 {
+ if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
+ // soft fail, so we still create user but don't auto-join team
+ l4g.Error("%v", result.Err)
+ } else {
+ return result.Data.(*model.Team).Id, nil
+ }
+ }
+
+ return "", nil
+}
diff --git a/app/team_test.go b/app/team_test.go
index 86a383a39..f2356d562 100644
--- a/app/team_test.go
+++ b/app/team_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/user.go b/app/user.go
index 99e9d46a3..e339dfd5b 100644
--- a/app/user.go
+++ b/app/user.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
@@ -186,7 +186,9 @@ func CreateUser(user *model.User) (*model.User, *model.AppError) {
}
}
- user.Locale = *utils.Cfg.LocalizationSettings.DefaultClientLocale
+ if _, ok := utils.GetSupportedLocales()[user.Locale]; !ok {
+ user.Locale = *utils.Cfg.LocalizationSettings.DefaultClientLocale
+ }
if ruser, err := createUser(user); err != nil {
return nil, err
diff --git a/app/user_test.go b/app/user_test.go
index ff9e4d500..4cd26e729 100644
--- a/app/user_test.go
+++ b/app/user_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/web_conn.go b/app/web_conn.go
index 8d604ff3e..000704791 100644
--- a/app/web_conn.go
+++ b/app/web_conn.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/web_hub.go b/app/web_hub.go
index f65683f70..da2a41ec4 100644
--- a/app/web_hub.go
+++ b/app/web_hub.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/webhook.go b/app/webhook.go
index 0ec06365d..e095c1b80 100644
--- a/app/webhook.go
+++ b/app/webhook.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/webrtc.go b/app/webrtc.go
new file mode 100644
index 000000000..6692fff60
--- /dev/null
+++ b/app/webrtc.go
@@ -0,0 +1,87 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "crypto/tls"
+ "encoding/base64"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+)
+
+func GetWebrtcInfoForSession(sessionId string) (*model.WebrtcInfoResponse, *model.AppError) {
+ token, err := GetWebrtcToken(sessionId)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &model.WebrtcInfoResponse{
+ Token: token,
+ GatewayUrl: *utils.Cfg.WebrtcSettings.GatewayWebsocketUrl,
+ }
+
+ if len(*utils.Cfg.WebrtcSettings.StunURI) > 0 {
+ result.StunUri = *utils.Cfg.WebrtcSettings.StunURI
+ }
+
+ if len(*utils.Cfg.WebrtcSettings.TurnURI) > 0 {
+ timestamp := strconv.FormatInt(utils.EndOfDay(time.Now().AddDate(0, 0, 1)).Unix(), 10)
+ username := timestamp + ":" + *utils.Cfg.WebrtcSettings.TurnUsername
+
+ result.TurnUri = *utils.Cfg.WebrtcSettings.TurnURI
+ result.TurnPassword = GenerateTurnPassword(username, *utils.Cfg.WebrtcSettings.TurnSharedKey)
+ result.TurnUsername = username
+ }
+
+ return result, nil
+}
+
+func GetWebrtcToken(sessionId string) (string, *model.AppError) {
+ if !*utils.Cfg.WebrtcSettings.Enable {
+ return "", model.NewAppError("WebRTC.getWebrtcToken", "api.webrtc.disabled.app_error", nil, "", http.StatusNotImplemented)
+ }
+
+ token := base64.StdEncoding.EncodeToString([]byte(sessionId))
+
+ data := make(map[string]string)
+ data["janus"] = "add_token"
+ data["token"] = token
+ data["transaction"] = model.NewId()
+ data["admin_secret"] = *utils.Cfg.WebrtcSettings.GatewayAdminSecret
+
+ rq, _ := http.NewRequest("POST", *utils.Cfg.WebrtcSettings.GatewayAdminUrl, strings.NewReader(model.MapToJson(data)))
+ rq.Header.Set("Content-Type", "application/json")
+
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections},
+ }
+ httpClient := &http.Client{Transport: tr}
+ if rp, err := httpClient.Do(rq); err != nil {
+ return "", model.NewAppError("WebRTC.Token", "model.client.connecting.app_error", nil, err.Error(), http.StatusInternalServerError)
+ } else if rp.StatusCode >= 300 {
+ defer CloseBody(rp)
+ return "", model.AppErrorFromJson(rp.Body)
+ } else {
+ janusResponse := model.GatewayResponseFromJson(rp.Body)
+ if janusResponse.Status != "success" {
+ return "", model.NewAppError("getWebrtcToken", "api.webrtc.register_token.app_error", nil, "", http.StatusInternalServerError)
+ }
+ }
+
+ return token, nil
+}
+
+func GenerateTurnPassword(username string, secret string) string {
+ key := []byte(secret)
+ h := hmac.New(sha1.New, key)
+ h.Write([]byte(username))
+ return base64.StdEncoding.EncodeToString(h.Sum(nil))
+}
diff --git a/app/websocket_router.go b/app/websocket_router.go
index 4569134b0..84806b5cf 100644
--- a/app/websocket_router.go
+++ b/app/websocket_router.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
diff --git a/app/webtrc.go b/app/webtrc.go
index b526c96a6..a2ead21ab 100644
--- a/app/webtrc.go
+++ b/app/webtrc.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app