Refactor OAuth 2.0 code into app layer (#6037)

Этот коммит содержится в:
Joram Wilander
2017-04-12 16:29:42 -04:00
коммит произвёл Harrison Healey
родитель 03502cf73b
Коммит 8b8aa2ca3c
12 изменённых файлов: 649 добавлений и 660 удалений

Просмотреть файл

@@ -4,20 +4,14 @@
package api package api
import ( import (
"fmt"
"io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
l4g "github.com/alecthomas/log4go" l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mattermost/platform/app" "github.com/mattermost/platform/app"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
) )
@@ -46,15 +40,8 @@ func InitOAuth() {
} }
func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("registerOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) { if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
c.Err = model.NewLocAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "") c.Err = model.NewAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
c.Err.StatusCode = http.StatusForbidden
return return
} }
@@ -65,72 +52,50 @@ func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
secret := model.NewId()
oauthApp.ClientSecret = secret
oauthApp.CreatorId = c.Session.UserId oauthApp.CreatorId = c.Session.UserId
if result := <-app.Srv.Store.OAuth().SaveApp(oauthApp); result.Err != nil { rapp, err := app.CreateOAuthApp(oauthApp)
c.Err = result.Err
return
} else {
oauthApp = result.Data.(*model.OAuthApp)
c.LogAudit("client_id=" + oauthApp.Id) if err != nil {
c.Err = err
w.Write([]byte(oauthApp.ToJson()))
return return
} }
c.LogAudit("client_id=" + rapp.Id)
w.Write([]byte(rapp.ToJson()))
} }
func getOAuthApps(c *Context, w http.ResponseWriter, r *http.Request) { func getOAuthApps(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("getOAuthAppsByUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) { if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
c.Err = model.NewLocAppError("getOAuthApps", "api.command.admin_only.app_error", nil, "") c.Err = model.NewAppError("getOAuthApps", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
c.Err.StatusCode = http.StatusForbidden
return return
} }
var ochan store.StoreChannel var apps []*model.OAuthApp
var err *model.AppError
if app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) { if app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
ochan = app.Srv.Store.OAuth().GetApps() apps, err = app.GetOAuthApps(0, 100000)
} else { } else {
c.Err = nil apps, err = app.GetOAuthAppsByCreator(c.Session.UserId, 0, 100000)
ochan = app.Srv.Store.OAuth().GetAppByUser(c.Session.UserId)
} }
if result := <-ochan; result.Err != nil { if err != nil {
c.Err = result.Err c.Err = err
return return
} else {
apps := result.Data.([]*model.OAuthApp)
w.Write([]byte(model.OAuthAppListToJson(apps)))
} }
w.Write([]byte(model.OAuthAppListToJson(apps)))
} }
func getOAuthAppInfo(c *Context, w http.ResponseWriter, r *http.Request) { func getOAuthAppInfo(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("getOAuthAppInfo", "api.oauth.allow_oauth.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
params := mux.Vars(r) params := mux.Vars(r)
clientId := params["client_id"] clientId := params["client_id"]
var oauthApp *model.OAuthApp oauthApp, err := app.GetOAuthApp(clientId)
if result := <-app.Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
c.Err = model.NewLocAppError("getOAuthAppInfo", "api.oauth.allow_oauth.database.app_error", nil, "") if err != nil {
c.Err = err
return return
} else {
oauthApp = result.Data.(*model.OAuthApp)
} }
oauthApp.Sanitize() oauthApp.Sanitize()
@@ -138,124 +103,50 @@ func getOAuthAppInfo(c *Context, w http.ResponseWriter, r *http.Request) {
} }
func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) { func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
c.LogAudit("attempt")
responseData := map[string]string{}
responseType := r.URL.Query().Get("response_type") responseType := r.URL.Query().Get("response_type")
if len(responseType) == 0 { if len(responseType) == 0 {
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.bad_response.app_error", nil, "") c.Err = model.NewAppError("allowOAuth", "api.oauth.allow_oauth.bad_response.app_error", nil, "", http.StatusBadRequest)
c.Err.StatusCode = http.StatusBadRequest
return return
} }
clientId := r.URL.Query().Get("client_id") clientId := r.URL.Query().Get("client_id")
if len(clientId) != 26 { if len(clientId) != 26 {
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.bad_client.app_error", nil, "") c.Err = model.NewAppError("allowOAuth", "api.oauth.allow_oauth.bad_client.app_error", nil, "", http.StatusBadRequest)
c.Err.StatusCode = http.StatusBadRequest
return return
} }
redirectUri := r.URL.Query().Get("redirect_uri") redirectUri := r.URL.Query().Get("redirect_uri")
if len(redirectUri) == 0 { if len(redirectUri) == 0 {
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.bad_redirect.app_error", nil, "") c.Err = model.NewAppError("allowOAuth", "api.oauth.allow_oauth.bad_redirect.app_error", nil, "", http.StatusBadRequest)
c.Err.StatusCode = http.StatusBadRequest
return return
} }
scope := r.URL.Query().Get("scope") scope := r.URL.Query().Get("scope")
state := r.URL.Query().Get("state") state := r.URL.Query().Get("state")
if len(scope) == 0 { c.LogAudit("attempt")
scope = model.DEFAULT_SCOPE
}
var oauthApp *model.OAuthApp redirectUrl, err := app.AllowOAuthAppAccessToUser(c.Session.UserId, responseType, clientId, redirectUri, scope, state)
if result := <-app.Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.database.app_error", nil, "")
return
} else {
oauthApp = result.Data.(*model.OAuthApp)
}
if !oauthApp.IsValidRedirectURL(redirectUri) { if err != nil {
c.LogAudit("fail - redirect_uri did not match registered callback") c.Err = err
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return return
} }
if responseType != model.AUTHCODE_RESPONSE_TYPE { c.LogAudit("")
responseData["redirect"] = redirectUri + "?error=unsupported_response_type&state=" + state
w.Write([]byte(model.MapToJson(responseData)))
return
}
authData := &model.AuthData{UserId: c.Session.UserId, ClientId: clientId, CreateAt: model.GetMillis(), RedirectUri: redirectUri, State: state, Scope: scope} w.Write([]byte(model.MapToJson(map[string]string{"redirect": redirectUrl})))
authData.Code = model.HashPassword(fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, c.Session.UserId))
// this saves the OAuth2 app as authorized
authorizedApp := model.Preference{
UserId: c.Session.UserId,
Category: model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP,
Name: clientId,
Value: scope,
}
if result := <-app.Srv.Store.Preference().Save(&model.Preferences{authorizedApp}); result.Err != nil {
responseData["redirect"] = redirectUri + "?error=server_error&state=" + state
w.Write([]byte(model.MapToJson(responseData)))
return
}
if result := <-app.Srv.Store.OAuth().SaveAuthData(authData); result.Err != nil {
responseData["redirect"] = redirectUri + "?error=server_error&state=" + state
w.Write([]byte(model.MapToJson(responseData)))
return
}
c.LogAudit("success")
responseData["redirect"] = redirectUri + "?code=" + url.QueryEscape(authData.Code) + "&state=" + url.QueryEscape(authData.State)
w.Write([]byte(model.MapToJson(responseData)))
} }
func getAuthorizedApps(c *Context, w http.ResponseWriter, r *http.Request) { func getAuthorizedApps(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider { apps, err := app.GetAuthorizedAppsForUser(c.Session.UserId, 0, 10000)
c.Err = model.NewLocAppError("getAuthorizedApps", "api.oauth.allow_oauth.turn_off.app_error", nil, "") if err != nil {
c.Err.StatusCode = http.StatusNotImplemented c.Err = err
return return
} }
ochan := app.Srv.Store.OAuth().GetAuthorizedApps(c.Session.UserId)
if result := <-ochan; result.Err != nil {
c.Err = result.Err
return
} else {
apps := result.Data.([]*model.OAuthApp)
for k, a := range apps {
a.Sanitize()
apps[k] = a
}
w.Write([]byte(model.OAuthAppListToJson(apps))) w.Write([]byte(model.OAuthAppListToJson(apps)))
} }
}
func GetAuthData(code string) *model.AuthData {
if result := <-app.Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
l4g.Error(utils.T("api.oauth.get_auth_data.find.error"), code)
return nil
} else {
return result.Data.(*model.AuthData)
}
}
func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r) params := mux.Vars(r)
@@ -271,60 +162,36 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
uri := c.GetSiteURLHeader() + "/signup/" + service + "/complete" uri := c.GetSiteURLHeader() + "/signup/" + service + "/complete"
if body, teamId, props, err := app.AuthorizeOAuthUser(service, code, state, uri); err != nil { body, teamId, props, err := app.AuthorizeOAuthUser(service, code, state, uri)
if err != nil {
c.Err = err c.Err = err
return return
} else { }
defer func() {
ioutil.ReadAll(body) user, err := app.CompleteOAuth(service, body, teamId, props)
body.Close() if err != nil {
}() c.Err = err
return
}
action := props["action"] action := props["action"]
switch action {
case model.OAUTH_ACTION_SIGNUP: var redirectUrl string
if user, err := app.CreateOAuthUser(service, body, teamId); err != nil { if action == model.OAUTH_ACTION_EMAIL_TO_SSO {
c.Err = err redirectUrl = c.GetSiteURLHeader() + "/login?extra=signin_change"
} else if action == model.OAUTH_ACTION_SSO_TO_EMAIL {
redirectUrl = app.GetProtocol(r) + "://" + r.Host + "/claim?email=" + url.QueryEscape(props["email"])
} else { } else {
doLogin(c, w, r, user, "") doLogin(c, w, r, user, "")
} if c.Err != nil {
if c.Err == nil {
http.Redirect(w, r, app.GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect)
}
break
case model.OAUTH_ACTION_LOGIN:
user := LoginByOAuth(c, w, r, service, body)
if len(teamId) > 0 {
c.Err = app.AddUserToTeamByTeamId(teamId, user)
}
if c.Err == nil {
if val, ok := props["redirect_to"]; ok {
http.Redirect(w, r, c.GetSiteURLHeader()+val, http.StatusTemporaryRedirect)
return return
} }
http.Redirect(w, r, app.GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect)
} redirectUrl = c.GetSiteURLHeader()
break
case model.OAUTH_ACTION_EMAIL_TO_SSO:
CompleteSwitchWithOAuth(c, w, r, service, body, props["email"])
if c.Err == nil {
http.Redirect(w, r, app.GetProtocol(r)+"://"+r.Host+"/login?extra=signin_change", http.StatusTemporaryRedirect)
}
break
case model.OAUTH_ACTION_SSO_TO_EMAIL:
LoginByOAuth(c, w, r, service, body)
if c.Err == nil {
http.Redirect(w, r, app.GetProtocol(r)+"://"+r.Host+"/claim?email="+url.QueryEscape(props["email"]), http.StatusTemporaryRedirect)
}
break
default:
LoginByOAuth(c, w, r, service, body)
if c.Err == nil {
http.Redirect(w, r, app.GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect)
}
break
}
} }
http.Redirect(w, r, redirectUrl, http.StatusTemporaryRedirect)
} }
func authorizeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { func authorizeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -371,42 +238,15 @@ func authorizeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
// Automatically allow if the app is trusted // Automatically allow if the app is trusted
if oauthApp.IsTrusted || isAuthorized { if oauthApp.IsTrusted || isAuthorized {
closeBody := func(r *http.Response) { redirectUrl, err := app.AllowOAuthAppAccessToUser(c.Session.UserId, model.AUTHCODE_RESPONSE_TYPE, clientId, redirect, scope, state)
if r.Body != nil {
ioutil.ReadAll(r.Body)
r.Body.Close()
}
}
doAllow := func() (*http.Response, *model.AppError) { if err != nil {
HttpClient := &http.Client{}
url := c.GetSiteURLHeader() + "/api/v3/oauth/allow?response_type=" + model.AUTHCODE_RESPONSE_TYPE + "&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirect) + "&scope=" + scope + "&state=" + url.QueryEscape(state)
rq, _ := http.NewRequest("GET", url, strings.NewReader(""))
rq.Header.Set(model.HEADER_AUTH, model.HEADER_BEARER+" "+c.Session.Token)
if rp, err := HttpClient.Do(rq); err != nil {
return nil, model.NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
} else if rp.StatusCode == 304 {
return rp, nil
} else if rp.StatusCode >= 300 {
defer closeBody(rp)
return rp, model.AppErrorFromJson(rp.Body)
} else {
return rp, nil
}
}
if result, err := doAllow(); err != nil {
c.Err = err c.Err = err
return return
} else {
//defer closeBody(result)
data := model.MapFromJson(result.Body)
redirectTo := data["redirect"]
http.Redirect(w, r, redirectTo, http.StatusFound)
return
} }
http.Redirect(w, r, redirectUrl, http.StatusFound)
return
} }
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
@@ -416,14 +256,6 @@ func authorizeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
} }
func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) { func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
c.LogAudit("attempt")
r.ParseForm() r.ParseForm()
code := r.FormValue("code") code := r.FormValue("code")
@@ -458,140 +290,21 @@ func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
var oauthApp *model.OAuthApp
achan := app.Srv.Store.OAuth().GetApp(clientId)
if result := <-achan; result.Err != nil {
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "")
return
} else {
oauthApp = result.Data.(*model.OAuthApp)
}
if oauthApp.ClientSecret != secret {
c.LogAudit("fail - invalid client credentials")
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "")
return
}
var user *model.User
var accessData *model.AccessData
var accessRsp *model.AccessResponse
if grantType == model.ACCESS_TOKEN_GRANT_TYPE {
redirectUri := r.FormValue("redirect_uri") redirectUri := r.FormValue("redirect_uri")
authData := GetAuthData(code)
if authData == nil { c.LogAudit("attempt")
c.LogAudit("fail - invalid auth code")
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "")
return
}
if authData.IsExpired() { accessRsp, err := app.GetOAuthAccessToken(clientId, grantType, redirectUri, code, secret, refreshToken)
<-app.Srv.Store.OAuth().RemoveAuthData(authData.Code) if err != nil {
c.LogAudit("fail - auth code expired")
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "")
return
}
if authData.RedirectUri != redirectUri {
c.LogAudit("fail - redirect uri provided did not match previous redirect uri")
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "")
return
}
if !model.ComparePassword(code, fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, authData.UserId)) {
c.LogAudit("fail - auth code is invalid")
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "")
return
}
uchan := app.Srv.Store.User().Get(authData.UserId)
if result := <-uchan; result.Err != nil {
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "")
return
} else {
user = result.Data.(*model.User)
}
tchan := app.Srv.Store.OAuth().GetPreviousAccessData(user.Id, clientId)
if result := <-tchan; result.Err != nil {
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "")
return
} else if result.Data != nil {
accessData := result.Data.(*model.AccessData)
if accessData.IsExpired() {
if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
c.Err = err c.Err = err
return return
} else {
accessRsp = access
}
} else {
//return the same token and no need to create a new session
accessRsp = &model.AccessResponse{
AccessToken: accessData.Token,
TokenType: model.ACCESS_TOKEN_TYPE,
ExpiresIn: int32((accessData.ExpiresAt - model.GetMillis()) / 1000),
}
}
} else {
// create a new session and return new access token
var session *model.Session
if result, err := newSession(oauthApp.Name, user); err != nil {
c.Err = err
return
} else {
session = result
}
accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt}
if result := <-app.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
l4g.Error(result.Err)
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "")
return
}
accessRsp = &model.AccessResponse{
AccessToken: session.Token,
TokenType: model.ACCESS_TOKEN_TYPE,
RefreshToken: accessData.RefreshToken,
ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
}
}
<-app.Srv.Store.OAuth().RemoveAuthData(authData.Code)
} else {
// when grantType is refresh_token
if result := <-app.Srv.Store.OAuth().GetAccessDataByRefreshToken(refreshToken); result.Err != nil {
c.LogAudit("fail - refresh token is invalid")
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "")
return
} else {
accessData = result.Data.(*model.AccessData)
}
uchan := app.Srv.Store.User().Get(accessData.UserId)
if result := <-uchan; result.Err != nil {
c.Err = model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "")
return
} else {
user = result.Data.(*model.User)
}
if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
c.Err = err
return
} else {
accessRsp = access
}
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store") w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Pragma", "no-cache") w.Header().Set("Pragma", "no-cache")
c.LogAuditWithUserId(user.Id, "success") c.LogAudit("success")
w.Write([]byte(accessRsp.ToJson())) w.Write([]byte(accessRsp.ToJson()))
} }
@@ -602,23 +315,13 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
loginHint := r.URL.Query().Get("login_hint") loginHint := r.URL.Query().Get("login_hint")
redirectTo := r.URL.Query().Get("redirect_to") redirectTo := r.URL.Query().Get("redirect_to")
teamId, err := getTeamIdFromQuery(r.URL.Query()) teamId, err := app.GetTeamIdFromQuery(r.URL.Query())
if err != nil { if err != nil {
c.Err = err c.Err = err
return return
} }
stateProps := map[string]string{} if authUrl, err := app.GetOAuthLoginEndpoint(service, teamId, redirectTo, loginHint); err != nil {
stateProps["action"] = model.OAUTH_ACTION_LOGIN
if len(teamId) != 0 {
stateProps["team_id"] = teamId
}
if len(redirectTo) != 0 {
stateProps["redirect_to"] = redirectTo
}
if authUrl, err := app.GetAuthorizationCode(service, stateProps, loginHint); err != nil {
c.Err = err c.Err = err
return return
} else { } else {
@@ -626,59 +329,22 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
} }
} }
func getTeamIdFromQuery(query url.Values) (string, *model.AppError) {
hash := query.Get("h")
inviteId := query.Get("id")
if len(hash) > 0 {
data := query.Get("d")
props := model.MapFromJson(strings.NewReader(data))
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
return "", model.NewLocAppError("getTeamIdFromQuery", "api.oauth.singup_with_oauth.invalid_link.app_error", nil, "")
}
t, err := strconv.ParseInt(props["time"], 10, 64)
if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
return "", model.NewLocAppError("getTeamIdFromQuery", "api.oauth.singup_with_oauth.expired_link.app_error", nil, "")
}
return props["id"], nil
} else if len(inviteId) > 0 {
if result := <-app.Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
// soft fail, so we still create user but don't auto-join team
l4g.Error("%v", result.Err)
} else {
return result.Data.(*model.Team).Id, nil
}
}
return "", nil
}
func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r) params := mux.Vars(r)
service := params["service"] service := params["service"]
if !utils.Cfg.TeamSettings.EnableUserCreation { if !utils.Cfg.TeamSettings.EnableUserCreation {
c.Err = model.NewLocAppError("signupWithOAuth", "api.oauth.singup_with_oauth.disabled.app_error", nil, "") c.Err = model.NewAppError("signupWithOAuth", "api.oauth.singup_with_oauth.disabled.app_error", nil, "", http.StatusNotImplemented)
c.Err.StatusCode = http.StatusNotImplemented
return return
} }
teamId, err := getTeamIdFromQuery(r.URL.Query()) teamId, err := app.GetTeamIdFromQuery(r.URL.Query())
if err != nil { if err != nil {
c.Err = err c.Err = err
return return
} }
stateProps := map[string]string{} if authUrl, err := app.GetOAuthSignupEndpoint(service, teamId); err != nil {
stateProps["action"] = model.OAUTH_ACTION_SIGNUP
if len(teamId) != 0 {
stateProps["team_id"] = teamId
}
if authUrl, err := app.GetAuthorizationCode(service, stateProps, ""); err != nil {
c.Err = err c.Err = err
return return
} else { } else {
@@ -686,75 +352,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
} }
} }
func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.ReadCloser, email string) {
authData := ""
ssoEmail := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
map[string]interface{}{"Service": strings.Title(service)}, "")
return
} else {
ssoUser := provider.GetUserFromJson(userData)
ssoEmail = ssoUser.Email
if ssoUser.AuthData != nil {
authData = *ssoUser.AuthData
}
}
if len(authData) == 0 {
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "")
return
}
if len(email) == 0 {
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "")
return
}
var user *model.User
if result := <-app.Srv.Store.User().GetByEmail(email); result.Err != nil {
c.Err = result.Err
return
} else {
user = result.Data.(*model.User)
}
if err := app.RevokeAllSessions(user.Id); err != nil {
c.Err = err
return
}
c.LogAuditWithUserId(user.Id, "Revoked all sessions for user")
if result := <-app.Srv.Store.User().UpdateAuthData(user.Id, service, &authData, ssoEmail, true); result.Err != nil {
c.Err = result.Err
return
}
go func() {
if err := app.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, utils.GetSiteURL()); err != nil {
l4g.Error(err.Error())
}
}()
}
func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("deleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
c.Err = model.NewLocAppError("deleteOAuthApp", "api.command.admin_only.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
c.LogAudit("attempt")
props := model.MapFromJson(r.Body) props := model.MapFromJson(r.Body)
id := props["id"] id := props["id"]
@@ -763,18 +361,27 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
if result := <-app.Srv.Store.OAuth().GetApp(id); result.Err != nil { c.LogAudit("attempt")
c.Err = result.Err
if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OAUTH) {
c.Err = model.NewAppError("deleteOAuthApp", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
return return
} else {
if c.Session.UserId != result.Data.(*model.OAuthApp).CreatorId && !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
c.LogAudit("fail - inappropriate permissions")
c.Err = model.NewLocAppError("deleteOAuthApp", "api.oauth.delete.permissions.app_error", nil, "user_id="+c.Session.UserId)
return
}
} }
if err := (<-app.Srv.Store.OAuth().DeleteApp(id)).Err; err != nil { oauthApp, err := app.GetOAuthApp(id)
if err != nil {
c.Err = err
return
}
if c.Session.UserId != oauthApp.CreatorId && !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
c.LogAudit("fail - inappropriate permissions")
c.Err = model.NewAppError("deleteOAuthApp", "api.oauth.delete.permissions.app_error", nil, "user_id="+c.Session.UserId, http.StatusForbidden)
return
}
err = app.DeleteOAuthApp(id)
if err != nil {
c.Err = err c.Err = err
return return
} }
@@ -784,37 +391,11 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
} }
func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("deleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
params := mux.Vars(r) params := mux.Vars(r)
id := params["id"] id := params["id"]
// revoke app sessions err := app.DeauthorizeOAuthAppForUser(c.Session.UserId, id)
if result := <-app.Srv.Store.OAuth().GetAccessDataByUserForApp(c.Session.UserId, id); result.Err != nil { if err != nil {
c.Err = result.Err
return
} else {
accessData := result.Data.([]*model.AccessData)
for _, a := range accessData {
if err := app.RevokeAccessToken(a.Token); err != nil {
c.Err = err
return
}
if rad := <-app.Srv.Store.OAuth().RemoveAccessData(a.Token); rad.Err != nil {
c.Err = rad.Err
return
}
}
}
// Deauthorize the app
if err := (<-app.Srv.Store.Preference().Delete(c.Session.UserId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, id)).Err; err != nil {
c.Err = err c.Err = err
return return
} }
@@ -824,78 +405,25 @@ func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
} }
func regenerateOAuthSecret(c *Context, w http.ResponseWriter, r *http.Request) { func regenerateOAuthSecret(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewLocAppError("registerOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
params := mux.Vars(r) params := mux.Vars(r)
id := params["id"] id := params["id"]
var oauthApp *model.OAuthApp oauthApp, err := app.GetOAuthApp(id)
if result := <-app.Srv.Store.OAuth().GetApp(id); result.Err != nil { if err != nil {
c.Err = model.NewLocAppError("regenerateOAuthSecret", "api.oauth.allow_oauth.database.app_error", nil, "") c.Err = err
return
} else {
oauthApp = result.Data.(*model.OAuthApp)
if oauthApp.CreatorId != c.Session.UserId && !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
c.Err = model.NewLocAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return return
} }
oauthApp.ClientSecret = model.NewId() if oauthApp.CreatorId != c.Session.UserId && !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) {
if update := <-app.Srv.Store.OAuth().UpdateApp(oauthApp); update.Err != nil { c.Err = model.NewAppError("regenerateOAuthSecret", "api.command.admin_only.app_error", nil, "", http.StatusForbidden)
c.Err = update.Err return
}
oauthApp, err = app.RegenerateOAuthAppSecret(oauthApp)
if err != nil {
c.Err = err
return return
} }
w.Write([]byte(oauthApp.ToJson())) w.Write([]byte(oauthApp.ToJson()))
return
}
}
func newSession(appName string, user *model.User) (*model.Session, *model.AppError) {
// set new token an session
session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true}
session.SetExpireInDays(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays)
session.AddProp(model.SESSION_PROP_PLATFORM, appName)
session.AddProp(model.SESSION_PROP_OS, "OAuth2")
session.AddProp(model.SESSION_PROP_BROWSER, "OAuth2")
if result := <-app.Srv.Store.Session().Save(session); result.Err != nil {
return nil, model.NewLocAppError("getAccessToken", "api.oauth.get_access_token.internal_session.app_error", nil, "")
} else {
session = result.Data.(*model.Session)
app.AddSessionToCache(session)
}
return session, nil
}
func newSessionUpdateToken(appName string, accessData *model.AccessData, user *model.User) (*model.AccessResponse, *model.AppError) {
var session *model.Session
<-app.Srv.Store.Session().Remove(accessData.Token) //remove the previous session
if result, err := newSession(appName, user); err != nil {
return nil, err
} else {
session = result
}
accessData.Token = session.Token
accessData.ExpiresAt = session.ExpiresAt
if result := <-app.Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
l4g.Error(result.Err)
return nil, model.NewLocAppError("getAccessToken", "web.get_access_token.internal_saving.app_error", nil, "")
}
accessRsp := &model.AccessResponse{
AccessToken: session.Token,
TokenType: model.ACCESS_TOKEN_TYPE,
ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
}
return accessRsp, nil
} }

