2015-06-14 23:53:32 -08:00
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"bytes"
l4g "code.google.com/p/log4go"
"fmt"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
"github.com/nfnt/resize"
"hash/fnv"
"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-06-14 23:53:32 -08:00
"net/http"
"net/url"
"strconv"
"strings"
)
func InitUser ( r * mux . Router ) {
l4g . Debug ( "Initializing user api routes" )
sr := r . PathPrefix ( "/users" ) . Subrouter ( )
sr . Handle ( "/create" , ApiAppHandler ( createUser ) ) . Methods ( "POST" )
sr . Handle ( "/update" , ApiUserRequired ( updateUser ) ) . Methods ( "POST" )
sr . Handle ( "/update_roles" , ApiUserRequired ( updateRoles ) ) . Methods ( "POST" )
sr . Handle ( "/update_active" , ApiUserRequired ( updateActive ) ) . Methods ( "POST" )
sr . Handle ( "/update_notify" , ApiUserRequired ( updateUserNotify ) ) . Methods ( "POST" )
sr . Handle ( "/newpassword" , ApiUserRequired ( updatePassword ) ) . Methods ( "POST" )
sr . Handle ( "/send_password_reset" , ApiAppHandler ( sendPasswordReset ) ) . Methods ( "POST" )
sr . Handle ( "/reset_password" , ApiAppHandler ( resetPassword ) ) . Methods ( "POST" )
sr . Handle ( "/login" , ApiAppHandler ( login ) ) . Methods ( "POST" )
sr . Handle ( "/logout" , ApiUserRequired ( logout ) ) . Methods ( "POST" )
sr . Handle ( "/revoke_session" , ApiUserRequired ( revokeSession ) ) . Methods ( "POST" )
sr . Handle ( "/newimage" , ApiUserRequired ( uploadProfileImage ) ) . Methods ( "POST" )
sr . Handle ( "/me" , ApiAppHandler ( getMe ) ) . Methods ( "GET" )
sr . Handle ( "/status" , ApiUserRequiredActivity ( getStatuses , false ) ) . Methods ( "GET" )
sr . Handle ( "/profiles" , ApiUserRequired ( getProfiles ) ) . Methods ( "GET" )
sr . Handle ( "/{id:[A-Za-z0-9]+}" , ApiUserRequired ( getUser ) ) . Methods ( "GET" )
sr . Handle ( "/{id:[A-Za-z0-9]+}/sessions" , ApiUserRequired ( getSessions ) ) . Methods ( "GET" )
sr . Handle ( "/{id:[A-Za-z0-9]+}/audits" , ApiUserRequired ( getAudits ) ) . Methods ( "GET" )
sr . Handle ( "/{id:[A-Za-z0-9]+}/image" , ApiUserRequired ( getProfileImage ) ) . Methods ( "GET" )
}
func createUser ( c * Context , w http . ResponseWriter , r * http . Request ) {
user := model . UserFromJson ( r . Body )
if user == nil {
c . SetInvalidParam ( "createUser" , "user" )
return
}
if ! model . IsUsernameValid ( user . Username ) {
c . Err = model . NewAppError ( "createUser" , "That username is invalid" , "might be using a resrved username" )
return
}
user . EmailVerified = false
var team * model . Team
if result := <- Srv . Store . Team ( ) . Get ( user . TeamId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
team = result . Data . ( * model . Team )
}
hash := r . URL . Query ( ) . Get ( "h" )
2015-07-22 12:13:45 -04:00
if IsVerifyHashRequired ( user , team , hash ) {
2015-06-14 23:53:32 -08:00
data := r . URL . Query ( ) . Get ( "d" )
props := model . MapFromJson ( strings . NewReader ( data ) )
if ! model . ComparePassword ( hash , fmt . Sprintf ( "%v:%v" , data , utils . Cfg . ServiceSettings . InviteSalt ) ) {
c . Err = model . NewAppError ( "createUser" , "The signup link does not appear to be valid" , "" )
return
}
t , err := strconv . ParseInt ( props [ "time" ] , 10 , 64 )
if err != nil || model . GetMillis ( ) - t > 1000 * 60 * 60 * 48 { // 48 hours
c . Err = model . NewAppError ( "createUser" , "The signup link has expired" , "" )
return
}
if user . TeamId != props [ "id" ] {
c . Err = model . NewAppError ( "createUser" , "Invalid team name" , data )
return
}
user . Email = props [ "email" ]
user . EmailVerified = true
}
2015-07-15 12:48:50 -04:00
if len ( user . AuthData ) > 0 && len ( user . AuthService ) > 0 {
user . EmailVerified = true
}
2015-06-14 23:53:32 -08:00
ruser := CreateUser ( c , team , user )
if c . Err != nil {
return
}
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
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 {
domains := strings . Fields ( strings . TrimSpace ( strings . ToLower ( strings . Replace ( strings . Replace ( team . AllowedDomains , "@" , " " , - 1 ) , "," , " " , - 1 ) ) ) )
matched := false
for _ , d := range domains {
if strings . HasSuffix ( user . Email , "@" + d ) {
matched = true
break
}
}
if matched {
shouldVerifyHash = false
} else {
return true
}
}
if team . Type == model . TEAM_OPEN {
shouldVerifyHash = false
}
if len ( hash ) > 0 {
shouldVerifyHash = true
}
return shouldVerifyHash
}
2015-06-14 23:53:32 -08:00
func CreateValet ( c * Context , team * model . Team ) * model . User {
valet := & model . User { }
valet . TeamId = team . Id
valet . Email = utils . Cfg . EmailSettings . FeedbackEmail
valet . EmailVerified = true
valet . Username = model . BOT_USERNAME
valet . Password = model . NewId ( )
return CreateUser ( c , team , valet )
}
func CreateUser ( c * Context , team * model . Team , user * model . User ) * model . User {
channelRole := ""
if team . Email == user . Email {
user . Roles = model . ROLE_ADMIN
channelRole = model . CHANNEL_ROLE_ADMIN
} else {
user . Roles = ""
}
user . MakeNonNil ( )
if len ( user . Props [ "theme" ] ) == 0 {
user . AddProp ( "theme" , utils . Cfg . TeamSettings . DefaultThemeColor )
}
if result := <- Srv . Store . User ( ) . Save ( user ) ; result . Err != nil {
c . Err = result . Err
return nil
} else {
ruser := result . Data . ( * model . User )
2015-06-29 10:24:45 -04:00
// Soft error if there is an issue joining the default channels
if err := JoinDefaultChannels ( c , ruser , channelRole ) ; err != nil {
l4g . Error ( "Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v" , ruser . Id , ruser . TeamId , err )
2015-06-14 23:53:32 -08:00
}
2015-07-08 11:50:10 -04:00
//fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square")
2015-06-14 23:53:32 -08:00
if user . EmailVerified {
if cresult := <- Srv . Store . User ( ) . VerifyEmail ( ruser . Id ) ; cresult . Err != nil {
2015-06-29 10:24:45 -04:00
l4g . Error ( "Failed to set email verified err=%v" , cresult . Err )
2015-06-14 23:53:32 -08:00
}
} else {
2015-07-08 11:50:10 -04:00
FireAndForgetVerifyEmail ( result . Data . ( * model . User ) . Id , ruser . FirstName , ruser . Email , team . DisplayName , c . GetTeamURLFromTeam ( team ) )
2015-06-14 23:53:32 -08:00
}
ruser . Sanitize ( map [ string ] bool { } )
2015-06-29 10:24:45 -04:00
// This message goes to every channel, so the channelId is irrelevant
2015-06-14 23:53:32 -08:00
message := model . NewMessage ( team . Id , "" , ruser . Id , model . ACTION_NEW_USER )
2015-07-14 15:12:04 -08:00
PublishAndForget ( message )
2015-06-14 23:53:32 -08:00
return ruser
}
}
2015-07-08 11:50:10 -04:00
func fireAndForgetWelcomeEmail ( name , email , teamDisplayName , link string ) {
2015-06-14 23:53:32 -08:00
go func ( ) {
subjectPage := NewServerTemplatePage ( "welcome_subject" , link )
bodyPage := NewServerTemplatePage ( "welcome_body" , link )
2015-07-09 13:59:19 -04:00
bodyPage . Props [ "Nickname" ] = name
2015-07-08 11:50:10 -04:00
bodyPage . Props [ "TeamDisplayName" ] = teamDisplayName
2015-06-14 23:53:32 -08:00
bodyPage . Props [ "FeedbackName" ] = utils . Cfg . EmailSettings . FeedbackName
if err := utils . SendMail ( email , subjectPage . Render ( ) , bodyPage . Render ( ) ) ; err != nil {
l4g . Error ( "Failed to send welcome email successfully err=%v" , err )
}
} ( )
}
2015-07-08 11:50:10 -04:00
func FireAndForgetVerifyEmail ( userId , name , email , teamDisplayName , teamURL string ) {
2015-06-14 23:53:32 -08:00
go func ( ) {
2015-07-08 11:50:10 -04:00
link := fmt . Sprintf ( "%s/verify_email?uid=%s&hid=%s" , teamURL , userId , model . HashPassword ( userId ) )
2015-06-14 23:53:32 -08:00
2015-07-08 11:50:10 -04:00
subjectPage := NewServerTemplatePage ( "verify_subject" , teamURL )
subjectPage . Props [ "TeamDisplayName" ] = teamDisplayName
bodyPage := NewServerTemplatePage ( "verify_body" , teamURL )
2015-07-09 13:59:19 -04:00
bodyPage . Props [ "Nickname" ] = name
2015-07-08 11:50:10 -04:00
bodyPage . Props [ "TeamDisplayName" ] = teamDisplayName
2015-06-14 23:53:32 -08:00
bodyPage . Props [ "VerifyUrl" ] = link
if err := utils . SendMail ( email , subjectPage . Render ( ) , bodyPage . Render ( ) ) ; err != nil {
l4g . Error ( "Failed to send verification email successfully err=%v" , err )
}
} ( )
}
2015-07-22 12:42:03 -04:00
func LoginById ( c * Context , w http . ResponseWriter , r * http . Request , userId , password , deviceId string ) * model . User {
2015-07-15 12:48:50 -04:00
if result := <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
c . Err = result . Err
2015-07-22 12:42:03 -04:00
return nil
2015-07-15 12:48:50 -04:00
} else {
user := result . Data . ( * model . User )
if checkUserPassword ( c , user , password ) {
Login ( c , w , r , user , deviceId )
2015-07-22 12:42:03 -04:00
return user
2015-06-14 23:53:32 -08:00
}
}
2015-07-22 12:42:03 -04:00
return nil
2015-07-15 12:48:50 -04:00
}
2015-06-14 23:53:32 -08:00
2015-07-22 12:42:03 -04:00
func LoginByEmail ( c * Context , w http . ResponseWriter , r * http . Request , email , name , password , deviceId string ) * model . User {
2015-06-14 23:53:32 -08:00
var team * model . Team
2015-07-15 12:48:50 -04:00
if result := <- Srv . Store . Team ( ) . GetByName ( name ) ; result . Err != nil {
c . Err = result . Err
2015-07-22 12:42:03 -04:00
return nil
2015-07-15 12:48:50 -04:00
} else {
team = result . Data . ( * model . Team )
2015-06-14 23:53:32 -08:00
}
2015-07-15 12:48:50 -04:00
if result := <- Srv . Store . User ( ) . GetByEmail ( team . Id , email ) ; result . Err != nil {
c . Err = result . Err
2015-07-22 12:42:03 -04:00
return nil
2015-07-15 12:48:50 -04:00
} else {
user := result . Data . ( * model . User )
2015-06-14 23:53:32 -08:00
2015-07-15 12:48:50 -04:00
if checkUserPassword ( c , user , password ) {
Login ( c , w , r , user , deviceId )
2015-07-22 12:42:03 -04:00
return user
2015-06-14 23:53:32 -08:00
}
}
2015-07-22 12:42:03 -04:00
return nil
2015-07-15 12:48:50 -04:00
}
2015-06-14 23:53:32 -08:00
2015-07-15 12:48:50 -04:00
func checkUserPassword ( c * Context , user * model . User , password string ) bool {
if ! model . ComparePassword ( user . Password , password ) {
2015-06-14 23:53:32 -08:00
c . LogAuditWithUserId ( user . Id , "fail" )
2015-07-15 12:48:50 -04:00
c . Err = model . NewAppError ( "checkUserPassword" , "Login failed because of invalid password" , "user_id=" + user . Id )
2015-07-06 11:20:40 -08:00
c . Err . StatusCode = http . StatusForbidden
2015-07-15 12:48:50 -04:00
return false
2015-06-14 23:53:32 -08:00
}
2015-07-15 12:48:50 -04:00
return true
}
// User MUST be validated before calling Login
func Login ( c * Context , w http . ResponseWriter , r * http . Request , user * model . User , deviceId string ) {
c . LogAuditWithUserId ( user . Id , "attempt" )
2015-06-14 23:53:32 -08:00
2015-07-12 23:36:52 -08:00
if ! user . EmailVerified && ! utils . Cfg . EmailSettings . ByPassEmail {
2015-07-15 12:48:50 -04:00
c . Err = model . NewAppError ( "Login" , "Login failed because email address has not been verified" , "user_id=" + user . Id )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusForbidden
return
}
if user . DeleteAt > 0 {
2015-07-15 12:48:50 -04:00
c . Err = model . NewAppError ( "Login" , "Login failed because your account has been set to inactive. Please contact an administrator." , "user_id=" + user . Id )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusForbidden
return
}
2015-07-15 12:48:50 -04:00
session := & model . Session { UserId : user . Id , TeamId : user . TeamId , Roles : user . Roles , DeviceId : deviceId }
2015-06-14 23:53:32 -08:00
maxAge := model . SESSION_TIME_WEB_IN_SECS
2015-07-15 12:48:50 -04:00
if len ( deviceId ) > 0 {
2015-06-14 23:53:32 -08:00
session . SetExpireInDays ( model . SESSION_TIME_MOBILE_IN_DAYS )
maxAge = model . SESSION_TIME_MOBILE_IN_SECS
} else {
session . SetExpireInDays ( model . SESSION_TIME_WEB_IN_DAYS )
}
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 ) )
if result := <- Srv . Store . Session ( ) . Save ( session ) ; result . Err != nil {
c . Err = result . Err
c . Err . StatusCode = http . StatusForbidden
return
} else {
session = result . Data . ( * model . Session )
sessionCache . Add ( session . Id , session )
}
w . Header ( ) . Set ( model . HEADER_TOKEN , session . Id )
sessionCookie := & http . Cookie {
Name : model . SESSION_TOKEN ,
Value : session . Id ,
Path : "/" ,
MaxAge : maxAge ,
HttpOnly : true ,
}
http . SetCookie ( w , sessionCookie )
c . Session = * session
c . LogAuditWithUserId ( user . Id , "success" )
2015-07-15 12:48:50 -04:00
}
2015-06-14 23:53:32 -08:00
2015-07-15 12:48:50 -04:00
func login ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
2015-07-23 08:19:51 -04:00
if len ( props [ "password" ] ) == 0 {
c . Err = model . NewAppError ( "login" , "Password field must not be blank" , "" )
c . Err . StatusCode = http . StatusForbidden
return
}
2015-07-22 12:42:03 -04:00
var user * model . User
2015-07-15 12:48:50 -04:00
if len ( props [ "id" ] ) != 0 {
2015-07-22 12:42:03 -04:00
user = LoginById ( c , w , r , props [ "id" ] , props [ "password" ] , props [ "device_id" ] )
2015-07-15 12:48:50 -04:00
} else if len ( props [ "email" ] ) != 0 && len ( props [ "name" ] ) != 0 {
2015-07-22 12:42:03 -04:00
user = LoginByEmail ( c , w , r , props [ "email" ] , props [ "name" ] , props [ "password" ] , props [ "device_id" ] )
} else {
c . Err = model . NewAppError ( "login" , "Either user id or team name and user email must be provided" , "" )
2015-07-22 15:05:20 -04:00
c . Err . StatusCode = http . StatusForbidden
2015-07-22 12:42:03 -04:00
return
2015-07-15 12:48:50 -04:00
}
if c . Err != nil {
return
}
2015-07-22 12:42:03 -04:00
if user != nil {
user . Sanitize ( map [ string ] bool { } )
} else {
user = & model . User { }
}
w . Write ( [ ] byte ( user . ToJson ( ) ) )
2015-06-14 23:53:32 -08:00
}
func revokeSession ( c * Context , w http . ResponseWriter , r * http . Request ) {
props := model . MapFromJson ( r . Body )
altId := props [ "id" ]
if result := <- Srv . Store . Session ( ) . GetSessions ( c . Session . UserId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
if session . AltId == altId {
c . LogAudit ( "session_id=" + session . AltId )
sessionCache . Remove ( session . Id )
if result := <- Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
c . Err = result . Err
return
} else {
w . Write ( [ ] byte ( model . MapToJson ( props ) ) )
return
}
}
}
}
}
func RevokeAllSession ( c * Context , userId string ) {
if result := <- Srv . Store . Session ( ) . GetSessions ( userId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
sessions := result . Data . ( [ ] * model . Session )
for _ , session := range sessions {
c . LogAuditWithUserId ( userId , "session_id=" + session . AltId )
sessionCache . Remove ( session . Id )
if result := <- Srv . Store . Session ( ) . Remove ( session . Id ) ; result . Err != nil {
c . Err = result . Err
return
}
}
}
}
func getSessions ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
id := params [ "id" ]
2015-07-06 11:20:40 -08:00
if ! c . HasPermissionsToUser ( id , "getSessions" ) {
2015-06-14 23:53:32 -08:00
return
}
if result := <- Srv . Store . Session ( ) . GetSessions ( id ) ; result . Err != nil {
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 ( "" )
c . RemoveSessionCookie ( w )
if result := <- Srv . Store . Session ( ) . Remove ( c . Session . Id ) ; result . Err != nil {
c . Err = result . Err
return
}
}
func getMe ( c * Context , w http . ResponseWriter , r * http . Request ) {
if len ( c . Session . UserId ) == 0 {
return
}
if result := <- Srv . Store . User ( ) . Get ( c . Session . UserId ) ; result . Err != nil {
c . Err = result . Err
c . RemoveSessionCookie ( w )
l4g . Error ( "Error in getting users profile for id=%v forcing logout" , c . Session . UserId )
return
} else if HandleEtag ( result . Data . ( * model . User ) . Etag ( ) , w , r ) {
return
} else {
result . Data . ( * model . User ) . Sanitize ( map [ string ] bool { } )
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , result . Data . ( * model . User ) . Etag ( ) )
w . Header ( ) . Set ( "Expires" , "-1" )
w . Write ( [ ] byte ( result . Data . ( * model . User ) . ToJson ( ) ) )
return
}
}
func getUser ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
id := params [ "id" ]
if ! c . HasPermissionsToUser ( id , "getUser" ) {
return
}
if result := <- Srv . Store . User ( ) . Get ( id ) ; result . Err != nil {
c . Err = result . Err
return
} else if HandleEtag ( result . Data . ( * model . User ) . Etag ( ) , w , r ) {
return
} else {
result . Data . ( * model . User ) . Sanitize ( map [ string ] bool { } )
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , result . Data . ( * model . User ) . Etag ( ) )
w . Write ( [ ] byte ( result . Data . ( * model . User ) . ToJson ( ) ) )
return
}
}
func getProfiles ( c * Context , w http . ResponseWriter , r * http . Request ) {
etag := ( <- Srv . Store . User ( ) . GetEtagForProfiles ( c . Session . TeamId ) ) . Data . ( string )
if HandleEtag ( etag , w , r ) {
return
}
if result := <- Srv . Store . User ( ) . GetProfiles ( c . Session . TeamId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
for k , p := range profiles {
options := utils . SanitizeOptions
options [ "passwordupdate" ] = false
p . Sanitize ( options )
profiles [ k ] = p
}
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , etag )
w . Write ( [ ] byte ( model . UserMapToJson ( profiles ) ) )
return
}
}
func getAudits ( c * Context , w http . ResponseWriter , r * http . Request ) {
params := mux . Vars ( r )
id := params [ "id" ]
if ! c . HasPermissionsToUser ( id , "getAudits" ) {
return
}
userChan := Srv . Store . User ( ) . Get ( id )
auditChan := Srv . Store . Audit ( ) . Get ( id , 20 )
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 ( )
if HandleEtag ( etag , w , r ) {
return
}
if len ( etag ) > 0 {
w . Header ( ) . Set ( model . HEADER_ETAG_SERVER , etag )
}
w . Write ( [ ] byte ( audits . ToJson ( ) ) )
return
}
}
2015-06-19 11:19:51 -04: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-06 18:08:52 -04:00
color := colors [ int64 ( seed ) % int64 ( len ( colors ) ) ]
2015-06-22 15:17:47 -04:00
img := image . NewRGBA ( image . Rect ( 0 , 0 , int ( utils . Cfg . ImageSettings . ProfileWidth ) , int ( utils . Cfg . ImageSettings . ProfileHeight ) ) )
2015-06-22 15:11:20 -04:00
draw . Draw ( img , img . Bounds ( ) , & image . Uniform { color } , image . ZP , draw . Src )
2015-06-14 23:53:32 -08:00
2015-06-19 11:19:51 -04:00
buf := new ( bytes . Buffer )
2015-06-22 15:11:20 -04:00
if imgErr := png . Encode ( buf , img ) ; imgErr != nil {
2015-07-16 08:54:09 -04:00
return nil , model . NewAppError ( "createProfileImage" , "Could not encode default profile image" , 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 )
id := params [ "id" ]
if result := <- Srv . Store . User ( ) . Get ( id ) ; result . Err != nil {
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-07-16 08:54:09 -04:00
if ! utils . IsS3Configured ( ) && ! utils . Cfg . ServiceSettings . UseLocalStorage {
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 {
path := "teams/" + c . Session . TeamId + "/users/" + id + "/profile.png"
2015-06-14 23:53:32 -08:00
2015-07-16 08:54:09 -04:00
if data , err := readFile ( path ) ; err != nil {
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
2015-07-16 08:54:09 -04:00
if err := writeFile ( img , path ) ; err != nil {
c . Err = err
2015-06-19 11:19:51 -04:00
return
}
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
}
}
if c . Session . UserId == id {
w . Header ( ) . Set ( "Cache-Control" , "max-age=300, public" ) // 5 mins
} else {
w . Header ( ) . Set ( "Cache-Control" , "max-age=86400, public" ) // 24 hrs
}
w . Write ( img )
}
}
func uploadProfileImage ( c * Context , w http . ResponseWriter , r * http . Request ) {
2015-07-16 08:54:09 -04:00
if ! utils . IsS3Configured ( ) && ! utils . Cfg . ServiceSettings . UseLocalStorage {
c . Err = model . NewAppError ( "uploadProfileImage" , "Unable to upload file. Amazon S3 not configured and local server storage turned off. " , "" )
2015-06-14 23:53:32 -08:00
c . Err . StatusCode = http . StatusNotImplemented
return
}
if err := r . ParseMultipartForm ( 10000000 ) ; err != nil {
c . Err = model . NewAppError ( "uploadProfileImage" , "Could not parse multipart form" , "" )
return
}
m := r . MultipartForm
imageArray , ok := m . File [ "image" ]
if ! ok {
c . Err = model . NewAppError ( "uploadProfileImage" , "No file under 'image' in request" , "" )
c . Err . StatusCode = http . StatusBadRequest
return
}
if len ( imageArray ) <= 0 {
c . Err = model . NewAppError ( "uploadProfileImage" , "Empty array under 'image' in request" , "" )
c . Err . StatusCode = http . StatusBadRequest
return
}
imageData := imageArray [ 0 ]
file , err := imageData . Open ( )
defer file . Close ( )
if err != nil {
c . Err = model . NewAppError ( "uploadProfileImage" , "Could not open image file" , err . Error ( ) )
return
}
// Decode image into Image object
img , _ , err := image . Decode ( file )
if err != nil {
c . Err = model . NewAppError ( "uploadProfileImage" , "Could not decode profile image" , err . Error ( ) )
return
}
// Scale profile image
img = resize . Resize ( utils . Cfg . ImageSettings . ProfileWidth , utils . Cfg . ImageSettings . ProfileHeight , img , resize . Lanczos3 )
buf := new ( bytes . Buffer )
err = png . Encode ( buf , img )
if err != nil {
c . Err = model . NewAppError ( "uploadProfileImage" , "Could not encode profile image" , err . Error ( ) )
return
}
path := "teams/" + c . Session . TeamId + "/users/" + c . Session . UserId + "/profile.png"
2015-07-16 08:54:09 -04:00
if err := writeFile ( buf . Bytes ( ) , path ) ; err != nil {
2015-06-14 23:53:32 -08:00
c . Err = model . NewAppError ( "uploadProfileImage" , "Couldn't upload profile image" , "" )
return
}
2015-07-16 08:55:37 -07:00
Srv . Store . User ( ) . UpdateLastPictureUpdate ( c . Session . UserId )
2015-07-10 09:56:41 -07:00
2015-06-14 23:53:32 -08:00
c . LogAudit ( "" )
}
func updateUser ( c * Context , w http . ResponseWriter , r * http . Request ) {
user := model . UserFromJson ( r . Body )
if user == nil {
c . SetInvalidParam ( "updateUser" , "user" )
return
}
2015-07-06 11:20:40 -08:00
if ! c . HasPermissionsToUser ( user . Id , "updateUser" ) {
2015-06-14 23:53:32 -08:00
return
}
if result := <- Srv . Store . User ( ) . Update ( user , false ) ; result . Err != nil {
c . Err = result . Err
return
} else {
c . LogAudit ( "" )
rusers := result . Data . ( [ 2 ] * model . User )
if rusers [ 0 ] . Email != rusers [ 1 ] . Email {
if tresult := <- Srv . Store . Team ( ) . Get ( rusers [ 1 ] . TeamId ) ; tresult . Err != nil {
l4g . Error ( tresult . Err . Message )
} else {
2015-07-08 11:50:10 -04:00
team := tresult . Data . ( * model . Team )
fireAndForgetEmailChangeEmail ( rusers [ 1 ] . Email , team . DisplayName , c . GetTeamURLFromTeam ( team ) )
2015-06-14 23:53:32 -08:00
}
}
rusers [ 0 ] . Password = ""
rusers [ 0 ] . AuthData = ""
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" ]
if len ( newPassword ) < 5 {
c . SetInvalidParam ( "updatePassword" , "new_password" )
return
}
if userId != c . Session . UserId {
c . Err = model . NewAppError ( "updatePassword" , "Update password failed because context user_id did not match props user_id" , "" )
c . Err . StatusCode = http . StatusForbidden
return
}
var result store . StoreResult
if result = <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
c . Err = result . Err
return
}
if result . Data == nil {
c . Err = model . NewAppError ( "updatePassword" , "Update password failed because we couldn't find a valid account" , "" )
c . Err . StatusCode = http . StatusBadRequest
return
}
user := result . Data . ( * model . User )
tchan := Srv . Store . Team ( ) . Get ( user . TeamId )
2015-07-22 15:36:58 -04:00
if user . AuthData != "" {
c . LogAudit ( "failed - tried to update user password who was logged in through oauth" )
c . Err = model . NewAppError ( "updatePassword" , "Update password failed because the user is logged in through an OAuth service" , "auth_service=" + user . AuthService )
c . Err . StatusCode = http . StatusForbidden
return
}
2015-06-14 23:53:32 -08:00
if ! model . ComparePassword ( user . Password , currentPassword ) {
c . Err = model . NewAppError ( "updatePassword" , "Update password failed because of invalid password" , "" )
2015-07-06 11:20:40 -08:00
c . Err . StatusCode = http . StatusForbidden
2015-06-14 23:53:32 -08:00
return
}
if uresult := <- Srv . Store . User ( ) . UpdatePassword ( c . Session . UserId , model . HashPassword ( newPassword ) ) ; uresult . Err != nil {
2015-07-06 11:20:40 -08:00
c . Err = model . NewAppError ( "updatePassword" , "Update password failed" , uresult . Err . Error ( ) )
c . Err . StatusCode = http . StatusForbidden
2015-06-14 23:53:32 -08:00
return
} else {
c . LogAudit ( "completed" )
if tresult := <- tchan ; tresult . Err != nil {
l4g . Error ( tresult . Err . Message )
} else {
2015-07-08 11:50:10 -04:00
team := tresult . Data . ( * model . Team )
fireAndForgetPasswordChangeEmail ( user . Email , team . DisplayName , c . GetTeamURLFromTeam ( team ) , "using the settings 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 )
user_id := props [ "user_id" ]
if len ( user_id ) != 26 {
c . SetInvalidParam ( "updateRoles" , "user_id" )
return
}
new_roles := props [ "new_roles" ]
// no check since we allow the clearing of Roles
var user * model . User
if result := <- Srv . Store . User ( ) . Get ( user_id ) ; result . Err != nil {
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
if ! c . HasPermissionsToTeam ( user . TeamId , "updateRoles" ) {
return
}
if ! strings . Contains ( c . Session . Roles , model . ROLE_ADMIN ) && ! c . IsSystemAdmin ( ) {
c . Err = model . NewAppError ( "updateRoles" , "You do not have the appropriate permissions" , "userId=" + user_id )
c . Err . StatusCode = http . StatusForbidden
return
}
// make sure there is at least 1 other active admin
if strings . Contains ( user . Roles , model . ROLE_ADMIN ) && ! strings . Contains ( new_roles , model . ROLE_ADMIN ) {
if result := <- Srv . Store . User ( ) . GetProfiles ( user . TeamId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
activeAdmins := - 1
profileUsers := result . Data . ( map [ string ] * model . User )
for _ , profileUser := range profileUsers {
if profileUser . DeleteAt == 0 && strings . Contains ( profileUser . Roles , model . ROLE_ADMIN ) {
activeAdmins = activeAdmins + 1
}
}
if activeAdmins <= 0 {
c . Err = model . NewAppError ( "updateRoles" , "There must be at least one active admin" , "userId=" + user_id )
return
}
}
}
user . Roles = new_roles
if result := <- Srv . Store . User ( ) . Update ( user , true ) ; result . Err != nil {
c . Err = result . Err
return
} else {
c . LogAuditWithUserId ( user . Id , "roles=" + new_roles )
ruser := result . Data . ( [ 2 ] * model . User ) [ 0 ]
options := utils . SanitizeOptions
options [ "passwordupdate" ] = false
ruser . Sanitize ( options )
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
}
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
if result := <- Srv . Store . User ( ) . Get ( user_id ) ; result . Err != nil {
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
if ! c . HasPermissionsToTeam ( user . TeamId , "updateActive" ) {
return
}
if ! strings . Contains ( c . Session . Roles , model . ROLE_ADMIN ) && ! c . IsSystemAdmin ( ) {
c . Err = model . NewAppError ( "updateActive" , "You do not have the appropriate permissions" , "userId=" + user_id )
c . Err . StatusCode = http . StatusForbidden
return
}
// make sure there is at least 1 other active admin
if ! active && strings . Contains ( user . Roles , model . ROLE_ADMIN ) {
if result := <- Srv . Store . User ( ) . GetProfiles ( user . TeamId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
activeAdmins := - 1
profileUsers := result . Data . ( map [ string ] * model . User )
for _ , profileUser := range profileUsers {
if profileUser . DeleteAt == 0 && strings . Contains ( profileUser . Roles , model . ROLE_ADMIN ) {
activeAdmins = activeAdmins + 1
}
}
if activeAdmins <= 0 {
c . Err = model . NewAppError ( "updateRoles" , "There must be at least one active admin" , "userId=" + user_id )
return
}
}
}
if active {
user . DeleteAt = 0
} else {
user . DeleteAt = model . GetMillis ( )
}
if result := <- Srv . Store . User ( ) . Update ( user , true ) ; result . Err != nil {
c . Err = result . Err
return
} else {
c . LogAuditWithUserId ( user . Id , fmt . Sprintf ( "active=%v" , active ) )
if user . DeleteAt > 0 {
RevokeAllSession ( c , user . Id )
}
ruser := result . Data . ( [ 2 ] * model . User ) [ 0 ]
options := utils . SanitizeOptions
options [ "passwordupdate" ] = false
ruser . Sanitize ( options )
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
}
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
}
2015-07-08 11:50:10 -04:00
name := props [ "name" ]
if len ( name ) == 0 {
c . SetInvalidParam ( "sendPasswordReset" , "name" )
2015-06-14 23:53:32 -08:00
return
}
var team * model . Team
2015-07-08 11:50:10 -04:00
if result := <- Srv . Store . Team ( ) . GetByName ( name ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
team = result . Data . ( * model . Team )
}
var user * model . User
if result := <- Srv . Store . User ( ) . GetByEmail ( team . Id , email ) ; result . Err != nil {
c . Err = model . NewAppError ( "sendPasswordReset" , "We couldn’ t find an account with that address." , "email=" + email + " team_id=" + team . Id )
return
} else {
user = result . Data . ( * model . User )
}
newProps := make ( map [ string ] string )
newProps [ "user_id" ] = user . Id
newProps [ "time" ] = fmt . Sprintf ( "%v" , model . GetMillis ( ) )
data := model . MapToJson ( newProps )
hash := model . HashPassword ( fmt . Sprintf ( "%v:%v" , data , utils . Cfg . ServiceSettings . ResetSalt ) )
2015-07-08 11:50:10 -04:00
link := fmt . Sprintf ( "%s/reset_password?d=%s&h=%s" , c . GetTeamURLFromTeam ( team ) , url . QueryEscape ( data ) , url . QueryEscape ( hash ) )
2015-06-14 23:53:32 -08:00
2015-07-08 11:50:10 -04:00
subjectPage := NewServerTemplatePage ( "reset_subject" , c . GetTeamURLFromTeam ( team ) )
bodyPage := NewServerTemplatePage ( "reset_body" , c . GetTeamURLFromTeam ( team ) )
2015-06-14 23:53:32 -08:00
bodyPage . Props [ "ResetUrl" ] = link
if err := utils . SendMail ( email , subjectPage . Render ( ) , bodyPage . Render ( ) ) ; err != nil {
c . Err = model . NewAppError ( "sendPasswordReset" , "Failed to send password reset email successfully" , "err=" + err . Message )
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 )
newPassword := props [ "new_password" ]
if len ( newPassword ) < 5 {
c . SetInvalidParam ( "resetPassword" , "new_password" )
return
}
hash := props [ "hash" ]
if len ( hash ) == 0 {
c . SetInvalidParam ( "resetPassword" , "hash" )
return
}
data := model . MapFromJson ( strings . NewReader ( props [ "data" ] ) )
userId := data [ "user_id" ]
if len ( userId ) != 26 {
c . SetInvalidParam ( "resetPassword" , "data:user_id" )
return
}
timeStr := data [ "time" ]
if len ( timeStr ) == 0 {
c . SetInvalidParam ( "resetPassword" , "data:time" )
return
}
2015-07-08 11:50:10 -04:00
name := props [ "name" ]
if len ( name ) == 0 {
c . SetInvalidParam ( "resetPassword" , "name" )
2015-06-14 23:53:32 -08:00
return
}
c . LogAuditWithUserId ( userId , "attempt" )
var team * model . Team
2015-07-08 11:50:10 -04:00
if result := <- Srv . Store . Team ( ) . GetByName ( name ) ; result . Err != nil {
2015-06-14 23:53:32 -08:00
c . Err = result . Err
return
} else {
team = result . Data . ( * model . Team )
}
var user * model . User
if result := <- Srv . Store . User ( ) . Get ( userId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
if user . TeamId != team . Id {
c . Err = model . NewAppError ( "resetPassword" , "Trying to reset password for user on wrong team." , "userId=" + user . Id + ", teamId=" + team . Id )
c . Err . StatusCode = http . StatusForbidden
return
}
if ! model . ComparePassword ( hash , fmt . Sprintf ( "%v:%v" , props [ "data" ] , utils . Cfg . ServiceSettings . ResetSalt ) ) {
c . Err = model . NewAppError ( "resetPassword" , "The reset password link does not appear to be valid" , "" )
return
}
t , err := strconv . ParseInt ( timeStr , 10 , 64 )
if err != nil || model . GetMillis ( ) - t > 1000 * 60 * 60 { // one hour
c . Err = model . NewAppError ( "resetPassword" , "The reset link has expired" , "" )
return
}
if result := <- Srv . Store . User ( ) . UpdatePassword ( userId , model . HashPassword ( newPassword ) ) ; result . Err != nil {
c . Err = result . Err
return
} else {
c . LogAuditWithUserId ( userId , "success" )
}
2015-07-08 11:50:10 -04:00
fireAndForgetPasswordChangeEmail ( user . Email , team . DisplayName , c . GetTeamURLFromTeam ( team ) , "using a reset password link" )
2015-06-14 23:53:32 -08:00
props [ "new_password" ] = ""
w . Write ( [ ] byte ( model . MapToJson ( props ) ) )
}
2015-07-08 11:50:10 -04:00
func fireAndForgetPasswordChangeEmail ( email , teamDisplayName , teamURL , method string ) {
2015-06-14 23:53:32 -08:00
go func ( ) {
2015-07-08 11:50:10 -04:00
subjectPage := NewServerTemplatePage ( "password_change_subject" , teamURL )
subjectPage . Props [ "TeamDisplayName" ] = teamDisplayName
bodyPage := NewServerTemplatePage ( "password_change_body" , teamURL )
bodyPage . Props [ "TeamDisplayName" ] = teamDisplayName
2015-06-14 23:53:32 -08:00
bodyPage . Props [ "Method" ] = method
if err := utils . SendMail ( email , subjectPage . Render ( ) , bodyPage . Render ( ) ) ; err != nil {
l4g . Error ( "Failed to send update password email successfully err=%v" , err )
}
} ( )
}
2015-07-08 11:50:10 -04:00
func fireAndForgetEmailChangeEmail ( email , teamDisplayName , teamURL string ) {
2015-06-14 23:53:32 -08:00
go func ( ) {
2015-07-08 11:50:10 -04:00
subjectPage := NewServerTemplatePage ( "email_change_subject" , teamURL )
subjectPage . Props [ "TeamDisplayName" ] = teamDisplayName
bodyPage := NewServerTemplatePage ( "email_change_body" , teamURL )
bodyPage . Props [ "TeamDisplayName" ] = teamDisplayName
2015-06-14 23:53:32 -08:00
if err := utils . SendMail ( email , subjectPage . Render ( ) , bodyPage . Render ( ) ) ; err != nil {
l4g . Error ( "Failed to send update password email successfully err=%v" , err )
}
} ( )
}
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
}
uchan := Srv . Store . User ( ) . Get ( user_id )
if ! c . HasPermissionsToUser ( user_id , "updateUserNotify" ) {
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
}
var user * model . User
if result := <- uchan ; result . Err != nil {
c . Err = result . Err
return
} else {
user = result . Data . ( * model . User )
}
user . NotifyProps = props
if result := <- Srv . Store . User ( ) . Update ( user , false ) ; result . Err != nil {
c . Err = result . Err
return
} else {
c . LogAuditWithUserId ( user . Id , "" )
ruser := result . Data . ( [ 2 ] * model . User ) [ 0 ]
options := utils . SanitizeOptions
options [ "passwordupdate" ] = false
ruser . Sanitize ( options )
w . Write ( [ ] byte ( ruser . ToJson ( ) ) )
}
}
func getStatuses ( c * Context , w http . ResponseWriter , r * http . Request ) {
if result := <- Srv . Store . User ( ) . GetProfiles ( c . Session . TeamId ) ; result . Err != nil {
c . Err = result . Err
return
} else {
profiles := result . Data . ( map [ string ] * model . User )
statuses := map [ string ] string { }
for _ , profile := range profiles {
if profile . IsOffline ( ) {
statuses [ profile . Id ] = model . USER_OFFLINE
} else if profile . IsAway ( ) {
statuses [ profile . Id ] = model . USER_AWAY
} else {
statuses [ profile . Id ] = model . USER_ONLINE
}
}
//w.Header().Set("Cache-Control", "max-age=9, public") // 2 mins
w . Write ( [ ] byte ( model . MapToJson ( statuses ) ) )
return
}
}
2015-07-15 12:48:50 -04:00
2015-07-22 12:13:45 -04:00
func GetAuthorizationCode ( c * Context , w http . ResponseWriter , r * http . Request , teamName , service , redirectUri string ) {
2015-07-17 09:47:25 -04:00
if s , ok := utils . Cfg . SSOSettings [ service ] ; ! ok || ! s . Allow {
c . Err = model . NewAppError ( "GetAuthorizationCode" , "Unsupported OAuth service provider" , "service=" + service )
c . Err . StatusCode = http . StatusBadRequest
return
}
clientId := utils . Cfg . SSOSettings [ service ] . Id
endpoint := utils . Cfg . SSOSettings [ service ] . AuthEndpoint
state := model . HashPassword ( clientId )
2015-07-22 10:12:28 -04:00
authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url . QueryEscape ( redirectUri + "?team=" + teamName ) + "&state=" + url . QueryEscape ( state )
2015-07-17 09:47:25 -04:00
http . Redirect ( w , r , authUrl , http . StatusFound )
}
func AuthorizeOAuthUser ( service , code , state , redirectUri string ) ( io . ReadCloser , * model . AppError ) {
if s , ok := utils . Cfg . SSOSettings [ service ] ; ! ok || ! s . Allow {
return nil , model . NewAppError ( "AuthorizeOAuthUser" , "Unsupported OAuth service provider" , "service=" + service )
}
if ! model . ComparePassword ( state , utils . Cfg . SSOSettings [ service ] . Id ) {
return nil , model . NewAppError ( "AuthorizeOAuthUser" , "Invalid state" , "" )
2015-07-15 12:48:50 -04:00
}
p := url . Values { }
2015-07-17 09:47:25 -04:00
p . Set ( "client_id" , utils . Cfg . SSOSettings [ service ] . Id )
p . Set ( "client_secret" , utils . Cfg . SSOSettings [ service ] . Secret )
2015-07-15 12:48:50 -04:00
p . Set ( "code" , code )
p . Set ( "grant_type" , model . ACCESS_TOKEN_GRANT_TYPE )
2015-07-17 09:47:25 -04:00
p . Set ( "redirect_uri" , redirectUri )
2015-07-15 12:48:50 -04:00
client := & http . Client { }
2015-07-17 09:47:25 -04:00
req , _ := http . NewRequest ( "POST" , utils . Cfg . SSOSettings [ service ] . TokenEndpoint , strings . NewReader ( p . Encode ( ) ) )
2015-07-15 12:48:50 -04:00
req . Header . Set ( "Content-Type" , "application/x-www-form-urlencoded" )
req . Header . Set ( "Accept" , "application/json" )
var ar * model . AccessResponse
if resp , err := client . Do ( req ) ; err != nil {
2015-07-22 11:26:55 -04:00
return nil , model . NewAppError ( "AuthorizeOAuthUser" , "Token request failed" , err . Error ( ) )
2015-07-15 12:48:50 -04:00
} else {
ar = model . AccessResponseFromJson ( resp . Body )
}
if ar . TokenType != model . ACCESS_TOKEN_TYPE {
2015-07-17 09:47:25 -04:00
return nil , model . NewAppError ( "AuthorizeOAuthUser" , "Bad token type" , "token_type=" + ar . TokenType )
2015-07-15 12:48:50 -04:00
}
if len ( ar . AccessToken ) == 0 {
2015-07-17 09:47:25 -04:00
return nil , model . NewAppError ( "AuthorizeOAuthUser" , "Missing access token" , "" )
2015-07-15 12:48:50 -04:00
}
p = url . Values { }
p . Set ( "access_token" , ar . AccessToken )
2015-07-17 09:47:25 -04:00
req , _ = http . NewRequest ( "GET" , utils . Cfg . SSOSettings [ service ] . UserApiEndpoint , strings . NewReader ( "" ) )
2015-07-15 12:48:50 -04:00
req . Header . Set ( "Content-Type" , "application/x-www-form-urlencoded" )
req . Header . Set ( "Accept" , "application/json" )
req . Header . Set ( "Authorization" , "Bearer " + ar . AccessToken )
if resp , err := client . Do ( req ) ; err != nil {
2015-07-17 09:47:25 -04:00
return nil , model . NewAppError ( "AuthorizeOAuthUser" , "Token request to " + service + " failed" , err . Error ( ) )
2015-07-15 12:48:50 -04:00
} else {
2015-07-17 09:47:25 -04:00
return resp . Body , nil
2015-07-15 12:48:50 -04:00
}
}