Skip to content

Commit

Permalink
Merge 9143682 into 1a09b2d
Browse files Browse the repository at this point in the history
  • Loading branch information
jwilander committed Apr 6, 2017
2 parents 1a09b2d + 9143682 commit b969cac
Show file tree
Hide file tree
Showing 11 changed files with 494 additions and 284 deletions.
114 changes: 3 additions & 111 deletions api/oauth.go
Expand Up @@ -4,8 +4,6 @@
package api

import (
"crypto/tls"
b64 "encoding/base64"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -273,7 +271,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {

uri := c.GetSiteURLHeader() + "/signup/" + service + "/complete"

if body, teamId, props, err := AuthorizeOAuthUser(service, code, state, uri); err != nil {
if body, teamId, props, err := app.AuthorizeOAuthUser(service, code, state, uri); err != nil {
c.Err = err
return
} else {
Expand Down Expand Up @@ -620,7 +618,7 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
stateProps["redirect_to"] = redirectTo
}

if authUrl, err := GetAuthorizationCode(c, service, stateProps, loginHint); err != nil {
if authUrl, err := app.GetAuthorizationCode(service, stateProps, loginHint); err != nil {
c.Err = err
return
} else {
Expand Down Expand Up @@ -680,120 +678,14 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
stateProps["team_id"] = teamId
}

if authUrl, err := GetAuthorizationCode(c, service, stateProps, ""); err != nil {
if authUrl, err := app.GetAuthorizationCode(service, stateProps, ""); err != nil {
c.Err = err
return
} else {
http.Redirect(w, r, authUrl, http.StatusFound)
}
}

func GetAuthorizationCode(c *Context, service string, props map[string]string, loginHint string) (string, *model.AppError) {

sso := utils.Cfg.GetSSOService(service)
if sso != nil && !sso.Enable {
return "", model.NewLocAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service)
}

clientId := sso.Id
endpoint := sso.AuthEndpoint
scope := sso.Scope

props["hash"] = model.HashPassword(clientId)
state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props)))

redirectUri := c.GetSiteURLHeader() + "/signup/" + service + "/complete"

authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state)

if len(scope) > 0 {
authUrl += "&scope=" + utils.UrlEncode(scope)
}

if len(loginHint) > 0 {
authUrl += "&login_hint=" + utils.UrlEncode(loginHint)
}

return authUrl, nil
}

func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) {
sso := utils.Cfg.GetSSOService(service)
if sso == nil || !sso.Enable {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service)
}

stateStr := ""
if b, err := b64.StdEncoding.DecodeString(state); err != nil {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error())
} else {
stateStr = string(b)
}

stateProps := model.MapFromJson(strings.NewReader(stateStr))

if !model.ComparePassword(stateProps["hash"], sso.Id) {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "")
}

teamId := stateProps["team_id"]

p := url.Values{}
p.Set("client_id", sso.Id)
p.Set("client_secret", sso.Secret)
p.Set("code", code)
p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
p.Set("redirect_uri", redirectUri)

tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections},
}
client := &http.Client{Transport: tr}
req, _ := http.NewRequest("POST", sso.TokenEndpoint, strings.NewReader(p.Encode()))

req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")

var ar *model.AccessResponse
var respBody []byte
if resp, err := client.Do(req); err != nil {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error())
} else {
ar = model.AccessResponseFromJson(resp.Body)
defer func() {
ioutil.ReadAll(resp.Body)
resp.Body.Close()
}()
if ar == nil {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "")
}
}

if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType+", response_body="+string(respBody))
}

if len(ar.AccessToken) == 0 {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "")
}

p = url.Values{}
p.Set("access_token", ar.AccessToken)
req, _ = http.NewRequest("GET", sso.UserApiEndpoint, strings.NewReader(""))

req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+ar.AccessToken)

if resp, err := client.Do(req); err != nil {
return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error",
map[string]interface{}{"Service": service}, err.Error())
} else {
return resp.Body, teamId, stateProps, nil
}

}

func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.ReadCloser, email string) {
authData := ""
ssoEmail := ""
Expand Down
168 changes: 13 additions & 155 deletions api/user.go
Expand Up @@ -914,41 +914,14 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

c.LogAudit("attempt")

var user *model.User
var err *model.AppError
if user, err = app.GetUserByEmail(email); err != nil {
c.LogAudit("fail - couldn't get user")
c.Err = err
return
}

if err := app.CheckPasswordAndAllCriteria(user, password, mfaToken); err != nil {
c.LogAuditWithUserId(user.Id, "failed - bad authentication")
link, err := app.SwitchEmailToOAuth(email, password, mfaToken, service)
if err != nil {
c.Err = err
return
}

stateProps := map[string]string{}
stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
stateProps["email"] = email

m := map[string]string{}
if service == model.USER_AUTH_SERVICE_SAML {
m["follow_link"] = c.GetSiteURLHeader() + "/login/sso/saml?action=" + model.OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + email
} else {
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
}
}

c.LogAuditWithUserId(user.Id, "success")
w.Write([]byte(model.MapToJson(m)))
c.LogAudit("success for email=" + email)
w.Write([]byte(model.MapToJson(map[string]string{"follow_link": link})))
}

func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) {
Expand All @@ -966,51 +939,19 @@ func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) {
return
}

c.LogAudit("attempt")

