Files
upstream-mirror 7e385fc126
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
2026-09-18 01:22:23 +05:00

162 lines
3.4 KiB
Go

package saml
import (
"crypto/rsa"
"crypto/x509"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp"
"go.uber.org/zap"
)
const defaultNameIdentifier = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
type (
SamlSPService struct {
log *zap.Logger
Enabled bool
IdpURL url.URL
Host url.URL
IDPUserMeta *IdpIdentityPayload
IDPMeta *saml.EntityDescriptor
sp saml.ServiceProvider
handler *samlsp.Middleware
}
SamlSPArgs struct {
Enabled bool
AcsURL string
MetaURL string
SloURL string
SignRequests bool
SignatureMethod string
Binding string
// user meta from idp
IdentityPayload IdpIdentityPayload
IdpURL url.URL
Host url.URL
Certificate *x509.Certificate
PrivateKey *rsa.PrivateKey
IdpMeta *saml.EntityDescriptor
}
)
// NewSamlSPService loads the certificates and registers the
// already fetched IDP metadata into the SAML middleware
func NewSamlSPService(log *zap.Logger, args SamlSPArgs) (s *SamlSPService, err error) {
metadataURL, _ := url.Parse(args.MetaURL)
acsURL, _ := url.Parse(args.AcsURL)
logoutURL, _ := url.Parse(args.SloURL)
sp := saml.ServiceProvider{
Key: args.PrivateKey,
Certificate: args.Certificate,
IDPMetadata: args.IdpMeta,
MetadataURL: *args.Host.ResolveReference(metadataURL),
AcsURL: *args.Host.ResolveReference(acsURL),
SloURL: *args.Host.ResolveReference(logoutURL),
}
if args.SignRequests {
sp.SignatureMethod = args.SignatureMethod
}
// default to GET
if args.Binding == "" {
args.Binding = saml.HTTPRedirectBinding
}
opts := samlsp.Options{
URL: args.Host,
Key: sp.Key,
Certificate: sp.Certificate,
IDPMetadata: args.IdpMeta,
}
// internal samlsp service
handler, err := samlsp.New(opts)
if err != nil {
err = fmt.Errorf("could not init SAML SP handler: %w", err)
return
}
if log != nil {
handler.OnError = func(w http.ResponseWriter, r *http.Request, err error) {
log.Error("error", zap.Error(err))
samlsp.DefaultOnError(w, r, err)
}
}
handler.RequestTracker = samlsp.DefaultRequestTracker(opts, &handler.ServiceProvider)
handler.ServiceProvider = sp
handler.Binding = args.Binding
s = &SamlSPService{
log: log,
Enabled: args.Enabled,
sp: sp,
handler: handler,
IdpURL: args.IdpURL,
Host: args.Host,
IDPUserMeta: &args.IdentityPayload,
}
return
}
func (ssp *SamlSPService) NameIdentifier() string {
return strings.TrimPrefix(defaultNameIdentifier, "urn:oasis:names:tc:SAML:1.1:nameid-format:")
}
// GuessIdentifier tries to guess the necessary (email) key
// for external authentication
func (ssp *SamlSPService) GuessIdentifier(payload map[string][]string) string {
tryValues := []string{
ssp.IDPUserMeta.Identifier,
ssp.NameIdentifier(),
defaultNameIdentifier,
"urn:oasis:names:tc:SAML:attribute:subject-id",
"email",
"mail",
}
for _, v := range tryValues {
if _, ok := payload[v]; ok {
return payload[v][0]
}
}
return ""
}
func (ssp *SamlSPService) Handler() *samlsp.Middleware {
return ssp.handler
}
// ServeHTTP enables us to use the service directly
// in the router
func (ssp *SamlSPService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if ssp.handler == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
ssp.handler.ServeHTTP(w, r)
}