saml-kit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/exe/saml-kit-decode-http-post +8 -0
  3. data/lib/saml/kit.rb +4 -4
  4. data/lib/saml/kit/authentication_request.rb +3 -13
  5. data/lib/saml/kit/bindings.rb +45 -0
  6. data/lib/saml/kit/bindings/binding.rb +42 -0
  7. data/lib/saml/kit/bindings/http_post.rb +29 -0
  8. data/lib/saml/kit/bindings/http_redirect.rb +61 -0
  9. data/lib/saml/kit/bindings/url_builder.rb +40 -0
  10. data/lib/saml/kit/configuration.rb +22 -1
  11. data/lib/saml/kit/crypto.rb +16 -0
  12. data/lib/saml/kit/crypto/oaep_cipher.rb +22 -0
  13. data/lib/saml/kit/crypto/rsa_cipher.rb +23 -0
  14. data/lib/saml/kit/crypto/simple_cipher.rb +38 -0
  15. data/lib/saml/kit/crypto/unknown_cipher.rb +18 -0
  16. data/lib/saml/kit/cryptography.rb +30 -0
  17. data/lib/saml/kit/default_registry.rb +1 -0
  18. data/lib/saml/kit/document.rb +6 -2
  19. data/lib/saml/kit/identity_provider_metadata.rb +9 -9
  20. data/lib/saml/kit/locales/en.yml +4 -3
  21. data/lib/saml/kit/logout_request.rb +2 -2
  22. data/lib/saml/kit/logout_response.rb +3 -3
  23. data/lib/saml/kit/metadata.rb +12 -37
  24. data/lib/saml/kit/namespaces.rb +1 -13
  25. data/lib/saml/kit/respondable.rb +4 -0
  26. data/lib/saml/kit/response.rb +120 -37
  27. data/lib/saml/kit/service_provider_metadata.rb +16 -7
  28. data/lib/saml/kit/signature.rb +16 -13
  29. data/lib/saml/kit/trustable.rb +14 -6
  30. data/lib/saml/kit/version.rb +1 -1
  31. data/lib/saml/kit/xml.rb +19 -3
  32. data/saml-kit.gemspec +2 -2
  33. metadata +23 -14
  34. data/lib/saml/kit/binding.rb +0 -40
  35. data/lib/saml/kit/http_post_binding.rb +0 -27
  36. data/lib/saml/kit/http_redirect_binding.rb +0 -58
  37. data/lib/saml/kit/url_builder.rb +0 -38
@@ -0,0 +1,38 @@
1
+ module Saml
2
+ module Kit
3
+ module Crypto
4
+ class SimpleCipher
5
+ ALGORITHMS = {
6
+ 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => 'DES-EDE3-CBC',
7
+ 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => 'AES-128-CBC',
8
+ 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => 'AES-192-CBC',
9
+ 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => 'AES-256-CBC',
10
+ }
11
+
12
+ def initialize(algorithm, private_key)
13
+ @algorithm = algorithm
14
+ @private_key = private_key
15
+ end
16
+
17
+ def self.matches?(algorithm)
18
+ ALGORITHMS[algorithm]
19
+ end
20
+
21
+ def decrypt(cipher_text)
22
+ cipher = OpenSSL::Cipher.new(ALGORITHMS[@algorithm])
23
+ cipher.decrypt
24
+ iv = cipher_text[0..cipher.iv_len-1]
25
+ data = cipher_text[cipher.iv_len..-1]
26
+ #cipher.padding = 0
27
+ cipher.key = @private_key
28
+ cipher.iv = iv
29
+
30
+ Saml::Kit.logger.debug ['-key', @private_key].inspect
31
+ Saml::Kit.logger.debug ['-iv', iv].inspect
32
+
33
+ cipher.update(data) + cipher.final
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ module Saml
2
+ module Kit
3
+ module Crypto
4
+ class UnknownCipher
5
+ def initialize(algorithm, key)
6
+ end
7
+
8
+ def self.matches?(algorithm)
9
+ true
10
+ end
11
+
12
+ def decrypt(cipher_text)
13
+ cipher_text
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,30 @@
1
+ module Saml
2
+ module Kit
3
+ class Cryptography
4
+ attr_reader :private_key
5
+
6
+ def initialize(private_key = Saml::Kit.configuration.encryption_private_key)
7
+ @private_key = private_key
8
+ end
9
+
10
+ def decrypt(data)
11
+ encrypt_data = data['EncryptedData']
12
+ symmetric_key = symmetric_key_from(encrypt_data)
13
+ cipher_text = Base64.decode64(encrypt_data["CipherData"]["CipherValue"])
14
+ to_plaintext(cipher_text, symmetric_key, encrypt_data["EncryptionMethod"]['Algorithm'])
15
+ end
16
+
17
+ private
18
+
19
+ def symmetric_key_from(encrypted_data)
20
+ encrypted_key = encrypted_data['KeyInfo']['EncryptedKey']
21
+ cipher_text = Base64.decode64(encrypted_key['CipherData']['CipherValue'])
22
+ to_plaintext(cipher_text, private_key, encrypted_key["EncryptionMethod"]['Algorithm'])
23
+ end
24
+
25
+ def to_plaintext(cipher_text, symmetric_key, algorithm)
26
+ return Crypto.decryptor_for(algorithm, symmetric_key).decrypt(cipher_text)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -6,6 +6,7 @@ module Saml
6
6
  end
