spid 0.16.1 → 0.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c1de73229206a3f51acebe2ffd176336baf6ecf7b7ab56587f2baa4338f5300
4
- data.tar.gz: 5c39d1327255aa6f6c91f8b893ff8d0698bcd2e8476a6fb55fc85cd2cd507c6b
3
+ metadata.gz: 8c918415822f2873b617da91c797c8987825252578e359c620a334ad9ba439d3
4
+ data.tar.gz: 2e699735bcefc0e37ed9efced5ed53ad845a29405b4c1616ec4985bcd497a346
5
5
  SHA512:
6
- metadata.gz: 8861c1b4c9d517f73dee139742e186e1cd96a10267ec9d73ed2987426bd65fc9832114ad41f34b00bcad05595d17e760602eb894344ff8d77f16309dda829757
7
- data.tar.gz: aace38dd40188f84b5dabc3d4f33dbaabe0005e81f4fa47a2777d437e9032112ab25fd6b981dbd99424017349eabbcf86e0b715582c0cb626c9aa0887338f7e0
6
+ metadata.gz: 2e80c2322252a27a0362c738a3eace0fdba76735026fe0a7dd344bbf25b4543d2a014724301915d87a97fb517399d3bf0695e792141d90b95d1e5df3d82ca40c
7
+ data.tar.gz: 9fd3aecb75d5a2dad2e6464532c774fd40f2737eecc471f693aabdabf688ca9c5644a6fa3f8c29ea8576076570616d0a402a389f5922f403f8e0c67087330709
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.17.0] - 2018-09-10
6
+ ### Added
7
+ - Ensure private key lenght and certificate validation
8
+ - Previous SPID specification use sp_entity_id for /samlp:Response/@Destination
9
+ - Validate Subject attributes
10
+ - Validate Issuer and Assertion/Issuer
11
+
5
12
  ## [0.16.1] - 2018-09-05
6
13
  ### Changed
7
14
  - Use login_path and logout_path instead of start_sso_path and start_slo_path
@@ -127,8 +134,9 @@
127
134
  - Coveralls Integration
128
135
  - Rubygems version badge in README
129
136
 
130
- [Unreleased]: https://github.com/italia/spid-ruby/compare/v0.16.1...HEAD
131
- [0.16.0]: https://github.com/italia/spid-ruby/compare/v0.16.0...v0.16.1
137
+ [Unreleased]: https://github.com/italia/spid-ruby/compare/v0.17.0...HEAD
138
+ [0.17.0]: https://github.com/italia/spid-ruby/compare/v0.16.1...v0.17.0
139
+ [0.16.1]: https://github.com/italia/spid-ruby/compare/v0.16.0...v0.16.1
132
140
  [0.16.0]: https://github.com/italia/spid-ruby/compare/v0.15.2...v0.16.0
133
141
  [0.15.2]: https://github.com/italia/spid-ruby/compare/v0.15.1...v0.15.2
134
142
  [0.15.1]: https://github.com/italia/spid-ruby/compare/v0.15.0...v0.15.1
data/lib/spid.rb CHANGED
@@ -18,6 +18,8 @@ module Spid # :nodoc:
18
18
  class UnknownSignatureMethodError < StandardError; end
19
19
  class UnknownAttributeFieldError < StandardError; end
20
20
  class MissingAttributeServicesError < StandardError; end
21
+ class PrivateKeyTooShortError < StandardError; end
22
+ class CertificateNotBelongsToPKeyError < StandardError; end
21
23
 
22
24
  EXACT_COMPARISON = :exact
23
25
  MINIMUM_COMPARISON = :minimum
data/lib/spid/saml2.rb CHANGED
@@ -4,6 +4,7 @@ require "spid/saml2/service_provider"
4
4
  require "spid/saml2/identity_provider"
5
5
  require "spid/saml2/settings"
6
6
  require "spid/saml2/authn_request"
7
+ require "spid/saml2/saml_parser"
7
8
  require "spid/saml2/response"
8
9
  require "spid/saml2/logout_request"
9
10
  require "spid/saml2/idp_logout_request"
