kl-ruby-saml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE +19 -0
  7. data/README.md +575 -0
  8. data/Rakefile +41 -0
  9. data/changelog.md +75 -0
  10. data/gemfiles/nokogiri-1.5.gemfile +5 -0
  11. data/lib/onelogin/ruby-saml.rb +17 -0
  12. data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
  13. data/lib/onelogin/ruby-saml/attributes.rb +128 -0
  14. data/lib/onelogin/ruby-saml/authrequest.rb +156 -0
  15. data/lib/onelogin/ruby-saml/http_error.rb +7 -0
  16. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
  17. data/lib/onelogin/ruby-saml/logging.rb +30 -0
  18. data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
  19. data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
  20. data/lib/onelogin/ruby-saml/metadata.rb +123 -0
  21. data/lib/onelogin/ruby-saml/response.rb +722 -0
  22. data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
  23. data/lib/onelogin/ruby-saml/settings.rb +165 -0
  24. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
  25. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
  26. data/lib/onelogin/ruby-saml/utils.rb +172 -0
  27. data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
  28. data/lib/onelogin/ruby-saml/version.rb +5 -0
  29. data/lib/ruby-saml.rb +1 -0
  30. data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
  31. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  32. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  33. data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
  34. data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
  35. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  36. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  37. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  38. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  39. data/lib/schemas/xenc-schema.xsd +136 -0
  40. data/lib/schemas/xml.xsd +287 -0
  41. data/lib/schemas/xmldsig-core-schema.xsd +309 -0
  42. data/lib/xml_security.rb +358 -0
  43. data/ruby-saml.gemspec +57 -0
  44. data/test/certificates/certificate1 +12 -0
  45. data/test/certificates/certificate_without_head_foot +1 -0
  46. data/test/certificates/formatted_certificate +14 -0
  47. data/test/certificates/formatted_private_key +12 -0
  48. data/test/certificates/formatted_rsa_private_key +12 -0
  49. data/test/certificates/invalid_certificate1 +1 -0
  50. data/test/certificates/invalid_certificate2 +1 -0
  51. data/test/certificates/invalid_certificate3 +12 -0
  52. data/test/certificates/invalid_private_key1 +1 -0
  53. data/test/certificates/invalid_private_key2 +1 -0
  54. data/test/certificates/invalid_private_key3 +10 -0
  55. data/test/certificates/invalid_rsa_private_key1 +1 -0
  56. data/test/certificates/invalid_rsa_private_key2 +1 -0
  57. data/test/certificates/invalid_rsa_private_key3 +10 -0
  58. data/test/certificates/ruby-saml.crt +14 -0
  59. data/test/certificates/ruby-saml.key +15 -0
  60. data/test/idp_metadata_parser_test.rb +95 -0
  61. data/test/logging_test.rb +62 -0
  62. data/test/logout_requests/invalid_slo_request.xml +6 -0
  63. data/test/logout_requests/slo_request.xml +4 -0
  64. data/test/logout_requests/slo_request.xml.base64 +1 -0
  65. data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
  66. data/test/logout_requests/slo_request_with_session_index.xml +5 -0
  67. data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
  68. data/test/logoutrequest_test.rb +211 -0
  69. data/test/logoutresponse_test.rb +258 -0
  70. data/test/metadata_test.rb +203 -0
  71. data/test/request_test.rb +282 -0
  72. data/test/response_test.rb +1094 -0
  73. data/test/responses/adfs_response_sha1.xml +46 -0
  74. data/test/responses/adfs_response_sha256.xml +46 -0
  75. data/test/responses/adfs_response_sha384.xml +46 -0
  76. data/test/responses/adfs_response_sha512.xml +46 -0
  77. data/test/responses/adfs_response_xmlns.xml +45 -0
  78. data/test/responses/attackxee.xml +13 -0
  79. data/test/responses/idp_descriptor.xml +3 -0
  80. data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
  81. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
  82. data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
  83. data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
  84. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
  85. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
  86. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
  87. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
  88. data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
  89. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  90. data/test/responses/invalids/no_id.xml.base64 +1 -0
  91. data/test/responses/invalids/no_saml2.xml.base64 +1 -0
  92. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  93. data/test/responses/invalids/no_status.xml.base64 +1 -0
  94. data/test/responses/invalids/no_status_code.xml.base64 +1 -0
  95. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
  96. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
  97. data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
  98. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
  99. data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
  100. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
  101. data/test/responses/no_signature_ns.xml +48 -0
  102. data/test/responses/open_saml_response.xml +56 -0
  103. data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
  104. data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
  105. data/test/responses/response_eval.xml +7 -0
  106. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  107. data/test/responses/response_unsigned_xml_base64 +1 -0
  108. data/test/responses/response_with_ampersands.xml +139 -0
  109. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  110. data/test/responses/response_with_multiple_attribute_values.xml +67 -0
  111. data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
  112. data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
  113. data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
  114. data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
  115. data/test/responses/response_without_attributes.xml.base64 +79 -0
  116. data/test/responses/response_wrapped.xml.base64 +150 -0
  117. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  118. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  119. data/test/responses/simple_saml_php.xml +71 -0
  120. data/test/responses/starfield_response.xml.base64 +1 -0
  121. data/test/responses/test_sign.xml +43 -0
  122. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  123. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  124. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  125. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  126. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  127. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  128. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  129. data/test/responses/valid_response.xml.base64 +1 -0
  130. data/test/saml_message_test.rb +56 -0
  131. data/test/settings_test.rb +218 -0
  132. data/test/slo_logoutrequest_test.rb +275 -0
  133. data/test/slo_logoutresponse_test.rb +185 -0
  134. data/test/test_helper.rb +252 -0
  135. data/test/utils_test.rb +145 -0
  136. data/test/xml_security_test.rb +329 -0
  137. metadata +415 -0
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ #not being used yet.
5
+ require 'rake/testtask'
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' << 'test'
8
+ test.pattern = 'test/**/*_test.rb'
9
+ test.verbose = true
10
+ end
11
+
12
+ begin
13
+ require 'rcov/rcovtask'
14
+ Rcov::RcovTask.new do |test|
15
+ test.libs << 'test'
16
+ test.pattern = 'test/**/*_test.rb'
17
+ test.verbose = true
18
+ end
19
+ rescue LoadError
20
+ task :rcov do
21
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
22
+ end
23
+ end
24
+
25
+ task :test
26
+
27
+ task :default => :test
28
+
29
+ # require 'rake/rdoctask'
30
+ # Rake::RDocTask.new do |rdoc|
31
+ # if File.exist?('VERSION')
32
+ # version = File.read('VERSION')
33
+ # else
34
+ # version = ""
35
+ # end
36
+
37
+ # rdoc.rdoc_dir = 'rdoc'
38
+ # rdoc.title = "ruby-saml #{version}"
39
+ # rdoc.rdoc_files.include('README*')
40
+ # rdoc.rdoc_files.include('lib/**/*.rb')
41
+ #end
data/changelog.md ADDED
@@ -0,0 +1,75 @@
1
+ # RubySaml Changelog
2
+
3
+ ### 1.0.0 (June 30, 2015)
4
+ * [#247](https://github.com/onelogin/ruby-saml/pull/247) Avoid entity expansion (XEE attacks)
5
+ * [#246](https://github.com/onelogin/ruby-saml/pull/246) Fix bug generating Logout Response (issuer was at wrong order)
6
+ * [#243](https://github.com/onelogin/ruby-saml/issues/243) and [#244](https://github.com/onelogin/ruby-saml/issues/244) Fix metadata builder errors. Fix metadata xsd.
7
+ * [#241](https://github.com/onelogin/ruby-saml/pull/241) Add decrypt support (EncryptID and EncryptedAssertion). Improve compatibility with namespaces.
8
+ * [#240](https://github.com/onelogin/ruby-saml/pull/240) and [#238](https://github.com/onelogin/ruby-saml/pull/238) Improve test coverage and refactor.
9
+ * [#239](https://github.com/onelogin/ruby-saml/pull/239) Improve security: Add more validations to SAMLResponse, LogoutRequest and LogoutResponse. Refactor code and improve tests coverage.
10
+ * [#237](https://github.com/onelogin/ruby-saml/pull/237) Don't pretty print metadata by default.
11
+ * [#235](https://github.com/onelogin/ruby-saml/pull/235) Remove the soft parameter from validation methods. Now can be configured on the settings and each class read it and store as an attribute of the class. Adding some validations and refactor old ones.
12
+ * [#232](https://github.com/onelogin/ruby-saml/pull/232) Improve validations: Store the causes in the errors array, code refactor
13
+ * [#231](https://github.com/onelogin/ruby-saml/pull/231) Refactor HTTP-Redirect Sign method, Move test data to right folder
14
+ * [#226](https://github.com/onelogin/ruby-saml/pull/226) Ensure IdP certificate is formatted properly
15
+ * [#225](https://github.com/onelogin/ruby-saml/pull/225) Add documentation to several methods. Fix xpath injection on xml_security.rb
16
+ * [#223](https://github.com/onelogin/ruby-saml/pull/223) Allow logging to be delegated to an arbitrary Logger
17
+ * [#222](https://github.com/onelogin/ruby-saml/pull/222) No more silent failure fetching idp metadata (OneLogin::RubySaml::HttpError raised).
18
+
19
+ ### 0.9.2 (Apr 28, 2015)
20
+ * [#216](https://github.com/onelogin/ruby-saml/pull/216) Add fingerprint algorithm support
21
+ * [#218](https://github.com/onelogin/ruby-saml/pull/218) Update README.md
22
+ * [#214](https://github.com/onelogin/ruby-saml/pull/214) Cleanup `SamlMessage` class
23
+ * [#213](https://github.com/onelogin/ruby-saml/pull/213) Add ability to sign metadata. (Improved)
24
+ * [#212](https://github.com/onelogin/ruby-saml/pull/212) Rename library entry point
25
+ * [#210](https://github.com/onelogin/ruby-saml/pull/210) Call assert in tests
26
+ * [#208](https://github.com/onelogin/ruby-saml/pull/208) Update tests and CI for Ruby 2.2.0
27
+ * [#205](https://github.com/onelogin/ruby-saml/pull/205) Allow requirement of single files
28
+ * [#204](https://github.com/onelogin/ruby-saml/pull/204) Require ‘net/http’ library
29
+ * [#201](https://github.com/onelogin/ruby-saml/pull/201) Freeze and duplicate default security settings hash so that it doesn't get modified.
30
+ * [#200](https://github.com/onelogin/ruby-saml/pull/200) Set default SSL certificate store in Ruby 1.8.
31
+ * [#199](https://github.com/onelogin/ruby-saml/pull/199) Change Nokogiri's runtime dependency to fix support for Ruby 1.8.7.
32
+ * [#179](https://github.com/onelogin/ruby-saml/pull/179) Add support for setting the entity ID and name ID format when parsing metadata
33
+ * [#175](https://github.com/onelogin/ruby-saml/pull/175) Introduce thread safety to SAML schema validation
34
+ * [#171](https://github.com/onelogin/ruby-saml/pull/171) Fix inconsistent results with using regex matches in decode_raw_saml
35
+
36
+ ### 0.9.1 (Feb 10, 2015)
37
+ * [#194](https://github.com/onelogin/ruby-saml/pull/194) Relax nokogiri gem requirements
38
+ * [#191](https://github.com/onelogin/ruby-saml/pull/191) Use Minitest instead of Test::Unit
39
+
40
+ ### 0.9 (Jan 26, 2015)
41
+ * [#169](https://github.com/onelogin/ruby-saml/pull/169) WantAssertionSigned should be either true or false
42
+ * [#167](https://github.com/onelogin/ruby-saml/pull/167) (doc update) make unit of clock drift obvious
43
+ * [#160](https://github.com/onelogin/ruby-saml/pull/160) Extended solution for Attributes method [] can raise NoMethodError
44
+ * [#158](https://github.com/onelogin/ruby-saml/pull/1) Added ability to specify attribute services in metadata
45
+ * [#154](https://github.com/onelogin/ruby-saml/pull/154) Fix incorrect gem declaration statement
46
+ * [#152](https://github.com/onelogin/ruby-saml/pull/152) Fix the PR #99
47
+ * [#150](https://github.com/onelogin/ruby-saml/pull/150) Nokogiri already in gemspec
48
+ * [#147](https://github.com/onelogin/ruby-saml/pull/147) Fix LogoutResponse issuer validation and implement SAML Response issuer validation.
49
+ * [#144](https://github.com/onelogin/ruby-saml/pull/144) Fix DigestMethod lookup bug
50
+ * [#139](https://github.com/onelogin/ruby-saml/pull/139) Fixes handling of some soft and hard validation failures
51
+ * [#138](https://github.com/onelogin/ruby-saml/pull/138) Change logoutrequest.rb to UTC time
52
+ * [#136](https://github.com/onelogin/ruby-saml/pull/136) Remote idp metadata
53
+ * [#135](https://github.com/onelogin/ruby-saml/pull/135) Restored support for NIL as well as empty AttributeValues
54
+ * [#134](https://github.com/onelogin/ruby-saml/pull/134) explicitly require "onelogin/ruby-saml/logging"
55
+ * [#133](https://github.com/onelogin/ruby-saml/pull/133) Added license to gemspec
56
+ * [#132](https://github.com/onelogin/ruby-saml/pull/132) Support AttributeConsumingServiceIndex in AuthnRequest
57
+ * [#131](https://github.com/onelogin/ruby-saml/pull/131) Add ruby 2.1.1 to .travis.yml
58
+ * [#122](https://github.com/onelogin/ruby-saml/pull/122) Fixes #112 and #117 in a backwards compatible manner
59
+ * [#119](https://github.com/onelogin/ruby-saml/pull/119) Add support for extracting IdP details from metadata xml
60
+
61
+ ### 0.8.2 (Jan 26, 2015)
62
+ * [#183](https://github.com/onelogin/ruby-saml/pull/183) Resolved a security vulnerability where string interpolation in a `REXML::XPath.first()` method call allowed for arbitrary code execution.
63
+
64
+ ### 0.8.0 (Feb 21, 2014)
65
+ **IMPORTANT**: This release changed namespace of the gem from `OneLogin::Saml` to `OneLogin::RubySaml`. Please update your implementations of the gem accordingly.
66
+
67
+ * [#111](https://github.com/onelogin/ruby-saml/pull/111) `Onelogin::` is `OneLogin::`
68
+ * [#108](https://github.com/onelogin/ruby-saml/pull/108) Change namespacing from `Onelogin::Saml` to `Onelogin::Rubysaml`
69
+
70
+
71
+ ### 0.7.3 (Feb 20, 2014)
72
+ Updated gem dependencies to be compatible with Ruby 1.8.7-p374 and 1.9.3-p448. Removed unnecessary `canonix` gem dependency.
73
+
74
+ * [#107](https://github.com/onelogin/ruby-saml/pull/107) Relax nokogiri version requirement to >= 1.5.0
75
+ * [#105](https://github.com/onelogin/ruby-saml/pull/105) Lock Gem versions, fix to resolve possible namespace collision
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "nokogiri", "~> 1.5.10"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,17 @@
1
+ require 'onelogin/ruby-saml/logging'
2
+ require 'onelogin/ruby-saml/saml_message'
3
+ require 'onelogin/ruby-saml/authrequest'
4
+ require 'onelogin/ruby-saml/logoutrequest'
5
+ require 'onelogin/ruby-saml/logoutresponse'
6
+ require 'onelogin/ruby-saml/attributes'
7
+ require 'onelogin/ruby-saml/slo_logoutrequest'
8
+ require 'onelogin/ruby-saml/slo_logoutresponse'
9
+ require 'onelogin/ruby-saml/response'
10
+ require 'onelogin/ruby-saml/settings'
11
+ require 'onelogin/ruby-saml/attribute_service'
12
+ require 'onelogin/ruby-saml/http_error'
13
+ require 'onelogin/ruby-saml/validation_error'
14
+ require 'onelogin/ruby-saml/metadata'
15
+ require 'onelogin/ruby-saml/idp_metadata_parser'
16
+ require 'onelogin/ruby-saml/utils'
17
+ require 'onelogin/ruby-saml/version'
@@ -0,0 +1,57 @@
1
+ module OneLogin
2
+ module RubySaml
3
+
4
+ # SAML2 AttributeService. Auxiliary class to build the AttributeService of the SP Metadata
5
+ #
6
+ class AttributeService
7
+ attr_reader :attributes
8
+ attr_reader :name
9
+ attr_reader :index
10
+
11
+ # Initializes the AttributeService, set the index value as 1 and an empty array as attributes
12
+ #
13
+ def initialize
14
+ @index = "1"
15
+ @attributes = []
16
+ end
17
+
18
+ def configure(&block)
19
+ instance_eval &block
20
+ end
21
+
22
+ # @return [Boolean] True if the AttributeService object has been initialized and set with the required values
23
+ # (has attributes and a name)
24
+ def configured?
25
+ @attributes.length > 0 && !@name.nil?
26
+ end
27
+
28
+ # Set a name to the service
29
+ # @param name [String] The service name
30
+ #
31
+ def service_name(name)
32
+ @name = name
33
+ end
34
+
35
+ # Set an index to the service
36
+ # @param index [Integer] An index
37
+ #
38
+ def service_index(index)
39
+ @index = index
40
+ end
41
+
42
+ # Add an AttributeService
43
+ # @param options [Hash] AttributeService option values
44
+ # add_attribute(
45
+ # :name => "Name",
46
+ # :name_format => "Name Format",
47
+ # :index => 1,
48
+ # :friendly_name => "Friendly Name",
49
+ # :attribute_value => "Attribute Value"
50
+ # )
51
+ #
52
+ def add_attribute(options={})
53
+ attributes << options
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,128 @@
1
+ module OneLogin
2
+ module RubySaml
3
+
4
+ # SAML2 Attributes. Parse the Attributes from the AttributeStatement of the SAML Response.
5
+ #
6
+ class Attributes
7
+ include Enumerable
8
+
9
+ attr_reader :attributes
10
+
11
+ # By default Attributes#[] is backwards compatible and
12
+ # returns only the first value for the attribute
13
+ # Setting this to `false` returns all values for an attribute
14
+ @@single_value_compatibility = true
15
+
16
+ # @return [Boolean] Get current status of backwards compatibility mode.
17
+ #
18
+ def self.single_value_compatibility
19
+ @@single_value_compatibility
20
+ end
21
+
22
+ # Sets the backwards compatibility mode on/off.
23
+ # @param value [Boolean]
24
+ #
25
+ def self.single_value_compatibility=(value)
26
+ @@single_value_compatibility = value
27
+ end
28
+
29
+ # @param attrs [Hash] The +attrs+ must be a Hash with attribute names as keys and **arrays** as values:
30
+ # Attributes.new({
31
+ # 'name' => ['value1', 'value2'],
32
+ # 'mail' => ['value1'],
33
+ # })
34
+ #
35
+ def initialize(attrs = {})
36
+ @attributes = attrs
37
+ end
38
+
39
+
40
+ # Iterate over all attributes
41
+ #
42
+ def each
43
+ attributes.each{|name, values| yield name, values}
44
+ end
45
+
46
+
47
+ # Test attribute presence by name
48
+ # @param name [String] The attribute name to be checked
49
+ #
50
+ def include?(name)
51
+ attributes.has_key?(canonize_name(name))
52
+ end
53
+
54
+ # Return first value for an attribute
55
+ # @param name [String] The attribute name
56
+ # @return [String] The value (First occurrence)
57
+ #
58
+ def single(name)
59
+ attributes[canonize_name(name)].first if include?(name)
60
+ end
61
+
62
+ # Return all values for an attribute
63
+ # @param name [String] The attribute name
64
+ # @return [Array] Values of the attribute
65
+ #
66
+ def multi(name)
67
+ attributes[canonize_name(name)]
68
+ end
69
+
70
+ # Retrieve attribute value(s)
71
+ # @param name [String] The attribute name
72
+ # @return [String|Array] Depending on the single value compatibility status this returns:
73
+ # - First value if single_value_compatibility = true
74
+ # response.attributes['mail'] # => 'user@example.com'
75
+ # - All values if single_value_compatibility = false
76
+ # response.attributes['mail'] # => ['user@example.com','user@example.net']
77
+ #
78
+ def [](name)
79
+ self.class.single_value_compatibility ? single(canonize_name(name)) : multi(canonize_name(name))
80
+ end
81
+
82
+ # @return [Array] Return all attributes as an array
83
+ #
84
+ def all
85
+ attributes
86
+ end
87
+
88
+ # @param name [String] The attribute name
89
+ # @param values [Array] The values
90
+ #
91
+ def set(name, values)
92
+ attributes[canonize_name(name)] = values
93
+ end
94
+ alias_method :[]=, :set
95
+
96
+ # @param name [String] The attribute name
97
+ # @param values [Array] The values
98
+ #
99
+ def add(name, values = [])
100
+ attributes[canonize_name(name)] ||= []
101
+ attributes[canonize_name(name)] += Array(values)
102
+ end
103
+
104
+ # Make comparable to another Attributes collection based on attributes
105
+ # @param other [Attributes] An Attributes object to compare with
106
+ # @return [Boolean] True if are contains the same attributes and values
107
+ #
108
+ def ==(other)
109
+ if other.is_a?(Attributes)
110
+ all == other.all
111
+ else
112
+ super
113
+ end
114
+ end
115
+
116
+ protected
117
+
118
+ # stringifies all names so both 'email' and :email return the same result
119
+ # @param name [String] The attribute name
120
+ # @return [String] stringified name
121
+ #
122
+ def canonize_name(name)
123
+ name.to_s
124
+ end
125
+
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,156 @@
1
+ require "uuid"
2
+ require "rexml/document"
3
+
4
+ require "onelogin/ruby-saml/logging"
5
+ require "onelogin/ruby-saml/saml_message"
6
+
7
+ # Only supports SAML 2.0
8
+ module OneLogin
9
+ module RubySaml
10
+ include REXML
11
+
12
+ # SAML2 Authentication. AuthNRequest (SSO SP initiated, Builder)
13
+ #
14
+ class Authrequest < SamlMessage
15
+
16
+ # AuthNRequest ID
17
+ attr_reader :uuid
18
+
19
+ # Initializes the AuthNRequest. An Authrequest Object that is an extension of the SamlMessage class.
20
+ # Asigns an ID, a random uuid.
21
+ #
22
+ def initialize
23
+ @uuid = "_" + UUID.new.generate
24
+ end
25
+
26
+ # Creates the AuthNRequest string.
27
+ # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
28
+ # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
29
+ # @return [String] AuthNRequest string that includes the SAMLRequest
30
+ #
31
+ def create(settings, params = {})
32
+ params = create_params(settings, params)
33
+ params_prefix = (settings.idp_sso_target_url =~ /\?/) ? '&' : '?'
34
+ saml_request = CGI.escape(params.delete("SAMLRequest"))
35
+ request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
36
+ params.each_pair do |key, value|
37
+ request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
38
+ end
39
+ @login_url = settings.idp_sso_target_url + request_params
40
+ end
41
+
42
+ # Creates the Get parameters for the request.
43
+ # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
44
+ # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
45
+ # @return [Hash] Parameters
46
+ #
47
+ def create_params(settings, params={})
48
+ # The method expects :RelayState but sometimes we get 'RelayState' instead.
49
+ # Based on the HashWithIndifferentAccess value in Rails we could experience
50
+ # conflicts so this line will solve them.
51
+ relay_state = params[:RelayState] || params['RelayState']
52
+
53
+ request_doc = create_authentication_xml_doc(settings)
54
+ request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
55
+
56
+ request = ""
57
+ request_doc.write(request)
58
+
59
+ Logging.debug "Created AuthnRequest: #{request}"
60
+
61
+ request = deflate(request) if settings.compress_request
62
+ base64_request = encode(request)
63
+ request_params = {"SAMLRequest" => base64_request}
64
+
65
+ if settings.security[:authn_requests_signed] && !settings.security[:embed_sign] && settings.private_key
66
+ params['SigAlg'] = settings.security[:signature_method]
67
+ url_string = OneLogin::RubySaml::Utils.build_query(
68
+ :type => 'SAMLRequest',
69
+ :data => base64_request,
70
+ :relay_state => relay_state,
71
+ :sig_alg => params['SigAlg']
72
+ )
73
+ sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
74
+ signature = settings.get_sp_key.sign(sign_algorithm.new, url_string)
75
+ params['Signature'] = encode(signature)
76
+ end
77
+
78
+ params.each_pair do |key, value|
79
+ request_params[key] = value.to_s
80
+ end
81
+
82
+ request_params
83
+ end
84
+
85
+ # Creates the SAMLRequest String.
86
+ # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
87
+ # @return [String] The SAMLRequest String.
88
+ #
89
+ def create_authentication_xml_doc(settings)
90
+ time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
91
+
92
+ request_doc = XMLSecurity::Document.new
93
+ request_doc.uuid = uuid
94
+
95
+ root = request_doc.add_element "samlp:AuthnRequest", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol", "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
96
+ root.attributes['ID'] = uuid
97
+ root.attributes['IssueInstant'] = time
98
+ root.attributes['Version'] = "2.0"
99
+ root.attributes['Destination'] = settings.idp_sso_target_url unless settings.idp_sso_target_url.nil?
100
+ root.attributes['IsPassive'] = settings.passive unless settings.passive.nil?
101
+ root.attributes['ProtocolBinding'] = settings.protocol_binding unless settings.protocol_binding.nil?
102
+ root.attributes["AttributeConsumingServiceIndex"] = settings.attributes_index unless settings.attributes_index.nil?
103
+ root.attributes['ForceAuthn'] = settings.force_authn unless settings.force_authn.nil?
104
+
105
+ # Conditionally defined elements based on settings
106
+ if settings.assertion_consumer_service_url != nil
107
+ root.attributes["AssertionConsumerServiceURL"] = settings.assertion_consumer_service_url
108
+ end
109
+ if settings.issuer != nil
110
+ issuer = root.add_element "saml:Issuer"
111
+ issuer.text = settings.issuer
112
+ end
113
+ if settings.name_identifier_format != nil
114
+ root.add_element "samlp:NameIDPolicy", {
115
+ # Might want to make AllowCreate a setting?
116
+ "AllowCreate" => "true",
117
+ "Format" => settings.name_identifier_format
118
+ }
119
+ end
120
+
121
+ if settings.authn_context || settings.authn_context_decl_ref
122
+
123
+ if settings.authn_context_comparison != nil
124
+ comparison = settings.authn_context_comparison
125
+ else
126
+ comparison = 'exact'
127
+ end
128
+
129
+ requested_context = root.add_element "samlp:RequestedAuthnContext", {
130
+ "Comparison" => comparison,
131
+ }
132
+
133
+ if settings.authn_context != nil
134
+ class_ref = requested_context.add_element "saml:AuthnContextClassRef"
135
+ class_ref.text = settings.authn_context
136
+ end
137
+ # add saml:AuthnContextDeclRef element
138
+ if settings.authn_context_decl_ref != nil
139
+ class_ref = requested_context.add_element "saml:AuthnContextDeclRef"
140
+ class_ref.text = settings.authn_context_decl_ref
141
+ end
142
+ end
143
+
144
+ # embed signature
145
+ if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
146
+ private_key = settings.get_sp_key
147
+ cert = settings.get_sp_cert
148
+ request_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
149
+ end
150
+
151
+ request_doc
152
+ end
153
+
154
+ end
155
+ end
156
+ end