Commit 8dcff17
Changed files (4)
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