@@ -2,51 +2,37 @@
2
2
 
3
3
  module Spid
4
4
  module Saml2
5
- class IdpLogoutRequest # :nodoc:
6
- attr_reader :saml_message
7
- attr_reader :document
8
-
9
- def initialize(saml_message:)
10
- @saml_message = saml_message
11
- @document = REXML::Document.new(@saml_message)
12
- end
13
-
5
+ class IdpLogoutRequest < SamlParser # :nodoc:
14
6
  def id
15
- document.elements["/samlp:LogoutRequest/@ID"]&.value
7
+ element_from_xpath("/samlp:LogoutRequest/@ID")
16
8
  end
17
9
 
18
10
  def destination
19
- document.elements["/samlp:LogoutRequest/@Destination"]&.value
11
+ element_from_xpath("/samlp:LogoutRequest/@Destination")
20
12
  end
21
13
 
22
14
  def issue_instant
23
- document.elements["/samlp:LogoutRequest/@IssueInstant"]&.value
15
+ element_from_xpath("/samlp:LogoutRequest/@IssueInstant")
24
16
  end
25
17
 
26
18
  def issuer_name_qualifier
27
- document.elements[
28
- "/samlp:LogoutRequest/saml:Issuer/@NameQualifier"
29
- ]&.value
19
+ element_from_xpath("/samlp:LogoutRequest/saml:Issuer/@NameQualifier")
30
20
  end
31
21
 
32
22
  def name_id
33
- document.elements["/samlp:LogoutRequest/saml:NameID/text()"]&.value
23
+ element_from_xpath("/samlp:LogoutRequest/saml:NameID/text()")
34
24
  end
35
25
 
36
26
  def name_id_name_qualifier
37
- document.elements[
38
- "/samlp:LogoutRequest/saml:NameID/@NameQualifier"
39
- ]&.value
27
+ element_from_xpath("/samlp:LogoutRequest/saml:NameID/@NameQualifier")
40
28
  end
41
29
 
42
30
  def issuer
43
- document.elements["/samlp:LogoutRequest/saml:Issuer/text()"]&.value
31
+ element_from_xpath("/samlp:LogoutRequest/saml:Issuer/text()")
44
32
  end
45
33
 
46
34
  def session_index
47
- document.elements[
48
- "/samlp:LogoutRequest/saml:SessionIndex/text()"
49
- ]&.value
35
+ element_from_xpath("/samlp:LogoutRequest/saml:SessionIndex/text()")
50
36
  end
51
37
  end
52
38
  end
@@ -4,35 +4,17 @@ require "spid/saml2/utils"
4
4
 
5
5
  module Spid
6
6
  module Saml2
7
- class LogoutResponse # :nodoc:
8
- include Spid::Saml2::Utils
9
-
10
- attr_reader :body
11
- attr_reader :saml_message
12
- attr_reader :document
13
-
14
- def initialize(body:)
15
- @body = body
16
- @saml_message = decode_and_inflate(body)
17
- @document = REXML::Document.new(@saml_message)
18
- end
19
-
7
+ class LogoutResponse < SamlParser # :nodoc:
20
8
  def issuer
21
- document.elements[
22
- "/samlp:LogoutResponse/saml:Issuer/text()"
23
- ]&.value&.strip
9
+ element_from_xpath("/samlp:LogoutResponse/saml:Issuer/text()")
24
10
  end
25
11
 
26
12
  def in_response_to
27
- document.elements[
28
- "/samlp:LogoutResponse/@InResponseTo"
29
- ]&.value&.strip
13
+ element_from_xpath("/samlp:LogoutResponse/@InResponseTo")
30
14
  end
31
15
 
32
16
  def destination
33
- document.elements[
34
- "/samlp:LogoutResponse/@Destination"
35
- ]&.value
17
+ element_from_xpath("/samlp:LogoutResponse/@Destination")
36
18
  end
37
19
  end
38
20
  end
@@ -1,20 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spid/saml2/utils"
4
-
5
3
  module Spid
6
4
  module Saml2
