r-saml 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +14 -0
- data/.travis.yml +23 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +584 -0
- data/Rakefile +27 -0
- data/changelog.md +75 -0
- data/gemfiles/nokogiri-1.5.gemfile +5 -0
- data/lib/onelogin/ruby-saml.rb +17 -0
- data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
- data/lib/onelogin/ruby-saml/attributes.rb +128 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +165 -0
- data/lib/onelogin/ruby-saml/http_error.rb +7 -0
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
- data/lib/onelogin/ruby-saml/logging.rb +30 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
- data/lib/onelogin/ruby-saml/metadata.rb +123 -0
- data/lib/onelogin/ruby-saml/response.rb +735 -0
- data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
- data/lib/onelogin/ruby-saml/settings.rb +165 -0
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
- data/lib/onelogin/ruby-saml/utils.rb +172 -0
- data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
- data/lib/onelogin/ruby-saml/version.rb +5 -0
- data/lib/ruby-saml.rb +1 -0
- data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
- data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/xenc-schema.xsd +136 -0
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/xmldsig-core-schema.xsd +309 -0
- data/lib/xml_security.rb +368 -0
- data/r-saml.gemspec +64 -0
- data/test/certificates/certificate1 +12 -0
- data/test/certificates/certificate_without_head_foot +1 -0
- data/test/certificates/formatted_certificate +14 -0
- data/test/certificates/formatted_private_key +12 -0
- data/test/certificates/formatted_rsa_private_key +12 -0
- data/test/certificates/invalid_certificate1 +1 -0
- data/test/certificates/invalid_certificate2 +1 -0
- data/test/certificates/invalid_certificate3 +12 -0
- data/test/certificates/invalid_private_key1 +1 -0
- data/test/certificates/invalid_private_key2 +1 -0
- data/test/certificates/invalid_private_key3 +10 -0
- data/test/certificates/invalid_rsa_private_key1 +1 -0
- data/test/certificates/invalid_rsa_private_key2 +1 -0
- data/test/certificates/invalid_rsa_private_key3 +10 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/idp_metadata_parser_test.rb +95 -0
- data/test/logging_test.rb +62 -0
- data/test/logout_requests/invalid_slo_request.xml +6 -0
- data/test/logout_requests/slo_request.xml +4 -0
- data/test/logout_requests/slo_request.xml.base64 +1 -0
- data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
- data/test/logout_requests/slo_request_with_session_index.xml +5 -0
- data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
- data/test/logoutrequest_test.rb +211 -0
- data/test/logoutresponse_test.rb +258 -0
- data/test/metadata_test.rb +203 -0
- data/test/request_test.rb +282 -0
- data/test/response_test.rb +1159 -0
- data/test/responses/adfs_response_sha1.xml +46 -0
- data/test/responses/adfs_response_sha256.xml +46 -0
- data/test/responses/adfs_response_sha384.xml +46 -0
- data/test/responses/adfs_response_sha512.xml +46 -0
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/attackxee.xml +13 -0
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
- data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
- data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_id.xml.base64 +1 -0
- data/test/responses/invalids/no_saml2.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/no_status.xml.base64 +1 -0
- data/test/responses/invalids/no_status_code.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
- data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
- data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
- data/test/responses/no_signature_ns.xml +48 -0
- data/test/responses/open_saml_response.xml +56 -0
- data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
- data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
- data/test/responses/response_eval.xml +7 -0
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_unsigned_xml_base64 +1 -0
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -0
- data/test/responses/response_with_multiple_attribute_values.xml +67 -0
- data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
- data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
- data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_without_attributes.xml.base64 +79 -0
- data/test/responses/response_without_reference_uri.xml.base64 +1 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/signed_nameid_in_atts.xml +47 -0
- data/test/responses/signed_unqual_nameid_in_atts.xml +47 -0
- data/test/responses/simple_saml_php.xml +71 -0
- data/test/responses/starfield_response.xml.base64 +1 -0
- data/test/responses/test_sign.xml +43 -0
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/saml_message_test.rb +56 -0
- data/test/settings_test.rb +218 -0
- data/test/slo_logoutrequest_test.rb +275 -0
- data/test/slo_logoutresponse_test.rb +185 -0
- data/test/test_helper.rb +257 -0
- data/test/utils_test.rb +145 -0
- data/test/xml_security_test.rb +328 -0
- metadata +421 -0
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
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
|
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,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,165 @@
|
|
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
|
+
document = create_xml_document(settings)
|
91
|
+
sign_document(document, settings)
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_xml_document(settings)
|
95
|
+
time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
96
|
+
|
97
|
+
request_doc = XMLSecurity::Document.new
|
98
|
+
request_doc.uuid = uuid
|
99
|
+
|
100
|
+
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" }
|
101
|
+
root.attributes['ID'] = uuid
|
102
|
+
root.attributes['IssueInstant'] = time
|
103
|
+
root.attributes['Version'] = "2.0"
|
104
|
+
root.attributes['Destination'] = settings.idp_sso_target_url unless settings.idp_sso_target_url.nil?
|
105
|
+
root.attributes['IsPassive'] = settings.passive unless settings.passive.nil?
|
106
|
+
root.attributes['ProtocolBinding'] = settings.protocol_binding unless settings.protocol_binding.nil?
|
107
|
+
root.attributes["AttributeConsumingServiceIndex"] = settings.attributes_index unless settings.attributes_index.nil?
|
108
|
+
root.attributes['ForceAuthn'] = settings.force_authn unless settings.force_authn.nil?
|
109
|
+
|
110
|
+
# Conditionally defined elements based on settings
|
111
|
+
if settings.assertion_consumer_service_url != nil
|
112
|
+
root.attributes["AssertionConsumerServiceURL"] = settings.assertion_consumer_service_url
|
113
|
+
end
|
114
|
+
if settings.issuer != nil
|
115
|
+
issuer = root.add_element "saml:Issuer"
|
116
|
+
issuer.text = settings.issuer
|
117
|
+
end
|
118
|
+
if settings.name_identifier_format != nil
|
119
|
+
root.add_element "samlp:NameIDPolicy", {
|
120
|
+
# Might want to make AllowCreate a setting?
|
121
|
+
"AllowCreate" => "true",
|
122
|
+
"Format" => settings.name_identifier_format
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
if settings.authn_context || settings.authn_context_decl_ref
|
127
|
+
|
128
|
+
if settings.authn_context_comparison != nil
|
129
|
+
comparison = settings.authn_context_comparison
|
130
|
+
else
|
131
|
+
comparison = 'exact'
|
132
|
+
end
|
133
|
+
|
134
|
+
requested_context = root.add_element "samlp:RequestedAuthnContext", {
|
135
|
+
"Comparison" => comparison,
|
136
|
+
}
|
137
|
+
|
138
|
+
if settings.authn_context != nil
|
139
|
+
class_ref = requested_context.add_element "saml:AuthnContextClassRef"
|
140
|
+
class_ref.text = settings.authn_context
|
141
|
+
end
|
142
|
+
# add saml:AuthnContextDeclRef element
|
143
|
+
if settings.authn_context_decl_ref != nil
|
144
|
+
class_ref = requested_context.add_element "saml:AuthnContextDeclRef"
|
145
|
+
class_ref.text = settings.authn_context_decl_ref
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
request_doc
|
150
|
+
end
|
151
|
+
|
152
|
+
def sign_document(document, settings)
|
153
|
+
# embed signature
|
154
|
+
if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
|
155
|
+
private_key = settings.get_sp_key
|
156
|
+
cert = settings.get_sp_cert
|
157
|
+
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
158
|
+
end
|
159
|
+
|
160
|
+
document
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|