saml2 1.0.10 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -7
- data/lib/saml2.rb +2 -0
- data/lib/saml2/attribute.rb +2 -0
- data/lib/saml2/attribute_consuming_service.rb +1 -0
- data/lib/saml2/authn_request.rb +19 -47
- data/lib/saml2/base.rb +5 -2
- data/lib/saml2/bindings.rb +7 -0
- data/lib/saml2/bindings/http_redirect.rb +141 -0
- data/lib/saml2/contact.rb +14 -16
- data/lib/saml2/endpoint.rb +5 -6
- data/lib/saml2/entity.rb +23 -18
- data/lib/saml2/identity_provider.rb +4 -4
- data/lib/saml2/indexed_object.rb +7 -3
- data/lib/saml2/key.rb +19 -1
- data/lib/saml2/logout_request.rb +43 -0
- data/lib/saml2/logout_response.rb +23 -0
- data/lib/saml2/message.rb +109 -0
- data/lib/saml2/name_id.rb +16 -8
- data/lib/saml2/organization_and_contacts.rb +2 -2
- data/lib/saml2/request.rb +8 -0
- data/lib/saml2/response.rb +7 -23
- data/lib/saml2/role.rb +2 -3
- data/lib/saml2/service_provider.rb +24 -2
- data/lib/saml2/sso.rb +2 -2
- data/lib/saml2/status.rb +28 -0
- data/lib/saml2/status_response.rb +33 -0
- data/lib/saml2/version.rb +1 -1
- data/spec/fixtures/identity_provider.xml +1 -0
- data/spec/fixtures/response_signed.xml +1 -1
- data/spec/fixtures/response_with_attribute_signed.xml +1 -1
- data/spec/lib/attribute_consuming_service_spec.rb +37 -37
- data/spec/lib/attribute_spec.rb +17 -17
- data/spec/lib/authn_request_spec.rb +15 -71
- data/spec/lib/bindings/http_redirect_spec.rb +151 -0
- data/spec/lib/conditions_spec.rb +10 -10
- data/spec/lib/entity_spec.rb +12 -12
- data/spec/lib/identity_provider_spec.rb +4 -4
- data/spec/lib/indexed_object_spec.rb +38 -7
- data/spec/lib/logout_request_spec.rb +31 -0
- data/spec/lib/logout_response_spec.rb +31 -0
- data/spec/lib/message_spec.rb +21 -0
- data/spec/lib/response_spec.rb +8 -9
- data/spec/lib/service_provider_spec.rb +29 -8
- data/spec/spec_helper.rb +0 -1
- metadata +41 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 382418cbc200bcf81a58ff09ebadd551d0c16101
|
4
|
+
data.tar.gz: 0f7318a0d65eb9b80afbe0405d6dc768cc4e3f0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b0462631aa2d2c0208655bf85e8a104555034176df8825f42029f1356a745d07f561f15a5c78719e8a1400eb70063cc89c6014abaf226a3294d5a6a0aa319c1
|
7
|
+
data.tar.gz: c1ad828d647e905e3e92e4e34c2e4856a370fe32abf9bfe337e344dd34e46a95b9d344f5c3cd7542a1636a068febad623af0a83ee2cc524d7c0e74eb7cb8a61e
|
data/Rakefile
CHANGED
@@ -2,12 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'bundler'
|
3
3
|
Bundler::GemHelper.install_tasks
|
4
4
|
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
8
|
-
Rake::TestTask.new do |t|
|
9
|
-
t.name = "spec"
|
10
|
-
t.pattern = "spec/**/*_spec.rb"
|
11
|
-
end
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new
|
12
7
|
|
13
8
|
task :default => :spec
|
data/lib/saml2.rb
CHANGED
data/lib/saml2/attribute.rb
CHANGED
@@ -65,6 +65,7 @@ module SAML2
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def from_xml(node)
|
68
|
+
super
|
68
69
|
@name = node['Name']
|
69
70
|
@friendly_name = node['FriendlyName']
|
70
71
|
@name_format = node['NameFormat']
|
@@ -124,6 +125,7 @@ module SAML2
|
|
124
125
|
end
|
125
126
|
|
126
127
|
def from_xml(node)
|
128
|
+
super
|
127
129
|
@attributes = node.xpath('saml:Attribute', Namespaces::ALL).map do |attr|
|
128
130
|
Attribute.from_xml(attr)
|
129
131
|
end
|
data/lib/saml2/authn_request.rb
CHANGED
@@ -2,49 +2,29 @@ require 'base64'
|
|
2
2
|
require 'zlib'
|
3
3
|
|
4
4
|
require 'saml2/attribute_consuming_service'
|
5
|
+
require 'saml2/bindings/http_redirect'
|
5
6
|
require 'saml2/endpoint'
|
6
7
|
require 'saml2/name_id'
|
7
8
|
require 'saml2/namespaces'
|
9
|
+
require 'saml2/request'
|
8
10
|
require 'saml2/schemas'
|
9
11
|
require 'saml2/subject'
|
10
12
|
|
11
13
|
module SAML2
|
12
|
-
class
|
13
|
-
|
14
|
-
|
15
|
-
class AuthnRequest
|
14
|
+
class AuthnRequest < Request
|
15
|
+
# deprecated; takes _just_ the SAMLRequest parameter's value
|
16
16
|
def self.decode(authnrequest)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
# do it in 1K slices, so we can protect against bombs
|
23
|
-
(0..authnrequest.bytesize / 1024).each do |i|
|
24
|
-
xml.concat(zstream.inflate(authnrequest.byteslice(i * 1024, 1024)))
|
25
|
-
raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size]
|
26
|
-
end
|
27
|
-
xml.concat(zstream.finish)
|
28
|
-
raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size]
|
29
|
-
|
30
|
-
zstream.close
|
31
|
-
rescue Zlib::DataError, Zlib::BufError
|
32
|
-
end
|
33
|
-
parse(xml)
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.parse(authnrequest)
|
37
|
-
new(Nokogiri::XML(authnrequest))
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize(document)
|
41
|
-
@document = document
|
17
|
+
result, _relay_state = Bindings::HTTPRedirect.decode("http://host/?SAMLRequest=#{authnrequest}")
|
18
|
+
return nil unless result.is_a?(AuthnRequest)
|
19
|
+
result
|
20
|
+
rescue CorruptMessage
|
21
|
+
AuthnRequest.from_xml(Nokogiri::XML('<xml></xml>').root)
|
42
22
|
end
|
43
23
|
|
44
24
|
def valid_schema?
|
45
|
-
return false unless
|
25
|
+
return false unless super
|
46
26
|
# Check for the correct root element
|
47
|
-
return false unless
|
27
|
+
return false unless xml.at_xpath('/samlp:AuthnRequest', Namespaces::ALL)
|
48
28
|
|
49
29
|
true
|
50
30
|
end
|
@@ -83,46 +63,38 @@ module SAML2
|
|
83
63
|
true
|
84
64
|
end
|
85
65
|
|
86
|
-
def issuer
|
87
|
-
@issuer ||= NameID.from_xml(@document.root.at_xpath('saml:Issuer', Namespaces::ALL))
|
88
|
-
end
|
89
|
-
|
90
66
|
def name_id_policy
|
91
|
-
@name_id_policy ||= NameID::Policy.from_xml(
|
92
|
-
end
|
93
|
-
|
94
|
-
def id
|
95
|
-
@document.root['ID']
|
67
|
+
@name_id_policy ||= NameID::Policy.from_xml(xml.at_xpath('samlp:NameIDPolicy', Namespaces::ALL))
|
96
68
|
end
|
97
69
|
|
98
70
|
attr_reader :assertion_consumer_service, :attribute_consuming_service
|
99
71
|
|
100
72
|
def assertion_consumer_service_url
|
101
|
-
|
73
|
+
xml['AssertionConsumerServiceURL']
|
102
74
|
end
|
103
75
|
|
104
76
|
def assertion_consumer_service_index
|
105
|
-
|
77
|
+
xml['AssertionConsumerServiceIndex'] && xml['AssertionConsumerServiceIndex'].to_i
|
106
78
|
end
|
107
79
|
|
108
80
|
def attribute_consuming_service_index
|
109
|
-
|
81
|
+
xml['AttributeConsumerServiceIndex'] && xml['AttributeConsumerServiceIndex'].to_i
|
110
82
|
end
|
111
83
|
|
112
84
|
def force_authn?
|
113
|
-
|
85
|
+
xml['ForceAuthn']
|
114
86
|
end
|
115
87
|
|
116
88
|
def passive?
|
117
|
-
|
89
|
+
xml['IsPassive']
|
118
90
|
end
|
119
91
|
|
120
92
|
def protocol_binding
|
121
|
-
|
93
|
+
xml['ProtocolBinding']
|
122
94
|
end
|
123
95
|
|
124
96
|
def subject
|
125
|
-
@subject ||= Subject.from_xml(
|
97
|
+
@subject ||= Subject.from_xml(xml.at_xpath('saml:Subject', Namespaces::ALL))
|
126
98
|
end
|
127
99
|
end
|
128
100
|
end
|
data/lib/saml2/base.rb
CHANGED
@@ -9,7 +9,10 @@ module SAML2
|
|
9
9
|
result
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
attr_reader :xml
|
13
|
+
|
14
|
+
def from_xml(node)
|
15
|
+
@xml = node
|
13
16
|
end
|
14
17
|
|
15
18
|
def to_s
|
@@ -28,7 +31,7 @@ module SAML2
|
|
28
31
|
|
29
32
|
def self.load_string_array(node, element)
|
30
33
|
node.xpath(element, Namespaces::ALL).map do |element_node|
|
31
|
-
element_node.content
|
34
|
+
element_node.content&.strip
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'uri'
|
3
|
+
require 'zlib'
|
4
|
+
|
5
|
+
require 'saml2/bindings'
|
6
|
+
require 'saml2/message'
|
7
|
+
|
8
|
+
module SAML2
|
9
|
+
module Bindings
|
10
|
+
module HTTPRedirect
|
11
|
+
URN ="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect".freeze
|
12
|
+
|
13
|
+
module SigAlgs
|
14
|
+
DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1".freeze
|
15
|
+
RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1".freeze
|
16
|
+
|
17
|
+
RECOGNIZED = [DSA_SHA1, RSA_SHA1].freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def decode(url, public_key: nil, public_key_used: nil)
|
22
|
+
uri = begin
|
23
|
+
URI.parse(url)
|
24
|
+
rescue URI::InvalidURIError
|
25
|
+
raise CorruptMessage
|
26
|
+
end
|
27
|
+
|
28
|
+
raise MissingMessage unless uri.query
|
29
|
+
query = URI.decode_www_form(uri.query)
|
30
|
+
base64 = query.assoc('SAMLRequest')&.last
|
31
|
+
if base64
|
32
|
+
message_param = 'SAMLRequest'
|
33
|
+
else
|
34
|
+
base64 = query.assoc('SAMLResponse')&.last
|
35
|
+
message_param = 'SAMLResponse'
|
36
|
+
end
|
37
|
+
encoding = query.assoc('SAMLEncoding')&.last
|
38
|
+
relay_state = query.assoc('RelayState')&.last
|
39
|
+
signature = query.assoc('Signature')&.last
|
40
|
+
sig_alg = query.assoc('SigAlg')&.last
|
41
|
+
raise MissingMessage unless base64
|
42
|
+
|
43
|
+
raise UnsupportedEncoding if encoding && encoding != Encodings::DEFLATE
|
44
|
+
|
45
|
+
raise MessageTooLarge if base64.bytesize > SAML2.config[:max_message_size]
|
46
|
+
|
47
|
+
deflated = begin
|
48
|
+
Base64.strict_decode64(base64)
|
49
|
+
rescue ArgumentError
|
50
|
+
raise CorruptMessage
|
51
|
+
end
|
52
|
+
|
53
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
54
|
+
xml = ''
|
55
|
+
begin
|
56
|
+
# do it in 1K slices, so we can protect against bombs
|
57
|
+
(0..deflated.bytesize / 1024).each do |i|
|
58
|
+
xml.concat(zstream.inflate(deflated.byteslice(i * 1024, 1024)))
|
59
|
+
raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size]
|
60
|
+
end
|
61
|
+
xml.concat(zstream.finish)
|
62
|
+
raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size]
|
63
|
+
rescue Zlib::DataError, Zlib::BufError
|
64
|
+
raise CorruptMessage
|
65
|
+
end
|
66
|
+
|
67
|
+
zstream.close
|
68
|
+
message = Message.parse(xml)
|
69
|
+
# if a block is provided, it's to fetch the proper certificate
|
70
|
+
# based on the contents of the message
|
71
|
+
public_key ||= yield(message, sig_alg) if block_given?
|
72
|
+
if public_key
|
73
|
+
raise UnsignedMessage unless signature
|
74
|
+
raise UnsupportedSignatureAlgorithm unless SigAlgs::RECOGNIZED.include?(sig_alg)
|
75
|
+
|
76
|
+
begin
|
77
|
+
signature = Base64.strict_decode64(signature)
|
78
|
+
rescue ArgumentError
|
79
|
+
raise CorruptMessage
|
80
|
+
end
|
81
|
+
|
82
|
+
base_string = find_raw_query_param(uri.query, message_param)
|
83
|
+
base_string << '&' << find_raw_query_param(uri.query, 'RelayState') if relay_state
|
84
|
+
base_string << '&' << find_raw_query_param(uri.query, 'SigAlg')
|
85
|
+
|
86
|
+
valid_signature = false
|
87
|
+
# there could be multiple certificates to try
|
88
|
+
Array(public_key).each do |key|
|
89
|
+
if key.verify(OpenSSL::Digest::SHA1.new, signature, base_string)
|
90
|
+
# notify the caller which certificate was used
|
91
|
+
public_key_used&.call(key)
|
92
|
+
valid_signature = true
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
raise InvalidSignature unless valid_signature
|
97
|
+
end
|
98
|
+
[message, relay_state]
|
99
|
+
end
|
100
|
+
|
101
|
+
def encode(message, relay_state: nil, private_key: nil)
|
102
|
+
result = URI.parse(message.destination)
|
103
|
+
original_query = URI.decode_www_form(result.query) if result.query
|
104
|
+
original_query ||= []
|
105
|
+
# remove any SAML protocol parameters
|
106
|
+
%w{SAMLEncoding SAMLRequest SAMLResponse RelayState SigAlg Signature}.each do |param|
|
107
|
+
original_query.delete_if { |(k, v)| k == param }
|
108
|
+
end
|
109
|
+
|
110
|
+
xml = message.to_s
|
111
|
+
zstream = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS)
|
112
|
+
deflated = zstream.deflate(xml, Zlib::FINISH)
|
113
|
+
zstream.close
|
114
|
+
base64 = Base64.strict_encode64(deflated)
|
115
|
+
|
116
|
+
query = []
|
117
|
+
query << [message.is_a?(Request) ? 'SAMLRequest' : 'SAMLResponse', base64]
|
118
|
+
query << ['RelayState', relay_state] if relay_state
|
119
|
+
if private_key
|
120
|
+
query << ['SigAlg', SigAlgs::RSA_SHA1]
|
121
|
+
base_string = URI.encode_www_form(query)
|
122
|
+
signature = private_key.sign(OpenSSL::Digest::SHA1.new, base_string)
|
123
|
+
query << ['Signature', Base64.strict_encode64(signature)]
|
124
|
+
end
|
125
|
+
|
126
|
+
result.query = URI.encode_www_form(original_query + query)
|
127
|
+
result.to_s
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# we need to find the param, and return it still encoded from the URL
|
133
|
+
def find_raw_query_param(query, param)
|
134
|
+
start = query.index(param)
|
135
|
+
finish = (query.index('&', start + param.length + 1) || 0) - 1
|
136
|
+
query[start..finish]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/saml2/contact.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'saml2/base'
|
2
2
|
|
3
3
|
module SAML2
|
4
|
-
class Contact
|
4
|
+
class Contact < Base
|
5
5
|
module Type
|
6
6
|
ADMINISTRATIVE = 'administrative'.freeze
|
7
7
|
BILLING = 'billing'.freeze
|
@@ -12,27 +12,25 @@ module SAML2
|
|
12
12
|
|
13
13
|
attr_accessor :type, :company, :given_name, :surname, :email_addresses, :telephone_numbers
|
14
14
|
|
15
|
-
def self.from_xml(node)
|
16
|
-
return nil unless node
|
17
|
-
|
18
|
-
result = new(node['contactType'])
|
19
|
-
company = node.at_xpath('md:Company', Namespaces::ALL)
|
20
|
-
result.company = company && company.content && company.content.strip
|
21
|
-
given_name = node.at_xpath('md:GivenName', Namespaces::ALL)
|
22
|
-
result.given_name = given_name && given_name.content && given_name.content.strip
|
23
|
-
surname = node.at_xpath('md:SurName', Namespaces::ALL)
|
24
|
-
result.surname = surname && surname.content && surname.content.strip
|
25
|
-
result.email_addresses = Base.load_string_array(node, 'md:EmailAddress')
|
26
|
-
result.telephone_numbers = Base.load_string_array(node, 'md:TelephoneNumber')
|
27
|
-
result
|
28
|
-
end
|
29
|
-
|
30
15
|
def initialize(type = Type::OTHER)
|
31
16
|
@type = type
|
32
17
|
@email_addresses = []
|
33
18
|
@telephone_numbers = []
|
34
19
|
end
|
35
20
|
|
21
|
+
def from_xml(node)
|
22
|
+
self.type = node['contactType']
|
23
|
+
company = node.at_xpath('md:Company', Namespaces::ALL)
|
24
|
+
self.company = company && company.content && company.content.strip
|
25
|
+
given_name = node.at_xpath('md:GivenName', Namespaces::ALL)
|
26
|
+
self.given_name = given_name && given_name.content && given_name.content.strip
|
27
|
+
surname = node.at_xpath('md:SurName', Namespaces::ALL)
|
28
|
+
self.surname = surname && surname.content && surname.content.strip
|
29
|
+
self.email_addresses = load_string_array(node, 'md:EmailAddress')
|
30
|
+
self.telephone_numbers = load_string_array(node, 'md:TelephoneNumber')
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
36
34
|
def build(builder)
|
37
35
|
builder['md'].ContactPerson('contactType' => type) do |contact_person|
|
38
36
|
contact_person['md'].Company(company) if company
|
data/lib/saml2/endpoint.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
+
require 'saml2/bindings/http_redirect'
|
2
|
+
|
1
3
|
module SAML2
|
2
4
|
class Endpoint < Base
|
3
5
|
module Bindings
|
4
6
|
HTTP_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST".freeze
|
5
|
-
HTTP_REDIRECT =
|
6
|
-
end
|
7
|
-
|
8
|
-
module Encodings
|
9
|
-
DEFLATE= "urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE".freeze
|
7
|
+
HTTP_REDIRECT = ::SAML2::Bindings::HTTPRedirect::URN
|
10
8
|
end
|
11
9
|
|
12
10
|
attr_reader :location, :binding
|
@@ -20,6 +18,7 @@ module SAML2
|
|
20
18
|
end
|
21
19
|
|
22
20
|
def from_xml(node)
|
21
|
+
super
|
23
22
|
@location = node['Location']
|
24
23
|
@binding = node['Binding']
|
25
24
|
end
|
@@ -31,7 +30,7 @@ module SAML2
|
|
31
30
|
class Indexed < Endpoint
|
32
31
|
include IndexedObject
|
33
32
|
|
34
|
-
def initialize(location = nil, index = nil, is_default =
|
33
|
+
def initialize(location = nil, index = nil, is_default = nil, binding = Bindings::HTTP_POST)
|
35
34
|
super(location, binding)
|
36
35
|
@index, @is_default = index, is_default
|
37
36
|
end
|
data/lib/saml2/entity.rb
CHANGED
@@ -26,34 +26,40 @@ module SAML2
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
class Group <
|
30
|
-
|
31
|
-
|
29
|
+
class Group < Base
|
30
|
+
include Enumerable
|
31
|
+
[:each, :[]].each do |method|
|
32
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
33
|
+
def #{method}(*args, &block)
|
34
|
+
@entities.#{method}(*args, &block)
|
35
|
+
end
|
36
|
+
RUBY
|
32
37
|
end
|
33
38
|
|
34
39
|
def initialize
|
40
|
+
@entities = []
|
35
41
|
@valid_until = nil
|
36
42
|
end
|
37
43
|
|
38
44
|
def from_xml(node)
|
39
|
-
|
45
|
+
super
|
40
46
|
remove_instance_variable(:@valid_until)
|
41
|
-
|
47
|
+
@entities = Base.load_object_array(xml, "md:EntityDescriptor|md:EntitiesDescriptor",
|
42
48
|
'EntityDescriptor' => Entity,
|
43
|
-
'EntitiesDescriptor' => Group)
|
49
|
+
'EntitiesDescriptor' => Group)
|
44
50
|
end
|
45
51
|
|
46
52
|
def valid_schema?
|
47
|
-
Schemas.federation.valid?(
|
53
|
+
Schemas.federation.valid?(xml.document)
|
48
54
|
end
|
49
55
|
|
50
56
|
def signature
|
51
57
|
unless instance_variable_defined?(:@signature)
|
52
|
-
@signature =
|
58
|
+
@signature = xml.at_xpath('dsig:Signature', Namespaces::ALL)
|
53
59
|
signed_node = @signature.at_xpath('dsig:SignedInfo/dsig:Reference', Namespaces::ALL)['URI']
|
54
60
|
# validating the schema will automatically add ID attributes, so check that first
|
55
|
-
|
56
|
-
@signature = nil unless signed_node == "##{
|
61
|
+
xml.set_id_attribute('ID') unless xml.document.get_id(xml['ID'])
|
62
|
+
@signature = nil unless signed_node == "##{xml['ID']}"
|
57
63
|
end
|
58
64
|
@signature
|
59
65
|
end
|
@@ -68,7 +74,7 @@ module SAML2
|
|
68
74
|
|
69
75
|
def valid_until
|
70
76
|
unless instance_variable_defined?(:@valid_until)
|
71
|
-
@valid_until =
|
77
|
+
@valid_until = xml['validUntil'] && Time.parse(xml['validUntil'])
|
72
78
|
end
|
73
79
|
@valid_until
|
74
80
|
end
|
@@ -82,23 +88,22 @@ module SAML2
|
|
82
88
|
end
|
83
89
|
|
84
90
|
def from_xml(node)
|
85
|
-
|
91
|
+
super
|
86
92
|
remove_instance_variable(:@valid_until)
|
87
93
|
@roles = nil
|
88
|
-
super
|
89
94
|
end
|
90
95
|
|
91
96
|
def valid_schema?
|
92
|
-
Schemas.federation.valid?(
|
97
|
+
Schemas.federation.valid?(xml.document)
|
93
98
|
end
|
94
99
|
|
95
100
|
def entity_id
|
96
|
-
@entity_id ||
|
101
|
+
@entity_id || xml && xml['entityID']
|
97
102
|
end
|
98
103
|
|
99
104
|
def valid_until
|
100
105
|
unless instance_variable_defined?(:@valid_until)
|
101
|
-
@valid_until =
|
106
|
+
@valid_until = xml['validUntil'] && Time.parse(xml['validUntil'])
|
102
107
|
end
|
103
108
|
@valid_until
|
104
109
|
end
|
@@ -112,8 +117,8 @@ module SAML2
|
|
112
117
|
end
|
113
118
|
|
114
119
|
def roles
|
115
|
-
@roles ||= load_object_array(
|
116
|
-
load_object_array(
|
120
|
+
@roles ||= load_object_array(xml, 'md:IDPSSODescriptor', IdentityProvider) +
|
121
|
+
load_object_array(xml, 'md:SPSSODescriptor', ServiceProvider)
|
117
122
|
end
|
118
123
|
|
119
124
|
def build(builder)
|