main
1# frozen_string_literal: true
2
3module Saml
4 module Kit
5 # This class parses the IDPSSODescriptor from a SAML metadata document.
6 #
7 # raw_xml = <<-XML
8 # <?xml version="1.0" encoding="UTF-8"?>
9 # <EntityDescriptor
10 # xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
11 # xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
12 # xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
13 # ID="_cfa24e2f-0ec0-4ee3-abb8-b2fcfe394c1c"
14 # entityID="my-entity-id">
15 # <IDPSSODescriptor
16 # WantAuthnRequestsSigned="true"
17 # protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
18 # <SingleLogoutService
19 # Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
20 # Location="https://www.example.com/logout" />
21 # <NameIDFormat>
22 # urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
23 # </NameIDFormat>
24 # <SingleSignOnService
25 # Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
26 # Location="https://www.example.com/login" />
27 # <SingleSignOnService
28 # Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
29 # Location="https://www.example.com/login" />
30 # <saml:Attribute Name="id"/>
31 # </IDPSSODescriptor>
32 # </EntityDescriptor>
33 # XML
34 #
35 # metadata = Saml::Kit::IdentityProviderMetadata.new(raw_xml)
36 # puts metadata.entity_id
37 #
38 # It can also be used to generate IDP metadata.
39 #
40 # metadata = Saml::Kit::IdentityProviderMetadata.build do |builder|
41 # builder.entity_id = "my-entity-id"
42 # end
43 # puts metadata.to_xml
44 #
45 # For more details on generating metadata see {Saml::Kit::Metadata}.
46 #
47 # Example:
48 #
49 # {include:file:spec/examples/identity_provider_metadata_spec.rb}
50 class IdentityProviderMetadata < Metadata
51 def initialize(xml)
52 super('IDPSSODescriptor', xml)
53 end
54
55 # Returns the IDPSSODescriptor/@WantAuthnRequestsSigned attribute.
56 def want_authn_requests_signed
57 xpath = "/md:EntityDescriptor/md:#{name}"
58 attribute = at_xpath(xpath).attribute('WantAuthnRequestsSigned')
59 return true if attribute.nil?
60
61 attribute.text.casecmp('true').zero?
62 end
63
64 # Returns each of the SingleSignOnService elements.
65 def single_sign_on_services
66 services('SingleSignOnService')
67 end
68
69 # Returns a SingleSignOnService elements with the specified binding.
70 #
71 # @param binding [Symbol] `:http_post` or `:http_redirect`.
72 def single_sign_on_service_for(binding:)
73 service_for(binding: binding, type: 'SingleSignOnService')
74 end
75
76 # Returns each of the Attributes in the metadata.
77 def attributes
78 search("/md:EntityDescriptor/md:#{name}/saml:Attribute").map do |item|
79 {
80 format: item.attribute('NameFormat').try(:value),
81 name: item.attribute('Name').value,
82 }
83 end
84 end
85
86 # Creates a AuthnRequest document for the specified binding.
87 #
88 # @param binding [Symbol] `:http_post` or `:http_redirect`.
89 # @param relay_state [Object] RelayState to include the returned params.
90 # @param configuration [Saml::Kit::Configuration] the configuration to
91 # use for generating the request.
92 # @return [Array] Url and params encoded using rules for binding.
93 def login_request_for(
94 binding:, relay_state: nil, configuration: Saml::Kit.configuration
95 )
96 builder =
97 AuthenticationRequest.builder(configuration: configuration) do |x|
98 x.embed_signature = want_authn_requests_signed
99 yield x if block_given?
100 end
101 request_binding = single_sign_on_service_for(binding: binding)
102 request_binding.serialize(builder, relay_state: relay_state)
103 end
104
105 # @!visibility private
106 def self.builder_class
107 Saml::Kit::Builders::IdentityProviderMetadata
108 end
109 end
110 end
111end