mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
made oauth architecture more generalized
This commit is contained in:
61
api/user.go
61
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -385,3 +385,7 @@ func GitLabUserFromJson(data io.Reader) *GitLabUser {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (glu *GitLabUser) GetAuthData() string {
|
||||
return strconv.FormatInt(glu.Id, 10)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
108
web/web.go
108
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")
|
||||
func signupWithOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
|
||||
if len(teamId) != 26 {
|
||||
c.Err = model.NewAppError("signupWithGitLab", "Invalid team id", "team_id="+teamId)
|
||||
return
|
||||
}
|
||||
redirectUri := c.GetSiteURL() + "/signup/" + service + "/complete"
|
||||
|
||||
state := model.HashPassword(utils.Cfg.SSOSettings.GitLabId)
|
||||
|
||||
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 glu, err := api.AuthorizeGitLabUser(code, state, uri); err != nil {
|
||||
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 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")
|
||||
func loginWithOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
|
||||
if len(teamId) != 26 {
|
||||
c.Err = model.NewAppError("signupWithGitLab", "Invalid team id", "team_id="+teamId)
|
||||
return
|
||||
}
|
||||
redirectUri := c.GetSiteURL() + "/login/" + service + "/complete"
|
||||
|
||||
state := model.HashPassword(utils.Cfg.SSOSettings.GitLabId)
|
||||
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user