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:
Willem van Dreumel 2017-05-01 15:26:53 +02:00 committed by Lunny Xiao
parent 2368bbb672
commit 950f2e2074
44 changed files with 4164 additions and 159 deletions

View file

@ -11,34 +11,40 @@ import (
// AuthenticationForm form for authentication
type AuthenticationForm struct {
ID int64
Type int `binding:"Range(2,6)"`
Name string `binding:"Required;MaxSize(30)"`
Host string
Port int
BindDN string
BindPassword string
UserBase string
UserDN string
AttributeUsername string
AttributeName string
AttributeSurname string
AttributeMail string
AttributesInBind bool
Filter string
AdminFilter string
IsActive bool
SMTPAuth string
SMTPHost string
SMTPPort int
AllowedDomains string
SecurityProtocol int `binding:"Range(0,2)"`
TLS bool
SkipVerify bool
PAMServiceName string
Oauth2Provider string
Oauth2Key string
Oauth2Secret string
ID int64
Type int `binding:"Range(2,6)"`
Name string `binding:"Required;MaxSize(30)"`
Host string
Port int
BindDN string
BindPassword string
UserBase string
UserDN string
AttributeUsername string
AttributeName string
AttributeSurname string
AttributeMail string
AttributesInBind bool
Filter string
AdminFilter string
IsActive bool
SMTPAuth string
SMTPHost string
SMTPPort int
AllowedDomains string
SecurityProtocol int `binding:"Range(0,2)"`
TLS bool
SkipVerify bool
PAMServiceName string
Oauth2Provider string
Oauth2Key string
Oauth2Secret string
OpenIDConnectAutoDiscoveryURL string
Oauth2UseCustomURL bool
Oauth2TokenURL string
Oauth2AuthURL string
Oauth2ProfileURL string
Oauth2EmailURL string
}
// Validate validates fields

View file

@ -5,6 +5,7 @@
package oauth2
import (
"math"
"net/http"
"os"
"path/filepath"
@ -15,7 +16,14 @@ import (
"github.com/gorilla/sessions"
"github.com/markbates/goth"
"github.com/markbates/goth/gothic"
"github.com/markbates/goth/providers/bitbucket"
"github.com/markbates/goth/providers/dropbox"
"github.com/markbates/goth/providers/facebook"
"github.com/markbates/goth/providers/github"
"github.com/markbates/goth/providers/gitlab"
"github.com/markbates/goth/providers/gplus"
"github.com/markbates/goth/providers/openidConnect"
"github.com/markbates/goth/providers/twitter"
"github.com/satori/go.uuid"
)
@ -24,6 +32,14 @@ var (
providerHeaderKey = "gitea-oauth2-provider"
)
// CustomURLMapping describes the urls values to use when customizing OAuth2 provider URLs
type CustomURLMapping struct {
AuthURL string
TokenURL string
ProfileURL string
EmailURL string
}
// Init initialize the setup of the OAuth2 library
func Init() {
sessionDir := filepath.Join(setting.AppDataPath, "sessions", "oauth2")
@ -31,7 +47,15 @@ func Init() {
log.Fatal(4, "Fail to create dir %s: %v", sessionDir, err)
}
gothic.Store = sessions.NewFilesystemStore(sessionDir, []byte(sessionUsersStoreKey))
store := sessions.NewFilesystemStore(sessionDir, []byte(sessionUsersStoreKey))
// according to the Goth lib:
// set the maxLength of the cookies stored on the disk to a larger number to prevent issues with:
// securecookie: the value is too long
// when using OpenID Connect , since this can contain a large amount of extra information in the id_token
// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
store.MaxLength(math.MaxInt16)
gothic.Store = store
gothic.SetState = func(req *http.Request) string {
return uuid.NewV4().String()
@ -74,12 +98,14 @@ func ProviderCallback(provider string, request *http.Request, response http.Resp
}
// RegisterProvider register a OAuth2 provider in goth lib
func RegisterProvider(providerName, providerType, clientID, clientSecret string) {
provider := createProvider(providerName, providerType, clientID, clientSecret)
func RegisterProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) error {
provider, err := createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL, customURLMapping)
if provider != nil {
if err == nil && provider != nil {
goth.UseProviders(provider)
}
return err
}
// RemoveProvider removes the given OAuth2 provider from the goth lib
@ -88,20 +114,111 @@ func RemoveProvider(providerName string) {
}
// used to create different types of goth providers
func createProvider(providerName, providerType, clientID, clientSecret string) goth.Provider {
func createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) (goth.Provider, error) {
callbackURL := setting.AppURL + "user/oauth2/" + providerName + "/callback"
var provider goth.Provider
var err error
switch providerType {
case "bitbucket":
provider = bitbucket.New(clientID, clientSecret, callbackURL, "account")
case "dropbox":
provider = dropbox.New(clientID, clientSecret, callbackURL)
case "facebook":
provider = facebook.New(clientID, clientSecret, callbackURL, "email")
case "github":
provider = github.New(clientID, clientSecret, callbackURL, "user:email")
authURL := github.AuthURL
tokenURL := github.TokenURL
profileURL := github.ProfileURL
emailURL := github.EmailURL
if customURLMapping != nil {
if len(customURLMapping.AuthURL) > 0 {
authURL = customURLMapping.AuthURL
}
if len(customURLMapping.TokenURL) > 0 {
tokenURL = customURLMapping.TokenURL
}
if len(customURLMapping.ProfileURL) > 0 {
profileURL = customURLMapping.ProfileURL
}
if len(customURLMapping.EmailURL) > 0 {
emailURL = customURLMapping.EmailURL
}
}
provider = github.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, emailURL)
case "gitlab":
authURL := gitlab.AuthURL
tokenURL := gitlab.TokenURL
profileURL := gitlab.ProfileURL
if customURLMapping != nil {
if len(customURLMapping.AuthURL) > 0 {
authURL = customURLMapping.AuthURL
}
if len(customURLMapping.TokenURL) > 0 {
tokenURL = customURLMapping.TokenURL
}
if len(customURLMapping.ProfileURL) > 0 {
profileURL = customURLMapping.ProfileURL
}
}
provider = gitlab.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL)
case "gplus":
provider = gplus.New(clientID, clientSecret, callbackURL, "email")
case "openidConnect":
if provider, err = openidConnect.New(clientID, clientSecret, callbackURL, openIDConnectAutoDiscoveryURL); err != nil {
log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, openIDConnectAutoDiscoveryURL, err)
}
case "twitter":
provider = twitter.NewAuthenticate(clientID, clientSecret, callbackURL)
}
// always set the name if provider is created so we can support multiple setups of 1 provider
if provider != nil {
if err == nil && provider != nil {
provider.SetName(providerName)
}
return provider
return provider, err
}
// GetDefaultTokenURL return the default token url for the given provider
func GetDefaultTokenURL(provider string) string {
switch provider {
case "github":
return github.TokenURL
case "gitlab":
return gitlab.TokenURL
}
return ""
}
// GetDefaultAuthURL return the default authorize url for the given provider
func GetDefaultAuthURL(provider string) string {
switch provider {
case "github":
return github.AuthURL
case "gitlab":
return gitlab.AuthURL
}
return ""
}
// GetDefaultProfileURL return the default profile url for the given provider
func GetDefaultProfileURL(provider string) string {
switch provider {
case "github":
return github.ProfileURL
case "gitlab":
return gitlab.ProfileURL
}
return ""
}
// GetDefaultEmailURL return the default email url for the given provider
func GetDefaultEmailURL(provider string) string {
switch provider {
case "github":
return github.EmailURL
}
return ""
}