var user *model.User
var err *model.AppError
if user, err = app.GetUserByEmail(email); err != nil {
c.LogAudit("fail - couldn't get user")
c.Err = err
return
}

if user.Id != c.Session.UserId {
c.LogAudit("fail - user ids didn't match")
c.Err = model.NewLocAppError("oauthToEmail", "api.user.oauth_to_email.context.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}

if err := app.UpdatePassword(user, password); err != nil {
c.LogAudit("fail - database issue")
c.Err = err
return
}

go func() {
if err := app.SendSignInChangeEmail(user.Email, c.T("api.templates.signin_change_email.body.method_email"), user.Locale, utils.GetSiteURL()); err != nil {
l4g.Error(err.Error())
}
}()

if err := app.RevokeAllSessions(c.Session.UserId); err != nil {
link, err := app.SwitchOAuthToEmail(email, password, c.Session.UserId)
if err != nil {
c.Err = err
return
}
c.LogAuditWithUserId(c.Session.UserId, "Revoked all sessions for user")

c.RemoveSessionCookie(w, r)
if c.Err != nil {
return
}

m := map[string]string{}
m["follow_link"] = "/login?extra=signin_change"

c.LogAudit("success")
w.Write([]byte(model.MapToJson(m)))
w.Write([]byte(model.MapToJson(map[string]string{"follow_link": link})))
}

func emailToLdap(c *Context, w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -1044,55 +985,19 @@ func emailToLdap(c *Context, w http.ResponseWriter, r *http.Request) {

c.LogAudit("attempt")

var user *model.User
var err *model.AppError
if user, err = app.GetUserByEmail(email); err != nil {
c.LogAudit("fail - couldn't get user")
c.Err = err
return
}

if err := app.CheckPasswordAndAllCriteria(user, emailPassword, token); err != nil {
c.LogAuditWithUserId(user.Id, "failed - bad authentication")
c.Err = err
return
}

if err := app.RevokeAllSessions(user.Id); err != nil {
link, err := app.SwitchEmailToLdap(email, emailPassword, token, ldapId, ldapPassword)
if err != nil {
c.Err = err
return
}
c.LogAuditWithUserId(user.Id, "Revoked all sessions for user")

c.RemoveSessionCookie(w, r)
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
}

if err := ldapInterface.SwitchToLdap(user.Id, ldapId, ldapPassword); err != nil {
c.LogAuditWithUserId(user.Id, "fail - ldap switch failed")
c.Err = err
return
}

go func() {
if err := app.SendSignInChangeEmail(user.Email, "AD/LDAP", user.Locale, utils.GetSiteURL()); err != nil {
l4g.Error(err.Error())
}
}()

m := map[string]string{}
m["follow_link"] = "/login?extra=signin_change"

c.LogAudit("success")
w.Write([]byte(model.MapToJson(m)))
w.Write([]byte(model.MapToJson(map[string]string{"follow_link": link})))
}

func ldapToEmail(c *Context, w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -1120,66 +1025,19 @@ func ldapToEmail(c *Context, w http.ResponseWriter, r *http.Request) {

c.LogAudit("attempt")

var user *model.User
var err *model.AppError
if user, err = app.GetUserByEmail(email); err != nil {
c.LogAudit("fail - couldn't get user")
c.Err = err
return
}

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()
if ldapInterface == nil || user.AuthData == nil {
c.Err = model.NewLocAppError("ldapToEmail", "api.user.ldap_to_email.not_available.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}

if err := ldapInterface.CheckPassword(*user.AuthData, ldapPassword); err != nil {
c.LogAuditWithUserId(user.Id, "fail - ldap authentication failed")
c.Err = err
return
}

if err := app.CheckUserMfa(user, token); err != nil {
c.LogAuditWithUserId(user.Id, "fail - mfa token failed")
c.Err = err
return
}

if err := app.UpdatePassword(user, emailPassword); err != nil {
c.LogAudit("fail - database issue")
c.Err = err
return
}

if err := app.RevokeAllSessions(user.Id); err != nil {
link, err := app.SwitchLdapToEmail(ldapPassword, token, email, emailPassword)
if err != nil {
c.Err = err
return
}
c.LogAuditWithUserId(user.Id, "Revoked all sessions for user")

c.RemoveSessionCookie(w, r)
if c.Err != nil {
return
}

go func() {
if err := app.SendSignInChangeEmail(user.Email, c.T("api.templates.signin_change_email.body.method_email"), user.Locale, utils.GetSiteURL()); err != nil {
l4g.Error(err.Error())
}
}()

m := map[string]string{}
m["follow_link"] = "/login?extra=signin_change"

c.LogAudit("success")
w.Write([]byte(model.MapToJson(m)))
w.Write([]byte(model.MapToJson(map[string]string{"follow_link": link})))
}

func verifyEmail(c *Context, w http.ResponseWriter, r *http.Request) {
Expand Down
3 changes: 1 addition & 2 deletions api4/context.go
Expand Up @@ -242,8 +242,7 @@ func (c *Context) IsSystemAdmin() bool {

func (c *Context) SessionRequired() {
if len(c.Session.UserId) == 0 {
c.Err = model.NewLocAppError("", "api.context.session_expired.app_error", nil, "UserRequired")
c.Err.StatusCode = http.StatusUnauthorized
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserRequired", http.StatusUnauthorized)
return
}
}
Expand Down

0 comments on commit b969cac

Please sign in to comment.