saml-kit 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13c5ee20e28acd63c6ec3dde99cd1fea092be3db550dca1bd129a4b0e1fb83d3
4
- data.tar.gz: 0337c7d33845d4fbfb9dd6cc817cfde1dc620c3745cfd83b96f246e29ec30dc3
3
+ metadata.gz: b98601350af83c7090bc7fead240f7fabb4736f642a262502a96c48e533f3203
4
+ data.tar.gz: a51e398301b1115654dbe0a8a31bf61b029456c12302bb42512b7dac716d38e8
5
5
  SHA512:
6
- metadata.gz: cc09a60391991a964b3f6114e6c039da70eb917ba268787abe8940bc00a8883449f5e751aeb2365b9cae5f693d177ded5fd2d9d152ae766569a9feb5a8198ad2
7
- data.tar.gz: e6cd00b9f963348a4b9cce442d09fe7b5819c0b970334ccff01c6fcfcce54e60cfff7692878f5603a6e54061c2b75b0df61364e2eaa1717547fdc079efbe588c
6
+ metadata.gz: 1d032fb605d2c62e45e491ddde0e90274f3ae0ddab82c7e4daa72cfcc7c992483abcd5151399c67d7205bcb0de2ebe1fb1e1953fe416c9efac333e1dba86ab56
7
+ data.tar.gz: d330dc2cc0a7dfd9d7fb29e9ec9da4b9549739160f77bc6de73f1d6bd9ca15851faa595a3ccf7a75064479751b81123beaf4fb0dd6b31f5f193fbc4fd0ce182e
data/lib/saml/kit.rb CHANGED
@@ -24,6 +24,7 @@ require "saml/kit/xsd_validatable"
24
24
  require "saml/kit/respondable"
25
25
  require "saml/kit/requestable"
26
26
  require "saml/kit/trustable"
27
+ require "saml/kit/translatable"
27
28
  require "saml/kit/document"
28
29
 
29
30
  require "saml/kit/assertion"
@@ -1,7 +1,15 @@
1
1
  module Saml
2
2
  module Kit
3
3
  class Assertion
4
+ include ActiveModel::Validations
5
+ include Translatable
6
+
7
+ validate :must_match_issuer
8
+ validate :must_be_active_session
9
+ attr_reader :name
10
+
4
11
  def initialize(xml_hash, configuration:)
12
+ @name = "Assertion"
5
13
  @xml_hash = xml_hash
6
14
  @configuration = configuration
7
15
  end
@@ -11,7 +19,20 @@ module Saml
11
19
  end
12
20
 
13
21
  def signed?
14
- assertion.fetch('Signature', nil).present?
22
+ signature.present?
23
+ end
24
+
25
+ def signature
26
+ xml_hash = assertion.fetch('Signature', nil)
27
+ xml_hash ? Signature.new(xml_hash) : nil
28
+ end
29
+
30
+ def expired?
31
+ Time.current > expired_at
32
+ end
33
+
34
+ def active?
35
+ Time.current > started_at && !expired?
15
36
  end
16
37
 
17
38
  def attributes
@@ -35,10 +56,6 @@ module Saml
35
56
  parse_date(assertion.fetch('Conditions', {}).fetch('NotOnOrAfter', nil))
36
57
  end
37
58
 
38
- def certificate
39
- assertion.fetch('Signature', {}).fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
40
- end
41
-
42
59
  def audiences
43
60
  Array(assertion['Conditions']['AudienceRestriction']['Audience'])
44
61
  rescue => error
@@ -68,6 +85,17 @@ module Saml
68
85
  Saml::Kit.logger.error(error)
69
86
  Time.at(0).to_datetime
70
87
  end
88
+
89
+ def must_match_issuer
90
+ unless audiences.include?(@configuration.issuer)
91
+ errors[:audience] << error_message(:must_match_issuer)
92
+ end
93
+ end
94
+
95
+ def must_be_active_session
96
+ return if active?
97
+ errors[:base] << error_message(:expired)
98
+ end
71
99
  end
72
100
  end
73
101
  end
@@ -21,7 +21,7 @@ module Saml
21
21
  private
22
22
 
23
23
  def signature_for(payload)
24
- private_key = configuration.private_keys(use: :signing).sample
24
+ private_key = configuration.private_keys(use: :signing).last
25
25
  encode(private_key.sign(OpenSSL::Digest::SHA256.new, payload))
26
26
  end
27
27
 
@@ -8,6 +8,10 @@ module Saml
8
8
  builder(*args, &block).build
9
9
  end
10
10
 
11
+ def build_xml(*args, &block)
12
+ builder(*args, &block).to_xml
13
+ end
14
+
11
15
  def builder(*args)
12
16
  builder_class.new(*args).tap do |builder|
13
17
  yield builder if block_given?
@@ -24,7 +24,7 @@ module Saml
24
24
  def initialize(reference_id, configuration:)
25
25
  @configuration = configuration
26
26
  @reference_id = reference_id
