saml-kit 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/Gemfile +2 -0
  4. data/Rakefile +2 -0
  5. data/bin/cibuild +2 -3
  6. data/bin/console +1 -0
  7. data/bin/lint +2 -4
  8. data/bin/setup +0 -2
  9. data/bin/test +2 -4
  10. data/exe/saml-kit-create-self-signed-certificate +2 -0
  11. data/exe/saml-kit-decode-http-post +2 -0
  12. data/exe/saml-kit-decode-http-redirect +2 -0
  13. data/lib/saml-kit.rb +2 -0
  14. data/lib/saml/kit.rb +2 -0
  15. data/lib/saml/kit/assertion.rb +21 -41
  16. data/lib/saml/kit/authentication_request.rb +9 -3
  17. data/lib/saml/kit/bindings.rb +2 -0
  18. data/lib/saml/kit/bindings/binding.rb +2 -0
  19. data/lib/saml/kit/bindings/http_post.rb +2 -0
  20. data/lib/saml/kit/bindings/http_redirect.rb +2 -0
  21. data/lib/saml/kit/bindings/url_builder.rb +2 -0
  22. data/lib/saml/kit/buildable.rb +2 -0
  23. data/lib/saml/kit/builders.rb +2 -0
  24. data/lib/saml/kit/builders/assertion.rb +2 -0
  25. data/lib/saml/kit/builders/authentication_request.rb +2 -0
  26. data/lib/saml/kit/builders/encrypted_assertion.rb +2 -0
  27. data/lib/saml/kit/builders/identity_provider_metadata.rb +2 -0
  28. data/lib/saml/kit/builders/logout_request.rb +2 -0
  29. data/lib/saml/kit/builders/logout_response.rb +2 -0
  30. data/lib/saml/kit/builders/metadata.rb +2 -0
  31. data/lib/saml/kit/builders/response.rb +2 -0
  32. data/lib/saml/kit/builders/service_provider_metadata.rb +2 -0
  33. data/lib/saml/kit/builders/templates/assertion.builder +2 -0
  34. data/lib/saml/kit/builders/templates/authentication_request.builder +2 -0
  35. data/lib/saml/kit/builders/templates/encrypted_assertion.builder +2 -0
  36. data/lib/saml/kit/builders/templates/identity_provider_metadata.builder +2 -0
  37. data/lib/saml/kit/builders/templates/logout_request.builder +2 -0
  38. data/lib/saml/kit/builders/templates/logout_response.builder +2 -0
  39. data/lib/saml/kit/builders/templates/metadata.builder +2 -0
  40. data/lib/saml/kit/builders/templates/response.builder +2 -0
  41. data/lib/saml/kit/builders/templates/service_provider_metadata.builder +2 -0
  42. data/lib/saml/kit/composite_metadata.rb +3 -1
  43. data/lib/saml/kit/configuration.rb +2 -0
  44. data/lib/saml/kit/default_registry.rb +2 -0
  45. data/lib/saml/kit/document.rb +9 -14
  46. data/lib/saml/kit/identity_provider_metadata.rb +4 -2
  47. data/lib/saml/kit/invalid_document.rb +2 -0
  48. data/lib/saml/kit/logout_request.rb +7 -1
  49. data/lib/saml/kit/logout_response.rb +2 -0
  50. data/lib/saml/kit/metadata.rb +35 -32
  51. data/lib/saml/kit/namespaces.rb +2 -0
  52. data/lib/saml/kit/null_assertion.rb +2 -0
  53. data/lib/saml/kit/requestable.rb +2 -0
  54. data/lib/saml/kit/respondable.rb +4 -2
  55. data/lib/saml/kit/response.rb +2 -0
  56. data/lib/saml/kit/rspec.rb +2 -0
  57. data/lib/saml/kit/rspec/have_query_param.rb +2 -0
  58. data/lib/saml/kit/rspec/have_xpath.rb +3 -8
  59. data/lib/saml/kit/serializable.rb +2 -0
  60. data/lib/saml/kit/service_provider_metadata.rb +3 -1
  61. data/lib/saml/kit/signature.rb +2 -0
  62. data/lib/saml/kit/translatable.rb +2 -0
  63. data/lib/saml/kit/trustable.rb +2 -0
  64. data/lib/saml/kit/version.rb +3 -1
  65. data/lib/saml/kit/xml_templatable.rb +2 -0
  66. data/lib/saml/kit/xsd_validatable.rb +3 -2
  67. data/saml-kit.gemspec +2 -0
  68. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0d58bbb3c3a82d505f9898c871e9038d22cbf0fe6b50c59615e3a528e3770a0
