summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/user.go61
-rw-r--r--config/config.json12
-rw-r--r--model/user.go4
-rw-r--r--utils/config.go14
-rw-r--r--web/web.go108
5 files changed, 141 insertions, 58 deletions
diff --git a/api/user.go b/api/user.go
index 4765a5611..a9c8a0065 100644
--- a/api/user.go
+++ b/api/user.go
@@ -20,6 +20,7 @@ import (
_ "image/gif"
_ "image/jpeg"
"image/png"
+ "io"
"net/http"
"net/url"
"strconv"
@@ -1222,51 +1223,85 @@ func getStatuses(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
-func AuthorizeGitLabUser(code, state, uri string) (*model.GitLabUser, *model.AppError) {
- if !model.ComparePassword(state, utils.Cfg.SSOSettings.GitLabId) {
- return nil, model.NewAppError("AuthorizeGitLabUser", "Invalid state", "")
+func GetAuthorizationCode(c *Context, w http.ResponseWriter, r *http.Request, service, redirectUri string) {
+
+ teamId := r.FormValue("id")
+
+ if len(teamId) != 26 {
+ c.Err = model.NewAppError("GetAuthorizationCode", "Invalid team id", "team_id="+teamId)
+ c.Err.StatusCode = http.StatusBadRequest
+ return
+ }
+
+ // Make sure team exists
+ if result := <-Srv.Store.Team().Get(teamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ }
+
+ if s, ok := utils.Cfg.SSOSettings[service]; !ok || !s.Allow {
+ c.Err = model.NewAppError("GetAuthorizationCode", "Unsupported OAuth service provider", "service="+service)
+ c.Err.StatusCode = http.StatusBadRequest
+ return
+ }
+
+ clientId := utils.Cfg.SSOSettings[service].Id
+ endpoint := utils.Cfg.SSOSettings[service].AuthEndpoint
+ state := model.HashPassword(clientId)
+
+ authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri+"?id="+teamId) + "&state=" + url.QueryEscape(state)
+ http.Redirect(w, r, authUrl, http.StatusFound)
+}
+
+func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, *model.AppError) {
+ if s, ok := utils.Cfg.SSOSettings[service]; !ok || !s.Allow {
+ return nil, model.NewAppError("AuthorizeOAuthUser", "Unsupported OAuth service provider", "service="+service)
+ }
+
+ if !model.ComparePassword(state, utils.Cfg.SSOSettings[service].Id) {
+ return nil, model.NewAppError("AuthorizeOAuthUser", "Invalid state", "")
}
p := url.Values{}
- p.Set("client_id", utils.Cfg.SSOSettings.GitLabId)
- p.Set("client_secret", utils.Cfg.SSOSettings.GitLabSecret)
+ p.Set("client_id", utils.Cfg.SSOSettings[service].Id)
+ p.Set("client_secret", utils.Cfg.SSOSettings[service].Secret)
p.Set("code", code)
p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
- p.Set("redirect_uri", uri)
+ p.Set("redirect_uri", redirectUri)
client := &http.Client{}
- req, _ := http.NewRequest("POST", utils.Cfg.SSOSettings.GitLabUrl+"/oauth/token", strings.NewReader(p.Encode()))
+ req, _ := http.NewRequest("POST", utils.Cfg.SSOSettings[service].TokenEndpoint, strings.NewReader(p.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
var ar *model.AccessResponse
if resp, err := client.Do(req); err != nil {
- return nil, model.NewAppError("AuthorizeGitLabUser", "Token request to GitLab failed", err.Error())
+ return nil, model.NewAppError("AuthorizeOAuthUser", "Token request to GitLab failed", err.Error())
} else {
ar = model.AccessResponseFromJson(resp.Body)
}
if ar.TokenType != model.ACCESS_TOKEN_TYPE {
- return nil, model.NewAppError("AuthorizeGitLabUser", "Bad token type", "token_type="+ar.TokenType)
+ return nil, model.NewAppError("AuthorizeOAuthUser", "Bad token type", "token_type="+ar.TokenType)
}
if len(ar.AccessToken) == 0 {
- return nil, model.NewAppError("AuthorizeGitLabUser", "Missing access token", "")
+ return nil, model.NewAppError("AuthorizeOAuthUser", "Missing access token", "")
}
p = url.Values{}
p.Set("access_token", ar.AccessToken)
- req, _ = http.NewRequest("GET", utils.Cfg.SSOSettings.GitLabUrl+"/api/v3/user", strings.NewReader(""))
+ req, _ = http.NewRequest("GET", utils.Cfg.SSOSettings[service].UserApiEndpoint, strings.NewReader(""))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+ar.AccessToken)
if resp, err := client.Do(req); err != nil {
- return nil, model.NewAppError("AuthorizeGitLabUser", "Token request to GitLab failed", err.Error())
+ return nil, model.NewAppError("AuthorizeOAuthUser", "Token request to "+service+" failed", err.Error())
} else {
- return model.GitLabUserFromJson(resp.Body), nil
+ return resp.Body, nil
}
}
diff --git a/config/config.json b/config/config.json
index 61183948f..f92b873d1 100644
--- a/config/config.json
+++ b/config/config.json
@@ -24,10 +24,14 @@
"StorageDirectory": "./data/"
},
"SSOSettings": {
- "AllowGitLabSSO": true,
- "GitLabSecret" : "8526ada64f38a1a67cafe6650d54310f1484f8a5d06ad23abb9f8e4b8af1c429",
- "GitLabId": "0af4138195d246d5d4e958a93100379066bb087fa9892cd323b0c97bbd696008",
- "GitLabUrl": "http://dockerhost:8080"
+ "gitlab": {
+ "Allow": true,
+ "Secret" : "8526ada64f38a1a67cafe6650d54310f1484f8a5d06ad23abb9f8e4b8af1c429",
+ "Id": "0af4138195d246d5d4e958a93100379066bb087fa9892cd323b0c97bbd696008",
+ "AuthEndpoint": "http://dockerhost:8080/oauth/authorize",
+ "TokenEndpoint": "http://dockerhost:8080/oauth/token",
+ "UserApiEndpoint": "http://dockerhost:8080/api/v3/user"
+ }
},
"SqlSettings": {
"DriverName": "mysql",
diff --git a/model/user.go b/model/user.go
index 22158b6ac..78b033327 100644
--- a/model/user.go
+++ b/model/user.go
@@ -385,3 +385,7 @@ func GitLabUserFromJson(data io.Reader) *GitLabUser {
return nil
}
}
+
+func (glu *GitLabUser) GetAuthData() string {
+ return strconv.FormatInt(glu.Id, 10)
+}
diff --git a/utils/config.go b/utils/config.go
index 163c912bf..b90337b7e 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -32,11 +32,13 @@ type ServiceSettings struct {
StorageDirectory string
}
-type SSOSettings struct {
- AllowGitLabSSO bool
- GitLabSecret string
- GitLabId string
- GitLabUrl string
+type SSOSetting struct {
+ Allow bool
+ Secret string
+ Id string
+ AuthEndpoint string
+ TokenEndpoint string
+ UserApiEndpoint string
}
type SqlSettings struct {
@@ -116,7 +118,7 @@ type Config struct {
EmailSettings EmailSettings
PrivacySettings PrivacySettings
TeamSettings TeamSettings
- SSOSettings SSOSettings
+ SSOSettings map[string]SSOSetting
}
func (o *Config) ToJson() string {
diff --git a/web/web.go b/web/web.go
index 85901a8d2..71cf87335 100644
--- a/web/web.go
+++ b/web/web.go
@@ -14,7 +14,6 @@ import (
"gopkg.in/fsnotify.v1"
"html/template"
"net/http"
- "net/url"
"strconv"
"strings"
)
@@ -54,8 +53,8 @@ func InitWeb() {
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-]+}/login", api.AppHandler(login)).Methods("GET")
- mainrouter.Handle("/login/gitlab", api.AppHandlerIndependent(loginWithGitLab)).Methods("GET")
- mainrouter.Handle("/login/gitlab/complete", api.AppHandlerIndependent(loginCompleteGitLab)).Methods("GET")
+ mainrouter.Handle("/login/{service:[A-Za-z]+}", api.AppHandlerIndependent(loginWithOAuth)).Methods("GET")
+ mainrouter.Handle("/login/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(loginCompleteOAuth)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
@@ -67,8 +66,8 @@ func InitWeb() {
mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
mainrouter.Handle("/signup_team_confirm/", api.AppHandlerIndependent(signupTeamConfirm)).Methods("GET")
- mainrouter.Handle("/signup/gitlab", api.AppHandlerIndependent(signupWithGitLab)).Methods("GET")
- mainrouter.Handle("/signup/gitlab/complete", api.AppHandlerIndependent(signupCompleteGitLab)).Methods("GET")
+ mainrouter.Handle("/signup/{service:[A-Za-z]+}", api.AppHandlerIndependent(signupWithOAuth)).Methods("GET")
+ mainrouter.Handle("/signup/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(signupCompleteOAuth)).Methods("GET")
mainrouter.Handle("/verify_email", api.AppHandlerIndependent(verifyEmail)).Methods("GET")
mainrouter.Handle("/find_team", api.AppHandlerIndependent(findTeam)).Methods("GET")
@@ -449,33 +448,52 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) {
page.Render(c, w)
}
-func signupWithGitLab(c *api.Context, w http.ResponseWriter, r *http.Request) {
- teamId := r.FormValue("id")
-
- if len(teamId) != 26 {
- c.Err = model.NewAppError("signupWithGitLab", "Invalid team id", "team_id="+teamId)
- return
- }
+func signupWithOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
+ params := mux.Vars(r)
+ service := params["service"]
- state := model.HashPassword(utils.Cfg.SSOSettings.GitLabId)
+ redirectUri := c.GetSiteURL() + "/signup/" + service + "/complete"
- authUrl := utils.Cfg.SSOSettings.GitLabUrl + "/oauth/authorize"
- authUrl += "?response_type=code&client_id=" + utils.Cfg.SSOSettings.GitLabId + "&redirect_uri=" + url.QueryEscape("http://localhost:8065/signup/gitlab/complete?id="+teamId) + "&state=" + url.QueryEscape(state)
- http.Redirect(w, r, authUrl, http.StatusFound)
+ api.GetAuthorizationCode(c, w, r, service, redirectUri)
}
-func signupCompleteGitLab(c *api.Context, w http.ResponseWriter, r *http.Request) {
+func signupCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
+ params := mux.Vars(r)
+ service := params["service"]
+
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")
teamId := r.FormValue("id")
- uri := "http://localhost:8065/signup/gitlab/complete?id=" + teamId
+ uri := c.GetSiteURL() + "/signup/" + service + "/complete?id=" + teamId
+
+ if len(teamId) != 26 {
+ c.Err = model.NewAppError("signupCompleteOAuth", "Invalid team id", "team_id="+teamId)
+ c.Err.StatusCode = http.StatusBadRequest
+ return
+ }
+
+ // Make sure team exists
+ if result := <-api.Srv.Store.Team().Get(teamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ }
- if glu, err := api.AuthorizeGitLabUser(code, state, uri); err != nil {
+ if body, err := api.AuthorizeOAuthUser(service, code, state, uri); err != nil {
c.Err = err
return
} else {
- user := model.UserFromGitLabUser(glu)
+ var user *model.User
+ if service == model.USER_AUTH_SERVICE_GITLAB {
+ glu := model.GitLabUserFromJson(body)
+ user = model.UserFromGitLabUser(glu)
+ }
+
+ if user == nil {
+ c.Err = model.NewAppError("signupCompleteOAuth", "Could not create user out of "+service+" user object", "")
+ return
+ }
+
user.TeamId = teamId
page := NewHtmlTemplatePage("signup_user_oauth", "Complete User Sign Up")
@@ -484,34 +502,54 @@ func signupCompleteGitLab(c *api.Context, w http.ResponseWriter, r *http.Request
}
}
-func loginWithGitLab(c *api.Context, w http.ResponseWriter, r *http.Request) {
- teamId := r.FormValue("id")
-
- if len(teamId) != 26 {
- c.Err = model.NewAppError("signupWithGitLab", "Invalid team id", "team_id="+teamId)
- return
- }
+func loginWithOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
+ params := mux.Vars(r)
+ service := params["service"]
- state := model.HashPassword(utils.Cfg.SSOSettings.GitLabId)
+ redirectUri := c.GetSiteURL() + "/login/" + service + "/complete"
- authUrl := utils.Cfg.SSOSettings.GitLabUrl + "/oauth/authorize"
- authUrl += "?response_type=code&client_id=" + utils.Cfg.SSOSettings.GitLabId + "&redirect_uri=" + url.QueryEscape("http://localhost:8065/login/gitlab/complete?id="+teamId) + "&state=" + url.QueryEscape(state)
- http.Redirect(w, r, authUrl, http.StatusFound)
+ api.GetAuthorizationCode(c, w, r, service, redirectUri)
}
-func loginCompleteGitLab(c *api.Context, w http.ResponseWriter, r *http.Request) {
+func loginCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
+ params := mux.Vars(r)
+ service := params["service"]
+
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")
teamId := r.FormValue("id")
- uri := "http://localhost:8065/login/gitlab/complete?id=" + teamId
+ uri := c.GetSiteURL() + "/login/" + service + "/complete?id=" + teamId
- if glu, err := api.AuthorizeGitLabUser(code, state, uri); err != nil {
+ if len(teamId) != 26 {
+ c.Err = model.NewAppError("loginCompleteOAuth", "Invalid team id", "team_id="+teamId)
+ c.Err.StatusCode = http.StatusBadRequest
+ return
+ }
+
+ // Make sure team exists
+ if result := <-api.Srv.Store.Team().Get(teamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ }
+
+ if body, err := api.AuthorizeOAuthUser(service, code, state, uri); err != nil {
c.Err = err
return
} else {
+ authData := ""
+ if service == model.USER_AUTH_SERVICE_GITLAB {
+ glu := model.GitLabUserFromJson(body)
+ authData = glu.GetAuthData()
+ }
+
+ if len(authData) == 0 {
+ c.Err = model.NewAppError("loginCompleteOAuth", "Could not parse auth data out of "+service+" user object", "")
+ return
+ }
+
var user *model.User
- if result := <-api.Srv.Store.User().GetByAuth(teamId, strconv.FormatInt(glu.Id, 10), model.USER_AUTH_SERVICE_GITLAB); result.Err != nil {
+ if result := <-api.Srv.Store.User().GetByAuth(teamId, authData, service); result.Err != nil {
c.Err = result.Err
return
} else {