2015-10-08 12:27:09 -04:00
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
2015-06-14 23:53:32 -08:00
// See License.txt for license information.
package api
import (
"bytes"
2016-07-05 15:49:00 -04:00
b64 "encoding/base64"
2015-06-14 23:53:32 -08:00
"fmt"
"hash/fnv"
2016-01-24 17:10:54 -03:00
"html/template"
2015-06-14 23:53:32 -08:00
"image"
"image/color"
2015-06-22 15:11:20 -04:00
"image/draw"
2015-06-14 23:53:32 -08:00
_ "image/gif"
_ "image/jpeg"
"image/png"
2015-07-17 09:47:25 -04:00
"io"
2015-07-28 16:39:50 -07:00
"io/ioutil"
2015-06-14 23:53:32 -08:00
"net/http"
"net/url"
"strconv"
"strings"
2016-02-08 11:29:11 -05:00
"time"
2016-05-18 13:54:33 +02:00
l4g "github.com/alecthomas/log4go"
"github.com/disintegration/imaging"
"github.com/golang/freetype"
"github.com/gorilla/mux"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
2015-06-14 23:53:32 -08:00
)
2016-04-21 22:37:01 -07:00
func InitUser ( ) {
2016-01-22 01:37:11 -03:00
l4g . Debug ( utils . T ( "api.user.init.debug" ) )
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
BaseRoutes . Users . Handle ( "/create" , ApiAppHandler ( createUser ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/update" , ApiUserRequired ( updateUser ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/update_active" , ApiUserRequired ( updateActive ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/update_notify" , ApiUserRequired ( updateUserNotify ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/newpassword" , ApiUserRequired ( updatePassword ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/send_password_reset" , ApiAppHandler ( sendPasswordReset ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/reset_password" , ApiAppHandler ( resetPassword ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/login" , ApiAppHandler ( login ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/logout" , ApiAppHandler ( logout ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/revoke_session" , ApiUserRequired ( revokeSession ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/attach_device" , ApiUserRequired ( attachDeviceId ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/verify_email" , ApiAppHandler ( verifyEmail ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/resend_verification" , ApiAppHandler ( resendVerification ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/newimage" , ApiUserRequired ( uploadProfileImage ) ) . Methods ( "POST" )
2016-10-17 11:43:28 -03:00
BaseRoutes . Users . Handle ( "/me" , ApiUserRequired ( getMe ) ) . Methods ( "GET" )
2016-04-21 22:37:01 -07:00
BaseRoutes . Users . Handle ( "/initial_load" , ApiAppHandler ( getInitialLoad ) ) . Methods ( "GET" )
2016-10-19 14:49:25 -04:00
BaseRoutes . Users . Handle ( "/{offset:[0-9]+}/{limit:[0-9]+}" , ApiUserRequired ( getProfiles ) ) . Methods ( "GET" )
BaseRoutes . NeedTeam . Handle ( "/users/{offset:[0-9]+}/{limit:[0-9]+}" , ApiUserRequired ( getProfilesInTeam ) ) . Methods ( "GET" )
BaseRoutes . NeedChannel . Handle ( "/users/{offset:[0-9]+}/{limit:[0-9]+}" , ApiUserRequired ( getProfilesInChannel ) ) . Methods ( "GET" )
BaseRoutes . NeedChannel . Handle ( "/users/not_in_channel/{offset:[0-9]+}/{limit:[0-9]+}" , ApiUserRequired ( getProfilesNotInChannel ) ) . Methods ( "GET" )
BaseRoutes . Users . Handle ( "/search" , ApiUserRequired ( searchUsers ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/ids" , ApiUserRequired ( getProfilesByIds ) ) . Methods ( "POST" )
2016-11-29 10:12:59 -05:00
BaseRoutes . Users . Handle ( "/autocomplete" , ApiUserRequired ( autocompleteUsers ) ) . Methods ( "GET" )
2016-10-19 14:49:25 -04:00
BaseRoutes . NeedTeam . Handle ( "/users/autocomplete" , ApiUserRequired ( autocompleteUsersInTeam ) ) . Methods ( "GET" )
BaseRoutes . NeedChannel . Handle ( "/users/autocomplete" , ApiUserRequired ( autocompleteUsersInChannel ) ) . Methods ( "GET" )
2016-04-21 22:37:01 -07:00
BaseRoutes . Users . Handle ( "/mfa" , ApiAppHandler ( checkMfa ) ) . Methods ( "POST" )
2016-12-12 08:16:10 -05:00
BaseRoutes . Users . Handle ( "/generate_mfa_secret" , ApiUserRequiredMfa ( generateMfaSecret ) ) . Methods ( "GET" )
BaseRoutes . Users . Handle ( "/update_mfa" , ApiUserRequiredMfa ( updateMfa ) ) . Methods ( "POST" )
2016-04-21 22:37:01 -07:00
BaseRoutes . Users . Handle ( "/claim/email_to_oauth" , ApiAppHandler ( emailToOAuth ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/claim/oauth_to_email" , ApiUserRequired ( oauthToEmail ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/claim/email_to_ldap" , ApiAppHandler ( emailToLdap ) ) . Methods ( "POST" )
BaseRoutes . Users . Handle ( "/claim/ldap_to_email" , ApiAppHandler ( ldapToEmail ) ) . Methods ( "POST" )
BaseRoutes . NeedUser . Handle ( "/get" , ApiUserRequired ( getUser ) ) . Methods ( "GET" )
2016-11-22 00:50:57 +01:00
BaseRoutes . Users . Handle ( "/name/{username:[A-Za-z0-9_\\-.]+}" , ApiUserRequired ( getByUsername ) ) . Methods ( "GET" )
2016-04-21 22:37:01 -07:00
BaseRoutes . NeedUser . Handle ( "/sessions" , ApiUserRequired ( getSessions ) ) . Methods ( "GET" )
BaseRoutes . NeedUser . Handle ( "/audits" , ApiUserRequired ( getAudits ) ) . Methods ( "GET" )
BaseRoutes . NeedUser . Handle ( "/image" , ApiUserRequiredTrustRequester ( getProfileImage ) ) . Methods ( "GET" )
2016-09-22 08:31:38 -04:00
BaseRoutes . NeedUser . Handle ( "/update_roles" , ApiUserRequired ( updateRoles ) ) . Methods ( "POST" )
2016-07-05 15:49:00 -04:00
BaseRoutes . Root . Handle ( "/login/sso/saml" , AppHandlerIndependent ( loginWithSaml ) ) . Methods ( "GET" )
BaseRoutes . Root . Handle ( "/login/sso/saml" , AppHandlerIndependent ( completeSaml ) ) . Methods ( "POST" )
2016-07-12 09:36:27 -04:00
BaseRoutes . WebSocket . Handle ( "user_typing" , ApiWebSocketHandler ( userTyping ) )
2015-06-14 23:53:32 -08:00
}
func createUser ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-12-08 13:38:43 -05:00
if ! utils . Cfg . EmailSettings . EnableSignUpWithEmail || ! utils . Cfg . TeamSettings . EnableUserCreation {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "signupTeam" , "api.user.create_user.signup_email_disabled.app_error" , nil , "" )
2015-08-28 08:37:55 -04:00
c . Err . StatusCode = http . StatusNotImplemented
return
}
2015-06-14 23:53:32 -08:00
user := model . UserFromJson ( r . Body )
if user == nil {
c . SetInvalidParam ( "createUser" , "user" )
return
}
hash := r . URL . Query ( ) . Get ( "h" )
2016-04-21 22:37:01 -07:00
teamId := ""
var team * model . Team
2016-05-18 17:14:55 +02:00
shouldSendWelcomeEmail := true
2016-04-21 22:37:01 -07:00
user . EmailVerified = false
2015-11-05 08:59:29 -05:00
2016-04-21 22:37:01 -07:00
if len ( hash ) > 0 {
2015-06-14 23:53:32 -08:00
data := r . URL . Query ( ) . Get ( "d" )
props := model . MapFromJson ( strings . NewReader ( data ) )
2015-09-22 12:12:50 -07:00
if ! model . ComparePassword ( hash , fmt . Sprintf ( "%v:%v" , data , utils . Cfg . EmailSettings . InviteSalt ) ) {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "createUser" , "api.user.create_user.signup_link_invalid.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
return
}
t , err := strconv . ParseInt ( props [ "time" ] , 10 , 64 )
if err != nil || model . GetMillis ( ) - t > 1000 * 60 * 60 * 48 { // 48 hours
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "createUser" , "api.user.create_user.signup_link_expired.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
return
}
2016-04-21 22:37:01 -07:00
teamId = props [ "id" ]
// try to load the team to make sure it exists
if result := <- Srv . Store . Team ( ) . Get ( teamId ) ; result . Err != nil {
c . Err = result . Err
2015-06-14 23:53:32 -08:00
return
2016-04-21 22:37:01 -07:00
} else {
team = result . Data . ( * model . Team )
2015-06-14 23:53:32 -08:00
}
user . Email = props [ "email" ]
user . EmailVerified = true
2016-05-18 17:14:55 +02:00
shouldSendWelcomeEmail = false
2015-06-14 23:53:32 -08:00
}
2016-04-21 22:37:01 -07:00
inviteId := r . URL . Query ( ) . Get ( "iid" )
if len ( inviteId ) > 0 {
if result := <- Srv . Store . Team ( ) . GetByInviteId ( inviteId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
team = result . Data . ( * model . Team )
teamId = team . Id
}
}
firstAccount := false
if sessionCache . Len ( ) == 0 {
if cr := <- Srv . Store . User ( ) . GetTotalUsersCount ( ) ; cr . Err != nil {
c . Err = cr . Err
return
} else {
count := cr . Data . ( int64 )
if count <= 0 {
firstAccount = true
}
}
}
if ! firstAccount && ! * utils . Cfg . TeamSettings . EnableOpenServer && len ( teamId ) == 0 {
c . Err = model . NewLocAppError ( "createUser" , "api.user.create_user.no_open_server" , nil , "email=" + user . Email )
return
2015-07-15 12:48:50 -04:00
}
2016-01-13 10:54:12 -05:00
if ! CheckUserDomain ( user , utils . Cfg . TeamSettings . RestrictCreationToDomains ) {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "createUser" , "api.user.create_user.accepted_domain.app_error" , nil , "" )
2016-01-13 10:54:12 -05:00
return
}
2016-04-21 22:37:01 -07:00
ruser , err := CreateUser ( user )
2015-12-08 13:38:43 -05:00
if err != nil {
c . Err = err
2015-06-14 23:53:32 -08:00
return
}
2016-04-21 22:37:01 -07:00
if len ( teamId ) > 0 {
err := JoinUserToTeam ( team , ruser )
if err != nil {
c . Err = err
return
}
2016-05-18 17:14:55 +02:00
go addDirectChannels ( team . Id , ruser )
2016-04-21 22:37:01 -07:00
}
2016-05-18 17:14:55 +02:00
if shouldSendWelcomeEmail {
go sendWelcomeEmail ( c , ruser . Id , ruser . Email , c . GetSiteURL ( ) , ruser . EmailVerified )
2015-11-05 08:59:29 -05:00
}
2015-06-14 23:53:32 -08:00
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
2016-11-14 13:36:59 +01:00
// Check that a user's email domain matches a list of space-delimited domains as a string.
2016-01-13 10:54:12 -05:00
func CheckUserDomain ( user * model . User , domains string ) bool {
2016-01-14 08:23:48 -05:00
if len ( domains ) == 0 {
return true
}
2016-01-13 10:54:12 -05:00
domainArray := strings . Fields ( strings . TrimSpace ( strings . ToLower ( strings . Replace ( strings . Replace ( domains , "@" , " " , - 1 ) , "," , " " , - 1 ) ) ) )
matched := false
for _ , d := range domainArray {
2016-06-27 08:08:16 -04:00
if strings . HasSuffix ( strings . ToLower ( user . Email ) , "@" + d ) {
2016-01-13 10:54:12 -05:00
matched = true
break
}
}
return matched
}
2015-07-22 12:13:45 -04:00
func IsVerifyHashRequired ( user * model . User , team * model . Team , hash string ) bool {
shouldVerifyHash := true
if team . Type == model . TEAM_INVITE && len ( team . AllowedDomains ) > 0 && len ( hash ) == 0 && user != nil {
2016-01-13 10:54:12 -05:00
matched := CheckUserDomain ( user , team . AllowedDomains )
2015-07-22 12:13:45 -04:00
if matched {
shouldVerifyHash = false
} else {
return true
}
}
if team . Type == model . TEAM_OPEN {
shouldVerifyHash = false
}
if len ( hash ) > 0 {
shouldVerifyHash = true
}
return shouldVerifyHash
}
2016-04-21 22:37:01 -07:00
func CreateUser ( user * model . User ) ( * model . User , * model . AppError ) {
2015-09-22 01:15:41 -07:00
2016-09-13 12:42:48 -04:00
user . Roles = model . ROLE_SYSTEM_USER . Id
2015-09-23 15:52:59 -07:00
2016-05-25 14:19:54 +02:00
// Below is a special case where the first user in the entire
2016-09-13 12:42:48 -04:00
// system is granted the system_admin role
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetTotalUsersCount ( ) ; result . Err != nil {
return nil , result . Err
2015-06-14 23:53:32 -08:00
} else {
2016-04-21 22:37:01 -07:00
count := result . Data . ( int64 )
if count <= 0 {
2016-09-13 12:42:48 -04:00
user . Roles = model . ROLE_SYSTEM_ADMIN . Id + " " + model . ROLE_SYSTEM_USER . Id
2016-04-21 22:37:01 -07:00
}
2015-06-14 23:53:32 -08:00
}
user . MakeNonNil ( )
2016-06-02 16:47:26 -03:00
user . Locale = * utils . Cfg . LocalizationSettings . DefaultClientLocale
2015-06-14 23:53:32 -08:00
2016-07-06 18:54:54 -04:00
if err := utils . IsPasswordValid ( user . Password ) ; user . AuthService == "" && err != nil {
return nil , err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Save ( user ) ; result . Err != nil {
2016-01-22 01:37:11 -03:00
l4g . Error ( utils . T ( "api.user.create_user.save.error" ) , result . Err )
2015-12-08 13:38:43 -05:00
return nil , result . Err
2015-06-14 23:53:32 -08:00
} else {
ruser := result . Data . ( * model . User )
if user . EmailVerified {
2016-01-20 13:36:16 -06:00
if cresult := <- Srv . Store . User ( ) . VerifyEmail ( ruser . Id ) ; cresult . Err != nil {
2016-01-22 01:37:11 -03:00
l4g . Error ( utils . T ( "api.user.create_user.verified.error" ) , cresult . Err )
2015-06-14 23:53:32 -08:00
}
}
2015-11-06 12:55:05 -05:00
pref := model . Preference { UserId : ruser . Id , Category : model . PREFERENCE_CATEGORY_TUTORIAL_STEPS , Name : ruser . Id , Value : "0" }
2016-01-20 13:36:16 -06:00
if presult := <- Srv . Store . Preference ( ) . Save ( & model . Preferences { pref } ) ; presult . Err != nil {
2016-01-22 01:37:11 -03:00
l4g . Error ( utils . T ( "api.user.create_user.tutorial.error" ) , presult . Err . Message )
2015-11-06 12:55:05 -05:00
}
2015-06-14 23:53:32 -08:00
ruser . Sanitize ( map [ string ] bool { } )
2016-09-27 11:19:50 -03:00
// This message goes to everyone, so the teamId, channelId and userId are irrelevant
2016-10-19 14:49:25 -04:00
message := model . NewWebSocketEvent ( model . WEBSOCKET_EVENT_NEW_USER , "" , "" , "" , nil )
message . Add ( "user_id" , ruser . Id )
go Publish ( message )
2015-06-14 23:53:32 -08:00
2015-12-08 13:38:43 -05:00
return ruser , nil
2015-06-14 23:53:32 -08:00
}
}
2016-04-21 22:37:01 -07:00
func CreateOAuthUser ( c * Context , w http . ResponseWriter , r * http . Request , service string , userData io . Reader , teamId string ) * model . User {
2015-12-17 12:44:46 -05:00
var user * model . User
provider := einterfaces . GetOauthProvider ( service )
if provider == nil {
2016-07-29 14:58:37 -04:00
c . Err = model . NewLocAppError ( "CreateOAuthUser" , "api.user.create_oauth_user.not_available.app_error" , map [ string ] interface { } { "Service" : strings . Title ( service ) } , "" )
2015-12-17 12:44:46 -05:00
return nil
} else {
user = provider . GetUserFromJson ( userData )
}
if user == nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "CreateOAuthUser" , "api.user.create_oauth_user.create.app_error" , map [ string ] interface { } { "Service" : service } , "" )
2015-12-17 12:44:46 -05:00
return nil
}
2016-04-21 22:37:01 -07:00
suchan := Srv . Store . User ( ) . GetByAuth ( user . AuthData , service )
euchan := Srv . Store . User ( ) . GetByEmail ( user . Email )
2015-12-17 12:44:46 -05:00
2016-04-21 22:37:01 -07:00
found := true
count := 0
for found {
if found = IsUsernameTaken ( user . Username ) ; found {
user . Username = user . Username + strconv . Itoa ( count )
count += 1
2015-12-17 12:44:46 -05:00
}
}
if result := <- suchan ; result . Err == nil {
2016-04-21 22:37:01 -07:00
c . Err = model . NewLocAppError ( "CreateOAuthUser" , "api.user.create_oauth_user.already_used.app_error" ,
map [ string ] interface { } { "Service" : service } , "email=" + user . Email )
2015-12-17 12:44:46 -05:00
return nil
}
if result := <- euchan ; result . Err == nil {
2016-06-14 05:59:55 -07:00
authService := result . Data . ( * model . User ) . AuthService
if authService == "" {
c . Err = model . NewLocAppError ( "CreateOAuthUser" , "api.user.create_oauth_user.already_attached.app_error" ,
map [ string ] interface { } { "Service" : service , "Auth" : model . USER_AUTH_SERVICE_EMAIL } , "email=" + user . Email )
} else {
c . Err = model . NewLocAppError ( "CreateOAuthUser" , "api.user.create_oauth_user.already_attached.app_error" ,
map [ string ] interface { } { "Service" : service , "Auth" : authService } , "email=" + user . Email )
}
2015-12-17 12:44:46 -05:00
return nil
}
user . EmailVerified = true
2016-04-21 22:37:01 -07:00
ruser , err := CreateUser ( user )
2015-12-17 12:44:46 -05:00
if err != nil {
c . Err = err
return nil
}
2016-06-07 17:43:06 -04:00
if len ( teamId ) > 0 {
err = JoinUserToTeamById ( teamId , user )
if err != nil {
c . Err = err
2016-04-21 22:37:01 -07:00
return nil
}
2016-06-07 17:43:06 -04:00
go addDirectChannels ( teamId , user )
2016-04-21 22:37:01 -07:00
}
2016-05-03 14:10:36 -04:00
doLogin ( c , w , r , ruser , "" )
2015-12-17 12:44:46 -05:00
if c . Err != nil {
return nil
}
return ruser
}
2016-05-18 17:14:55 +02:00
func sendWelcomeEmail ( c * Context , userId string , email string , siteURL string , verified bool ) {
2016-07-13 13:03:46 -04:00
rawUrl , _ := url . Parse ( siteURL )
2016-11-14 09:11:54 -03:00
subject := c . T ( "api.templates.welcome_subject" , map [ string ] interface { } { "ServerURL" : rawUrl . Host } )
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "welcome_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
2016-07-13 13:03:46 -04:00
bodyPage . Props [ "Title" ] = c . T ( "api.templates.welcome_body.title" , map [ string ] interface { } { "ServerURL" : rawUrl . Host } )
2016-05-18 17:14:55 +02:00
bodyPage . Props [ "Info" ] = c . T ( "api.templates.welcome_body.info" )
bodyPage . Props [ "Button" ] = c . T ( "api.templates.welcome_body.button" )
bodyPage . Props [ "Info2" ] = c . T ( "api.templates.welcome_body.info2" )
bodyPage . Props [ "Info3" ] = c . T ( "api.templates.welcome_body.info3" )
2016-07-13 13:03:46 -04:00
bodyPage . Props [ "SiteURL" ] = siteURL
2016-05-18 17:14:55 +02:00
2016-07-22 10:53:57 -04:00
if * utils . Cfg . NativeAppSettings . AppDownloadLink != "" {
bodyPage . Props [ "AppDownloadInfo" ] = c . T ( "api.templates.welcome_body.app_download_info" )
bodyPage . Props [ "AppDownloadLink" ] = * utils . Cfg . NativeAppSettings . AppDownloadLink
}
2016-05-18 17:14:55 +02:00
if ! verified {
2016-11-14 10:48:33 -08:00
link := fmt . Sprintf ( "%s/do_verify_email?uid=%s&hid=%s&email=%s" , siteURL , userId , model . HashPassword ( userId + utils . Cfg . EmailSettings . InviteSalt ) , url . QueryEscape ( email ) )
2016-05-18 17:14:55 +02:00
bodyPage . Props [ "VerifyUrl" ] = link
}
2015-10-13 09:47:31 -07:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( email , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_welcome_email_and_forget.failed.error" ) , err )
}
2015-06-14 23:53:32 -08:00
}
2016-05-18 17:14:55 +02:00
func addDirectChannels ( teamId string , user * model . User ) {
var profiles map [ string ] * model . User
2016-10-19 14:49:25 -04:00
if result := <- Srv . Store . User ( ) . GetProfiles ( teamId , 0 , 100 ) ; result . Err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.add_direct_channels_and_forget.failed.error" ) , user . Id , teamId , result . Err . Error ( ) )
return
} else {
profiles = result . Data . ( map [ string ] * model . User )
}
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
var preferences model . Preferences
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
for id := range profiles {
if id == user . Id {
continue
}
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
profile := profiles [ id ]
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
preference := model . Preference {
UserId : user . Id ,
Category : model . PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW ,
Name : profile . Id ,
Value : "true" ,
}
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
preferences = append ( preferences , preference )
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
if len ( preferences ) >= 10 {
break
2015-10-15 14:52:59 -04:00
}
2016-05-18 17:14:55 +02:00
}
2015-10-15 14:52:59 -04:00
2016-05-18 17:14:55 +02:00
if result := <- Srv . Store . Preference ( ) . Save ( & preferences ) ; result . Err != nil {
l4g . Error ( utils . T ( "api.user.add_direct_channels_and_forget.failed.error" ) , user . Id , teamId , result . Err . Error ( ) )
}
2015-10-15 14:52:59 -04:00
}
2016-05-18 17:14:55 +02:00
func SendVerifyEmail ( c * Context , userId , userEmail , siteURL string ) {
2016-11-14 10:48:33 -08:00
link := fmt . Sprintf ( "%s/do_verify_email?uid=%s&hid=%s&email=%s" , siteURL , userId , model . HashPassword ( userId + utils . Cfg . EmailSettings . InviteSalt ) , url . QueryEscape ( userEmail ) )
2015-06-14 23:53:32 -08:00
2016-07-13 13:03:46 -04:00
url , _ := url . Parse ( siteURL )
2016-11-14 09:11:54 -03:00
subject := c . T ( "api.templates.verify_subject" ,
2016-07-13 13:03:46 -04:00
map [ string ] interface { } { "SiteName" : utils . ClientCfg [ "SiteName" ] } )
2016-01-24 17:10:54 -03:00
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "verify_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
2016-07-13 13:03:46 -04:00
bodyPage . Props [ "Title" ] = c . T ( "api.templates.verify_body.title" , map [ string ] interface { } { "ServerURL" : url . Host } )
2016-05-18 17:14:55 +02:00
bodyPage . Props [ "Info" ] = c . T ( "api.templates.verify_body.info" )
bodyPage . Props [ "VerifyUrl" ] = link
bodyPage . Props [ "Button" ] = c . T ( "api.templates.verify_body.button" )
2015-06-14 23:53:32 -08:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( userEmail , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_verify_email_and_forget.failed.error" ) , err )
}
2015-06-14 23:53:32 -08:00
}
2016-04-21 22:37:01 -07:00
func login ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
2016-05-03 14:10:36 -04:00
id := props [ "id" ]
loginId := props [ "login_id" ]
password := props [ "password" ]
mfaToken := props [ "token" ]
deviceId := props [ "device_id" ]
2016-05-09 15:56:50 -04:00
ldapOnly := props [ "ldap_only" ] == "true"
2016-05-03 14:10:36 -04:00
if len ( password ) == 0 {
2016-04-21 22:37:01 -07:00
c . Err = model . NewLocAppError ( "login" , "api.user.login.blank_pwd.app_error" , nil , "" )
c . Err . StatusCode = http . StatusBadRequest
return
}
var user * model . User
2016-05-03 14:10:36 -04:00
var err * model . AppError
2016-04-21 22:37:01 -07:00
2016-05-03 14:10:36 -04:00
if len ( id ) != 0 {
c . LogAuditWithUserId ( id , "attempt" )
2016-04-21 22:37:01 -07:00
2016-05-03 14:10:36 -04:00
if result := <- Srv . Store . User ( ) . Get ( id ) ; result . Err != nil {
2016-12-05 04:45:29 -08:00
c . LogAuditWithUserId ( id , "failure" )
2016-07-12 15:18:25 -04:00
c . Err = result . Err
c . Err . StatusCode = http . StatusBadRequest
2016-11-22 11:05:54 -08:00
if einterfaces . GetMetricsInterface ( ) != nil {
einterfaces . GetMetricsInterface ( ) . IncrementLoginFail ( )
}
2016-05-03 14:10:36 -04:00
return
} else {
user = result . Data . ( * model . User )
}
2015-07-15 12:48:50 -04:00
} else {
2016-05-03 14:10:36 -04:00
c . LogAudit ( "attempt" )
2016-03-30 12:49:29 -04:00
2016-05-09 15:56:50 -04:00
if user , err = getUserForLogin ( loginId , ldapOnly ) ; err != nil {
2016-05-03 14:10:36 -04:00
c . LogAudit ( "failure" )
2016-07-12 15:18:25 -04:00
c . Err = err
2016-11-22 11:05:54 -08:00
if einterfaces . GetMetricsInterface ( ) != nil {
einterfaces . GetMetricsInterface ( ) . IncrementLoginFail ( )
}
2016-05-03 14:10:36 -04:00
return
2016-04-21 22:37:01 -07:00
}
2016-05-03 14:10:36 -04:00
c . LogAuditWithUserId ( user . Id , "attempt" )
2015-06-14 23:53:32 -08:00
}
2015-07-22 12:42:03 -04:00
2016-05-03 14:10:36 -04:00
// and then authenticate them
if user , err = authenticateUser ( user , password , mfaToken ) ; err != nil {
c . LogAuditWithUserId ( user . Id , "failure" )
2016-07-12 15:18:25 -04:00
c . Err = err
2016-11-22 11:05:54 -08:00
if einterfaces . GetMetricsInterface ( ) != nil {
einterfaces . GetMetricsInterface ( ) . IncrementLoginFail ( )
}
2016-05-03 14:10:36 -04:00
return
}
2015-06-14 23:53:32 -08:00
2016-05-03 14:10:36 -04:00
c . LogAuditWithUserId ( user . Id , "success" )
2016-11-22 11:05:54 -08:00
if einterfaces . GetMetricsInterface ( ) != nil {
einterfaces . GetMetricsInterface ( ) . IncrementLogin ( )
}
2015-06-14 23:53:32 -08:00
2016-05-03 14:10:36 -04:00
doLogin ( c , w , r , user , deviceId )
2016-09-21 04:17:58 -07:00
if c . Err != nil {
return
}
2015-12-17 12:44:46 -05:00
2016-05-03 14:10:36 -04:00
user . Sanitize ( map [ string ] bool { } )
2015-07-22 12:42:03 -04:00
2016-05-03 14:10:36 -04:00
w . Write ( [ ] byte ( user . ToJson ( ) ) )
2015-07-15 12:48:50 -04:00
}
2015-06-14 23:53:32 -08:00
2016-05-09 15:56:50 -04:00
func getUserForLogin ( loginId string , onlyLdap bool ) ( * model . User , * model . AppError ) {
2016-05-12 21:36:02 -04:00
ldapAvailable := * utils . Cfg . LdapSettings . Enable && einterfaces . GetLdapInterface ( ) != nil && utils . IsLicensed && * utils . License . Features . LDAP
2016-01-13 14:58:49 -08:00
2016-05-03 14:10:36 -04:00
if result := <- Srv . Store . User ( ) . GetForLogin (
loginId ,
2016-05-09 15:56:50 -04:00
* utils . Cfg . EmailSettings . EnableSignInWithUsername && ! onlyLdap ,
* utils . Cfg . EmailSettings . EnableSignInWithEmail && ! onlyLdap ,
2016-05-03 14:10:36 -04:00
ldapAvailable ,
2016-05-12 12:06:26 -04:00
) ; result . Err != nil && result . Err . Id == "store.sql_user.get_for_login.multiple_users" {
// don't fall back to LDAP in this case since we already know there's an LDAP user, but that it shouldn't work
result . Err . StatusCode = http . StatusBadRequest
return nil , result . Err
} else if result . Err != nil {
2016-05-03 14:10:36 -04:00
if ! ldapAvailable {
// failed to find user and no LDAP server to fall back on
result . Err . StatusCode = http . StatusBadRequest
return nil , result . Err
2016-01-13 14:58:49 -08:00
}
2016-05-03 14:10:36 -04:00
// fall back to LDAP server to see if we can find a user
if ldapUser , ldapErr := einterfaces . GetLdapInterface ( ) . GetUser ( loginId ) ; ldapErr != nil {
ldapErr . StatusCode = http . StatusBadRequest
return nil , ldapErr
} else {
return ldapUser , nil
2016-01-13 14:58:49 -08:00
}
2016-05-03 14:10:36 -04:00
} else {
return result . Data . ( * model . User ) , nil
2016-01-13 14:58:49 -08:00
}
}
2016-04-21 22:37:01 -07:00
func LoginByOAuth ( c * Context , w http . ResponseWriter , r * http . Request , service string , userData io . Reader ) * model . User {
2016-03-11 10:48:43 -05:00
buf := bytes . Buffer { }
buf . ReadFrom ( userData )
2015-12-17 12:44:46 -05:00
authData := ""
provider := einterfaces . GetOauthProvider ( service )
if provider == nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "LoginByOAuth" , "api.user.login_by_oauth.not_available.app_error" ,
2016-07-29 14:58:37 -04:00
map [ string ] interface { } { "Service" : strings . Title ( service ) } , "" )
2015-12-17 12:44:46 -05:00
return nil
} else {
2016-03-11 10:48:43 -05:00
authData = provider . GetAuthDataFromJson ( bytes . NewReader ( buf . Bytes ( ) ) )
2015-12-17 12:44:46 -05:00
}
if len ( authData ) == 0 {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "LoginByOAuth" , "api.user.login_by_oauth.parse.app_error" ,
map [ string ] interface { } { "Service" : service } , "" )
2015-12-17 12:44:46 -05:00
return nil
}
var user * model . User
2016-05-11 11:04:30 -07:00
if result := <- Srv . Store . User ( ) . GetByAuth ( & authData , service ) ; result . Err != nil {
2016-04-21 22:37:01 -07:00
if result . Err . Id == store . MISSING_AUTH_ACCOUNT_ERROR {
return CreateOAuthUser ( c , w , r , service , bytes . NewReader ( buf . Bytes ( ) ) , "" )
2016-03-11 10:48:43 -05:00
}
2015-12-17 12:44:46 -05:00
c . Err = result . Err
return nil
} else {
user = result . Data . ( * model . User )
2016-05-03 14:10:36 -04:00
doLogin ( c , w , r , user , "" )
2016-09-21 04:17:58 -07:00
if c . Err != nil {
return nil
}
2015-12-17 12:44:46 -05:00
return user
}
}
2016-04-21 22:37:01 -07:00
// User MUST be authenticated completely before calling Login
2016-05-03 14:10:36 -04:00
func doLogin ( c * Context , w http . ResponseWriter , r * http . Request , user * model . User , deviceId string ) {
2016-04-21 22:37:01 -07:00
2016-09-13 12:42:48 -04:00
session := & model . Session { UserId : user . Id , Roles : user . GetRawRoles ( ) , DeviceId : deviceId , IsOAuth : false }
2015-06-14 23:53:32 -08:00
2016-01-05 11:13:25 -06:00
maxAge := * utils . Cfg . ServiceSettings . SessionLengthWebInDays * 60 * 60 * 24
2015-06-14 23:53:32 -08:00
2015-07-15 12:48:50 -04:00
if len ( deviceId ) > 0 {
2016-01-05 11:13:25 -06:00
session . SetExpireInDays ( * utils . Cfg . ServiceSettings . SessionLengthMobileInDays )
maxAge = * utils . Cfg . ServiceSettings . SessionLengthMobileInDays * 60 * 60 * 24
2016-01-05 09:58:21 -06:00
// A special case where we logout of all other sessions with the same Id
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . GetSessions ( user . Id ) ; result . Err != nil {
2016-01-05 09:58:21 -06:00
c . Err = result . Err
2016-04-21 22:37:01 -07:00
c . Err . StatusCode = http . StatusInternalServerError
2016-01-05 09:58:21 -06:00
return
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
if session . DeviceId == deviceId {
2016-01-22 01:37:11 -03:00
l4g . Debug ( utils . T ( "api.user.login.revoking.app_error" ) , session . Id , user . Id )
2016-01-05 09:58:21 -06:00
RevokeSessionById ( c , session . Id )
if c . Err != nil {
c . LogError ( c . Err )
c . Err = nil
}
}
}
}
2015-06-14 23:53:32 -08:00
} else {
2016-01-05 11:13:25 -06:00
session . SetExpireInDays ( * utils . Cfg . ServiceSettings . SessionLengthWebInDays )
2015-06-14 23:53:32 -08:00
}
ua := user_agent . New ( r . UserAgent ( ) )
plat := ua . Platform ( )
if plat == "" {
plat = "unknown"
}
os := ua . OS ( )
if os == "" {
os = "unknown"
}
bname , bversion := ua . Browser ( )
if bname == "" {
bname = "unknown"
}
if bversion == "" {
bversion = "0.0"
}
session . AddProp ( model . SESSION_PROP_PLATFORM , plat )
session . AddProp ( model . SESSION_PROP_OS , os )
session . AddProp ( model . SESSION_PROP_BROWSER , fmt . Sprintf ( "%v/%v" , bname , bversion ) )
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . Save ( session ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
2016-04-21 22:37:01 -07:00
c . Err . StatusCode = http . StatusInternalServerError
2015-06-14 23:53:32 -08:00
return
} else {
session = result . Data . ( * model . Session )
2015-09-16 15:49:12 -04:00
AddSessionToCache ( session )
2015-06-14 23:53:32 -08:00
}
2015-09-16 15:49:12 -04:00
w . Header ( ) . Set ( model . HEADER_TOKEN , session . Token )
2015-06-14 23:53:32 -08:00
2016-04-28 17:03:52 -07:00
secure := false
if GetProtocol ( r ) == "https" {
2016-04-28 17:14:19 -07:00
secure = true
2016-04-28 17:03:52 -07:00
}
2016-02-08 11:29:11 -05:00
expiresAt := time . Unix ( model . GetMillis ( ) / 1000 + int64 ( maxAge ) , 0 )
2016-02-08 07:26:10 -05:00
sessionCookie := & http . Cookie {
2015-10-20 04:37:51 -07:00
Name : model . SESSION_COOKIE_TOKEN ,
2016-02-08 07:26:10 -05:00
Value : session . Token ,
2015-10-01 17:52:47 -07:00
Path : "/" ,
MaxAge : maxAge ,
2016-02-08 11:29:11 -05:00
Expires : expiresAt ,
2015-10-01 17:52:47 -07:00
HttpOnly : true ,
2016-04-28 17:03:52 -07:00
Secure : secure ,
2015-10-01 17:52:47 -07:00
}
2016-02-08 07:26:10 -05:00
http . SetCookie ( w , sessionCookie )
2015-10-01 17:52:47 -07:00
2015-06-14 23:53:32 -08:00
c . Session = * session
2015-12-08 13:38:43 -05:00
}
2015-06-14 23:53:32 -08:00
func revokeSession ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
2015-09-16 15:49:12 -04:00
id := props [ "id" ]
2016-01-05 09:58:21 -06:00
RevokeSessionById ( c , id )
w . Write ( [ ] byte ( model . MapToJson ( props ) ) )
}
2015-06-14 23:53:32 -08:00
2016-01-26 20:32:24 -05:00
func attachDeviceId ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
deviceId := props [ "device_id" ]
if len ( deviceId ) == 0 {
c . SetInvalidParam ( "attachDevice" , "deviceId" )
return
}
if ! ( strings . HasPrefix ( deviceId , model . PUSH_NOTIFY_APPLE + ":" ) || strings . HasPrefix ( deviceId , model . PUSH_NOTIFY_ANDROID + ":" ) ) {
c . SetInvalidParam ( "attachDevice" , "deviceId" )
return
}
// A special case where we logout of all other sessions with the same Id
if result := <- Srv . Store . Session ( ) . GetSessions ( c . Session . UserId ) ; result . Err != nil {
c . Err = result . Err
2016-04-21 22:37:01 -07:00
c . Err . StatusCode = http . StatusInternalServerError
2016-01-26 20:32:24 -05:00
return
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
if session . DeviceId == deviceId && session . Id != c . Session . Id {
l4g . Debug ( utils . T ( "api.user.login.revoking.app_error" ) , session . Id , c . Session . UserId )
RevokeSessionById ( c , session . Id )
if c . Err != nil {
c . LogError ( c . Err )
c . Err = nil
}
}
}
}
2016-12-08 07:18:15 -08:00
RemoveAllSessionsForUserId ( c . Session . UserId )
2016-07-21 08:36:11 -08:00
c . Session . SetExpireInDays ( * utils . Cfg . ServiceSettings . SessionLengthMobileInDays )
2016-01-26 20:32:24 -05:00
2016-07-21 08:36:11 -08:00
maxAge := * utils . Cfg . ServiceSettings . SessionLengthMobileInDays * 60 * 60 * 24
secure := false
if GetProtocol ( r ) == "https" {
secure = true
}
expiresAt := time . Unix ( model . GetMillis ( ) / 1000 + int64 ( maxAge ) , 0 )
sessionCookie := & http . Cookie {
Name : model . SESSION_COOKIE_TOKEN ,
Value : c . Session . Token ,
Path : "/" ,
MaxAge : maxAge ,
Expires : expiresAt ,
HttpOnly : true ,
Secure : secure ,
}
http . SetCookie ( w , sessionCookie )
if result := <- Srv . Store . Session ( ) . UpdateDeviceId ( c . Session . Id , deviceId , c . Session . ExpiresAt ) ; result . Err != nil {
2016-01-26 20:32:24 -05:00
c . Err = result . Err
return
}
2016-02-08 14:30:58 -08:00
w . Write ( [ ] byte ( model . MapToJson ( props ) ) )
2016-01-26 20:32:24 -05:00
}
2016-01-05 09:58:21 -06:00
func RevokeSessionById ( c * Context , sessionId string ) {
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . Get ( sessionId ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
} else {
2015-09-16 15:49:12 -04:00
session := result . Data . ( * model . Session )
c . LogAudit ( "session_id=" + session . Id )
if session . IsOAuth {
2016-01-20 13:36:16 -06:00
RevokeAccessToken ( session . Token )
2015-09-16 15:49:12 -04:00
} else {
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
2015-09-16 15:49:12 -04:00
c . Err = result . Err
2015-06-14 23:53:32 -08:00
}
}
2016-09-15 09:35:44 -03:00
2016-11-04 13:05:32 -03:00
RevokeWebrtcToken ( session . Id )
2016-12-08 07:18:15 -08:00
RemoveAllSessionsForUserId ( session . UserId )
2015-06-14 23:53:32 -08:00
}
}
2016-06-03 09:33:59 -04:00
// IF YOU UPDATE THIS PLEASE UPDATE BELOW
2015-06-14 23:53:32 -08:00
func RevokeAllSession ( c * Context , userId string ) {
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . GetSessions ( userId ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
2015-09-16 15:49:12 -04:00
c . LogAuditWithUserId ( userId , "session_id=" + session . Id )
2015-09-24 08:02:01 -04:00
if session . IsOAuth {
2016-01-20 13:36:16 -06:00
RevokeAccessToken ( session . Token )
2015-09-24 08:02:01 -04:00
} else {
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
2015-09-24 08:02:01 -04:00
c . Err = result . Err
return
}
2015-06-14 23:53:32 -08:00
}
2016-09-15 09:35:44 -03:00
2016-11-04 13:05:32 -03:00
RevokeWebrtcToken ( session . Id )
2015-06-14 23:53:32 -08:00
}
}
2016-11-15 10:38:58 -05:00
RemoveAllSessionsForUserId ( userId )
2015-06-14 23:53:32 -08:00
}
2016-06-03 09:33:59 -04:00
// UGH...
// If you update this please update above
func RevokeAllSessionsNoContext ( userId string ) * model . AppError {
if result := <- Srv . Store . Session ( ) . GetSessions ( userId ) ; result . Err != nil {
return result . Err
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
if session . IsOAuth {
RevokeAccessToken ( session . Token )
} else {
if result := <- Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
return result . Err
}
}
2016-09-15 09:35:44 -03:00
2016-11-04 13:05:32 -03:00
RevokeWebrtcToken ( session . Id )
2016-06-03 09:33:59 -04:00
}
}
2016-11-15 10:38:58 -05:00
RemoveAllSessionsForUserId ( userId )
2016-06-03 09:33:59 -04:00
return nil
}
2015-06-14 23:53:32 -08:00
func getSessions ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
2016-04-21 22:37:01 -07:00
id := params [ "user_id" ]
2015-06-14 23:53:32 -08:00
2016-09-13 12:42:48 -04:00
if ! HasPermissionToUser ( c , id ) {
2015-06-14 23:53:32 -08:00
return
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . GetSessions ( id ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
session . Sanitize ( )
}
w . Write ( [ ] byte ( model . SessionsToJson ( sessions ) ) )
}
}
func logout ( c * Context , w http . ResponseWriter , r * http . Request ) {
data := make ( map [ string ] string )
data [ "user_id" ] = c . Session . UserId
Logout ( c , w , r )
if c . Err == nil {
w . Write ( [ ] byte ( model . MapToJson ( data ) ) )
}
}
func Logout ( c * Context , w http . ResponseWriter , r * http . Request ) {
c . LogAudit ( "" )
2015-10-01 17:52:47 -07:00
c . RemoveSessionCookie ( w , r )
2016-04-21 22:37:01 -07:00
if c . Session . Id != "" {
2016-05-06 12:08:49 -04:00
RevokeSessionById ( c , c . Session . Id )
2015-06-14 23:53:32 -08:00
}
}
func getMe ( c * Context , w http . ResponseWriter , r * http . Request ) {
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Get ( c . Session . UserId ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
2015-10-01 17:52:47 -07:00
c . RemoveSessionCookie ( w , r )
2016-01-22 01:37:11 -03:00
l4g . Error ( utils . T ( "api.user.get_me.getting.error" ) , c . Session . UserId )
2015-06-14 23:53:32 -08:00
return
2016-12-19 10:16:22 -05:00
} else if HandleEtag ( result . Data . ( * model . User ) . Etag ( utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress ) , "Get Me" , w , r ) {
2015-06-14 23:53:32 -08:00
return
} else {
result . Data . ( * model . User ) . Sanitize ( map [ string ] bool { } )
2016-06-14 12:12:46 -04:00
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , result . Data . ( * model . User ) . Etag ( utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress ) )
2015-06-14 23:53:32 -08:00
w . Write ( [ ] byte ( result . Data . ( * model . User ) . ToJson ( ) ) )
return
}
}
2016-04-21 22:37:01 -07:00
func getInitialLoad ( c * Context , w http . ResponseWriter , r * http . Request ) {
il := model . InitialLoad { }
var cchan store . StoreChannel
if sessionCache . Len ( ) == 0 {
2016-05-25 14:19:54 +02:00
// Below is a special case when intializating a new server
2016-04-21 22:37:01 -07:00
// Lets check to make sure the server is really empty
cchan = Srv . Store . User ( ) . GetTotalUsersCount ( )
}
2016-02-08 07:26:10 -05:00
if len ( c . Session . UserId ) != 0 {
2016-04-21 22:37:01 -07:00
uchan := Srv . Store . User ( ) . Get ( c . Session . UserId )
pchan := Srv . Store . Preference ( ) . GetAll ( c . Session . UserId )
tchan := Srv . Store . Team ( ) . GetTeamsByUserId ( c . Session . UserId )
il . TeamMembers = c . Session . TeamMembers
if ru := <- uchan ; ru . Err != nil {
c . Err = ru . Err
return
} else {
il . User = ru . Data . ( * model . User )
il . User . Sanitize ( map [ string ] bool { } )
}
if rp := <- pchan ; rp . Err != nil {
c . Err = rp . Err
return
} else {
il . Preferences = rp . Data . ( model . Preferences )
}
if rt := <- tchan ; rt . Err != nil {
c . Err = rt . Err
return
} else {
il . Teams = rt . Data . ( [ ] * model . Team )
for _ , team := range il . Teams {
team . Sanitize ( )
}
}
2016-02-08 07:26:10 -05:00
}
2016-04-21 22:37:01 -07:00
if cchan != nil {
if cr := <- cchan ; cr . Err != nil {
c . Err = cr . Err
return
} else {
count := cr . Data . ( int64 )
if count <= 0 {
il . NoAccounts = true
}
}
}
il . ClientCfg = utils . ClientCfg
2016-09-13 12:42:48 -04:00
if HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
2016-07-05 14:02:00 -04:00
il . LicenseCfg = utils . ClientLicense
} else {
2016-08-25 04:32:44 -08:00
il . LicenseCfg = utils . GetSanitizedClientLicense ( )
2016-07-05 14:02:00 -04:00
}
2016-09-13 12:42:48 -04:00
c . Err = nil
2016-04-21 22:37:01 -07:00
w . Write ( [ ] byte ( il . ToJson ( ) ) )
2016-02-08 07:26:10 -05:00
}
2015-06-14 23:53:32 -08:00
func getUser ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
2016-04-21 22:37:01 -07:00
id := params [ "user_id" ]
2015-06-14 23:53:32 -08:00
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Get ( id ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
2016-12-19 10:16:22 -05:00
} else if HandleEtag ( result . Data . ( * model . User ) . Etag ( utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress ) , "Get User" , w , r ) {
2015-06-14 23:53:32 -08:00
return
} else {
2016-09-14 08:57:33 -04:00
user := sanitizeProfile ( c , result . Data . ( * model . User ) )
2016-11-22 00:50:57 +01:00
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , user . Etag ( utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress ) )
w . Write ( [ ] byte ( result . Data . ( * model . User ) . ToJson ( ) ) )
return
}
}
func getByUsername ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
username := params [ "username" ]
if result := <- Srv . Store . User ( ) . GetByUsername ( username ) ; result . Err != nil {
c . Err = result . Err
return
2016-12-19 10:16:22 -05:00
} else if HandleEtag ( result . Data . ( * model . User ) . Etag ( utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress ) , "Get By Username" , w , r ) {
2016-11-22 00:50:57 +01:00
return
} else {
user := sanitizeProfile ( c , result . Data . ( * model . User ) )
2016-09-14 08:57:33 -04:00
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , user . Etag ( utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress ) )
2015-06-14 23:53:32 -08:00
w . Write ( [ ] byte ( result . Data . ( * model . User ) . ToJson ( ) ) )
return
}
}
2016-10-19 14:49:25 -04:00
func getProfiles ( c * Context , w http . ResponseWriter , r * http . Request ) {
2016-05-04 06:31:42 -07:00
params := mux . Vars ( r )
2016-10-19 14:49:25 -04:00
offset , err := strconv . Atoi ( params [ "offset" ] )
if err != nil {
c . SetInvalidParam ( "getProfiles" , "offset" )
return
}
2016-05-04 06:31:42 -07:00
2016-10-19 14:49:25 -04:00
limit , err := strconv . Atoi ( params [ "limit" ] )
if err != nil {
c . SetInvalidParam ( "getProfiles" , "limit" )
return
}
2016-05-04 06:31:42 -07:00
2016-10-19 14:49:25 -04:00
etag := ( <- Srv . Store . User ( ) . GetEtagForAllProfiles ( ) ) . Data . ( string )
2016-12-19 10:16:22 -05:00
if HandleEtag ( etag , "Get Profiles" , w , r ) {
2016-10-19 14:49:25 -04:00
return
2016-05-04 06:31:42 -07:00
}
2016-10-19 14:49:25 -04:00
if result := <- Srv . Store . User ( ) . GetAllProfiles ( offset , limit ) ; result . Err != nil {
2016-05-04 06:31:42 -07:00
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
for k , p := range profiles {
2016-09-02 12:24:20 -04:00
profiles [ k ] = sanitizeProfile ( c , p )
2016-05-04 06:31:42 -07:00
}
2016-10-19 14:49:25 -04:00
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , etag )
2016-05-04 06:31:42 -07:00
w . Write ( [ ] byte ( model . UserMapToJson ( profiles ) ) )
}
}
2016-10-19 14:49:25 -04:00
func getProfilesInTeam ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-09-23 12:49:28 -07:00
params := mux . Vars ( r )
2016-10-19 14:49:25 -04:00
teamId := params [ "team_id" ]
2015-06-14 23:53:32 -08:00
2016-10-19 14:49:25 -04:00
if c . Session . GetTeamByTeamId ( teamId ) == nil {
2016-09-13 12:42:48 -04:00
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
2016-04-21 22:37:01 -07:00
return
}
2015-09-23 12:49:28 -07:00
}
2015-06-14 23:53:32 -08:00
2016-10-19 14:49:25 -04:00
offset , err := strconv . Atoi ( params [ "offset" ] )
if err != nil {
c . SetInvalidParam ( "getProfilesInTeam" , "offset" )
return
}
limit , err := strconv . Atoi ( params [ "limit" ] )
if err != nil {
c . SetInvalidParam ( "getProfilesInTeam" , "limit" )
return
}
etag := ( <- Srv . Store . User ( ) . GetEtagForProfiles ( teamId ) ) . Data . ( string )
2016-12-19 10:16:22 -05:00
if HandleEtag ( etag , "Get Profiles In Team" , w , r ) {
2015-06-14 23:53:32 -08:00
return
}
2016-10-19 14:49:25 -04:00
if result := <- Srv . Store . User ( ) . GetProfiles ( teamId , offset , limit ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
for k , p := range profiles {
2016-09-02 12:24:20 -04:00
profiles [ k ] = sanitizeProfile ( c , p )
2016-04-21 22:37:01 -07:00
}
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , etag )
w . Write ( [ ] byte ( model . UserMapToJson ( profiles ) ) )
}
}
2016-10-19 14:49:25 -04:00
func getProfilesInChannel ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
channelId := params [ "channel_id" ]
if c . Session . GetTeamByTeamId ( c . TeamId ) == nil {
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
return
}
}
if ! HasPermissionToChannelContext ( c , channelId , model . PERMISSION_READ_CHANNEL ) {
return
}
offset , err := strconv . Atoi ( params [ "offset" ] )
if err != nil {
c . SetInvalidParam ( "getProfiles" , "offset" )
2016-04-21 22:37:01 -07:00
return
}
2016-10-19 14:49:25 -04:00
limit , err := strconv . Atoi ( params [ "limit" ] )
if err != nil {
c . SetInvalidParam ( "getProfiles" , "limit" )
return
}
if result := <- Srv . Store . User ( ) . GetProfilesInChannel ( channelId , offset , limit , false ) ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
for k , p := range profiles {
profiles [ k ] = sanitizeProfile ( c , p )
}
w . Write ( [ ] byte ( model . UserMapToJson ( profiles ) ) )
}
}
func getProfilesNotInChannel ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
channelId := params [ "channel_id" ]
if c . Session . GetTeamByTeamId ( c . TeamId ) == nil {
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
return
}
}
if ! HasPermissionToChannelContext ( c , channelId , model . PERMISSION_READ_CHANNEL ) {
return
}
offset , err := strconv . Atoi ( params [ "offset" ] )
if err != nil {
c . SetInvalidParam ( "getProfiles" , "offset" )
return
}
limit , err := strconv . Atoi ( params [ "limit" ] )
if err != nil {
c . SetInvalidParam ( "getProfiles" , "limit" )
return
}
if result := <- Srv . Store . User ( ) . GetProfilesNotInChannel ( c . TeamId , channelId , offset , limit ) ; result . Err != nil {
2016-04-21 22:37:01 -07:00
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
for k , p := range profiles {
2016-09-02 12:24:20 -04:00
profiles [ k ] = sanitizeProfile ( c , p )
2015-06-14 23:53:32 -08:00
}
w . Write ( [ ] byte ( model . UserMapToJson ( profiles ) ) )
}
}
func getAudits ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
2016-04-21 22:37:01 -07:00
id := params [ "user_id" ]
2015-06-14 23:53:32 -08:00
2016-09-13 12:42:48 -04:00
if ! HasPermissionToUser ( c , id ) {
2015-06-14 23:53:32 -08:00
return
}
2016-01-20 13:36:16 -06:00
userChan := Srv . Store . User ( ) . Get ( id )
auditChan := Srv . Store . Audit ( ) . Get ( id , 20 )
2015-06-14 23:53:32 -08:00
if c . Err = ( <- userChan ) . Err ; c . Err != nil {
return
}
if result := <- auditChan ; result . Err != nil {
c . Err = result . Err
return
} else {
audits := result . Data . ( model . Audits )
etag := audits . Etag ( )
2016-12-19 10:16:22 -05:00
if HandleEtag ( etag , "Get Audits" , w , r ) {
2015-06-14 23:53:32 -08:00
return
}
if len ( etag ) > 0 {
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , etag )
}
w . Write ( [ ] byte ( audits . ToJson ( ) ) )
return
}
}
2015-07-28 16:39:50 -07:00
func createProfileImage ( username string , userId string ) ( [ ] byte , * model . AppError ) {
2015-06-14 23:53:32 -08:00
colors := [ ] color . NRGBA {
{ 197 , 8 , 126 , 255 } ,
{ 227 , 207 , 18 , 255 } ,
{ 28 , 181 , 105 , 255 } ,
{ 35 , 188 , 224 , 255 } ,
{ 116 , 49 , 196 , 255 } ,
{ 197 , 8 , 126 , 255 } ,
{ 197 , 19 , 19 , 255 } ,
{ 250 , 134 , 6 , 255 } ,
{ 227 , 207 , 18 , 255 } ,
{ 123 , 201 , 71 , 255 } ,
{ 28 , 181 , 105 , 255 } ,
{ 35 , 188 , 224 , 255 } ,
{ 116 , 49 , 196 , 255 } ,
{ 197 , 8 , 126 , 255 } ,
{ 197 , 19 , 19 , 255 } ,
{ 250 , 134 , 6 , 255 } ,
{ 227 , 207 , 18 , 255 } ,
{ 123 , 201 , 71 , 255 } ,
{ 28 , 181 , 105 , 255 } ,
{ 35 , 188 , 224 , 255 } ,
{ 116 , 49 , 196 , 255 } ,
{ 197 , 8 , 126 , 255 } ,
{ 197 , 19 , 19 , 255 } ,
{ 250 , 134 , 6 , 255 } ,
{ 227 , 207 , 18 , 255 } ,
{ 123 , 201 , 71 , 255 } ,
}
h := fnv . New32a ( )
h . Write ( [ ] byte ( userId ) )
seed := h . Sum32 ( )
2015-07-28 17:31:27 -07:00
initial := string ( strings . ToUpper ( username ) [ 0 ] )
2015-07-28 16:39:50 -07:00
2016-03-14 08:50:46 -04:00
fontBytes , err := ioutil . ReadFile ( utils . FindDir ( "fonts" ) + utils . Cfg . FileSettings . InitialFont )
2015-07-28 16:39:50 -07:00
if err != nil {
2016-01-22 01:37:11 -03:00
return nil , model . NewLocAppError ( "createProfileImage" , "api.user.create_profile_image.default_font.app_error" , nil , err . Error ( ) )
2015-07-28 16:39:50 -07:00
}
font , err := freetype . ParseFont ( fontBytes )
if err != nil {
2016-01-22 01:37:11 -03:00
return nil , model . NewLocAppError ( "createProfileImage" , "api.user.create_profile_image.default_font.app_error" , nil , err . Error ( ) )
2015-07-28 16:39:50 -07:00
}
2015-09-23 13:47:10 -07:00
width := int ( utils . Cfg . FileSettings . ProfileWidth )
height := int ( utils . Cfg . FileSettings . ProfileHeight )
2015-07-06 18:08:52 -04:00
color := colors [ int64 ( seed ) % int64 ( len ( colors ) ) ]
2015-07-29 13:40:24 -07:00
dstImg := image . NewRGBA ( image . Rect ( 0 , 0 , width , height ) )
2015-07-28 16:39:50 -07:00
srcImg := image . White
draw . Draw ( dstImg , dstImg . Bounds ( ) , & image . Uniform { color } , image . ZP , draw . Src )
2015-07-29 13:40:24 -07:00
size := float64 ( ( width + height ) / 4 )
2015-07-28 16:39:50 -07:00
c := freetype . NewContext ( )
c . SetFont ( font )
2015-07-28 17:31:27 -07:00
c . SetFontSize ( size )
2015-07-28 16:39:50 -07:00
c . SetClip ( dstImg . Bounds ( ) )
c . SetDst ( dstImg )
c . SetSrc ( srcImg )
2015-07-29 13:40:24 -07:00
pt := freetype . Pt ( width / 6 , height * 2 / 3 )
2015-07-28 17:31:27 -07:00
_ , err = c . DrawString ( initial , pt )
2015-07-28 16:39:50 -07:00
if err != nil {
2016-01-22 01:37:11 -03:00
return nil , model . NewLocAppError ( "createProfileImage" , "api.user.create_profile_image.initial.app_error" , nil , err . Error ( ) )
2015-07-28 16:39:50 -07:00
}
2015-06-14 23:53:32 -08:00
2015-06-19 11:19:51 -04:00
buf := new ( bytes . Buffer )
2015-07-28 16:39:50 -07:00
if imgErr := png . Encode ( buf , dstImg ) ; imgErr != nil {
2016-01-22 01:37:11 -03:00
return nil , model . NewLocAppError ( "createProfileImage" , "api.user.create_profile_image.encode.app_error" , nil , imgErr . Error ( ) )
2015-06-19 11:19:51 -04:00
} else {
return buf . Bytes ( ) , nil
2015-06-14 23:53:32 -08:00
}
2015-06-19 11:19:51 -04:00
}
2015-06-14 23:53:32 -08:00
2015-06-19 11:19:51 -04:00
func getProfileImage ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-06-14 23:53:32 -08:00
params := mux . Vars ( r )
2016-04-21 22:37:01 -07:00
id := params [ "user_id" ]
2016-12-21 05:33:04 -08:00
readFailed := false
2015-06-14 23:53:32 -08:00
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Get ( id ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
2015-06-19 11:19:51 -04:00
var img [ ] byte
2015-06-14 23:53:32 -08:00
2015-09-23 13:47:10 -07:00
if len ( utils . Cfg . FileSettings . DriverName ) == 0 {
2015-07-16 08:54:09 -04:00
var err * model . AppError
if img , err = createProfileImage ( result . Data . ( * model . User ) . Username , id ) ; err != nil {
2015-06-19 11:19:51 -04:00
c . Err = err
return
}
} else {
2016-10-26 05:21:07 -07:00
path := "users/" + id + "/profile.png"
2015-06-14 23:53:32 -08:00
2016-04-11 13:45:03 -04:00
if data , err := ReadFile ( path ) ; err != nil {
2016-12-21 05:33:04 -08:00
readFailed = true
2015-07-16 08:54:09 -04:00
if img , err = createProfileImage ( result . Data . ( * model . User ) . Username , id ) ; err != nil {
2015-06-19 11:19:51 -04:00
c . Err = err
return
}
2015-06-14 23:53:32 -08:00
2016-12-21 05:33:04 -08:00
if result . Data . ( * model . User ) . LastPictureUpdate == 0 {
if err := WriteFile ( img , path ) ; err != nil {
c . Err = err
return
}
2015-06-19 11:19:51 -04:00
}
2015-06-14 23:53:32 -08:00
2015-06-19 11:19:51 -04:00
} else {
img = data
2015-06-14 23:53:32 -08:00
}
}
2016-12-21 05:33:04 -08:00
if c . Session . UserId == id || readFailed {
2015-06-14 23:53:32 -08:00
w . Header ( ) . Set ( "Cache-Control" , "max-age=300, public" ) // 5 mins
} else {
w . Header ( ) . Set ( "Cache-Control" , "max-age=86400, public" ) // 24 hrs
}
2015-10-25 18:02:36 +01:00
w . Header ( ) . Set ( "Content-Type" , "image/png" )
2015-06-14 23:53:32 -08:00
w . Write ( img )
}
}
func uploadProfileImage ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-09-23 13:47:10 -07:00
if len ( utils . Cfg . FileSettings . DriverName ) == 0 {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.storage.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusNotImplemented
return
}
2016-05-24 15:07:42 +02:00
if r . ContentLength > * utils . Cfg . FileSettings . MaxFileSize {
2016-04-11 13:45:03 -04:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.too_large.app_error" , nil , "" )
c . Err . StatusCode = http . StatusRequestEntityTooLarge
return
}
2016-05-24 15:07:42 +02:00
if err := r . ParseMultipartForm ( * utils . Cfg . FileSettings . MaxFileSize ) ; err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.parse.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
return
}
m := r . MultipartForm
imageArray , ok := m . File [ "image" ]
if ! ok {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.no_file.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusBadRequest
return
}
if len ( imageArray ) <= 0 {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.array.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusBadRequest
return
}
imageData := imageArray [ 0 ]
file , err := imageData . Open ( )
defer file . Close ( )
if err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.open.app_error" , nil , err . Error ( ) )
2015-06-14 23:53:32 -08:00
return
}
2015-10-26 14:38:46 -04:00
// Decode image config first to check dimensions before loading the whole thing into memory later on
config , _ , err := image . DecodeConfig ( file )
if err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileFile" , "api.user.upload_profile_user.decode_config.app_error" , nil , err . Error ( ) )
2015-10-26 14:38:46 -04:00
return
} else if config . Width * config . Height > MaxImageSize {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileFile" , "api.user.upload_profile_user.too_large.app_error" , nil , err . Error ( ) )
2015-10-26 14:38:46 -04:00
return
}
file . Seek ( 0 , 0 )
2015-06-14 23:53:32 -08:00
// Decode image into Image object
img , _ , err := image . Decode ( file )
if err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.decode.app_error" , nil , err . Error ( ) )
2015-06-14 23:53:32 -08:00
return
}
// Scale profile image
2015-09-23 15:57:49 -07:00
img = imaging . Resize ( img , utils . Cfg . FileSettings . ProfileWidth , utils . Cfg . FileSettings . ProfileHeight , imaging . Lanczos )
2015-06-14 23:53:32 -08:00
buf := new ( bytes . Buffer )
err = png . Encode ( buf , img )
if err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.encode.app_error" , nil , err . Error ( ) )
2015-06-14 23:53:32 -08:00
return
}
2016-04-21 22:37:01 -07:00
path := "users/" + c . Session . UserId + "/profile.png"
2015-06-14 23:53:32 -08:00
2016-04-11 13:45:03 -04:00
if err := WriteFile ( buf . Bytes ( ) , path ) ; err != nil {
2016-01-27 10:56:15 -05:00
c . Err = model . NewLocAppError ( "uploadProfileImage" , "api.user.upload_profile_user.upload_profile.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
return
}
2016-01-20 13:36:16 -06:00
Srv . Store . User ( ) . UpdateLastPictureUpdate ( c . Session . UserId )
2015-07-10 09:56:41 -07:00
2016-08-24 18:10:49 -03:00
if result := <- Srv . Store . User ( ) . Get ( c . Session . UserId ) ; result . Err != nil {
l4g . Error ( utils . T ( "api.user.get_me.getting.error" ) , c . Session . UserId )
} else {
user := result . Data . ( * model . User )
2016-09-02 12:24:20 -04:00
user = sanitizeProfile ( c , user )
2016-09-27 11:19:50 -03:00
omitUsers := make ( map [ string ] bool , 1 )
omitUsers [ user . Id ] = true
message := model . NewWebSocketEvent ( model . WEBSOCKET_EVENT_USER_UPDATED , "" , "" , "" , omitUsers )
2016-08-24 18:10:49 -03:00
message . Add ( "user" , user )
2016-09-27 11:19:50 -03:00
2016-08-24 18:10:49 -03:00
go Publish ( message )
}
2015-06-14 23:53:32 -08:00
c . LogAudit ( "" )
2015-09-24 16:47:27 -04:00
// write something as the response since jQuery expects a json response
w . Write ( [ ] byte ( "true" ) )
2015-06-14 23:53:32 -08:00
}
func updateUser ( c * Context , w http . ResponseWriter , r * http . Request ) {
user := model . UserFromJson ( r . Body )
if user == nil {
c . SetInvalidParam ( "updateUser" , "user" )
return
}
2016-09-13 12:42:48 -04:00
if ! HasPermissionToUser ( c , user . Id ) {
2015-06-14 23:53:32 -08:00
return
}
2016-07-06 18:54:54 -04:00
if err := utils . IsPasswordValid ( user . Password ) ; user . Password != "" && err != nil {
c . Err = err
return
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Update ( user , false ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
c . LogAudit ( "" )
rusers := result . Data . ( [ 2 ] * model . User )
if rusers [ 0 ] . Email != rusers [ 1 ] . Email {
2016-05-18 17:14:55 +02:00
go sendEmailChangeEmail ( c , rusers [ 1 ] . Email , rusers [ 0 ] . Email , c . GetSiteURL ( ) )
2015-10-05 14:18:05 -07:00
2016-04-21 22:37:01 -07:00
if utils . Cfg . EmailSettings . RequireEmailVerification {
2016-05-18 17:14:55 +02:00
go SendEmailChangeVerifyEmail ( c , rusers [ 0 ] . Id , rusers [ 0 ] . Email , c . GetSiteURL ( ) )
2015-06-14 23:53:32 -08:00
}
}
2016-05-18 13:54:33 +02:00
if rusers [ 0 ] . Username != rusers [ 1 ] . Username {
2016-05-18 17:14:55 +02:00
go sendEmailChangeUsername ( c , rusers [ 1 ] . Username , rusers [ 0 ] . Username , rusers [ 0 ] . Email , c . GetSiteURL ( ) )
2016-05-18 13:54:33 +02:00
}
2016-11-10 06:25:23 -08:00
InvalidateCacheForUser ( user . Id )
2016-08-24 18:10:49 -03:00
updatedUser := rusers [ 0 ]
2016-09-02 12:24:20 -04:00
updatedUser = sanitizeProfile ( c , updatedUser )
2016-08-24 18:10:49 -03:00
2016-09-27 11:19:50 -03:00
omitUsers := make ( map [ string ] bool , 1 )
omitUsers [ user . Id ] = true
message := model . NewWebSocketEvent ( model . WEBSOCKET_EVENT_USER_UPDATED , "" , "" , "" , omitUsers )
2016-08-24 18:10:49 -03:00
message . Add ( "user" , updatedUser )
go Publish ( message )
2015-06-14 23:53:32 -08:00
rusers [ 0 ] . Password = ""
2016-05-11 11:04:30 -07:00
rusers [ 0 ] . AuthData = new ( string )
* rusers [ 0 ] . AuthData = ""
2015-06-14 23:53:32 -08:00
w . Write ( [ ] byte ( rusers [ 0 ] . ToJson ( ) ) )
}
}
func updatePassword ( c * Context , w http . ResponseWriter , r * http . Request ) {
c . LogAudit ( "attempted" )
props := model . MapFromJson ( r . Body )
userId := props [ "user_id" ]
if len ( userId ) != 26 {
c . SetInvalidParam ( "updatePassword" , "user_id" )
return
}
currentPassword := props [ "current_password" ]
if len ( currentPassword ) <= 0 {
c . SetInvalidParam ( "updatePassword" , "current_password" )
return
}
newPassword := props [ "new_password" ]
2016-07-06 18:54:54 -04:00
if err := utils . IsPasswordValid ( newPassword ) ; err != nil {
c . Err = err
2015-06-14 23:53:32 -08:00
return
}
if userId != c . Session . UserId {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "updatePassword" , "api.user.update_password.context.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusForbidden
return
}
var result store . StoreResult
2016-01-20 13:36:16 -06:00
if result = <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
}
if result . Data == nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "updatePassword" , "api.user.update_password.valid_account.app_error" , nil , "" )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusBadRequest
return
}
user := result . Data . ( * model . User )
2016-05-11 11:04:30 -07:00
if user . AuthData != nil && * user . AuthData != "" {
2015-07-22 15:36:58 -04:00
c . LogAudit ( "failed - tried to update user password who was logged in through oauth" )
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "updatePassword" , "api.user.update_password.oauth.app_error" , nil , "auth_service=" + user . AuthService )
2016-04-21 22:37:01 -07:00
c . Err . StatusCode = http . StatusBadRequest
2015-07-22 15:36:58 -04:00
return
}
2016-07-12 10:09:04 -04:00
if err := doubleCheckPassword ( user , currentPassword ) ; err != nil {
if err . Id == "api.user.check_user_password.invalid.app_error" {
c . Err = model . NewLocAppError ( "updatePassword" , "api.user.update_password.incorrect.app_error" , nil , "" )
} else {
c . Err = err
}
2015-07-06 11:20:40 -08:00
c . Err . StatusCode = http . StatusForbidden
2015-06-14 23:53:32 -08:00
return
}
2016-01-20 13:36:16 -06:00
if uresult := <- Srv . Store . User ( ) . UpdatePassword ( c . Session . UserId , model . HashPassword ( newPassword ) ) ; uresult . Err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "updatePassword" , "api.user.update_password.failed.app_error" , nil , uresult . Err . Error ( ) )
2015-06-14 23:53:32 -08:00
return
} else {
c . LogAudit ( "completed" )
2016-05-18 17:14:55 +02:00
go sendPasswordChangeEmail ( c , user . Email , c . GetSiteURL ( ) , c . T ( "api.user.update_password.menu" ) )
2015-06-14 23:53:32 -08:00
data := make ( map [ string ] string )
data [ "user_id" ] = uresult . Data . ( string )
w . Write ( [ ] byte ( model . MapToJson ( data ) ) )
}
}
func updateRoles ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
2016-09-22 08:31:38 -04:00
params := mux . Vars ( r )
2015-06-14 23:53:32 -08:00
2016-09-22 08:31:38 -04:00
userId := params [ "user_id" ]
if len ( userId ) != 26 {
c . SetInvalidParam ( "updateMemberRoles" , "user_id" )
2015-09-04 11:59:10 -07:00
return
}
2016-09-22 08:31:38 -04:00
newRoles := props [ "new_roles" ]
if ! ( model . IsValidUserRoles ( newRoles ) ) {
c . SetInvalidParam ( "updateMemberRoles" , "new_roles" )
2016-05-06 11:28:22 -07:00
return
2016-04-21 22:37:01 -07:00
}
2016-09-22 08:31:38 -04:00
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_ROLES ) {
2015-09-04 11:59:10 -07:00
return
}
2015-06-14 23:53:32 -08:00
var user * model . User
2016-09-22 08:31:38 -04:00
if result := <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
2016-12-06 10:49:34 -05:00
if _ , err := UpdateUserRoles ( user , newRoles ) ; err != nil {
2016-07-06 13:57:32 -04:00
return
2016-12-06 10:49:34 -05:00
} else {
c . LogAuditWithUserId ( user . Id , "roles=" + newRoles )
2016-07-06 13:57:32 -04:00
}
2016-09-22 08:31:38 -04:00
rdata := map [ string ] string { }
rdata [ "status" ] = "ok"
w . Write ( [ ] byte ( model . MapToJson ( rdata ) ) )
2015-06-14 23:53:32 -08:00
}
2016-12-06 10:49:34 -05:00
func UpdateUserRoles ( user * model . User , newRoles string ) ( * model . User , * model . AppError ) {
2015-09-04 11:59:10 -07:00
2016-09-22 08:31:38 -04:00
user . Roles = newRoles
uchan := Srv . Store . User ( ) . Update ( user , true )
schan := Srv . Store . Session ( ) . UpdateRoles ( user . Id , newRoles )
2015-09-04 11:59:10 -07:00
var ruser * model . User
2016-09-22 08:31:38 -04:00
if result := <- uchan ; result . Err != nil {
2016-12-06 10:49:34 -05:00
return nil , result . Err
2015-09-04 11:59:10 -07:00
} else {
ruser = result . Data . ( [ 2 ] * model . User ) [ 0 ]
}
2016-09-22 08:31:38 -04:00
if result := <- schan ; result . Err != nil {
// soft error since the user roles were still updated
l4g . Error ( result . Err )
}
RemoveAllSessionsForUserId ( user . Id )
2016-12-06 10:49:34 -05:00
return ruser , nil
2015-09-04 11:59:10 -07:00
}
2015-06-14 23:53:32 -08:00
func updateActive ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
user_id := props [ "user_id" ]
if len ( user_id ) != 26 {
c . SetInvalidParam ( "updateActive" , "user_id" )
return
}
active := props [ "active" ] == "true"
var user * model . User
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Get ( user_id ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
2016-04-21 22:37:01 -07:00
// true when you're trying to de-activate yourself
isSelfDeactive := ! active && user_id == c . Session . UserId
2015-06-14 23:53:32 -08:00
2016-09-13 12:42:48 -04:00
if ! isSelfDeactive && ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "updateActive" , "api.user.update_active.permissions.app_error" , nil , "userId=" + user_id )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusForbidden
2016-04-21 22:37:01 -07:00
return
2015-06-14 23:53:32 -08:00
}
2016-07-26 12:39:35 -04:00
if user . IsLDAPUser ( ) {
c . Err = model . NewLocAppError ( "updateActive" , "api.user.update_active.no_deactivate_ldap.app_error" , nil , "userId=" + user_id )
c . Err . StatusCode = http . StatusBadRequest
return
}
2015-11-16 17:12:49 -08:00
2016-07-26 12:39:35 -04:00
if ruser , err := UpdateActive ( user , active ) ; err != nil {
c . Err = err
} else {
2016-11-15 10:38:58 -05:00
if ! active {
SetStatusOffline ( ruser . Id , false )
}
2016-07-26 12:39:35 -04:00
c . LogAuditWithUserId ( ruser . Id , fmt . Sprintf ( "active=%v" , active ) )
2015-11-16 17:12:49 -08:00
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
}
2016-07-26 12:39:35 -04:00
func UpdateActive ( user * model . User , active bool ) ( * model . User , * model . AppError ) {
2015-06-14 23:53:32 -08:00
if active {
user . DeleteAt = 0
} else {
user . DeleteAt = model . GetMillis ( )
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Update ( user , true ) ; result . Err != nil {
2016-07-26 12:39:35 -04:00
return nil , result . Err
2015-06-14 23:53:32 -08:00
} else {
if user . DeleteAt > 0 {
2016-07-26 12:39:35 -04:00
RevokeAllSessionsNoContext ( user . Id )
2015-06-14 23:53:32 -08:00
}
2016-02-25 13:46:17 -03:00
if extra := <- Srv . Store . Channel ( ) . ExtraUpdateByUser ( user . Id , model . GetMillis ( ) ) ; extra . Err != nil {
2016-07-26 12:39:35 -04:00
return nil , extra . Err
2016-02-25 13:46:17 -03:00
}
2016-02-25 04:24:03 -03:00
2015-06-14 23:53:32 -08:00
ruser := result . Data . ( [ 2 ] * model . User ) [ 0 ]
2015-12-07 15:13:31 -08:00
options := utils . Cfg . GetSanitizeOptions ( )
2015-06-14 23:53:32 -08:00
options [ "passwordupdate" ] = false
ruser . Sanitize ( options )
2016-07-26 12:39:35 -04:00
return ruser , nil
2015-11-16 17:12:49 -08:00
}
}
2016-12-06 10:49:34 -05:00
func PermanentDeleteUser ( user * model . User ) * model . AppError {
2016-01-22 01:37:11 -03:00
l4g . Warn ( utils . T ( "api.user.permanent_delete_user.attempting.warn" ) , user . Email , user . Id )
2016-09-13 12:42:48 -04:00
if user . IsInRole ( model . ROLE_SYSTEM_ADMIN . Id ) {
2016-01-22 01:37:11 -03:00
l4g . Warn ( utils . T ( "api.user.permanent_delete_user.system_admin.warn" ) , user . Email )
2015-11-16 17:12:49 -08:00
}
2016-07-26 12:39:35 -04:00
if _ , err := UpdateActive ( user , false ) ; err != nil {
return err
}
2015-11-16 17:12:49 -08:00
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Session ( ) . PermanentDeleteSessionsByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . OAuth ( ) . PermanentDeleteAuthDataByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
2015-06-14 23:53:32 -08:00
}
2015-11-16 17:12:49 -08:00
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Webhook ( ) . PermanentDeleteIncomingByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Webhook ( ) . PermanentDeleteOutgoingByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-08 12:41:26 -06:00
if result := <- Srv . Store . Command ( ) . PermanentDeleteByUser ( user . Id ) ; result . Err != nil {
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Preference ( ) . PermanentDeleteByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Channel ( ) . PermanentDeleteMembersByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Post ( ) . PermanentDeleteByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . PermanentDelete ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . Audit ( ) . PermanentDeleteByUser ( user . Id ) ; result . Err != nil {
2015-11-16 17:12:49 -08:00
return result . Err
}
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . Team ( ) . RemoveAllMembersByUser ( user . Id ) ; result . Err != nil {
return result . Err
}
if result := <- Srv . Store . PasswordRecovery ( ) . Delete ( user . Id ) ; result . Err != nil {
return result . Err
}
2016-01-22 01:37:11 -03:00
l4g . Warn ( utils . T ( "api.user.permanent_delete_user.deleted.warn" ) , user . Email , user . Id )
2015-11-16 17:12:49 -08:00
return nil
2015-06-14 23:53:32 -08:00
}
2016-12-06 10:49:34 -05:00
func PermanentDeleteAllUsers ( ) * model . AppError {
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetAll ( ) ; result . Err != nil {
return result . Err
} else {
users := result . Data . ( [ ] * model . User )
for _ , user := range users {
2016-12-06 10:49:34 -05:00
PermanentDeleteUser ( user )
2016-04-21 22:37:01 -07:00
}
}
return nil
}
2015-06-14 23:53:32 -08:00
func sendPasswordReset ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
email := props [ "email" ]
if len ( email ) == 0 {
c . SetInvalidParam ( "sendPasswordReset" , "email" )
return
}
var user * model . User
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetByEmail ( email ) ; result . Err != nil {
2016-11-17 15:07:16 +01:00
w . Write ( [ ] byte ( model . MapToJson ( props ) ) )
2015-06-14 23:53:32 -08:00
return
} else {
user = result . Data . ( * model . User )
}
2016-05-11 11:04:30 -07:00
if user . AuthData != nil && len ( * user . AuthData ) != 0 {
2016-04-21 22:37:01 -07:00
c . Err = model . NewLocAppError ( "sendPasswordReset" , "api.user.send_password_reset.sso.app_error" , nil , "userId=" + user . Id )
2015-10-22 10:53:39 -04:00
return
}
2016-04-21 22:37:01 -07:00
recovery := & model . PasswordRecovery { }
recovery . UserId = user . Id
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . PasswordRecovery ( ) . SaveOrUpdate ( recovery ) ; result . Err != nil {
c . Err = result . Err
return
}
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
link := fmt . Sprintf ( "%s/reset_password_complete?code=%s" , c . GetSiteURL ( ) , url . QueryEscape ( recovery . Code ) )
2015-06-14 23:53:32 -08:00
2016-11-14 09:11:54 -03:00
subject := c . T ( "api.templates.reset_subject" )
2016-01-24 17:10:54 -03:00
2016-02-08 07:26:10 -05:00
bodyPage := utils . NewHTMLTemplate ( "reset_body" , c . Locale )
2015-09-15 18:59:14 -07:00
bodyPage . Props [ "SiteURL" ] = c . GetSiteURL ( )
2016-01-24 17:10:54 -03:00
bodyPage . Props [ "Title" ] = c . T ( "api.templates.reset_body.title" )
bodyPage . Html [ "Info" ] = template . HTML ( c . T ( "api.templates.reset_body.info" ) )
2015-06-14 23:53:32 -08:00
bodyPage . Props [ "ResetUrl" ] = link
2016-01-24 17:10:54 -03:00
bodyPage . Props [ "Button" ] = c . T ( "api.templates.reset_body.button" )
2015-06-14 23:53:32 -08:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( email , subject , bodyPage . Render ( ) ) ; err != nil {
2016-01-22 01:37:11 -03:00
c . Err = model . NewLocAppError ( "sendPasswordReset" , "api.user.send_password_reset.send.app_error" , nil , "err=" + err . Message )
2015-06-14 23:53:32 -08:00
return
}
c . LogAuditWithUserId ( user . Id , "sent=" + email )
w . Write ( [ ] byte ( model . MapToJson ( props ) ) )
}
func resetPassword ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
2016-04-21 22:37:01 -07:00
code := props [ "code" ]
if len ( code ) != model . PASSWORD_RECOVERY_CODE_SIZE {
c . SetInvalidParam ( "resetPassword" , "code" )
2015-06-14 23:53:32 -08:00
return
}
2016-07-06 18:54:54 -04:00
newPassword := props [ "new_password" ]
if err := utils . IsPasswordValid ( newPassword ) ; err != nil {
c . Err = err
return
}
2016-04-21 22:37:01 -07:00
c . LogAudit ( "attempt" )
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
userId := ""
2015-09-23 12:49:28 -07:00
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . PasswordRecovery ( ) . GetByCode ( code ) ; result . Err != nil {
c . LogAuditWithUserId ( userId , "fail - bad code" )
c . Err = model . NewLocAppError ( "resetPassword" , "api.user.reset_password.invalid_link.app_error" , nil , result . Err . Error ( ) )
return
} else {
recovery := result . Data . ( * model . PasswordRecovery )
2015-09-23 12:49:28 -07:00
2016-04-21 22:37:01 -07:00
if model . GetMillis ( ) - recovery . CreateAt < model . PASSWORD_RECOVER_EXPIRY_TIME {
userId = recovery . UserId
} else {
c . LogAuditWithUserId ( userId , "fail - link expired" )
c . Err = model . NewLocAppError ( "resetPassword" , "api.user.reset_password.link_expired.app_error" , nil , "" )
2015-09-23 12:49:28 -07:00
return
}
2016-04-21 22:37:01 -07:00
go func ( ) {
if result := <- Srv . Store . PasswordRecovery ( ) . Delete ( userId ) ; result . Err != nil {
l4g . Error ( "%v" , result . Err )
}
} ( )
2015-06-14 23:53:32 -08:00
}
2016-04-21 22:37:01 -07:00
if err := ResetPassword ( c , userId , newPassword ) ; err != nil {
c . Err = err
2015-06-14 23:53:32 -08:00
return
}
2016-04-21 22:37:01 -07:00
c . LogAuditWithUserId ( userId , "success" )
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
rdata := map [ string ] string { }
rdata [ "status" ] = "ok"
w . Write ( [ ] byte ( model . MapToJson ( rdata ) ) )
}
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
func ResetPassword ( c * Context , userId , newPassword string ) * model . AppError {
2015-06-14 23:53:32 -08:00
var user * model . User
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
2016-04-21 22:37:01 -07:00
return result . Err
2015-06-14 23:53:32 -08:00
} else {
user = result . Data . ( * model . User )
}
2016-09-13 12:42:48 -04:00
if user . AuthData != nil && len ( * user . AuthData ) != 0 && ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
2016-04-21 22:37:01 -07:00
return model . NewLocAppError ( "ResetPassword" , "api.user.reset_password.sso.app_error" , nil , "userId=" + user . Id )
2015-06-14 23:53:32 -08:00
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . UpdatePassword ( userId , model . HashPassword ( newPassword ) ) ; result . Err != nil {
2016-04-21 22:37:01 -07:00
return result . Err
2015-06-14 23:53:32 -08:00
}
2016-05-18 17:14:55 +02:00
go sendPasswordChangeEmail ( c , user . Email , c . GetSiteURL ( ) , c . T ( "api.user.reset_password.method" ) )
2015-06-14 23:53:32 -08:00
2016-04-21 22:37:01 -07:00
return nil
2015-06-14 23:53:32 -08:00
}
2016-05-18 17:14:55 +02:00
func sendPasswordChangeEmail ( c * Context , email , siteURL , method string ) {
2016-11-14 09:11:54 -03:00
subject := c . T ( "api.templates.password_change_subject" ,
2016-05-18 17:14:55 +02:00
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName , "SiteName" : utils . Cfg . TeamSettings . SiteName } )
2016-01-24 17:10:54 -03:00
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "password_change_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
bodyPage . Props [ "Title" ] = c . T ( "api.templates.password_change_body.title" )
bodyPage . Html [ "Info" ] = template . HTML ( c . T ( "api.templates.password_change_body.info" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName , "TeamURL" : siteURL , "Method" : method } ) )
2015-06-14 23:53:32 -08:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( email , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_password_change_email_and_forget.error" ) , err )
}
2015-06-14 23:53:32 -08:00
}
2016-12-12 08:16:10 -05:00
func sendMfaChangeEmail ( c * Context , email string , siteURL string , activated bool ) {
subject := c . T ( "api.templates.mfa_change_subject" ,
map [ string ] interface { } { "SiteName" : utils . Cfg . TeamSettings . SiteName } )
bodyPage := utils . NewHTMLTemplate ( "mfa_change_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
bodyText := ""
if activated {
bodyText = "api.templates.mfa_activated_body.info"
bodyPage . Props [ "Title" ] = c . T ( "api.templates.mfa_activated_body.title" )
} else {
bodyText = "api.templates.mfa_deactivated_body.info"
bodyPage . Props [ "Title" ] = c . T ( "api.templates.mfa_deactivated_body.title" )
}
bodyPage . Html [ "Info" ] = template . HTML ( c . T ( bodyText ,
map [ string ] interface { } { "SiteURL" : siteURL } ) )
if err := utils . SendMail ( email , subject , bodyPage . Render ( ) ) ; err != nil {
l4g . Error ( utils . T ( "api.user.send_mfa_change_email.error" ) , err )
}
}
2016-05-18 17:14:55 +02:00
func sendEmailChangeEmail ( c * Context , oldEmail , newEmail , siteURL string ) {
2016-11-14 09:11:54 -03:00
subject := fmt . Sprintf ( "[%v] %v" , utils . Cfg . TeamSettings . SiteName , c . T ( "api.templates.email_change_subject" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName } ) )
2015-06-14 23:53:32 -08:00
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "email_change_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
bodyPage . Props [ "Title" ] = c . T ( "api.templates.email_change_body.title" )
bodyPage . Html [ "Info" ] = template . HTML ( c . T ( "api.templates.email_change_body.info" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName , "NewEmail" : newEmail } ) )
2015-06-14 23:53:32 -08:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( oldEmail , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_email_change_email_and_forget.error" ) , err )
}
2015-06-14 23:53:32 -08:00
}
2016-05-18 17:14:55 +02:00
func SendEmailChangeVerifyEmail ( c * Context , userId , newUserEmail , siteURL string ) {
2016-11-14 10:48:33 -08:00
link := fmt . Sprintf ( "%s/do_verify_email?uid=%s&hid=%s&email=%s" , siteURL , userId , model . HashPassword ( userId + utils . Cfg . EmailSettings . InviteSalt ) , url . QueryEscape ( newUserEmail ) )
2015-10-05 14:18:05 -07:00
2016-11-14 09:11:54 -03:00
subject := fmt . Sprintf ( "[%v] %v" , utils . Cfg . TeamSettings . SiteName , c . T ( "api.templates.email_change_verify_subject" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName } ) )
2015-10-05 14:18:05 -07:00
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "email_change_verify_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
bodyPage . Props [ "Title" ] = c . T ( "api.templates.email_change_verify_body.title" )
bodyPage . Props [ "Info" ] = c . T ( "api.templates.email_change_verify_body.info" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName } )
bodyPage . Props [ "VerifyUrl" ] = link
bodyPage . Props [ "VerifyButton" ] = c . T ( "api.templates.email_change_verify_body.button" )
2016-01-24 17:10:54 -03:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( newUserEmail , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_email_change_verify_email_and_forget.error" ) , err )
}
2015-10-05 14:18:05 -07:00
}
2016-05-18 17:14:55 +02:00
func sendEmailChangeUsername ( c * Context , oldUsername , newUsername , email , siteURL string ) {
2016-11-14 09:11:54 -03:00
subject := fmt . Sprintf ( "[%v] %v" , utils . Cfg . TeamSettings . SiteName , c . T ( "api.templates.username_change_subject" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName } ) )
2016-05-18 13:54:33 +02:00
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "email_change_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
bodyPage . Props [ "Title" ] = c . T ( "api.templates.username_change_body.title" )
bodyPage . Html [ "Info" ] = template . HTML ( c . T ( "api.templates.username_change_body.info" ,
map [ string ] interface { } { "TeamDisplayName" : utils . Cfg . TeamSettings . SiteName , "NewUsername" : newUsername } ) )
2016-05-18 13:54:33 +02:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( email , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_email_change_username_and_forget.error" ) , err )
}
2016-05-18 13:54:33 +02:00
}
2015-06-14 23:53:32 -08:00
func updateUserNotify ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
user_id := props [ "user_id" ]
if len ( user_id ) != 26 {
c . SetInvalidParam ( "updateUserNotify" , "user_id" )
return
}
2016-01-20 13:36:16 -06:00
uchan := Srv . Store . User ( ) . Get ( user_id )
2015-06-14 23:53:32 -08:00
2016-09-13 12:42:48 -04:00
if ! HasPermissionToUser ( c , user_id ) {
2015-06-14 23:53:32 -08:00
return
}
delete ( props , "user_id" )
email := props [ "email" ]
if len ( email ) == 0 {
c . SetInvalidParam ( "updateUserNotify" , "email" )
return
}
desktop_sound := props [ "desktop_sound" ]
if len ( desktop_sound ) == 0 {
c . SetInvalidParam ( "updateUserNotify" , "desktop_sound" )
return
}
desktop := props [ "desktop" ]
if len ( desktop ) == 0 {
c . SetInvalidParam ( "updateUserNotify" , "desktop" )
return
}
2016-07-19 15:27:23 +03:00
comments := props [ "comments" ]
if len ( comments ) == 0 {
c . SetInvalidParam ( "updateUserNotify" , "comments" )
return
}
2015-06-14 23:53:32 -08:00
var user * model . User
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
user . NotifyProps = props
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . Update ( user , false ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
c . LogAuditWithUserId ( user . Id , "" )
2016-11-10 06:25:23 -08:00
InvalidateCacheForUser ( user . Id )
2015-06-14 23:53:32 -08:00
ruser := result . Data . ( [ 2 ] * model . User ) [ 0 ]
2015-12-07 15:13:31 -08:00
options := utils . Cfg . GetSanitizeOptions ( )
2015-06-14 23:53:32 -08:00
options [ "passwordupdate" ] = false
ruser . Sanitize ( options )
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
}
2016-11-14 13:36:59 +01:00
// Check if the username is already used by another user. Return false if the username is invalid.
2016-04-21 22:37:01 -07:00
func IsUsernameTaken ( name string ) bool {
2015-08-28 08:37:55 -04:00
if ! model . IsValidUsername ( name ) {
return false
}
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetByUsername ( name ) ; result . Err != nil {
2015-08-28 08:37:55 -04:00
return false
} else {
return true
}
return false
}
2015-12-17 12:44:46 -05:00
2016-03-17 15:46:16 -04:00
func emailToOAuth ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-12-17 12:44:46 -05:00
props := model . MapFromJson ( r . Body )
password := props [ "password" ]
if len ( password ) == 0 {
2016-03-17 15:46:16 -04:00
c . SetInvalidParam ( "emailToOAuth" , "password" )
2015-12-17 12:44:46 -05:00
return
}
2016-12-12 08:16:10 -05:00
mfaToken := props [ "token" ]
2015-12-17 12:44:46 -05:00
service := props [ "service" ]
if len ( service ) == 0 {
2016-03-17 15:46:16 -04:00
c . SetInvalidParam ( "emailToOAuth" , "service" )
2015-12-17 12:44:46 -05:00
return
}
email := props [ "email" ]
if len ( email ) == 0 {
2016-03-17 15:46:16 -04:00
c . SetInvalidParam ( "emailToOAuth" , "email" )
2015-12-17 12:44:46 -05:00
return
}
c . LogAudit ( "attempt" )
var user * model . User
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetByEmail ( email ) ; result . Err != nil {
2015-12-17 12:44:46 -05:00
c . LogAudit ( "fail - couldn't get user" )
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
2016-12-12 08:16:10 -05:00
if err := checkPasswordAndAllCriteria ( user , password , mfaToken ) ; err != nil {
2016-04-21 22:37:01 -07:00
c . LogAuditWithUserId ( user . Id , "failed - bad authentication" )
c . Err = err
2015-12-17 12:44:46 -05:00
return
}
stateProps := map [ string ] string { }
stateProps [ "action" ] = model . OAUTH_ACTION_EMAIL_TO_SSO
stateProps [ "email" ] = email
m := map [ string ] string { }
2016-07-05 15:49:00 -04:00
if service == model . USER_AUTH_SERVICE_SAML {
m [ "follow_link" ] = c . GetSiteURL ( ) + "/login/sso/saml?action=" + model . OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + email
2015-12-17 12:44:46 -05:00
} else {
2016-07-05 15:49:00 -04:00
if authUrl , err := GetAuthorizationCode ( c , service , stateProps , "" ) ; err != nil {
c . LogAuditWithUserId ( user . Id , "fail - oauth issue" )
c . Err = err
return
} else {
m [ "follow_link" ] = authUrl
}
2015-12-17 12:44:46 -05:00
}
c . LogAuditWithUserId ( user . Id , "success" )
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
}
2016-03-17 15:46:16 -04:00
func oauthToEmail ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-12-17 12:44:46 -05:00
props := model . MapFromJson ( r . Body )
password := props [ "password" ]
2016-07-06 18:54:54 -04:00
if err := utils . IsPasswordValid ( password ) ; err != nil {
c . Err = err
2015-12-17 12:44:46 -05:00
return
}
email := props [ "email" ]
if len ( email ) == 0 {
2016-03-17 15:46:16 -04:00
c . SetInvalidParam ( "oauthToEmail" , "email" )
2015-12-17 12:44:46 -05:00
return
}
c . LogAudit ( "attempt" )
var user * model . User
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetByEmail ( email ) ; result . Err != nil {
2015-12-17 12:44:46 -05:00
c . LogAudit ( "fail - couldn't get user" )
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
if user . Id != c . Session . UserId {
c . LogAudit ( "fail - user ids didn't match" )
2016-03-17 15:46:16 -04:00
c . Err = model . NewLocAppError ( "oauthToEmail" , "api.user.oauth_to_email.context.app_error" , nil , "" )
2015-12-17 12:44:46 -05:00
c . Err . StatusCode = http . StatusForbidden
return
}
2016-01-20 13:36:16 -06:00
if result := <- Srv . Store . User ( ) . UpdatePassword ( c . Session . UserId , model . HashPassword ( password ) ) ; result . Err != nil {
2015-12-17 12:44:46 -05:00
c . LogAudit ( "fail - database issue" )
c . Err = result . Err
return
}
2016-05-18 17:14:55 +02:00
go sendSignInChangeEmail ( c , user . Email , c . GetSiteURL ( ) , c . T ( "api.templates.signin_change_email.body.method_email" ) )
2015-12-17 12:44:46 -05:00
RevokeAllSession ( c , c . Session . UserId )
2016-06-15 04:10:22 -08:00
c . RemoveSessionCookie ( w , r )
2015-12-17 12:44:46 -05:00
if c . Err != nil {
return
}
m := map [ string ] string { }
2016-04-21 22:37:01 -07:00
m [ "follow_link" ] = "/login?extra=signin_change"
2015-12-17 12:44:46 -05:00
c . LogAudit ( "success" )
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
}
2016-03-17 15:46:16 -04:00
func emailToLdap ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
email := props [ "email" ]
if len ( email ) == 0 {
c . SetInvalidParam ( "emailToLdap" , "email" )
return
}
emailPassword := props [ "email_password" ]
if len ( emailPassword ) == 0 {
c . SetInvalidParam ( "emailToLdap" , "email_password" )
return
}
ldapId := props [ "ldap_id" ]
if len ( ldapId ) == 0 {
c . SetInvalidParam ( "emailToLdap" , "ldap_id" )
return
}
ldapPassword := props [ "ldap_password" ]
if len ( ldapPassword ) == 0 {
c . SetInvalidParam ( "emailToLdap" , "ldap_password" )
return
}
2016-12-12 08:16:10 -05:00
token := props [ "token" ]
2016-03-17 15:46:16 -04:00
c . LogAudit ( "attempt" )
var user * model . User
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetByEmail ( email ) ; result . Err != nil {
2016-03-17 15:46:16 -04:00
c . LogAudit ( "fail - couldn't get user" )
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
2016-12-12 08:16:10 -05:00
if err := checkPasswordAndAllCriteria ( user , emailPassword , token ) ; err != nil {
2016-04-21 22:37:01 -07:00
c . LogAuditWithUserId ( user . Id , "failed - bad authentication" )
c . Err = err
2016-03-17 15:46:16 -04:00
return
}
RevokeAllSession ( c , user . Id )
2016-06-15 04:10:22 -08:00
c . RemoveSessionCookie ( w , r )
2016-03-17 15:46:16 -04:00
if c . Err != nil {
return
}
ldapInterface := einterfaces . GetLdapInterface ( )
if ldapInterface == nil {
c . Err = model . NewLocAppError ( "emailToLdap" , "api.user.email_to_ldap.not_available.app_error" , nil , "" )
c . Err . StatusCode = http . StatusNotImplemented
return
}
2016-04-21 22:37:01 -07:00
if err := ldapInterface . SwitchToLdap ( user . Id , ldapId , ldapPassword ) ; err != nil {
2016-03-17 15:46:16 -04:00
c . LogAuditWithUserId ( user . Id , "fail - ldap switch failed" )
c . Err = err
return
}
2016-09-08 08:48:12 -04:00
go sendSignInChangeEmail ( c , user . Email , c . GetSiteURL ( ) , "AD/LDAP" )
2016-03-17 15:46:16 -04:00
m := map [ string ] string { }
2016-04-21 22:37:01 -07:00
m [ "follow_link" ] = "/login?extra=signin_change"
2016-03-17 15:46:16 -04:00
c . LogAudit ( "success" )
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
}
func ldapToEmail ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
email := props [ "email" ]
if len ( email ) == 0 {
c . SetInvalidParam ( "ldapToEmail" , "email" )
return
}
emailPassword := props [ "email_password" ]
2016-07-06 18:54:54 -04:00
if err := utils . IsPasswordValid ( emailPassword ) ; err != nil {
c . Err = err
2016-03-17 15:46:16 -04:00
return
}
ldapPassword := props [ "ldap_password" ]
if len ( ldapPassword ) == 0 {
c . SetInvalidParam ( "ldapToEmail" , "ldap_password" )
return
}
2016-12-12 08:16:10 -05:00
token := props [ "token" ]
2016-03-17 15:46:16 -04:00
c . LogAudit ( "attempt" )
var user * model . User
2016-04-21 22:37:01 -07:00
if result := <- Srv . Store . User ( ) . GetByEmail ( email ) ; result . Err != nil {
2016-03-17 15:46:16 -04:00
c . LogAudit ( "fail - couldn't get user" )
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
if user . AuthService != model . USER_AUTH_SERVICE_LDAP {
c . Err = model . NewLocAppError ( "ldapToEmail" , "api.user.ldap_to_email.not_ldap_account.app_error" , nil , "" )
return
}
ldapInterface := einterfaces . GetLdapInterface ( )
2016-05-11 11:04:30 -07:00
if ldapInterface == nil || user . AuthData == nil {
2016-03-17 15:46:16 -04:00
c . Err = model . NewLocAppError ( "ldapToEmail" , "api.user.ldap_to_email.not_available.app_error" , nil , "" )
c . Err . StatusCode = http . StatusNotImplemented
return
}
2016-05-11 11:04:30 -07:00
if err := ldapInterface . CheckPassword ( * user . AuthData , ldapPassword ) ; err != nil {
2016-03-17 15:46:16 -04:00
c . LogAuditWithUserId ( user . Id , "fail - ldap authentication failed" )
c . Err = err
return
}
2016-12-12 08:16:10 -05:00
if err := checkUserMfa ( user , token ) ; err != nil {
c . LogAuditWithUserId ( user . Id , "fail - mfa token failed" )
c . Err = err
return
}
2016-03-17 15:46:16 -04:00
if result := <- Srv . Store . User ( ) . UpdatePassword ( user . Id , model . HashPassword ( emailPassword ) ) ; result . Err != nil {
c . LogAudit ( "fail - database issue" )
c . Err = result . Err
return
}
RevokeAllSession ( c , user . Id )
2016-06-15 04:10:22 -08:00
c . RemoveSessionCookie ( w , r )
2016-03-17 15:46:16 -04:00
if c . Err != nil {
return
}
2016-05-18 17:14:55 +02:00
go sendSignInChangeEmail ( c , user . Email , c . GetSiteURL ( ) , c . T ( "api.templates.signin_change_email.body.method_email" ) )
2016-03-17 15:46:16 -04:00
m := map [ string ] string { }
2016-04-21 22:37:01 -07:00
m [ "follow_link" ] = "/login?extra=signin_change"
2016-03-17 15:46:16 -04:00
c . LogAudit ( "success" )
w . Write ( [ ] byte ( model . MapToJson ( m ) ) )
}
2016-05-18 17:14:55 +02:00
func sendSignInChangeEmail ( c * Context , email , siteURL , method string ) {
2016-11-14 09:11:54 -03:00
subject := c . T ( "api.templates.singin_change_email.subject" ,
2016-05-18 17:14:55 +02:00
map [ string ] interface { } { "SiteName" : utils . ClientCfg [ "SiteName" ] } )
2015-12-17 12:44:46 -05:00
2016-05-18 17:14:55 +02:00
bodyPage := utils . NewHTMLTemplate ( "signin_change_body" , c . Locale )
bodyPage . Props [ "SiteURL" ] = siteURL
bodyPage . Props [ "Title" ] = c . T ( "api.templates.signin_change_email.body.title" )
bodyPage . Html [ "Info" ] = template . HTML ( c . T ( "api.templates.singin_change_email.body.info" ,
map [ string ] interface { } { "SiteName" : utils . ClientCfg [ "SiteName" ] , "Method" : method } ) )
2015-12-17 12:44:46 -05:00
2016-11-14 09:11:54 -03:00
if err := utils . SendMail ( email , subject , bodyPage . Render ( ) ) ; err != nil {
2016-05-18 17:14:55 +02:00
l4g . Error ( utils . T ( "api.user.send_sign_in_change_email_and_forget.error" ) , err )
}
2015-12-17 12:44:46 -05:00
}
2016-02-08 07:26:10 -05:00
func verifyEmail ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
userId := props [ "uid" ]
if len ( userId ) != 26 {
c . SetInvalidParam ( "verifyEmail" , "uid" )
return
}
hashedId := props [ "hid" ]
if len ( hashedId ) == 0 {
c . SetInvalidParam ( "verifyEmail" , "hid" )
return
}
2016-11-14 10:48:33 -08:00
if model . ComparePassword ( hashedId , userId + utils . Cfg . EmailSettings . InviteSalt ) {
2016-02-08 07:26:10 -05:00
if c . Err = ( <- Srv . Store . User ( ) . VerifyEmail ( userId ) ) . Err ; c . Err != nil {
return
} else {
c . LogAudit ( "Email Verified" )
return
}
}
c . Err = model . NewLocAppError ( "verifyEmail" , "api.user.verify_email.bad_link.app_error" , nil , "" )
2016-04-21 22:37:01 -07:00
c . Err . StatusCode = http . StatusBadRequest
2016-02-08 07:26:10 -05:00
}
func resendVerification ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
email := props [ "email" ]
if len ( email ) == 0 {
c . SetInvalidParam ( "resendVerification" , "email" )
return
}
2016-07-05 11:20:26 -04:00
if user , error := getUserForLogin ( email , false ) ; error != nil {
c . Err = error
2016-02-08 07:26:10 -05:00
return
} else {
2016-07-18 11:10:03 -04:00
if _ , err := GetStatus ( user . Id ) ; err != nil {
2016-05-18 17:14:55 +02:00
go SendVerifyEmail ( c , user . Id , user . Email , c . GetSiteURL ( ) )
2016-08-12 07:50:35 -04:00
} else {
go SendEmailChangeVerifyEmail ( c , user . Id , user . Email , c . GetSiteURL ( ) )
2016-02-08 07:26:10 -05:00
}
}
}
2016-03-30 12:49:29 -04:00
2016-11-03 10:41:11 -04:00
func generateMfaSecret ( c * Context , w http . ResponseWriter , r * http . Request ) {
2016-03-30 12:49:29 -04:00
uchan := Srv . Store . User ( ) . Get ( c . Session . UserId )
var user * model . User
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
mfaInterface := einterfaces . GetMfaInterface ( )
if mfaInterface == nil {
2016-11-03 10:41:11 -04:00
c . Err = model . NewLocAppError ( "generateMfaSecret" , "api.user.generate_mfa_qr.not_available.app_error" , nil , "" )
2016-03-30 12:49:29 -04:00
c . Err . StatusCode = http . StatusNotImplemented
return
}
2016-11-03 10:41:11 -04:00
secret , img , err := mfaInterface . GenerateSecret ( user )
2016-03-30 12:49:29 -04:00
if err != nil {
c . Err = err
return
}
2016-11-03 10:41:11 -04:00
resp := map [ string ] string { }
resp [ "qr_code" ] = b64 . StdEncoding . EncodeToString ( img )
resp [ "secret" ] = secret
2016-04-26 08:40:47 -04:00
w . Header ( ) . Set ( "Cache-Control" , "no-cache" )
w . Header ( ) . Set ( "Pragma" , "no-cache" )
w . Header ( ) . Set ( "Expires" , "0" )
2016-11-03 10:41:11 -04:00
w . Write ( [ ] byte ( model . MapToJson ( resp ) ) )
2016-03-30 12:49:29 -04:00
}
func updateMfa ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . StringInterfaceFromJson ( r . Body )
activate , ok := props [ "activate" ] . ( bool )
if ! ok {
c . SetInvalidParam ( "updateMfa" , "activate" )
return
}
token := ""
if activate {
token = props [ "token" ] . ( string )
if len ( token ) == 0 {
c . SetInvalidParam ( "updateMfa" , "token" )
return
}
}
2016-12-12 08:16:10 -05:00
c . LogAudit ( "attempt" )
2016-03-30 12:49:29 -04:00
if activate {
2016-04-21 22:37:01 -07:00
if err := ActivateMfa ( c . Session . UserId , token ) ; err != nil {
2016-03-30 12:49:29 -04:00
c . Err = err
return
}
2016-12-12 08:16:10 -05:00
c . LogAudit ( "success - activated" )
2016-03-30 12:49:29 -04:00
} else {
2016-04-21 22:37:01 -07:00
if err := DeactivateMfa ( c . Session . UserId ) ; err != nil {
2016-03-30 12:49:29 -04:00
c . Err = err
return
}
2016-12-12 08:16:10 -05:00
c . LogAudit ( "success - deactivated" )
2016-03-30 12:49:29 -04:00
}
2016-12-12 08:16:10 -05:00
go func ( ) {
var user * model . User
if result := <- Srv . Store . User ( ) . Get ( c . Session . UserId ) ; result . Err != nil {
l4g . Warn ( result . Err )
} else {
user = result . Data . ( * model . User )
}
sendMfaChangeEmail ( c , user . Email , c . GetSiteURL ( ) , activate )
} ( )
2016-03-30 12:49:29 -04:00
rdata := map [ string ] string { }
rdata [ "status" ] = "ok"
w . Write ( [ ] byte ( model . MapToJson ( rdata ) ) )
}
2016-04-21 22:37:01 -07:00
func ActivateMfa ( userId , token string ) * model . AppError {
mfaInterface := einterfaces . GetMfaInterface ( )
if mfaInterface == nil {
err := model . NewLocAppError ( "ActivateMfa" , "api.user.update_mfa.not_available.app_error" , nil , "" )
err . StatusCode = http . StatusNotImplemented
return err
}
var user * model . User
if result := <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
return result . Err
} else {
user = result . Data . ( * model . User )
}
2016-05-27 11:36:53 -04:00
if len ( user . AuthService ) > 0 && user . AuthService != model . USER_AUTH_SERVICE_LDAP {
return model . NewLocAppError ( "ActivateMfa" , "api.user.activate_mfa.email_and_ldap_only.app_error" , nil , "" )
}
2016-04-21 22:37:01 -07:00
if err := mfaInterface . Activate ( user , token ) ; err != nil {
return err
}
return nil
}
func DeactivateMfa ( userId string ) * model . AppError {
mfaInterface := einterfaces . GetMfaInterface ( )
if mfaInterface == nil {
err := model . NewLocAppError ( "DeactivateMfa" , "api.user.update_mfa.not_available.app_error" , nil , "" )
err . StatusCode = http . StatusNotImplemented
return err
}
if err := mfaInterface . Deactivate ( userId ) ; err != nil {
return err
}
return nil
}
2016-03-30 12:49:29 -04:00
func checkMfa ( c * Context , w http . ResponseWriter , r * http . Request ) {
if ! utils . IsLicensed || ! * utils . License . Features . MFA || ! * utils . Cfg . ServiceSettings . EnableMultifactorAuthentication {
rdata := map [ string ] string { }
rdata [ "mfa_required" ] = "false"
w . Write ( [ ] byte ( model . MapToJson ( rdata ) ) )
return
}
props := model . MapFromJson ( r . Body )
loginId := props [ "login_id" ]
if len ( loginId ) == 0 {
c . SetInvalidParam ( "checkMfa" , "login_id" )
return
}
2016-05-03 14:10:36 -04:00
// we don't need to worry about contacting the ldap server to get this user because
// only users already in the system could have MFA enabled
uchan := Srv . Store . User ( ) . GetForLogin (
loginId ,
* utils . Cfg . EmailSettings . EnableSignInWithUsername ,
* utils . Cfg . EmailSettings . EnableSignInWithEmail ,
* utils . Cfg . LdapSettings . Enable ,
)
2016-03-30 12:49:29 -04:00
rdata := map [ string ] string { }
if result := <- uchan ; result . Err != nil {
rdata [ "mfa_required" ] = "false"
} else {
rdata [ "mfa_required" ] = strconv . FormatBool ( result . Data . ( * model . User ) . MfaActive )
}
w . Write ( [ ] byte ( model . MapToJson ( rdata ) ) )
}
2016-07-05 15:49:00 -04:00
func loginWithSaml ( c * Context , w http . ResponseWriter , r * http . Request ) {
samlInterface := einterfaces . GetSamlInterface ( )
if samlInterface == nil {
c . Err = model . NewLocAppError ( "loginWithSaml" , "api.user.saml.not_available.app_error" , nil , "" )
c . Err . StatusCode = http . StatusFound
return
}
teamId , err := getTeamIdFromQuery ( r . URL . Query ( ) )
if err != nil {
c . Err = err
return
}
action := r . URL . Query ( ) . Get ( "action" )
2016-08-03 12:19:27 -05:00
redirectTo := r . URL . Query ( ) . Get ( "redirect_to" )
relayProps := map [ string ] string { }
2016-07-05 15:49:00 -04:00
relayState := ""
if len ( action ) != 0 {
relayProps [ "team_id" ] = teamId
relayProps [ "action" ] = action
if action == model . OAUTH_ACTION_EMAIL_TO_SSO {
relayProps [ "email" ] = r . URL . Query ( ) . Get ( "email" )
}
2016-08-03 12:19:27 -05:00
}
if len ( redirectTo ) != 0 {
relayProps [ "redirect_to" ] = redirectTo
}
if len ( relayProps ) > 0 {
2016-07-05 15:49:00 -04:00
relayState = b64 . StdEncoding . EncodeToString ( [ ] byte ( model . MapToJson ( relayProps ) ) )
}
if data , err := samlInterface . BuildRequest ( relayState ) ; err != nil {
c . Err = err
return
} else {
w . Header ( ) . Set ( "Content-Type" , "application/x-www-form-urlencoded" )
http . Redirect ( w , r , data . URL , http . StatusFound )
}
}
func completeSaml ( c * Context , w http . ResponseWriter , r * http . Request ) {
samlInterface := einterfaces . GetSamlInterface ( )
if samlInterface == nil {
c . Err = model . NewLocAppError ( "completeSaml" , "api.user.saml.not_available.app_error" , nil , "" )
c . Err . StatusCode = http . StatusFound
return
}
//Validate that the user is with SAML and all that
encodedXML := r . FormValue ( "SAMLResponse" )
relayState := r . FormValue ( "RelayState" )
relayProps := make ( map [ string ] string )
if len ( relayState ) > 0 {
stateStr := ""
if b , err := b64 . StdEncoding . DecodeString ( relayState ) ; err != nil {
c . Err = model . NewLocAppError ( "completeSaml" , "api.user.authorize_oauth_user.invalid_state.app_error" , nil , err . Error ( ) )
c . Err . StatusCode = http . StatusFound
return
} else {
stateStr = string ( b )
}
relayProps = model . MapFromJson ( strings . NewReader ( stateStr ) )
}
if user , err := samlInterface . DoLogin ( encodedXML , relayProps ) ; err != nil {
c . Err = err
c . Err . StatusCode = http . StatusFound
return
} else {
if err := checkUserAdditionalAuthenticationCriteria ( user , "" ) ; err != nil {
c . Err = err
c . Err . StatusCode = http . StatusFound
return
}
action := relayProps [ "action" ]
switch action {
case model . OAUTH_ACTION_SIGNUP :
teamId := relayProps [ "team_id" ]
2016-07-13 13:13:13 -04:00
if len ( teamId ) > 0 {
go addDirectChannels ( teamId , user )
}
2016-07-05 15:49:00 -04:00
break
case model . OAUTH_ACTION_EMAIL_TO_SSO :
RevokeAllSession ( c , user . Id )
go sendSignInChangeEmail ( c , user . Email , c . GetSiteURL ( ) , strings . Title ( model . USER_AUTH_SERVICE_SAML ) + " SSO" )
break
}
doLogin ( c , w , r , user , "" )
2016-09-21 04:17:58 -07:00
if c . Err != nil {
return
}
2016-08-03 12:19:27 -05:00
if val , ok := relayProps [ "redirect_to" ] ; ok {
http . Redirect ( w , r , c . GetSiteURL ( ) + val , http . StatusFound )
return
}
2016-07-05 15:49:00 -04:00
http . Redirect ( w , r , GetProtocol ( r ) + "://" + r . Host , http . StatusFound )
}
}
2016-07-12 09:36:27 -04:00
2016-07-18 11:10:03 -04:00
func userTyping ( req * model . WebSocketRequest ) ( map [ string ] interface { } , * model . AppError ) {
2016-07-12 09:36:27 -04:00
var ok bool
var channelId string
if channelId , ok = req . Data [ "channel_id" ] . ( string ) ; ! ok || len ( channelId ) != 26 {
2016-07-18 11:10:03 -04:00
return nil , NewInvalidWebSocketParamError ( req . Action , "channel_id" )
2016-07-12 09:36:27 -04:00
}
var parentId string
if parentId , ok = req . Data [ "parent_id" ] . ( string ) ; ! ok {
parentId = ""
}
2016-09-27 11:19:50 -03:00
omitUsers := make ( map [ string ] bool , 1 )
omitUsers [ req . Session . UserId ] = true
event := model . NewWebSocketEvent ( model . WEBSOCKET_EVENT_TYPING , "" , channelId , "" , omitUsers )
2016-07-12 09:36:27 -04:00
event . Add ( "parent_id" , parentId )
2016-09-27 11:19:50 -03:00
event . Add ( "user_id" , req . Session . UserId )
2016-07-12 09:36:27 -04:00
go Publish ( event )
2016-07-18 11:10:03 -04:00
return nil , nil
2016-07-12 09:36:27 -04:00
}
2016-09-02 12:24:20 -04:00
func sanitizeProfile ( c * Context , user * model . User ) * model . User {
options := utils . Cfg . GetSanitizeOptions ( )
2016-09-13 12:42:48 -04:00
if HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
2016-09-02 12:24:20 -04:00
options [ "email" ] = true
options [ "fullname" ] = true
2016-09-28 09:49:54 -03:00
options [ "authservice" ] = true
2016-09-02 12:24:20 -04:00
}
2016-09-13 12:42:48 -04:00
c . Err = nil
2016-09-02 12:24:20 -04:00
user . SanitizeProfile ( options )
return user
}
2016-10-19 14:49:25 -04:00
func searchUsers ( c * Context , w http . ResponseWriter , r * http . Request ) {
2016-11-02 14:38:34 -04:00
props := model . UserSearchFromJson ( r . Body )
if props == nil {
c . SetInvalidParam ( "searchUsers" , "" )
return
}
2016-10-19 14:49:25 -04:00
2016-11-02 14:38:34 -04:00
if len ( props . Term ) == 0 {
2016-10-19 14:49:25 -04:00
c . SetInvalidParam ( "searchUsers" , "term" )
return
}
2016-11-02 14:38:34 -04:00
if props . InChannelId != "" && ! HasPermissionToChannelContext ( c , props . InChannelId , model . PERMISSION_READ_CHANNEL ) {
2016-10-19 14:49:25 -04:00
return
}
2016-11-02 14:38:34 -04:00
if props . NotInChannelId != "" && ! HasPermissionToChannelContext ( c , props . NotInChannelId , model . PERMISSION_READ_CHANNEL ) {
2016-10-19 14:49:25 -04:00
return
}
2016-11-02 14:38:34 -04:00
searchOptions := map [ string ] bool { }
searchOptions [ store . USER_SEARCH_OPTION_ALLOW_INACTIVE ] = props . AllowInactive
2016-12-02 12:24:22 -05:00
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
hideFullName := ! utils . Cfg . PrivacySettings . ShowFullName
hideEmail := ! utils . Cfg . PrivacySettings . ShowEmailAddress
if hideFullName && hideEmail {
searchOptions [ store . USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME ] = true
} else if hideFullName {
searchOptions [ store . USER_SEARCH_OPTION_ALL_NO_FULL_NAME ] = true
} else if hideEmail {
searchOptions [ store . USER_SEARCH_OPTION_NAMES_ONLY ] = true
}
c . Err = nil
}
2016-10-19 14:49:25 -04:00
var uchan store . StoreChannel
2016-11-02 14:38:34 -04:00
if props . InChannelId != "" {
uchan = Srv . Store . User ( ) . SearchInChannel ( props . InChannelId , props . Term , searchOptions )
} else if props . NotInChannelId != "" {
uchan = Srv . Store . User ( ) . SearchNotInChannel ( props . TeamId , props . NotInChannelId , props . Term , searchOptions )
2016-10-19 14:49:25 -04:00
} else {
2016-11-02 14:38:34 -04:00
uchan = Srv . Store . User ( ) . Search ( props . TeamId , props . Term , searchOptions )
2016-10-19 14:49:25 -04:00
}
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( [ ] * model . User )
for _ , p := range profiles {
sanitizeProfile ( c , p )
}
w . Write ( [ ] byte ( model . UserListToJson ( profiles ) ) )
}
}
func getProfilesByIds ( c * Context , w http . ResponseWriter , r * http . Request ) {
userIds := model . ArrayFromJson ( r . Body )
if len ( userIds ) == 0 {
c . SetInvalidParam ( "getProfilesByIds" , "user_ids" )
return
}
2016-12-13 19:23:36 -08:00
if result := <- Srv . Store . User ( ) . GetProfileByIds ( userIds , true ) ; result . Err != nil {
2016-10-19 14:49:25 -04:00
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
for _ , p := range profiles {
sanitizeProfile ( c , p )
}
w . Write ( [ ] byte ( model . UserMapToJson ( profiles ) ) )
}
}
func autocompleteUsersInChannel ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
channelId := params [ "channel_id" ]
teamId := params [ "team_id" ]
term := r . URL . Query ( ) . Get ( "term" )
if c . Session . GetTeamByTeamId ( teamId ) == nil {
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
return
}
}
if ! HasPermissionToChannelContext ( c , channelId , model . PERMISSION_READ_CHANNEL ) {
return
}
2016-11-03 11:24:45 -04:00
searchOptions := map [ string ] bool { }
2016-12-02 12:24:22 -05:00
hideFullName := ! utils . Cfg . PrivacySettings . ShowFullName
if hideFullName && ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
searchOptions [ store . USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME ] = true
c . Err = nil
} else {
searchOptions [ store . USER_SEARCH_OPTION_NAMES_ONLY ] = true
}
2016-11-03 11:24:45 -04:00
2016-11-24 09:35:09 -05:00
uchan := Srv . Store . User ( ) . SearchInChannel ( channelId , term , searchOptions )
nuchan := Srv . Store . User ( ) . SearchNotInChannel ( teamId , channelId , term , searchOptions )
2016-10-19 14:49:25 -04:00
autocomplete := & model . UserAutocompleteInChannel { }
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( [ ] * model . User )
for _ , p := range profiles {
sanitizeProfile ( c , p )
}
autocomplete . InChannel = profiles
}
if result := <- nuchan ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( [ ] * model . User )
for _ , p := range profiles {
sanitizeProfile ( c , p )
}
autocomplete . OutOfChannel = profiles
}
w . Write ( [ ] byte ( autocomplete . ToJson ( ) ) )
}
func autocompleteUsersInTeam ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
teamId := params [ "team_id" ]
term := r . URL . Query ( ) . Get ( "term" )
if c . Session . GetTeamByTeamId ( teamId ) == nil {
if ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
return
}
}
2016-11-24 09:35:09 -05:00
searchOptions := map [ string ] bool { }
2016-12-02 12:24:22 -05:00
hideFullName := ! utils . Cfg . PrivacySettings . ShowFullName
if hideFullName && ! HasPermissionToContext ( c , model . PERMISSION_MANAGE_SYSTEM ) {
searchOptions [ store . USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME ] = true
c . Err = nil
} else {
searchOptions [ store . USER_SEARCH_OPTION_NAMES_ONLY ] = true
}
2016-11-24 09:35:09 -05:00
uchan := Srv . Store . User ( ) . Search ( teamId , term , searchOptions )
2016-10-19 14:49:25 -04:00
autocomplete := & model . UserAutocompleteInTeam { }
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( [ ] * model . User )
for _ , p := range profiles {
sanitizeProfile ( c , p )
}
autocomplete . InTeam = profiles
}
w . Write ( [ ] byte ( autocomplete . ToJson ( ) ) )
}
2016-11-29 10:12:59 -05:00
func autocompleteUsers ( c * Context , w http . ResponseWriter , r * http . Request ) {
term := r . URL . Query ( ) . Get ( "term" )
uchan := Srv . Store . User ( ) . Search ( "" , term , map [ string ] bool { } )
var profiles [ ] * model . User
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles = result . Data . ( [ ] * model . User )
for _ , p := range profiles {
sanitizeProfile ( c , p )
}
}
w . Write ( [ ] byte ( model . UserListToJson ( profiles ) ) )
}