4
- data.tar.gz: 527f1c7a86aafa31d7bb5629325c177dfaeb3517c4c2d570832298c33622c647
3
+ metadata.gz: 48040329dac3aa9d3171bf1098ec49937b7667067dbd7c8f8a0fa7adc0837edb
4
+ data.tar.gz: fc968fad4d56cb939ce34cd7da486e19844f55e11f976cd8454228571cfbc2a7
5
5
  SHA512:
6
- metadata.gz: 315cb5a0c5f725577fdae560a924d27cf421299af013bdc10a69a8842add199723088165b266f68dec8fa6af47fbc3b26d8c1f72fde4da5b857e627ae6618696
7
- data.tar.gz: 05b34c3e338867fc0ebd8fe22bcddd41b41ccbf0629deb2813a2f63081cf0e05def3aaf48e9a6caa7474f754bbdffe6a224b90697a8ba7a6f9549176059d4d78
6
+ metadata.gz: 93afbfeeabbfc19f6f8d772adc63b96206ce27ad6cc3b1620d4cd934e14230123400d1b94e03f2c8b10b5a52ca341d116106dd7f3d82b272ffda1f8bc510d746
7
+ data.tar.gz: 7148ddf6fb5511bfe17f4659c1b79b2122bf7646e9b06a4f7445335fd7925b1e9117bb9c43a0efb1aca80e3f310b492e81c39522d0a3e66afed3ceb4538ba280
data/.rubocop.yml CHANGED
@@ -70,6 +70,9 @@ Naming/FileName:
70
70
  Style/Documentation:
71
71
  Enabled: false
72
72
 
73
+ Style/EachWithObject:
74
+ Enabled: false
75
+
73
76
  Style/StringLiterals:
74
77
  EnforcedStyle: 'single_quotes'
75
78
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
data/bin/cibuild CHANGED
@@ -7,8 +7,7 @@ set -e
7
7
 
8
8
  cd "$(dirname "$0")/.."
9
9
 
10
- echo "Started at…"
11
- date "+%H:%M:%S"
10
+ echo [$(date "+%H:%M:%S")] "==> Started at…"
12
11
 
13
12
  # GC customizations
14
13
  export RUBY_GC_MALLOC_LIMIT=79000000
@@ -19,4 +18,4 @@ export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
19
18
 
20
19
  ruby -v
21
20
  gem install bundler --no-ri --no-rdoc --conservative
22
- time bin/test
21
+ bin/test
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'saml/kit'
data/bin/lint CHANGED
@@ -4,10 +4,8 @@ set -e
4
4
 
5
5
  [ -z "$DEBUG" ] || set -x
6
6
 
7
- echo "==> Running setup…"
8
- date "+%H:%M:%S"
7
+ echo [$(date "+%H:%M:%S")] "==> Running setup…"
9
8
  bin/setup
10
9
 
11
- echo "==> Running linters…"
12
- date "+%H:%M:%S"
10
+ echo [$(date "+%H:%M:%S")] "==> Running linters…"
13
11
  bundle exec rake rubocop
data/bin/setup CHANGED
@@ -4,5 +4,3 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle check || bundle install --jobs $(nproc)
7
-
8
- # Do any other automated setup that you need to do here
data/bin/test CHANGED
@@ -10,10 +10,8 @@ cd "$(dirname "$0")/.."
10
10
 
11
11
  [ -z "$DEBUG" ] || set -x
12
12
 
13
- echo "==> Running setup…"
14
- date "+%H:%M:%S"
13
+ echo [$(date "+%H:%M:%S")] "==> Running setup…"
15
14
  bin/setup
16
15
 
17
- echo "==> Running tests…"
18
- date "+%H:%M:%S"
16
+ echo [$(date "+%H:%M:%S")] "==> Running tests…"
19
17
  bundle exec rake spec
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'saml/kit'
3
5
 
4
6
  Saml::Kit.deprecate("Use the 'saml-kit-cli' gem instead. saml-kit-create-self-signed-certificate")
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'saml/kit'
3
5
 
