saml-kit 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,56 +8,7 @@ module Saml
8
8
  super(xml, name: "LogoutResponse")
9
9
  end
10
10
 
11
- private
12
-
13
- class Builder
14
- attr_accessor :id, :issuer, :version, :status_code, :sign, :now, :destination
15
- attr_reader :request
16
-
17
- def initialize(user, request, configuration: Saml::Kit.configuration, sign: true)
18
- @user = user
19
- @now = Time.now.utc
20
- @request = request
21
- @id = SecureRandom.uuid
22
- @version = "2.0"
23
- @status_code = Namespaces::SUCCESS
24
- @sign = sign
25
- @issuer = configuration.issuer
26
- provider = configuration.registry.metadata_for(@issuer)
27
- if provider
28
- @destination = provider.single_logout_service_for(binding: :http_post).try(:location)
29
- end
30
- end
31
-
32
- def to_xml
33
- Signature.sign(sign: sign) do |xml, signature|
34
- xml.LogoutResponse logout_response_options do
35
- xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
36
- signature.template(id)
37
- xml.Status do
38
- xml.StatusCode Value: status_code
39
- end
40
- end
41
- end
42
- end
43
-
44
- def build
45
- LogoutResponse.new(to_xml, request_id: request.id)
46
- end
47
-
48
- private
49
-
50
- def logout_response_options
51
- {
52
- xmlns: Namespaces::PROTOCOL,
53
- ID: "_#{id}",
54
- Version: version,
55
- IssueInstant: now.utc.iso8601,
56
- Destination: destination,
57
- InResponseTo: request.id,
58
- }
59
- end
60
- end
11
+ Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::LogoutResponse::Builder', 'Saml::Kit::Builders::LogoutResponse')
61
12
  end
62
13
  end
63
14
  end
@@ -3,6 +3,7 @@ module Saml
3
3
  class Metadata
4
4
  include ActiveModel::Validations
5
5
  include XsdValidatable
6
+ include Buildable
6
7
  METADATA_XSD = File.expand_path("./xsd/saml-schema-metadata-2.0.xsd", File.dirname(__FILE__)).freeze
7
8
 
8
9
  validates_presence_of :metadata
@@ -63,6 +64,14 @@ module Saml
63
64
  service_for(binding: binding, type: 'SingleLogoutService')
64
65
  end
65
66
 
67
+ def logout_request_for(user, binding: :http_post, relay_state: nil)
68
+ builder = Saml::Kit::LogoutRequest.builder(user) do |x|
69
+ yield x if block_given?
70
+ end
71
+ request_binding = single_logout_service_for(binding: binding)
72
+ request_binding.serialize(builder, relay_state: relay_state)
73
+ end
74
+
66
75
  def matches?(fingerprint, use: :signing)
67
76
  certificates.find do |certificate|
68
77
  certificate.for?(use) && certificate.fingerprint == fingerprint
@@ -20,11 +20,16 @@ module Saml
20
20
  end
21
21
 
22
22
  def attributes
23
- @attributes ||= Hash[
24
- assertion.fetch('AttributeStatement', {}).fetch('Attribute', []).map do |item|
25
- [item['Name'].to_sym, item['AttributeValue']]
23
+ @attributes ||=
24
+ begin
25
+ attrs = assertion.fetch('AttributeStatement', {}).fetch('Attribute', [])
26
+ items = if attrs.is_a? Hash
27
+ [[attrs["Name"], attrs["AttributeValue"]]]
28
+ else
29
+ attrs.map { |item| [item['Name'], item['AttributeValue']] }
30
+ end
31
+ Hash[items].with_indifferent_access
26
32
  end
27
- ].with_indifferent_access
28
33
  end
29
34
 
30
35
  def started_at
@@ -51,7 +56,7 @@ module Saml
51
56
  @assertion =
52
57
  begin
53
58
  if encrypted?
54
- decrypted = Cryptography.new.decrypt(to_h.fetch(name, {}).fetch('EncryptedAssertion', {}))
59
+ decrypted = XmlDecryption.new.decrypt(to_h.fetch(name, {}).fetch('EncryptedAssertion', {}))
55
60
  Saml::Kit.logger.debug(decrypted)
56
61
  Hash.from_xml(decrypted)['Assertion']
