Commit 8dcff17

mo <mo.khan@gmail.com>
2017-10-24 03:27:16
generate IDP metadata.
1 parent 30cde18
lib/saml/kit/identity_provider_metadata.rb
@@ -0,0 +1,62 @@
+module Saml
+  module Kit
+    class IdentityProviderMetadata
+      def initialize(xml)
+        @xml = xml
+      end
+
+      def to_xml
+        @xml
+      end
+
+      class Builder
+        attr_accessor :id, :organization_name, :organization_url, :contact_email, :entity_id, :single_sign_on_location, :single_logout_location, :attributes
+
+        def initialize
+          @id = SecureRandom.uuid
+          @attributes = []
+        end
+
+        def to_xml
+          xml = ::Builder::XmlMarkup.new
+          xml.instruct!
+          xml.EntityDescriptor entity_descriptor_options do
+            xml.IDPSSODescriptor protocolSupportEnumeration: Namespaces::PROTOCOL do
+              xml.NameIDFormat Namespaces::Formats::NameId::PERSISTENT
+              xml.SingleLogoutService Binding: Namespaces::Bindings::POST, Location: single_logout_location
+              xml.SingleSignOnService Binding: Namespaces::Bindings::HTTP_REDIRECT, Location: single_sign_on_location
+              attributes.each do |attribute|
+                xml.tag! 'saml:Attribute', NameFormat: Namespaces::Formats::Attr::URI, Name: attribute, FriendlyName: attribute
+              end
+            end
+            xml.Organization do
+              xml.OrganizationName organization_name, 'xml:lang': "en"
+              xml.OrganizationDisplayName organization_name, 'xml:lang': "en"
+              xml.OrganizationURL organization_url, 'xml:lang': "en"
+            end
+            xml.ContactPerson contactType: "technical" do
+              xml.Company "mailto:#{contact_email}"
+            end
+          end
+          xml.target!
+        end
+
+        def build
+          IdentityProviderMetadata.new(to_xml)
+        end
+
+        private
+
+        def entity_descriptor_options
+          {
+            'xmlns': Namespaces::METADATA,
+            'xmlns:ds': Namespaces::SIGNATURE,
+            'xmlns:saml': Namespaces::ASSERTION,
+            ID: "_#{id}",
+            entityID: entity_id,
+          }
+        end
+      end
+    end
+  end
+end
lib/saml/kit/identity_provider_metadata_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+RSpec.describe Saml::Kit::IdentityProviderMetadata do
+  describe described_class::Builder do
+    subject { described_class.new }
+    let(:email) { FFaker::Internet.email }
+    let(:org_name) { FFaker::Movie.title }
+    let(:url) { "https://#{FFaker::Internet.domain_name}" }
+    let(:entity_id) { FFaker::Movie.title }
+
+    it 'builds a proper metadata' do
+      subject.contact_email = email
+      subject.entity_id = entity_id
+      subject.organization_name = org_name
+      subject.organization_url = url
+      subject.single_sign_on_location = "https://www.example.com/login"
+      subject.single_logout_location = "https://www.example.com/logout"
+      subject.attributes << "id"
+
+      result = Hash.from_xml(subject.build.to_xml)
+
+      expect(result['EntityDescriptor']['ID']).to be_present
+      expect(result['EntityDescriptor']['entityID']).to eql(entity_id)
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['protocolSupportEnumeration']).to eql('urn:oasis:names:tc:SAML:2.0:protocol')
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['NameIDFormat']).to eql('urn:oasis:names:tc:SAML:2.0:nameid-format:persistent')
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['SingleSignOnService']['Binding']).to eql('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect')
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['SingleSignOnService']['Location']).to eql("https://www.example.com/login")
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['SingleLogoutService']['Binding']).to eql('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST')
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['SingleLogoutService']['Location']).to eql("https://www.example.com/logout")
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['Attribute']['Name']).to eql("id")
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['Attribute']['FriendlyName']).to eql("id")
+      expect(result['EntityDescriptor']['IDPSSODescriptor']['Attribute']['NameFormat']).to eql("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
+
+      expect(result['EntityDescriptor']['Organization']['OrganizationName']).to eql(org_name)
+      expect(result['EntityDescriptor']['Organization']['OrganizationDisplayName']).to eql(org_name)
+      expect(result['EntityDescriptor']['Organization']['OrganizationURL']).to eql(url)
+      expect(result['EntityDescriptor']['ContactPerson']['contactType']).to eql("technical")
+      expect(result['EntityDescriptor']['ContactPerson']['Company']).to eql("mailto:#{email}")
+    end
+  end
+end
lib/saml/kit/namespaces.rb
@@ -6,6 +6,11 @@ module Saml
       SIGNATURE = "http://www.w3.org/2000/09/xmldsig#"
       PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
 
+      module Bindings
+        POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+        HTTP_REDIRECT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+      end
+
       module Statuses
         SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"
       end
lib/saml/kit.rb
@@ -11,6 +11,7 @@ require "saml/kit/namespaces"
 require "saml/kit/request"
 require "saml/kit/response"
 require "saml/kit/service_provider_registry"
+require "saml/kit/identity_provider_metadata"
 
 module Saml
   module Kit