4
6
  Saml::Kit.deprecate("Use the 'saml-kit-cli' gem instead. saml-kit-decode-http-post")
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
2
4
  require 'saml/kit'
3
5
 
4
6
  Saml::Kit.deprecate("Use the 'saml-kit-cli' gem instead. saml-kit-decode-http-redirect*")
data/lib/saml-kit.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'saml/kit'
data/lib/saml/kit.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'saml/kit/version'
2
4
 
3
5
  require 'active_model'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  class Assertion
@@ -18,22 +20,18 @@ module Saml
18
20
  def initialize(node, configuration: Saml::Kit.configuration, private_keys: [])
19
21
  @name = 'Assertion'
20
22
  @node = node
21
- @xml_hash = hash_from(node)['Response'] || {}
22
23
  @configuration = configuration
23
24
  @occurred_at = Time.current
24
- decrypt!(::Xml::Kit::Decryption.new(
25
- private_keys: (
26
- configuration.private_keys(use: :encryption) + private_keys
27
- ).uniq
28
- ))
25
+ private_keys = (configuration.private_keys(use: :encryption) + private_keys).uniq
26
+ decrypt!(::Xml::Kit::Decryption.new(private_keys: private_keys))
29
27
  end
30
28
 
31
29
  def issuer
32
- assertion.fetch('Issuer')
30
+ at_xpath('./saml:Issuer').try(:text)
33
31
  end
34
32
 
35
33
  def name_id
36
- assertion.fetch('Subject', {}).fetch('NameID', nil)
34
+ at_xpath('./saml:Subject/saml:NameID').try(:text)
37
35
  end
38
36
 
39
37
  def signed?
@@ -54,35 +52,26 @@ module Saml
54
52
  end
55
53
 
56
54
  def attributes
57
- @attributes ||=
58
- begin
59
- attrs = assertion.fetch('AttributeStatement', {}).fetch('Attribute', [])
60
- items = if attrs.is_a? Hash
61
- [[attrs['Name'], attrs['AttributeValue']]]
62
- else
63
- attrs.map { |item| [item['Name'], item['AttributeValue']] }
64
- end
65
- Hash[items].with_indifferent_access
66
- end
55
+ @attributes ||= search('./saml:AttributeStatement/saml:Attribute').inject({}) do |memo, item|
56
+ memo[item.attribute('Name').value] = item.at_xpath('./saml:AttributeValue', Saml::Kit::Document::NAMESPACES).try(:text)
57
+ memo
58
+ end.with_indifferent_access
67
59
  end
68
60
 
69
61
  def started_at
70
- parse_date(assertion.fetch('Conditions', {}).fetch('NotBefore', nil))
62
+ parse_date(at_xpath('./saml:Conditions/@NotBefore').try(:value))
71
63
  end
72
64
 
73
65
  def expired_at
74
- parse_date(assertion.fetch('Conditions', {}).fetch('NotOnOrAfter', nil))
66
+ parse_date(at_xpath('./saml:Conditions/@NotOnOrAfter').try(:value))
75
67
  end
76
68
 
77
69
  def audiences
78
- Array(assertion['Conditions']['AudienceRestriction']['Audience'])
79
- rescue StandardError => error
80
- Saml::Kit.logger.error(error)
81
- []
70
+ search('./saml:Conditions/saml:AudienceRestriction/saml:Audience').map(&:text)
82
71
  end
83
72
 
84
73
  def encrypted?
85
- @xml_hash.fetch('EncryptedAssertion', nil).present?
74
+ @encrypted
86
75
  end
87
76
 
88
77
  def decryptable?
@@ -91,7 +80,7 @@ module Saml
91
80
  end
92
81
 
93
82
  def present?
94
- assertion.present?
83
+ @node.present?
95
84
  end
96
85
 
97
86
  def to_xml(pretty: false)
@@ -102,19 +91,10 @@ module Saml
102
91
 
103
92
  attr_reader :configuration
104
93
 
105
- def assertion
106
- @assertion ||=
107
- begin
108
- result = (hash_from(@node)['Response'] || {})['Assertion']
109
- return result if result.is_a?(Hash)
110
- {}
111
- end
112
- end
113
-
114
94
  def decrypt!(decryptor)