Просмотреть файл

@@ -491,7 +491,7 @@ func TestOAuthAuthorize(t *testing.T) {
} }
authToken := Client.AuthType + " " + Client.AuthToken authToken := Client.AuthType + " " + Client.AuthToken
if r, err := HttpGet(Client.Url+"/oauth/authorize?client_id="+oauthApp.Id+"&&redirect_uri=http://example.com&response_type="+model.AUTHCODE_RESPONSE_TYPE, Client.HttpClient, authToken, true); err != nil { if r, err := HttpGet(Client.Url+"/oauth/authorize?client_id="+oauthApp.Id+"&redirect_uri=http://example.com&response_type="+model.AUTHCODE_RESPONSE_TYPE, Client.HttpClient, authToken, true); err != nil {
t.Fatal(err) t.Fatal(err)
closeBody(r) closeBody(r)
} }

Просмотреть файл

@@ -4,10 +4,8 @@
package api package api
import ( import (
"bytes"
b64 "encoding/base64" b64 "encoding/base64"
"fmt" "fmt"
"io"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@@ -132,52 +130,6 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(user.ToJson())) w.Write([]byte(user.ToJson()))
} }
func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader) *model.User {
buf := bytes.Buffer{}
buf.ReadFrom(userData)
authData := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
c.Err = model.NewLocAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
map[string]interface{}{"Service": strings.Title(service)}, "")
return nil
} else {
authData = provider.GetAuthDataFromJson(bytes.NewReader(buf.Bytes()))
}
if len(authData) == 0 {
c.Err = model.NewLocAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "")
return nil
}
var user *model.User
var err *model.AppError
if user, err = app.GetUserByAuth(&authData, service); err != nil {
if err.Id == store.MISSING_AUTH_ACCOUNT_ERROR {
if user, err = app.CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), ""); err != nil {
c.Err = err
return nil
}
}
c.Err = err
return nil
}
if err = app.UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
c.Err = err
return nil
}
doLogin(c, w, r, user, "")
if c.Err != nil {
return nil
}
return user
}
// User MUST be authenticated completely before calling Login // User MUST be authenticated completely before calling Login
func doLogin(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) { func doLogin(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) {
session, err := app.DoLogin(w, r, user, deviceId) session, err := app.DoLogin(w, r, user, deviceId)
@@ -1188,7 +1140,7 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
teamId, err := getTeamIdFromQuery(r.URL.Query()) teamId, err := app.GetTeamIdFromQuery(r.URL.Query())
if err != nil { if err != nil {
c.Err = err c.Err = err
return return

Просмотреть файл

@@ -4,8 +4,10 @@
package app package app
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
b64 "encoding/base64" b64 "encoding/base64"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@@ -13,10 +15,371 @@ import (
"strings" "strings"
l4g "github.com/alecthomas/log4go" l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
) )
func CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("CreateOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
secret := model.NewId()
app.ClientSecret = secret
if result := <-Srv.Store.OAuth().SaveApp(app); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.OAuthApp), nil
}
}
func GetOAuthApp(appId string) (*model.OAuthApp, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("GetOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
if result := <-Srv.Store.OAuth().GetApp(appId); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.OAuthApp), nil
}
}
func DeleteOAuthApp(appId string) *model.AppError {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return model.NewAppError("DeleteOAuthApp", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
if err := (<-Srv.Store.OAuth().DeleteApp(appId)).Err; err != nil {
return err
}
return nil
}
func GetOAuthApps(page, perPage int) ([]*model.OAuthApp, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("GetOAuthApps", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
if result := <-Srv.Store.OAuth().GetApps(page*perPage, perPage); result.Err != nil {
return nil, result.Err
} else {
return result.Data.([]*model.OAuthApp), nil
}
}
func GetOAuthAppsByCreator(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("GetOAuthAppsByUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
if result := <-Srv.Store.OAuth().GetAppByUser(userId, page*perPage, perPage); result.Err != nil {
return nil, result.Err
} else {
return result.Data.([]*model.OAuthApp), nil
}
}
func AllowOAuthAppAccessToUser(userId, responseType, clientId, redirectUri, scope, state string) (string, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
if len(scope) == 0 {
scope = model.DEFAULT_SCOPE
}
var oauthApp *model.OAuthApp
if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
return "", result.Err
} else {
oauthApp = result.Data.(*model.OAuthApp)
}
if !oauthApp.IsValidRedirectURL(redirectUri) {
return "", model.NewAppError("AllowOAuthAppAccessToUser", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest)
}
if responseType != model.AUTHCODE_RESPONSE_TYPE {
return redirectUri + "?error=unsupported_response_type&state=" + state, nil
}
authData := &model.AuthData{UserId: userId, ClientId: clientId, CreateAt: model.GetMillis(), RedirectUri: redirectUri, State: state, Scope: scope}
authData.Code = model.HashPassword(fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, userId))
// this saves the OAuth2 app as authorized
authorizedApp := model.Preference{
UserId: userId,
Category: model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP,
Name: clientId,
Value: scope,
}
if result := <-Srv.Store.Preference().Save(&model.Preferences{authorizedApp}); result.Err != nil {
return redirectUri + "?error=server_error&state=" + state, nil
}
if result := <-Srv.Store.OAuth().SaveAuthData(authData); result.Err != nil {
return redirectUri + "?error=server_error&state=" + state, nil
}
return redirectUri + "?code=" + url.QueryEscape(authData.Code) + "&state=" + url.QueryEscape(authData.State), nil
}
func GetOAuthAccessToken(clientId, grantType, redirectUri, code, secret, refreshToken string) (*model.AccessResponse, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.disabled.app_error", nil, "", http.StatusNotImplemented)
}
var oauthApp *model.OAuthApp
if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusNotFound)
} else {
oauthApp = result.Data.(*model.OAuthApp)
}
if oauthApp.ClientSecret != secret {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.credentials.app_error", nil, "", http.StatusForbidden)
}
var user *model.User
var accessData *model.AccessData
var accessRsp *model.AccessResponse
if grantType == model.ACCESS_TOKEN_GRANT_TYPE {
var authData *model.AuthData
if result := <-Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusInternalServerError)
} else {
authData = result.Data.(*model.AuthData)
}
if authData.IsExpired() {
<-Srv.Store.OAuth().RemoveAuthData(authData.Code)
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
}
if authData.RedirectUri != redirectUri {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.redirect_uri.app_error", nil, "", http.StatusBadRequest)
}
if !model.ComparePassword(code, fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, authData.UserId)) {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusBadRequest)
}
if result := <-Srv.Store.User().Get(authData.UserId); result.Err != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
} else {
user = result.Data.(*model.User)
}
if result := <-Srv.Store.OAuth().GetPreviousAccessData(user.Id, clientId); result.Err != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "", http.StatusInternalServerError)
} else if result.Data != nil {
accessData := result.Data.(*model.AccessData)
if accessData.IsExpired() {
if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
return nil, err
} else {
accessRsp = access
}
} else {
//return the same token and no need to create a new session
accessRsp = &model.AccessResponse{
AccessToken: accessData.Token,
TokenType: model.ACCESS_TOKEN_TYPE,
ExpiresIn: int32((accessData.ExpiresAt - model.GetMillis()) / 1000),
}
}
} else {
// create a new session and return new access token
var session *model.Session
if result, err := newSession(oauthApp.Name, user); err != nil {
return nil, err
} else {
session = result
}
accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt, Scope: authData.Scope}
if result := <-Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
l4g.Error(result.Err)
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
}
accessRsp = &model.AccessResponse{
AccessToken: session.Token,
TokenType: model.ACCESS_TOKEN_TYPE,
RefreshToken: accessData.RefreshToken,
ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
}
}
<-Srv.Store.OAuth().RemoveAuthData(authData.Code)
} else {
// when grantType is refresh_token
if result := <-Srv.Store.OAuth().GetAccessDataByRefreshToken(refreshToken); result.Err != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.refresh_token.app_error", nil, "", http.StatusNotFound)
} else {
accessData = result.Data.(*model.AccessData)
}
if result := <-Srv.Store.User().Get(accessData.UserId); result.Err != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
} else {
user = result.Data.(*model.User)
}
if access, err := newSessionUpdateToken(oauthApp.Name, accessData, user); err != nil {
return nil, err
} else {
accessRsp = access
}
}
return accessRsp, nil
}
func newSession(appName string, user *model.User) (*model.Session, *model.AppError) {
// set new token an session
session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true}
session.SetExpireInDays(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays)
session.AddProp(model.SESSION_PROP_PLATFORM, appName)
session.AddProp(model.SESSION_PROP_OS, "OAuth2")
session.AddProp(model.SESSION_PROP_BROWSER, "OAuth2")
if result := <-Srv.Store.Session().Save(session); result.Err != nil {
return nil, model.NewAppError("newSession", "api.oauth.get_access_token.internal_session.app_error", nil, "", http.StatusInternalServerError)
} else {
session = result.Data.(*model.Session)
AddSessionToCache(session)
}
return session, nil
}
func newSessionUpdateToken(appName string, accessData *model.AccessData, user *model.User) (*model.AccessResponse, *model.AppError) {
var session *model.Session
<-Srv.Store.Session().Remove(accessData.Token) //remove the previous session
if result, err := newSession(appName, user); err != nil {
return nil, err
} else {
session = result
}
accessData.Token = session.Token
accessData.ExpiresAt = session.ExpiresAt
if result := <-Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
l4g.Error(result.Err)
return nil, model.NewAppError("newSessionUpdateToken", "web.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
}
accessRsp := &model.AccessResponse{
AccessToken: session.Token,
TokenType: model.ACCESS_TOKEN_TYPE,
ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24),
}
return accessRsp, nil
}
func GetOAuthLoginEndpoint(service, teamId, redirectTo, loginHint string) (string, *model.AppError) {
stateProps := map[string]string{}
stateProps["action"] = model.OAUTH_ACTION_LOGIN
if len(teamId) != 0 {
stateProps["team_id"] = teamId
}
if len(redirectTo) != 0 {
stateProps["redirect_to"] = redirectTo
}
if authUrl, err := GetAuthorizationCode(service, stateProps, loginHint); err != nil {
return "", err
} else {
return authUrl, nil
}
}
func GetOAuthSignupEndpoint(service, teamId string) (string, *model.AppError) {
stateProps := map[string]string{}
stateProps["action"] = model.OAUTH_ACTION_SIGNUP
if len(teamId) != 0 {
stateProps["team_id"] = teamId
}
if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil {
return "", err
} else {
return authUrl, nil
}
}
func GetAuthorizedAppsForUser(userId string, page, perPage int) ([]*model.OAuthApp, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("GetAuthorizedAppsForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
if result := <-Srv.Store.OAuth().GetAuthorizedApps(userId, page*perPage, perPage); result.Err != nil {
return nil, result.Err
} else {
apps := result.Data.([]*model.OAuthApp)
for k, a := range apps {
a.Sanitize()
apps[k] = a
}
return apps, nil
}
}
func DeauthorizeOAuthAppForUser(userId, appId string) *model.AppError {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return model.NewAppError("DeauthorizeOAuthAppForUser", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
// revoke app sessions
if result := <-Srv.Store.OAuth().GetAccessDataByUserForApp(userId, appId); result.Err != nil {
return result.Err
} else {
accessData := result.Data.([]*model.AccessData)
for _, a := range accessData {
if err := RevokeAccessToken(a.Token); err != nil {
return err
}
if rad := <-Srv.Store.OAuth().RemoveAccessData(a.Token); rad.Err != nil {
return rad.Err
}
}
}
// Deauthorize the app
if err := (<-Srv.Store.Preference().Delete(userId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, appId)).Err; err != nil {
return err
}
return nil
}
func RegenerateOAuthAppSecret(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
return nil, model.NewAppError("RegenerateOAuthAppSecret", "api.oauth.allow_oauth.turn_off.app_error", nil, "", http.StatusNotImplemented)
}
app.ClientSecret = model.NewId()
if update := <-Srv.Store.OAuth().UpdateApp(app); update.Err != nil {
return nil, update.Err
}
return app, nil
}
func RevokeAccessToken(token string) *model.AppError { func RevokeAccessToken(token string) *model.AppError {
session, _ := GetSession(token) session, _ := GetSession(token)
schan := Srv.Store.Session().Remove(token) schan := Srv.Store.Session().Remove(token)
@@ -42,10 +405,122 @@ func RevokeAccessToken(token string) *model.AppError {
return nil return nil
} }
func CompleteOAuth(service string, body io.ReadCloser, teamId string, props map[string]string) (*model.User, *model.AppError) {
defer func() {
ioutil.ReadAll(body)
body.Close()
}()
action := props["action"]
switch action {
case model.OAUTH_ACTION_SIGNUP:
return CreateOAuthUser(service, body, teamId)
case model.OAUTH_ACTION_LOGIN:
return LoginByOAuth(service, body, teamId)
case model.OAUTH_ACTION_EMAIL_TO_SSO:
return CompleteSwitchWithOAuth(service, body, props["email"])
case model.OAUTH_ACTION_SSO_TO_EMAIL:
return LoginByOAuth(service, body, teamId)
default:
return LoginByOAuth(service, body, teamId)
}
}
func LoginByOAuth(service string, userData io.Reader, teamId string) (*model.User, *model.AppError) {
buf := bytes.Buffer{}
buf.ReadFrom(userData)
authData := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
} else {
authData = provider.GetAuthDataFromJson(bytes.NewReader(buf.Bytes()))
}
if len(authData) == 0 {
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
}
user, err := GetUserByAuth(&authData, service)
if err != nil {
if err.Id == store.MISSING_AUTH_ACCOUNT_ERROR {
return CreateOAuthUser(service, bytes.NewReader(buf.Bytes()), teamId)
}
return nil, err
}
if err = UpdateOAuthUserAttrs(bytes.NewReader(buf.Bytes()), user, provider, service); err != nil {
return nil, err
}
if len(teamId) > 0 {
err = AddUserToTeamByTeamId(teamId, user)
}
if err != nil {
return nil, err
}
return user, nil
}
func CompleteSwitchWithOAuth(service string, userData io.ReadCloser, email string) (*model.User, *model.AppError) {
authData := ""
ssoEmail := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
} else {
ssoUser := provider.GetUserFromJson(userData)
ssoEmail = ssoUser.Email
if ssoUser.AuthData != nil {
authData = *ssoUser.AuthData
}
}
if len(authData) == 0 {
return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "", http.StatusBadRequest)
}
if len(email) == 0 {
return nil, model.NewAppError("CompleteSwitchWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "", http.StatusBadRequest)
}
var user *model.User
if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil {
return nil, result.Err
} else {
user = result.Data.(*model.User)
}
if err := RevokeAllSessions(user.Id); err != nil {
return nil, err
}
if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, &authData, ssoEmail, true); result.Err != nil {
return nil, result.Err
}
go func() {
if err := SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, utils.GetSiteURL()); err != nil {
l4g.Error(err.Error())
}
}()
return user, nil
}
func GetAuthorizationCode(service string, props map[string]string, loginHint string) (string, *model.AppError) { func GetAuthorizationCode(service string, props map[string]string, loginHint string) (string, *model.AppError) {
sso := utils.Cfg.GetSSOService(service) sso := utils.Cfg.GetSSOService(service)
if sso != nil && !sso.Enable { if sso != nil && !sso.Enable {
return "", model.NewLocAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service) return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented)
} }
clientId := sso.Id clientId := sso.Id

