diff options
author | zeripath <art27@cantab.net> | 2021-08-06 02:11:08 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-05 21:11:08 -0400 |
commit | ab9bb54144f136bbbba2ce2e94fd88c0be0ee1cf (patch) | |
tree | f8b283e33e70cbc3d827879c4774de2b41450ab0 /services/auth/source/oauth2/providers.go | |
parent | 7e7006e00d8d0d5ce4c871685d421269049e4b39 (diff) | |
download | gitea-ab9bb54144f136bbbba2ce2e94fd88c0be0ee1cf.tar.gz gitea-ab9bb54144f136bbbba2ce2e94fd88c0be0ee1cf.zip |
Add microsoft oauth2 providers (#16544)
* Clean up oauth2 providers
Signed-off-by: Andrew Thornton <art27@cantab.net>
* Add AzureAD, AzureADv2, MicrosoftOnline OAuth2 providers
Signed-off-by: Andrew Thornton <art27@cantab.net>
* Apply suggestions from code review
* remove unused Scopes
Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'services/auth/source/oauth2/providers.go')
-rw-r--r-- | services/auth/source/oauth2/providers.go | 252 |
1 files changed, 75 insertions, 177 deletions
diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index 8df8d62961..2196e30492 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -13,80 +13,72 @@ import ( "code.gitea.io/gitea/modules/setting" "github.com/markbates/goth" - "github.com/markbates/goth/providers/bitbucket" - "github.com/markbates/goth/providers/discord" - "github.com/markbates/goth/providers/dropbox" - "github.com/markbates/goth/providers/facebook" - "github.com/markbates/goth/providers/gitea" - "github.com/markbates/goth/providers/github" - "github.com/markbates/goth/providers/gitlab" - "github.com/markbates/goth/providers/google" - "github.com/markbates/goth/providers/mastodon" - "github.com/markbates/goth/providers/nextcloud" - "github.com/markbates/goth/providers/openidConnect" - "github.com/markbates/goth/providers/twitter" - "github.com/markbates/goth/providers/yandex" ) -// Provider describes the display values of a single OAuth2 provider -type Provider struct { - Name string - DisplayName string - Image string - CustomURLMapping *CustomURLMapping +// Provider is an interface for describing a single OAuth2 provider +type Provider interface { + Name() string + DisplayName() string + Image() string + CustomURLSettings() *CustomURLSettings +} + +// GothProviderCreator provides a function to create a goth.Provider +type GothProviderCreator interface { + CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error) +} + +// GothProvider is an interface for describing a single OAuth2 provider +type GothProvider interface { + Provider + GothProviderCreator +} + +// ImagedProvider provide an overrided image setting for the provider +type ImagedProvider struct { + GothProvider + image string +} + +// Image returns the image path for this provider +func (i *ImagedProvider) Image() string { + return i.image +} + +// NewImagedProvider is a constructor function for the ImagedProvider +func NewImagedProvider(image string, provider GothProvider) *ImagedProvider { + return &ImagedProvider{ + GothProvider: provider, + image: image, + } } // Providers 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 Providers = map[string]Provider{ - "bitbucket": {Name: "bitbucket", DisplayName: "Bitbucket", Image: "/assets/img/auth/bitbucket.png"}, - "dropbox": {Name: "dropbox", DisplayName: "Dropbox", Image: "/assets/img/auth/dropbox.png"}, - "facebook": {Name: "facebook", DisplayName: "Facebook", Image: "/assets/img/auth/facebook.png"}, - "github": { - Name: "github", DisplayName: "GitHub", Image: "/assets/img/auth/github.png", - CustomURLMapping: &CustomURLMapping{ - TokenURL: github.TokenURL, - AuthURL: github.AuthURL, - ProfileURL: github.ProfileURL, - EmailURL: github.EmailURL, - }, - }, - "gitlab": { - Name: "gitlab", DisplayName: "GitLab", Image: "/assets/img/auth/gitlab.png", - CustomURLMapping: &CustomURLMapping{ - TokenURL: gitlab.TokenURL, - AuthURL: gitlab.AuthURL, - ProfileURL: gitlab.ProfileURL, - }, - }, - "gplus": {Name: "gplus", DisplayName: "Google", Image: "/assets/img/auth/google.png"}, - "openidConnect": {Name: "openidConnect", DisplayName: "OpenID Connect", Image: "/assets/img/auth/openid_connect.svg"}, - "twitter": {Name: "twitter", DisplayName: "Twitter", Image: "/assets/img/auth/twitter.png"}, - "discord": {Name: "discord", DisplayName: "Discord", Image: "/assets/img/auth/discord.png"}, - "gitea": { - Name: "gitea", DisplayName: "Gitea", Image: "/assets/img/auth/gitea.png", - CustomURLMapping: &CustomURLMapping{ - TokenURL: gitea.TokenURL, - AuthURL: gitea.AuthURL, - ProfileURL: gitea.ProfileURL, - }, - }, - "nextcloud": { - Name: "nextcloud", DisplayName: "Nextcloud", Image: "/assets/img/auth/nextcloud.png", - CustomURLMapping: &CustomURLMapping{ - TokenURL: nextcloud.TokenURL, - AuthURL: nextcloud.AuthURL, - ProfileURL: nextcloud.ProfileURL, - }, - }, - "yandex": {Name: "yandex", DisplayName: "Yandex", Image: "/assets/img/auth/yandex.png"}, - "mastodon": { - Name: "mastodon", DisplayName: "Mastodon", Image: "/assets/img/auth/mastodon.png", - CustomURLMapping: &CustomURLMapping{ - AuthURL: mastodon.InstanceURL, - }, - }, +var gothProviders = map[string]GothProvider{} + +// RegisterGothProvider registers a GothProvider +func RegisterGothProvider(provider GothProvider) { + if _, has := gothProviders[provider.Name()]; has { + log.Fatal("Duplicate oauth2provider type provided: %s", provider.Name()) + } + gothProviders[provider.Name()] = provider +} + +// GetOAuth2Providers returns the map of unconfigured OAuth2 providers +// key is used as technical name (like in the callbackURL) +// values to display +func GetOAuth2Providers() []Provider { + providers := make([]Provider, 0, len(gothProviders)) + + for _, provider := range gothProviders { + providers = append(providers, provider) + } + sort.Slice(providers, func(i, j int) bool { + return providers[i].Name() < providers[j].Name() + }) + return providers } // GetActiveOAuth2Providers returns the map of configured active OAuth2 providers @@ -103,9 +95,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]Provider, error) { var orderedKeys []string providers := make(map[string]Provider) for _, source := range loginSources { - prov := Providers[source.Cfg.(*Source).Provider] + prov := gothProviders[source.Cfg.(*Source).Provider] if source.Cfg.(*Source).IconURL != "" { - prov.Image = source.Cfg.(*Source).IconURL + prov = &ImagedProvider{prov, source.Cfg.(*Source).IconURL} } providers[source.Name] = prov orderedKeys = append(orderedKeys, source.Name) @@ -116,9 +108,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]Provider, error) { return orderedKeys, providers, nil } -// RegisterProvider register a OAuth2 provider in goth lib -func RegisterProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) error { - provider, err := createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL, customURLMapping) +// RegisterProviderWithGothic register a OAuth2 provider in goth lib +func RegisterProviderWithGothic(providerName string, source *Source) error { + provider, err := createProvider(providerName, source) if err == nil && provider != nil { gothRWMutex.Lock() @@ -130,8 +122,8 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID return err } -// RemoveProvider removes the given OAuth2 provider from the goth lib -func RemoveProvider(providerName string) { +// RemoveProviderFromGothic removes the given OAuth2 provider from the goth lib +func RemoveProviderFromGothic(providerName string) { gothRWMutex.Lock() defer gothRWMutex.Unlock() @@ -147,114 +139,20 @@ func ClearProviders() { } // used to create different types of goth providers -func createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) (goth.Provider, error) { +func createProvider(providerName string, source *Source) (goth.Provider, error) { callbackURL := setting.AppURL + "user/oauth2/" + url.PathEscape(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": - 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 - } - } - scopes := []string{} - if setting.OAuth2Client.EnableAutoRegistration { - scopes = append(scopes, "user:email") - } - provider = github.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, emailURL, scopes...) - 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, "read_user") - case "gplus": // named gplus due to legacy gplus -> google migration (Google killed Google+). This ensures old connections still work - scopes := []string{"email"} - if setting.OAuth2Client.UpdateAvatar || setting.OAuth2Client.EnableAutoRegistration { - scopes = append(scopes, "profile") - } - provider = google.New(clientID, clientSecret, callbackURL, scopes...) - case "openidConnect": - if provider, err = openidConnect.New(clientID, clientSecret, callbackURL, openIDConnectAutoDiscoveryURL, setting.OAuth2Client.OpenIDConnectScopes...); 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) - case "discord": - provider = discord.New(clientID, clientSecret, callbackURL, discord.ScopeIdentify, discord.ScopeEmail) - case "gitea": - authURL := gitea.AuthURL - tokenURL := gitea.TokenURL - profileURL := gitea.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 = gitea.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL) - case "nextcloud": - authURL := nextcloud.AuthURL - tokenURL := nextcloud.TokenURL - profileURL := nextcloud.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 = nextcloud.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL) - case "yandex": - // See https://tech.yandex.com/passport/doc/dg/reference/response-docpage/ - provider = yandex.New(clientID, clientSecret, callbackURL, "login:email", "login:info", "login:avatar") - case "mastodon": - instanceURL := mastodon.InstanceURL - if customURLMapping != nil && len(customURLMapping.AuthURL) > 0 { - instanceURL = customURLMapping.AuthURL - } - provider = mastodon.NewCustomisedURL(clientID, clientSecret, callbackURL, instanceURL) + p, ok := gothProviders[source.Provider] + if !ok { + return nil, models.ErrLoginSourceNotActived + } + + provider, err = p.CreateGothProvider(providerName, callbackURL, source) + if err != nil { + return provider, err } // always set the name if provider is created so we can support multiple setups of 1 provider |