115
- return unless encrypted?
116
-
117
- encrypted_assertion = @node.at_xpath('./xmlenc:EncryptedData', Saml::Kit::Document::NAMESPACES)
95
+ encrypted_assertion = at_xpath('./xmlenc:EncryptedData')
96
+ @encrypted = encrypted_assertion.present?
97
+ return unless @encrypted
118
98
  @node = decryptor.decrypt_node(encrypted_assertion)
119
99
  rescue Xml::Kit::DecryptionError => error
120
100
  @cannot_decrypt = true
@@ -151,12 +131,12 @@ module Saml
151
131
  end
152
132
 
153
133
  def at_xpath(xpath)
134
+ return unless @node
154
135
  @node.at_xpath(xpath, Saml::Kit::Document::NAMESPACES)
155
136
  end
156
137
 
157
- def hash_from(node)
158
- return {} if node.nil?
159
- Hash.from_xml(node.document.root.to_s) || {}
138
+ def search(xpath)
139
+ @node.search(xpath, Saml::Kit::Document::NAMESPACES)
160
140
  end
161
141
  end
162
142
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # This class can be used to parse a SAML AuthnRequest or generate one.
@@ -31,15 +33,19 @@ module Saml
31
33
  # Extract the AssertionConsumerServiceURL from the AuthnRequest
32
34
  # <samlp:AuthnRequest AssertionConsumerServiceURL="https://carroll.com/acs"></samlp:AuthnRequest>
33
35
  def assertion_consumer_service_url
34
- to_h[name]['AssertionConsumerServiceURL']
36
+ at_xpath('./*/@AssertionConsumerServiceURL').try(:value)
37
+ end
38
+
39
+ def name_id_format
40
+ name_id_policy
35
41
  end
36
42
 
37
43
  # Extract the NameIDPolicy from the AuthnRequest
38
44
  # <samlp:AuthnRequest>
39
45
  # <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/>
40
46
  # </samlp:AuthnRequest>
41
- def name_id_format
42
- to_h[name]['NameIDPolicy']['Format']
47
+ def name_id_policy
48
+ at_xpath('./*/samlp:NameIDPolicy/@Format').try(:value)
43
49
  end
44
50
 
45
51
  # Generate a Response for a specific user.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'saml/kit/bindings/binding'
2
4
  require 'saml/kit/bindings/http_post'
3
5
  require 'saml/kit/bindings/http_redirect'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Bindings
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Bindings
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Bindings
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Bindings
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Buildable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'saml/kit/xml_templatable'
2
4
  require 'saml/kit/builders/assertion'
3
5
  require 'saml/kit/builders/authentication_request'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Builders
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.Assertion(assertion_options) do
2
4
  xml.Issuer issuer
3
5
  signature_for(reference_id: reference_id, xml: xml)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.instruct!
2
4
  xml.tag!('samlp:AuthnRequest', request_options) do
3
5
  xml.tag!('saml:Issuer', issuer)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.EncryptedAssertion xmlns: Saml::Kit::Namespaces::ASSERTION do
2
4
  encryption_for(xml: xml) do |xml|
3
5
  render assertion, xml: xml
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.IDPSSODescriptor descriptor_options do
2
4
  configuration.certificates(use: :signing).each do |certificate|
3
5
  render certificate, xml: xml
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.instruct!
2
4
  xml.LogoutRequest logout_request_options do
3
5
  xml.Issuer({ xmlns: Saml::Kit::Namespaces::ASSERTION }, issuer)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.instruct!
2
4
  xml.LogoutResponse logout_response_options do
3
5
  xml.Issuer(issuer, xmlns: Saml::Kit::Namespaces::ASSERTION)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.instruct!
2
4
  xml.EntityDescriptor entity_descriptor_options do
3
5
  signature_for(reference_id: id, xml: xml)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.instruct!
2
4
  xml.Response response_options do
3
5
  xml.Issuer(issuer, xmlns: Saml::Kit::Namespaces::ASSERTION)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  xml.SPSSODescriptor descriptor_options do
2
4
  configuration.certificates(use: :signing).each do |certificate|
3
5
  render certificate, xml: xml
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  class CompositeMetadata < Metadata # :nodoc:
@@ -14,7 +16,7 @@ module Saml
14
16
 
15
17
  def services(type)
16
18
  xpath = map { |x| "//md:EntityDescriptor/md:#{x.name}/md:#{type}" }.join('|')
