Additional OAuth2 providers (#1010)
* add google+ * sort signin oauth2 providers based on the name so order is always the same * update auth tip for google+ * add gitlab provider * add bitbucket provider (and some go fmt) * add twitter provider * add facebook provider * add dropbox provider * add openid connect provider incl. new format of tips section in "Add New Source" * lower the amount of disk storage for each session to prevent issues while building cross platform (and disk overflow) * imports according to goimport and code style * make it possible to set custom urls to gitlab and github provider (only these could have a different host) * split up oauth2 into multiple files * small typo in comment * fix indention * fix indentation * fix new line before external import * fix layout of signin part * update "broken" dependency
This commit is contained in:
parent
2368bbb672
commit
950f2e2074
44 changed files with 4164 additions and 159 deletions
24
models/error_oauth2.go
Normal file
24
models/error_oauth2.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrOpenIDConnectInitialize represents a "OpenIDConnectInitialize" kind of error.
|
||||
type ErrOpenIDConnectInitialize struct {
|
||||
OpenIDConnectAutoDiscoveryURL string
|
||||
ProviderName string
|
||||
Cause error
|
||||
}
|
||||
|
||||
// IsErrOpenIDConnectInitialize checks if an error is a ExternalLoginUserAlreadyExist.
|
||||
func IsErrOpenIDConnectInitialize(err error) bool {
|
||||
_, ok := err.(ErrOpenIDConnectInitialize)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrOpenIDConnectInitialize) Error() string {
|
||||
return fmt.Sprintf("Failed to initialize OpenID Connect Provider with name '%s' with url '%s': %v", err.ProviderName, err.OpenIDConnectAutoDiscoveryURL, err.Cause)
|
||||
}
|
|
@ -121,9 +121,11 @@ func (cfg *PAMConfig) ToDB() ([]byte, error) {
|
|||
|
||||
// OAuth2Config holds configuration for the OAuth2 login source.
|
||||
type OAuth2Config struct {
|
||||
Provider string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Provider string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
OpenIDConnectAutoDiscoveryURL string
|
||||
CustomURLMapping *oauth2.CustomURLMapping
|
||||
}
|
||||
|
||||
// FromDB fills up an OAuth2Config from serialized format.
|
||||
|
@ -294,9 +296,15 @@ func CreateLoginSource(source *LoginSource) error {
|
|||
}
|
||||
|
||||
_, err = x.Insert(source)
|
||||
if err == nil && source.IsOAuth2() {
|
||||
if err == nil && source.IsOAuth2() && source.IsActived {
|
||||
oAuth2Config := source.OAuth2()
|
||||
oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret)
|
||||
err = oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping)
|
||||
err = wrapOpenIDConnectInitializeError(err, source.Name, oAuth2Config)
|
||||
|
||||
if err != nil {
|
||||
// remove the LoginSource in case of errors while registering OAuth2 providers
|
||||
x.Delete(source)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -321,11 +329,25 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
|
|||
|
||||
// UpdateSource updates a LoginSource record in DB.
|
||||
func UpdateSource(source *LoginSource) error {
|
||||
var originalLoginSource *LoginSource
|
||||
if source.IsOAuth2() {
|
||||
// keep track of the original values so we can restore in case of errors while registering OAuth2 providers
|
||||
var err error
|
||||
if originalLoginSource, err = GetLoginSourceByID(source.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := x.Id(source.ID).AllCols().Update(source)
|
||||
if err == nil && source.IsOAuth2() {
|
||||
if err == nil && source.IsOAuth2() && source.IsActived {
|
||||
oAuth2Config := source.OAuth2()
|
||||
oauth2.RemoveProvider(source.Name)
|
||||
oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret)
|
||||
err = oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping)
|
||||
err = wrapOpenIDConnectInitializeError(err, source.Name, oAuth2Config)
|
||||
|
||||
if err != nil {
|
||||
// restore original values since we cannot update the provider it self
|
||||
x.Id(source.ID).AllCols().Update(originalLoginSource)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -580,27 +602,6 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
|
|||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
// ________ _____ __ .__ ________
|
||||
// \_____ \ / _ \ __ ___/ |_| |__ \_____ \
|
||||
// / | \ / /_\ \| | \ __\ | \ / ____/
|
||||
// / | \/ | \ | /| | | Y \/ \
|
||||
// \_______ /\____|__ /____/ |__| |___| /\_______ \
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// OAuth2Provider describes the display values of a single OAuth2 provider
|
||||
type OAuth2Provider struct {
|
||||
Name string
|
||||
DisplayName string
|
||||
Image string
|
||||
}
|
||||
|
||||
// OAuth2Providers contains the map of registered OAuth2 providers in Gitea (based on goth)
|
||||
// key is used to map the OAuth2Provider with the goth provider type (also in LoginSource.OAuth2Config.Provider)
|
||||
// value is used to store display data
|
||||
var OAuth2Providers = map[string]OAuth2Provider{
|
||||
"github": {Name: "github", DisplayName: "GitHub", Image: "/img/github.png"},
|
||||
}
|
||||
|
||||
// ExternalUserLogin attempts a login using external source types.
|
||||
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
if !source.IsActived {
|
||||
|
@ -684,59 +685,4 @@ func UserSignIn(username, password string) (*User, error) {
|
|||
}
|
||||
|
||||
return nil, ErrUserNotExist{user.ID, user.Name, 0}
|
||||
}
|
||||
|
||||
// GetActiveOAuth2ProviderLoginSources returns all actived LoginOAuth2 sources
|
||||
func GetActiveOAuth2ProviderLoginSources() ([]*LoginSource, error) {
|
||||
sources := make([]*LoginSource, 0, 1)
|
||||
if err := x.UseBool().Find(&sources, &LoginSource{IsActived: true, Type: LoginOAuth2}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
// GetActiveOAuth2LoginSourceByName returns a OAuth2 LoginSource based on the given name
|
||||
func GetActiveOAuth2LoginSourceByName(name string) (*LoginSource, error) {
|
||||
loginSource := &LoginSource{
|
||||
Name: name,
|
||||
Type: LoginOAuth2,
|
||||
IsActived: true,
|
||||
}
|
||||
|
||||
has, err := x.UseBool().Get(loginSource)
|
||||
if !has || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return loginSource, nil
|
||||
}
|
||||
|
||||
// GetActiveOAuth2Providers returns the map of configured active OAuth2 providers
|
||||
// key is used as technical name (like in the callbackURL)
|
||||
// values to display
|
||||
func GetActiveOAuth2Providers() (map[string]OAuth2Provider, error) {
|
||||
// Maybe also separate used and unused providers so we can force the registration of only 1 active provider for each type
|
||||
|
||||
loginSources, err := GetActiveOAuth2ProviderLoginSources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
providers := make(map[string]OAuth2Provider)
|
||||
for _, source := range loginSources {
|
||||
providers[source.Name] = OAuth2Providers[source.OAuth2().Provider]
|
||||
}
|
||||
|
||||
return providers, nil
|
||||
}
|
||||
|
||||
// InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library
|
||||
func InitOAuth2() {
|
||||
oauth2.Init()
|
||||
loginSources, _ := GetActiveOAuth2ProviderLoginSources()
|
||||
|
||||
for _, source := range loginSources {
|
||||
oAuth2Config := source.OAuth2()
|
||||
oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret)
|
||||
}
|
||||
}
|
||||
}
|
122
models/oauth2.go
Normal file
122
models/oauth2.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||
)
|
||||
|
||||
// OAuth2Provider describes the display values of a single OAuth2 provider
|
||||
type OAuth2Provider struct {
|
||||
Name string
|
||||
DisplayName string
|
||||
Image string
|
||||
CustomURLMapping *oauth2.CustomURLMapping
|
||||
}
|
||||
|
||||
// OAuth2Providers contains the map of registered OAuth2 providers in Gitea (based on goth)
|
||||
// key is used to map the OAuth2Provider with the goth provider type (also in LoginSource.OAuth2Config.Provider)
|
||||
// value is used to store display data
|
||||
var OAuth2Providers = map[string]OAuth2Provider{
|
||||
"bitbucket": {Name: "bitbucket", DisplayName: "Bitbucket", Image: "/img/auth/bitbucket.png"},
|
||||
"dropbox": {Name: "dropbox", DisplayName: "Dropbox", Image: "/img/auth/dropbox.png"},
|
||||
"facebook": {Name: "facebook", DisplayName: "Facebook", Image: "/img/auth/facebook.png"},
|
||||
"github": {Name: "github", DisplayName: "GitHub", Image: "/img/auth/github.png",
|
||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
||||
TokenURL: oauth2.GetDefaultTokenURL("github"),
|
||||
AuthURL: oauth2.GetDefaultAuthURL("github"),
|
||||
ProfileURL: oauth2.GetDefaultProfileURL("github"),
|
||||
EmailURL: oauth2.GetDefaultEmailURL("github"),
|
||||
},
|
||||
},
|
||||
"gitlab": {Name: "gitlab", DisplayName: "GitLab", Image: "/img/auth/gitlab.png",
|
||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
||||
TokenURL: oauth2.GetDefaultTokenURL("gitlab"),
|
||||
AuthURL: oauth2.GetDefaultAuthURL("gitlab"),
|
||||
ProfileURL: oauth2.GetDefaultProfileURL("gitlab"),
|
||||
},
|
||||
},
|
||||
"gplus": {Name: "gplus", DisplayName: "Google+", Image: "/img/auth/google_plus.png"},
|
||||
"openidConnect": {Name: "openidConnect", DisplayName: "OpenID Connect", Image: "/img/auth/openid_connect.png"},
|
||||
"twitter": {Name: "twitter", DisplayName: "Twitter", Image: "/img/auth/twitter.png"},
|
||||
}
|
||||
|
||||
// OAuth2DefaultCustomURLMappings contains the map of default URL's for OAuth2 providers that are allowed to have custom urls
|
||||
// key is used to map the OAuth2Provider
|
||||
// value is the mapping as defined for the OAuth2Provider
|
||||
var OAuth2DefaultCustomURLMappings = map[string]*oauth2.CustomURLMapping {
|
||||
"github": OAuth2Providers["github"].CustomURLMapping,
|
||||
"gitlab": OAuth2Providers["gitlab"].CustomURLMapping,
|
||||
}
|
||||
|
||||
// GetActiveOAuth2ProviderLoginSources returns all actived LoginOAuth2 sources
|
||||
func GetActiveOAuth2ProviderLoginSources() ([]*LoginSource, error) {
|
||||
sources := make([]*LoginSource, 0, 1)
|
||||
if err := x.UseBool().Find(&sources, &LoginSource{IsActived: true, Type: LoginOAuth2}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
// GetActiveOAuth2LoginSourceByName returns a OAuth2 LoginSource based on the given name
|
||||
func GetActiveOAuth2LoginSourceByName(name string) (*LoginSource, error) {
|
||||
loginSource := &LoginSource{
|
||||
Name: name,
|
||||
Type: LoginOAuth2,
|
||||
IsActived: true,
|
||||
}
|
||||
|
||||
has, err := x.UseBool().Get(loginSource)
|
||||
if !has || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return loginSource, nil
|
||||
}
|
||||
|
||||
// GetActiveOAuth2Providers returns the map of configured active OAuth2 providers
|
||||
// key is used as technical name (like in the callbackURL)
|
||||
// values to display
|
||||
func GetActiveOAuth2Providers() ([]string, map[string]OAuth2Provider, error) {
|
||||
// Maybe also separate used and unused providers so we can force the registration of only 1 active provider for each type
|
||||
|
||||
loginSources, err := GetActiveOAuth2ProviderLoginSources()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var orderedKeys []string
|
||||
providers := make(map[string]OAuth2Provider)
|
||||
for _, source := range loginSources {
|
||||
providers[source.Name] = OAuth2Providers[source.OAuth2().Provider]
|
||||
orderedKeys = append(orderedKeys, source.Name)
|
||||
}
|
||||
|
||||
sort.Strings(orderedKeys)
|
||||
|
||||
return orderedKeys, providers, nil
|
||||
}
|
||||
|
||||
// InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library
|
||||
func InitOAuth2() {
|
||||
oauth2.Init()
|
||||
loginSources, _ := GetActiveOAuth2ProviderLoginSources()
|
||||
|
||||
for _, source := range loginSources {
|
||||
oAuth2Config := source.OAuth2()
|
||||
oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping)
|
||||
}
|
||||
}
|
||||
|
||||
// wrapOpenIDConnectInitializeError is used to wrap the error but this cannot be done in modules/auth/oauth2
|
||||
// inside oauth2: import cycle not allowed models -> modules/auth/oauth2 -> models
|
||||
func wrapOpenIDConnectInitializeError(err error, providerName string, oAuth2Config *OAuth2Config) error {
|
||||
if err != nil && "openidConnect" == oAuth2Config.Provider {
|
||||
err = ErrOpenIDConnectInitialize{ProviderName: providerName, OpenIDConnectAutoDiscoveryURL: oAuth2Config.OpenIDConnectAutoDiscoveryURL, Cause: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue