saml-kit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/saml-kit-decode-http-post +8 -0
- data/lib/saml/kit.rb +4 -4
- data/lib/saml/kit/authentication_request.rb +3 -13
- data/lib/saml/kit/bindings.rb +45 -0
- data/lib/saml/kit/bindings/binding.rb +42 -0
- data/lib/saml/kit/bindings/http_post.rb +29 -0
- data/lib/saml/kit/bindings/http_redirect.rb +61 -0
- data/lib/saml/kit/bindings/url_builder.rb +40 -0
- data/lib/saml/kit/configuration.rb +22 -1
- data/lib/saml/kit/crypto.rb +16 -0
- data/lib/saml/kit/crypto/oaep_cipher.rb +22 -0
- data/lib/saml/kit/crypto/rsa_cipher.rb +23 -0
- data/lib/saml/kit/crypto/simple_cipher.rb +38 -0
- data/lib/saml/kit/crypto/unknown_cipher.rb +18 -0
- data/lib/saml/kit/cryptography.rb +30 -0
- data/lib/saml/kit/default_registry.rb +1 -0
- data/lib/saml/kit/document.rb +6 -2
- data/lib/saml/kit/identity_provider_metadata.rb +9 -9
- data/lib/saml/kit/locales/en.yml +4 -3
- data/lib/saml/kit/logout_request.rb +2 -2
- data/lib/saml/kit/logout_response.rb +3 -3
- data/lib/saml/kit/metadata.rb +12 -37
- data/lib/saml/kit/namespaces.rb +1 -13
- data/lib/saml/kit/respondable.rb +4 -0
- data/lib/saml/kit/response.rb +120 -37
- data/lib/saml/kit/service_provider_metadata.rb +16 -7
- data/lib/saml/kit/signature.rb +16 -13
- data/lib/saml/kit/trustable.rb +14 -6
- data/lib/saml/kit/version.rb +1 -1
- data/lib/saml/kit/xml.rb +19 -3
- data/saml-kit.gemspec +2 -2
- metadata +23 -14
- data/lib/saml/kit/binding.rb +0 -40
- data/lib/saml/kit/http_post_binding.rb +0 -27
- data/lib/saml/kit/http_redirect_binding.rb +0 -58
- 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,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
|
data/lib/saml/kit/document.rb
CHANGED
@@ -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: :
|
52
|
-
@single_sign_on_urls.push(location: url, 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: :
|
56
|
-
@logout_urls.push(location: url, 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(
|
60
|
+
Signature.sign(sign: sign) do |xml, signature|
|
61
61
|
xml.instruct!
|
62
62
|
xml.EntityDescriptor entity_descriptor_options do
|
63
|
-
signature.template(
|
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
|
data/lib/saml/kit/locales/en.yml
CHANGED
@@ -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
|
-
|
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(
|
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(
|
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: :
|
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(
|
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(
|
36
|
+
signature.template(id)
|
37
37
|
xml.Status do
|
38
38
|
xml.StatusCode Value: status_code
|
39
39
|
end
|
data/lib/saml/kit/metadata.rb
CHANGED
@@ -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
|
-
|
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::
|
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
|
-
|
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 ||=
|
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
|
data/lib/saml/kit/namespaces.rb
CHANGED
@@ -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
|
data/lib/saml/kit/respondable.rb
CHANGED
data/lib/saml/kit/response.rb
CHANGED
@@ -12,7 +12,7 @@ module Saml
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def name_id
|
15
|
-
|
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[
|
24
|
-
|
25
|
-
|
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(
|
31
|
+
parse_date(assertion.fetch('Conditions', {}).fetch('NotBefore', nil))
|
31
32
|
end
|
32
33
|
|
33
34
|
def expired_at
|
34
|
-
parse_date(
|
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(
|
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
|
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(
|
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(
|
133
|
+
signature.template(id)
|
105
134
|
xml.Status do
|
106
135
|
xml.StatusCode Value: status_code
|
107
136
|
end
|
108
|
-
xml
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
158
|
+
end
|
159
|
+
xml.Conditions conditions_options do
|
160
|
+
xml.AudienceRestriction do
|
161
|
+
xml.Audience request.issuer
|
120
162
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
163
|
+
end
|
164
|
+
xml.AuthnStatement authn_statement_options do
|
165
|
+
xml.AuthnContext do
|
166
|
+
xml.AuthnContextClassRef Namespaces::PASSWORD
|
125
167
|
end
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
142
|
-
|
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
|
-
|
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
|