7
- class Response # :nodoc:
8
- include Spid::Saml2::Utils
9
-
10
- attr_reader :saml_message
11
- attr_reader :document
12
-
13
- def initialize(saml_message:)
14
- @saml_message = saml_message
15
- @document = REXML::Document.new(@saml_message)
16
- end
17
-
5
+ class Response < SamlParser # :nodoc:
18
6
  def issuer
19
7
  document.elements["/samlp:Response/saml:Issuer/text()"]&.value
20
8
  end
@@ -24,15 +12,16 @@ module Spid
24
12
  end
25
13
 
26
14
  def name_id
27
- document.elements[
15
+ element_from_xpath(
28
16
  "/samlp:Response/saml:Assertion/saml:Subject/saml:NameID/text()"
29
- ]&.value
17
+ )
30
18
  end
31
19
 
32
20
  def raw_certificate
33
- xpath = "/samlp:Response/saml:Assertion/ds:Signature/ds:KeyInfo"
34
- xpath = "#{xpath}/ds:X509Data/ds:X509Certificate/text()"
35
- document.elements[xpath]&.value
21
+ element_from_xpath(
22
+ "/samlp:Response/saml:Assertion/ds:Signature/ds:KeyInfo" \
23
+ "/ds:X509Data/ds:X509Certificate/text()"
24
+ )
36
25
  end
37
26
 
38
27
  def certificate
@@ -40,59 +29,77 @@ module Spid
40
29
  end
41
30
 
42
31
  def assertion_issuer
43
- document.elements[
44
- "/samlp:Response/saml:Assertion/saml:Issuer/text()"
45
- ]&.value
32
+ element_from_xpath("/samlp:Response/saml:Assertion/saml:Issuer/text()")
46
33
  end
47
34
 
48
35
  def session_index
49
- document.elements[
36
+ element_from_xpath(
50
37
  "/samlp:Response/saml:Assertion/saml:AuthnStatement/@SessionIndex"
51
- ]&.value
38
+ )
52
39
  end
53
40
 
54
41
  def destination
55
- document.elements[
56
- "/samlp:Response/@Destination"
57
- ]&.value
42
+ element_from_xpath("/samlp:Response/@Destination")
58
43
  end
59
44
 
60
45
  def conditions_not_before
61
- document.elements[
46
+ element_from_xpath(
62
47
  "/samlp:Response/saml:Assertion/saml:Conditions/@NotBefore"
63
- ]&.value
48
+ )
64
49
  end
65
50
 
66
51
  def conditions_not_on_or_after
67
- document.elements[
52
+ element_from_xpath(
68
53
  "/samlp:Response/saml:Assertion/saml:Conditions/@NotOnOrAfter"
69
- ]&.value
54
+ )
70
55
  end
71
56
 
72
57
  def audience
73
- xpath = "/samlp:Response/saml:Assertion/saml:Conditions"
74
- xpath = "#{xpath}/saml:AudienceRestriction/saml:Audience/text()"
75
- document.elements[xpath]&.value
58
+ element_from_xpath(
59
+ "/samlp:Response/saml:Assertion/saml:Conditions" \
60
+ "/saml:AudienceRestriction/saml:Audience/text()"
61
+ )
62
+ end
63
+
64
+ def subject_confirmation_data_node_xpath
65
+ xpath = "/samlp:Response/saml:Assertion/saml:Subject/"
66
+ "#{xpath}/saml:SubjectConfirmation/saml:SubjectConfirmationData"
67
+ end
68
+
69
+ def subject_recipient
70
+ element_from_xpath("#{subject_confirmation_data_node_xpath}/@Recipient")
71
+ end
72
+
73
+ def subject_in_response_to
74
+ element_from_xpath(
75
+ "#{subject_confirmation_data_node_xpath}/@InResponseTo"
76
+ )
77
+ end
78
+
79
+ def subject_not_on_or_after
80
+ element_from_xpath(
81
+ "#{subject_confirmation_data_node_xpath}/@NotOnOrAfter"
82
+ )
76
83
  end
77
84
 
78
85
  def status_code
