icn_saml_idp 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +238 -0
- data/app/controllers/saml_idp/idp_controller.rb +53 -0
- data/app/views/saml_idp/idp/new.html.erb +22 -0
- data/app/views/saml_idp/idp/saml_post.html.erb +14 -0
- data/lib/saml_idp.rb +92 -0
- data/lib/saml_idp/algorithmable.rb +19 -0
- data/lib/saml_idp/assertion_builder.rb +172 -0
- data/lib/saml_idp/attribute_decorator.rb +27 -0
- data/lib/saml_idp/attributeable.rb +24 -0
- data/lib/saml_idp/configurator.rb +48 -0
- data/lib/saml_idp/controller.rb +128 -0
- data/lib/saml_idp/default.rb +49 -0
- data/lib/saml_idp/encryptor.rb +86 -0
- data/lib/saml_idp/engine.rb +5 -0
- data/lib/saml_idp/hashable.rb +26 -0
- data/lib/saml_idp/incoming_metadata.rb +144 -0
- data/lib/saml_idp/logout_builder.rb +42 -0
- data/lib/saml_idp/logout_request_builder.rb +34 -0
- data/lib/saml_idp/logout_response_builder.rb +35 -0
- data/lib/saml_idp/metadata_builder.rb +160 -0
- data/lib/saml_idp/name_id_formatter.rb +68 -0
- data/lib/saml_idp/persisted_metadata.rb +10 -0
- data/lib/saml_idp/request.rb +180 -0
- data/lib/saml_idp/response_builder.rb +62 -0
- data/lib/saml_idp/saml_response.rb +79 -0
- data/lib/saml_idp/service_provider.rb +76 -0
- data/lib/saml_idp/signable.rb +131 -0
- data/lib/saml_idp/signature_builder.rb +42 -0
- data/lib/saml_idp/signed_info_builder.rb +88 -0
- data/lib/saml_idp/version.rb +4 -0
- data/lib/saml_idp/xml_security.rb +181 -0
- data/saml_idp.gemspec +65 -0
- data/spec/acceptance/acceptance_helper.rb +9 -0
- data/spec/acceptance/idp_controller_spec.rb +16 -0
- data/spec/lib/saml_idp/algorithmable_spec.rb +48 -0
- data/spec/lib/saml_idp/assertion_builder_spec.rb +106 -0
- data/spec/lib/saml_idp/attribute_decorator_spec.rb +53 -0
- data/spec/lib/saml_idp/configurator_spec.rb +43 -0
- data/spec/lib/saml_idp/controller_spec.rb +94 -0
- data/spec/lib/saml_idp/encryptor_spec.rb +27 -0
- data/spec/lib/saml_idp/logout_request_builder_spec.rb +41 -0
- data/spec/lib/saml_idp/logout_response_builder_spec.rb +41 -0
- data/spec/lib/saml_idp/metadata_builder_spec.rb +19 -0
- data/spec/lib/saml_idp/name_id_formatter_spec.rb +42 -0
- data/spec/lib/saml_idp/request_spec.rb +106 -0
- data/spec/lib/saml_idp/response_builder_spec.rb +42 -0
- data/spec/lib/saml_idp/saml_response_spec.rb +68 -0
- data/spec/lib/saml_idp/service_provider_spec.rb +27 -0
- data/spec/lib/saml_idp/signable_spec.rb +77 -0
- data/spec/lib/saml_idp/signature_builder_spec.rb +19 -0
- data/spec/lib/saml_idp/signed_info_builder_spec.rb +25 -0
- data/spec/rails_app/.gitignore +15 -0
- data/spec/rails_app/README.rdoc +261 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +15 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_app/app/controllers/application_controller.rb +3 -0
- data/spec/rails_app/app/controllers/saml_controller.rb +8 -0
- data/spec/rails_app/app/controllers/saml_idp_controller.rb +11 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/.gitkeep +0 -0
- data/spec/rails_app/app/models/.gitkeep +0 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +60 -0
- data/spec/rails_app/config/boot.rb +6 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +37 -0
- data/spec/rails_app/config/environments/production.rb +67 -0
- data/spec/rails_app/config/environments/test.rb +37 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +8 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +6 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/doc/README_FOR_APP +2 -0
- data/spec/rails_app/lib/assets/.gitkeep +0 -0
- data/spec/rails_app/lib/tasks/.gitkeep +0 -0
- data/spec/rails_app/log/.gitkeep +0 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/index.html +241 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/rails_app/test/fixtures/.gitkeep +0 -0
- data/spec/rails_app/test/functional/.gitkeep +0 -0
- data/spec/rails_app/test/integration/.gitkeep +0 -0
- data/spec/rails_app/test/performance/browsing_test.rb +12 -0
- data/spec/rails_app/test/test_helper.rb +13 -0
- data/spec/rails_app/test/unit/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/rails_app/vendor/plugins/.gitkeep +0 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/support/certificates/certificate1 +12 -0
- data/spec/support/certificates/r1_certificate2_base64 +1 -0
- data/spec/support/responses/adfs_response_sha1.xml +46 -0
- data/spec/support/responses/adfs_response_sha256.xml +46 -0
- data/spec/support/responses/adfs_response_sha384.xml +46 -0
- data/spec/support/responses/adfs_response_sha512.xml +46 -0
- data/spec/support/responses/logoutresponse_fixtures.rb +67 -0
- data/spec/support/responses/no_signature_ns.xml +48 -0
- data/spec/support/responses/open_saml_response.xml +56 -0
- data/spec/support/responses/r1_response6.xml.base64 +1 -0
- data/spec/support/responses/response1.xml.base64 +1 -0
- data/spec/support/responses/response2.xml.base64 +79 -0
- data/spec/support/responses/response3.xml.base64 +66 -0
- data/spec/support/responses/response4.xml.base64 +93 -0
- data/spec/support/responses/response5.xml.base64 +102 -0
- data/spec/support/responses/response_with_ampersands.xml +139 -0
- data/spec/support/responses/response_with_ampersands.xml.base64 +93 -0
- data/spec/support/responses/simple_saml_php.xml +71 -0
- data/spec/support/responses/starfield_response.xml.base64 +1 -0
- data/spec/support/responses/wrapped_response_2.xml.base64 +150 -0
- data/spec/support/saml_request_macros.rb +38 -0
- data/spec/support/security_helpers.rb +61 -0
- data/spec/xml_security_spec.rb +137 -0
- metadata +465 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
module SamlIdp
|
2
|
+
class NameIdFormatter
|
3
|
+
attr_accessor :list
|
4
|
+
def initialize(list)
|
5
|
+
self.list = (list || {})
|
6
|
+
end
|
7
|
+
|
8
|
+
def all
|
9
|
+
if split?
|
10
|
+
one_one.map { |key_val| build("1.1", key_val)[:name] } +
|
11
|
+
two_zero.map { |key_val| build("2.0", key_val)[:name] }
|
12
|
+
else
|
13
|
+
list.map { |key_val| build("2.0", key_val)[:name] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def chosen
|
18
|
+
if split?
|
19
|
+
version, choose = "1.1", one_one.first
|
20
|
+
version, choose = "2.0", two_zero.first unless choose
|
21
|
+
version, choose = "2.0", "persistent" unless choose
|
22
|
+
build(version, choose)
|
23
|
+
else
|
24
|
+
choose = list.first || "persistent"
|
25
|
+
build("2.0", choose)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def build(version, key_val)
|
30
|
+
key_val = Array(key_val)
|
31
|
+
name = key_val.first.to_s.underscore
|
32
|
+
getter = build_getter key_val.last || name
|
33
|
+
{
|
34
|
+
name: "urn:oasis:names:tc:SAML:#{version}:nameid-format:#{name.camelize(:lower)}",
|
35
|
+
getter: getter
|
36
|
+
}
|
37
|
+
end
|
38
|
+
private :build
|
39
|
+
|
40
|
+
def build_getter(getter_val)
|
41
|
+
if getter_val.respond_to?(:call)
|
42
|
+
getter_val
|
43
|
+
else
|
44
|
+
->(p) { p.public_send getter_val.to_s }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
private :build_getter
|
48
|
+
|
49
|
+
def split?
|
50
|
+
list.is_a?(Hash) && (list.key?("2.0") || list.key?("1.1"))
|
51
|
+
end
|
52
|
+
private :split?
|
53
|
+
|
54
|
+
def one_one
|
55
|
+
list["1.1"] || {}
|
56
|
+
rescue TypeError
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
private :one_one
|
60
|
+
|
61
|
+
def two_zero
|
62
|
+
list["2.0"] || {}
|
63
|
+
rescue TypeError
|
64
|
+
{}
|
65
|
+
end
|
66
|
+
private :two_zero
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'saml_idp/xml_security'
|
2
|
+
require 'saml_idp/service_provider'
|
3
|
+
module SamlIdp
|
4
|
+
class Request
|
5
|
+
def self.from_deflated_request(raw)
|
6
|
+
if raw
|
7
|
+
decoded = Base64.decode64(raw)
|
8
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
9
|
+
begin
|
10
|
+
inflated = zstream.inflate(decoded).tap do
|
11
|
+
zstream.finish
|
12
|
+
zstream.close
|
13
|
+
end
|
14
|
+
rescue Zlib::BufError, Zlib::DataError # not compressed
|
15
|
+
inflated = decoded
|
16
|
+
end
|
17
|
+
else
|
18
|
+
inflated = ""
|
19
|
+
end
|
20
|
+
new(inflated)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :raw_xml
|
24
|
+
|
25
|
+
delegate :config, to: :SamlIdp
|
26
|
+
private :config
|
27
|
+
delegate :xpath, to: :document
|
28
|
+
private :xpath
|
29
|
+
|
30
|
+
def initialize(raw_xml = "")
|
31
|
+
self.raw_xml = raw_xml
|
32
|
+
end
|
33
|
+
|
34
|
+
def logout_request?
|
35
|
+
logout_request.nil? ? false : true
|
36
|
+
end
|
37
|
+
|
38
|
+
def authn_request?
|
39
|
+
authn_request.nil? ? false : true
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_id
|
43
|
+
request["ID"]
|
44
|
+
end
|
45
|
+
|
46
|
+
def request
|
47
|
+
if authn_request?
|
48
|
+
authn_request
|
49
|
+
elsif logout_request?
|
50
|
+
logout_request
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def requested_authn_context
|
55
|
+
if authn_request? && authn_context_node
|
56
|
+
authn_context_node.content
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def acs_url
|
63
|
+
service_provider.acs_url ||
|
64
|
+
authn_request["AssertionConsumerServiceURL"].to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def logout_url
|
68
|
+
service_provider.assertion_consumer_logout_service_url
|
69
|
+
end
|
70
|
+
|
71
|
+
def response_url
|
72
|
+
if authn_request?
|
73
|
+
acs_url
|
74
|
+
elsif logout_request?
|
75
|
+
logout_url
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def log(msg)
|
80
|
+
if Rails && Rails.logger
|
81
|
+
Rails.logger.info msg
|
82
|
+
else
|
83
|
+
puts msg
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def valid?
|
88
|
+
unless service_provider?
|
89
|
+
log "Unable to find service provider for issuer #{issuer}"
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
unless (authn_request? ^ logout_request?)
|
94
|
+
log "One and only one of authnrequest and logout request is required. authnrequest: #{authn_request?} logout_request: #{logout_request?} "
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
|
98
|
+
unless valid_signature?
|
99
|
+
log "Signature is invalid in #{raw_xml}"
|
100
|
+
return false
|
101
|
+
end
|
102
|
+
|
103
|
+
if response_url.nil?
|
104
|
+
log "Unable to find response url for #{issuer}: #{raw_xml}"
|
105
|
+
return false
|
106
|
+
end
|
107
|
+
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
|
111
|
+
def valid_signature?
|
112
|
+
# Force signatures for logout requests because there is no other
|
113
|
+
# protection against a cross-site DoS.
|
114
|
+
service_provider.valid_signature?(document, logout_request?)
|
115
|
+
end
|
116
|
+
|
117
|
+
def service_provider?
|
118
|
+
service_provider.valid?
|
119
|
+
end
|
120
|
+
|
121
|
+
def service_provider
|
122
|
+
@_service_provider ||= ServiceProvider.new((service_provider_finder[issuer] || {}).merge(identifier: issuer))
|
123
|
+
end
|
124
|
+
|
125
|
+
def issuer
|
126
|
+
@_issuer ||= xpath("//saml:Issuer", saml: assertion).first.try(:content)
|
127
|
+
@_issuer if @_issuer.present?
|
128
|
+
end
|
129
|
+
|
130
|
+
def name_id
|
131
|
+
@_name_id ||= xpath("//saml:NameID", saml: assertion).first.try(:content)
|
132
|
+
end
|
133
|
+
|
134
|
+
def session_index
|
135
|
+
@_session_index ||= xpath("//samlp:SessionIndex", samlp: samlp).first.try(:content)
|
136
|
+
end
|
137
|
+
|
138
|
+
def document
|
139
|
+
@_document ||= Saml::XML::Document.parse(raw_xml)
|
140
|
+
end
|
141
|
+
private :document
|
142
|
+
|
143
|
+
def authn_context_node
|
144
|
+
@_authn_context_node ||= xpath("//samlp:AuthnRequest/samlp:RequestedAuthnContext/saml:AuthnContextClassRef",
|
145
|
+
samlp: samlp,
|
146
|
+
saml: assertion).first
|
147
|
+
end
|
148
|
+
private :authn_context_node
|
149
|
+
|
150
|
+
def authn_request
|
151
|
+
@_authn_request ||= xpath("//samlp:AuthnRequest", samlp: samlp).first
|
152
|
+
end
|
153
|
+
private :authn_request
|
154
|
+
|
155
|
+
def logout_request
|
156
|
+
@_logout_request ||= xpath("//samlp:LogoutRequest", samlp: samlp).first
|
157
|
+
end
|
158
|
+
private :logout_request
|
159
|
+
|
160
|
+
def samlp
|
161
|
+
Saml::XML::Namespaces::PROTOCOL
|
162
|
+
end
|
163
|
+
private :samlp
|
164
|
+
|
165
|
+
def assertion
|
166
|
+
Saml::XML::Namespaces::ASSERTION
|
167
|
+
end
|
168
|
+
private :assertion
|
169
|
+
|
170
|
+
def signature_namespace
|
171
|
+
Saml::XML::Namespaces::SIGNATURE
|
172
|
+
end
|
173
|
+
private :signature_namespace
|
174
|
+
|
175
|
+
def service_provider_finder
|
176
|
+
config.service_provider.finder
|
177
|
+
end
|
178
|
+
private :service_provider_finder
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'builder'
|
2
|
+
module SamlIdp
|
3
|
+
class ResponseBuilder
|
4
|
+
attr_accessor :response_id
|
5
|
+
attr_accessor :issuer_uri
|
6
|
+
attr_accessor :saml_acs_url
|
7
|
+
attr_accessor :saml_request_id
|
8
|
+
attr_accessor :assertion_and_signature
|
9
|
+
|
10
|
+
def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature)
|
11
|
+
self.response_id = response_id
|
12
|
+
self.issuer_uri = issuer_uri
|
13
|
+
self.saml_acs_url = saml_acs_url
|
14
|
+
self.saml_request_id = saml_request_id
|
15
|
+
self.assertion_and_signature = assertion_and_signature
|
16
|
+
end
|
17
|
+
|
18
|
+
def encoded
|
19
|
+
@encoded ||= encode
|
20
|
+
end
|
21
|
+
|
22
|
+
def raw
|
23
|
+
build
|
24
|
+
end
|
25
|
+
|
26
|
+
def encode
|
27
|
+
Base64.strict_encode64(raw)
|
28
|
+
end
|
29
|
+
private :encode
|
30
|
+
|
31
|
+
def build
|
32
|
+
resp_options = {}
|
33
|
+
resp_options[:ID] = response_id_string
|
34
|
+
resp_options[:Version] = "2.0"
|
35
|
+
resp_options[:IssueInstant] = now_iso
|
36
|
+
resp_options[:Destination] = saml_acs_url
|
37
|
+
resp_options[:Consent] = Saml::XML::Namespaces::Consents::UNSPECIFIED
|
38
|
+
resp_options[:InResponseTo] = saml_request_id unless saml_request_id.nil?
|
39
|
+
resp_options["xmlns:samlp"] = Saml::XML::Namespaces::PROTOCOL
|
40
|
+
|
41
|
+
builder = Builder::XmlMarkup.new
|
42
|
+
builder.tag! "samlp:Response", resp_options do |response|
|
43
|
+
response.Issuer issuer_uri, xmlns: Saml::XML::Namespaces::ASSERTION
|
44
|
+
response.tag! "samlp:Status" do |status|
|
45
|
+
status.tag! "samlp:StatusCode", Value: Saml::XML::Namespaces::Statuses::SUCCESS
|
46
|
+
end
|
47
|
+
response << assertion_and_signature
|
48
|
+
end
|
49
|
+
end
|
50
|
+
private :build
|
51
|
+
|
52
|
+
def response_id_string
|
53
|
+
"_#{response_id}"
|
54
|
+
end
|
55
|
+
private :response_id_string
|
56
|
+
|
57
|
+
def now_iso
|
58
|
+
Time.now.utc.iso8601
|
59
|
+
end
|
60
|
+
private :now_iso
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'saml_idp/assertion_builder'
|
2
|
+
require 'saml_idp/response_builder'
|
3
|
+
module SamlIdp
|
4
|
+
class SamlResponse
|
5
|
+
attr_accessor :assertion_with_signature
|
6
|
+
attr_accessor :reference_id
|
7
|
+
attr_accessor :response_id
|
8
|
+
attr_accessor :issuer_uri
|
9
|
+
attr_accessor :principal
|
10
|
+
attr_accessor :audience_uri
|
11
|
+
attr_accessor :saml_request_id
|
12
|
+
attr_accessor :saml_acs_url
|
13
|
+
attr_accessor :algorithm
|
14
|
+
attr_accessor :secret_key
|
15
|
+
attr_accessor :x509_certificate
|
16
|
+
attr_accessor :authn_context_classref
|
17
|
+
attr_accessor :expiry
|
18
|
+
attr_accessor :encryption_opts
|
19
|
+
|
20
|
+
def initialize(reference_id,
|
21
|
+
response_id,
|
22
|
+
issuer_uri,
|
23
|
+
principal,
|
24
|
+
audience_uri,
|
25
|
+
saml_request_id,
|
26
|
+
saml_acs_url,
|
27
|
+
algorithm,
|
28
|
+
authn_context_classref,
|
29
|
+
expiry=60*60,
|
30
|
+
encryption_opts=nil
|
31
|
+
)
|
32
|
+
self.reference_id = reference_id
|
33
|
+
self.response_id = response_id
|
34
|
+
self.issuer_uri = issuer_uri
|
35
|
+
self.principal = principal
|
36
|
+
self.audience_uri = audience_uri
|
37
|
+
self.saml_request_id = saml_request_id
|
38
|
+
self.saml_acs_url = saml_acs_url
|
39
|
+
self.algorithm = algorithm
|
40
|
+
self.secret_key = secret_key
|
41
|
+
self.x509_certificate = x509_certificate
|
42
|
+
self.authn_context_classref = authn_context_classref
|
43
|
+
self.expiry = expiry
|
44
|
+
self.encryption_opts = encryption_opts
|
45
|
+
end
|
46
|
+
|
47
|
+
def build
|
48
|
+
@built ||= response_builder.encoded
|
49
|
+
end
|
50
|
+
|
51
|
+
def signed_assertion
|
52
|
+
if encryption_opts
|
53
|
+
assertion_builder.encrypt(sign: true)
|
54
|
+
else
|
55
|
+
assertion_builder.signed
|
56
|
+
end
|
57
|
+
end
|
58
|
+
private :signed_assertion
|
59
|
+
|
60
|
+
def response_builder
|
61
|
+
ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion)
|
62
|
+
end
|
63
|
+
private :response_builder
|
64
|
+
|
65
|
+
def assertion_builder
|
66
|
+
@assertion_builder ||= AssertionBuilder.new reference_id,
|
67
|
+
issuer_uri,
|
68
|
+
principal,
|
69
|
+
audience_uri,
|
70
|
+
saml_request_id,
|
71
|
+
saml_acs_url,
|
72
|
+
algorithm,
|
73
|
+
authn_context_classref,
|
74
|
+
expiry,
|
75
|
+
encryption_opts
|
76
|
+
end
|
77
|
+
private :assertion_builder
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'saml_idp/attributeable'
|
3
|
+
require 'saml_idp/incoming_metadata'
|
4
|
+
require 'saml_idp/persisted_metadata'
|
5
|
+
module SamlIdp
|
6
|
+
class ServiceProvider
|
7
|
+
include Attributeable
|
8
|
+
attribute :identifier
|
9
|
+
attribute :cert
|
10
|
+
attribute :fingerprint
|
11
|
+
attribute :metadata_url
|
12
|
+
attribute :validate_signature
|
13
|
+
attribute :acs_url
|
14
|
+
attribute :assertion_consumer_logout_service_url
|
15
|
+
|
16
|
+
delegate :config, to: :SamlIdp
|
17
|
+
|
18
|
+
def valid?
|
19
|
+
attributes.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_signature?(doc, require_signature = false)
|
23
|
+
if require_signature || should_validate_signature?
|
24
|
+
doc.valid_signature?(fingerprint)
|
25
|
+
else
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def should_validate_signature?
|
31
|
+
attributes[:validate_signature] ||
|
32
|
+
current_metadata.respond_to?(:sign_assertions?) && current_metadata.sign_assertions?
|
33
|
+
end
|
34
|
+
|
35
|
+
def refresh_metadata
|
36
|
+
fresh = fresh_incoming_metadata
|
37
|
+
if valid_signature?(fresh.document)
|
38
|
+
metadata_persister[identifier, fresh]
|
39
|
+
@current_metadata = nil
|
40
|
+
fresh
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def current_metadata
|
45
|
+
@current_metadata ||= get_current_or_build
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_current_or_build
|
49
|
+
persisted = metadata_getter[identifier, self]
|
50
|
+
if persisted.is_a? Hash
|
51
|
+
PersistedMetadata.new(persisted)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
private :get_current_or_build
|
55
|
+
|
56
|
+
def metadata_getter
|
57
|
+
config.service_provider.persisted_metadata_getter
|
58
|
+
end
|
59
|
+
private :metadata_getter
|
60
|
+
|
61
|
+
def metadata_persister
|
62
|
+
config.service_provider.metadata_persister
|
63
|
+
end
|
64
|
+
private :metadata_persister
|
65
|
+
|
66
|
+
def fresh_incoming_metadata
|
67
|
+
IncomingMetadata.new request_metadata
|
68
|
+
end
|
69
|
+
private :fresh_incoming_metadata
|
70
|
+
|
71
|
+
def request_metadata
|
72
|
+
metadata_url.present? ? HTTParty.get(metadata_url).body : ""
|
73
|
+
end
|
74
|
+
private :request_metadata
|
75
|
+
end
|
76
|
+
end
|