func (b *Backend) ConfigureLogger(logger *zap.Logger) error {
	if logger == nil {
		return fmt.Errorf("upstream logger is nil")
	}
	b.logger = logger
	return nil
}
ConfigureLogger configures backend with the same logger as its user.
package saml
import (
	"context"
	"encoding/xml"
	"fmt"
	samllib "github.com/crewjam/saml"
	"github.com/crewjam/saml/samlsp"
	"github.com/greenpau/caddy-auth-portal/pkg/errors"
	"github.com/greenpau/caddy-auth-portal/pkg/utils"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"go.uber.org/zap"
)
type Config struct {
	Name string `json:"name,omitempty"`
	Method string `json:"method,omitempty"`
	Realm string `json:"realm,omitempty"`
	Provider string `json:"provider,omitempty"`
	IdpMetadataLocation string `json:"idp_metadata_location,omitempty"`
	IdpSignCertLocation string `json:"idp_sign_cert_location,omitempty"`
	TenantID string `json:"tenant_id,omitempty"`
	ApplicationID string `json:"application_id,omitempty"`
	ApplicationName string `json:"application_name,omitempty"`
	EntityID string `json:"entity_id,omitempty"`
	AssertionConsumerServiceURLs []string `json:"acs_urls,omitempty"`
}
func (b *Backend) Configure() error {
	if b.Config.Name == "" {
		return errors.ErrBackendConfigureNameEmpty
	}
	if b.Config.Method == "" {
		return errors.ErrBackendConfigureMethodEmpty
	}
	if b.Config.Realm == "" {
		return errors.ErrBackendConfigureRealmEmpty
	}
	switch b.Config.Provider {
	case "azure":
		if b.Config.TenantID == "" {
			return fmt.Errorf("no tenant id found")
		}
		if b.Config.ApplicationID == "" {
			return fmt.Errorf("no application id found")
		}
		if b.Config.ApplicationName == "" {
			return fmt.Errorf("no application name found")
		}
		if b.Config.IdpMetadataLocation == "" {
			b.Config.IdpMetadataLocation = fmt.Sprintf(
				"https://login.microsoftonline.com/%s/federationmetadata/2007-06/federationmetadata.xml",
				b.Config.TenantID,
			)
		}
		b.loginURL = fmt.Sprintf(
			"https://account.activedirectory.windowsazure.com/applications/signin/%s/%s?tenantId=%s",
			b.Config.ApplicationName, b.Config.ApplicationID, b.Config.TenantID,
		)
	case "":
		return fmt.Errorf("no SAML provider found")
	default:
		return fmt.Errorf("unsupported SAML provider %s", b.Config.Provider)
	}
	if len(b.Config.AssertionConsumerServiceURLs) < 1 {
		return fmt.Errorf("ACS URLs are missing")
	}
	if b.Config.IdpSignCertLocation == "" {
		return fmt.Errorf("IdP Signing Certificate not found")
	}
	idpSignCert, err := utils.ReadCertFile(b.Config.IdpSignCertLocation)
	if err != nil {
		return err
	}
	azureOptions := samlsp.Options{}
	if strings.HasPrefix(b.Config.IdpMetadataLocation, "http") {
		idpMetadataURL, err := url.Parse(b.Config.IdpMetadataLocation)
		if err != nil {
			return err
		}
		b.idpMetadataURL = idpMetadataURL
		azureOptions.URL = *idpMetadataURL
		idpMetadata, err := samlsp.FetchMetadata(
			context.Background(),
			http.DefaultClient,
			*idpMetadataURL,
		)
		if err != nil {
			return err
		}
		azureOptions.IDPMetadata = idpMetadata
	} else {
		metadataFileContent, err := ioutil.ReadFile(b.Config.IdpMetadataLocation)
		if err != nil {
			return err
		}
		idpMetadata, err := samlsp.ParseMetadata(metadataFileContent)
		if err != nil {
			return err
		}
		azureOptions.IDPMetadata = idpMetadata
	}
	b.serviceProviders = make(map[string]*samllib.ServiceProvider)
	for _, acsURL := range b.Config.AssertionConsumerServiceURLs {
		sp := samlsp.DefaultServiceProvider(azureOptions)
		sp.AllowIDPInitiated = true
		cfgAcsURL, _ := url.Parse(acsURL)
		sp.AcsURL = *cfgAcsURL
		entityID, _ := url.Parse(b.Config.EntityID)
		sp.MetadataURL = *entityID
		if b.idpMetadataURL != nil {
			sp.MetadataURL = *b.idpMetadataURL
		}
		for i := range sp.IDPMetadata.IDPSSODescriptors {
			idpSSODescriptor := &sp.IDPMetadata.IDPSSODescriptors[i]
			keyDescriptor := &samllib.KeyDescriptor{
				Use: "signing",
				KeyInfo: samllib.KeyInfo{
					XMLName: xml.Name{
						Space: "http://www.w3.org/2000/09/xmldsig#",
						Local: "KeyInfo",
					},
					Certificate: idpSignCert,
				},
			}
			idpSSODescriptor.KeyDescriptors = append(idpSSODescriptor.KeyDescriptors, *keyDescriptor)
			break
		}
		b.serviceProviders[acsURL] = &sp
	}
	b.logger.Info(
		"successfully configured SAML backend",
		zap.String("tenant_id", b.Config.TenantID),
		zap.String("application_id", b.Config.ApplicationID),
		zap.String("application_name", b.Config.ApplicationName),
		zap.Any("acs_urls", b.Config.AssertionConsumerServiceURLs),
		zap.String("login_url", b.loginURL),
		zap.String("idp_sign_cert_location", b.Config.IdpSignCertLocation),
		zap.String("idp_metadata_location", b.Config.IdpMetadataLocation),
	)
	return nil
}
func (b *Backend) ValidateConfig() error {
	return nil
}
func (b *Backend) Validate() error {
	if err := b.ValidateConfig(); err != nil {
		return err
	}
	if b.logger == nil {
		return fmt.Errorf("SAML backend logger is nil")
	}
	b.logger.Info("successfully validated SAML backend")
	return nil
}