79
- document.elements[
86
+ element_from_xpath(
80
87
  "/samlp:Response/samlp:Status/samlp:StatusCode/@Value"
81
- ]&.value
88
+ )
82
89
  end
83
90
 
84
91
  def status_message
85
- document.elements[
86
- "/samlp:Response/samlp:Status/samlp:StatusCode/" \
87
- "samlp:StatusMessage/@Value"
88
- ]&.value
92
+ element_from_xpath(
93
+ "samlp:StatusMessage/@Value" \
94
+ "/samlp:Response/samlp:Status/samlp:StatusCode/"
95
+ )
89
96
  end
90
97
 
91
98
  def status_detail
92
- document.elements[
99
+ element_from_xpath(
93
100
  "/samlp:Response/samlp:Status/samlp:StatusCode/" \
94
101
  "samlp:StatusDetail/@Value"
95
- ]&.value
102
+ )
96
103
  end
97
104
 
98
105
  def attributes
@@ -4,6 +4,7 @@ require "xmldsig"
4
4
 
5
5
  module Spid
6
6
  module Saml2
7
+ # rubocop:disable Metrics/ClassLength
7
8
  class ResponseValidator # :nodoc:
8
9
  attr_reader :response
9
10
  attr_reader :settings
@@ -20,13 +21,8 @@ module Spid
20
21
  def call
21
22
  return false unless success?
22
23
  [
23
- matches_request_uuid,
24
- issuer,
25
- certificate,
26
- destination,
27
- conditions,
28
- audience,
29
- signature
24
+ matches_request_uuid, issuer, assertion_issuer, certificate,
25
+ destination, conditions, audience, signature
30
26
  ].all?
31
27
  end
32
28
 
@@ -50,11 +46,22 @@ module Spid
50
46
  end
51
47
 
52
48
  def issuer
53
- return true if response.assertion_issuer == settings.idp_entity_id
49
+ return true if response.issuer == settings.idp_entity_id
54
50
 
55
51
  @errors["issuer"] =
56
52
  begin
57
- "Response Issuer is '#{response.assertion_issuer}'" \
53
+ "Response Issuer is '#{response.issuer}'" \
54
+ " but was expected '#{settings.idp_entity_id}'"
55
+ end
56
+ false
57
+ end
58
+
59
+ def assertion_issuer
60
+ return true if response.assertion_issuer == settings.idp_entity_id
61
+
62
+ @errors["assertion_issuer"] =
63
+ begin
64
+ "Response Assertion Issuer is '#{response.assertion_issuer}'" \
58
65
  " but was expected '#{settings.idp_entity_id}'"
59
66
  end
60
67
  false
@@ -71,6 +78,7 @@ module Spid
71
78
 
72
79
  def destination
73
80
  return true if response.destination == settings.sp_acs_url
81
+ return true if response.destination == settings.sp_entity_id
74
82
 
75
83
  @errors["destination"] =
76
84
  begin
@@ -85,6 +93,7 @@ module Spid
85
93
 
86
94
  if response.conditions_not_before <= time &&
87
95
  response.conditions_not_on_or_after > time
96
+
88
97
  return true
89
98
  end
90
99
 
@@ -109,6 +118,21 @@ module Spid
109
118
  @errors["signature"] = "Signature mismatch"
110
119
  false
111
120
  end
121
+
122
+ def subject_recipient
123
+ return true if response.subject_recipient == settings.sp_acs_url
124
+ end
125
+
126
+ def subject_in_response_to
127
+ return true if response.subject_in_response_to == request_uuid
128
+ end
129
+
130
+ def subject_not_on_or_after
131
+ time = Time.now.utc.iso8601
132
+
133
+ return true if response.subject_not_on_or_after > time
134
+ end
112
135
  end
136
+ # rubocop:enable Metrics/ClassLength
113
137
  end