57
62
  else
@@ -99,182 +104,7 @@ module Saml
99
104
  Time.at(0).to_datetime
100
105
  end
101
106
 
102
- class Builder
103
- attr_reader :user, :request
104
- attr_accessor :id, :reference_id, :now
105
- attr_accessor :version, :status_code
106
- attr_accessor :issuer, :sign, :destination, :encrypt
107
-
108
- def initialize(user, request)
109
- @user = user
110
- @request = request
111
- @id = SecureRandom.uuid
112
- @reference_id = SecureRandom.uuid
113
- @now = Time.now.utc
114
- @version = "2.0"
115
- @status_code = Namespaces::SUCCESS
116
- @issuer = configuration.issuer
117
- @destination = destination_for(request)
118
- @sign = want_assertions_signed
119
- @encrypt = false
120
- end
121
-
122
- def want_assertions_signed
123
- request.provider.want_assertions_signed
124
- rescue => error
125
- Saml::Kit.logger.error(error)
126
- true
127
- end
128
-
129
- def to_xml
130
- Signature.sign(sign: sign) do |xml, signature|
131
- xml.Response response_options do
132
- xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
133
- signature.template(id)
134
- xml.Status do
135
- xml.StatusCode Value: status_code
136
- end
137
- assertion(xml, signature)
138
- end
139
- end
140
- end
141
-
142
- def build
143
- Response.new(to_xml, request_id: request.id)
144
- end
145
-
146
- private
147
-
148
- def assertion(xml, signature)
149
- with_encryption(xml) do |xml|
150
- xml.Assertion(assertion_options) do
151
- xml.Issuer issuer
152
- signature.template(reference_id) unless encrypt
153
- xml.Subject do
154
- xml.NameID user.name_id_for(request.name_id_format), Format: request.name_id_format
155
- xml.SubjectConfirmation Method: Namespaces::BEARER do
156
- xml.SubjectConfirmationData "", subject_confirmation_data_options
157
- end
158
- end
159
- xml.Conditions conditions_options do
160
- xml.AudienceRestriction do
161
- xml.Audience request.issuer
162
- end
163
- end
164
- xml.AuthnStatement authn_statement_options do
165
- xml.AuthnContext do
166
- xml.AuthnContextClassRef Namespaces::PASSWORD
167
- end
168
- end
169
- assertion_attributes = user.assertion_attributes_for(request)
170
- if assertion_attributes.any?
171
- xml.AttributeStatement do
172
- assertion_attributes.each do |key, value|
173
- xml.Attribute Name: key, NameFormat: Namespaces::URI, FriendlyName: key do
174
- xml.AttributeValue value.to_s
175
- end
176
- end
177
- end
178
- end
179
- end
180
- end
181
- end
182
-
183
- def with_encryption(xml)
184
- if encrypt
185
- temp = ::Builder::XmlMarkup.new
186
- yield temp
187
- raw_xml_to_encrypt = temp.target!
188
-
189
- encryption_certificate = request.provider.encryption_certificates.first
190
- public_key = encryption_certificate.public_key
191
-
192
- cipher = OpenSSL::Cipher.new('AES-256-CBC')
193
- cipher.encrypt
194
- key = cipher.random_key
195
- iv = cipher.random_iv
196
- encrypted = cipher.update(raw_xml_to_encrypt) + cipher.final
197
-
198
- Saml::Kit.logger.debug ['+iv', iv].inspect
199
- Saml::Kit.logger.debug ['+key', key].inspect
200
-
201
- xml.EncryptedAssertion xmlns: Namespaces::ASSERTION do
202
- xml.EncryptedData xmlns: Namespaces::XMLENC do
203
- xml.EncryptionMethod Algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
204
- xml.KeyInfo xmlns: Namespaces::XMLDSIG do
205
- xml.EncryptedKey xmlns: Namespaces::XMLENC do
206
- xml.EncryptionMethod Algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
207
- xml.CipherData do
208
- xml.CipherValue Base64.encode64(public_key.public_encrypt(key))
209
- end
210
- end
211
- end
212
- xml.CipherData do
213
- xml.CipherValue Base64.encode64(iv + encrypted)
214
- end
215
- end
216
- end
217
- else
218
- yield xml
219
- end
220
- end
221
-
222
- def destination_for(request)
223
- if request.signed? && request.trusted?
224
- request.acs_url || request.provider.assertion_consumer_service_for(binding: :http_post).try(:location)
225
- else
226
- request.provider.assertion_consumer_service_for(binding: :http_post).try(:location)
227
- end
228
- end
229
-
230
- def configuration
231
- Saml::Kit.configuration
232
- end
233
-
234
- def response_options
235
- {
236
- ID: id.present? ? "_#{id}" : nil,
237
- Version: version,
238
- IssueInstant: now.iso8601,
239
- Destination: destination,
240
- Consent: Namespaces::UNSPECIFIED,
241
- InResponseTo: request.id,
242
- xmlns: Namespaces::PROTOCOL,
243
- }
244
- end
245
-
246
- def assertion_options
247
- {
248
- ID: "_#{reference_id}",
249
- IssueInstant: now.iso8601,
250
- Version: "2.0",
251
- xmlns: Namespaces::ASSERTION,
252
- }
253
- end
254
-
255
- def subject_confirmation_data_options
256
- {
257
- InResponseTo: request.id,
258
- NotOnOrAfter: 3.hours.since(now).utc.iso8601,
259
- Recipient: request.acs_url,
260
- }
261
- end
262
-
263
- def conditions_options
264
- {
265
- NotBefore: now.utc.iso8601,
266
- NotOnOrAfter: Saml::Kit.configuration.session_timeout.from_now.utc.iso8601,
267
- }
268
- end
269
-
270
- def authn_statement_options
271
- {
272
- AuthnInstant: now.iso8601,
273
- SessionIndex: assertion_options[:ID],
274
- SessionNotOnOrAfter: 3.hours.since(now).utc.iso8601,
275
- }
276
- end
277
- end
107
+ Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::Response::Builder', 'Saml::Kit::Builders::Response')
278
108
  end