27
- @x509_certificate = configuration.certificates(use: :signing).sample.stripped
27
+ @x509_certificate = configuration.certificates(use: :signing).last.stripped
28
28
  end
29
29
 
30
30
  def signature_method
@@ -19,11 +19,11 @@ module Saml
19
19
  end
20
20
 
21
21
  def encryption?
22
- :encryption == use
22
+ for?(:encryption)
23
23
  end
24
24
 
25
25
  def signing?
26
- :signing == use
26
+ for?(:signing)
27
27
  end
28
28
 
29
29
  def x509
@@ -2,8 +2,9 @@ module Saml
2
2
  module Kit
3
3
  class Document
4
4
  PROTOCOL_XSD = File.expand_path("./xsd/saml-schema-protocol-2.0.xsd", File.dirname(__FILE__)).freeze
5
- include XsdValidatable
6
5
  include ActiveModel::Validations
6
+ include XsdValidatable
7
+ include Translatable
7
8
  include Trustable
8
9
  include Buildable
9
10
  validates_presence_of :content
@@ -2,6 +2,8 @@
2
2
  en:
3
3
  saml/kit:
4
4
  errors:
5
+ Assertion:
6
+ expired: "must not be expired."
5
7
  AuthnRequest:
6
8
  invalid: "must contain AuthnRequest."
7
9
  invalid_fingerprint: "does not match."
@@ -17,7 +19,6 @@ en:
17
19
  LogoutResponse:
18
20
  unregistered: "is unregistered."
19
21
  Response:
20
- expired: "must not be expired."
21
22
  invalid: "must contain Response."
22
23
  invalid_fingerprint: "does not match."
23
24
  invalid_response_to: "must match request id."
@@ -1,10 +1,11 @@
1
1
  module Saml
2
2
  module Kit
3
3
  class Metadata
4
+ METADATA_XSD = File.expand_path("./xsd/saml-schema-metadata-2.0.xsd", File.dirname(__FILE__)).freeze
4
5
  include ActiveModel::Validations
5
6
  include XsdValidatable
7
+ include Translatable
6
8
  include Buildable
7
- METADATA_XSD = File.expand_path("./xsd/saml-schema-metadata-2.0.xsd", File.dirname(__FILE__)).freeze
8
9
 
9
10
  validates_presence_of :metadata
10
11
  validate :must_contain_descriptor
@@ -4,50 +4,25 @@ module Saml
4
4
  include Respondable
5
5
  extend Forwardable
6
6
 
7
- def_delegators :assertion, :name_id, :[], :attributes, :started_at, :expired_at, :audiences
7
+ def_delegators :assertion, :name_id, :[], :attributes
8
8
 
9
- validate :must_be_active_session
10
- validate :must_match_issuer
9
+ validate :must_be_valid_assertion
11
10
 
12
11
  def initialize(xml, request_id: nil, configuration: Saml::Kit.configuration)
13
12
  @request_id = request_id
14
13
  super(xml, name: "Response", configuration: configuration)
15
14
  end
16
15
 
17
- def expired?
18
- Time.current > expired_at
19
- end
20
-
21
- def active?
22
- Time.current > started_at && !expired?
23
- end
24
-
25
16
  def assertion
26
- @assertion = Saml::Kit::Assertion.new(to_h, configuration: @configuration)
27
- end
28
-
29
- def signed?
30
- super || assertion.signed?
31
- end
32
-
33
- def certificate
34
- super || assertion.certificate
17
+ @assertion ||= Saml::Kit::Assertion.new(to_h, configuration: @configuration)
35
18
  end
36
19
 
37
20
  private
38
21
 
39
- def must_be_active_session
40
- return unless expected_type?
41
- return unless success?
42
- errors[:base] << error_message(:expired) unless active?
43
- end
44
-
45
- def must_match_issuer
46
- return unless expected_type?
47
- return unless success?
48
-
49
- unless audiences.include?(configuration.issuer)
50
- errors[:audience] << error_message(:must_match_issuer)
22
+ def must_be_valid_assertion
23
+ assertion.valid?
24
+ assertion.errors.each do |attribute, error|
25
+ self.errors[attribute] << error
51
26
  end
52
27
  end
53
28
 
@@ -1,22 +1,23 @@
1
1
  module Saml
2
2
  module Kit
3
3
  class Signature
4
- attr_reader :signatures
5
- attr_reader :xml
4
+ def initialize(xml_hash)
5
+ @xml_hash = xml_hash
6
+ end
6
7
 
7
- def initialize(xml, signatures)
8
- @signatures = signatures
9
- @xml = xml
8
+ def certificate
9
+ value = to_h.fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
10
+ return if value.nil?
11
+ Saml::Kit::Certificate.new(value, use: :signing)
10
12
  end
11
13
 
12
- def template(reference_id)
13
- Template.new(signatures.build(reference_id)).to_xml(xml: xml)
14
+ def trusted?(metadata)
15
+ return false if metadata.nil?
16
+ metadata.matches?(certificate.fingerprint, use: :signing)
14
17
  end
15
18
 
