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 }