7
7
 
8
8
  def register(metadata)
9
+ Saml::Kit.logger.debug(metadata.to_xml(pretty: true))
9
10
  @items[metadata.entity_id] = metadata
10
11
  end
11
12
 
@@ -48,8 +48,12 @@ module Saml
48
48
  @xml_hash
49
49
  end
50
50
 
51
- def to_xml
52
- content
51
+ def to_xml(pretty: false)
52
+ pretty ? Nokogiri::XML(content).to_xml(indent: 2) : content
53
+ end
54
+
55
+ def to_xhtml
56
+ Nokogiri::XML(content, &:noblanks).to_xhtml
53
57
  end
54
58
 
55
59
  def to_s
@@ -7,7 +7,7 @@ module Saml
7
7
 
8
8
  def want_authn_requests_signed
9
9
  xpath = "/md:EntityDescriptor/md:#{name}"
10
- attribute = find_by(xpath).attribute("WantAuthnRequestsSigned")
10
+ attribute = document.find_by(xpath).attribute("WantAuthnRequestsSigned")
11
11
  return true if attribute.nil?
12
12
  attribute.text.downcase == "true"
13
13
  end
@@ -21,7 +21,7 @@ module Saml
21
21
  end
22
22
 
23
23
  def attributes
24
- find_all("/md:EntityDescriptor/md:#{name}/saml:Attribute").map do |item|
24
+ document.find_all("/md:EntityDescriptor/md:#{name}/saml:Attribute").map do |item|
25
25
  {
26
26
  format: item.attribute("NameFormat").try(:value),
27
27
  name: item.attribute("Name").value,
@@ -48,19 +48,19 @@ module Saml
48
48
  @want_authn_requests_signed = true
49
49
  end
50
50
 
51
- def add_single_sign_on_service(url, binding: :post)
52
- @single_sign_on_urls.push(location: url, binding: Namespaces.binding_for(binding))
51
+ def add_single_sign_on_service(url, binding: :http_post)
52
+ @single_sign_on_urls.push(location: url, binding: Bindings.binding_for(binding))
53
53
  end
54
54
 
55
- def add_single_logout_service(url, binding: :post)
56
- @logout_urls.push(location: url, binding: Namespaces.binding_for(binding))
55
+ def add_single_logout_service(url, binding: :http_post)
56
+ @logout_urls.push(location: url, binding: Bindings.binding_for(binding))
57
57
  end
58
58
 
59
59
  def to_xml
60
- Signature.sign(id, sign: sign) do |xml, signature|
60
+ Signature.sign(sign: sign) do |xml, signature|
61
61
  xml.instruct!
62
62
  xml.EntityDescriptor entity_descriptor_options do
63
- signature.template(xml)
63
+ signature.template(id)
64
64
  xml.IDPSSODescriptor idp_sso_descriptor_options do
65
65
  xml.KeyDescriptor use: "signing" do
66
66
  xml.KeyInfo "xmlns": Namespaces::XMLDSIG do
@@ -112,8 +112,8 @@ module Saml
112
112
 
113
113
  def idp_sso_descriptor_options
114
114
  {
115
+ WantAuthnRequestsSigned: want_authn_requests_signed,
115
116
  protocolSupportEnumeration: Namespaces::PROTOCOL,
116
- WantAuthnRequestsSigned: want_authn_requests_signed
117
117
  }
118
118
  end
119
119
  end
@@ -14,12 +14,13 @@ en:
14
14
  LogoutResponse:
15
15
  unregistered: "is unregistered."
16
16
  Response:
17
- invalid: "must contain Response."
18
- unregistered: "must originate from registered identity provider."
19
17
  expired: "must not be expired."
20
- invalid_version: "must be 2.0."
18
+ invalid: "must contain Response."
19
+ invalid_fingerprint: "does not match."
21
20
  invalid_response_to: "must match request id."
21
+ invalid_version: "must be 2.0."
22
22
  must_match_issuer: "must match entityId."
23
+ unregistered: "must originate from registered identity provider."
23
24
  SPSSODescriptor:
24
25
  invalid: "must contain SPSSODescriptor."
25
26
  invalid_signature: "invalid signature."
@@ -40,11 +40,11 @@ module Saml
40
40
  end
41
41
 
42
42
  def to_xml
43
- Signature.sign(id, sign: sign) do |xml, signature|
43
+ Signature.sign(sign: sign) do |xml, signature|
44
44
  xml.instruct!
45
45
  xml.LogoutRequest logout_request_options do
46
46
  xml.Issuer({ xmlns: Namespaces::ASSERTION }, issuer)
47
- signature.template(xml)
47
+ signature.template(id)
48
48
  xml.NameID name_id_options, user.name_id_for(name_id_format)
49
49
  end
50
50
  end
@@ -25,15 +25,15 @@ module Saml
25
25
  @issuer = configuration.issuer
26
26
  provider = configuration.registry.metadata_for(@issuer)
27
27
  if provider
28
- @destination = provider.single_logout_service_for(binding: :post).try(:location)
28
+ @destination = provider.single_logout_service_for(binding: :http_post).try(:location)
29
29
  end
30
30
  end
31
31
 
32
32
  def to_xml
33
- Signature.sign(id, sign: sign) do |xml, signature|
33
+ Signature.sign(sign: sign) do |xml, signature|
34
34
  xml.LogoutResponse logout_response_options do
35
35
  xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
36
- signature.template(xml)
36
+ signature.template(id)
37
37
  xml.Status do
38
38
  xml.StatusCode Value: status_code
39
39
  end
@@ -3,14 +3,7 @@ module Saml
3
3
  class Metadata
4
4
  include ActiveModel::Validations
5
5
  include XsdValidatable
6
-
7
6
  METADATA_XSD = File.expand_path("./xsd/saml-schema-metadata-2.0.xsd", File.dirname(__FILE__)).freeze
8
- NAMESPACES = {
9
- "NameFormat": Namespaces::ATTR_SPLAT,
10
- "ds": Namespaces::XMLDSIG,
11
- "md": Namespaces::METADATA,
12
- "saml": Namespaces::ASSERTION,
13
- }.freeze
14
7
 
15
8
  validates_presence_of :metadata
16
9
  validate :must_contain_descriptor
@@ -27,16 +20,16 @@ module Saml
27
20
  end
28
21
 
29
22
  def entity_id
30
- find_by("/md:EntityDescriptor/@entityID").value
23
+ document.find_by("/md:EntityDescriptor/@entityID").value
31
24
  end
32
25
 
33
26
  def name_id_formats
34
- find_all("/md:EntityDescriptor/md:#{name}/md:NameIDFormat").map(&:text)
27
+ document.find_all("/md:EntityDescriptor/md:#{name}/md:NameIDFormat").map(&:text)
35
28
  end
36
29
 
37
30
  def certificates
38
- @certificates ||= find_all("/md:EntityDescriptor/md:#{name}/md:KeyDescriptor").map do |item|
39
- cert = item.at_xpath("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", NAMESPACES).text
31
+ @certificates ||= document.find_all("/md:EntityDescriptor/md:#{name}/md:KeyDescriptor").map do |item|
32
+ cert = item.at_xpath("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", Xml::NAMESPACES).text
40
33
  {
41
34
  text: cert,
42
35
  fingerprint: Fingerprint.new(cert).algorithm(hash_algorithm),
@@ -54,15 +47,15 @@ module Saml
54
47
  end
55
48
 
56
49
  def services(type)
57
- find_all("/md:EntityDescriptor/md:#{name}/md:#{type}").map do |item|
50
+ document.find_all("/md:EntityDescriptor/md:#{name}/md:#{type}").map do |item|
58
51
  binding = item.attribute("Binding").value
59
52
  location = item.attribute("Location").value
60
- binding_for(binding, location)
53
+ Saml::Kit::Bindings.create_for(binding, location)
61
54
  end
62
55
  end
63
56
 
64
57
  def service_for(binding:, type:)
65
- binding = Saml::Kit::Namespaces.binding_for(binding)
58
+ binding = Saml::Kit::Bindings.binding_for(binding)
66
59
  services(type).find { |x| x.binding?(binding) }
67
60
  end
68
61
 
@@ -78,6 +71,7 @@ module Saml
78
71
  if :signing == use.to_sym
79
72
  hash_value = fingerprint.algorithm(hash_algorithm)
80
73
  signing_certificates.find do |signing_certificate|
74
+ Saml::Kit.logger.debug [hash_value, signing_certificate[:fingerprint]].inspect
81
75
  hash_value == signing_certificate[:fingerprint]
82
76
  end
83
77
  end
@@ -87,8 +81,8 @@ module Saml
87
81
  @xml_hash ||= Hash.from_xml(to_xml)
88
82
  end
89
83
 
90
- def to_xml
91
- @xml
84
+ def to_xml(pretty: false)
85
+ document.to_xml(pretty: pretty)
92
86
  end
93
87
 
94
88
  def to_s
@@ -116,19 +110,11 @@ module Saml
116
110
  private
117
111
 
118
112
  def document
119
- @document ||= Nokogiri::XML(@xml)
120
- end
121
-
122
- def find_by(xpath)
123
- document.at_xpath(xpath, NAMESPACES)
124
- end
125
-
126
- def find_all(xpath)
127
- document.search(xpath, NAMESPACES)
113
+ @document ||= Xml.new(xml)
128
114
  end
129
115
 
130
116
  def metadata
131
- find_by("/md:EntityDescriptor/md:#{name}").present?
117
+ document.find_by("/md:EntityDescriptor/md:#{name}").present?
132
118
  end
133
119
 
134
120
  def must_contain_descriptor
@@ -155,17 +141,6 @@ module Saml
155
141
  end
156
142
  result
157
143
  end
158
-
159
- def binding_for(binding, location)
160
- case binding
161
- when Namespaces::HTTP_REDIRECT
162
- Saml::Kit::HttpRedirectBinding.new(location: location)
163
- when Namespaces::POST
164
- Saml::Kit::HttpPostBinding.new(location: location)
165
- else
166
- Saml::Kit::Binding.new(binding: binding, location: location)
167
- end
168
- end
169
144
  end
170
145
  end
171
146
  end
@@ -7,9 +7,6 @@ module Saml
7
7
  BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
8
8
  EMAIL_ADDRESS = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
9
9
  ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
10
- HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'
11
- HTTP_POST = POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
12
- HTTP_REDIRECT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
13
10
  METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
14
11
  PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
15
12
  PASSWORD_PROTECTED = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
@@ -32,16 +29,7 @@ module Saml
32
29
  URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
33
30
  VERSION_MISMATCH_ERROR = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"
34
31
  XMLDSIG = "http://www.w3.org/2000/09/xmldsig#"
35
-
36
- def self.binding_for(binding)
37
- if :post == binding
38
- Namespaces::HTTP_POST
39
- elsif :http_redirect == binding
40
- Namespaces::HTTP_REDIRECT
41
- else
42
- nil
43
- end
44
- end
32
+ XMLENC = "http://www.w3.org/2001/04/xmlenc#"
45
33
  end
46
34
  end
47
35
  end
@@ -21,6 +21,10 @@ module Saml
21
21
  to_h.fetch(name, {}).fetch('InResponseTo', nil)
22
22
  end
23
23
 
24
+ def success?
25
+ Namespaces::SUCCESS == status_code
26
+ end
27
+
24
28
  private
25
29
 
26
30
  def must_match_request_id
@@ -12,7 +12,7 @@ module Saml
12
12
  end
13
13
 
14
14
  def name_id
15
- to_h.fetch(name, {}).fetch('Assertion', {}).fetch('Subject', {}).fetch('NameID', nil)
15
+ assertion.fetch('Subject', {}).fetch('NameID', nil)
16
16
  end
17
17
 
18
18
  def [](key)
@@ -20,18 +20,19 @@ module Saml
20
20
  end
21
21
 
22
22
  def attributes
23
- @attributes ||= Hash[to_h.fetch(name, {}).fetch('Assertion', {}).fetch('AttributeStatement', {}).fetch('Attribute', []).map do |item|
24
- [item['Name'].to_sym, item['AttributeValue']]
25
- end].with_indifferent_access
23
+ @attributes ||= Hash[
24
+ assertion.fetch('AttributeStatement', {}).fetch('Attribute', []).map do |item|
25
+ [item['Name'].to_sym, item['AttributeValue']]
26
+ end
27
+ ].with_indifferent_access
26
28
  end
27
29
 
28
-
29
30
  def started_at
30
- parse_date(to_h.fetch(name, {}).fetch('Assertion', {}).fetch('Conditions', {}).fetch('NotBefore', nil))
31
+ parse_date(assertion.fetch('Conditions', {}).fetch('NotBefore', nil))
31
32
  end
32
33
 
33
34
  def expired_at
34
- parse_date(to_h.fetch(name, {}).fetch('Assertion', {}).fetch('Conditions', {}).fetch('NotOnOrAfter', nil))
35
+ parse_date(assertion.fetch('Conditions', {}).fetch('NotOnOrAfter', nil))
35
36
  end
36
37
 
37
38
  def expired?
@@ -42,15 +43,42 @@ module Saml
42
43
  Time.current > started_at && !expired?
43
44
  end
44
45
 
46
+ def encrypted?
47
+ to_h[name]['EncryptedAssertion'].present?
48
+ end
49
+
50
+ def assertion
51
+ @assertion =
52
+ begin
53
+ if encrypted?
54
+ decrypted = Cryptography.new.decrypt(to_h.fetch(name, {}).fetch('EncryptedAssertion', {}))
55
+ Saml::Kit.logger.debug(decrypted)
56
+ Hash.from_xml(decrypted)['Assertion']
57
+ else
58
+ to_h.fetch(name, {}).fetch('Assertion', {})
59
+ end
60
+ end
61
+ end
62
+
63
+ def signed?
64
+ super || assertion.fetch('Signature', nil).present?
65
+ end
66
+
67
+ def certificate
68
+ super || assertion.fetch('Signature', {}).fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
69
+ end
70
+
45
71
  private
46
72
 
47
73
  def must_be_active_session
48
74
  return unless expected_type?
75
+ return unless success?
49
76
  errors[:base] << error_message(:expired) unless active?
50
77
  end
51
78
 
52
79
  def must_match_issuer
53
80
  return unless expected_type?
81
+ return unless success?
54
82
 
55
83
  unless audiences.include?(Saml::Kit.configuration.issuer)
56
84
  errors[:audience] << error_message(:must_match_issuer)
@@ -58,7 +86,7 @@ module Saml
58
86
  end
59
87
 
60
88
  def audiences
61
- Array(to_h[name]['Assertion']['Conditions']['AudienceRestriction']['Audience'])
89
+ Array(assertion['Conditions']['AudienceRestriction']['Audience'])
62
90
  rescue => error
63
91
  Saml::Kit.logger.error(error)
64
92
  []
@@ -75,7 +103,7 @@ module Saml
75
103
  attr_reader :user, :request
76
104
  attr_accessor :id, :reference_id, :now
77
105
  attr_accessor :version, :status_code
78
- attr_accessor :issuer, :sign, :destination
106
+ attr_accessor :issuer, :sign, :destination, :encrypt
79
107
 
80
108
  def initialize(user, request)
81
109
  @user = user
@@ -86,8 +114,9 @@ module Saml
86
114
  @version = "2.0"
87
115
  @status_code = Namespaces::SUCCESS
88
116
  @issuer = configuration.issuer
89
- @destination = request.acs_url
117
+ @destination = destination_for(request)
90
118
  @sign = want_assertions_signed
119
+ @encrypt = false
91
120
  end
92
121
 
93
122
  def want_assertions_signed
@@ -98,38 +127,51 @@ module Saml
98
127
  end
99
128
 
100
129
  def to_xml
101
- Signature.sign(id, sign: sign) do |xml, signature|
130
+ Signature.sign(sign: sign) do |xml, signature|
102
131
  xml.Response response_options do
103
132
  xml.Issuer(issuer, xmlns: Namespaces::ASSERTION)
104
- signature.template(xml)
133
+ signature.template(id)
105
134
  xml.Status do
106
135
  xml.StatusCode Value: status_code
107
136
  end
108
- xml.Assertion(assertion_options) do
109
- xml.Issuer issuer
110
- xml.Subject do
111
- xml.NameID user.name_id_for(request.name_id_format), Format: request.name_id_format
112
- xml.SubjectConfirmation Method: Namespaces::BEARER do
113
- xml.SubjectConfirmationData "", subject_confirmation_data_options
114
- 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
115
157
  end
116
- xml.Conditions conditions_options do
117
- xml.AudienceRestriction do
118
- xml.Audience request.issuer
119
- end
158
+ end
159
+ xml.Conditions conditions_options do
160
+ xml.AudienceRestriction do
161
+ xml.Audience request.issuer
120
162
  end
121
- xml.AuthnStatement authn_statement_options do
122
- xml.AuthnContext do
123
- xml.AuthnContextClassRef Namespaces::PASSWORD
124
- end
163
+ end
164
+ xml.AuthnStatement authn_statement_options do
165
+ xml.AuthnContext do
166
+ xml.AuthnContextClassRef Namespaces::PASSWORD
125
167
  end
126
- assertion_attributes = user.assertion_attributes_for(request)
127
- if assertion_attributes.any?
128
- xml.AttributeStatement do
129
- assertion_attributes.each do |key, value|
130
- xml.Attribute Name: key, NameFormat: Namespaces::URI, FriendlyName: key do
131
- xml.AttributeValue value.to_s
132
- 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
133
175
  end
134
176
  end
135
177
  end
@@ -138,11 +180,52 @@ module Saml
138
180
  end
139
181
  end
140
182
 
141
- def build
142
- Response.new(to_xml, request_id: request.id)
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 = OpenSSL::X509::Certificate.new(Base64.decode64(request.provider.encryption_certificates.first[:text]))
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
143
220
  end
144
221
 
145
- private
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
146
229
 
147
230
  def configuration
148
231
  Saml::Kit.configuration