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.
- 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
|