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