Просмотреть файл

@@ -6,6 +6,7 @@ package app
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
@@ -747,3 +748,33 @@ func GetTeamStats(teamId string) (*model.TeamStats, *model.AppError) {
return stats, nil return stats, nil
} }
func GetTeamIdFromQuery(query url.Values) (string, *model.AppError) {
hash := query.Get("h")
inviteId := query.Get("id")
if len(hash) > 0 {
data := query.Get("d")
props := model.MapFromJson(strings.NewReader(data))
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
return "", model.NewAppError("GetTeamIdFromQuery", "api.oauth.singup_with_oauth.invalid_link.app_error", nil, "", http.StatusBadRequest)
}
t, err := strconv.ParseInt(props["time"], 10, 64)
if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
return "", model.NewAppError("GetTeamIdFromQuery", "api.oauth.singup_with_oauth.expired_link.app_error", nil, "", http.StatusBadRequest)
}
return props["id"], nil
} else if len(inviteId) > 0 {
if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
// soft fail, so we still create user but don't auto-join team
l4g.Error("%v", result.Err)
} else {
return result.Data.(*model.Team).Id, nil
}
}
return "", nil
}

Просмотреть файл

@@ -21,6 +21,7 @@ type AccessData struct {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
RedirectUri string `json:"redirect_uri"` RedirectUri string `json:"redirect_uri"`
ExpiresAt int64 `json:"expires_at"` ExpiresAt int64 `json:"expires_at"`
Scope string `json:"scope"`
} }
type AccessResponse struct { type AccessResponse struct {

Просмотреть файл

@@ -4,6 +4,7 @@
package store package store
import ( import (
"net/http"
"strings" "strings"
"github.com/go-gorp/gorp" "github.com/go-gorp/gorp"
@@ -42,6 +43,7 @@ func NewSqlOAuthStore(sqlStore *SqlStore) OAuthStore {
tableAccess.ColMap("Token").SetMaxSize(26) tableAccess.ColMap("Token").SetMaxSize(26)
tableAccess.ColMap("RefreshToken").SetMaxSize(26) tableAccess.ColMap("RefreshToken").SetMaxSize(26)
tableAccess.ColMap("RedirectUri").SetMaxSize(256) tableAccess.ColMap("RedirectUri").SetMaxSize(256)
tableAccess.ColMap("Scope").SetMaxSize(128)
tableAccess.SetUniqueTogether("ClientId", "UserId") tableAccess.SetUniqueTogether("ClientId", "UserId")
} }
@@ -138,9 +140,9 @@ func (as SqlOAuthStore) GetApp(id string) StoreChannel {
result := StoreResult{} result := StoreResult{}
if obj, err := as.GetReplica().Get(model.OAuthApp{}, id); err != nil { if obj, err := as.GetReplica().Get(model.OAuthApp{}, id); err != nil {
result.Err = model.NewLocAppError("SqlOAuthStore.GetApp", "store.sql_oauth.get_app.finding.app_error", nil, "app_id="+id+", "+err.Error()) result.Err = model.NewAppError("SqlOAuthStore.GetApp", "store.sql_oauth.get_app.finding.app_error", nil, "app_id="+id+", "+err.Error(), http.StatusInternalServerError)
} else if obj == nil { } else if obj == nil {
result.Err = model.NewLocAppError("SqlOAuthStore.GetApp", "store.sql_oauth.get_app.find.app_error", nil, "app_id="+id) result.Err = model.NewAppError("SqlOAuthStore.GetApp", "store.sql_oauth.get_app.find.app_error", nil, "app_id="+id, http.StatusNotFound)
} else { } else {
result.Data = obj.(*model.OAuthApp) result.Data = obj.(*model.OAuthApp)
} }
@@ -153,7 +155,7 @@ func (as SqlOAuthStore) GetApp(id string) StoreChannel {
return storeChannel return storeChannel
} }
func (as SqlOAuthStore) GetAppByUser(userId string) StoreChannel { func (as SqlOAuthStore) GetAppByUser(userId string, offset, limit int) StoreChannel {
storeChannel := make(StoreChannel, 1) storeChannel := make(StoreChannel, 1)
@@ -162,8 +164,8 @@ func (as SqlOAuthStore) GetAppByUser(userId string) StoreChannel {
var apps []*model.OAuthApp var apps []*model.OAuthApp
if _, err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { if _, err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps WHERE CreatorId = :UserId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"UserId": userId, "Offset": offset, "Limit": limit}); err != nil {
result.Err = model.NewLocAppError("SqlOAuthStore.GetAppByUser", "store.sql_oauth.get_app_by_user.find.app_error", nil, "user_id="+userId+", "+err.Error()) result.Err = model.NewAppError("SqlOAuthStore.GetAppByUser", "store.sql_oauth.get_app_by_user.find.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
} }
result.Data = apps result.Data = apps
@@ -175,7 +177,7 @@ func (as SqlOAuthStore) GetAppByUser(userId string) StoreChannel {
return storeChannel return storeChannel
} }
func (as SqlOAuthStore) GetApps() StoreChannel { func (as SqlOAuthStore) GetApps(offset, limit int) StoreChannel {
storeChannel := make(StoreChannel, 1) storeChannel := make(StoreChannel, 1)
@@ -184,8 +186,8 @@ func (as SqlOAuthStore) GetApps() StoreChannel {
var apps []*model.OAuthApp var apps []*model.OAuthApp
if _, err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps"); err != nil { if _, err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil {
result.Err = model.NewLocAppError("SqlOAuthStore.GetAppByUser", "store.sql_oauth.get_apps.find.app_error", nil, "err="+err.Error()) result.Err = model.NewAppError("SqlOAuthStore.GetAppByUser", "store.sql_oauth.get_apps.find.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
} }
result.Data = apps result.Data = apps
@@ -197,7 +199,7 @@ func (as SqlOAuthStore) GetApps() StoreChannel {
return storeChannel return storeChannel
} }
func (as SqlOAuthStore) GetAuthorizedApps(userId string) StoreChannel { func (as SqlOAuthStore) GetAuthorizedApps(userId string, offset, limit int) StoreChannel {
storeChannel := make(StoreChannel, 1) storeChannel := make(StoreChannel, 1)
go func() { go func() {
@@ -207,8 +209,8 @@ func (as SqlOAuthStore) GetAuthorizedApps(userId string) StoreChannel {
if _, err := as.GetReplica().Select(&apps, if _, err := as.GetReplica().Select(&apps,
`SELECT o.* FROM OAuthApps AS o INNER JOIN `SELECT o.* FROM OAuthApps AS o INNER JOIN
Preferences AS p ON p.Name=o.Id AND p.UserId=:UserId`, map[string]interface{}{"UserId": userId}); err != nil { Preferences AS p ON p.Name=o.Id AND p.UserId=:UserId LIMIT :Limit OFFSET :Offset`, map[string]interface{}{"UserId": userId, "Offset": offset, "Limit": limit}); err != nil {
result.Err = model.NewLocAppError("SqlOAuthStore.GetAuthorizedApps", "store.sql_oauth.get_apps.find.app_error", nil, "err="+err.Error()) result.Err = model.NewAppError("SqlOAuthStore.GetAuthorizedApps", "store.sql_oauth.get_apps.find.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
} }
result.Data = apps result.Data = apps

Просмотреть файл

@@ -56,7 +56,7 @@ func TestOAuthStoreGetApp(t *testing.T) {
} }
// Lets try and get the app from a user that hasn't created any apps // Lets try and get the app from a user that hasn't created any apps
if result := (<-store.OAuth().GetAppByUser("fake0123456789abcderfgret1")); result.Err == nil { if result := (<-store.OAuth().GetAppByUser("fake0123456789abcderfgret1", 0, 1000)); result.Err == nil {
if len(result.Data.([]*model.OAuthApp)) > 0 { if len(result.Data.([]*model.OAuthApp)) > 0 {
t.Fatal("Should have failed. Fake user hasn't created any apps") t.Fatal("Should have failed. Fake user hasn't created any apps")
} }
@@ -64,11 +64,11 @@ func TestOAuthStoreGetApp(t *testing.T) {
t.Fatal(result.Err) t.Fatal(result.Err)
} }
if err := (<-store.OAuth().GetAppByUser(a1.CreatorId)).Err; err != nil { if err := (<-store.OAuth().GetAppByUser(a1.CreatorId, 0, 1000)).Err; err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := (<-store.OAuth().GetApps()).Err; err != nil { if err := (<-store.OAuth().GetApps(0, 1000)).Err; err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@@ -324,7 +324,7 @@ func TestOAuthGetAuthorizedApps(t *testing.T) {
Must(store.OAuth().SaveApp(&a1)) Must(store.OAuth().SaveApp(&a1))
// Lets try and get an Authorized app for a user who hasn't authorized it // Lets try and get an Authorized app for a user who hasn't authorized it
if result := <-store.OAuth().GetAuthorizedApps("fake0123456789abcderfgret1"); result.Err == nil { if result := <-store.OAuth().GetAuthorizedApps("fake0123456789abcderfgret1", 0, 1000); result.Err == nil {
if len(result.Data.([]*model.OAuthApp)) > 0 { if len(result.Data.([]*model.OAuthApp)) > 0 {
t.Fatal("Should have failed. Fake user hasn't authorized the app") t.Fatal("Should have failed. Fake user hasn't authorized the app")
} }
@@ -340,7 +340,7 @@ func TestOAuthGetAuthorizedApps(t *testing.T) {
p.Value = "true" p.Value = "true"
Must(store.Preference().Save(&model.Preferences{p})) Must(store.Preference().Save(&model.Preferences{p}))
if result := <-store.OAuth().GetAuthorizedApps(a1.CreatorId); result.Err != nil { if result := <-store.OAuth().GetAuthorizedApps(a1.CreatorId, 0, 1000); result.Err != nil {
t.Fatal(result.Err) t.Fatal(result.Err)
} else { } else {
apps := result.Data.([]*model.OAuthApp) apps := result.Data.([]*model.OAuthApp)
@@ -368,7 +368,7 @@ func TestOAuthGetAccessDataByUserForApp(t *testing.T) {
p.Value = "true" p.Value = "true"
Must(store.Preference().Save(&model.Preferences{p})) Must(store.Preference().Save(&model.Preferences{p}))
if result := <-store.OAuth().GetAuthorizedApps(a1.CreatorId); result.Err != nil { if result := <-store.OAuth().GetAuthorizedApps(a1.CreatorId, 0, 1000); result.Err != nil {
t.Fatal(result.Err) t.Fatal(result.Err)
} else { } else {
apps := result.Data.([]*model.OAuthApp) apps := result.Data.([]*model.OAuthApp)

Просмотреть файл

@@ -257,6 +257,7 @@ func UpgradeDatabaseToVersion38(sqlStore *SqlStore) {
func UpgradeDatabaseToVersion39(sqlStore *SqlStore) { func UpgradeDatabaseToVersion39(sqlStore *SqlStore) {
// TODO: Uncomment following condition when version 3.9.0 is released // TODO: Uncomment following condition when version 3.9.0 is released
//if shouldPerformUpgrade(sqlStore, VERSION_3_8_0, VERSION_3_9_0) { //if shouldPerformUpgrade(sqlStore, VERSION_3_8_0, VERSION_3_9_0) {
sqlStore.CreateColumnIfNotExists("OAuthAccessData", "Scope", "varchar(128)", "varchar(128)", model.DEFAULT_SCOPE)
// saveSchemaVersion(sqlStore, VERSION_3_9_0) // saveSchemaVersion(sqlStore, VERSION_3_9_0)
//} //}

Просмотреть файл

@@ -389,8 +389,7 @@ func (us SqlUserStore) Get(id string) StoreChannel {
if obj, err := us.GetReplica().Get(model.User{}, id); err != nil { if obj, err := us.GetReplica().Get(model.User{}, id); err != nil {
result.Err = model.NewLocAppError("SqlUserStore.Get", "store.sql_user.get.app_error", nil, "user_id="+id+", "+err.Error()) result.Err = model.NewLocAppError("SqlUserStore.Get", "store.sql_user.get.app_error", nil, "user_id="+id+", "+err.Error())
} else if obj == nil { } else if obj == nil {
result.Err = model.NewLocAppError("SqlUserStore.Get", MISSING_ACCOUNT_ERROR, nil, "user_id="+id) result.Err = model.NewAppError("SqlUserStore.Get", MISSING_ACCOUNT_ERROR, nil, "user_id="+id, http.StatusNotFound)
result.Err.StatusCode = http.StatusNotFound
} else { } else {
result.Data = obj.(*model.User) result.Data = obj.(*model.User)
} }

Просмотреть файл

@@ -246,9 +246,9 @@ type OAuthStore interface {
SaveApp(app *model.OAuthApp) StoreChannel SaveApp(app *model.OAuthApp) StoreChannel
UpdateApp(app *model.OAuthApp) StoreChannel UpdateApp(app *model.OAuthApp) StoreChannel
GetApp(id string) StoreChannel GetApp(id string) StoreChannel
GetAppByUser(userId string) StoreChannel GetAppByUser(userId string, offset, limit int) StoreChannel
GetApps() StoreChannel GetApps(offset, limit int) StoreChannel
GetAuthorizedApps(userId string) StoreChannel GetAuthorizedApps(userId string, offset, limit int) StoreChannel
DeleteApp(id string) StoreChannel DeleteApp(id string) StoreChannel
SaveAuthData(authData *model.AuthData) StoreChannel SaveAuthData(authData *model.AuthData) StoreChannel
GetAuthData(code string) StoreChannel GetAuthData(code string) StoreChannel

Просмотреть файл

@@ -12,7 +12,7 @@ describe('Client.OAuth', function() {
app.name = 'test'; app.name = 'test';
app.homepage = 'homepage'; app.homepage = 'homepage';
app.description = 'desc'; app.description = 'desc';
app.callback_urls = ''; app.callback_urls = [''];
TestHelper.basicClient().registerOAuthApp( TestHelper.basicClient().registerOAuthApp(
app, app,
@@ -33,7 +33,7 @@ describe('Client.OAuth', function() {
TestHelper.basicClient().allowOAuth2( TestHelper.basicClient().allowOAuth2(
'GET', 'GET',
'123456', '12345678901234567890123456',
'http://nowhere.com', 'http://nowhere.com',
'state', 'state',
'scope', 'scope',