Commit ef22f59
Changed files (16)
lib
saml
kit
builders
spec
saml
builders
lib/saml/kit/builders/templates/authn_request.builder
@@ -0,0 +1,5 @@
+xml.tag!('samlp:AuthnRequest', request_options) do
+ xml.tag!('saml:Issuer', issuer)
+ signature.template(id)
+ xml.tag!('samlp:NameIDPolicy', Format: name_id_format)
+end
lib/saml/kit/builders/templates/identity_provider_metadata.builder
@@ -0,0 +1,44 @@
+xml.instruct!
+xml.EntityDescriptor entity_descriptor_options do
+ signature.template(id)
+ xml.IDPSSODescriptor idp_sso_descriptor_options do
+ if configuration.signing_certificate_pem.present?
+ xml.KeyDescriptor use: "signing" do
+ xml.KeyInfo "xmlns": Saml::Kit::Namespaces::XMLDSIG do
+ xml.X509Data do
+ xml.X509Certificate configuration.stripped_signing_certificate
+ end
+ end
+ end
+ end
+ if configuration.encryption_certificate_pem.present?
+ xml.KeyDescriptor use: "encryption" do
+ xml.KeyInfo "xmlns": Saml::Kit::Namespaces::XMLDSIG do
+ xml.X509Data do
+ xml.X509Certificate configuration.stripped_encryption_certificate
+ end
+ end
+ end
+ end
+ logout_urls.each do |item|
+ xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
+ end
+ name_id_formats.each do |format|
+ xml.NameIDFormat format
+ end
+ single_sign_on_urls.each do |item|
+ xml.SingleSignOnService Binding: item[:binding], Location: item[:location]
+ end
+ attributes.each do |attribute|
+ xml.tag! 'saml:Attribute', Name: 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
lib/saml/kit/builders/templates/logout_request.builder
@@ -0,0 +1,6 @@
+xml.instruct!
+xml.LogoutRequest logout_request_options do
+ xml.Issuer({ xmlns: Saml::Kit::Namespaces::ASSERTION }, issuer)
+ signature.template(id)
+ xml.NameID name_id_options, user.name_id_for(name_id_format)
+end
lib/saml/kit/builders/templates/logout_response.builder
@@ -0,0 +1,7 @@
+xml.LogoutResponse logout_response_options do
+ xml.Issuer(issuer, xmlns: Saml::Kit::Namespaces::ASSERTION)
+ signature.template(id)
+ xml.Status do
+ xml.StatusCode Value: status_code
+ end
+end
lib/saml/kit/builders/templates/service_provider_metadata.builder
@@ -0,0 +1,41 @@
+xml.instruct!
+xml.EntityDescriptor entity_descriptor_options do
+ signature.template(id)
+ xml.SPSSODescriptor descriptor_options do
+ if configuration.signing_certificate_pem.present?
+ xml.KeyDescriptor use: "signing" do
+ xml.KeyInfo "xmlns": Saml::Kit::Namespaces::XMLDSIG do
+ xml.X509Data do
+ xml.X509Certificate configuration.stripped_signing_certificate
+ end
+ end
+ end
+ end
+ if configuration.encryption_certificate_pem.present?
+ xml.KeyDescriptor use: "encryption" do
+ xml.KeyInfo "xmlns": Saml::Kit::Namespaces::XMLDSIG do
+ xml.X509Data do
+ xml.X509Certificate configuration.stripped_encryption_certificate
+ end
+ end
+ end
+ end
+ logout_urls.each do |item|
+ xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
+ end
+ name_id_formats.each do |format|
+ xml.NameIDFormat format
+ end
+ acs_urls.each_with_index do |item, index|
+ xml.AssertionConsumerService Binding: item[:binding], Location: item[:location], index: index, isDefault: index == 0 ? true : false
+ 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
lib/saml/kit/builders/authentication_request.rb
@@ -2,16 +2,20 @@ module Saml
module Kit
module Builders
class AuthenticationRequest
+ include Saml::Kit::Templatable
attr_accessor :id, :now, :issuer, :assertion_consumer_service_url, :name_id_format, :sign, :destination
attr_accessor :version
+ attr_reader :template_name, :configuration
def initialize(configuration: Saml::Kit.configuration, sign: true)
+ @configuration = configuration
@id = Id.generate
@issuer = configuration.issuer
@name_id_format = Namespaces::PERSISTENT
@now = Time.now.utc
- @version = "2.0"
@sign = sign
+ @template_name = 'authn_request'
+ @version = "2.0"
end
def acs_url
@@ -24,16 +28,6 @@ module Saml
self.assertion_consumer_service_url = value
end
- def to_xml
- Signature.sign(sign: sign) do |xml, signature|
- xml.tag!('samlp:AuthnRequest', request_options) do
- xml.tag!('saml:Issuer', issuer)
- signature.template(id)
- xml.tag!('samlp:NameIDPolicy', Format: name_id_format)
- end
- end
- end
-
def build
Saml::Kit::AuthenticationRequest.new(to_xml)
end
lib/saml/kit/builders/identity_provider_metadata.rb
@@ -2,19 +2,22 @@ module Saml
module Kit
module Builders
class IdentityProviderMetadata
+ include Saml::Kit::Templatable
attr_accessor :id, :organization_name, :organization_url, :contact_email, :entity_id, :attributes, :name_id_formats
attr_accessor :want_authn_requests_signed, :sign
attr_reader :logout_urls, :single_sign_on_urls
+ attr_reader :template_name, :configuration
def initialize(configuration = Saml::Kit.configuration)
- @id = Id.generate
- @entity_id = configuration.issuer
@attributes = []
- @name_id_formats = [Namespaces::PERSISTENT]
- @single_sign_on_urls = []
- @logout_urls = []
@configuration = configuration
+ @entity_id = configuration.issuer
+ @id = Id.generate
+ @logout_urls = []
+ @name_id_formats = [Namespaces::PERSISTENT]
@sign = true
+ @single_sign_on_urls = []
+ @template_name = 'identity_provider_metadata'
@want_authn_requests_signed = true
end
@@ -26,55 +29,6 @@ module Saml
@logout_urls.push(location: url, binding: Bindings.binding_for(binding))
end
- def to_xml
- Signature.sign(sign: sign) do |xml, signature|
- xml.instruct!
- xml.EntityDescriptor entity_descriptor_options do
- signature.template(id)
- xml.IDPSSODescriptor idp_sso_descriptor_options do
- if @configuration.signing_certificate_pem.present?
- xml.KeyDescriptor use: "signing" do
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
- xml.X509Data do
- xml.X509Certificate @configuration.stripped_signing_certificate
- end
- end
- end
- end
- if @configuration.encryption_certificate_pem.present?
- xml.KeyDescriptor use: "encryption" do
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
- xml.X509Data do
- xml.X509Certificate @configuration.stripped_encryption_certificate
- end
- end
- end
- end
- logout_urls.each do |item|
- xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
- end
- name_id_formats.each do |format|
- xml.NameIDFormat format
- end
- single_sign_on_urls.each do |item|
- xml.SingleSignOnService Binding: item[:binding], Location: item[:location]
- end
- attributes.each do |attribute|
- xml.tag! 'saml:Attribute', Name: 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
- end
- end
-
def build
Saml::Kit::IdentityProviderMetadata.new(to_xml)
end
lib/saml/kit/builders/logout_request.rb
@@ -2,11 +2,13 @@ module Saml
module Kit
module Builders
class LogoutRequest
+ include Saml::Kit::Templatable
attr_accessor :id, :destination, :issuer, :name_id_format, :now
attr_accessor :sign, :version
- attr_reader :user
+ attr_reader :user, :configuration, :template_name
def initialize(user, configuration: Saml::Kit.configuration, sign: true)
+ @configuration = configuration
@user = user
@id = "_#{SecureRandom.uuid}"
@issuer = configuration.issuer
@@ -14,17 +16,7 @@ module Saml
@now = Time.now.utc
@version = "2.0"
@sign = sign
- end
-
- def to_xml
- Signature.sign(sign: sign) do |xml, signature|
- xml.instruct!
- xml.LogoutRequest logout_request_options do
- xml.Issuer({ xmlns: Namespaces::ASSERTION }, issuer)
- signature.template(id)
- xml.NameID name_id_options, user.name_id_for(name_id_format)
- end
- end
+ @template_name = 'logout_request'
end
def build
lib/saml/kit/builders/logout_response.rb
@@ -2,30 +2,22 @@ module Saml
module Kit
module Builders
class LogoutResponse
+ include Saml::Kit::Templatable
attr_accessor :id, :issuer, :version, :status_code, :sign, :now, :destination
attr_reader :request
+ attr_reader :configuration, :template_name
def initialize(user, request, configuration: Saml::Kit.configuration, sign: true)
- @user = user
+ @configuration = configuration
+ @id = Id.generate
+ @issuer = configuration.issuer
@now = Time.now.utc
@request = request
- @id = Id.generate
- @version = "2.0"
- @status_code = Namespaces::SUCCESS
@sign = sign
- @issuer = configuration.issuer
- end
-
- def to_xml
- Signature.sign(sign: sign) do |xml, signature|
- xml.LogoutResponse logout_response_options do
- xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
- signature.template(id)
- xml.Status do
- xml.StatusCode Value: status_code
- end
- end
- end
+ @status_code = Namespaces::SUCCESS
+ @template_name = 'logout_response'
+ @user = user
+ @version = "2.0"
end
def build
lib/saml/kit/builders/response.rb
@@ -6,8 +6,9 @@ module Saml
attr_accessor :id, :reference_id, :now
attr_accessor :version, :status_code
attr_accessor :issuer, :sign, :destination, :encrypt
+ attr_reader :configuration
- def initialize(user, request)
+ def initialize(user, request, configuration: Saml::Kit.configuration)
@user = user
@request = request
@id = Id.generate
@@ -19,6 +20,7 @@ module Saml
@destination = destination_for(request)
@sign = want_assertions_signed
@encrypt = false
+ @configuration = configuration
end
def want_assertions_signed
@@ -129,10 +131,6 @@ module Saml
end
end
- def configuration
- Saml::Kit.configuration
- end
-
def response_options
{
ID: id,
@@ -165,7 +163,7 @@ module Saml
def conditions_options
{
NotBefore: now.utc.iso8601,
- NotOnOrAfter: Saml::Kit.configuration.session_timeout.from_now.utc.iso8601,
+ NotOnOrAfter: configuration.session_timeout.from_now.utc.iso8601,
}
end
lib/saml/kit/builders/service_provider_metadata.rb
@@ -2,18 +2,21 @@ module Saml
module Kit
module Builders
class ServiceProviderMetadata
+ include Saml::Kit::Templatable
attr_accessor :id, :entity_id, :acs_urls, :logout_urls, :name_id_formats, :sign
attr_accessor :organization_name, :organization_url, :contact_email
attr_accessor :want_assertions_signed
+ attr_reader :configuration, :template_name
def initialize(configuration = Saml::Kit.configuration)
- @id = Id.generate
+ @acs_urls = []
@configuration = configuration
@entity_id = configuration.issuer
- @acs_urls = []
+ @id = Id.generate
@logout_urls = []
@name_id_formats = [Namespaces::PERSISTENT]
@sign = true
+ @template_name = 'service_provider_metadata'
@want_assertions_signed = true
end
@@ -25,52 +28,6 @@ module Saml
@logout_urls.push(location: url, binding: Bindings.binding_for(binding))
end
- def to_xml
- Signature.sign(sign: sign) do |xml, signature|
- xml.instruct!
- xml.EntityDescriptor entity_descriptor_options do
- signature.template(id)
- xml.SPSSODescriptor descriptor_options do
- if @configuration.signing_certificate_pem.present?
- xml.KeyDescriptor use: "signing" do
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
- xml.X509Data do
- xml.X509Certificate @configuration.stripped_signing_certificate
- end
- end
- end
- end
- if @configuration.encryption_certificate_pem.present?
- xml.KeyDescriptor use: "encryption" do
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
- xml.X509Data do
- xml.X509Certificate @configuration.stripped_encryption_certificate
- end
- end
- end
- end
- logout_urls.each do |item|
- xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
- end
- name_id_formats.each do |format|
- xml.NameIDFormat format
- end
- acs_urls.each_with_index do |item, index|
- xml.AssertionConsumerService Binding: item[:binding], Location: item[:location], index: index, isDefault: index == 0 ? true : false
- 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
- end
- end
-
def build
Saml::Kit::ServiceProviderMetadata.new(to_xml)
end
lib/saml/kit/signature.rb
@@ -53,9 +53,12 @@ module Saml
end
def finalize
- return xml.target! unless sign
+ sign ? apply_to(xml.target!) : xml.target!
+ end
+
+ def apply_to(raw_xml)
+ return raw_xml unless sign
- raw_xml = xml.target!
@reference_ids.each do |reference_id|
raw_xml = Xmldsig::SignedDocument.new(raw_xml).sign(private_key)
end
lib/saml/kit/templatable.rb
@@ -0,0 +1,24 @@
+module Saml
+ module Kit
+ module Templatable
+ def template_path
+ File.join(File.expand_path(File.dirname(__FILE__)), "builders/templates/#{template_name}.builder")
+ end
+
+ def template
+ Tilt.new(template_path)
+ end
+
+ def to_xml(xml: ::Builder::XmlMarkup.new)
+ signature = Saml::Kit::Signature.new(
+ xml,
+ configuration: configuration,
+ sign: sign
+ )
+ signature.apply_to(
+ template.render(self, xml: xml, signature: signature)
+ )
+ end
+ end
+ end
+end
lib/saml/kit.rb
@@ -12,9 +12,11 @@ require "logger"
require "net/http"
require "nokogiri"
require "securerandom"
+require "tilt"
require "xmldsig"
require "saml/kit/buildable"
+require "saml/kit/templatable"
require "saml/kit/builders"
require "saml/kit/namespaces"
require "saml/kit/serializable"
spec/saml/builders/logout_response_spec.rb
@@ -1,8 +1,7 @@
require 'spec_helper'
RSpec.describe Saml::Kit::Builders::LogoutResponse do
- subject { described_class.new(user, request, configuration: configuration) }
- let(:configuration) { double(issuer: issuer) }
+ subject { described_class.new(user, request) }
let(:user) { double(:user, name_id_for: SecureRandom.uuid) }
let(:request) { Saml::Kit::Builders::LogoutRequest.new(user).build }
let(:issuer) { FFaker::Internet.http_url }
@@ -12,6 +11,7 @@ RSpec.describe Saml::Kit::Builders::LogoutResponse do
it 'builds a logout response' do
travel_to 1.second.from_now
+ subject.issuer = issuer
subject.destination = destination
result = subject.build
expect(result.id).to be_present
saml-kit.gemspec
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "activemodel", ">= 4.2.0"
spec.add_dependency "builder", "~> 3.2"
spec.add_dependency "nokogiri", "~> 1.8"
+ spec.add_dependency "tilt", "~> 2.0"
spec.add_dependency "xmldsig", "~> 0.6"
spec.add_development_dependency "bundler", "~> 1.15"
spec.add_development_dependency "ffaker", "~> 2.7"