17
- document.find_all(xpath).map do |item|
19
+ search(xpath).map do |item|
18
20
  binding = item.attribute('Binding').value
19
21
  location = item.attribute('Location').value
20
22
  Saml::Kit::Bindings.create_for(binding, location)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # This class represents the main configuration that is use for generating SAML documents.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # The default metadata registry is used to fetch the metadata associated with an issuer or entity id.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  class Document
@@ -29,27 +31,27 @@ module Saml
29
31
 
30
32
  # Returns the ID for the SAML document.
31
33
  def id
32
- root.fetch('ID', nil)
34
+ at_xpath('./*/@ID').try(:value)
33
35
  end
34
36
 
35
37
  # Returns the Issuer for the SAML document.
36
38
  def issuer
37
- root.fetch('Issuer', nil)
39
+ at_xpath('./*/saml:Issuer').try(:text)
38
40
  end
39
41
 
40
42
  # Returns the Version of the SAML document.
41
43
  def version
42
- root.fetch('Version', {})
44
+ at_xpath('./*/@Version').try(:value)
43
45
  end
44
46
 
45
47
  # Returns the Destination of the SAML document.
46
48
  def destination
47
- root.fetch('Destination', nil)
49
+ at_xpath('./*/@Destination').try(:value)
48
50
  end
49
51
 
50
52
  # Returns the Destination of the SAML document.
51
53
  def issue_instant
52
- Time.parse(root['IssueInstant'])
54
+ Time.parse(at_xpath('./*/@IssueInstant').try(:value))
53
55
  end
54
56
 
55
57
  # Returns the SAML document returned as a Hash.
@@ -102,15 +104,12 @@ module Saml
102
104
  # @param xml [String] the raw xml string.
103
105
  # @param configuration [Saml::Kit::Configuration] the configuration to use for unpacking the document.
104
106
  def to_saml_document(xml, configuration: Saml::Kit.configuration)
105
- xml_document = ::Xml::Kit::Document.new(xml, namespaces: {
106
- "samlp": ::Saml::Kit::Namespaces::PROTOCOL
107
- })
108
107
  constructor = {
109
108
  'AuthnRequest' => Saml::Kit::AuthenticationRequest,
110
109
  'LogoutRequest' => Saml::Kit::LogoutRequest,
111
110
  'LogoutResponse' => Saml::Kit::LogoutResponse,
112
111
  'Response' => Saml::Kit::Response,
113
- }[xml_document.find_by(XPATH).name] || InvalidDocument
112
+ }[Nokogiri::XML(xml).at_xpath(XPATH, "samlp": ::Saml::Kit::Namespaces::PROTOCOL).name] || InvalidDocument
114
113
  constructor.new(xml, configuration: configuration)
115
114
  rescue StandardError => error
116
115
  Saml::Kit.logger.error(error)
@@ -138,10 +137,6 @@ module Saml
138
137
 
139
138
  attr_reader :content, :name, :configuration
140
139
 
141
- def root
142
- to_h.fetch(name, {})
143
- end
144
-
145
140
  def must_match_xsd
146
141
  matches_xsd?(PROTOCOL_XSD)
147
142
  end
@@ -151,7 +146,7 @@ module Saml
151
146
  end
152
147
 
153
148
  def expected_type?
154
- to_h[name].present?
149
+ at_xpath("./samlp:#{name}").present?
155
150
  end
156
151
 
157
152
  def must_be_valid_version
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # This class is used to parse the IDPSSODescriptor from a SAML metadata document.
@@ -38,7 +40,7 @@ module Saml
38
40
  # Returns the IDPSSODescriptor/@WantAuthnRequestsSigned attribute.
39
41
  def want_authn_requests_signed
40
42
  xpath = "/md:EntityDescriptor/md:#{name}"
41
- attribute = document.find_by(xpath).attribute('WantAuthnRequestsSigned')
43
+ attribute = at_xpath(xpath).attribute('WantAuthnRequestsSigned')
42
44
  return true if attribute.nil?
43
45
  attribute.text.casecmp('true').zero?
44
46
  end
@@ -57,7 +59,7 @@ module Saml
57
59
 
58
60
  # Returns each of the Attributes in the metadata.
59
61
  def attributes
