initial server implementation for using google as a single-sign-on oauth service
Этот коммит содержится в:
@@ -1314,9 +1314,15 @@ func GetAuthorizationCode(c *Context, w http.ResponseWriter, r *http.Request, te
|
|||||||
|
|
||||||
clientId := utils.Cfg.SSOSettings[service].Id
|
clientId := utils.Cfg.SSOSettings[service].Id
|
||||||
endpoint := utils.Cfg.SSOSettings[service].AuthEndpoint
|
endpoint := utils.Cfg.SSOSettings[service].AuthEndpoint
|
||||||
|
scope := utils.Cfg.SSOSettings[service].Scope
|
||||||
state := model.HashPassword(clientId)
|
state := model.HashPassword(clientId)
|
||||||
|
|
||||||
authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri+"?team="+teamName) + "&state=" + url.QueryEscape(state)
|
authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri+"?team="+teamName) + "&state=" + url.QueryEscape(state)
|
||||||
|
|
||||||
|
if len(scope) > 0 {
|
||||||
|
authUrl += "&scope=" + utils.UrlEncode(scope)
|
||||||
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, authUrl, http.StatusFound)
|
http.Redirect(w, r, authUrl, http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,19 @@
|
|||||||
"Allow": false,
|
"Allow": false,
|
||||||
"Secret" : "",
|
"Secret" : "",
|
||||||
"Id": "",
|
"Id": "",
|
||||||
|
"Scope": "",
|
||||||
"AuthEndpoint": "",
|
"AuthEndpoint": "",
|
||||||
"TokenEndpoint": "",
|
"TokenEndpoint": "",
|
||||||
"UserApiEndpoint": ""
|
"UserApiEndpoint": ""
|
||||||
|
},
|
||||||
|
"google": {
|
||||||
|
"Allow": true,
|
||||||
|
"Secret": "hWN1kiPeyihN6nHr36j0oDqx",
|
||||||
|
"Id": "974585606220-ek5q66olpagb069pkg9ok0d7h8djeov6.apps.googleusercontent.com",
|
||||||
|
"Scope": "email profile",
|
||||||
|
"AuthEndpoint": "https://accounts.google.com/o/oauth2/auth",
|
||||||
|
"TokenEndpoint": "https://www.googleapis.com/oauth2/v3/token",
|
||||||
|
"UserApiEndpoint": "https://www.googleapis.com/plus/v1/people/me"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SqlSettings": {
|
"SqlSettings": {
|
||||||
|
|||||||
57
model/gitlab.go
Обычный файл
57
model/gitlab.go
Обычный файл
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
USER_AUTH_SERVICE_GITLAB = "gitlab"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitLabUser struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserFromGitLabUser(glu *GitLabUser) *User {
|
||||||
|
user := &User{}
|
||||||
|
user.Username = glu.Username
|
||||||
|
splitName := strings.Split(glu.Name, " ")
|
||||||
|
if len(splitName) == 2 {
|
||||||
|
user.FirstName = splitName[0]
|
||||||
|
user.LastName = splitName[1]
|
||||||
|
} else if len(splitName) >= 2 {
|
||||||
|
user.FirstName = splitName[0]
|
||||||
|
user.LastName = strings.Join(splitName[1:], " ")
|
||||||
|
} else {
|
||||||
|
user.FirstName = glu.Name
|
||||||
|
}
|
||||||
|
user.Email = glu.Email
|
||||||
|
user.AuthData = strconv.FormatInt(glu.Id, 10)
|
||||||
|
user.AuthService = USER_AUTH_SERVICE_GITLAB
|
||||||
|
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
func GitLabUserFromJson(data io.Reader) *GitLabUser {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var glu GitLabUser
|
||||||
|
err := decoder.Decode(&glu)
|
||||||
|
if err == nil {
|
||||||
|
return &glu
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (glu *GitLabUser) GetAuthData() string {
|
||||||
|
return strconv.FormatInt(glu.Id, 10)
|
||||||
|
}
|
||||||
54
model/google.go
Обычный файл
54
model/google.go
Обычный файл
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
USER_AUTH_SERVICE_GOOGLE = "google"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoogleUser struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Emails []map[string]string `json:"emails"`
|
||||||
|
Names map[string]string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserFromGoogleUser(gu *GoogleUser) *User {
|
||||||
|
user := &User{}
|
||||||
|
user.Username = gu.Nickname
|
||||||
|
user.FirstName = gu.Names["givenName"]
|
||||||
|
user.LastName = gu.Names["familyName"]
|
||||||
|
user.Nickname = gu.Nickname
|
||||||
|
|
||||||
|
for _, e := range gu.Emails {
|
||||||
|
if e["type"] == "account" {
|
||||||
|
user.Email = e["value"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user.AuthData = gu.Id
|
||||||
|
user.AuthService = USER_AUTH_SERVICE_GOOGLE
|
||||||
|
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
func GoogleUserFromJson(data io.Reader) *GoogleUser {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var gu GoogleUser
|
||||||
|
err := decoder.Decode(&gu)
|
||||||
|
if err == nil {
|
||||||
|
return &gu
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gu *GoogleUser) GetAuthData() string {
|
||||||
|
return gu.Id
|
||||||
|
}
|
||||||
@@ -8,24 +8,22 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ROLE_ADMIN = "admin"
|
ROLE_ADMIN = "admin"
|
||||||
ROLE_SYSTEM_ADMIN = "system_admin"
|
ROLE_SYSTEM_ADMIN = "system_admin"
|
||||||
ROLE_SYSTEM_SUPPORT = "system_support"
|
ROLE_SYSTEM_SUPPORT = "system_support"
|
||||||
USER_AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutes
|
USER_AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutes
|
||||||
USER_OFFLINE_TIMEOUT = 1 * 60 * 1000 // 1 minute
|
USER_OFFLINE_TIMEOUT = 1 * 60 * 1000 // 1 minute
|
||||||
USER_OFFLINE = "offline"
|
USER_OFFLINE = "offline"
|
||||||
USER_AWAY = "away"
|
USER_AWAY = "away"
|
||||||
USER_ONLINE = "online"
|
USER_ONLINE = "online"
|
||||||
USER_NOTIFY_ALL = "all"
|
USER_NOTIFY_ALL = "all"
|
||||||
USER_NOTIFY_MENTION = "mention"
|
USER_NOTIFY_MENTION = "mention"
|
||||||
USER_NOTIFY_NONE = "none"
|
USER_NOTIFY_NONE = "none"
|
||||||
BOT_USERNAME = "valet"
|
BOT_USERNAME = "valet"
|
||||||
USER_AUTH_SERVICE_GITLAB = "gitlab"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@@ -54,13 +52,6 @@ type User struct {
|
|||||||
FailedAttempts int `json:"failed_attempts"`
|
FailedAttempts int `json:"failed_attempts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GitLabUser struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid validates the user and returns an error if it isn't configured
|
// IsValid validates the user and returns an error if it isn't configured
|
||||||
// correctly.
|
// correctly.
|
||||||
func (u *User) IsValid() *AppError {
|
func (u *User) IsValid() *AppError {
|
||||||
@@ -355,38 +346,3 @@ func IsUsernameValid(username string) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserFromGitLabUser(glu *GitLabUser) *User {
|
|
||||||
user := &User{}
|
|
||||||
user.Username = glu.Username
|
|
||||||
splitName := strings.Split(glu.Name, " ")
|
|
||||||
if len(splitName) == 2 {
|
|
||||||
user.FirstName = splitName[0]
|
|
||||||
user.LastName = splitName[1]
|
|
||||||
} else if len(splitName) >= 2 {
|
|
||||||
user.FirstName = splitName[0]
|
|
||||||
user.LastName = strings.Join(splitName[1:], " ")
|
|
||||||
} else {
|
|
||||||
user.FirstName = glu.Name
|
|
||||||
}
|
|
||||||
user.Email = glu.Email
|
|
||||||
user.AuthData = strconv.FormatInt(glu.Id, 10)
|
|
||||||
user.AuthService = USER_AUTH_SERVICE_GITLAB
|
|
||||||
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
func GitLabUserFromJson(data io.Reader) *GitLabUser {
|
|
||||||
decoder := json.NewDecoder(data)
|
|
||||||
var glu GitLabUser
|
|
||||||
err := decoder.Decode(&glu)
|
|
||||||
if err == nil {
|
|
||||||
return &glu
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (glu *GitLabUser) GetAuthData() string {
|
|
||||||
return strconv.FormatInt(glu.Id, 10)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type SSOSetting struct {
|
|||||||
Allow bool
|
Allow bool
|
||||||
Secret string
|
Secret string
|
||||||
Id string
|
Id string
|
||||||
|
Scope string
|
||||||
AuthEndpoint string
|
AuthEndpoint string
|
||||||
TokenEndpoint string
|
TokenEndpoint string
|
||||||
UserApiEndpoint string
|
UserApiEndpoint string
|
||||||
|
|||||||
@@ -53,13 +53,13 @@ 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-]+}/", api.AppHandler(login)).Methods("GET")
|
||||||
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
|
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
|
||||||
|
|
||||||
// Bug in gorilla.mux pervents us from using regex here.
|
// Bug in gorilla.mux prevents us from using regex here.
|
||||||
mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET")
|
mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET")
|
||||||
mainrouter.Handle("/login/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(loginCompleteOAuth)).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-]+}/logout", api.AppHandler(logout)).Methods("GET")
|
||||||
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
|
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
|
||||||
// Bug in gorilla.mux pervents us from using regex here.
|
// Bug in gorilla.mux prevents us from using regex here.
|
||||||
mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET")
|
mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET")
|
||||||
|
|
||||||
// Anything added here must have an _ in it so it does not conflict with team names
|
// Anything added here must have an _ in it so it does not conflict with team names
|
||||||
@@ -67,7 +67,7 @@ func InitWeb() {
|
|||||||
mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
|
mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
|
||||||
mainrouter.Handle("/signup_team_confirm/", api.AppHandlerIndependent(signupTeamConfirm)).Methods("GET")
|
mainrouter.Handle("/signup_team_confirm/", api.AppHandlerIndependent(signupTeamConfirm)).Methods("GET")
|
||||||
|
|
||||||
// Bug in gorilla.mux pervents us from using regex here.
|
// Bug in gorilla.mux prevents us from using regex here.
|
||||||
mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET")
|
mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET")
|
||||||
mainrouter.Handle("/signup/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(signupCompleteOAuth)).Methods("GET")
|
mainrouter.Handle("/signup/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(signupCompleteOAuth)).Methods("GET")
|
||||||
|
|
||||||
@@ -532,6 +532,9 @@ func signupCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request)
|
|||||||
if service == model.USER_AUTH_SERVICE_GITLAB {
|
if service == model.USER_AUTH_SERVICE_GITLAB {
|
||||||
glu := model.GitLabUserFromJson(body)
|
glu := model.GitLabUserFromJson(body)
|
||||||
user = model.UserFromGitLabUser(glu)
|
user = model.UserFromGitLabUser(glu)
|
||||||
|
} else if service == model.USER_AUTH_SERVICE_GOOGLE {
|
||||||
|
gu := model.GoogleUserFromJson(body)
|
||||||
|
user = model.UserFromGoogleUser(gu)
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user