samlr 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of samlr might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0f3f8a1071f3f774c3a9d624731c7e6aab77ce61
4
- data.tar.gz: c4c25e98aa62abfaa17733a41fe1732003148bb5
3
+ metadata.gz: e8cdc251b3fddf94ba10cfc58f73e92992433b7f
4
+ data.tar.gz: 9f3d8faf8246aee7e277fb594243e814dcddf5f7
5
5
  SHA512:
6
- metadata.gz: f7b190788159255d83cef081bdae91f344865168926f2e63dc083e4ef4bf7e12780a6154af4ce398c7726260bf92df0eec38237b5e6197e2fc2b70e1d7ad72fa
7
- data.tar.gz: 8c8f29c3e09782ed5f870ed9a73763a78f24fa6f28f158d8e06b91e2d81f7d2d7781600d9e25b692d4f1102e3dd24443fc444ee7114f6c259f9818979f07644e
6
+ metadata.gz: 4ab81e921e414ef961a943226d4b048f80a0f9f1f9c0233052a25821ed7c0448e89e5c184506b12c61c91f17273f17cb2a53bf4c506a17f83ad94ddd906242c5
7
+ data.tar.gz: 8a7101fda53f16d3c67602d48a1461b033a153c996a04c655612ec3e36aa6791d31ea9098dc90d7d721f49696604e2ceaa6a7261beb34bf18431ebcbcea6271e
data/README.md CHANGED
@@ -7,13 +7,14 @@ Samlr leverages Nokogiri for the heavy lifting and keeps things simple. Samlr al
7
7
  ### Initiating an authentication request
8
8
 
9
9
  ```ruby
10
- saml_request = Samlr::Request.new(
11
- :issuer => request.host,
12
- :name_identity_format => Samlr::EMAIL_FORMAT,
13
- :consumer_service_url => "https://#{request.host}/auth/saml"
14
- )
10
+ saml_request = Samlr::Request.new(nil, {
11
+ :issuer => request.host,
12
+ :name_identity_format => Samlr::EMAIL_FORMAT,
13
+ :consumer_service_url => "https://#{request.host}/auth/saml"
14
+ })
15
15
  ```
16
16
 
17
+
17
18
  At this point you can access `request.param` if all you want is the encoded params, or you can get a fully valid request URL with an appropriate `RelayState` value:
18
19
 
