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 +4 -4
- data/README.md +28 -5
- data/lib/samlr.rb +1 -0
- data/lib/samlr/errors.rb +3 -0
- data/lib/samlr/logout_request.rb +4 -0
- data/lib/samlr/logout_response.rb +11 -0
- data/lib/samlr/request.rb +30 -7
- data/lib/samlr/response.rb +1 -21
- data/lib/samlr/tools.rb +37 -0
- data/lib/samlr/tools/logout_response_builder.rb +33 -0
- data/lib/samlr/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8cdc251b3fddf94ba10cfc58f73e92992433b7f
|
4
|
+
data.tar.gz: 9f3d8faf8246aee7e277fb594243e814dcddf5f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
12
|
-
|
13
|
-
|
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:
|
data/lib/samlr.rb
CHANGED
data/lib/samlr/errors.rb
CHANGED
data/lib/samlr/logout_request.rb
CHANGED
data/lib/samlr/request.rb
CHANGED
@@ -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
|
-
|
25
|
-
|
26
|
-
|
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
|
data/lib/samlr/response.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/samlr/tools.rb
CHANGED
@@ -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
|
data/lib/samlr/version.rb
CHANGED
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
|
+
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-
|
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.
|
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.
|
158
|
+
rubygems_version: 2.5.1
|
157
159
|
signing_key:
|
158
160
|
specification_version: 4
|
159
161
|
summary: Ruby tools for SAML
|