60
- document.find_all("/md:EntityDescriptor/md:#{name}/saml:Attribute").map do |item|
62
+ search("/md:EntityDescriptor/md:#{name}/saml:Attribute").map do |item|
61
63
  {
62
64
  format: item.attribute('NameFormat').try(:value),
63
65
  name: item.attribute('Name').value,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # {include:file:spec/saml/invalid_document_spec.rb}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # This class can be used to parse a LogoutRequest SAML document.
@@ -36,7 +38,11 @@ module Saml
36
38
 
37
39
  # Returns the NameID value.
38
40
  def name_id
39
- to_h[name]['NameID']
41
+ at_xpath('./*/saml:NameID').try(:text)
42
+ end
43
+
44
+ def name_id_format
45
+ at_xpath('./*/saml:NameID/@Format').try(:value)
40
46
  end
41
47
 
42
48
  # Generates a Serialized LogoutResponse using the encoding rules for the specified binding.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # This class is used to parse a LogoutResponse SAML document.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # The Metadata object can be used to parse an XML string of metadata.
@@ -29,11 +31,11 @@ module Saml
29
31
  include Buildable
30
32
  METADATA_XSD = File.expand_path('./xsd/saml-schema-metadata-2.0.xsd', File.dirname(__FILE__)).freeze
31
33
  NAMESPACES = {
32
- "NameFormat": Namespaces::ATTR_SPLAT,
33
- "ds": ::Xml::Kit::Namespaces::XMLDSIG,
34
- "md": Namespaces::METADATA,
35
- "saml": Namespaces::ASSERTION,
36
- "samlp": Namespaces::PROTOCOL,
34
+ NameFormat: Namespaces::ATTR_SPLAT,
35
+ ds: ::Xml::Kit::Namespaces::XMLDSIG,
36
+ md: Namespaces::METADATA,
37
+ saml: Namespaces::ASSERTION,
38
+ samlp: Namespaces::PROTOCOL,
37
39
  }.freeze
38
40
 
39
41
  validates_presence_of :metadata
@@ -50,36 +52,34 @@ module Saml
50
52
 
51
53
  # Returns the /EntityDescriptor/@entityID
52
54
  def entity_id
53
- document.find_by('/md:EntityDescriptor/@entityID').value
55
+ at_xpath('/md:EntityDescriptor/@entityID').try(:value)
54
56
  end
55
57
 
56
58
  # Returns the supported NameIDFormats.
57
59
  def name_id_formats
58
- document.find_all("/md:EntityDescriptor/md:#{name}/md:NameIDFormat").map(&:text)
60
+ search("/md:EntityDescriptor/md:#{name}/md:NameIDFormat").map(&:text)
59
61
  end
60
62
 
61
63
  # Returns the Organization Name
62
64
  def organization_name
63
- document.find_by('/md:EntityDescriptor/md:Organization/md:OrganizationName').try(:text)
65
+ at_xpath('/md:EntityDescriptor/md:Organization/md:OrganizationName').try(:text)
64
66
  end
65
67
 
66
68
  # Returns the Organization URL
67
69
  def organization_url
68
- document.find_by('/md:EntityDescriptor/md:Organization/md:OrganizationURL').try(:text)
70
+ at_xpath('/md:EntityDescriptor/md:Organization/md:OrganizationURL').try(:text)
69
71
  end
70
72
 
71
73
  # Returns the Company
72
74
  def contact_person_company
73
- document.find_by('/md:EntityDescriptor/md:ContactPerson/md:Company').try(:text)
75
+ at_xpath('/md:EntityDescriptor/md:ContactPerson/md:Company').try(:text)
74
76
  end
75
77
 
76
78
  # Returns each of the X509 certificates.
77
79
  def certificates
78
- @certificates ||= document.find_all("/md:EntityDescriptor/md:#{name}/md:KeyDescriptor").map do |item|
79
- cert = item.at_xpath('./ds:KeyInfo/ds:X509Data/ds:X509Certificate', NAMESPACES).text
80
- attribute = item.attribute('use')
81
- use = attribute.nil? ? nil : item.attribute('use').value
82
- ::Xml::Kit::Certificate.new(cert, use: use)
80
+ @certificates ||= search("/md:EntityDescriptor/md:#{name}/md:KeyDescriptor").map do |item|
81
+ cert = item.at_xpath('./ds:KeyInfo/ds:X509Data/ds:X509Certificate', 'ds' => ::Xml::Kit::Namespaces::XMLDSIG).try(:text)
82
+ ::Xml::Kit::Certificate.new(cert, use: item.attribute('use').try(:value))
83
83
  end
84
84
  end
85
85
 
@@ -97,7 +97,7 @@ module Saml
97
97
  #
98
98
  # @param type [String] the type of service. .E.g. `AssertionConsumerServiceURL`
99
99
  def services(type)
100
- document.find_all("/md:EntityDescriptor/md:#{name}/md:#{type}").map do |item|
100
+ search("/md:EntityDescriptor/md:#{name}/md:#{type}").map do |item|
101
101
  binding = item.attribute('Binding').value
102
102
  location = item.attribute('Location').value
103
103
  Saml::Kit::Bindings.create_for(binding, location)
@@ -132,9 +132,7 @@ module Saml
132
132
  # @param relay_state [String] the relay state to have echo'd back.
133
133
  # @return [Array] Returns an array with a url and Hash of parameters to send to the other party.
134
134
  def logout_request_for(user, binding: :http_post, relay_state: nil)
135
- builder = Saml::Kit::LogoutRequest.builder(user) do |x|
136
- yield x if block_given?
137
- end
135
+ builder = Saml::Kit::LogoutRequest.builder(user) { |x| yield x if block_given? }
138
136
  request_binding = single_logout_service_for(binding: binding)
139
137
  request_binding.serialize(builder, relay_state: relay_state)
140
138
  end
@@ -145,9 +143,7 @@ module Saml
145
143
  # @param use [Symbol] the type of certificates to look at. Can be `:signing` or `:encryption`.
146
144
  # @return [Xml::Kit::Certificate] returns the matching `{Xml::Kit::Certificate}`
147
145
  def matches?(fingerprint, use: :signing)
148
- certificates.find do |certificate|
149
- certificate.for?(use) && certificate.fingerprint == fingerprint
150
- end
146
+ certificates.find { |x| x.for?(use) && x.fingerprint == fingerprint }
151
147
  end
152
148
 
153
149
  # Returns the XML document converted to a Hash.
@@ -159,7 +155,7 @@ module Saml
159
155
  #
160
156
  # @param pretty [Symbol] true to return a human friendly version of the XML.
161
157
  def to_xml(pretty: false)
162
- document.to_xml(pretty: pretty)
158
+ pretty ? to_nokogiri.to_xml(indent: 2) : @xml
163
159
  end
164
160
 
165
161
  # Returns the XML document as a [String].
@@ -189,13 +185,15 @@ module Saml
189
185
  # @param content [String] the raw metadata XML.
190
186
  # @return [Saml::Kit::Metadata] the metadata document or subclass.
191
187
  def from(content)
192
- hash = Hash.from_xml(content)
193
- entity_descriptor = hash['EntityDescriptor']
194
- if entity_descriptor.key?('SPSSODescriptor') && entity_descriptor.key?('IDPSSODescriptor')
188
+ document = Nokogiri::XML(content)
189
+ return unless document.at_xpath('/md:EntityDescriptor', NAMESPACES)
190
+ sp = document.at_xpath('/md:EntityDescriptor/md:SPSSODescriptor', NAMESPACES)
191
+ idp = document.at_xpath('/md:EntityDescriptor/md:IDPSSODescriptor', NAMESPACES)
192
+ if sp && idp
195
193
  Saml::Kit::CompositeMetadata.new(content)
196
- elsif entity_descriptor.keys.include?('SPSSODescriptor')
194
+ elsif sp
197
195
  Saml::Kit::ServiceProviderMetadata.new(content)
198
- elsif entity_descriptor.keys.include?('IDPSSODescriptor')
196
+ elsif idp
199
197
  Saml::Kit::IdentityProviderMetadata.new(content)
200
198
  end
201
199
  end
@@ -210,16 +208,21 @@ module Saml
210
208
 
211
209
  attr_reader :xml
212
210
 
213
- def document
214
- @document ||= ::Xml::Kit::Document.new(xml, namespaces: NAMESPACES)
211
+ # @!visibility private
212
+ def to_nokogiri
213
+ @nokogiri ||= Nokogiri::XML(xml)
215
214
  end
216
215
 
217
216
  def at_xpath(xpath)
218
- document.find_by(xpath)
217
+ to_nokogiri.at_xpath(xpath, NAMESPACES)
218
+ end
219
+
220
+ def search(xpath)
221
+ to_nokogiri.search(xpath, NAMESPACES)
219
222
  end
220
223
 
221
224
  def metadata
222
- document.find_by("/md:EntityDescriptor/md:#{name}").present?
225
+ at_xpath("/md:EntityDescriptor/md:#{name}").present?
223
226
  end
224
227
 
225
228
  def must_contain_descriptor
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Namespaces
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  class NullAssertion
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Requestable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Respondable
@@ -16,12 +18,12 @@ module Saml
16
18
 
17
19
  # Returns the /Status/StatusCode@Value
18
20
  def status_code
19
- to_h.fetch(name, {}).fetch('Status', {}).fetch('StatusCode', {}).fetch('Value', nil)
21
+ at_xpath('./*/samlp:Status/samlp:StatusCode/@Value').try(:value)
20
22
  end
21
23
 
22
24
  # Returns the /InResponseTo attribute.
23
25
  def in_response_to
24
- to_h.fetch(name, {}).fetch('InResponseTo', nil)
26
+ at_xpath('./*/@InResponseTo').try(:value)
25
27
  end
26
28
 
27
29
  # Returns true if the Status code is #{Saml::Kit::Namespaces::SUCCESS}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # {include:file:spec/examples/response_spec.rb}
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'saml/kit/rspec/have_query_param'
2
4
  require 'saml/kit/rspec/have_xpath'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
 
3
5
  RSpec::Matchers.define :have_query_param do |key|
@@ -1,13 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec::Matchers.define :have_xpath do |xpath|
2
4
  match do |actual|
3
- namespaces = {
4
- "NameFormat": Saml::Kit::Namespaces::ATTR_SPLAT,
5
- "ds": ::Xml::Kit::Namespaces::XMLDSIG,
6
- "md": Saml::Kit::Namespaces::METADATA,
7
- "saml": Saml::Kit::Namespaces::ASSERTION,
8
- "samlp": Saml::Kit::Namespaces::PROTOCOL,
9
- }
10
- xml_document(actual).xpath(xpath, namespaces).any?
5
+ xml_document(actual).xpath(xpath, Saml::Kit::Document::NAMESPACES).any?
11
6
  end
12
7
 
13
8
  failure_message do |actual|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Serializable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  # {include:file:spec/examples/service_provider_metadata_spec.rb}
@@ -20,7 +22,7 @@ module Saml
20
22
 
21
23
  # Returns true when the metadata demands that Assertions must be signed.
22
24
  def want_assertions_signed
23
- attribute = document.find_by("/md:EntityDescriptor/md:#{name}").attribute('WantAssertionsSigned')
25
+ attribute = at_xpath("/md:EntityDescriptor/md:#{name}").attribute('WantAssertionsSigned')
24
26
  return true if attribute.nil?
25
27
  attribute.text.casecmp('true').zero?
26
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  class Signature
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Translatable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module Trustable
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
- VERSION = '1.0.9'.freeze
5
+ VERSION = '1.0.10'.freeze
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module XmlTemplatable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Saml
2
4
  module Kit
3
5
  module XsdValidatable
@@ -5,8 +7,7 @@ module Saml
5
7
  def matches_xsd?(xsd)
6
8
  Dir.chdir(File.dirname(xsd)) do
7
9
  xsd = Nokogiri::XML::Schema(IO.read(xsd))
8
- document = Nokogiri::XML(to_xml)
9
- xsd.validate(document).each do |error|
10
+ xsd.validate(to_nokogiri).each do |error|
10
11
  errors[:base] << error.message
11
12
  end
12
13
  end
data/saml-kit.gemspec CHANGED
@@ -1,4 +1,6 @@
1
1
 
2
+ # frozen_string_literal: true
3
+
2
4
  lib = File.expand_path('../lib', __FILE__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require 'saml/kit/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saml-kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 1.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-18 00:00:00.000000000 Z
11
+ date: 2018-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -294,7 +294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
294
  version: '0'
295
295
  requirements: []
296
296
  rubyforge_project:
297
- rubygems_version: 2.7.5
297
+ rubygems_version: 2.7.6
298
298
  signing_key:
299
299
  specification_version: 4
300
300
  summary: A simple toolkit for working with SAML.