16
- def self.sign(xml: ::Builder::XmlMarkup.new, configuration: Saml::Kit.configuration)
17
- signatures = Saml::Kit::Signatures.new(configuration: configuration)
18
- yield xml, new(xml, signatures)
19
- signatures.complete(xml.target!)
19
+ def to_h
20
+ @xml_hash
20
21
  end
21
22
  end
22
23
  end
@@ -14,9 +14,28 @@ module Saml
14
14
 
15
15
  def complete(raw_xml)
16
16
  return raw_xml unless configuration.sign?
17
- private_key = configuration.private_keys(use: :signing).sample
17
+ private_key = configuration.private_keys(use: :signing).last
18
18
  Xmldsig::SignedDocument.new(raw_xml).sign(private_key)
19
19
  end
20
+
21
+ def self.sign(xml: ::Builder::XmlMarkup.new, configuration: Saml::Kit.configuration)
22
+ signatures = Saml::Kit::Signatures.new(configuration: configuration)
23
+ yield xml, XmlSignatureTemplate.new(xml, signatures)
24
+ signatures.complete(xml.target!)
25
+ end
26
+
27
+ class XmlSignatureTemplate
28
+ attr_reader :signatures, :xml
29
+
30
+ def initialize(xml, signatures)
31
+ @signatures = signatures
32
+ @xml = xml
33
+ end
34
+
35
+ def template(reference_id)
36
+ Template.new(signatures.build(reference_id)).to_xml(xml: xml)
37
+ end
38
+ end
20
39
  end
21
40
  end
22
41
  end
@@ -21,7 +21,7 @@ module Saml
21
21
  end
22
22
 
23
23
  def encryption_for(xml:)
24
- if encrypt && encryption_certificate
24
+ if encrypt?
25
25
  temp = ::Builder::XmlMarkup.new
26
26
  yield temp
27
27
  xml_encryption = Saml::Kit::Builders::XmlEncryption.new(temp.target!, encryption_certificate.public_key)
@@ -31,6 +31,10 @@ module Saml
31
31
  end
32
32
  end
33
33
 
34
+ def encrypt?
35
+ encrypt && encryption_certificate
36
+ end
37
+
34
38
  def render(model, options)
35
39
  Saml::Kit::Template.new(model).to_xml(options)
36
40
  end
@@ -0,0 +1,9 @@
1
+ module Saml
2
+ module Kit
3
+ module Translatable
4
+ def error_message(attribute, type: :invalid)
5
+ I18n.translate(attribute, scope: "saml/kit.errors.#{name}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -9,24 +9,18 @@ module Saml
9
9
  validate :must_be_trusted, unless: :signature_manually_verified
10
10
  end
11
11
 
12
- def certificate
13
- return unless signed?
14
- to_h.fetch(name, {}).fetch('Signature', {}).fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
15
- end
16
-
17
- def fingerprint
18
- return if certificate.blank?
19
- Fingerprint.new(certificate)
12
+ def signed?
13
+ signature.present?
20
14
  end
21
15
 
22
- def signed?
23
- to_h.fetch(name, {}).fetch('Signature', nil).present?
16
+ def signature
17
+ xml_hash = to_h.fetch(name, {}).fetch('Signature', nil)
18
+ xml_hash ? Signature.new(xml_hash) : nil
24
19
  end
25
20
 
26
21
  def trusted?
27
- return false if provider.nil?
28
22
  return false unless signed?
29
- provider.matches?(fingerprint, use: :signing)
23
+ signature.trusted?(provider)
30
24
  end
31
25
 
32
26
  def provider
@@ -59,6 +53,7 @@ module Saml
59
53
 
60
54
  def must_be_trusted
61
55
  return if trusted?
56
+ return if provider.present? && !signed?
62
57
  errors[:fingerprint] << error_message(:invalid_fingerprint)
63
58
  end
64
59
  end
@@ -1,5 +1,5 @@
1
1
  module Saml
2
2
  module Kit
3
- VERSION = "0.2.4"
3
+ VERSION = "0.2.5"
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ module Saml
4
4
  attr_reader :private_key
5
5
 
6
6
  def initialize(configuration: Saml::Kit.configuration)
7
- @private_key = configuration.private_keys(use: :encryption).sample
7
+ @private_key = configuration.private_keys(use: :encryption).last
8
8
  end
9
9
 
10
10
  def decrypt(data)
@@ -10,10 +10,6 @@ module Saml
10
10
  end
11
11
  end
12
12
  end
13
-
14
- def error_message(key)
15
- I18n.translate(key, scope: "saml/kit.errors.#{name}")
16
- end
17
13
  end
18
14
  end
19
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saml-kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
@@ -246,6 +246,7 @@ files:
246
246
  - lib/saml/kit/signatures.rb
247
247
  - lib/saml/kit/templatable.rb
248
248
  - lib/saml/kit/template.rb
249
+ - lib/saml/kit/translatable.rb
249
250
  - lib/saml/kit/trustable.rb
250
251
  - lib/saml/kit/version.rb
251
252
  - lib/saml/kit/xml.rb