Implement OAuth2 service provider functionality.
Этот коммит содержится в:
@@ -43,6 +43,7 @@ func InitApi() {
|
|||||||
InitFile(r)
|
InitFile(r)
|
||||||
InitCommand(r)
|
InitCommand(r)
|
||||||
InitAdmin(r)
|
InitAdmin(r)
|
||||||
|
InitOAuth(r)
|
||||||
|
|
||||||
templatesDir := utils.FindDir("api/templates")
|
templatesDir := utils.FindDir("api/templates")
|
||||||
l4g.Debug("Parsing server templates at %v", templatesDir)
|
l4g.Debug("Parsing server templates at %v", templatesDir)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func Setup() {
|
|||||||
NewServer()
|
NewServer()
|
||||||
StartServer()
|
StartServer()
|
||||||
InitApi()
|
InitApi()
|
||||||
Client = model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1")
|
Client = model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func TestCreateChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := Client.DoPost("/channels/create", "garbage"); err == nil {
|
if _, err := Client.DoApiPost("/channels/create", "garbage"); err == nil {
|
||||||
t.Fatal("should have been an error")
|
t.Fatal("should have been an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +627,7 @@ func TestGetChannelExtraInfo(t *testing.T) {
|
|||||||
currentEtag = cache_result.Etag
|
currentEtag = cache_result.Etag
|
||||||
}
|
}
|
||||||
|
|
||||||
Client2 := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1")
|
Client2 := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port)
|
||||||
|
|
||||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "tester2@test.com", Nickname: "Tester 2", Password: "pwd"}
|
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "tester2@test.com", Nickname: "Tester 2", Password: "pwd"}
|
||||||
user2 = Client2.Must(Client2.CreateUser(user2, "")).Data.(*model.User)
|
user2 = Client2.Must(Client2.CreateUser(user2, "")).Data.(*model.User)
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
|
|||||||
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
|
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client := model.NewClient(c.GetSiteURL() + "/api/v1")
|
client := model.NewClient(c.GetSiteURL())
|
||||||
|
|
||||||
if doTeams {
|
if doTeams {
|
||||||
if err := CreateBasicUser(client); err != nil {
|
if err := CreateBasicUser(client); err != nil {
|
||||||
@@ -375,7 +375,7 @@ func loadTestUsersCommand(c *Context, command *model.Command) bool {
|
|||||||
if err == false {
|
if err == false {
|
||||||
usersr = utils.Range{10, 15}
|
usersr = utils.Range{10, 15}
|
||||||
}
|
}
|
||||||
client := model.NewClient(c.GetSiteURL() + "/api/v1")
|
client := model.NewClient(c.GetSiteURL())
|
||||||
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
|
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
|
||||||
userCreator.Fuzzy = doFuzz
|
userCreator.Fuzzy = doFuzz
|
||||||
userCreator.CreateTestUsers(usersr)
|
userCreator.CreateTestUsers(usersr)
|
||||||
@@ -405,7 +405,7 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool {
|
|||||||
if err == false {
|
if err == false {
|
||||||
channelsr = utils.Range{20, 30}
|
channelsr = utils.Range{20, 30}
|
||||||
}
|
}
|
||||||
client := model.NewClient(c.GetSiteURL() + "/api/v1")
|
client := model.NewClient(c.GetSiteURL())
|
||||||
client.MockSession(c.Session.Id)
|
client.MockSession(c.Session.Id)
|
||||||
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
|
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
|
||||||
channelCreator.Fuzzy = doFuzz
|
channelCreator.Fuzzy = doFuzz
|
||||||
@@ -457,7 +457,7 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client := model.NewClient(c.GetSiteURL() + "/api/v1")
|
client := model.NewClient(c.GetSiteURL())
|
||||||
client.MockSession(c.Session.Id)
|
client.MockSession(c.Session.Id)
|
||||||
testPoster := NewAutoPostCreator(client, command.ChannelId)
|
testPoster := NewAutoPostCreator(client, command.ChannelId)
|
||||||
testPoster.Fuzzy = doFuzz
|
testPoster.Fuzzy = doFuzz
|
||||||
|
|||||||
@@ -80,9 +80,36 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
c.RequestId = model.NewId()
|
c.RequestId = model.NewId()
|
||||||
c.IpAddress = GetIpAddress(r)
|
c.IpAddress = GetIpAddress(r)
|
||||||
|
|
||||||
|
token := ""
|
||||||
|
isTokenFromQueryString := false
|
||||||
|
|
||||||
|
// Attempt to parse token out of the header
|
||||||
|
authHeader := r.Header.Get(model.HEADER_AUTH)
|
||||||
|
if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == model.HEADER_BEARER {
|
||||||
|
// Default session token
|
||||||
|
token = authHeader[7:]
|
||||||
|
|
||||||
|
} else if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == model.HEADER_TOKEN {
|
||||||
|
// OAuth token
|
||||||
|
token = authHeader[6:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse the token from the cookie
|
||||||
|
if len(token) == 0 {
|
||||||
|
if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil {
|
||||||
|
token = cookie.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse token out of the query string
|
||||||
|
if len(token) == 0 {
|
||||||
|
token = r.URL.Query().Get("access_token")
|
||||||
|
isTokenFromQueryString = true
|
||||||
|
}
|
||||||
|
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
|
|
||||||
// if the request came from the ELB then assume this is produciton
|
// If the request came from the ELB then assume this is produciton
|
||||||
// and redirect all http requests to https
|
// and redirect all http requests to https
|
||||||
if utils.Cfg.ServiceSettings.UseSSL {
|
if utils.Cfg.ServiceSettings.UseSSL {
|
||||||
forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO)
|
forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO)
|
||||||
@@ -105,36 +132,19 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
w.Header().Set("Content-Security-Policy", "frame-ancestors none")
|
w.Header().Set("Content-Security-Policy", "frame-ancestors none")
|
||||||
} else {
|
} else {
|
||||||
// All api response bodies will be JSON formatted
|
// All api response bodies will be JSON formatted by default
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionId := ""
|
if len(token) != 0 {
|
||||||
|
|
||||||
// attempt to parse the session token from the header
|
|
||||||
if ah := r.Header.Get(model.HEADER_AUTH); ah != "" {
|
|
||||||
if len(ah) > 6 && strings.ToUpper(ah[0:6]) == "BEARER" {
|
|
||||||
sessionId = ah[7:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to parse the session token from the cookie
|
|
||||||
if sessionId == "" {
|
|
||||||
if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil {
|
|
||||||
sessionId = cookie.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sessionId != "" {
|
|
||||||
|
|
||||||
var session *model.Session
|
var session *model.Session
|
||||||
if ts, ok := sessionCache.Get(sessionId); ok {
|
if ts, ok := sessionCache.Get(token); ok {
|
||||||
session = ts.(*model.Session)
|
session = ts.(*model.Session)
|
||||||
}
|
}
|
||||||
|
|
||||||
if session == nil {
|
if session == nil {
|
||||||
if sessionResult := <-Srv.Store.Session().Get(sessionId); sessionResult.Err != nil {
|
if sessionResult := <-Srv.Store.Session().Get(token); sessionResult.Err != nil {
|
||||||
c.LogError(model.NewAppError("ServeHTTP", "Invalid session", "id="+sessionId+", err="+sessionResult.Err.DetailedError))
|
c.LogError(model.NewAppError("ServeHTTP", "Invalid session", "token="+token+", err="+sessionResult.Err.DetailedError))
|
||||||
} else {
|
} else {
|
||||||
session = sessionResult.Data.(*model.Session)
|
session = sessionResult.Data.(*model.Session)
|
||||||
}
|
}
|
||||||
@@ -142,7 +152,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if session == nil || session.IsExpired() {
|
if session == nil || session.IsExpired() {
|
||||||
c.RemoveSessionCookie(w)
|
c.RemoveSessionCookie(w)
|
||||||
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "id="+sessionId)
|
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "token="+token)
|
||||||
|
c.Err.StatusCode = http.StatusUnauthorized
|
||||||
|
} else if !session.IsOAuth && isTokenFromQueryString {
|
||||||
|
c.Err = model.NewAppError("ServeHTTP", "Session is not OAuth but token was provided in the query string", "token="+token)
|
||||||
c.Err.StatusCode = http.StatusUnauthorized
|
c.Err.StatusCode = http.StatusUnauthorized
|
||||||
} else {
|
} else {
|
||||||
c.Session = *session
|
c.Session = *session
|
||||||
@@ -166,10 +179,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
c.SystemAdminRequired()
|
c.SystemAdminRequired()
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Err == nil && h.isUserActivity && sessionId != "" && len(c.Session.UserId) > 0 {
|
if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, sessionId, model.GetMillis())).Err; err != nil {
|
if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, c.Session.Id, model.GetMillis())).Err; err != nil {
|
||||||
l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", c.Session.UserId, sessionId, err)
|
l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", c.Session.UserId, c.Session.Id, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -197,7 +210,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) LogAudit(extraInfo string) {
|
func (c *Context) LogAudit(extraInfo string) {
|
||||||
audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId}
|
audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id}
|
||||||
if r := <-Srv.Store.Audit().Save(audit); r.Err != nil {
|
if r := <-Srv.Store.Audit().Save(audit); r.Err != nil {
|
||||||
c.LogError(r.Err)
|
c.LogError(r.Err)
|
||||||
}
|
}
|
||||||
@@ -209,7 +222,7 @@ func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
|
|||||||
extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId)
|
extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId}
|
audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id}
|
||||||
if r := <-Srv.Store.Audit().Save(audit); r.Err != nil {
|
if r := <-Srv.Store.Audit().Save(audit); r.Err != nil {
|
||||||
c.LogError(r.Err)
|
c.LogError(r.Err)
|
||||||
}
|
}
|
||||||
@@ -315,7 +328,7 @@ func (c *Context) IsTeamAdmin(userId string) bool {
|
|||||||
|
|
||||||
func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
|
func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
|
||||||
|
|
||||||
sessionCache.Remove(c.Session.Id)
|
sessionCache.Remove(c.Session.Token)
|
||||||
|
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: model.SESSION_TOKEN,
|
Name: model.SESSION_TOKEN,
|
||||||
@@ -471,3 +484,7 @@ func Handle404(w http.ResponseWriter, r *http.Request) {
|
|||||||
l4g.Error("%v: code=404 ip=%v", r.URL.Path, GetIpAddress(r))
|
l4g.Error("%v: code=404 ip=%v", r.URL.Path, GetIpAddress(r))
|
||||||
RenderWebError(err, w, r)
|
RenderWebError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddSessionToCache(session *model.Session) {
|
||||||
|
sessionCache.Add(session.Token, session)
|
||||||
|
}
|
||||||
|
|||||||
165
api/oauth.go
Обычный файл
165
api/oauth.go
Обычный файл
@@ -0,0 +1,165 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
l4g "code.google.com/p/log4go"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
"github.com/mattermost/platform/utils"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitOAuth(r *mux.Router) {
|
||||||
|
l4g.Debug("Initializing oauth api routes")
|
||||||
|
|
||||||
|
sr := r.PathPrefix("/oauth").Subrouter()
|
||||||
|
|
||||||
|
sr.Handle("/register", ApiUserRequired(registerOAuthApp)).Methods("POST")
|
||||||
|
sr.Handle("/allow", ApiUserRequired(allowOAuth)).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
c.Err = model.NewAppError("registerOAuthApp", "The system admin has turned off OAuth service providing.", "")
|
||||||
|
c.Err.StatusCode = http.StatusNotImplemented
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app := model.OAuthAppFromJson(r.Body)
|
||||||
|
|
||||||
|
if app == nil {
|
||||||
|
c.SetInvalidParam("registerOAuthApp", "app")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := model.NewId()
|
||||||
|
|
||||||
|
app.ClientSecret = secret
|
||||||
|
app.CreatorId = c.Session.UserId
|
||||||
|
|
||||||
|
if result := <-Srv.Store.OAuth().SaveApp(app); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
app = result.Data.(*model.OAuthApp)
|
||||||
|
app.ClientSecret = secret
|
||||||
|
|
||||||
|
c.LogAudit("client_id=" + app.Id)
|
||||||
|
|
||||||
|
w.Write([]byte(app.ToJson()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
c.Err = model.NewAppError("allowOAuth", "The system admin has turned off OAuth service providing.", "")
|
||||||
|
c.Err.StatusCode = http.StatusNotImplemented
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LogAudit("attempt")
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
responseData := map[string]string{}
|
||||||
|
|
||||||
|
responseType := r.URL.Query().Get("response_type")
|
||||||
|
if len(responseType) == 0 {
|
||||||
|
c.Err = model.NewAppError("allowOAuth", "invalid_request: Bad response_type", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientId := r.URL.Query().Get("client_id")
|
||||||
|
if len(clientId) != 26 {
|
||||||
|
c.Err = model.NewAppError("allowOAuth", "invalid_request: Bad client_id", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectUri := r.URL.Query().Get("redirect_uri")
|
||||||
|
if len(redirectUri) == 0 {
|
||||||
|
c.Err = model.NewAppError("allowOAuth", "invalid_request: Missing or bad redirect_uri", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := r.URL.Query().Get("scope")
|
||||||
|
state := r.URL.Query().Get("state")
|
||||||
|
|
||||||
|
var app *model.OAuthApp
|
||||||
|
if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
|
||||||
|
c.Err = model.NewAppError("allowOAuth", "server_error: Error accessing the database", "")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
app = result.Data.(*model.OAuthApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !app.IsValidRedirectURL(redirectUri) {
|
||||||
|
c.LogAudit("fail - redirect_uri did not match registered callback")
|
||||||
|
c.Err = model.NewAppError("allowOAuth", "invalid_request: Supplied redirect_uri did not match registered callback_url", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseType != model.AUTHCODE_RESPONSE_TYPE {
|
||||||
|
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}
|
||||||
|
authData.Code = model.HashPassword(fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, c.Session.UserId))
|
||||||
|
|
||||||
|
if result := <-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 RevokeAccessToken(token string) *model.AppError {
|
||||||
|
|
||||||
|
schan := Srv.Store.Session().Remove(token)
|
||||||
|
sessionCache.Remove(token)
|
||||||
|
|
||||||
|
var accessData *model.AccessData
|
||||||
|
if result := <-Srv.Store.OAuth().GetAccessData(token); result.Err != nil {
|
||||||
|
return model.NewAppError("RevokeAccessToken", "Error getting access token from DB before deletion", "")
|
||||||
|
} else {
|
||||||
|
accessData = result.Data.(*model.AccessData)
|
||||||
|
}
|
||||||
|
|
||||||
|
tchan := Srv.Store.OAuth().RemoveAccessData(token)
|
||||||
|
cchan := Srv.Store.OAuth().RemoveAuthData(accessData.AuthCode)
|
||||||
|
|
||||||
|
if result := <-tchan; result.Err != nil {
|
||||||
|
return model.NewAppError("RevokeAccessToken", "Error deleting access token from DB", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-cchan; result.Err != nil {
|
||||||
|
return model.NewAppError("RevokeAccessToken", "Error deleting authorization code from DB", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-schan; result.Err != nil {
|
||||||
|
return model.NewAppError("RevokeAccessToken", "Error deleting session from DB", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAuthData(code string) *model.AuthData {
|
||||||
|
if result := <-Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
|
||||||
|
l4g.Error("Couldn't find auth code for code=%s", code)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return result.Data.(*model.AuthData)
|
||||||
|
}
|
||||||
|
}
|
||||||
157
api/oauth_test.go
Обычный файл
157
api/oauth_test.go
Обычный файл
@@ -0,0 +1,157 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
"github.com/mattermost/platform/store"
|
||||||
|
"github.com/mattermost/platform/utils"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterApp(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||||
|
rteam, _ := Client.CreateTeam(&team)
|
||||||
|
|
||||||
|
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Password: "pwd"}
|
||||||
|
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
|
||||||
|
store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
|
||||||
|
|
||||||
|
app := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||||
|
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
|
||||||
|
if _, err := Client.RegisterApp(app); err == nil {
|
||||||
|
t.Fatal("should have failed - oauth providing turned off")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Client.Logout()
|
||||||
|
|
||||||
|
if _, err := Client.RegisterApp(app); err == nil {
|
||||||
|
t.Fatal("not logged in - should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Must(Client.LoginById(ruser.Id, "pwd"))
|
||||||
|
|
||||||
|
if result, err := Client.RegisterApp(app); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
rapp := result.Data.(*model.OAuthApp)
|
||||||
|
if len(rapp.Id) != 26 {
|
||||||
|
t.Fatal("clientid didn't return properly")
|
||||||
|
}
|
||||||
|
if len(rapp.ClientSecret) != 26 {
|
||||||
|
t.Fatal("client secret didn't return properly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app = &model.OAuthApp{Name: "", Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||||
|
if _, err := Client.RegisterApp(app); err == nil {
|
||||||
|
t.Fatal("missing name - should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
app = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||||
|
if _, err := Client.RegisterApp(app); err == nil {
|
||||||
|
t.Fatal("missing homepage - should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
app = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{}}
|
||||||
|
if _, err := Client.RegisterApp(app); err == nil {
|
||||||
|
t.Fatal("missing callback url - should have failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllowOAuth(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||||
|
rteam, _ := Client.CreateTeam(&team)
|
||||||
|
|
||||||
|
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Password: "pwd"}
|
||||||
|
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
|
||||||
|
store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
|
||||||
|
|
||||||
|
app := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||||
|
|
||||||
|
Client.Must(Client.LoginById(ruser.Id, "pwd"))
|
||||||
|
|
||||||
|
state := "123"
|
||||||
|
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "12345678901234567890123456", app.CallbackUrls[0], "all", state); err == nil {
|
||||||
|
t.Fatal("should have failed - oauth service providing turned off")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app = Client.Must(Client.RegisterApp(app)).Data.(*model.OAuthApp)
|
||||||
|
|
||||||
|
if result, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, app.Id, app.CallbackUrls[0], "all", state); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
redirect := result.Data.(map[string]string)["redirect"]
|
||||||
|
if len(redirect) == 0 {
|
||||||
|
t.Fatal("redirect url should be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
ru, _ := url.Parse(redirect)
|
||||||
|
if ru == nil {
|
||||||
|
t.Fatal("redirect url unparseable")
|
||||||
|
} else {
|
||||||
|
if len(ru.Query().Get("code")) == 0 {
|
||||||
|
t.Fatal("authorization code not returned")
|
||||||
|
}
|
||||||
|
if ru.Query().Get("state") != state {
|
||||||
|
t.Fatal("returned state doesn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, app.Id, "", "all", state); err == nil {
|
||||||
|
t.Fatal("should have failed - no redirect_url given")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, app.Id, "", "", state); err == nil {
|
||||||
|
t.Fatal("should have failed - no redirect_url given")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err := Client.AllowOAuth("junk", app.Id, app.CallbackUrls[0], "all", state); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
redirect := result.Data.(map[string]string)["redirect"]
|
||||||
|
if len(redirect) == 0 {
|
||||||
|
t.Fatal("redirect url should be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
ru, _ := url.Parse(redirect)
|
||||||
|
if ru == nil {
|
||||||
|
t.Fatal("redirect url unparseable")
|
||||||
|
} else {
|
||||||
|
if ru.Query().Get("error") != "unsupported_response_type" {
|
||||||
|
t.Fatal("wrong error returned")
|
||||||
|
}
|
||||||
|
if ru.Query().Get("state") != state {
|
||||||
|
t.Fatal("returned state doesn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "", app.CallbackUrls[0], "all", state); err == nil {
|
||||||
|
t.Fatal("should have failed - empty client id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "junk", app.CallbackUrls[0], "all", state); err == nil {
|
||||||
|
t.Fatal("should have failed - bad client id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, app.Id, "https://somewhereelse.com", "all", state); err == nil {
|
||||||
|
t.Fatal("should have failed - redirect uri host does not match app host")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@ func TestCreatePost(t *testing.T) {
|
|||||||
t.Fatal("Should have been forbidden")
|
t.Fatal("Should have been forbidden")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = Client.DoPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
|
if _, err = Client.DoApiPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
|
||||||
t.Fatal("should have been an error")
|
t.Fatal("should have been an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ func TestCreateValetPost(t *testing.T) {
|
|||||||
t.Fatal("Should have been forbidden")
|
t.Fatal("Should have been forbidden")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = Client.DoPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
|
if _, err = Client.DoApiPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
|
||||||
t.Fatal("should have been an error")
|
t.Fatal("should have been an error")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func TestCreateTeam(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := Client.DoPost("/teams/create", "garbage"); err == nil {
|
if _, err := Client.DoApiPost("/teams/create", "garbage"); err == nil {
|
||||||
t.Fatal("should have been an error")
|
t.Fatal("should have been an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
api/user.go
42
api/user.go
@@ -336,7 +336,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, DeviceId: deviceId}
|
session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false}
|
||||||
|
|
||||||
maxAge := model.SESSION_TIME_WEB_IN_SECS
|
maxAge := model.SESSION_TIME_WEB_IN_SECS
|
||||||
|
|
||||||
@@ -378,13 +378,13 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
|
|||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
session = result.Data.(*model.Session)
|
session = result.Data.(*model.Session)
|
||||||
sessionCache.Add(session.Id, session)
|
AddSessionToCache(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set(model.HEADER_TOKEN, session.Id)
|
w.Header().Set(model.HEADER_TOKEN, session.Token)
|
||||||
sessionCookie := &http.Cookie{
|
sessionCookie := &http.Cookie{
|
||||||
Name: model.SESSION_TOKEN,
|
Name: model.SESSION_TOKEN,
|
||||||
Value: session.Id,
|
Value: session.Token,
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: maxAge,
|
MaxAge: maxAge,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
@@ -430,25 +430,27 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) {
|
func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
props := model.MapFromJson(r.Body)
|
props := model.MapFromJson(r.Body)
|
||||||
altId := props["id"]
|
id := props["id"]
|
||||||
|
|
||||||
if result := <-Srv.Store.Session().GetSessions(c.Session.UserId); result.Err != nil {
|
if result := <-Srv.Store.Session().Get(id); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
sessions := result.Data.([]*model.Session)
|
session := result.Data.(*model.Session)
|
||||||
|
|
||||||
for _, session := range sessions {
|
c.LogAudit("session_id=" + session.Id)
|
||||||
if session.AltId == altId {
|
|
||||||
c.LogAudit("session_id=" + session.AltId)
|
if session.IsOAuth {
|
||||||
sessionCache.Remove(session.Id)
|
RevokeAccessToken(session.Token)
|
||||||
if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
|
} else {
|
||||||
c.Err = result.Err
|
sessionCache.Remove(session.Token)
|
||||||
return
|
|
||||||
} else {
|
if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
|
||||||
w.Write([]byte(model.MapToJson(props)))
|
c.Err = result.Err
|
||||||
return
|
return
|
||||||
}
|
} else {
|
||||||
|
w.Write([]byte(model.MapToJson(props)))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,8 +464,8 @@ func RevokeAllSession(c *Context, userId string) {
|
|||||||
sessions := result.Data.([]*model.Session)
|
sessions := result.Data.([]*model.Session)
|
||||||
|
|
||||||
for _, session := range sessions {
|
for _, session := range sessions {
|
||||||
c.LogAuditWithUserId(userId, "session_id="+session.AltId)
|
c.LogAuditWithUserId(userId, "session_id="+session.Id)
|
||||||
sessionCache.Remove(session.Id)
|
sessionCache.Remove(session.Token)
|
||||||
if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
|
if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func TestCreateUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := Client.DoPost("/users/create", "garbage"); err == nil {
|
if _, err := Client.DoApiPost("/users/create", "garbage"); err == nil {
|
||||||
t.Fatal("should have been an error")
|
t.Fatal("should have been an error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,11 +190,11 @@ func TestSessions(t *testing.T) {
|
|||||||
|
|
||||||
for _, session := range sessions {
|
for _, session := range sessions {
|
||||||
if session.DeviceId == deviceId {
|
if session.DeviceId == deviceId {
|
||||||
otherSession = session.AltId
|
otherSession = session.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(session.Id) != 0 {
|
if len(session.Token) != 0 {
|
||||||
t.Fatal("shouldn't return sessions")
|
t.Fatal("shouldn't return session tokens")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,11 +212,6 @@ func TestSessions(t *testing.T) {
|
|||||||
if len(sessions2) != 1 {
|
if len(sessions2) != 1 {
|
||||||
t.Fatal("invalid number of sessions")
|
t.Fatal("invalid number of sessions")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := Client.RevokeSession(otherSession); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetUser(t *testing.T) {
|
func TestGetUser(t *testing.T) {
|
||||||
@@ -355,7 +350,7 @@ func TestUserCreateImage(t *testing.T) {
|
|||||||
|
|
||||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||||
|
|
||||||
Client.DoGet("/users/"+user.Id+"/image", "", "")
|
Client.DoApiGet("/users/"+user.Id+"/image", "", "")
|
||||||
|
|
||||||
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
|
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
|
||||||
var auth aws.Auth
|
var auth aws.Auth
|
||||||
@@ -453,7 +448,7 @@ func TestUserUploadProfileImage(t *testing.T) {
|
|||||||
t.Fatal(upErr)
|
t.Fatal(upErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.DoGet("/users/"+user.Id+"/image", "", "")
|
Client.DoApiGet("/users/"+user.Id+"/image", "", "")
|
||||||
|
|
||||||
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
|
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
|
||||||
var auth aws.Auth
|
var auth aws.Auth
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"UseLocalStorage": true,
|
"UseLocalStorage": true,
|
||||||
"StorageDirectory": "./data/",
|
"StorageDirectory": "./data/",
|
||||||
"AllowedLoginAttempts": 10,
|
"AllowedLoginAttempts": 10,
|
||||||
"DisableEmailSignUp": false
|
"DisableEmailSignUp": false,
|
||||||
|
"EnableOAuthServiceProvider": false
|
||||||
},
|
},
|
||||||
"SSOSettings": {
|
"SSOSettings": {
|
||||||
"gitlab": {
|
"gitlab": {
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"UseLocalStorage": true,
|
"UseLocalStorage": true,
|
||||||
"StorageDirectory": "/mattermost/data/",
|
"StorageDirectory": "/mattermost/data/",
|
||||||
"AllowedLoginAttempts": 10,
|
"AllowedLoginAttempts": 10,
|
||||||
"DisableEmailSignUp": false
|
"DisableEmailSignUp": false,
|
||||||
|
"EnableOAuthServiceProvider": false
|
||||||
},
|
},
|
||||||
"SSOSettings": {
|
"SSOSettings": {
|
||||||
"gitlab": {
|
"gitlab": {
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"UseLocalStorage": true,
|
"UseLocalStorage": true,
|
||||||
"StorageDirectory": "/mattermost/data/",
|
"StorageDirectory": "/mattermost/data/",
|
||||||
"AllowedLoginAttempts": 10,
|
"AllowedLoginAttempts": 10,
|
||||||
"DisableEmailSignUp": false
|
"DisableEmailSignUp": false,
|
||||||
|
"EnableOAuthServiceProvider": false
|
||||||
},
|
},
|
||||||
"SSOSettings": {
|
"SSOSettings": {
|
||||||
"gitlab": {
|
"gitlab": {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a client for tests to use
|
// Create a client for tests to use
|
||||||
client := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1")
|
client := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port)
|
||||||
|
|
||||||
// Check for username parameter and create a user if present
|
// Check for username parameter and create a user if present
|
||||||
username, ok1 := params["username"]
|
username, ok1 := params["username"]
|
||||||
@@ -65,7 +65,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
// Create team for testing
|
// Create team for testing
|
||||||
team := &model.Team{
|
team := &model.Team{
|
||||||
DisplayName: teamDisplayName[0],
|
DisplayName: teamDisplayName[0],
|
||||||
Name: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
|
Name: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
|
||||||
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
|
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
|
||||||
Type: model.TEAM_OPEN,
|
Type: model.TEAM_OPEN,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,69 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ACCESS_TOKEN_GRANT_TYPE = "authorization_code"
|
ACCESS_TOKEN_GRANT_TYPE = "authorization_code"
|
||||||
ACCESS_TOKEN_TYPE = "bearer"
|
ACCESS_TOKEN_TYPE = "bearer"
|
||||||
|
REFRESH_TOKEN_GRANT_TYPE = "refresh_token"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AccessData struct {
|
||||||
|
AuthCode string `json:"auth_code"`
|
||||||
|
Token string `json"token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
RedirectUri string `json:"redirect_uri"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessResponse struct {
|
type AccessResponse struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
ExpiresIn int32 `json:"expires_in"`
|
ExpiresIn int32 `json:"expires_in"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValid validates the AccessData and returns an error if it isn't configured
|
||||||
|
// correctly.
|
||||||
|
func (ad *AccessData) IsValid() *AppError {
|
||||||
|
|
||||||
|
if len(ad.AuthCode) == 0 || len(ad.AuthCode) > 128 {
|
||||||
|
return NewAppError("AccessData.IsValid", "Invalid auth code", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.Token) != 26 {
|
||||||
|
return NewAppError("AccessData.IsValid", "Invalid access token", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.RefreshToken) > 26 {
|
||||||
|
return NewAppError("AccessData.IsValid", "Invalid refresh token", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.RedirectUri) > 256 {
|
||||||
|
return NewAppError("AccessData.IsValid", "Invalid redirect uri", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ad *AccessData) ToJson() string {
|
||||||
|
b, err := json.Marshal(ad)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AccessDataFromJson(data io.Reader) *AccessData {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var ad AccessData
|
||||||
|
err := decoder.Decode(&ad)
|
||||||
|
if err == nil {
|
||||||
|
return &ad
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ar *AccessResponse) ToJson() string {
|
func (ar *AccessResponse) ToJson() string {
|
||||||
b, err := json.Marshal(ar)
|
b, err := json.Marshal(ar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
41
model/access_test.go
Обычный файл
41
model/access_test.go
Обычный файл
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccessJson(t *testing.T) {
|
||||||
|
a1 := AccessData{}
|
||||||
|
a1.AuthCode = NewId()
|
||||||
|
a1.Token = NewId()
|
||||||
|
a1.RefreshToken = NewId()
|
||||||
|
|
||||||
|
json := a1.ToJson()
|
||||||
|
ra1 := AccessDataFromJson(strings.NewReader(json))
|
||||||
|
|
||||||
|
if a1.Token != ra1.Token {
|
||||||
|
t.Fatal("tokens didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccessIsValid(t *testing.T) {
|
||||||
|
ad := AccessData{}
|
||||||
|
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal("should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.AuthCode = NewId()
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal("should have failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.Token = NewId()
|
||||||
|
if err := ad.IsValid(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
103
model/authorize.go
Обычный файл
103
model/authorize.go
Обычный файл
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AUTHCODE_EXPIRE_TIME = 60 * 10 // 10 minutes
|
||||||
|
AUTHCODE_RESPONSE_TYPE = "code"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthData struct {
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
ExpiresIn int32 `json:"expires_in"`
|
||||||
|
CreateAt int64 `json:"create_at"`
|
||||||
|
RedirectUri string `json:"redirect_uri"`
|
||||||
|
State string `json:"state"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid validates the AuthData and returns an error if it isn't configured
|
||||||
|
// correctly.
|
||||||
|
func (ad *AuthData) IsValid() *AppError {
|
||||||
|
|
||||||
|
if len(ad.ClientId) != 26 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Invalid client id", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.UserId) != 26 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Invalid user id", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.Code) == 0 || len(ad.Code) > 128 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Invalid authorization code", "client_id="+ad.ClientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ad.ExpiresIn == 0 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Expires in must be set", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ad.CreateAt <= 0 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Create at must be a valid time", "client_id="+ad.ClientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.RedirectUri) > 256 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Invalid redirect uri", "client_id="+ad.ClientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.State) > 128 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Invalid state", "client_id="+ad.ClientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ad.Scope) > 128 {
|
||||||
|
return NewAppError("AuthData.IsValid", "Invalid scope", "client_id="+ad.ClientId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ad *AuthData) PreSave() {
|
||||||
|
if ad.ExpiresIn == 0 {
|
||||||
|
ad.ExpiresIn = AUTHCODE_EXPIRE_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
if ad.CreateAt == 0 {
|
||||||
|
ad.CreateAt = GetMillis()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ad *AuthData) ToJson() string {
|
||||||
|
b, err := json.Marshal(ad)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthDataFromJson(data io.Reader) *AuthData {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var ad AuthData
|
||||||
|
err := decoder.Decode(&ad)
|
||||||
|
if err == nil {
|
||||||
|
return &ad
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ad *AuthData) IsExpired() bool {
|
||||||
|
|
||||||
|
if GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
66
model/authorize_test.go
Обычный файл
66
model/authorize_test.go
Обычный файл
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthJson(t *testing.T) {
|
||||||
|
a1 := AuthData{}
|
||||||
|
a1.ClientId = NewId()
|
||||||
|
a1.UserId = NewId()
|
||||||
|
a1.Code = NewId()
|
||||||
|
|
||||||
|
json := a1.ToJson()
|
||||||
|
ra1 := AuthDataFromJson(strings.NewReader(json))
|
||||||
|
|
||||||
|
if a1.Code != ra1.Code {
|
||||||
|
t.Fatal("codes didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthPreSave(t *testing.T) {
|
||||||
|
a1 := AuthData{}
|
||||||
|
a1.ClientId = NewId()
|
||||||
|
a1.UserId = NewId()
|
||||||
|
a1.Code = NewId()
|
||||||
|
a1.PreSave()
|
||||||
|
a1.IsExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthIsValid(t *testing.T) {
|
||||||
|
|
||||||
|
ad := AuthData{}
|
||||||
|
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.ClientId = NewId()
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.UserId = NewId()
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.Code = NewId()
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.ExpiresIn = 1
|
||||||
|
if err := ad.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.CreateAt = 1
|
||||||
|
if err := ad.IsValid(); err != nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
192
model/client.go
192
model/client.go
@@ -23,7 +23,9 @@ const (
|
|||||||
HEADER_FORWARDED = "X-Forwarded-For"
|
HEADER_FORWARDED = "X-Forwarded-For"
|
||||||
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
|
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
|
||||||
HEADER_TOKEN = "token"
|
HEADER_TOKEN = "token"
|
||||||
|
HEADER_BEARER = "BEARER"
|
||||||
HEADER_AUTH = "Authorization"
|
HEADER_AUTH = "Authorization"
|
||||||
|
API_URL_SUFFIX = "/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
@@ -33,22 +35,37 @@ type Result struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
Url string // The location of the server like "http://localhost/api/v1"
|
Url string // The location of the server like "http://localhost:8065"
|
||||||
|
ApiUrl string // The api location of the server like "http://localhost:8065/api/v1"
|
||||||
HttpClient *http.Client // The http client
|
HttpClient *http.Client // The http client
|
||||||
AuthToken string
|
AuthToken string
|
||||||
|
AuthType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient constructs a new client with convienence methods for talking to
|
// NewClient constructs a new client with convienence methods for talking to
|
||||||
// the server.
|
// the server.
|
||||||
func NewClient(url string) *Client {
|
func NewClient(url string) *Client {
|
||||||
return &Client{url, &http.Client{}, ""}
|
return &Client{url, url + API_URL_SUFFIX, &http.Client{}, "", ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoPost(url string, data string) (*http.Response, *AppError) {
|
func (c *Client) DoPost(url string, data, contentType string) (*http.Response, *AppError) {
|
||||||
rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data))
|
rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data))
|
||||||
|
rq.Header.Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||||
|
return nil, NewAppError(url, "We encountered an error while connecting to the server", err.Error())
|
||||||
|
} else if rp.StatusCode >= 300 {
|
||||||
|
return nil, AppErrorFromJson(rp.Body)
|
||||||
|
} else {
|
||||||
|
return rp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DoApiPost(url string, data string) (*http.Response, *AppError) {
|
||||||
|
rq, _ := http.NewRequest("POST", c.ApiUrl+url, strings.NewReader(data))
|
||||||
|
|
||||||
if len(c.AuthToken) > 0 {
|
if len(c.AuthToken) > 0 {
|
||||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||||
@@ -60,15 +77,15 @@ func (c *Client) DoPost(url string, data string) (*http.Response, *AppError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoGet(url string, data string, etag string) (*http.Response, *AppError) {
|
func (c *Client) DoApiGet(url string, data string, etag string) (*http.Response, *AppError) {
|
||||||
rq, _ := http.NewRequest("GET", c.Url+url, strings.NewReader(data))
|
rq, _ := http.NewRequest("GET", c.ApiUrl+url, strings.NewReader(data))
|
||||||
|
|
||||||
if len(etag) > 0 {
|
if len(etag) > 0 {
|
||||||
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
|
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.AuthToken) > 0 {
|
if len(c.AuthToken) > 0 {
|
||||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||||
@@ -106,7 +123,7 @@ func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppErro
|
|||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
m["email"] = email
|
m["email"] = email
|
||||||
m["display_name"] = displayName
|
m["display_name"] = displayName
|
||||||
if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/teams/signup", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -115,7 +132,7 @@ func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppError) {
|
func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -124,7 +141,7 @@ func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppErro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
|
func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/teams/create", team.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/teams/create", team.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -136,7 +153,7 @@ func (c *Client) FindTeamByName(name string, allServers bool) (*Result, *AppErro
|
|||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
m["name"] = name
|
m["name"] = name
|
||||||
m["all"] = fmt.Sprintf("%v", allServers)
|
m["all"] = fmt.Sprintf("%v", allServers)
|
||||||
if r, err := c.DoPost("/teams/find_team_by_name", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/teams/find_team_by_name", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
val := false
|
val := false
|
||||||
@@ -152,7 +169,7 @@ func (c *Client) FindTeamByName(name string, allServers bool) (*Result, *AppErro
|
|||||||
func (c *Client) FindTeams(email string) (*Result, *AppError) {
|
func (c *Client) FindTeams(email string) (*Result, *AppError) {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
m["email"] = email
|
m["email"] = email
|
||||||
if r, err := c.DoPost("/teams/find_teams", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/teams/find_teams", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -164,7 +181,7 @@ func (c *Client) FindTeams(email string) (*Result, *AppError) {
|
|||||||
func (c *Client) FindTeamsSendEmail(email string) (*Result, *AppError) {
|
func (c *Client) FindTeamsSendEmail(email string) (*Result, *AppError) {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
m["email"] = email
|
m["email"] = email
|
||||||
if r, err := c.DoPost("/teams/email_teams", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/teams/email_teams", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -173,7 +190,7 @@ func (c *Client) FindTeamsSendEmail(email string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
|
func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/teams/invite_members", invites.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/teams/invite_members", invites.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -182,7 +199,7 @@ func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppError) {
|
func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/teams/update_name", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/teams/update_name", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -191,7 +208,7 @@ func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppErr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateValetFeature(data map[string]string) (*Result, *AppError) {
|
func (c *Client) UpdateValetFeature(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/teams/update_valet_feature", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/teams/update_valet_feature", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -200,7 +217,7 @@ func (c *Client) UpdateValetFeature(data map[string]string) (*Result, *AppError)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
|
func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/create", user.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/users/create", user.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -209,7 +226,7 @@ func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Result, *AppError) {
|
func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/create?d="+data+"&h="+hash, user.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/users/create?d="+data+"&h="+hash, user.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -218,7 +235,7 @@ func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
|
func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/users/"+id, "", etag); err != nil {
|
if r, err := c.DoApiGet("/users/"+id, "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -227,7 +244,7 @@ func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetMe(etag string) (*Result, *AppError) {
|
func (c *Client) GetMe(etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/users/me", "", etag); err != nil {
|
if r, err := c.DoApiGet("/users/me", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -236,7 +253,7 @@ func (c *Client) GetMe(etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) {
|
func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/users/profiles", "", etag); err != nil {
|
if r, err := c.DoApiGet("/users/profiles", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -269,13 +286,14 @@ func (c *Client) LoginByEmailWithDevice(name string, email string, password stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) login(m map[string]string) (*Result, *AppError) {
|
func (c *Client) login(m map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/login", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
c.AuthToken = r.Header.Get(HEADER_TOKEN)
|
c.AuthToken = r.Header.Get(HEADER_TOKEN)
|
||||||
sessionId := getCookie(SESSION_TOKEN, r)
|
c.AuthType = HEADER_BEARER
|
||||||
|
sessionToken := getCookie(SESSION_TOKEN, r)
|
||||||
|
|
||||||
if c.AuthToken != sessionId.Value {
|
if c.AuthToken != sessionToken.Value {
|
||||||
NewAppError("/users/login", "Authentication tokens didn't match", "")
|
NewAppError("/users/login", "Authentication tokens didn't match", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,21 +303,32 @@ func (c *Client) login(m map[string]string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Logout() (*Result, *AppError) {
|
func (c *Client) Logout() (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/logout", ""); err != nil {
|
if r, err := c.DoApiPost("/users/logout", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
c.AuthToken = ""
|
c.AuthToken = ""
|
||||||
|
c.AuthType = HEADER_BEARER
|
||||||
|
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) SetOAuthToken(token string) {
|
||||||
|
c.AuthToken = token
|
||||||
|
c.AuthType = HEADER_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ClearOAuthToken() {
|
||||||
|
c.AuthToken = ""
|
||||||
|
c.AuthType = HEADER_BEARER
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) RevokeSession(sessionAltId string) (*Result, *AppError) {
|
func (c *Client) RevokeSession(sessionAltId string) (*Result, *AppError) {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
m["id"] = sessionAltId
|
m["id"] = sessionAltId
|
||||||
|
|
||||||
if r, err := c.DoPost("/users/revoke_session", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/users/revoke_session", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -308,7 +337,7 @@ func (c *Client) RevokeSession(sessionAltId string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetSessions(id string) (*Result, *AppError) {
|
func (c *Client) GetSessions(id string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/users/"+id+"/sessions", "", ""); err != nil {
|
if r, err := c.DoApiGet("/users/"+id+"/sessions", "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -321,7 +350,7 @@ func (c *Client) Command(channelId string, command string, suggest bool) (*Resul
|
|||||||
m["command"] = command
|
m["command"] = command
|
||||||
m["channelId"] = channelId
|
m["channelId"] = channelId
|
||||||
m["suggest"] = strconv.FormatBool(suggest)
|
m["suggest"] = strconv.FormatBool(suggest)
|
||||||
if r, err := c.DoPost("/command", MapToJson(m)); err != nil {
|
if r, err := c.DoApiPost("/command", MapToJson(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -330,7 +359,7 @@ func (c *Client) Command(channelId string, command string, suggest bool) (*Resul
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
|
func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/users/"+id+"/audits", "", etag); err != nil {
|
if r, err := c.DoApiGet("/users/"+id+"/audits", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -339,7 +368,7 @@ func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetLogs() (*Result, *AppError) {
|
func (c *Client) GetLogs() (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/admin/logs", "", ""); err != nil {
|
if r, err := c.DoApiGet("/admin/logs", "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -348,7 +377,7 @@ func (c *Client) GetLogs() (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetClientProperties() (*Result, *AppError) {
|
func (c *Client) GetClientProperties() (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/admin/client_props", "", ""); err != nil {
|
if r, err := c.DoApiGet("/admin/client_props", "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -357,7 +386,7 @@ func (c *Client) GetClientProperties() (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
|
func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/create", channel.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/channels/create", channel.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -366,7 +395,7 @@ func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateDirectChannel(data map[string]string) (*Result, *AppError) {
|
func (c *Client) CreateDirectChannel(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/create_direct", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/channels/create_direct", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -375,7 +404,7 @@ func (c *Client) CreateDirectChannel(data map[string]string) (*Result, *AppError
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/update", channel.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/channels/update", channel.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -384,7 +413,7 @@ func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateChannelDesc(data map[string]string) (*Result, *AppError) {
|
func (c *Client) UpdateChannelDesc(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/update_desc", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/channels/update_desc", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -393,7 +422,7 @@ func (c *Client) UpdateChannelDesc(data map[string]string) (*Result, *AppError)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateNotifyLevel(data map[string]string) (*Result, *AppError) {
|
func (c *Client) UpdateNotifyLevel(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/update_notify_level", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/channels/update_notify_level", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -402,7 +431,7 @@ func (c *Client) UpdateNotifyLevel(data map[string]string) (*Result, *AppError)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetChannels(etag string) (*Result, *AppError) {
|
func (c *Client) GetChannels(etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/channels/", "", etag); err != nil {
|
if r, err := c.DoApiGet("/channels/", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -411,7 +440,7 @@ func (c *Client) GetChannels(etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetChannel(id, etag string) (*Result, *AppError) {
|
func (c *Client) GetChannel(id, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/channels/"+id+"/", "", etag); err != nil {
|
if r, err := c.DoApiGet("/channels/"+id+"/", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -420,7 +449,7 @@ func (c *Client) GetChannel(id, etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) {
|
func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/channels/more", "", etag); err != nil {
|
if r, err := c.DoApiGet("/channels/more", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -429,7 +458,7 @@ func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
|
func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/channels/counts", "", etag); err != nil {
|
if r, err := c.DoApiGet("/channels/counts", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -438,7 +467,7 @@ func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) JoinChannel(id string) (*Result, *AppError) {
|
func (c *Client) JoinChannel(id string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+id+"/join", ""); err != nil {
|
if r, err := c.DoApiPost("/channels/"+id+"/join", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -447,7 +476,7 @@ func (c *Client) JoinChannel(id string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) LeaveChannel(id string) (*Result, *AppError) {
|
func (c *Client) LeaveChannel(id string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+id+"/leave", ""); err != nil {
|
if r, err := c.DoApiPost("/channels/"+id+"/leave", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -456,7 +485,7 @@ func (c *Client) LeaveChannel(id string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteChannel(id string) (*Result, *AppError) {
|
func (c *Client) DeleteChannel(id string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+id+"/delete", ""); err != nil {
|
if r, err := c.DoApiPost("/channels/"+id+"/delete", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -467,7 +496,7 @@ func (c *Client) DeleteChannel(id string) (*Result, *AppError) {
|
|||||||
func (c *Client) AddChannelMember(id, user_id string) (*Result, *AppError) {
|
func (c *Client) AddChannelMember(id, user_id string) (*Result, *AppError) {
|
||||||
data := make(map[string]string)
|
data := make(map[string]string)
|
||||||
data["user_id"] = user_id
|
data["user_id"] = user_id
|
||||||
if r, err := c.DoPost("/channels/"+id+"/add", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/channels/"+id+"/add", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -478,7 +507,7 @@ func (c *Client) AddChannelMember(id, user_id string) (*Result, *AppError) {
|
|||||||
func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
|
func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
|
||||||
data := make(map[string]string)
|
data := make(map[string]string)
|
||||||
data["user_id"] = user_id
|
data["user_id"] = user_id
|
||||||
if r, err := c.DoPost("/channels/"+id+"/remove", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/channels/"+id+"/remove", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -487,7 +516,7 @@ func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateLastViewedAt(channelId string) (*Result, *AppError) {
|
func (c *Client) UpdateLastViewedAt(channelId string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+channelId+"/update_last_viewed_at", ""); err != nil {
|
if r, err := c.DoApiPost("/channels/"+channelId+"/update_last_viewed_at", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -496,7 +525,7 @@ func (c *Client) UpdateLastViewedAt(channelId string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetChannelExtraInfo(id string, etag string) (*Result, *AppError) {
|
func (c *Client) GetChannelExtraInfo(id string, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/channels/"+id+"/extra_info", "", etag); err != nil {
|
if r, err := c.DoApiGet("/channels/"+id+"/extra_info", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -505,7 +534,7 @@ func (c *Client) GetChannelExtraInfo(id string, etag string) (*Result, *AppError
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
|
func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+post.ChannelId+"/create", post.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/channels/"+post.ChannelId+"/create", post.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -514,7 +543,7 @@ func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CreateValetPost(post *Post) (*Result, *AppError) {
|
func (c *Client) CreateValetPost(post *Post) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+post.ChannelId+"/valet_create", post.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/channels/"+post.ChannelId+"/valet_create", post.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -523,7 +552,7 @@ func (c *Client) CreateValetPost(post *Post) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdatePost(post *Post) (*Result, *AppError) {
|
func (c *Client) UpdatePost(post *Post) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/channels/"+post.ChannelId+"/update", post.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/channels/"+post.ChannelId+"/update", post.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -532,7 +561,7 @@ func (c *Client) UpdatePost(post *Post) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetPosts(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
|
func (c *Client) GetPosts(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet(fmt.Sprintf("/channels/%v/posts/%v/%v", channelId, offset, limit), "", etag); err != nil {
|
if r, err := c.DoApiGet(fmt.Sprintf("/channels/%v/posts/%v/%v", channelId, offset, limit), "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -541,7 +570,7 @@ func (c *Client) GetPosts(channelId string, offset int, limit int, etag string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetPostsSince(channelId string, time int64) (*Result, *AppError) {
|
func (c *Client) GetPostsSince(channelId string, time int64) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet(fmt.Sprintf("/channels/%v/posts/%v", channelId, time), "", ""); err != nil {
|
if r, err := c.DoApiGet(fmt.Sprintf("/channels/%v/posts/%v", channelId, time), "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -550,7 +579,7 @@ func (c *Client) GetPostsSince(channelId string, time int64) (*Result, *AppError
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetPost(channelId string, postId string, etag string) (*Result, *AppError) {
|
func (c *Client) GetPost(channelId string, postId string, etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet(fmt.Sprintf("/channels/%v/post/%v", channelId, postId), "", etag); err != nil {
|
if r, err := c.DoApiGet(fmt.Sprintf("/channels/%v/post/%v", channelId, postId), "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -559,7 +588,7 @@ func (c *Client) GetPost(channelId string, postId string, etag string) (*Result,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
|
func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost(fmt.Sprintf("/channels/%v/post/%v/delete", channelId, postId), ""); err != nil {
|
if r, err := c.DoApiPost(fmt.Sprintf("/channels/%v/post/%v/delete", channelId, postId), ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -568,7 +597,7 @@ func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SearchPosts(terms string) (*Result, *AppError) {
|
func (c *Client) SearchPosts(terms string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/posts/search?terms="+url.QueryEscape(terms), "", ""); err != nil {
|
if r, err := c.DoApiGet("/posts/search?terms="+url.QueryEscape(terms), "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -577,7 +606,7 @@ func (c *Client) SearchPosts(terms string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UploadFile(url string, data []byte, contentType string) (*Result, *AppError) {
|
func (c *Client) UploadFile(url string, data []byte, contentType string) (*Result, *AppError) {
|
||||||
rq, _ := http.NewRequest("POST", c.Url+url, bytes.NewReader(data))
|
rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
|
||||||
rq.Header.Set("Content-Type", contentType)
|
rq.Header.Set("Content-Type", contentType)
|
||||||
|
|
||||||
if len(c.AuthToken) > 0 {
|
if len(c.AuthToken) > 0 {
|
||||||
@@ -599,7 +628,7 @@ func (c *Client) GetFile(url string, isFullUrl bool) (*Result, *AppError) {
|
|||||||
if isFullUrl {
|
if isFullUrl {
|
||||||
rq, _ = http.NewRequest("GET", url, nil)
|
rq, _ = http.NewRequest("GET", url, nil)
|
||||||
} else {
|
} else {
|
||||||
rq, _ = http.NewRequest("GET", c.Url+"/files/get"+url, nil)
|
rq, _ = http.NewRequest("GET", c.ApiUrl+"/files/get"+url, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.AuthToken) > 0 {
|
if len(c.AuthToken) > 0 {
|
||||||
@@ -618,7 +647,7 @@ func (c *Client) GetFile(url string, isFullUrl bool) (*Result, *AppError) {
|
|||||||
|
|
||||||
func (c *Client) GetFileInfo(url string) (*Result, *AppError) {
|
func (c *Client) GetFileInfo(url string) (*Result, *AppError) {
|
||||||
var rq *http.Request
|
var rq *http.Request
|
||||||
rq, _ = http.NewRequest("GET", c.Url+"/files/get_info"+url, nil)
|
rq, _ = http.NewRequest("GET", c.ApiUrl+"/files/get_info"+url, nil)
|
||||||
|
|
||||||
if len(c.AuthToken) > 0 {
|
if len(c.AuthToken) > 0 {
|
||||||
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
||||||
@@ -635,7 +664,7 @@ func (c *Client) GetFileInfo(url string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetPublicLink(data map[string]string) (*Result, *AppError) {
|
func (c *Client) GetPublicLink(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/files/get_public_link", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/files/get_public_link", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -644,7 +673,7 @@ func (c *Client) GetPublicLink(data map[string]string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
|
func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/update", user.ToJson()); err != nil {
|
if r, err := c.DoApiPost("/users/update", user.ToJson()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -653,7 +682,7 @@ func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateUserRoles(data map[string]string) (*Result, *AppError) {
|
func (c *Client) UpdateUserRoles(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/update_roles", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/users/update_roles", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -665,7 +694,7 @@ func (c *Client) UpdateActive(userId string, active bool) (*Result, *AppError) {
|
|||||||
data := make(map[string]string)
|
data := make(map[string]string)
|
||||||
data["user_id"] = userId
|
data["user_id"] = userId
|
||||||
data["active"] = strconv.FormatBool(active)
|
data["active"] = strconv.FormatBool(active)
|
||||||
if r, err := c.DoPost("/users/update_active", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/users/update_active", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -674,7 +703,7 @@ func (c *Client) UpdateActive(userId string, active bool) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateUserNotify(data map[string]string) (*Result, *AppError) {
|
func (c *Client) UpdateUserNotify(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/update_notify", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/users/update_notify", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -688,7 +717,7 @@ func (c *Client) UpdateUserPassword(userId, currentPassword, newPassword string)
|
|||||||
data["new_password"] = newPassword
|
data["new_password"] = newPassword
|
||||||
data["user_id"] = userId
|
data["user_id"] = userId
|
||||||
|
|
||||||
if r, err := c.DoPost("/users/newpassword", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/users/newpassword", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -697,7 +726,7 @@ func (c *Client) UpdateUserPassword(userId, currentPassword, newPassword string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SendPasswordReset(data map[string]string) (*Result, *AppError) {
|
func (c *Client) SendPasswordReset(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/send_password_reset", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/users/send_password_reset", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -706,7 +735,7 @@ func (c *Client) SendPasswordReset(data map[string]string) (*Result, *AppError)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ResetPassword(data map[string]string) (*Result, *AppError) {
|
func (c *Client) ResetPassword(data map[string]string) (*Result, *AppError) {
|
||||||
if r, err := c.DoPost("/users/reset_password", MapToJson(data)); err != nil {
|
if r, err := c.DoApiPost("/users/reset_password", MapToJson(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -715,7 +744,7 @@ func (c *Client) ResetPassword(data map[string]string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetStatuses() (*Result, *AppError) {
|
func (c *Client) GetStatuses() (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/users/status", "", ""); err != nil {
|
if r, err := c.DoApiGet("/users/status", "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -724,7 +753,7 @@ func (c *Client) GetStatuses() (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
|
func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
|
||||||
if r, err := c.DoGet("/teams/me", "", etag); err != nil {
|
if r, err := c.DoApiGet("/teams/me", "", etag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
@@ -732,6 +761,33 @@ func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiPost("/oauth/register", app.ToJson()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) AllowOAuth(rspType, clientId, redirect, scope, state string) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiGet("/oauth/allow?response_type="+rspType+"&client_id="+clientId+"&redirect_uri="+url.QueryEscape(redirect)+"&scope="+scope+"&state="+url.QueryEscape(state), "", ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetAccessToken(data url.Values) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoPost("/oauth/access_token", data.Encode(), "application/x-www-form-urlencoded"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), AccessResponseFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) MockSession(sessionToken string) {
|
func (c *Client) MockSession(sessionToken string) {
|
||||||
c.AuthToken = sessionToken
|
c.AuthToken = sessionToken
|
||||||
}
|
}
|
||||||
|
|||||||
151
model/oauth.go
Обычный файл
151
model/oauth.go
Обычный файл
@@ -0,0 +1,151 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OAuthApp struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreatorId string `json:"creator_id"`
|
||||||
|
CreateAt int64 `json:"update_at"`
|
||||||
|
UpdateAt int64 `json:"update_at"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
CallbackUrls StringArray `json:"callback_urls"`
|
||||||
|
Homepage string `json:"homepage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid validates the app and returns an error if it isn't configured
|
||||||
|
// correctly.
|
||||||
|
func (a *OAuthApp) IsValid() *AppError {
|
||||||
|
|
||||||
|
if len(a.Id) != 26 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid app id", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.CreateAt == 0 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Create at must be a valid time", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.UpdateAt == 0 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Update at must be a valid time", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.CreatorId) != 26 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid creator id", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.ClientSecret) == 0 || len(a.ClientSecret) > 128 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid client secret", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Name) == 0 || len(a.Name) > 64 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid name", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.CallbackUrls) == 0 || len(fmt.Sprintf("%s", a.CallbackUrls)) > 1024 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid callback urls", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Homepage) == 0 || len(a.Homepage) > 256 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid homepage", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a.Description) > 512 {
|
||||||
|
return NewAppError("OAuthApp.IsValid", "Invalid description", "app_id="+a.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreSave will set the Id and ClientSecret if missing. It will also fill
|
||||||
|
// in the CreateAt, UpdateAt times. It should be run before saving the app to the db.
|
||||||
|
func (a *OAuthApp) PreSave() {
|
||||||
|
if a.Id == "" {
|
||||||
|
a.Id = NewId()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.ClientSecret == "" {
|
||||||
|
a.ClientSecret = NewId()
|
||||||
|
}
|
||||||
|
|
||||||
|
a.CreateAt = GetMillis()
|
||||||
|
a.UpdateAt = a.CreateAt
|
||||||
|
|
||||||
|
if len(a.ClientSecret) > 0 {
|
||||||
|
a.ClientSecret = HashPassword(a.ClientSecret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreUpdate should be run before updating the app in the db.
|
||||||
|
func (a *OAuthApp) PreUpdate() {
|
||||||
|
a.UpdateAt = GetMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJson convert a User to a json string
|
||||||
|
func (a *OAuthApp) ToJson() string {
|
||||||
|
b, err := json.Marshal(a)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a valid strong etag so the browser can cache the results
|
||||||
|
func (a *OAuthApp) Etag() string {
|
||||||
|
return Etag(a.Id, a.UpdateAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any private data from the app object
|
||||||
|
func (a *OAuthApp) Sanitize() {
|
||||||
|
a.ClientSecret = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *OAuthApp) IsValidRedirectURL(url string) bool {
|
||||||
|
for _, u := range a.CallbackUrls {
|
||||||
|
if u == url {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// OAuthAppFromJson will decode the input and return a User
|
||||||
|
func OAuthAppFromJson(data io.Reader) *OAuthApp {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var app OAuthApp
|
||||||
|
err := decoder.Decode(&app)
|
||||||
|
if err == nil {
|
||||||
|
return &app
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OAuthAppMapToJson(a map[string]*OAuthApp) string {
|
||||||
|
b, err := json.Marshal(a)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OAuthAppMapFromJson(data io.Reader) map[string]*OAuthApp {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var apps map[string]*OAuthApp
|
||||||
|
err := decoder.Decode(&apps)
|
||||||
|
if err == nil {
|
||||||
|
return apps
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
95
model/oauth_test.go
Обычный файл
95
model/oauth_test.go
Обычный файл
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOAuthAppJson(t *testing.T) {
|
||||||
|
a1 := OAuthApp{}
|
||||||
|
a1.Id = NewId()
|
||||||
|
a1.Name = "TestOAuthApp" + NewId()
|
||||||
|
a1.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
a1.Homepage = "https://nowhere.com"
|
||||||
|
a1.ClientSecret = NewId()
|
||||||
|
|
||||||
|
json := a1.ToJson()
|
||||||
|
ra1 := OAuthAppFromJson(strings.NewReader(json))
|
||||||
|
|
||||||
|
if a1.Id != ra1.Id {
|
||||||
|
t.Fatal("ids did not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthAppPreSave(t *testing.T) {
|
||||||
|
a1 := OAuthApp{}
|
||||||
|
a1.Id = NewId()
|
||||||
|
a1.Name = "TestOAuthApp" + NewId()
|
||||||
|
a1.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
a1.Homepage = "https://nowhere.com"
|
||||||
|
a1.ClientSecret = NewId()
|
||||||
|
a1.PreSave()
|
||||||
|
a1.Etag()
|
||||||
|
a1.Sanitize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthAppPreUpdate(t *testing.T) {
|
||||||
|
a1 := OAuthApp{}
|
||||||
|
a1.Id = NewId()
|
||||||
|
a1.Name = "TestOAuthApp" + NewId()
|
||||||
|
a1.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
a1.Homepage = "https://nowhere.com"
|
||||||
|
a1.ClientSecret = NewId()
|
||||||
|
a1.PreUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthAppIsValid(t *testing.T) {
|
||||||
|
app := OAuthApp{}
|
||||||
|
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Id = NewId()
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.CreateAt = 1
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UpdateAt = 1
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.CreatorId = NewId()
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.ClientSecret = NewId()
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Name = "TestOAuthApp"
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
if err := app.IsValid(); err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Homepage = "https://nowhere.com"
|
||||||
|
if err := app.IsValid(); err != nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ const (
|
|||||||
SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS
|
SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS
|
||||||
SESSION_TIME_MOBILE_IN_DAYS = 30
|
SESSION_TIME_MOBILE_IN_DAYS = 30
|
||||||
SESSION_TIME_MOBILE_IN_SECS = 60 * 60 * 24 * SESSION_TIME_MOBILE_IN_DAYS
|
SESSION_TIME_MOBILE_IN_SECS = 60 * 60 * 24 * SESSION_TIME_MOBILE_IN_DAYS
|
||||||
|
SESSION_TIME_OAUTH_IN_DAYS = 365
|
||||||
|
SESSION_TIME_OAUTH_IN_SECS = 60 * 60 * 24 * SESSION_TIME_OAUTH_IN_DAYS
|
||||||
SESSION_CACHE_IN_SECS = 60 * 10
|
SESSION_CACHE_IN_SECS = 60 * 10
|
||||||
SESSION_CACHE_SIZE = 10000
|
SESSION_CACHE_SIZE = 10000
|
||||||
SESSION_PROP_PLATFORM = "platform"
|
SESSION_PROP_PLATFORM = "platform"
|
||||||
@@ -23,7 +25,7 @@ const (
|
|||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
AltId string `json:"alt_id"`
|
Token string `json:"token"`
|
||||||
CreateAt int64 `json:"create_at"`
|
CreateAt int64 `json:"create_at"`
|
||||||
ExpiresAt int64 `json:"expires_at"`
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
LastActivityAt int64 `json:"last_activity_at"`
|
LastActivityAt int64 `json:"last_activity_at"`
|
||||||
@@ -31,6 +33,7 @@ type Session struct {
|
|||||||
TeamId string `json:"team_id"`
|
TeamId string `json:"team_id"`
|
||||||
DeviceId string `json:"device_id"`
|
DeviceId string `json:"device_id"`
|
||||||
Roles string `json:"roles"`
|
Roles string `json:"roles"`
|
||||||
|
IsOAuth bool `json:"is_oauth"`
|
||||||
Props StringMap `json:"props"`
|
Props StringMap `json:"props"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@ func (me *Session) PreSave() {
|
|||||||
me.Id = NewId()
|
me.Id = NewId()
|
||||||
}
|
}
|
||||||
|
|
||||||
me.AltId = NewId()
|
me.Token = NewId()
|
||||||
|
|
||||||
me.CreateAt = GetMillis()
|
me.CreateAt = GetMillis()
|
||||||
me.LastActivityAt = me.CreateAt
|
me.LastActivityAt = me.CreateAt
|
||||||
@@ -70,7 +73,7 @@ func (me *Session) PreSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (me *Session) Sanitize() {
|
func (me *Session) Sanitize() {
|
||||||
me.Id = ""
|
me.Token = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me *Session) IsExpired() bool {
|
func (me *Session) IsExpired() bool {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type AppError struct {
|
|||||||
RequestId string `json:"request_id"` // The RequestId that's also set in the header
|
RequestId string `json:"request_id"` // The RequestId that's also set in the header
|
||||||
StatusCode int `json:"status_code"` // The http status code
|
StatusCode int `json:"status_code"` // The http status code
|
||||||
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
Where string `json:"-"` // The function where it happened in the form of Struct.Func
|
||||||
|
IsOAuth bool `json:"is_oauth"` // Whether the error is OAuth specific
|
||||||
}
|
}
|
||||||
|
|
||||||
func (er *AppError) Error() string {
|
func (er *AppError) Error() string {
|
||||||
@@ -65,6 +66,7 @@ func NewAppError(where string, message string, details string) *AppError {
|
|||||||
ap.Where = where
|
ap.Where = where
|
||||||
ap.DetailedError = details
|
ap.DetailedError = details
|
||||||
ap.StatusCode = 500
|
ap.StatusCode = 500
|
||||||
|
ap.IsOAuth = false
|
||||||
return ap
|
return ap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
334
store/sql_oauth_store.go
Обычный файл
334
store/sql_oauth_store.go
Обычный файл
@@ -0,0 +1,334 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SqlOAuthStore struct {
|
||||||
|
*SqlStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqlOAuthStore(sqlStore *SqlStore) OAuthStore {
|
||||||
|
as := &SqlOAuthStore{sqlStore}
|
||||||
|
|
||||||
|
for _, db := range sqlStore.GetAllConns() {
|
||||||
|
table := db.AddTableWithName(model.OAuthApp{}, "OAuthApps").SetKeys(false, "Id")
|
||||||
|
table.ColMap("Id").SetMaxSize(26)
|
||||||
|
table.ColMap("CreatorId").SetMaxSize(26)
|
||||||
|
table.ColMap("ClientSecret").SetMaxSize(128)
|
||||||
|
table.ColMap("Name").SetMaxSize(64)
|
||||||
|
table.ColMap("Description").SetMaxSize(512)
|
||||||
|
table.ColMap("CallbackUrls").SetMaxSize(1024)
|
||||||
|
table.ColMap("Homepage").SetMaxSize(256)
|
||||||
|
|
||||||
|
tableAuth := db.AddTableWithName(model.AuthData{}, "OAuthAuthData").SetKeys(false, "Code")
|
||||||
|
tableAuth.ColMap("UserId").SetMaxSize(26)
|
||||||
|
tableAuth.ColMap("ClientId").SetMaxSize(26)
|
||||||
|
tableAuth.ColMap("Code").SetMaxSize(128)
|
||||||
|
tableAuth.ColMap("RedirectUri").SetMaxSize(256)
|
||||||
|
tableAuth.ColMap("State").SetMaxSize(128)
|
||||||
|
tableAuth.ColMap("Scope").SetMaxSize(128)
|
||||||
|
|
||||||
|
tableAccess := db.AddTableWithName(model.AccessData{}, "OAuthAccessData").SetKeys(false, "Token")
|
||||||
|
tableAccess.ColMap("AuthCode").SetMaxSize(128)
|
||||||
|
tableAccess.ColMap("Token").SetMaxSize(26)
|
||||||
|
tableAccess.ColMap("RefreshToken").SetMaxSize(26)
|
||||||
|
tableAccess.ColMap("RedirectUri").SetMaxSize(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
return as
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) UpgradeSchemaIfNeeded() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) CreateIndexesIfNotExists() {
|
||||||
|
as.CreateIndexIfNotExists("idx_oauthapps_creator_id", "OAuthApps", "CreatorId")
|
||||||
|
as.CreateIndexIfNotExists("idx_oauthaccessdata_auth_code", "OAuthAccessData", "AuthCode")
|
||||||
|
as.CreateIndexIfNotExists("idx_oauthauthdata_client_id", "OAuthAuthData", "Code")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) SaveApp(app *model.OAuthApp) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if len(app.Id) > 0 {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.SaveApp", "Must call update for exisiting app", "app_id="+app.Id)
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.PreSave()
|
||||||
|
if result.Err = app.IsValid(); result.Err != nil {
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := as.GetMaster().Insert(app); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.SaveApp", "We couldn't save the app.", "app_id="+app.Id+", "+err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = app
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) UpdateApp(app *model.OAuthApp) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
app.PreUpdate()
|
||||||
|
|
||||||
|
if result.Err = app.IsValid(); result.Err != nil {
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldAppResult, err := as.GetMaster().Get(model.OAuthApp{}, app.Id); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.UpdateApp", "We encounted an error finding the app", "app_id="+app.Id+", "+err.Error())
|
||||||
|
} else if oldAppResult == nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.UpdateApp", "We couldn't find the existing app to update", "app_id="+app.Id)
|
||||||
|
} else {
|
||||||
|
oldApp := oldAppResult.(*model.OAuthApp)
|
||||||
|
app.CreateAt = oldApp.CreateAt
|
||||||
|
app.ClientSecret = oldApp.ClientSecret
|
||||||
|
app.CreatorId = oldApp.CreatorId
|
||||||
|
|
||||||
|
if count, err := as.GetMaster().Update(app); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.UpdateApp", "We encounted an error updating the app", "app_id="+app.Id+", "+err.Error())
|
||||||
|
} else if count != 1 {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.UpdateApp", "We couldn't update the app", "app_id="+app.Id)
|
||||||
|
} else {
|
||||||
|
result.Data = [2]*model.OAuthApp{app, oldApp}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) GetApp(id string) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if obj, err := as.GetReplica().Get(model.OAuthApp{}, id); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetApp", "We encounted an error finding the app", "app_id="+id+", "+err.Error())
|
||||||
|
} else if obj == nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetApp", "We couldn't find the existing app", "app_id="+id)
|
||||||
|
} else {
|
||||||
|
result.Data = obj.(*model.OAuthApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) GetAppByUser(userId string) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
var apps []*model.OAuthApp
|
||||||
|
|
||||||
|
if _, err := as.GetReplica().Select(&apps, "SELECT * FROM OAuthApps WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetAppByUser", "We couldn't find any existing apps", "user_id="+userId+", "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = apps
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) SaveAccessData(accessData *model.AccessData) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if result.Err = accessData.IsValid(); result.Err != nil {
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := as.GetMaster().Insert(accessData); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.SaveAccessData", "We couldn't save the access token.", err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = accessData
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) GetAccessData(token string) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
accessData := model.AccessData{}
|
||||||
|
|
||||||
|
if err := as.GetReplica().SelectOne(&accessData, "SELECT * FROM OAuthAccessData WHERE Token = :Token", map[string]interface{}{"Token": token}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetAccessData", "We encounted an error finding the access token", err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = &accessData
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) GetAccessDataByAuthCode(authCode string) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
accessData := model.AccessData{}
|
||||||
|
|
||||||
|
if err := as.GetReplica().SelectOne(&accessData, "SELECT * FROM OAuthAccessData WHERE AuthCode = :AuthCode", map[string]interface{}{"AuthCode": authCode}); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "no rows") {
|
||||||
|
result.Data = nil
|
||||||
|
} else {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetAccessDataByAuthCode", "We encountered an error finding the access token", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.Data = &accessData
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) RemoveAccessData(token string) StoreChannel {
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if _, err := as.GetMaster().Exec("DELETE FROM OAuthAccessData WHERE Token = :Token", map[string]interface{}{"Token": token}); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.RemoveAccessData", "We couldn't remove the access token", "err="+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) SaveAuthData(authData *model.AuthData) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
authData.PreSave()
|
||||||
|
if result.Err = authData.IsValid(); result.Err != nil {
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := as.GetMaster().Insert(authData); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.SaveAuthData", "We couldn't save the authorization code.", err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = authData
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) GetAuthData(code string) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if obj, err := as.GetReplica().Get(model.AuthData{}, code); err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetAuthData", "We encounted an error finding the authorization code", err.Error())
|
||||||
|
} else if obj == nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.GetAuthData", "We couldn't find the existing authorization code", "")
|
||||||
|
} else {
|
||||||
|
result.Data = obj.(*model.AuthData)
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as SqlOAuthStore) RemoveAuthData(code string) StoreChannel {
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
_, err := as.GetMaster().Exec("DELETE FROM OAuthAuthData WHERE Code = :Code", map[string]interface{}{"Code": code})
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlOAuthStore.RemoveAuthData", "We couldn't remove the authorization code", "err="+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
182
store/sql_oauth_store_test.go
Обычный файл
182
store/sql_oauth_store_test.go
Обычный файл
@@ -0,0 +1,182 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOAuthStoreSaveApp(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.OAuthApp{}
|
||||||
|
a1.CreatorId = model.NewId()
|
||||||
|
a1.Name = "TestApp" + model.NewId()
|
||||||
|
a1.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
a1.Homepage = "https://nowhere.com"
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().SaveApp(&a1)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreGetApp(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.OAuthApp{}
|
||||||
|
a1.CreatorId = model.NewId()
|
||||||
|
a1.Name = "TestApp" + model.NewId()
|
||||||
|
a1.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
a1.Homepage = "https://nowhere.com"
|
||||||
|
Must(store.OAuth().SaveApp(&a1))
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().GetApp(a1.Id)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().GetAppByUser(a1.CreatorId)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreUpdateApp(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.OAuthApp{}
|
||||||
|
a1.CreatorId = model.NewId()
|
||||||
|
a1.Name = "TestApp" + model.NewId()
|
||||||
|
a1.CallbackUrls = []string{"https://nowhere.com"}
|
||||||
|
a1.Homepage = "https://nowhere.com"
|
||||||
|
Must(store.OAuth().SaveApp(&a1))
|
||||||
|
|
||||||
|
a1.CreateAt = 1
|
||||||
|
a1.ClientSecret = "pwd"
|
||||||
|
a1.CreatorId = "12345678901234567890123456"
|
||||||
|
a1.Name = "NewName"
|
||||||
|
if result := <-store.OAuth().UpdateApp(&a1); result.Err != nil {
|
||||||
|
t.Fatal(result.Err)
|
||||||
|
} else {
|
||||||
|
ua1 := (result.Data.([2]*model.OAuthApp)[0])
|
||||||
|
if ua1.Name != "NewName" {
|
||||||
|
t.Fatal("name did not update")
|
||||||
|
}
|
||||||
|
if ua1.CreateAt == 1 {
|
||||||
|
t.Fatal("create at should not have updated")
|
||||||
|
}
|
||||||
|
if ua1.ClientSecret == "pwd" {
|
||||||
|
t.Fatal("client secret should not have updated")
|
||||||
|
}
|
||||||
|
if ua1.CreatorId == "12345678901234567890123456" {
|
||||||
|
t.Fatal("creator id should not have updated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreSaveAccessData(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.AccessData{}
|
||||||
|
a1.AuthCode = model.NewId()
|
||||||
|
a1.Token = model.NewId()
|
||||||
|
a1.RefreshToken = model.NewId()
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().SaveAccessData(&a1)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreGetAccessData(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.AccessData{}
|
||||||
|
a1.AuthCode = model.NewId()
|
||||||
|
a1.Token = model.NewId()
|
||||||
|
a1.RefreshToken = model.NewId()
|
||||||
|
Must(store.OAuth().SaveAccessData(&a1))
|
||||||
|
|
||||||
|
if result := <-store.OAuth().GetAccessData(a1.Token); result.Err != nil {
|
||||||
|
t.Fatal(result.Err)
|
||||||
|
} else {
|
||||||
|
ra1 := result.Data.(*model.AccessData)
|
||||||
|
if a1.Token != ra1.Token {
|
||||||
|
t.Fatal("tokens didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().GetAccessDataByAuthCode(a1.AuthCode)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().GetAccessDataByAuthCode("junk")).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreRemoveAccessData(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.AccessData{}
|
||||||
|
a1.AuthCode = model.NewId()
|
||||||
|
a1.Token = model.NewId()
|
||||||
|
a1.RefreshToken = model.NewId()
|
||||||
|
Must(store.OAuth().SaveAccessData(&a1))
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().RemoveAccessData(a1.Token)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-store.OAuth().GetAccessDataByAuthCode(a1.AuthCode); result.Err != nil {
|
||||||
|
t.Fatal(result.Err)
|
||||||
|
} else {
|
||||||
|
if result.Data != nil {
|
||||||
|
t.Fatal("did not delete access token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreSaveAuthData(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.AuthData{}
|
||||||
|
a1.ClientId = model.NewId()
|
||||||
|
a1.UserId = model.NewId()
|
||||||
|
a1.Code = model.NewId()
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().SaveAuthData(&a1)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreGetAuthData(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.AuthData{}
|
||||||
|
a1.ClientId = model.NewId()
|
||||||
|
a1.UserId = model.NewId()
|
||||||
|
a1.Code = model.NewId()
|
||||||
|
Must(store.OAuth().SaveAuthData(&a1))
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().GetAuthData(a1.Code)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOAuthStoreRemoveAuthData(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
a1 := model.AuthData{}
|
||||||
|
a1.ClientId = model.NewId()
|
||||||
|
a1.UserId = model.NewId()
|
||||||
|
a1.Code = model.NewId()
|
||||||
|
Must(store.OAuth().SaveAuthData(&a1))
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().RemoveAuthData(a1.Code)).Err; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := (<-store.OAuth().GetAuthData(a1.Code)).Err; err == nil {
|
||||||
|
t.Fatal("should have errored - auth code removed")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ func NewSqlSessionStore(sqlStore *SqlStore) SessionStore {
|
|||||||
for _, db := range sqlStore.GetAllConns() {
|
for _, db := range sqlStore.GetAllConns() {
|
||||||
table := db.AddTableWithName(model.Session{}, "Sessions").SetKeys(false, "Id")
|
table := db.AddTableWithName(model.Session{}, "Sessions").SetKeys(false, "Id")
|
||||||
table.ColMap("Id").SetMaxSize(26)
|
table.ColMap("Id").SetMaxSize(26)
|
||||||
table.ColMap("AltId").SetMaxSize(26)
|
table.ColMap("Token").SetMaxSize(26)
|
||||||
table.ColMap("UserId").SetMaxSize(26)
|
table.ColMap("UserId").SetMaxSize(26)
|
||||||
table.ColMap("TeamId").SetMaxSize(26)
|
table.ColMap("TeamId").SetMaxSize(26)
|
||||||
table.ColMap("DeviceId").SetMaxSize(128)
|
table.ColMap("DeviceId").SetMaxSize(128)
|
||||||
@@ -34,7 +34,7 @@ func (me SqlSessionStore) UpgradeSchemaIfNeeded() {
|
|||||||
|
|
||||||
func (me SqlSessionStore) CreateIndexesIfNotExists() {
|
func (me SqlSessionStore) CreateIndexesIfNotExists() {
|
||||||
me.CreateIndexIfNotExists("idx_sessions_user_id", "Sessions", "UserId")
|
me.CreateIndexIfNotExists("idx_sessions_user_id", "Sessions", "UserId")
|
||||||
me.CreateIndexIfNotExists("idx_sessions_alt_id", "Sessions", "AltId")
|
me.CreateIndexIfNotExists("idx_sessions_token", "Sessions", "Token")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me SqlSessionStore) Save(session *model.Session) StoreChannel {
|
func (me SqlSessionStore) Save(session *model.Session) StoreChannel {
|
||||||
@@ -70,19 +70,21 @@ func (me SqlSessionStore) Save(session *model.Session) StoreChannel {
|
|||||||
return storeChannel
|
return storeChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me SqlSessionStore) Get(id string) StoreChannel {
|
func (me SqlSessionStore) Get(sessionIdOrToken string) StoreChannel {
|
||||||
|
|
||||||
storeChannel := make(StoreChannel)
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
result := StoreResult{}
|
result := StoreResult{}
|
||||||
|
|
||||||
if obj, err := me.GetReplica().Get(model.Session{}, id); err != nil {
|
var sessions []*model.Session
|
||||||
result.Err = model.NewAppError("SqlSessionStore.Get", "We encounted an error finding the session", "id="+id+", "+err.Error())
|
|
||||||
} else if obj == nil {
|
if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE Token = :Token OR Id = :Id LIMIT 1", map[string]interface{}{"Token": sessionIdOrToken, "Id": sessionIdOrToken}); err != nil {
|
||||||
result.Err = model.NewAppError("SqlSessionStore.Get", "We couldn't find the existing session", "id="+id)
|
result.Err = model.NewAppError("SqlSessionStore.Get", "We encounted an error finding the session", "sessionIdOrToken="+sessionIdOrToken+", "+err.Error())
|
||||||
|
} else if sessions == nil || len(sessions) == 0 {
|
||||||
|
result.Err = model.NewAppError("SqlSessionStore.Get", "We encounted an error finding the session", "sessionIdOrToken="+sessionIdOrToken)
|
||||||
} else {
|
} else {
|
||||||
result.Data = obj.(*model.Session)
|
result.Data = sessions[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
storeChannel <- result
|
storeChannel <- result
|
||||||
@@ -120,15 +122,15 @@ func (me SqlSessionStore) GetSessions(userId string) StoreChannel {
|
|||||||
return storeChannel
|
return storeChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me SqlSessionStore) Remove(sessionIdOrAlt string) StoreChannel {
|
func (me SqlSessionStore) Remove(sessionIdOrToken string) StoreChannel {
|
||||||
storeChannel := make(StoreChannel)
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
result := StoreResult{}
|
result := StoreResult{}
|
||||||
|
|
||||||
_, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE Id = :Id Or AltId = :AltId", map[string]interface{}{"Id": sessionIdOrAlt, "AltId": sessionIdOrAlt})
|
_, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE Id = :Id Or Token = :Token", map[string]interface{}{"Id": sessionIdOrToken, "Token": sessionIdOrToken})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlSessionStore.RemoveSession", "We couldn't remove the session", "id="+sessionIdOrAlt+", err="+err.Error())
|
result.Err = model.NewAppError("SqlSessionStore.RemoveSession", "We couldn't remove the session", "id="+sessionIdOrToken+", err="+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
storeChannel <- result
|
storeChannel <- result
|
||||||
@@ -181,7 +183,6 @@ func (me SqlSessionStore) UpdateRoles(userId, roles string) StoreChannel {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
result := StoreResult{}
|
result := StoreResult{}
|
||||||
|
|
||||||
if _, err := me.GetMaster().Exec("UPDATE Sessions SET Roles = :Roles WHERE UserId = :UserId", map[string]interface{}{"Roles": roles, "UserId": userId}); err != nil {
|
if _, err := me.GetMaster().Exec("UPDATE Sessions SET Roles = :Roles WHERE UserId = :UserId", map[string]interface{}{"Roles": roles, "UserId": userId}); err != nil {
|
||||||
result.Err = model.NewAppError("SqlSessionStore.UpdateRoles", "We couldn't update the roles", "userId="+userId)
|
result.Err = model.NewAppError("SqlSessionStore.UpdateRoles", "We couldn't update the roles", "userId="+userId)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func TestSessionRemove(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSessionRemoveAlt(t *testing.T) {
|
func TestSessionRemoveToken(t *testing.T) {
|
||||||
Setup()
|
Setup()
|
||||||
|
|
||||||
s1 := model.Session{}
|
s1 := model.Session{}
|
||||||
@@ -96,7 +96,7 @@ func TestSessionRemoveAlt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Must(store.Session().Remove(s1.AltId))
|
Must(store.Session().Remove(s1.Token))
|
||||||
|
|
||||||
if rs2 := (<-store.Session().Get(s1.Id)); rs2.Err == nil {
|
if rs2 := (<-store.Session().Get(s1.Id)); rs2.Err == nil {
|
||||||
t.Fatal("should have been removed")
|
t.Fatal("should have been removed")
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type SqlStore struct {
|
|||||||
user UserStore
|
user UserStore
|
||||||
audit AuditStore
|
audit AuditStore
|
||||||
session SessionStore
|
session SessionStore
|
||||||
|
oauth OAuthStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqlStore() Store {
|
func NewSqlStore() Store {
|
||||||
@@ -55,28 +56,36 @@ func NewSqlStore() Store {
|
|||||||
utils.Cfg.SqlSettings.Trace)
|
utils.Cfg.SqlSettings.Trace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary upgrade code, remove after 0.8.0 release
|
||||||
|
if sqlStore.DoesColumnExist("Sessions", "AltId") {
|
||||||
|
sqlStore.GetMaster().Exec("DROP TABLE IF EXISTS Sessions")
|
||||||
|
}
|
||||||
|
|
||||||
sqlStore.team = NewSqlTeamStore(sqlStore)
|
sqlStore.team = NewSqlTeamStore(sqlStore)
|
||||||
sqlStore.channel = NewSqlChannelStore(sqlStore)
|
sqlStore.channel = NewSqlChannelStore(sqlStore)
|
||||||
sqlStore.post = NewSqlPostStore(sqlStore)
|
sqlStore.post = NewSqlPostStore(sqlStore)
|
||||||
sqlStore.user = NewSqlUserStore(sqlStore)
|
sqlStore.user = NewSqlUserStore(sqlStore)
|
||||||
sqlStore.audit = NewSqlAuditStore(sqlStore)
|
sqlStore.audit = NewSqlAuditStore(sqlStore)
|
||||||
sqlStore.session = NewSqlSessionStore(sqlStore)
|
sqlStore.session = NewSqlSessionStore(sqlStore)
|
||||||
|
sqlStore.oauth = NewSqlOAuthStore(sqlStore)
|
||||||
|
|
||||||
sqlStore.master.CreateTablesIfNotExists()
|
sqlStore.master.CreateTablesIfNotExists()
|
||||||
|
|
||||||
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
|
||||||
sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
|
|
||||||
sqlStore.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
|
||||||
sqlStore.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
|
||||||
sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
|
||||||
sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
|
|
||||||
|
|
||||||
sqlStore.team.(*SqlTeamStore).UpgradeSchemaIfNeeded()
|
sqlStore.team.(*SqlTeamStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.channel.(*SqlChannelStore).UpgradeSchemaIfNeeded()
|
sqlStore.channel.(*SqlChannelStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.post.(*SqlPostStore).UpgradeSchemaIfNeeded()
|
sqlStore.post.(*SqlPostStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.user.(*SqlUserStore).UpgradeSchemaIfNeeded()
|
sqlStore.user.(*SqlUserStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
|
sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
|
sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
|
||||||
|
sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
|
||||||
|
|
||||||
|
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
|
||||||
|
|
||||||
return sqlStore
|
return sqlStore
|
||||||
}
|
}
|
||||||
@@ -363,6 +372,10 @@ func (ss SqlStore) Audit() AuditStore {
|
|||||||
return ss.audit
|
return ss.audit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss SqlStore) OAuth() OAuthStore {
|
||||||
|
return ss.oauth
|
||||||
|
}
|
||||||
|
|
||||||
type mattermConverter struct{}
|
type mattermConverter struct{}
|
||||||
|
|
||||||
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
|
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type Store interface {
|
|||||||
User() UserStore
|
User() UserStore
|
||||||
Audit() AuditStore
|
Audit() AuditStore
|
||||||
Session() SessionStore
|
Session() SessionStore
|
||||||
|
OAuth() OAuthStore
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +105,9 @@ type UserStore interface {
|
|||||||
|
|
||||||
type SessionStore interface {
|
type SessionStore interface {
|
||||||
Save(session *model.Session) StoreChannel
|
Save(session *model.Session) StoreChannel
|
||||||
Get(id string) StoreChannel
|
Get(sessionIdOrToken string) StoreChannel
|
||||||
GetSessions(userId string) StoreChannel
|
GetSessions(userId string) StoreChannel
|
||||||
Remove(sessionIdOrAlt string) StoreChannel
|
Remove(sessionIdOrToken string) StoreChannel
|
||||||
UpdateLastActivityAt(sessionId string, time int64) StoreChannel
|
UpdateLastActivityAt(sessionId string, time int64) StoreChannel
|
||||||
UpdateRoles(userId string, roles string) StoreChannel
|
UpdateRoles(userId string, roles string) StoreChannel
|
||||||
}
|
}
|
||||||
@@ -115,3 +116,17 @@ type AuditStore interface {
|
|||||||
Save(audit *model.Audit) StoreChannel
|
Save(audit *model.Audit) StoreChannel
|
||||||
Get(user_id string, limit int) StoreChannel
|
Get(user_id string, limit int) StoreChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OAuthStore interface {
|
||||||
|
SaveApp(app *model.OAuthApp) StoreChannel
|
||||||
|
UpdateApp(app *model.OAuthApp) StoreChannel
|
||||||
|
GetApp(id string) StoreChannel
|
||||||
|
GetAppByUser(userId string) StoreChannel
|
||||||
|
SaveAuthData(authData *model.AuthData) StoreChannel
|
||||||
|
GetAuthData(code string) StoreChannel
|
||||||
|
RemoveAuthData(code string) StoreChannel
|
||||||
|
SaveAccessData(accessData *model.AccessData) StoreChannel
|
||||||
|
GetAccessData(token string) StoreChannel
|
||||||
|
GetAccessDataByAuthCode(authCode string) StoreChannel
|
||||||
|
RemoveAccessData(token string) StoreChannel
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,20 +21,21 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ServiceSettings struct {
|
type ServiceSettings struct {
|
||||||
SiteName string
|
SiteName string
|
||||||
Mode string
|
Mode string
|
||||||
AllowTesting bool
|
AllowTesting bool
|
||||||
UseSSL bool
|
UseSSL bool
|
||||||
Port string
|
Port string
|
||||||
Version string
|
Version string
|
||||||
InviteSalt string
|
InviteSalt string
|
||||||
PublicLinkSalt string
|
PublicLinkSalt string
|
||||||
ResetSalt string
|
ResetSalt string
|
||||||
AnalyticsUrl string
|
AnalyticsUrl string
|
||||||
UseLocalStorage bool
|
UseLocalStorage bool
|
||||||
StorageDirectory string
|
StorageDirectory string
|
||||||
AllowedLoginAttempts int
|
AllowedLoginAttempts int
|
||||||
DisableEmailSignUp bool
|
DisableEmailSignUp bool
|
||||||
|
EnableOAuthServiceProvider bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type SSOSetting struct {
|
type SSOSetting struct {
|
||||||
@@ -286,6 +287,7 @@ func getClientProperties(c *Config) map[string]string {
|
|||||||
props["ProfileHeight"] = fmt.Sprintf("%v", c.ImageSettings.ProfileHeight)
|
props["ProfileHeight"] = fmt.Sprintf("%v", c.ImageSettings.ProfileHeight)
|
||||||
props["ProfileWidth"] = fmt.Sprintf("%v", c.ImageSettings.ProfileWidth)
|
props["ProfileWidth"] = fmt.Sprintf("%v", c.ImageSettings.ProfileWidth)
|
||||||
props["ProfileWidth"] = fmt.Sprintf("%v", c.ImageSettings.ProfileWidth)
|
props["ProfileWidth"] = fmt.Sprintf("%v", c.ImageSettings.ProfileWidth)
|
||||||
|
props["EnableOAuthServiceProvider"] = strconv.FormatBool(c.ServiceSettings.EnableOAuthServiceProvider)
|
||||||
|
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|||||||
72
web/react/components/authorize.jsx
Обычный файл
72
web/react/components/authorize.jsx
Обычный файл
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
var Client = require('../utils/client.jsx');
|
||||||
|
|
||||||
|
export default class Authorize extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleAllow = this.handleAllow.bind(this);
|
||||||
|
this.handleDeny = this.handleDeny.bind(this);
|
||||||
|
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
handleAllow() {
|
||||||
|
const responseType = this.props.responseType;
|
||||||
|
const clientId = this.props.clientId;
|
||||||
|
const redirectUri = this.props.redirectUri;
|
||||||
|
const state = this.props.state;
|
||||||
|
const scope = this.props.scope;
|
||||||
|
|
||||||
|
Client.allowOAuth2(responseType, clientId, redirectUri, state, scope,
|
||||||
|
(data) => {
|
||||||
|
if (data.redirect) {
|
||||||
|
window.location.replace(data.redirect);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleDeny() {
|
||||||
|
window.location.replace(this.props.redirectUri + '?error=access_denied');
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='authorize-box'>
|
||||||
|
<div className='authorize-inner'>
|
||||||
|
<h3>{'An application would like to connect to your '}{this.props.teamName}{' account'}</h3>
|
||||||
|
<label>{'The app '}{this.props.appName}{' would like the ability to access and modify your basic information.'}</label>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<label>{'Allow '}{this.props.appName}{' access?'}</label>
|
||||||
|
<br/>
|
||||||
|
<button
|
||||||
|
type='submit'
|
||||||
|
className='btn authorize-btn'
|
||||||
|
onClick={this.handleDeny}
|
||||||
|
>
|
||||||
|
{'Deny'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type='submit'
|
||||||
|
className='btn btn-primary authorize-btn'
|
||||||
|
onClick={this.handleAllow}
|
||||||
|
>
|
||||||
|
{'Allow'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Authorize.propTypes = {
|
||||||
|
appName: React.PropTypes.string,
|
||||||
|
teamName: React.PropTypes.string,
|
||||||
|
responseType: React.PropTypes.string,
|
||||||
|
clientId: React.PropTypes.string,
|
||||||
|
redirectUri: React.PropTypes.string,
|
||||||
|
state: React.PropTypes.string,
|
||||||
|
scope: React.PropTypes.string
|
||||||
|
};
|
||||||
@@ -25,7 +25,7 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
|
|
||||||
$('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true});
|
$('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true});
|
||||||
$('body').on('click', function onClick(e) {
|
$('body').on('click', function onClick(e) {
|
||||||
if ($(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) {
|
if (e.target.parentNode && $(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) {
|
||||||
$('#member_popover').popover('hide');
|
$('#member_popover').popover('hide');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
249
web/react/components/register_app_modal.jsx
Обычный файл
249
web/react/components/register_app_modal.jsx
Обычный файл
@@ -0,0 +1,249 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
var Client = require('../utils/client.jsx');
|
||||||
|
|
||||||
|
export default class RegisterAppModal extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.register = this.register.bind(this);
|
||||||
|
this.onHide = this.onHide.bind(this);
|
||||||
|
this.save = this.save.bind(this);
|
||||||
|
|
||||||
|
this.state = {clientId: '', clientSecret: '', saved: false};
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
$(React.findDOMNode(this)).on('hide.bs.modal', this.onHide);
|
||||||
|
}
|
||||||
|
register() {
|
||||||
|
var state = this.state;
|
||||||
|
state.serverError = null;
|
||||||
|
|
||||||
|
var app = {};
|
||||||
|
|
||||||
|
var name = this.refs.name.getDOMNode().value;
|
||||||
|
if (!name || name.length === 0) {
|
||||||
|
state.nameError = 'Application name must be filled in.';
|
||||||
|
this.setState(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.nameError = null;
|
||||||
|
app.name = name;
|
||||||
|
|
||||||
|
var homepage = this.refs.homepage.getDOMNode().value;
|
||||||
|
if (!homepage || homepage.length === 0) {
|
||||||
|
state.homepageError = 'Homepage must be filled in.';
|
||||||
|
this.setState(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.homepageError = null;
|
||||||
|
app.homepage = homepage;
|
||||||
|
|
||||||
|
var desc = this.refs.desc.getDOMNode().value;
|
||||||
|
app.description = desc;
|
||||||
|
|
||||||
|
var rawCallbacks = this.refs.callback.getDOMNode().value.trim();
|
||||||
|
if (!rawCallbacks || rawCallbacks.length === 0) {
|
||||||
|
state.callbackError = 'At least one callback URL must be filled in.';
|
||||||
|
this.setState(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.callbackError = null;
|
||||||
|
app.callback_urls = rawCallbacks.split('\n');
|
||||||
|
|
||||||
|
Client.registerOAuthApp(app,
|
||||||
|
(data) => {
|
||||||
|
state.clientId = data.id;
|
||||||
|
state.clientSecret = data.client_secret;
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
state.serverError = err.message;
|
||||||
|
this.setState(state);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onHide(e) {
|
||||||
|
if (!this.state.saved && this.state.clientId !== '') {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({clientId: '', clientSecret: '', saved: false});
|
||||||
|
}
|
||||||
|
save() {
|
||||||
|
this.setState({saved: this.refs.save.getDOMNode().checked});
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
var nameError;
|
||||||
|
if (this.state.nameError) {
|
||||||
|
nameError = <div className='form-group has-error'><label className='control-label'>{this.state.nameError}</label></div>;
|
||||||
|
}
|
||||||
|
var homepageError;
|
||||||
|
if (this.state.homepageError) {
|
||||||
|
homepageError = <div className='form-group has-error'><label className='control-label'>{this.state.homepageError}</label></div>;
|
||||||
|
}
|
||||||
|
var callbackError;
|
||||||
|
if (this.state.callbackError) {
|
||||||
|
callbackError = <div className='form-group has-error'><label className='control-label'>{this.state.callbackError}</label></div>;
|
||||||
|
}
|
||||||
|
var serverError;
|
||||||
|
if (this.state.serverError) {
|
||||||
|
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = '';
|
||||||
|
if (this.state.clientId === '') {
|
||||||
|
body = (
|
||||||
|
<div className='form-group user-settings'>
|
||||||
|
<h3>{'Register a New Application'}</h3>
|
||||||
|
<br/>
|
||||||
|
<label className='col-sm-4 control-label'>{'Application Name'}</label>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<input
|
||||||
|
ref='name'
|
||||||
|
className='form-control'
|
||||||
|
type='text'
|
||||||
|
placeholder='Required'
|
||||||
|
/>
|
||||||
|
{nameError}
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<label className='col-sm-4 control-label'>{'Homepage URL'}</label>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<input
|
||||||
|
ref='homepage'
|
||||||
|
className='form-control'
|
||||||
|
type='text'
|
||||||
|
placeholder='Required'
|
||||||
|
/>
|
||||||
|
{homepageError}
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<label className='col-sm-4 control-label'>{'Description'}</label>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<input
|
||||||
|
ref='desc'
|
||||||
|
className='form-control'
|
||||||
|
type='text'
|
||||||
|
placeholder='Optional'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<label className='col-sm-4 control-label'>{'Callback URL'}</label>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<textarea
|
||||||
|
ref='callback'
|
||||||
|
className='form-control'
|
||||||
|
type='text'
|
||||||
|
placeholder='Required'
|
||||||
|
rows='5'
|
||||||
|
/>
|
||||||
|
{callbackError}
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
{serverError}
|
||||||
|
<a
|
||||||
|
className='btn btn-sm theme pull-right'
|
||||||
|
href='#'
|
||||||
|
data-dismiss='modal'
|
||||||
|
aria-label='Close'
|
||||||
|
>
|
||||||
|
{'Cancel'}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
className='btn btn-sm btn-primary pull-right'
|
||||||
|
onClick={this.register}
|
||||||
|
>
|
||||||
|
{'Register'}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var btnClass = ' disabled';
|
||||||
|
if (this.state.saved) {
|
||||||
|
btnClass = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
body = (
|
||||||
|
<div className='form-group user-settings'>
|
||||||
|
<h3>{'Your Application Credentials'}</h3>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<label className='col-sm-12 control-label'>{'Client ID: '}{this.state.clientId}</label>
|
||||||
|
<label className='col-sm-12 control-label'>{'Client Secret: '}{this.state.clientSecret}</label>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<strong>{'Save these somewhere SAFE and SECURE. We can retrieve your Client Id if you lose it, but your Client Secret will be lost forever if you were to lose it.'}</strong>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<div className='checkbox'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
ref='save'
|
||||||
|
type='checkbox'
|
||||||
|
checked={this.state.saved}
|
||||||
|
onClick={this.save}
|
||||||
|
>
|
||||||
|
{'I have saved both my Client Id and Client Secret somewhere safe'}
|
||||||
|
</input>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
className={'btn btn-sm btn-primary pull-right' + btnClass}
|
||||||
|
href='#'
|
||||||
|
data-dismiss='modal'
|
||||||
|
aria-label='Close'
|
||||||
|
>
|
||||||
|
{'Close'}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='modal fade'
|
||||||
|
ref='modal'
|
||||||
|
id='register_app'
|
||||||
|
role='dialog'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<div className='modal-dialog'>
|
||||||
|
<div className='modal-content'>
|
||||||
|
<div className='modal-header'>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='close'
|
||||||
|
data-dismiss='modal'
|
||||||
|
aria-label='Close'
|
||||||
|
>
|
||||||
|
<span aria-hidden='true'>{'x'}</span>
|
||||||
|
</button>
|
||||||
|
<h4
|
||||||
|
className='modal-title'
|
||||||
|
ref='title'
|
||||||
|
>
|
||||||
|
{'Developer Applications'}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div className='modal-body'>
|
||||||
|
{body}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ var NotificationsTab = require('./user_settings_notifications.jsx');
|
|||||||
var SecurityTab = require('./user_settings_security.jsx');
|
var SecurityTab = require('./user_settings_security.jsx');
|
||||||
var GeneralTab = require('./user_settings_general.jsx');
|
var GeneralTab = require('./user_settings_general.jsx');
|
||||||
var AppearanceTab = require('./user_settings_appearance.jsx');
|
var AppearanceTab = require('./user_settings_appearance.jsx');
|
||||||
|
var DeveloperTab = require('./user_settings_developer.jsx');
|
||||||
|
|
||||||
export default class UserSettings extends React.Component {
|
export default class UserSettings extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -76,6 +77,15 @@ export default class UserSettings extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (this.props.activeTab === 'developer') {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DeveloperTab
|
||||||
|
activeSection={this.props.activeSection}
|
||||||
|
updateSection={this.props.updateSection}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div/>;
|
return <div/>;
|
||||||
|
|||||||
93
web/react/components/user_settings_developer.jsx
Обычный файл
93
web/react/components/user_settings_developer.jsx
Обычный файл
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
var SettingItemMin = require('./setting_item_min.jsx');
|
||||||
|
var SettingItemMax = require('./setting_item_max.jsx');
|
||||||
|
|
||||||
|
export default class DeveloperTab extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
register() {
|
||||||
|
$('#user_settings1').modal('hide');
|
||||||
|
$('#register_app').modal('show');
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
var appSection;
|
||||||
|
var self = this;
|
||||||
|
if (this.props.activeSection === 'app') {
|
||||||
|
var inputs = [];
|
||||||
|
|
||||||
|
inputs.push(
|
||||||
|
<div className='form-group'>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<a
|
||||||
|
className='btn btn-sm btn-primary'
|
||||||
|
onClick={this.register}
|
||||||
|
>
|
||||||
|
{'Register New Application'}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
appSection = (
|
||||||
|
<SettingItemMax
|
||||||
|
title='Applications (Preview)'
|
||||||
|
inputs={inputs}
|
||||||
|
updateSection={function updateSection(e) {
|
||||||
|
self.props.updateSection('');
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
appSection = (
|
||||||
|
<SettingItemMin
|
||||||
|
title='Applications (Preview)'
|
||||||
|
describe='Open to register a new third-party application'
|
||||||
|
updateSection={function updateSection() {
|
||||||
|
self.props.updateSection('app');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='modal-header'>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='close'
|
||||||
|
data-dismiss='modal'
|
||||||
|
aria-label='Close'
|
||||||
|
>
|
||||||
|
<span aria-hidden='true'>{'x'}</span>
|
||||||
|
</button>
|
||||||
|
<h4
|
||||||
|
className='modal-title'
|
||||||
|
ref='title'
|
||||||
|
>
|
||||||
|
<i className='modal-back'></i>{'Developer Settings'}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div className='user-settings'>
|
||||||
|
<h3 className='tab-header'>{'Developer Settings'}</h3>
|
||||||
|
<div className='divider-dark first'/>
|
||||||
|
{appSection}
|
||||||
|
<div className='divider-dark'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeveloperTab.defaultProps = {
|
||||||
|
activeSection: ''
|
||||||
|
};
|
||||||
|
DeveloperTab.propTypes = {
|
||||||
|
activeSection: React.PropTypes.string,
|
||||||
|
updateSection: React.PropTypes.func
|
||||||
|
};
|
||||||
@@ -17,8 +17,8 @@ export default class UserSettingsModal extends React.Component {
|
|||||||
$('body').on('click', '.modal-back', function changeDisplay() {
|
$('body').on('click', '.modal-back', function changeDisplay() {
|
||||||
$(this).closest('.modal-dialog').removeClass('display--content');
|
$(this).closest('.modal-dialog').removeClass('display--content');
|
||||||
});
|
});
|
||||||
$('body').on('click', '.modal-header .close', function closeModal() {
|
$('body').on('click', '.modal-header .close', () => {
|
||||||
setTimeout(function finishClose() {
|
setTimeout(() => {
|
||||||
$('.modal-dialog.display--content').removeClass('display--content');
|
$('.modal-dialog.display--content').removeClass('display--content');
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
@@ -35,6 +35,9 @@ export default class UserSettingsModal extends React.Component {
|
|||||||
tabs.push({name: 'security', uiName: 'Security', icon: 'glyphicon glyphicon-lock'});
|
tabs.push({name: 'security', uiName: 'Security', icon: 'glyphicon glyphicon-lock'});
|
||||||
tabs.push({name: 'notifications', uiName: 'Notifications', icon: 'glyphicon glyphicon-exclamation-sign'});
|
tabs.push({name: 'notifications', uiName: 'Notifications', icon: 'glyphicon glyphicon-exclamation-sign'});
|
||||||
tabs.push({name: 'appearance', uiName: 'Appearance', icon: 'glyphicon glyphicon-wrench'});
|
tabs.push({name: 'appearance', uiName: 'Appearance', icon: 'glyphicon glyphicon-wrench'});
|
||||||
|
if (global.window.config.EnableOAuthServiceProvider) {
|
||||||
|
tabs.push({name: 'developer', uiName: 'Developer', icon: 'glyphicon glyphicon-th'});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -54,13 +57,13 @@ export default class UserSettingsModal extends React.Component {
|
|||||||
data-dismiss='modal'
|
data-dismiss='modal'
|
||||||
aria-label='Close'
|
aria-label='Close'
|
||||||
>
|
>
|
||||||
<span aria-hidden='true'>×</span>
|
<span aria-hidden='true'>{'x'}</span>
|
||||||
</button>
|
</button>
|
||||||
<h4
|
<h4
|
||||||
className='modal-title'
|
className='modal-title'
|
||||||
ref='title'
|
ref='title'
|
||||||
>
|
>
|
||||||
Account Settings
|
{'Account Settings'}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className='modal-body'>
|
<div className='modal-body'>
|
||||||
|
|||||||
21
web/react/pages/authorize.jsx
Обычный файл
21
web/react/pages/authorize.jsx
Обычный файл
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
var Authorize = require('../components/authorize.jsx');
|
||||||
|
|
||||||
|
function setupAuthorizePage(teamName, appName, responseType, clientId, redirectUri, scope, state) {
|
||||||
|
React.render(
|
||||||
|
<Authorize
|
||||||
|
teamName={teamName}
|
||||||
|
appName={appName}
|
||||||
|
responseType={responseType}
|
||||||
|
clientId={clientId}
|
||||||
|
redirectUri={redirectUri}
|
||||||
|
scope={scope}
|
||||||
|
state={state}
|
||||||
|
/>,
|
||||||
|
document.getElementById('authorize')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.window.setup_authorize_page = setupAuthorizePage;
|
||||||
@@ -33,6 +33,7 @@ var AccessHistoryModal = require('../components/access_history_modal.jsx');
|
|||||||
var ActivityLogModal = require('../components/activity_log_modal.jsx');
|
var ActivityLogModal = require('../components/activity_log_modal.jsx');
|
||||||
var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx');
|
var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx');
|
||||||
var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
|
var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
|
||||||
|
var RegisterAppModal = require('../components/register_app_modal.jsx');
|
||||||
|
|
||||||
var Constants = require('../utils/constants.jsx');
|
var Constants = require('../utils/constants.jsx');
|
||||||
var ActionTypes = Constants.ActionTypes;
|
var ActionTypes = Constants.ActionTypes;
|
||||||
@@ -222,6 +223,11 @@ function setupChannelPage(props) {
|
|||||||
/>,
|
/>,
|
||||||
document.getElementById('file_upload_overlay')
|
document.getElementById('file_upload_overlay')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
React.render(
|
||||||
|
<RegisterAppModal />,
|
||||||
|
document.getElementById('register_app_modal')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.window.setup_channel_page = setupChannelPage;
|
global.window.setup_channel_page = setupChannelPage;
|
||||||
|
|||||||
@@ -987,3 +987,36 @@ export function updateValetFeature(data, success, error) {
|
|||||||
|
|
||||||
track('api', 'api_teams_update_valet_feature');
|
track('api', 'api_teams_update_valet_feature');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function registerOAuthApp(app, success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/oauth/register',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(app),
|
||||||
|
success: success,
|
||||||
|
error: (xhr, status, err) => {
|
||||||
|
const e = handleError('registerApp', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.track('api', 'api_apps_register');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function allowOAuth2(responseType, clientId, redirectUri, state, scope, success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/oauth/allow?response_type=' + responseType + '&client_id=' + clientId + '&redirect_uri=' + redirectUri + '&scope=' + scope + '&state=' + state,
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
type: 'GET',
|
||||||
|
success: success,
|
||||||
|
error: (xhr, status, err) => {
|
||||||
|
const e = handleError('allowOAuth2', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.track('api', 'api_users_allow_oauth2');
|
||||||
|
}
|
||||||
|
|||||||
@@ -315,3 +315,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.authorize-box {
|
||||||
|
margin: 100px auto;
|
||||||
|
width:500px;
|
||||||
|
height:280px;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authorize-inner {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authorize-btn {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|||||||
26
web/templates/authorize.html
Обычный файл
26
web/templates/authorize.html
Обычный файл
@@ -0,0 +1,26 @@
|
|||||||
|
{{define "authorize"}}
|
||||||
|
<html>
|
||||||
|
{{template "head" . }}
|
||||||
|
<body class="white">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="inner__wrap">
|
||||||
|
<div class="row content">
|
||||||
|
<div class="signup-header">
|
||||||
|
{{.Props.TeamName}}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div id="authorize"></div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-push"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row footer">
|
||||||
|
{{template "footer" . }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
window.setup_authorize_page('{{ .Props.TeamName }}', '{{ .Props.AppName }}', '{{ .Props.ResponseType }}', '{{ .Props.ClientId }}', '{{ .Props.RedirectUri }}', '{{ .Props.Scope }}', '{{ .Props.State }}' );
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
<div id="access_history_modal"></div>
|
<div id="access_history_modal"></div>
|
||||||
<div id="activity_log_modal"></div>
|
<div id="activity_log_modal"></div>
|
||||||
<div id="removed_from_channel_modal"></div>
|
<div id="removed_from_channel_modal"></div>
|
||||||
|
<div id="register_app_modal"></div>
|
||||||
<script>
|
<script>
|
||||||
window.setup_channel_page({{ .Props }});
|
window.setup_channel_page({{ .Props }});
|
||||||
$('body').tooltip( {selector: '[data-toggle=tooltip]'} );
|
$('body').tooltip( {selector: '[data-toggle=tooltip]'} );
|
||||||
|
|||||||
204
web/web.go
204
web/web.go
@@ -4,19 +4,18 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
l4g "code.google.com/p/log4go"
|
l4g "code.google.com/p/log4go"
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mattermost/platform/api"
|
"github.com/mattermost/platform/api"
|
||||||
"github.com/mattermost/platform/model"
|
"github.com/mattermost/platform/model"
|
||||||
"github.com/mattermost/platform/utils"
|
"github.com/mattermost/platform/utils"
|
||||||
"github.com/mssola/user_agent"
|
"github.com/mssola/user_agent"
|
||||||
"gopkg.in/fsnotify.v1"
|
"gopkg.in/fsnotify.v1"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Templates *template.Template
|
var Templates *template.Template
|
||||||
@@ -50,6 +49,8 @@ func InitWeb() {
|
|||||||
mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
|
mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
|
||||||
|
|
||||||
mainrouter.Handle("/", api.AppHandlerIndependent(root)).Methods("GET")
|
mainrouter.Handle("/", api.AppHandlerIndependent(root)).Methods("GET")
|
||||||
|
mainrouter.Handle("/oauth/authorize", api.UserRequired(authorizeOAuth)).Methods("GET")
|
||||||
|
mainrouter.Handle("/oauth/access_token", api.ApiAppHandler(getAccessToken)).Methods("POST")
|
||||||
|
|
||||||
mainrouter.Handle("/signup_team_complete/", api.AppHandlerIndependent(signupTeamComplete)).Methods("GET")
|
mainrouter.Handle("/signup_team_complete/", api.AppHandlerIndependent(signupTeamComplete)).Methods("GET")
|
||||||
mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
|
mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
|
||||||
@@ -63,7 +64,7 @@ func InitWeb() {
|
|||||||
mainrouter.Handle("/admin_console", api.UserRequired(adminConsole)).Methods("GET")
|
mainrouter.Handle("/admin_console", api.UserRequired(adminConsole)).Methods("GET")
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------
|
||||||
// *ANYTHING* team spefic should go below this line
|
// *ANYTHING* team specific should go below this line
|
||||||
// ----------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}", api.AppHandler(login)).Methods("GET")
|
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}", api.AppHandler(login)).Methods("GET")
|
||||||
@@ -648,3 +649,192 @@ func adminConsole(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
page := NewHtmlTemplatePage("admin_console", "Admin Console")
|
page := NewHtmlTemplatePage("admin_console", "Admin Console")
|
||||||
page.Render(c, w)
|
page.Render(c, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authorizeOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
c.Err = model.NewAppError("authorizeOAuth", "The system admin has turned off OAuth service providing.", "")
|
||||||
|
c.Err.StatusCode = http.StatusNotImplemented
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !CheckBrowserCompatability(c, r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
responseType := r.URL.Query().Get("response_type")
|
||||||
|
clientId := r.URL.Query().Get("client_id")
|
||||||
|
redirect := r.URL.Query().Get("redirect_uri")
|
||||||
|
scope := r.URL.Query().Get("scope")
|
||||||
|
state := r.URL.Query().Get("state")
|
||||||
|
|
||||||
|
if len(responseType) == 0 || len(clientId) == 0 || len(redirect) == 0 {
|
||||||
|
c.Err = model.NewAppError("authorizeOAuth", "Missing one or more of response_type, client_id, or redirect_uri", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var app *model.OAuthApp
|
||||||
|
if result := <-api.Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
app = result.Data.(*model.OAuthApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var team *model.Team
|
||||||
|
if result := <-api.Srv.Store.Team().Get(c.Session.TeamId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
team = result.Data.(*model.Team)
|
||||||
|
}
|
||||||
|
|
||||||
|
page := NewHtmlTemplatePage("authorize", "Authorize Application")
|
||||||
|
page.Props["TeamName"] = team.Name
|
||||||
|
page.Props["AppName"] = app.Name
|
||||||
|
page.Props["ResponseType"] = responseType
|
||||||
|
page.Props["ClientId"] = clientId
|
||||||
|
page.Props["RedirectUri"] = redirect
|
||||||
|
page.Props["Scope"] = scope
|
||||||
|
page.Props["State"] = state
|
||||||
|
page.Render(c, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAccessToken(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "The system admin has turned off OAuth service providing.", "")
|
||||||
|
c.Err.StatusCode = http.StatusNotImplemented
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LogAudit("attempt")
|
||||||
|
|
||||||
|
r.ParseForm()
|
||||||
|
|
||||||
|
grantType := r.FormValue("grant_type")
|
||||||
|
if grantType != model.ACCESS_TOKEN_GRANT_TYPE {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_request: Bad grant_type", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientId := r.FormValue("client_id")
|
||||||
|
if len(clientId) != 26 {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_request: Bad client_id", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := r.FormValue("client_secret")
|
||||||
|
if len(secret) == 0 {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_request: Missing client_secret", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code := r.FormValue("code")
|
||||||
|
if len(code) == 0 {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_request: Missing code", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectUri := r.FormValue("redirect_uri")
|
||||||
|
|
||||||
|
achan := api.Srv.Store.OAuth().GetApp(clientId)
|
||||||
|
tchan := api.Srv.Store.OAuth().GetAccessDataByAuthCode(code)
|
||||||
|
|
||||||
|
authData := api.GetAuthData(code)
|
||||||
|
|
||||||
|
if authData == nil {
|
||||||
|
c.LogAudit("fail - invalid auth code")
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_grant: Invalid or expired authorization code", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uchan := api.Srv.Store.User().Get(authData.UserId)
|
||||||
|
|
||||||
|
if authData.IsExpired() {
|
||||||
|
c.LogAudit("fail - auth code expired")
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_grant: Invalid or expired authorization code", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if authData.RedirectUri != redirectUri {
|
||||||
|
c.LogAudit("fail - redirect uri provided did not match previous redirect uri")
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_request: Supplied redirect_uri does not match authorization code redirect_uri", "")
|
||||||
|
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.NewAppError("getAccessToken", "invalid_grant: Invalid or expired authorization code", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var app *model.OAuthApp
|
||||||
|
if result := <-achan; result.Err != nil {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_client: Invalid client credentials", "")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
app = result.Data.(*model.OAuthApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.ComparePassword(app.ClientSecret, secret) {
|
||||||
|
c.LogAudit("fail - invalid client credentials")
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_client: Invalid client credentials", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
callback := redirectUri
|
||||||
|
if len(callback) == 0 {
|
||||||
|
callback = app.CallbackUrls[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-tchan; result.Err != nil {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "server_error: Encountered internal server error while accessing database", "")
|
||||||
|
return
|
||||||
|
} else if result.Data != nil {
|
||||||
|
c.LogAudit("fail - auth code has been used previously")
|
||||||
|
accessData := result.Data.(*model.AccessData)
|
||||||
|
|
||||||
|
// Revoke access token, related auth code, and session from DB as well as from cache
|
||||||
|
if err := api.RevokeAccessToken(accessData.Token); err != nil {
|
||||||
|
l4g.Error("Encountered an error revoking an access token, err=" + err.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "invalid_grant: Authorization code already exchanged for an access token", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user *model.User
|
||||||
|
if result := <-uchan; result.Err != nil {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "server_error: Encountered internal server error while pulling user from database", "")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
user = result.Data.(*model.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, IsOAuth: true}
|
||||||
|
|
||||||
|
if result := <-api.Srv.Store.Session().Save(session); result.Err != nil {
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "server_error: Encountered internal server error while saving session to database", "")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
session = result.Data.(*model.Session)
|
||||||
|
api.AddSessionToCache(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessData := &model.AccessData{AuthCode: authData.Code, Token: session.Token, RedirectUri: callback}
|
||||||
|
|
||||||
|
if result := <-api.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
|
||||||
|
l4g.Error(result.Err)
|
||||||
|
c.Err = model.NewAppError("getAccessToken", "server_error: Encountered internal server error while saving access token to database", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accessRsp := &model.AccessResponse{AccessToken: session.Token, TokenType: model.ACCESS_TOKEN_TYPE, ExpiresIn: model.SESSION_TIME_OAUTH_IN_SECS}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
w.Header().Set("Pragma", "no-cache")
|
||||||
|
|
||||||
|
c.LogAuditWithUserId(user.Id, "success")
|
||||||
|
|
||||||
|
w.Write([]byte(accessRsp.ToJson()))
|
||||||
|
}
|
||||||
|
|||||||
134
web/web_test.go
134
web/web_test.go
@@ -6,8 +6,11 @@ package web
|
|||||||
import (
|
import (
|
||||||
"github.com/mattermost/platform/api"
|
"github.com/mattermost/platform/api"
|
||||||
"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"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -23,7 +26,7 @@ func Setup() {
|
|||||||
api.InitApi()
|
api.InitApi()
|
||||||
InitWeb()
|
InitWeb()
|
||||||
URL = "http://localhost:" + utils.Cfg.ServiceSettings.Port
|
URL = "http://localhost:" + utils.Cfg.ServiceSettings.Port
|
||||||
ApiClient = model.NewClient(URL + "/api/v1")
|
ApiClient = model.NewClient(URL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +51,135 @@ func TestStatic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetAccessToken(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||||
|
rteam, _ := ApiClient.CreateTeam(&team)
|
||||||
|
|
||||||
|
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Password: "pwd"}
|
||||||
|
ruser := ApiClient.Must(ApiClient.CreateUser(&user, "")).Data.(*model.User)
|
||||||
|
store.Must(api.Srv.Store.User().VerifyEmail(ruser.Id))
|
||||||
|
|
||||||
|
app := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
|
||||||
|
|
||||||
|
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||||
|
data := url.Values{"grant_type": []string{"junk"}, "client_id": []string{"12345678901234567890123456"}, "client_secret": []string{"12345678901234567890123456"}, "code": []string{"junk"}, "redirect_uri": []string{app.CallbackUrls[0]}}
|
||||||
|
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - oauth providing turned off")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ApiClient.Must(ApiClient.LoginById(ruser.Id, "pwd"))
|
||||||
|
app = ApiClient.Must(ApiClient.RegisterApp(app)).Data.(*model.OAuthApp)
|
||||||
|
|
||||||
|
redirect := ApiClient.Must(ApiClient.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, app.Id, app.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
|
||||||
|
rurl, _ := url.Parse(redirect)
|
||||||
|
|
||||||
|
ApiClient.Logout()
|
||||||
|
|
||||||
|
data := url.Values{"grant_type": []string{"junk"}, "client_id": []string{app.Id}, "client_secret": []string{app.ClientSecret}, "code": []string{rurl.Query().Get("code")}, "redirect_uri": []string{app.CallbackUrls[0]}}
|
||||||
|
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - bad grant type")
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||||
|
data.Set("client_id", "")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - missing client id")
|
||||||
|
}
|
||||||
|
data.Set("client_id", "junk")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - bad client id")
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("client_id", app.Id)
|
||||||
|
data.Set("client_secret", "")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - missing client secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("client_secret", "junk")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - bad client secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("client_secret", app.ClientSecret)
|
||||||
|
data.Set("code", "")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - missing code")
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("code", "junk")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - bad code")
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Set("code", rurl.Query().Get("code"))
|
||||||
|
data.Set("redirect_uri", "junk")
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - non-matching redirect uri")
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset data for successful request
|
||||||
|
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
|
||||||
|
data.Set("client_id", app.Id)
|
||||||
|
data.Set("client_secret", app.ClientSecret)
|
||||||
|
data.Set("code", rurl.Query().Get("code"))
|
||||||
|
data.Set("redirect_uri", app.CallbackUrls[0])
|
||||||
|
|
||||||
|
token := ""
|
||||||
|
if result, err := ApiClient.GetAccessToken(data); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
rsp := result.Data.(*model.AccessResponse)
|
||||||
|
if len(rsp.AccessToken) == 0 {
|
||||||
|
t.Fatal("access token not returned")
|
||||||
|
} else {
|
||||||
|
token = rsp.AccessToken
|
||||||
|
}
|
||||||
|
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
|
||||||
|
t.Fatal("access token type incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err := ApiClient.DoApiGet("/users/profiles?access_token="+token, "", ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
userMap := model.UserMapFromJson(result.Body)
|
||||||
|
if len(userMap) == 0 {
|
||||||
|
t.Fatal("user map empty - did not get results correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ApiClient.DoApiGet("/users/profiles", "", ""); err == nil {
|
||||||
|
t.Fatal("should have failed - no access token provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ApiClient.DoApiGet("/users/profiles?access_token=junk", "", ""); err == nil {
|
||||||
|
t.Fatal("should have failed - bad access token provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiClient.SetOAuthToken(token)
|
||||||
|
if result, err := ApiClient.DoApiGet("/users/profiles", "", ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
userMap := model.UserMapFromJson(result.Body)
|
||||||
|
if len(userMap) == 0 {
|
||||||
|
t.Fatal("user map empty - did not get results correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ApiClient.GetAccessToken(data); err == nil {
|
||||||
|
t.Fatal("should have failed - tried to reuse auth code")
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiClient.ClearOAuthToken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestZZWebTearDown(t *testing.T) {
|
func TestZZWebTearDown(t *testing.T) {
|
||||||
// *IMPORTANT*
|
// *IMPORTANT*
|
||||||
// This should be the last function in any test file
|
// This should be the last function in any test file
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user