114
138
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spid/saml2/utils"
4
+
5
+ module Spid
6
+ module Saml2
7
+ class SamlParser # :nodoc:
8
+ include Spid::Saml2::Utils
9
+
10
+ attr_reader :saml_message
11
+ attr_reader :document
12
+
13
+ def initialize(saml_message:)
14
+ @saml_message = saml_message
15
+ @document = REXML::Document.new(@saml_message)
16
+ end
17
+
18
+ def element_from_xpath(xpath)
19
+ document.elements[xpath]&.value&.strip
20
+ end
21
+ end
22
+ end
23
+ end
@@ -45,6 +45,8 @@ module Spid
45
45
  @attribute_services = attribute_services
46
46
  validate_digest_methods
47
47
  validate_attributes
48
+ validate_private_key
49
+ validate_certificate
48
50
  end
49
51
  # rubocop:enable Metrics/MethodLength
50
52
  # rubocop:enable Metrics/ParameterLists
@@ -92,6 +94,19 @@ module Spid
92
94
  " use one of #{SIGNATURE_METHODS.join(', ')}"
93
95
  end
94
96
  end
97
+
98
+ def validate_private_key
99
+ return true if private_key.n.num_bits >= 1024
100
+ raise PrivateKeyTooShortError,
101
+ "Private key is too short: provide at least a " \
102
+ " private key with 1024 bits"
103
+ end
104
+
105
+ def validate_certificate
106
+ return true if certificate.verify(private_key)
107
+ raise CertificateNotBelongsToPKeyError,
108
+ "Provided a certificate signed with current private key"
109
+ end
95
110
  end
96
111
  end
97
112
  end
@@ -91,8 +91,7 @@ module Spid
91
91
  def x509_certificate_der
92
92
  @x509_certificate_der ||=
93
93
  begin
94
- cert = OpenSSL::X509::Certificate.new(certificate)
95
- Base64.encode64(cert.to_der).delete("\n")
94
+ Base64.encode64(certificate.to_der).delete("\n")
96
95
  end
97
96
  end
98
97
  end
@@ -6,12 +6,10 @@ module Spid
6
6
  class SPMetadata # :nodoc:
7
7
  attr_reader :document
8
8
  attr_reader :settings
9
- attr_reader :uuid
10
9
 
11
- def initialize(settings:, uuid: nil)
10
+ def initialize(settings:)
12
11
  @document = REXML::Document.new
13
12
  @settings = settings
14
- @uuid = uuid || SecureRandom.uuid
15
13
  end
16
14
 
17
15
  def to_saml
@@ -34,7 +32,7 @@ module Spid
34
32
  "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#",
35
33
  "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata",
36
34
  "entityID" => settings.sp_entity_id,
37
- "ID" => "_#{uuid}"
35
+ "ID" => settings.sp_entity_id
38
36
  }
39
37
  end
40
38
 
@@ -3,14 +3,18 @@
3
3
  module Spid
4
4
  module Slo
5
5
  class Response # :nodoc:
6
+ include Spid::Saml2::Utils
7
+
6
8
  attr_reader :body
7
9
  attr_reader :session_index
8
10
  attr_reader :request_uuid
11
+ attr_reader :saml_message
9
12
 
10
13
  def initialize(body:, session_index:, request_uuid:)
11
14
  @body = body
12
15
  @session_index = session_index
13
16
  @request_uuid = request_uuid
17
+ @saml_message = decode_and_inflate(body)
14
18
  end
15
19
 
16
20
  def valid?
@@ -59,7 +63,7 @@ module Spid
59
63
 
60
64
  def saml_response
61
65
  @saml_response ||= Spid::Saml2::LogoutResponse.new(
62
- body: body
66
+ saml_message: saml_message
63
67
  )
64
68
  end
65
69
  end
data/lib/spid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spid
4
- VERSION = "0.16.1"
4
+ VERSION = "0.17.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Librera
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-05 00:00:00.000000000 Z
11
+ date: 2018-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -343,6 +343,7 @@ files:
343
343
  - lib/spid/saml2/logout_response_validator.rb
344
344
  - lib/spid/saml2/response.rb
345
345
  - lib/spid/saml2/response_validator.rb
346
+ - lib/spid/saml2/saml_parser.rb
346
347
  - lib/spid/saml2/service_provider.rb
347
348
  - lib/spid/saml2/settings.rb
348
349
  - lib/spid/saml2/sp_metadata.rb