19
20
  ```ruby
@@ -58,6 +59,28 @@ end
58
59
 
59
60
  When the verification suceeds,the resulting response object will surface `saml_response.name_id` (String) and `saml_response.attributes` (Hash).
60
61
 
62
+ ### Handling a LogoutRequest from the IdP
63
+
64
+ i.e. (https://example.com/logout?SAMLRequest=encoded_saml_logout_request)
65
+
66
+ **Decode the request**
67
+
68
+ ```ruby
69
+ idp_logout_request = Samlr::LogoutRequest.new(params["SAMLRequest"])
70
+ ```
71
+
72
+ Then after logging out the user out you can get a fully valid response URL by:
73
+
74
+ ```ruby
75
+ logout_response_options = {
76
+ :destination => remote_logout_url,
77
+ :in_response_to => idp_logout_request.id
78
+ }
79
+ logout_response = Samlr::LogoutResponse.new(nil, logout_response_options)
80
+
81
+ logout_response.url(authentication.remote_logout_url)
82
+ ```
83
+
61
84
  ### Metadata
62
85
 
63
86
  Currently no support for signing, but that should be fairly easy to extract from the `Samlr::Tools::ResponseBuilder`. Get a metadata XML document like this:
@@ -51,3 +51,4 @@ require "samlr/signature"
51
51
  require "samlr/response"
52
52
  require "samlr/request"
53
53
  require "samlr/logout_request"
54
+ require "samlr/logout_response"
@@ -19,4 +19,7 @@ module Samlr
19
19
 
20
20
  class ConditionsError < SamlrError
21
21
  end
22
+
23
+ class NoDataError < SamlrError
24
+ end
22
25
  end
@@ -3,5 +3,9 @@ module Samlr
3
3
  def body
4
4
  @body ||= Samlr::Tools::LogoutRequestBuilder.build(options)
5
5
  end
6
+
7
+ def id
8
+ @id ||= get_attribute_or_element("//samlp:LogoutRequest", "ID")
9
+ end
6
10
  end
7
11
  end
@@ -0,0 +1,11 @@
1
+ module Samlr
2
+ class LogoutResponse < Request
3
+ def body
4
+ @body ||= Samlr::Tools::LogoutResponseBuilder.build(options)
5
+ end
6
+
7
+ def type
8
+ "SAMLResponse"
9
+ end
10
+ end
11
+ end
@@ -2,10 +2,11 @@ require "cgi"
2
2
 
3
3
  module Samlr
4
4
  class Request
5
- attr_reader :options
5
+ attr_reader :options, :document
6
6
 
7
- def initialize(options = {})
7
+ def initialize(data = nil, options = {})
8
8
  @options = options
9
+ @document = Request.parse(data)
9
10
  end
10
11
 
11
12
  # The encoded SAML request
@@ -18,14 +19,16 @@ module Samlr
18
19
  @body ||= Samlr::Tools::RequestBuilder.build(options)
19
20
  end
20
21
 
22
+ def type
23
+ "SAMLRequest"
24
+ end
25
+
21
26
  # Utility method to get the full redirect destination, Request#url("https://idp.example.com/saml", { :RelayState => "https://sp.example.com/saml" })
22
27
  def url(root, params = {})
23
28
  dest = root.dup
24
- if dest.include?("?")
25
- dest << "&SAMLRequest=#{param}"
26
- else
27
- dest << "?SAMLRequest=#{param}"
28
- end
29
+
30
+ dest << (dest.include?("?") ? "&" : "?")
31
+ dest << "#{type}=#{param}"
29
32
 
30
33
  params.each_pair do |key, value|
31
34
  dest << "&#{key}=#{CGI.escape(value.to_s)}"
@@ -33,5 +36,25 @@ module Samlr
33
36
 
34
37
  dest
35
38
  end
39
+
40
+ def self.parse(data)
41
+ Samlr::Tools.parse(data, compressed: true)
42
+ end
43
+
44
+ def get_attribute_or_element(x_path,attribute=nil)
45
+ if document
46
+ element = document.xpath(x_path)
47
+ if element.length == 0
48
+ nil
49
+ elsif attribute
50
+ value = element.attr(attribute)
51
+ value.to_s if value
52
+ else
53
+ element
54
+ end
55
+ else
56
+ raise Samlr::NoDataError.new("Attempting to get attributes of a Request that has no data")
57
+ end
58
+ end
36
59
  end
37
60
  end
@@ -41,28 +41,8 @@ module Samlr
41
41
  @assertion ||= Samlr::Assertion.new(document, options)
42
42
  end
43
43
 
44
- # Tries to parse the SAML response. First, it assumes it to be Base64 encoded
45
- # If this fails, it subsequently attempts to parse the raw input as select IdP's
46
- # send that rather than a Base64 encoded value
47
44
  def self.parse(data)
48
- begin
49
- document = Nokogiri::XML(Base64.decode64(data)) { |config| config.strict }
50
- rescue Nokogiri::XML::SyntaxError => e
51
- begin
52
- document = Nokogiri::XML(data) { |config| config.strict }
53
- rescue
54
- raise Samlr::FormatError.new(e.message)
55
- end
56
- end
57
-
58
- begin
59
- Samlr::Tools.validate!(:document => document)
60
- rescue Samlr::SamlrError => e
61
- Samlr.logger.warn("Accepting non schema conforming response: #{e.message}, #{e.details}")
62
- raise e unless Samlr.validation_mode == :log
63
- end
64
-
65
- document
45
+ Samlr::Tools.parse(data)
66
46
  end
67
47
  end
68
48
  end
@@ -10,6 +10,7 @@ require "samlr/tools/request_builder"
10
10
  require "samlr/tools/response_builder"
11
11
  require "samlr/tools/metadata_builder"
12
12
  require "samlr/tools/logout_request_builder"
13
+ require "samlr/tools/logout_response_builder"
13
14
 
14
15
  module Samlr
15
16
  module Tools
@@ -104,5 +105,41 @@ module Samlr
104
105
  end
105
106
  end
106
107
  end
108
+
109
+ # Tries to parse the SAML request, returns nil if no data passed.
110
+ # First, it assumes it to be Base64 encoded.
111
+ # If this fails, it subsequently attempts to parse the raw input as select IdP's
112
+ # send that rather than a Base64 encoded value
113
+ def self.parse(data, compressed: false)
114
+ return unless data
115
+ decoded = Base64.decode64(data)
116
+ decoded = self.inflate(decoded) if compressed
117
+ begin
118
+ doc = Nokogiri::XML(decoded) { |config| config.strict }
119
+ rescue Nokogiri::XML::SyntaxError => e
120
+ begin
121
+ doc = Nokogiri::XML(data) { |config| config.strict }
122
+ rescue
123
+ raise Samlr::FormatError.new(e.message)
124
+ end
125
+ end
126
+
127
+ begin
128
+ Samlr::Tools.validate!(:document => doc)
129
+ rescue Samlr::SamlrError => e
130
+ Samlr.logger.warn("Accepting non schema conforming response: #{e.message}, #{e.details}")
131
+ raise e unless Samlr.validation_mode == :log
132
+ end
133
+ doc
134
+ end
135
+
136
+ def self.inflate(data)
137
+ inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
138
+ decoded = inflater.inflate(data)
139
+
140
+ inflater.finish
141
+ inflater.close
142
+ decoded
143
+ end
107
144
  end
108
145
  end
@@ -0,0 +1,33 @@
1
+ require "nokogiri"
2
+
3
+ module Samlr
4
+ module Tools
5
+ # Use this for building the SAML logout response XML
6
+ module LogoutResponseBuilder
7
+ def self.build(options = {})
8
+ status_code = options[:status_code] || "urn:oasis:names:tc:SAML:2.0:status:Success"
9
+ builder = Nokogiri::XML::Builder.new do |xml|
10
+ xml.LogoutResponse(logout_response_options(options)) do
11
+ xml.doc.root.namespace = xml.doc.root.namespace_definitions.find { |ns| ns.prefix == "samlp" }
12
+ xml["saml"].Issuer(options[:issuer]) if options[:issuer]
13
+ xml["samlp"].Status { |xml| xml["samlp"].StatusCode("Value" => status_code) }
14
+ end
15
+ end
16
+ builder.to_xml(COMPACT)
17
+ end
18
+
19
+ def self.logout_response_options(options)
20
+ result = {
21
+ "xmlns:samlp" => NS_MAP["samlp"],
22
+ "xmlns:saml" => NS_MAP["saml"],
23
+ "ID" => Samlr::Tools.uuid,
24
+ "IssueInstant" => Samlr::Tools::Timestamp.stamp,
25
+ "Version" => "2.0"
26
+ }
27
+ result["InResponseTo"] = options[:in_response_to] if options[:in_response_to]
28
+ result["Destination"] = options[:destination] if options[:destination]
29
+ result
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  module Samlr
2
- VERSION = "2.4.1"
2
+ VERSION = "2.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samlr
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Morten Primdahl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-18 00:00:00.000000000 Z
11
+ date: 2016-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -121,6 +121,7 @@ files:
121
121
  - lib/samlr/fingerprint_sha1.rb
122
122
  - lib/samlr/fingerprint_sha256.rb
123
123
  - lib/samlr/logout_request.rb
124
+ - lib/samlr/logout_response.rb
124
125
  - lib/samlr/reference.rb
125
126
  - lib/samlr/request.rb
126
127
  - lib/samlr/response.rb
@@ -128,6 +129,7 @@ files:
128
129
  - lib/samlr/tools.rb
129
130
  - lib/samlr/tools/certificate_builder.rb
130
131
  - lib/samlr/tools/logout_request_builder.rb
132
+ - lib/samlr/tools/logout_response_builder.rb
131
133
  - lib/samlr/tools/metadata_builder.rb
132
134
  - lib/samlr/tools/request_builder.rb
133
135
  - lib/samlr/tools/response_builder.rb
@@ -145,7 +147,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
145
147
  requirements:
146
148
  - - ">="
147
149
  - !ruby/object:Gem::Version
148
- version: 1.9.3
150
+ version: 2.1.10
149
151
  required_rubygems_version: !ruby/object:Gem::Requirement
150
152
  requirements:
151
153
  - - ">="
@@ -153,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
155
  version: '0'
154
156
  requirements: []
155
157
  rubyforge_project:
156
- rubygems_version: 2.4.5.1
158
+ rubygems_version: 2.5.1
157
159
  signing_key:
158
160
  specification_version: 4
159
161
  summary: Ruby tools for SAML