saml-kit 1.0.15 → 1.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -5
- data/exe/saml-kit-create-self-signed-certificate +6 -2
- data/exe/saml-kit-decode-http-post +2 -1
- data/exe/saml-kit-decode-http-redirect +2 -1
- data/lib/saml/kit/assertion.rb +22 -48
- data/lib/saml/kit/attribute_statement.rb +25 -0
- data/lib/saml/kit/authentication_request.rb +34 -15
- data/lib/saml/kit/bindings/binding.rb +4 -7
- data/lib/saml/kit/bindings/http_post.rb +6 -2
- data/lib/saml/kit/bindings/http_redirect.rb +8 -5
- data/lib/saml/kit/bindings/url_builder.rb +7 -7
- data/lib/saml/kit/bindings.rb +4 -3
- data/lib/saml/kit/builders/assertion.rb +6 -3
- data/lib/saml/kit/builders/authentication_request.rb +4 -2
- data/lib/saml/kit/builders/encrypted_assertion.rb +3 -1
- data/lib/saml/kit/builders/identity_provider_metadata.rb +14 -4
- data/lib/saml/kit/builders/metadata.rb +8 -4
- data/lib/saml/kit/builders/null.rb +0 -1
- data/lib/saml/kit/builders/response.rb +14 -5
- data/lib/saml/kit/builders/service_provider_metadata.rb +10 -3
- data/lib/saml/kit/builders.rb +0 -1
- data/lib/saml/kit/composite_metadata.rb +18 -3
- data/lib/saml/kit/{buildable.rb → concerns/buildable.rb} +0 -0
- data/lib/saml/kit/{requestable.rb → concerns/requestable.rb} +0 -0
- data/lib/saml/kit/{respondable.rb → concerns/respondable.rb} +0 -0
- data/lib/saml/kit/{serializable.rb → concerns/serializable.rb} +0 -0
- data/lib/saml/kit/{translatable.rb → concerns/translatable.rb} +0 -0
- data/lib/saml/kit/{trustable.rb → concerns/trustable.rb} +9 -7
- data/lib/saml/kit/concerns/xml_parseable.rb +62 -0
- data/lib/saml/kit/{xml_templatable.rb → concerns/xml_templatable.rb} +3 -2
- data/lib/saml/kit/{xsd_validatable.rb → concerns/xsd_validatable.rb} +10 -0
- data/lib/saml/kit/conditions.rb +37 -0
- data/lib/saml/kit/configuration.rb +28 -10
- data/lib/saml/kit/default_registry.rb +19 -4
- data/lib/saml/kit/document.rb +21 -67
- data/lib/saml/kit/identity_provider_metadata.rb +34 -15
- data/lib/saml/kit/invalid_document.rb +1 -1
- data/lib/saml/kit/logout_request.rb +11 -6
- data/lib/saml/kit/logout_response.rb +3 -1
- data/lib/saml/kit/metadata.rb +63 -109
- data/lib/saml/kit/namespaces.rb +2 -1
- data/lib/saml/kit/organization.rb +36 -0
- data/lib/saml/kit/parser.rb +28 -0
- data/lib/saml/kit/response.rb +10 -2
- data/lib/saml/kit/rspec/have_xpath.rb +4 -2
- data/lib/saml/kit/service_provider_metadata.rb +2 -1
- data/lib/saml/kit/signature.rb +21 -5
- data/lib/saml/kit/version.rb +1 -1
- data/lib/saml/kit.rb +14 -7
- data/saml-kit.gemspec +0 -1
- metadata +16 -25
data/lib/saml/kit/builders.rb
CHANGED
@@ -9,6 +9,7 @@ module Saml
|
|
9
9
|
# and SPSSODescriptor element.
|
10
10
|
class CompositeMetadata < Metadata # :nodoc:
|
11
11
|
include Enumerable
|
12
|
+
|
12
13
|
attr_reader :service_provider, :identity_provider
|
13
14
|
|
14
15
|
def initialize(xml)
|
@@ -19,8 +20,22 @@ module Saml
|
|
19
20
|
]
|
20
21
|
end
|
21
22
|
|
23
|
+
def organization
|
24
|
+
find { |x| x.organization.present? }.try(:organization)
|
25
|
+
end
|
26
|
+
|
27
|
+
def organization_name
|
28
|
+
organization.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def organization_url
|
32
|
+
organization.url
|
33
|
+
end
|
34
|
+
|
22
35
|
def services(type)
|
23
|
-
xpath = map
|
36
|
+
xpath = map do |x|
|
37
|
+
"//md:EntityDescriptor/md:#{x.name}/md:#{type}"
|
38
|
+
end.join('|')
|
24
39
|
search(xpath).map do |item|
|
25
40
|
binding = item.attribute('Binding').value
|
26
41
|
location = item.attribute('Location').value
|
@@ -37,7 +52,7 @@ module Saml
|
|
37
52
|
end
|
38
53
|
|
39
54
|
def method_missing(name, *args)
|
40
|
-
if (target = find { |
|
55
|
+
if (target = find { |x| x.respond_to?(name) })
|
41
56
|
target.public_send(name, *args)
|
42
57
|
else
|
43
58
|
super
|
@@ -45,7 +60,7 @@ module Saml
|
|
45
60
|
end
|
46
61
|
|
47
62
|
def respond_to_missing?(method, *)
|
48
|
-
find { |
|
63
|
+
find { |x| x.respond_to?(method) }
|
49
64
|
end
|
50
65
|
end
|
51
66
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -9,14 +9,15 @@ module Saml
|
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
11
|
included do
|
12
|
-
validate :must_have_valid_signature, unless: :
|
12
|
+
validate :must_have_valid_signature, unless: :signature_verified
|
13
13
|
validate :must_be_registered
|
14
14
|
validate :must_be_trusted
|
15
15
|
end
|
16
16
|
|
17
|
-
# Returns true when the document has an embedded XML Signature or has
|
17
|
+
# Returns true when the document has an embedded XML Signature or has
|
18
|
+
# been verified externally.
|
18
19
|
def signed?
|
19
|
-
|
20
|
+
signature_verified || signature.present?
|
20
21
|
end
|
21
22
|
|
22
23
|
# @!visibility private
|
@@ -24,9 +25,10 @@ module Saml
|
|
24
25
|
@signature ||= Signature.new(at_xpath("/samlp:#{name}/ds:Signature"))
|
25
26
|
end
|
26
27
|
|
27
|
-
# Returns true when documents is signed and the signing certificate
|
28
|
+
# Returns true when documents is signed and the signing certificate
|
29
|
+
# belongs to a known service entity.
|
28
30
|
def trusted?
|
29
|
-
return true if
|
31
|
+
return true if signature_verified
|
30
32
|
return false unless signed?
|
31
33
|
signature.trusted?(provider)
|
32
34
|
end
|
@@ -38,12 +40,12 @@ module Saml
|
|
38
40
|
|
39
41
|
# @!visibility private
|
40
42
|
def signature_verified!
|
41
|
-
@
|
43
|
+
@signature_verified = true
|
42
44
|
end
|
43
45
|
|
44
46
|
private
|
45
47
|
|
46
|
-
attr_reader :
|
48
|
+
attr_reader :signature_verified
|
47
49
|
|
48
50
|
def must_have_valid_signature
|
49
51
|
return if to_xml.blank?
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'saml/kit/namespaces'
|
4
|
+
|
5
|
+
module Saml
|
6
|
+
module Kit
|
7
|
+
module XmlParseable
|
8
|
+
NAMESPACES = {
|
9
|
+
NameFormat: ::Saml::Kit::Namespaces::ATTR_SPLAT,
|
10
|
+
ds: ::Xml::Kit::Namespaces::XMLDSIG,
|
11
|
+
md: ::Saml::Kit::Namespaces::METADATA,
|
12
|
+
saml: ::Saml::Kit::Namespaces::ASSERTION,
|
13
|
+
samlp: ::Saml::Kit::Namespaces::PROTOCOL,
|
14
|
+
xmlenc: ::Xml::Kit::Namespaces::XMLENC,
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
# Returns the SAML document returned as a Hash.
|
18
|
+
def to_h
|
19
|
+
@to_h ||= Hash.from_xml(to_s) || {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the XML document as a String.
|
23
|
+
#
|
24
|
+
# @param pretty [Boolean] true to return a human friendly version
|
25
|
+
# of the XML.
|
26
|
+
def to_xml(pretty: nil)
|
27
|
+
pretty ? to_nokogiri.to_xml(indent: 2) : to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the SAML document as an XHTML string.
|
31
|
+
# This is useful for rendering in a web page.
|
32
|
+
def to_xhtml
|
33
|
+
Nokogiri::XML(to_xml, &:noblanks).to_xhtml
|
34
|
+
end
|
35
|
+
|
36
|
+
def present?
|
37
|
+
to_s.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
# @!visibility private
|
41
|
+
def to_nokogiri
|
42
|
+
@to_nokogiri ||= Nokogiri::XML(to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!visibility private
|
46
|
+
def at_xpath(xpath)
|
47
|
+
return unless present?
|
48
|
+
to_nokogiri.at_xpath(xpath, NAMESPACES)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!visibility private
|
52
|
+
def search(xpath)
|
53
|
+
to_nokogiri.search(xpath, NAMESPACES)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the XML document as a [String].
|
57
|
+
def to_s
|
58
|
+
content
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -5,7 +5,7 @@ module Saml
|
|
5
5
|
# This module is responsible for
|
6
6
|
# generating converting templates to xml.
|
7
7
|
module XmlTemplatable
|
8
|
-
TEMPLATES_DIR = Pathname.new(File.join(__dir__, 'builders/templates/'))
|
8
|
+
TEMPLATES_DIR = Pathname.new(File.join(__dir__, '../builders/templates/'))
|
9
9
|
include ::Xml::Kit::Templatable
|
10
10
|
|
11
11
|
def template_path
|
@@ -16,7 +16,8 @@ module Saml
|
|
16
16
|
"#{self.class.name.split('::').last.underscore}.builder"
|
17
17
|
end
|
18
18
|
|
19
|
-
# Returns true if an embedded signature is requested and at least one
|
19
|
+
# Returns true if an embedded signature is requested and at least one
|
20
|
+
# signing certificate is available via the configuration.
|
20
21
|
def sign?
|
21
22
|
return configuration.sign? if embed_signature.nil?
|
22
23
|
(embed_signature && configuration.sign?) ||
|
@@ -5,8 +5,18 @@ module Saml
|
|
5
5
|
# This module is responsible for validating
|
6
6
|
# xml documents against the SAML XSD's
|
7
7
|
module XsdValidatable
|
8
|
+
PROTOCOL_XSD = File.expand_path(
|
9
|
+
'../xsd/saml-schema-protocol-2.0.xsd', File.dirname(__FILE__)
|
10
|
+
).freeze
|
11
|
+
|
12
|
+
METADATA_XSD = File.expand_path(
|
13
|
+
'../xsd/saml-schema-metadata-2.0.xsd', File.dirname(__FILE__)
|
14
|
+
).freeze
|
15
|
+
|
8
16
|
# @!visibility private
|
9
17
|
def matches_xsd?(xsd)
|
18
|
+
return unless to_nokogiri.present?
|
19
|
+
|
10
20
|
Dir.chdir(File.dirname(xsd)) do
|
11
21
|
xsd = Nokogiri::XML::Schema(IO.read(xsd))
|
12
22
|
xsd.validate(to_nokogiri).each do |error|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Saml
|
4
|
+
module Kit
|
5
|
+
class Conditions
|
6
|
+
include XmlParseable
|
7
|
+
|
8
|
+
attr_reader :content
|
9
|
+
|
10
|
+
def initialize(node)
|
11
|
+
@to_nokogiri = node
|
12
|
+
@content = node.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def started_at
|
16
|
+
parse_iso8601(at_xpath('./@NotBefore').try(:value))
|
17
|
+
end
|
18
|
+
|
19
|
+
def expired_at
|
20
|
+
parse_iso8601(at_xpath('./@NotOnOrAfter').try(:value))
|
21
|
+
end
|
22
|
+
|
23
|
+
def audiences
|
24
|
+
search('./saml:AudienceRestriction/saml:Audience').map(&:text)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def parse_iso8601(value)
|
30
|
+
DateTime.parse(value)
|
31
|
+
rescue StandardError => error
|
32
|
+
Saml::Kit.logger.error(error)
|
33
|
+
Time.at(0).to_datetime
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module Saml
|
4
4
|
module Kit
|
5
|
-
# This class represents the main configuration that is use for generating
|
5
|
+
# This class represents the main configuration that is use for generating
|
6
|
+
# SAML documents.
|
6
7
|
#
|
7
8
|
# Saml::Kit::Configuration.new do |config|
|
8
9
|
# config.entity_id = "com:saml:kit"
|
@@ -19,17 +20,25 @@ module Saml
|
|
19
20
|
# Saml::Kit.configure do |configuration|
|
20
21
|
# configuration.entity_id = "https://www.example.com/saml/metadata"
|
21
22
|
# configuration.generate_key_pair_for(use: :signing)
|
22
|
-
# configuration.add_key_pair(
|
23
|
+
# configuration.add_key_pair(
|
24
|
+
# ENV["X509_CERTIFICATE"],
|
25
|
+
# ENV["PRIVATE_KEY"],
|
26
|
+
# passphrase: ENV['PRIVATE_KEY_PASSPHRASE'],
|
27
|
+
# use: :encryption
|
28
|
+
# )
|
23
29
|
# end
|
24
30
|
class Configuration
|
25
31
|
USES = %i[signing encryption].freeze
|
26
32
|
# The issuer to use in requests or responses from this entity to use.
|
27
33
|
attr_accessor :entity_id
|
28
|
-
# The signature method to use when generating signatures
|
34
|
+
# The signature method to use when generating signatures
|
35
|
+
# (See {Saml::Kit::Builders::XmlSignature::SIGNATURE_METHODS})
|
29
36
|
attr_accessor :signature_method
|
30
|
-
# The digest method to use when generating signatures
|
37
|
+
# The digest method to use when generating signatures
|
38
|
+
# (See {Saml::Kit::Builders::XmlSignature::DIGEST_METHODS})
|
31
39
|
attr_accessor :digest_method
|
32
|
-
# The metadata registry to use for searching for metadata associated
|
40
|
+
# The metadata registry to use for searching for metadata associated
|
41
|
+
# with an issuer.
|
33
42
|
attr_accessor :registry
|
34
43
|
# The session timeout to use when generating an Assertion.
|
35
44
|
attr_accessor :session_timeout
|
@@ -57,7 +66,11 @@ module Saml
|
|
57
66
|
# @param use [Symbol] the type of key pair, `:signing` or `:encryption`
|
58
67
|
def add_key_pair(certificate, private_key, passphrase: nil, use: :signing)
|
59
68
|
ensure_proper_use(use)
|
60
|
-
@key_pairs.push(
|
69
|
+
@key_pairs.push(
|
70
|
+
::Xml::Kit::KeyPair.new(
|
71
|
+
certificate, private_key, passphrase, use.to_sym
|
72
|
+
)
|
73
|
+
)
|
61
74
|
end
|
62
75
|
|
63
76
|
# Generates a unique key pair that can be used for signing or encryption.
|
@@ -66,27 +79,32 @@ module Saml
|
|
66
79
|
# @param passphrase [String] the private key passphrase to use.
|
67
80
|
def generate_key_pair_for(use:, passphrase: SecureRandom.uuid)
|
68
81
|
ensure_proper_use(use)
|
69
|
-
certificate, private_key = ::Xml::Kit::SelfSignedCertificate.new.create(
|
82
|
+
certificate, private_key = ::Xml::Kit::SelfSignedCertificate.new.create(
|
83
|
+
passphrase: passphrase
|
84
|
+
)
|
70
85
|
add_key_pair(certificate, private_key, passphrase: passphrase, use: use)
|
71
86
|
end
|
72
87
|
|
73
88
|
# Return each key pair for a specific use.
|
74
89
|
#
|
75
|
-
# @param use [Symbol] the type of key pair to return
|
90
|
+
# @param use [Symbol] the type of key pair to return
|
91
|
+
# `nil`, `:signing` or `:encryption`
|
76
92
|
def key_pairs(use: nil)
|
77
93
|
use.present? ? @key_pairs.find_all { |xxx| xxx.for?(use) } : @key_pairs
|
78
94
|
end
|
79
95
|
|
80
96
|
# Return each certificate for a specific use.
|
81
97
|
#
|
82
|
-
# @param use [Symbol] the type of key pair to return
|
98
|
+
# @param use [Symbol] the type of key pair to return
|
99
|
+
# `nil`, `:signing` or `:encryption`
|
83
100
|
def certificates(use: nil)
|
84
101
|
key_pairs(use: use).flat_map(&:certificate)
|
85
102
|
end
|
86
103
|
|
87
104
|
# Return each private for a specific use.
|
88
105
|
#
|
89
|
-
# @param use [Symbol] the type of key pair to return
|
106
|
+
# @param use [Symbol] the type of key pair to return
|
107
|
+
# `nil`, `:signing` or `:encryption`
|
90
108
|
def private_keys(use: nil)
|
91
109
|
key_pairs(use: use).flat_map(&:private_key)
|
92
110
|
end
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
module Saml
|
4
4
|
module Kit
|
5
|
-
# The default metadata registry is used to fetch the metadata associated
|
6
|
-
#
|
5
|
+
# The default metadata registry is used to fetch the metadata associated
|
6
|
+
# with an issuer or entity id.
|
7
|
+
# The metadata associated with an issuer is used to verify trust for any
|
8
|
+
# SAML documents that are received.
|
7
9
|
#
|
8
10
|
# You can replace the default registry with your own at startup.
|
9
11
|
#
|
@@ -41,12 +43,14 @@ module Saml
|
|
41
43
|
#
|
42
44
|
# @param metadata [Saml::Kit::Metadata] the metadata to register.
|
43
45
|
def register(metadata)
|
46
|
+
ensure_valid_metadata(metadata)
|
44
47
|
Saml::Kit.logger.debug(metadata.to_xml(pretty: true))
|
45
48
|
@items[metadata.entity_id] = metadata
|
46
49
|
end
|
47
50
|
|
48
51
|
# Register metadata via a remote URL.
|
49
|
-
# This will attempt to connect to the remove URL to download the
|
52
|
+
# This will attempt to connect to the remove URL to download the
|
53
|
+
# metadata and register it in the registry.
|
50
54
|
#
|
51
55
|
# @param url [String] the url to download the metadata from.
|
52
56
|
# @param verify_ssl [Boolean] enable/disable SSL peer verification.
|
@@ -57,7 +61,8 @@ module Saml
|
|
57
61
|
|
58
62
|
# Returns the metadata document associated with an issuer or entityID.
|
59
63
|
#
|
60
|
-
# @param entity_id [String]
|
64
|
+
# @param entity_id [String] unique entityID/Issuer associated with
|
65
|
+
# metadata.
|
61
66
|
def metadata_for(entity_id)
|
62
67
|
@items[entity_id]
|
63
68
|
end
|
@@ -69,6 +74,16 @@ module Saml
|
|
69
74
|
end
|
70
75
|
end
|
71
76
|
|
77
|
+
private
|
78
|
+
|
79
|
+
def ensure_valid_metadata(metadata)
|
80
|
+
error = ArgumentError.new('Cannot register invalid metadata')
|
81
|
+
raise error if
|
82
|
+
metadata.nil? ||
|
83
|
+
!metadata.respond_to?(:entity_id) ||
|
84
|
+
metadata.invalid?
|
85
|
+
end
|
86
|
+
|
72
87
|
# This class is responsible for
|
73
88
|
# making HTTP requests to fetch metadata
|
74
89
|
# from remote locations.
|
data/lib/saml/kit/document.rb
CHANGED
@@ -5,19 +5,12 @@ module Saml
|
|
5
5
|
# This class is a base class for SAML documents.
|
6
6
|
class Document
|
7
7
|
include ActiveModel::Validations
|
8
|
-
include
|
8
|
+
include Buildable
|
9
9
|
include Translatable
|
10
10
|
include Trustable
|
11
|
-
include
|
12
|
-
|
13
|
-
|
14
|
-
"NameFormat": ::Saml::Kit::Namespaces::ATTR_SPLAT,
|
15
|
-
"ds": ::Xml::Kit::Namespaces::XMLDSIG,
|
16
|
-
"md": ::Saml::Kit::Namespaces::METADATA,
|
17
|
-
"saml": ::Saml::Kit::Namespaces::ASSERTION,
|
18
|
-
"samlp": ::Saml::Kit::Namespaces::PROTOCOL,
|
19
|
-
'xmlenc' => ::Xml::Kit::Namespaces::XMLENC,
|
20
|
-
}.freeze
|
11
|
+
include XmlParseable
|
12
|
+
include XsdValidatable
|
13
|
+
|
21
14
|
attr_accessor :registry
|
22
15
|
attr_reader :name
|
23
16
|
validates_presence_of :content
|
@@ -58,44 +51,13 @@ module Saml
|
|
58
51
|
Time.parse(at_xpath('./*/@IssueInstant').try(:value))
|
59
52
|
end
|
60
53
|
|
61
|
-
# Returns the SAML document returned as a Hash.
|
62
|
-
def to_h
|
63
|
-
@to_h ||= Hash.from_xml(content) || {}
|
64
|
-
end
|
65
|
-
|
66
|
-
# Returns the SAML document as an XML string.
|
67
|
-
#
|
68
|
-
# @param pretty [Boolean] formats the xml or returns the raw xml.
|
69
|
-
def to_xml(pretty: nil)
|
70
|
-
pretty ? to_nokogiri.to_xml(indent: 2) : to_s
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the SAML document as an XHTML string.
|
74
|
-
# This is useful for rendering in a web page.
|
75
|
-
def to_xhtml
|
76
|
-
Nokogiri::XML(to_xml, &:noblanks).to_xhtml
|
77
|
-
end
|
78
|
-
|
79
|
-
# @!visibility private
|
80
|
-
def to_nokogiri
|
81
|
-
@to_nokogiri ||= Nokogiri::XML(to_s)
|
82
|
-
end
|
83
|
-
|
84
|
-
# @!visibility private
|
85
|
-
def at_xpath(xpath)
|
86
|
-
to_nokogiri.at_xpath(xpath, NAMESPACES)
|
87
|
-
end
|
88
|
-
|
89
|
-
# @!visibility private
|
90
|
-
def search(xpath)
|
91
|
-
to_nokogiri.search(xpath, NAMESPACES)
|
92
|
-
end
|
93
|
-
|
94
|
-
def to_s
|
95
|
-
content
|
96
|
-
end
|
97
|
-
|
98
54
|
class << self
|
55
|
+
CONSTRUCTORS = {
|
56
|
+
'AuthnRequest' => -> { Saml::Kit::AuthenticationRequest },
|
57
|
+
'LogoutRequest' => -> { Saml::Kit::LogoutRequest },
|
58
|
+
'LogoutResponse' => -> { Saml::Kit::LogoutResponse },
|
59
|
+
'Response' => -> { Saml::Kit::Response },
|
60
|
+
}.freeze
|
99
61
|
XPATH = [
|
100
62
|
'/samlp:AuthnRequest',
|
101
63
|
'/samlp:LogoutRequest',
|
@@ -106,14 +68,12 @@ module Saml
|
|
106
68
|
# Returns the raw xml as a Saml::Kit SAML document.
|
107
69
|
#
|
108
70
|
# @param xml [String] the raw xml string.
|
109
|
-
# @param configuration [Saml::Kit::Configuration]
|
71
|
+
# @param configuration [Saml::Kit::Configuration] configuration to use
|
72
|
+
# for unpacking the document.
|
110
73
|
def to_saml_document(xml, configuration: Saml::Kit.configuration)
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
'LogoutResponse' => Saml::Kit::LogoutResponse,
|
115
|
-
'Response' => Saml::Kit::Response,
|
116
|
-
}[Nokogiri::XML(xml).at_xpath(XPATH, "samlp": ::Saml::Kit::Namespaces::PROTOCOL).name] || InvalidDocument
|
74
|
+
namespaces = { samlp: Namespaces::PROTOCOL }
|
75
|
+
element = Nokogiri::XML(xml).at_xpath(XPATH, namespaces)
|
76
|
+
constructor = CONSTRUCTORS[element.name].try(:call) || InvalidDocument
|
117
77
|
constructor.new(xml, configuration: configuration)
|
118
78
|
rescue StandardError => error
|
119
79
|
Saml::Kit.logger.error(error)
|
@@ -122,18 +82,12 @@ module Saml
|
|
122
82
|
|
123
83
|
# @!visibility private
|
124
84
|
def builder_class # :nodoc:
|
125
|
-
|
126
|
-
|
127
|
-
Saml::Kit::Builders::
|
128
|
-
|
129
|
-
Saml::Kit::Builders::
|
130
|
-
|
131
|
-
Saml::Kit::Builders::AuthenticationRequest
|
132
|
-
when Saml::Kit::LogoutRequest.to_s
|
133
|
-
Saml::Kit::Builders::LogoutRequest
|
134
|
-
else
|
135
|
-
raise ArgumentError, "Unknown SAML Document #{name}"
|
136
|
-
end
|
85
|
+
{
|
86
|
+
Response.to_s => Saml::Kit::Builders::Response,
|
87
|
+
LogoutResponse.to_s => Saml::Kit::Builders::LogoutResponse,
|
88
|
+
AuthenticationRequest.to_s => Saml::Kit::Builders::AuthenticationRequest,
|
89
|
+
LogoutRequest.to_s => Saml::Kit::Builders::LogoutRequest,
|
90
|
+
}[name] || (raise ArgumentError, "Unknown SAML Document #{name}")
|
137
91
|
end
|
138
92
|
end
|
139
93
|
|
@@ -2,16 +2,31 @@
|
|
2
2
|
|
3
3
|
module Saml
|
4
4
|
module Kit
|
5
|
-
# This class
|
5
|
+
# This class parses the IDPSSODescriptor from a SAML metadata document.
|
6
6
|
#
|
7
7
|
# raw_xml = <<-XML
|
8
8
|
# <?xml version="1.0" encoding="UTF-8"?>
|
9
|
-
# <EntityDescriptor
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
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" />
|
15
30
|
# <saml:Attribute Name="id"/>
|
16
31
|
# </IDPSSODescriptor>
|
17
32
|
# </EntityDescriptor>
|
@@ -70,14 +85,18 @@ module Saml
|
|
70
85
|
# Creates a AuthnRequest document for the specified binding.
|
71
86
|
#
|
72
87
|
# @param binding [Symbol] `:http_post` or `:http_redirect`.
|
73
|
-
# @param relay_state [Object]
|
74
|
-
# @param configuration [Saml::Kit::Configuration] the configuration to
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
88
|
+
# @param relay_state [Object] RelayState to include the returned params.
|
89
|
+
# @param configuration [Saml::Kit::Configuration] the configuration to
|
90
|
+
# use for generating the request.
|
91
|
+
# @return [Array] Url and params encoded using rules for binding.
|
92
|
+
def login_request_for(
|
93
|
+
binding:, relay_state: nil, configuration: Saml::Kit.configuration
|
94
|
+
)
|
95
|
+
builder =
|
96
|
+
AuthenticationRequest.builder(configuration: configuration) do |x|
|
97
|
+
x.embed_signature = want_authn_requests_signed
|
98
|
+
yield x if block_given?
|
99
|
+
end
|
81
100
|
request_binding = single_sign_on_service_for(binding: binding)
|
82
101
|
request_binding.serialize(builder, relay_state: relay_state)
|
83
102
|
end
|
@@ -16,7 +16,8 @@ module Saml
|
|
16
16
|
#
|
17
17
|
# See {Saml::Kit::Builders::LogoutRequest} for a list of available settings.
|
18
18
|
#
|
19
|
-
# This class can also be used to generate the correspondong LogoutResponse
|
19
|
+
# This class can also be used to generate the correspondong LogoutResponse
|
20
|
+
# for a LogoutRequest.
|
20
21
|
#
|
21
22
|
# document = Saml::Kit::LogoutRequest.new(raw_xml)
|
22
23
|
# url, saml_params = document.response_for(binding: :http_post)
|
@@ -31,7 +32,7 @@ module Saml
|
|
31
32
|
# A new instance of LogoutRequest
|
32
33
|
#
|
33
34
|
# @param xml [String] The raw xml string.
|
34
|
-
# @param configuration [Saml::Kit::Configuration]
|
35
|
+
# @param configuration [Saml::Kit::Configuration] configuration to use.
|
35
36
|
def initialize(xml, configuration: Saml::Kit.configuration)
|
36
37
|
super(xml, name: 'LogoutRequest', configuration: configuration)
|
37
38
|
end
|
@@ -45,11 +46,15 @@ module Saml
|
|
45
46
|
at_xpath('./*/saml:NameID/@Format').try(:value)
|
46
47
|
end
|
47
48
|
|
48
|
-
# Generates a Serialized LogoutResponse using the encoding rules for
|
49
|
+
# Generates a Serialized LogoutResponse using the encoding rules for
|
50
|
+
# the specified binding.
|
49
51
|
#
|
50
|
-
# @param binding [Symbol] The binding to use `:http_redirect` or
|
51
|
-
#
|
52
|
-
# @
|
52
|
+
# @param binding [Symbol] The binding to use `:http_redirect` or
|
53
|
+
# `:http_post`.
|
54
|
+
# @param relay_state [Object] The RelayState to include in the
|
55
|
+
# RelayState param.
|
56
|
+
# @return [Array] Returns an array with a url and Hash of parameters to
|
57
|
+
# return to the requestor.
|
53
58
|
def response_for(binding:, relay_state: nil)
|
54
59
|
builder = Saml::Kit::LogoutResponse.builder(self) do |xxx|
|
55
60
|
yield xxx if block_given?
|
@@ -10,7 +10,9 @@ module Saml
|
|
10
10
|
class LogoutResponse < Document
|
11
11
|
include Respondable
|
12
12
|
|
13
|
-
def initialize(
|
13
|
+
def initialize(
|
14
|
+
xml, request_id: nil, configuration: Saml::Kit.configuration
|
15
|
+
)
|
14
16
|
@request_id = request_id
|
15
17
|
super(xml, name: 'LogoutResponse', configuration: configuration)
|
16
18
|
end
|