github.com/greenpau/go-authcrunch@v1.1.4/pkg/sso/metadata.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package sso
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/base64"
    20  	"encoding/xml"
    21  )
    22  
    23  // EntityDescriptor TODO.
    24  type EntityDescriptor struct {
    25  	XMLName  xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
    26  	ID       string   `xml:",attr,omitempty"`
    27  	EntityID string   `xml:"entityID,attr"`
    28  }
    29  
    30  // X509Data TODO.
    31  type X509Data struct {
    32  	XMLName         xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# X509Data"`
    33  	X509Certificate string   `xml:"http://www.w3.org/2000/09/xmldsig# X509Certificate"`
    34  }
    35  
    36  // KeyInfo TODO.
    37  type KeyInfo struct {
    38  	XMLName  xml.Name `xml:"http://www.w3.org/2000/09/xmldsig# KeyInfo"`
    39  	X509Data *X509Data
    40  }
    41  
    42  // KeyDescriptor TODO.
    43  type KeyDescriptor struct {
    44  	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata KeyDescriptor"`
    45  	Use     string   `xml:"use,attr,omitempty"`
    46  	KeyInfo KeyInfo
    47  }
    48  
    49  // Service TODO.
    50  type Service struct {
    51  	Binding  string `xml:",attr"`
    52  	Location string `xml:",attr"`
    53  }
    54  
    55  // SingleSignOnService TODO.
    56  type SingleSignOnService struct {
    57  	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SingleSignOnService"`
    58  	Service
    59  }
    60  
    61  // IDPSSODescriptor TODO.
    62  type IDPSSODescriptor struct {
    63  	XMLName                    xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
    64  	WantAuthnRequestsSigned    bool     `xml:",attr"`
    65  	ProtocolSupportEnumeration string   `xml:"protocolSupportEnumeration,attr"`
    66  	KeyDescriptor              KeyDescriptor
    67  	NameIDFormat               string `xml:"NameIDFormat"`
    68  	SingleSignOnService        []SingleSignOnService
    69  }
    70  
    71  // IDPEntityDescriptor TODO.
    72  type IDPEntityDescriptor struct {
    73  	*EntityDescriptor
    74  	IDPSSODescriptor *IDPSSODescriptor
    75  }
    76  
    77  // GetMetadata returns the contents of metadata.xml.
    78  func (p *Provider) GetMetadata() ([]byte, error) {
    79  	if len(p.metadata) > 0 {
    80  		return p.metadata, nil
    81  	}
    82  	entity := &IDPEntityDescriptor{
    83  		EntityDescriptor: &EntityDescriptor{
    84  			EntityID: p.config.EntityID,
    85  		},
    86  		IDPSSODescriptor: &IDPSSODescriptor{
    87  			ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
    88  			KeyDescriptor: KeyDescriptor{
    89  				Use: "signing",
    90  				KeyInfo: KeyInfo{
    91  					X509Data: &X509Data{
    92  						X509Certificate: base64.StdEncoding.EncodeToString(p.cert.Raw),
    93  					},
    94  				},
    95  			},
    96  			WantAuthnRequestsSigned: false,
    97  			NameIDFormat:            "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
    98  		},
    99  	}
   100  
   101  	for _, location := range p.config.Locations {
   102  		svc := SingleSignOnService{
   103  			Service: Service{
   104  				Binding:  "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
   105  				Location: location,
   106  			},
   107  		}
   108  		entity.IDPSSODescriptor.SingleSignOnService = append(entity.IDPSSODescriptor.SingleSignOnService, svc)
   109  	}
   110  
   111  	var b bytes.Buffer
   112  	b.Write([]byte(xml.Header))
   113  	encoder := xml.NewEncoder(&b)
   114  	encoder.Indent("", "  ")
   115  	if err := encoder.Encode(entity); err != nil {
   116  		return nil, err
   117  	}
   118  	output := bytes.ReplaceAll(b.Bytes(), []byte("></SingleSignOnService>"), []byte("/>"))
   119  	output = bytes.ReplaceAll(output,
   120  		[]byte(`<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"`),
   121  		[]byte(`<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"`))
   122  	output = bytes.ReplaceAll(output,
   123  		[]byte(`<IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"`),
   124  		[]byte(`<md:IDPSSODescriptor`))
   125  	output = bytes.ReplaceAll(output,
   126  		[]byte(`<KeyDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"`),
   127  		[]byte(`<md:KeyDescriptor`))
   128  	output = bytes.ReplaceAll(output,
   129  		[]byte(`<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">`),
   130  		[]byte(`<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">`))
   131  	output = bytes.ReplaceAll(output,
   132  		[]byte(`<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">`),
   133  		[]byte(`<ds:X509Data>`))
   134  	output = bytes.ReplaceAll(output,
   135  		[]byte(`<X509Certificate xmlns="http://www.w3.org/2000/09/xmldsig#">`),
   136  		[]byte(`<ds:X509Certificate>`))
   137  	output = bytes.ReplaceAll(output,
   138  		[]byte(`SingleSignOnService xmlns="urn:oasis:names:tc:SAML:2.0:metadata"`),
   139  		[]byte(`md:SingleSignOnService`))
   140  	output = bytes.ReplaceAll(output, []byte(`</IDPSSODescriptor>`), []byte(`</md:IDPSSODescriptor>`))
   141  	output = bytes.ReplaceAll(output, []byte(`</EntityDescriptor>`), []byte(`</md:EntityDescriptor>`))
   142  	output = bytes.ReplaceAll(output, []byte(`</X509Data>`), []byte(`</ds:X509Data>`))
   143  	output = bytes.ReplaceAll(output, []byte(`</KeyInfo>`), []byte(`</ds:KeyInfo>`))
   144  	output = bytes.ReplaceAll(output, []byte(`</KeyDescriptor>`), []byte(`</md:KeyDescriptor>`))
   145  	output = bytes.ReplaceAll(output, []byte(`</X509Certificate>`), []byte(`</ds:X509Certificate>`))
   146  	output = bytes.ReplaceAll(output, []byte(`NameIDFormat>`), []byte(`md:NameIDFormat>`))
   147  	p.metadata = output
   148  	return p.metadata, nil
   149  }