Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

providers.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package oauth2
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "html"
  9. "html/template"
  10. "net/url"
  11. "sort"
  12. "code.gitea.io/gitea/models/auth"
  13. "code.gitea.io/gitea/models/db"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/optional"
  16. "code.gitea.io/gitea/modules/setting"
  17. "github.com/markbates/goth"
  18. )
  19. // Provider is an interface for describing a single OAuth2 provider
  20. type Provider interface {
  21. Name() string
  22. DisplayName() string
  23. IconHTML(size int) template.HTML
  24. CustomURLSettings() *CustomURLSettings
  25. }
  26. // GothProviderCreator provides a function to create a goth.Provider
  27. type GothProviderCreator interface {
  28. CreateGothProvider(providerName, callbackURL string, source *Source) (goth.Provider, error)
  29. }
  30. // GothProvider is an interface for describing a single OAuth2 provider
  31. type GothProvider interface {
  32. Provider
  33. GothProviderCreator
  34. }
  35. // AuthSourceProvider provides a provider for an AuthSource. Multiple auth sources could use the same registered GothProvider
  36. // So each auth source should have its own DisplayName and IconHTML for display.
  37. // The Name is the GothProvider's name, to help to find the GothProvider to sign in.
  38. // The DisplayName is the auth source config's name, site admin set it on the admin page, the IconURL can also be set there.
  39. type AuthSourceProvider struct {
  40. GothProvider
  41. sourceName, iconURL string
  42. }
  43. func (p *AuthSourceProvider) Name() string {
  44. return p.GothProvider.Name()
  45. }
  46. func (p *AuthSourceProvider) DisplayName() string {
  47. return p.sourceName
  48. }
  49. func (p *AuthSourceProvider) IconHTML(size int) template.HTML {
  50. if p.iconURL != "" {
  51. img := fmt.Sprintf(`<img class="tw-object-contain tw-mr-2" width="%d" height="%d" src="%s" alt="%s">`,
  52. size,
  53. size,
  54. html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),
  55. )
  56. return template.HTML(img)
  57. }
  58. return p.GothProvider.IconHTML(size)
  59. }
  60. // Providers contains the map of registered OAuth2 providers in Gitea (based on goth)
  61. // key is used to map the OAuth2Provider with the goth provider type (also in AuthSource.OAuth2Config.Provider)
  62. // value is used to store display data
  63. var gothProviders = map[string]GothProvider{}
  64. // RegisterGothProvider registers a GothProvider
  65. func RegisterGothProvider(provider GothProvider) {
  66. if _, has := gothProviders[provider.Name()]; has {
  67. log.Fatal("Duplicate oauth2provider type provided: %s", provider.Name())
  68. }
  69. gothProviders[provider.Name()] = provider
  70. }
  71. // GetSupportedOAuth2Providers returns the map of unconfigured OAuth2 providers
  72. // key is used as technical name (like in the callbackURL)
  73. // values to display
  74. func GetSupportedOAuth2Providers() []Provider {
  75. providers := make([]Provider, 0, len(gothProviders))
  76. for _, provider := range gothProviders {
  77. providers = append(providers, provider)
  78. }
  79. sort.Slice(providers, func(i, j int) bool {
  80. return providers[i].Name() < providers[j].Name()
  81. })
  82. return providers
  83. }
  84. func CreateProviderFromSource(source *auth.Source) (Provider, error) {
  85. oauth2Cfg, ok := source.Cfg.(*Source)
  86. if !ok {
  87. return nil, fmt.Errorf("invalid OAuth2 source config: %v", oauth2Cfg)
  88. }
  89. gothProv := gothProviders[oauth2Cfg.Provider]
  90. return &AuthSourceProvider{GothProvider: gothProv, sourceName: source.Name, iconURL: oauth2Cfg.IconURL}, nil
  91. }
  92. // GetOAuth2Providers returns the list of configured OAuth2 providers
  93. func GetOAuth2Providers(ctx context.Context, isActive optional.Option[bool]) ([]Provider, error) {
  94. authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
  95. IsActive: isActive,
  96. LoginType: auth.OAuth2,
  97. })
  98. if err != nil {
  99. return nil, err
  100. }
  101. providers := make([]Provider, 0, len(authSources))
  102. for _, source := range authSources {
  103. provider, err := CreateProviderFromSource(source)
  104. if err != nil {
  105. return nil, err
  106. }
  107. providers = append(providers, provider)
  108. }
  109. sort.Slice(providers, func(i, j int) bool {
  110. return providers[i].Name() < providers[j].Name()
  111. })
  112. return providers, nil
  113. }
  114. // RegisterProviderWithGothic register a OAuth2 provider in goth lib
  115. func RegisterProviderWithGothic(providerName string, source *Source) error {
  116. provider, err := createProvider(providerName, source)
  117. if err == nil && provider != nil {
  118. gothRWMutex.Lock()
  119. defer gothRWMutex.Unlock()
  120. goth.UseProviders(provider)
  121. }
  122. return err
  123. }
  124. // RemoveProviderFromGothic removes the given OAuth2 provider from the goth lib
  125. func RemoveProviderFromGothic(providerName string) {
  126. gothRWMutex.Lock()
  127. defer gothRWMutex.Unlock()
  128. delete(goth.GetProviders(), providerName)
  129. }
  130. // ClearProviders clears all OAuth2 providers from the goth lib
  131. func ClearProviders() {
  132. gothRWMutex.Lock()
  133. defer gothRWMutex.Unlock()
  134. goth.ClearProviders()
  135. }
  136. var ErrAuthSourceNotActivated = errors.New("auth source is not activated")
  137. // used to create different types of goth providers
  138. func createProvider(providerName string, source *Source) (goth.Provider, error) {
  139. callbackURL := setting.AppURL + "user/oauth2/" + url.PathEscape(providerName) + "/callback"
  140. var provider goth.Provider
  141. var err error
  142. p, ok := gothProviders[source.Provider]
  143. if !ok {
  144. return nil, ErrAuthSourceNotActivated
  145. }
  146. provider, err = p.CreateGothProvider(providerName, callbackURL, source)
  147. if err != nil {
  148. return provider, err
  149. }
  150. // always set the name if provider is created so we can support multiple setups of 1 provider
  151. if provider != nil {
  152. provider.SetName(providerName)
  153. }
  154. return provider, err
  155. }