mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-18 19:39:14 +08:00
5bb8d1924d
Closes https://github.com/go-gitea/gitea/issues/5512 This PR adds basic SAML support - Adds SAML 2.0 as an auth source - Adds SAML configuration documentation - Adds integration test: - Use bare-bones SAML IdP to test protocol flow and test account is linked successfully (only runs on Postgres by default) - Adds documentation for configuring and running SAML integration test locally Future PRs: - Support group mapping - Support auto-registration (account linking) Co-Authored-By: @jackHay22 --------- Co-authored-by: jackHay22 <jack@allspice.io> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: KN4CK3R <admin@oldschoolhack.me> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: morphelinho <morphelinho@users.noreply.github.com> Co-authored-by: Zettat123 <zettat123@gmail.com> Co-authored-by: Yarden Shoham <git@yardenshoham.com> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: silverwind <me@silverwind.io>
110 lines
2.7 KiB
Go
110 lines
2.7 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package saml
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"html"
|
|
"html/template"
|
|
"io"
|
|
"net/http"
|
|
"sort"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/auth"
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/modules/httplib"
|
|
"code.gitea.io/gitea/modules/svg"
|
|
"code.gitea.io/gitea/modules/util"
|
|
)
|
|
|
|
// Providers is list of known/available providers.
|
|
type Providers map[string]Source
|
|
|
|
var providers = Providers{}
|
|
|
|
// Provider is an interface for describing a single SAML provider
|
|
type Provider interface {
|
|
Name() string
|
|
IconHTML(size int) template.HTML
|
|
}
|
|
|
|
// AuthSourceProvider is a SAML provider
|
|
type AuthSourceProvider struct {
|
|
sourceName, iconURL string
|
|
}
|
|
|
|
func (p *AuthSourceProvider) Name() string {
|
|
return p.sourceName
|
|
}
|
|
|
|
func (p *AuthSourceProvider) IconHTML(size int) template.HTML {
|
|
if p.iconURL != "" {
|
|
return template.HTML(fmt.Sprintf(`<img class="gt-object-contain gt-mr-3" width="%d" height="%d" src="%s" alt="%s">`,
|
|
size,
|
|
size,
|
|
html.EscapeString(p.iconURL), html.EscapeString(p.Name()),
|
|
))
|
|
}
|
|
return svg.RenderHTML("gitea-lock-cog", size, "gt-mr-3")
|
|
}
|
|
|
|
func readIdentityProviderMetadata(ctx context.Context, source *Source) ([]byte, error) {
|
|
if source.IdentityProviderMetadata != "" {
|
|
return []byte(source.IdentityProviderMetadata), nil
|
|
}
|
|
|
|
req := httplib.NewRequest(source.IdentityProviderMetadataURL, "GET")
|
|
req.SetTimeout(20*time.Second, time.Minute)
|
|
resp, err := req.Response()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to contact gitea: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, err
|
|
}
|
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func createProviderFromSource(source *auth.Source) (Provider, error) {
|
|
samlCfg, ok := source.Cfg.(*Source)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid SAML source config: %v", samlCfg)
|
|
}
|
|
return &AuthSourceProvider{sourceName: source.Name, iconURL: samlCfg.IconURL}, nil
|
|
}
|
|
|
|
// GetSAMLProviders returns the list of configured SAML providers
|
|
func GetSAMLProviders(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) {
|
|
authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
|
|
IsActive: isActive,
|
|
LoginType: auth.SAML,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
samlProviders := make([]Provider, 0, len(authSources))
|
|
for _, source := range authSources {
|
|
p, err := createProviderFromSource(source)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
samlProviders = append(samlProviders, p)
|
|
}
|
|
|
|
sort.Slice(samlProviders, func(i, j int) bool {
|
|
return samlProviders[i].Name() < samlProviders[j].Name()
|
|
})
|
|
|
|
return samlProviders, nil
|
|
}
|