ruby-samlnechotech 0.7.34 → 0.7.35

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 83acf516a41f0c28c18108b8f5b5ffbcc4698e1a
4
- data.tar.gz: f83184add5a75fcf488b2ff879a5816e0298a2e6
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ M2Y3NTY1OWQyMmI4ZmY4YWRjNmI1ZTllNTQ0MzQ0MGYwNDZiZTEzZQ==
5
+ data.tar.gz: !binary |-
6
+ ODExYjkyMTg0YWM4NDcyOTRkYjQzZjZiNmNhNTg4YTdhZWExM2MzZQ==
5
7
  SHA512:
6
- metadata.gz: 6348a6a9abeb1d41a1851351c473aaa0c9643ace578a811ddd3c3090f960da79b37eba635f5a30bf0ea2b8924e65d6f79d2415a96e164569886922522d73e60f
7
- data.tar.gz: 45eb2a6a53c8c03977e26038e995a64c544cff803e9a6c47664057889ce383211515ee334af85db7d0ab3bf5bbbb07abfcdeb850daba145af68bb3159dfeb289
8
+ metadata.gz: !binary |-
9
+ OWEwMTY0OGY5YmI4ZGM5N2ZlZjRkZDRhZmNkZGY5MWIzZjY2ZTZmNzE2YjJj
10
+ MTE2OTJiOWU2OGFkZmNlYjg5MzJiZmJmNTg4NDgyM2Q1MjhlZWJmMTY3MjJj
11
+ N2NiZjQ1NWM2NjY3MWQ0MDJhNTJlNzZjZDBmMzA2NzVkYTFhNmU=
12
+ data.tar.gz: !binary |-
13
+ ZGI0OWZmMTU3NWUwNWRlMzI1NmExNTMzOTlhYmI4OWFkNGY0NzdlOGNhNmI4
14
+ ZTRlYjQ3ZTA3MzY2ZjU2N2VmNGZkOTc5NThjMmI3NThhYmY5NjRlY2NjYzE5
15
+ M2EyNTVjMmIzMjNjMzI3MzgzMWY5M2U1ZTEzOWRmMmI0MWYxNGI=
@@ -0,0 +1,75 @@
1
+ require 'cgi'
2
+ require 'zlib'
3
+ require 'base64'
4
+
5
+ module Onelogin
6
+ module Saml
7
+ class SamlMessage
8
+
9
+ ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
10
+ PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
11
+
12
+ def valid_saml?(document, soft = true)
13
+ Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
14
+ @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
15
+ @xml = Nokogiri::XML(document.to_s)
16
+ end
17
+ if soft
18
+ @schema.validate(@xml).map{ return false }
19
+ else
20
+ @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
21
+ end
22
+ end
23
+
24
+ def validation_error(message)
25
+ raise ValidationError.new(message)
26
+ end
27
+
28
+ private
29
+
30
+ def decode_raw_saml(saml)
31
+ if saml =~ /^</
32
+ return saml
33
+ elsif (decoded = decode(saml)) =~ /^</
34
+ return decoded
35
+ elsif (inflated = inflate(decoded)) =~ /^</
36
+ return inflated
37
+ end
38
+
39
+ return nil
40
+ end
41
+
42
+ def encode_raw_saml(saml, settings)
43
+ saml = Zlib::Deflate.deflate(saml, 9)[2..-5] if settings.compress_request
44
+ base64_saml = Base64.encode64(saml)
45
+ return CGI.escape(base64_saml)
46
+ end
47
+
48
+ def decode(encoded)
49
+ Base64.decode64(encoded)
50
+ end
51
+
52
+ def encode(encoded)
53
+ Base64.encode64(encoded).gsub(/\n/, "")
54
+ end
55
+
56
+ def escape(unescaped)
57
+ CGI.escape(unescaped)
58
+ end
59
+
60
+ def unescape(escaped)
61
+ CGI.unescape(escaped)
62
+ end
63
+
64
+ def inflate(deflated)
65
+ zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
66
+ zlib.inflate(deflated)
67
+ end
68
+
69
+ def deflate(inflated)
70
+ Zlib::Deflate.deflate(inflated, 9)[2..-5]
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,66 @@
1
+ require 'zlib'
2
+ require 'time'
3
+ require 'nokogiri'
4
+
5
+ # Only supports SAML 2.0
6
+ module Onelogin
7
+ module Saml
8
+ class SloLogoutrequest < SamlMessage
9
+ attr_reader :options
10
+ attr_reader :request
11
+ attr_reader :document
12
+
13
+ def initialize(request, options = {})
14
+ raise ArgumentError.new("Request cannot be nil") if request.nil?
15
+ @options = options
16
+ @request = decode_raw_saml(request)
17
+ @document = REXML::Document.new(@request)
18
+ end
19
+
20
+ def is_valid?
21
+ validate
22
+ end
23
+
24
+ def validate!
25
+ validate(false)
26
+ end
27
+
28
+ # The value of the user identifier as designated by the initialization request response
29
+ def name_id
30
+ @name_id ||= begin
31
+ node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
32
+ node.nil? ? nil : node.text
33
+ end
34
+ end
35
+
36
+ def id
37
+ return @id if @id
38
+ element = REXML::XPath.first(document, "/p:LogoutRequest", {
39
+ "p" => PROTOCOL} )
40
+ return nil if element.nil?
41
+ return element.attributes["ID"]
42
+ end
43
+
44
+ def issuer
45
+ @issuer ||= begin
46
+ node = REXML::XPath.first(document, "/p:LogoutRequest/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
47
+ node.nil? ? nil : node.text
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def validate(soft = true)
54
+ valid_saml?(document, soft) && validate_request_state(soft)
55
+ end
56
+
57
+ def validate_request_state(soft = true)
58
+ if request.empty?
59
+ return soft ? false : validation_error("Blank request")
60
+ end
61
+ true
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,62 @@
1
+ module Onelogin
2
+ module Saml
3
+ class SloLogoutresponse < SamlMessage
4
+
5
+ def create(settings, request, logout_message = nil, params = {})
6
+ params = {} if params.nil?
7
+
8
+ response_doc = create_logout_response_xml_doc(settings, request, logout_message)
9
+ response_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
10
+
11
+ response = ''
12
+ response_doc.write(response)
13
+
14
+ Logging.debug "Created SLO Logout Response: #{response}"
15
+
16
+ encoded_response = encode_raw_saml(response, settings)
17
+ params_prefix = (settings.idp_slo_target_url =~ /\?/) ? '&' : '?'
18
+ response_params = "#{params_prefix}SAMLResponse=#{encoded_response}"
19
+
20
+ params.each_pair do |key, value|
21
+ response_params << "&#{key.to_s}=#{escape(value.to_s)}"
22
+ end
23
+
24
+ settings.idp_slo_target_url + response_params
25
+ end
26
+
27
+ def create_logout_response_xml_doc(settings, request, logout_message = nil)
28
+ uuid = '_' + UUID.new.generate
29
+ time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
30
+
31
+ response_doc = REXML::Document.new
32
+
33
+ root = response_doc.add_element 'samlp:LogoutResponse', { 'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol' }
34
+ root.attributes['ID'] = uuid
35
+ root.attributes['IssueInstant'] = time
36
+ root.attributes['Version'] = '2.0'
37
+ root.attributes['InResponseTo'] = request.id unless request.id.nil?
38
+ root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
39
+
40
+ # add success message
41
+ status = root.add_element 'samlp:Status'
42
+
43
+ # success status code
44
+ status_code = status.add_element 'samlp:StatusCode'
45
+ status_code.attributes['Value'] = 'urn:oasis:names:tc:SAML:2.0:status:Success'
46
+
47
+ # success status message
48
+ logout_message ||= 'Successfully Signed Out'
49
+ status_message = status.add_element 'samlp:StatusMessage'
50
+ status_message.text = logout_message
51
+
52
+ if settings.issuer != nil
53
+ issuer = root.add_element "saml:Issuer", { "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
54
+ issuer.text = settings.issuer
55
+ end
56
+
57
+ response_doc
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -1,5 +1,5 @@
1
1
  module Onelogin
2
2
  module Saml
3
- VERSION = '0.7.34'
3
+ VERSION = '0.7.35'
4
4
  end
5
5
  end
@@ -7,3 +7,6 @@ require 'onelogin/ruby-samlnechotech/settings'
7
7
  require 'onelogin/ruby-samlnechotech/validation_error'
8
8
  require 'onelogin/ruby-samlnechotech/metadata'
9
9
  require 'onelogin/ruby-samlnechotech/version'
10
+ require 'onelogin/ruby-samlnechotech/saml_message'
11
+ require 'onelogin/ruby-samlnechotech/slo_logoutrequest'
12
+ require 'onelogin/ruby-samlnechotech/slo_logoutresponse'
@@ -0,0 +1,82 @@
1
+ #encoding: utf-8
2
+
3
+ def default_request_opts
4
+ {
5
+ :uuid => "_28024690-000e-0130-b6d2-38f6b112be8b",
6
+ :issue_instant => Time.now.strftime('%Y-%m-%dT%H:%M:%SZ'),
7
+ :settings => settings
8
+ }
9
+ end
10
+
11
+ def valid_request(opts = {})
12
+ opts = default_request_opts.merge!(opts)
13
+
14
+ "<samlp:LogoutRequest
15
+ xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
16
+ ID=\"#{random_id}\" Version=\"2.0\"
17
+ IssueInstant=\"#{opts[:issue_instant]}\"
18
+ Destination=\"#{opts[:settings].assertion_consumer_logout_service_url}\">
19
+ <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{opts[:settings].issuer}</saml:Issuer>
20
+ </samlp:LogoutRequest>"
21
+ "<samlp:LogoutRequest ID=\"f8a62847-92f2-4f0c-936a-df9efe0cc42f\"
22
+ Version=\"2.0\"
23
+ IssueInstant=\"2013-08-29T20:53:50Z\"
24
+ Destination=\"https://server/adfs/ls/\"
25
+ Consent=\"urn:oasis:names:tc:SAML:2.0:consent:unspecified\"
26
+ xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
27
+ >
28
+ <saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">https://sp.com/</saml:Issuer>
29
+ <Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">
30
+ <SignedInfo>
31
+ <CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\" />
32
+ <SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\" />
33
+ <Reference URI=\"#f8a62847-92f2-4f0c-936a-df9efe0cc42f\">
34
+ <Transforms>
35
+ <Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\" />
36
+ <Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\" />
37
+ </Transforms>
38
+ <DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" />
39
+ <DigestValue>W7F1E2U1OAHRXn/ItbnsYZyXw/8=</DigestValue>
40
+ </Reference>
41
+ </SignedInfo>
42
+ <SignatureValue></SignatureValue>
43
+ <KeyInfo>
44
+ <X509Data>
45
+ <X509Certificate></X509Certificate>
46
+ </X509Data>
47
+ </KeyInfo>
48
+ </Signature>
49
+ <saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\"
50
+ Format=\"http://schemas.xmlsoap.org/claims/UPN\"
51
+ >user</saml:NameID>
52
+ <samlp:SessionIndex>_2537f94b-a150-415e-9a45-3c6fa2b6dd60</samlp:SessionIndex>
53
+ </samlp:LogoutRequest>"
54
+ end
55
+
56
+ def invalid_xml_request
57
+ "<samlp:SomethingAwful
58
+ xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"
59
+ ID=\"#{random_id}\" Version=\"2.0\">
60
+ </samlp:SomethingAwful>"
61
+ end
62
+
63
+ def settings
64
+ @settings ||= Onelogin::Saml::Settings.new(
65
+ {
66
+ :assertion_consumer_service_url => "http://app.muda.no/sso/consume",
67
+ :assertion_consumer_logout_service_url => "http://app.muda.no/sso/consume_logout",
68
+ :issuer => "http://app.muda.no",
69
+ :sp_name_qualifier => "http://sso.muda.no",
70
+ :idp_sso_target_url => "http://sso.muda.no/sso",
71
+ :idp_slo_target_url => "http://sso.muda.no/slo",
72
+ :idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
73
+ :name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
74
+ }
75
+ )
76
+ end
77
+
78
+ # logoutresponse fixtures
79
+ def random_id
80
+ "_#{UUID.new.generate}"
81
+ end
82
+
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+ require 'rexml/document'
3
+ require 'requests/logoutrequest_fixtures'
4
+ class RubySamlTest < Test::Unit::TestCase
5
+
6
+ context "SloLogoutrequest" do
7
+ context "#new" do
8
+ should "raise an exception when request is initialized with nil" do
9
+ assert_raises(ArgumentError) { Onelogin::Saml::SloLogoutrequest.new(nil) }
10
+ end
11
+ should "accept constructor-injected options" do
12
+ logoutrequest= Onelogin::Saml::SloLogoutrequest.new(valid_request, { :foo => :bar} )
13
+ assert !logoutrequest.options.empty?
14
+ end
15
+ should "support base64 encoded responses" do
16
+ expected_request = valid_request
17
+ logoutrequest= Onelogin::Saml::SloLogoutrequest.new(Base64.encode64(expected_request))
18
+
19
+ assert_equal expected_request, logoutrequest.request
20
+ end
21
+ end
22
+
23
+ context "#validate!" do
24
+ should "validates good requests" do
25
+ logoutrequest = Onelogin::Saml::SloLogoutrequest.new(valid_request)
26
+ logoutrequest.validate!
27
+ end
28
+
29
+ should "raise error for invalid xml" do
30
+ logoutrequest = Onelogin::Saml::SloLogoutrequest.new(invalid_xml_request)
31
+ assert_raises(Onelogin::Saml::ValidationError) { logoutrequest.validate! }
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ # logoutresponse fixtures
38
+ def random_id
39
+ "_#{UUID.new.generate}"
40
+ end
41
+
42
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+ require 'requests/logoutrequest_fixtures'
3
+
4
+ class SloResponseTest < Test::Unit::TestCase
5
+
6
+ context "SloLogoutresponse" do
7
+ settings = Onelogin::Saml::Settings.new
8
+ settings.idp_slo_target_url = "http://unauth.com/logout"
9
+ settings.name_identifier_value = "f00f00"
10
+ logoutrequest = Onelogin::Saml::SloLogoutrequest.new(valid_request)
11
+
12
+ should "create the deflated SAMLRequest URL parameter" do
13
+
14
+ unauth_url = Onelogin::Saml::SloLogoutresponse.new.create(settings, logoutrequest)
15
+ assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLResponse=/
16
+
17
+ inflated = decode_saml_response_payload(unauth_url)
18
+
19
+ assert_match /^<samlp:LogoutResponse/, inflated
20
+ end
21
+
22
+ should "set InResponseTo" do
23
+
24
+ unauth_url = Onelogin::Saml::SloLogoutresponse.new.create(settings, logoutrequest)
25
+ inflated = decode_saml_response_payload(unauth_url)
26
+
27
+ assert_match %r(InResponseTo='#{logoutrequest.id}'), inflated
28
+
29
+ end
30
+
31
+ end
32
+
33
+ def decode_saml_response_payload(unauth_url)
34
+ payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
35
+ decoded = Base64.decode64(payload)
36
+
37
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
38
+ inflated = zstream.inflate(decoded)
39
+ zstream.finish
40
+ zstream.close
41
+ inflated
42
+ end
43
+
44
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-samlnechotech
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.34
4
+ version: 0.7.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - OneLogin LLC, beekermememe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-24 00:00:00.000000000 Z
11
+ date: 2014-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: canonix
@@ -42,14 +42,14 @@ dependencies:
42
42
  name: nokogiri
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: 1.5.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.5.0
55
55
  description: SAML toolkit for Ruby on Rails forked and modified by beekermememe
@@ -73,7 +73,10 @@ files:
73
73
  - lib/onelogin/ruby-samlnechotech/logoutresponse.rb
74
74
  - lib/onelogin/ruby-samlnechotech/metadata.rb
75
75
  - lib/onelogin/ruby-samlnechotech/response.rb
76
+ - lib/onelogin/ruby-samlnechotech/saml_message.rb
76
77
  - lib/onelogin/ruby-samlnechotech/settings.rb
78
+ - lib/onelogin/ruby-samlnechotech/slo_logoutrequest.rb
79
+ - lib/onelogin/ruby-samlnechotech/slo_logoutresponse.rb
77
80
  - lib/onelogin/ruby-samlnechotech/validation_error.rb
78
81
  - lib/onelogin/ruby-samlnechotech/version.rb
79
82
  - lib/ruby-samlnechotech.rb
@@ -88,6 +91,7 @@ files:
88
91
  - test/logoutrequest_test.rb
89
92
  - test/logoutresponse_test.rb
90
93
  - test/request_test.rb
94
+ - test/requests/logoutrequest_fixtures.rb
91
95
  - test/response_test.rb
92
96
  - test/responses/adfs_response_sha1.xml
93
97
  - test/responses/adfs_response_sha256.xml
@@ -108,6 +112,8 @@ files:
108
112
  - test/responses/starfield_response.xml.base64
109
113
  - test/responses/wrapped_response_2.xml.base64
110
114
  - test/settings_test.rb
115
+ - test/slologoutrequest_test.rb
116
+ - test/slologoutresponse_test.rb
111
117
  - test/test_helper.rb
112
118
  - test/xml_security_test.rb
113
119
  homepage: https://github.com/beekermememe/ruby-saml
@@ -120,17 +126,17 @@ require_paths:
120
126
  - lib
121
127
  required_ruby_version: !ruby/object:Gem::Requirement
122
128
  requirements:
123
- - - '>='
129
+ - - ! '>='
124
130
  - !ruby/object:Gem::Version
125
131
  version: '0'
126
132
  required_rubygems_version: !ruby/object:Gem::Requirement
127
133
  requirements:
128
- - - '>='
134
+ - - ! '>='
129
135
  - !ruby/object:Gem::Version
130
136
  version: '0'
131
137
  requirements: []
132
138
  rubyforge_project: http://www.rubygems.org/gems/ruby-samlnechotech
133
- rubygems_version: 2.0.3
139
+ rubygems_version: 2.1.11
134
140
  signing_key:
135
141
  specification_version: 4
136
142
  summary: SAML Ruby Tookit
@@ -140,6 +146,7 @@ test_files:
140
146
  - test/logoutrequest_test.rb
141
147
  - test/logoutresponse_test.rb
142
148
  - test/request_test.rb
149
+ - test/requests/logoutrequest_fixtures.rb
143
150
  - test/response_test.rb
144
151
  - test/responses/adfs_response_sha1.xml
145
152
  - test/responses/adfs_response_sha256.xml
@@ -160,5 +167,7 @@ test_files:
160
167
  - test/responses/starfield_response.xml.base64
161
168
  - test/responses/wrapped_response_2.xml.base64
162
169
  - test/settings_test.rb
170
+ - test/slologoutrequest_test.rb
171
+ - test/slologoutresponse_test.rb
163
172
  - test/test_helper.rb
164
173
  - test/xml_security_test.rb