279
109
  end
280
110
  end
@@ -1,6 +1,8 @@
1
1
  module Saml
2
2
  module Kit
3
3
  class SelfSignedCertificate
4
+ SUBJECT="/C=CA/ST=Alberta/L=Calgary/O=SamlKit/OU=SamlKit/CN=SamlKit"
5
+
4
6
  def initialize(password)
5
7
  @password = password
6
8
  end
@@ -9,20 +11,25 @@ module Saml
9
11
  rsa_key = OpenSSL::PKey::RSA.new(2048)
10
12
  public_key = rsa_key.public_key
11
13
  certificate = OpenSSL::X509::Certificate.new
12
- certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse("/C=CA/ST=Alberta/L=Calgary/O=Xsig/OU=Xsig/CN=Xsig")
14
+ certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse(SUBJECT)
13
15
  certificate.not_before = DateTime.now.beginning_of_day
14
- certificate.not_after = 1.year.from_now.end_of_day
16
+ certificate.not_after = 30.days.from_now
15
17
  certificate.public_key = public_key
16
18
  certificate.serial = 0x0
17
19
  certificate.version = 2
18
20
  factory = OpenSSL::X509::ExtensionFactory.new
19
21
  factory.subject_certificate = factory.issuer_certificate = certificate
20
- certificate.extensions = [ factory.create_extension("basicConstraints","CA:TRUE", true), factory.create_extension("subjectKeyIdentifier", "hash"), ]
21
- certificate.add_extension(factory.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always"))
22
+ certificate.extensions = [
23
+ factory.create_extension("basicConstraints","CA:TRUE", true),
24
+ factory.create_extension("subjectKeyIdentifier", "hash"),
25
+ ]
26
+ certificate.add_extension(
27
+ factory.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
28
+ )
22
29
  certificate.sign(rsa_key, OpenSSL::Digest::SHA256.new)
23
30
  [
24
31
  certificate.to_pem,
25
- rsa_key.to_pem(OpenSSL::Cipher::Cipher.new('des3'), @password)
32
+ rsa_key.to_pem(OpenSSL::Cipher.new('AES-256-CBC'), @password)
26
33
  ]
27
34
  end
28
35
  end
@@ -15,92 +15,15 @@ module Saml
15
15
 
16
16
  def want_assertions_signed
17
17
  attribute = document.find_by("/md:EntityDescriptor/md:#{name}").attribute("WantAssertionsSigned")
18
+ return true if attribute.nil?
18
19
  attribute.text.downcase == "true"
19
20
  end
20
21
 
21
- class Builder
22
- attr_accessor :id, :entity_id, :acs_urls, :logout_urls, :name_id_formats, :sign
23
- attr_accessor :want_assertions_signed
24
-
25
- def initialize(configuration = Saml::Kit.configuration)
26
- @id = SecureRandom.uuid
27
- @configuration = configuration
28
- @entity_id = configuration.issuer
29
- @acs_urls = []
30
- @logout_urls = []
31
- @name_id_formats = [Namespaces::PERSISTENT]
32
- @sign = true
33
- @want_assertions_signed = true
34
- end
35
-
36
- def add_assertion_consumer_service(url, binding: :http_post)
37
- @acs_urls.push(location: url, binding: Bindings.binding_for(binding))
38
- end
39
-
40
- def add_single_logout_service(url, binding: :http_post)
41
- @logout_urls.push(location: url, binding: Bindings.binding_for(binding))
42
- end
43
-
44
- def to_xml
45
- Signature.sign(sign: sign) do |xml, signature|
46
- xml.instruct!
47
- xml.EntityDescriptor entity_descriptor_options do
48
- signature.template(id)
49
- xml.SPSSODescriptor descriptor_options do
50
- if @configuration.signing_certificate_pem.present?
51
- xml.KeyDescriptor use: "signing" do
52
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
53
- xml.X509Data do
54
- xml.X509Certificate @configuration.stripped_signing_certificate
55
- end
56
- end
57
- end
58
- end
59
- if @configuration.encryption_certificate_pem.present?
60
- xml.KeyDescriptor use: "encryption" do
61
- xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
62
- xml.X509Data do
63
- xml.X509Certificate @configuration.stripped_encryption_certificate
64
- end
65
- end
66
- end
67
- end
68
- logout_urls.each do |item|
69
- xml.SingleLogoutService Binding: item[:binding], Location: item[:location]
70
- end
71
- name_id_formats.each do |format|
72
- xml.NameIDFormat format
73
- end
74
- acs_urls.each_with_index do |item, index|
75
- xml.AssertionConsumerService Binding: item[:binding], Location: item[:location], index: index, isDefault: index == 0 ? true : false
76
- end
77
- end
78
- end
79
- end
80
- end
81
-
82
- def build
83
- ServiceProviderMetadata.new(to_xml)
84
- end
85
-
86
- private
87
-
88
- def entity_descriptor_options
89
- {
90
- 'xmlns': Namespaces::METADATA,
91
- ID: "_#{id}",
92
- entityID: entity_id,
93
- }
94
- end
95
-
96
- def descriptor_options
97
- {
98
- AuthnRequestsSigned: sign,
99
- WantAssertionsSigned: want_assertions_signed,
100
- protocolSupportEnumeration: Namespaces::PROTOCOL,
101
- }
102
- end
22
+ def self.builder_class
23
+ Saml::Kit::Builders::ServiceProviderMetadata
103
24
  end
25
+
26
+ Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::ServiceProviderMetadata::Builder', 'Saml::Kit::Builders::ServiceProviderMetadata')
104
27
  end
105
28
  end
106
29
  end
@@ -34,7 +34,7 @@ module Saml
34
34
  xml.SignedInfo do
35
35
  xml.CanonicalizationMethod Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#"
36
36
  xml.SignatureMethod Algorithm: SIGNATURE_METHODS[configuration.signature_method]
37
- xml.Reference URI: "#_#{reference_id}" do
37
+ xml.Reference URI: "##{reference_id}" do
38
38
  xml.Transforms do
39
39
  xml.Transform Algorithm: "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
40
40
  xml.Transform Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#"
@@ -30,11 +30,7 @@ module Saml
30
30
  end
31
31
 
32
32
  def provider
33
- registry.metadata_for(issuer)
34
- end
35
-
36
- def registry
37
- Saml::Kit.configuration.registry
33
+ Saml::Kit.registry.metadata_for(issuer)
38
34
  end
39
35
 
40
36
  def signature_verified!