spid 0.10.0 → 0.11.0

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: 960e16fa23660bf794d8569793c49f01671883612e6e96c56d9cc9ec7584c887
4
- data.tar.gz: 18aa49e2bd0173eb7aaae0141fd199c73fc97f4f9538e04e998fd459f4406e1f
3
+ metadata.gz: 8d65f97b5bd606942de9c2bba4a7fe60fadb90de3bf8526d8c80bec3a2ce29ff
4
+ data.tar.gz: 72d85f52db6688edb9f90f3176af9889b15052b8bc7a45b3eee1c61e269cb825
5
5
  SHA512:
6
- metadata.gz: 294072c36735dd72f29e0b66a5d8df0118637079223faa7a9f8b71778cd2b871b639174267b775c6505d1723d1b5e4126b4e243941a6a6f9cecdf4a028c87394
7
- data.tar.gz: 18525e552a24740ad2f51bc73f7d7e96c1f1ab583ea96372c04f6560a6df8efe7af2cb802b0631e68b61594e010f9c6790a40d8f0b17b5575207ce4fafe5bb83
6
+ metadata.gz: 7c0ed6e05c1b45a9a395ea4f4c41c6b45279adc712546bd167ed17a0a3dfba4f28367b287394c22078e95abe561253903153e253a1a967ac4adcf669393a0448
7
+ data.tar.gz: a8f863472e7f5dcd85abbdb514b3aa8509ac42d7eab1c76eac53aa5eab9af5ae4bef44d159855a0d4cdb609f085a58d878299390243cfde3bb9d9754d96ed316
@@ -3,6 +3,7 @@ require: rubocop-rspec
3
3
  AllCops:
4
4
  Exclude:
5
5
  - bin/stubs/*
6
+ - lib/spid/saml2/idp_metadata_parser.rb
6
7
  TargetRubyVersion: 2.3
7
8
 
8
9
  Layout/DotPosition:
@@ -22,6 +23,8 @@ RSpec/FilePath:
22
23
  Exclude:
23
24
  - spec/integration/**/*.rb
24
25
  - spec/requests/**/*.rb
26
+ RSpec/MultipleExpectations:
27
+ Max: 2
25
28
  RSpec/NestedGroups:
26
29
  Enabled: false
27
30
  RSpec/SubjectStub:
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.11.0] - 2018-08-23
6
+ ### Changed
7
+ - Use custom Saml2 library instead of ruby-saml gem
8
+
5
9
  ## [0.10.0] - 2018-08-02
6
10
  ### Added
7
11
  - Handled Relay State in sso/slo responses
@@ -89,7 +93,8 @@
89
93
  - Coveralls Integration
90
94
  - Rubygems version badge in README
91
95
 
92
- [Unreleased]: https://github.com/italia/spid-ruby/compare/v0.10.0...HEAD
96
+ [Unreleased]: https://github.com/italia/spid-ruby/compare/v0.11.0...HEAD
97
+ [0.11.0]: https://github.com/italia/spid-ruby/compare/v0.10.0...v0.11.0
93
98
  [0.10.0]: https://github.com/italia/spid-ruby/compare/v0.9.0...v0.10.0
94
99
  [0.9.0]: https://github.com/italia/spid-ruby/compare/v0.8.0...v0.9.0
95
100
  [0.8.0]: https://github.com/italia/spid-ruby/compare/v0.7.0...v0.8.0
data/Gemfile CHANGED
@@ -2,11 +2,5 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
-
7
5
  # Specify your gem's dependencies in spid.gemspec
8
6
  gemspec
9
-
10
- gem "ruby-saml",
11
- github: "davidlibrera/ruby-saml",
12
- ref: "4204afc2439f712db8909bccfd8d6e15f9320c34"
data/README.md CHANGED
@@ -17,9 +17,6 @@ Ruby library for SPID authentication
17
17
  Add into your Gemfile
18
18
 
19
19
  ```ruby
20
- gem "ruby-saml",
21
- github: "davidlibrera/ruby-saml",
22
- ref: "571e280031883abe3aad7c5b856353f94354fd06"
23
20
  gem "spid"
24
21
  ```
25
22
 
@@ -29,19 +26,19 @@ gem "spid"
29
26
  |<img src="https://github.com/italia/spid-graphics/blob/master/spid-logos/spid-logo-c-lb.png?raw=true" width="100" /><br />_Compliance with [SPID regulations](http://www.agid.gov.it/sites/default/files/circolari/spid-regole_tecniche_v1.pdf) (for Service Providers)_||
30
27
  |:---|:---|
31
28
  |**Metadata:**||
32
- |parsing of IdP XML metadata (1.2.2.4)||
29
+ |parsing of IdP XML metadata (1.2.2.4)|✓|
33
30
  |parsing of AA XML metadata (2.2.4)||
34
- |SP XML metadata generation (1.3.2)||
31
+ |SP XML metadata generation (1.3.2)|✓|
35
32
  |**AuthnRequest generation (1.2.2.1):**||
36
33
  |generation of AuthnRequest XML|✓|
37
- |HTTP-Redirect binding||
38
- |HTTP-POST binding|✓|
39
- |`AssertionConsumerServiceURL` customization||
34
+ |HTTP-Redirect binding|✓|
35
+ |HTTP-POST binding||
36
+ |`AssertionConsumerServiceURL` customization|✓|
40
37
  |`AssertionConsumerServiceIndex` customization||
41
38
  |`AttributeConsumingServiceIndex` customization||
42
39
  |`AuthnContextClassRef` (SPID level) customization|✓|
43
- |`RequestedAuthnContext/@Comparison` customization|✓|
44
- |`RelayState` customization (1.2.2)||
40
+ |`RequestedAuthnContext/@Comparison` customization||
41
+ |`RelayState` customization (1.2.2)|✓|
45
42
  |**Response/Assertion parsing**||
46
43
  |verification of `Response/Signature` value (if any)||
47
44
  |verification of `Response/Signature` certificate (if any) against IdP/AA metadata||
@@ -62,13 +59,13 @@ gem "spid"
62
59
  |parsing of `AuthnContextClassRef` (SPID level)||
63
60
  |parsing of attributes||
64
61
  |**Response/Assertion parsing for attribute query (2.2.2.2, 2.3.1):**||
65
- |parsing of attributes||
62
+ |parsing of attributes|✓|
66
63
  |**LogoutRequest generation (for SP-initiated logout):**||
67
- |generation of LogoutRequest XML||
68
- |HTTP-Redirect binding||
64
+ |generation of LogoutRequest XML|✓|
65
+ |HTTP-Redirect binding|✓|
69
66
  |HTTP-POST binding||
70
67
  |**LogoutResponse parsing (for SP-initiated logout):**||
71
- |parsing of LogoutResponse XML||
68
+ |parsing of LogoutResponse XML|✓|
72
69
  |verification of `Response/Signature` value (if any)||
73
70
  |verification of `Response/Signature` certificate (if any) against IdP metadata||
74
71
  |verification of `Issuer`||
@@ -1,15 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "spid/authn_request"
4
- require "spid/logout_request"
3
+ require "spid/saml2"
5
4
  require "spid/sso"
6
5
  require "spid/slo"
7
6
  require "spid/rack"
8
7
  require "spid/metadata"
9
8
  require "spid/version"
10
9
  require "spid/configuration"
11
- require "spid/identity_provider"
12
- require "spid/service_provider"
13
10
  require "spid/identity_provider_manager"
14
11
 
15
12
  module Spid # :nodoc:
@@ -33,9 +30,9 @@ module Spid # :nodoc:
33
30
  MAXIMUM_COMPARISON
34
31
  ].freeze
35
32
 
36
- SHA256 = XMLSecurity::Document::SHA256
37
- SHA384 = XMLSecurity::Document::SHA384
38
- SHA512 = XMLSecurity::Document::SHA512
33
+ SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"
34
+ SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
35
+ SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512"
39
36
 
40
37
  DIGEST_METHODS = [
41
38
  SHA256,
@@ -43,9 +40,9 @@ module Spid # :nodoc:
43
40
  SHA512
44
41
  ].freeze
45
42
 
46
- RSA_SHA256 = XMLSecurity::Document::RSA_SHA256
47
- RSA_SHA384 = XMLSecurity::Document::RSA_SHA384
48
- RSA_SHA512 = XMLSecurity::Document::RSA_SHA512
43
+ RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
44
+ RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
45
+ RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
49
46
 
50
47
  SIGNATURE_METHODS = [
51
48
  RSA_SHA256,
@@ -53,6 +50,15 @@ module Spid # :nodoc:
53
50
  RSA_SHA512
54
51
  ].freeze
55
52
 
53
+ SIGNATURE_ALGORITHMS = {
54
+ SHA256 => OpenSSL::Digest::SHA256.new,
55
+ SHA384 => OpenSSL::Digest::SHA384.new,
56
+ SHA512 => OpenSSL::Digest::SHA512.new,
57
+ RSA_SHA256 => OpenSSL::Digest::SHA256.new,
58
+ RSA_SHA384 => OpenSSL::Digest::SHA384.new,
59
+ RSA_SHA512 => OpenSSL::Digest::SHA512.new
60
+ }.freeze
61
+
56
62
  L1 = "https://www.spid.gov.it/SpidL1"
57
63
  L2 = "https://www.spid.gov.it/SpidL2"
58
64
  L3 = "https://www.spid.gov.it/SpidL3"
@@ -18,43 +18,51 @@ module Spid
18
18
  attr_accessor :acs_binding
19
19
  attr_accessor :slo_binding
20
20
 
21
- # rubocop:disable Metrics/MethodLength
22
21
  def initialize
23
22
  @idp_metadata_dir_path = "idp_metadata"
23
+ @attribute_service_name = nil
24
+ init_endpoint
25
+ init_bindings
26
+ init_dig_sig_methods
27
+ init_openssl_keys
28
+ end
29
+
30
+ def init_endpoint
31
+ @hostname = nil
24
32
  @metadata_path = "/spid/metadata"
25
33
  @start_sso_path = "/spid/login"
26
34
  @start_slo_path = "/spid/logout"
27
35
  @acs_path = "/spid/sso"
28
36
  @slo_path = "/spid/slo"
37
+ @default_relay_state_path = "/"
38
+ end
39
+
40
+ def init_bindings
41
+ @acs_binding = Spid::BINDINGS_HTTP_POST
42
+ @slo_binding = Spid::BINDINGS_HTTP_REDIRECT
43
+ end
44
+
45
+ def init_dig_sig_methods
29
46
  @digest_method = Spid::SHA256
30
47
  @signature_method = Spid::RSA_SHA256
31
- @attribute_service_name = nil
32
- @hostname = nil
48
+ end
49
+
50
+ def init_openssl_keys
33
51
  @private_key = nil
34
52
  @certificate = nil
35
- @default_relay_state_path = "/"
36
- @acs_binding = Spid::BINDINGS_HTTP_POST
37
- @slo_binding = Spid::BINDINGS_HTTP_REDIRECT
38
53
  end
39
- # rubocop:enable Metrics/MethodLength
40
54
 
41
- # rubocop:disable Metrics/MethodLength
42
55
  def service_provider
43
56
  @service_provider ||=
44
57
  begin
45
- Spid::ServiceProvider.new(
46
- host: hostname,
47
- acs_path: acs_path,
48
- slo_path: slo_path,
49
- metadata_path: metadata_path,
50
- private_key: private_key,
51
- certificate: certificate,
52
- digest_method: digest_method,
53
- signature_method: signature_method,
54
- attribute_service_name: attribute_service_name
58
+ Spid::Saml2::ServiceProvider.new(
59
+ acs_binding: acs_binding, acs_path: acs_path, slo_path: slo_path,
60
+ slo_binding: slo_binding, metadata_path: metadata_path,
61
+ private_key: private_key, certificate: certificate,
62
+ digest_method: digest_method, signature_method: signature_method,
63
+ attribute_service_name: attribute_service_name, host: hostname
55
64
  )
56
65
  end
57
66
  end
58
- # rubocop:enable Metrics/MethodLength
59
67
  end
60
68
  end
@@ -9,7 +9,7 @@ module Spid
9
9
  begin
10
10
  Dir.chdir(Spid.configuration.idp_metadata_dir_path) do
11
11
  Dir.glob("*.xml").map do |metadata_filepath|
12
- generate_identity_provider_from_file(
12
+ self.class.generate_identity_provider_from_file(
13
13
  File.expand_path(metadata_filepath)
14
14
  )
15
15
  end
@@ -29,12 +29,22 @@ module Spid
29
29
  end
30
30
  end
31
31
 
32
- private
32
+ def self.parse_from_xml(name:, metadata:)
33
+ idp_metadata_parser = ::Spid::Saml2::IdpMetadataParser.new
34
+ idp_settings = idp_metadata_parser.parse_to_hash(metadata)
35
+ ::Spid::Saml2::IdentityProvider.new(
36
+ name: name,
37
+ entity_id: idp_settings[:idp_entity_id],
38
+ sso_target_url: idp_settings[:idp_sso_target_url],
39
+ slo_target_url: idp_settings[:idp_slo_target_url],
40
+ cert_fingerprint: idp_settings[:idp_cert_fingerprint]
41
+ )
42
+ end
33
43
 
34
- def generate_identity_provider_from_file(metadata_filepath)
44
+ def self.generate_identity_provider_from_file(metadata_filepath)
35
45
  idp_name = File.basename(metadata_filepath, "-metadata.xml")
36
46
  metadata = File.read(metadata_filepath)
37
- IdentityProvider.parse_from_xml(
47
+ parse_from_xml(
38
48
  metadata: metadata,
39
49
  name: idp_name
40
50
  )
@@ -1,93 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "onelogin/ruby-saml/metadata"
4
- require "onelogin/ruby-saml/settings"
5
-
6
3
  module Spid
7
4
  class Metadata # :nodoc:
8
- attr_reader :metadata_attributes
9
-
10
- # rubocop:disable Metrics/MethodLength
11
- def initialize(
12
- digest_method: Spid::SHA256,
13
- signature_method: Spid::RSA_SHA256
14
- )
15
- @metadata_attributes = {
16
- issuer: issuer,
17
- private_key: private_key_content,
18
- certificate: certificate_content,
19
- assertion_consumer_service_url: assertion_consumer_service_url,
20
- assertion_consumer_service_binding: Spid.configuration.acs_binding,
21
- single_logout_service_url: single_logout_service_url,
22
- single_logout_service_binding: Spid.configuration.slo_binding,
23
- security: {
24
- authn_requests_signed: true,
25
- logout_requests_signed: false,
26
- logout_responses_signed: false,
27
- want_assertions_signed: false,
28
- want_assertions_encrypted: false,
29
- want_name_id: false,
30
- metadata_signed: true,
31
- embed_sign: false,
32
- digest_method: digest_method,
33
- signature_method: signature_method
34
- }
35
- }
36
- end
37
- # rubocop:enable Metrics/MethodLength
38
-
39
- def to_xml
40
- metadata.generate(saml_settings)
41
- end
5
+ attr_reader :sp_metadata
42
6
 
43
- def issuer
44
- service_provider.host
7
+ def initialize
8
+ @sp_metadata = Spid::Saml2::SPMetadata.new(settings: settings)
45
9
  end
46
10
 
47
- def private_key_content
48
- service_provider.private_key
11
+ def settings
12
+ @settings ||= Spid::Saml2::Settings.new(
13
+ service_provider: service_provider,
14
+ identity_provider: nil
15
+ )
49
16
  end
50
17
 
51
- def certificate_content
52
- service_provider.certificate
53
- end
54
-
55
- def assertion_consumer_service_url
56
- service_provider.acs_url
57
- end
58
-
59
- def single_logout_service_url
60
- service_provider.slo_url
61
- end
62
-
63
- def attribute_service_name
64
- service_provider.attribute_service_name
18
+ def to_xml
19
+ sp_metadata.to_saml
65
20
  end
66
21
 
67
22
  def service_provider
68
23
  @service_provider ||= Spid.configuration.service_provider
69
24
  end
70
-
71
- private
72
-
73
- def metadata
74
- ::OneLogin::RubySaml::Metadata.new
75
- end
76
-
77
- def saml_settings
78
- @saml_settings = ::OneLogin::RubySaml::Settings.new metadata_attributes
79
-
80
- outer_self = self
81
-
82
- @saml_settings.attribute_consuming_service.configure do
83
- service_index 0
84
- service_name outer_self.attribute_service_name
85
- add_attribute name: "Name",
86
- name_format: "Name Format",
87
- friendly_name: "Friendly Name"
88
- end
89
-
90
- @saml_settings
91
- end
92
25
  end
93
26
  end
@@ -38,7 +38,7 @@ module Spid
38
38
  Spid::Sso::Request.new(
39
39
  idp_name: idp_name,
40
40
  relay_state: relay_state
41
- ).to_saml
41
+ ).url
42
42
  end
43
43
 
44
44
  def valid_request?
@@ -39,7 +39,7 @@ module Spid
39
39
  idp_name: idp_name,
40
40
  relay_state: relay_state,
41
41
  session_index: spid_session["session-index"]
42
- ).to_saml
42
+ ).url
43
43
  end
44
44
 
45
45
  def valid_request?
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal:true
2
+
3
+ require "spid/saml2/service_provider"
4
+ require "spid/saml2/identity_provider"
5
+ require "spid/saml2/settings"
6
+ require "spid/saml2/authn_request"
7
+ require "spid/saml2/response"
8
+ require "spid/saml2/logout_request"
9
+ require "spid/saml2/logout_response"
10
+ require "spid/saml2/sp_metadata"
11
+ require "spid/saml2/utils"
12
+ require "spid/saml2/idp_metadata_parser"
13
+
14
+ module Spid
15
+ module Saml2 # :nodoc:
16
+ end
17
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rexml/document"
4
+
5
+ module Spid
6
+ module Saml2
7
+ class AuthnRequest # :nodoc:
8
+ attr_reader :document
9
+ attr_reader :uuid
10
+ attr_reader :settings
11
+
12
+ def initialize(uuid: nil, settings:)
13
+ @document = REXML::Document.new
14
+ @uuid = uuid || SecureRandom.uuid
15
+ @settings = settings
16
+ end
17
+
18
+ def to_saml
19
+ document.add_element(authn_request)
20
+ document.to_s
21
+ end
22
+
23
+ def authn_request
24
+ @authn_request ||=
25
+ begin
26
+ element = REXML::Element.new("samlp:AuthnRequest")
27
+ element.add_attributes(authn_request_attributes)
28
+ element.add_element(issuer)
29
+ element.add_element(name_id_policy)
30
+ element.add_element(requested_authn_context)
31
+ element
32
+ end
33
+ end
34
+
35
+ # rubocop:disable Metrics/MethodLength
36
+ def authn_request_attributes
37
+ @authn_request_attributes ||=
38
+ begin
39
+ attributes = {
40
+ "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
41
+ "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
42
+ "ID" => "_#{uuid}",
43
+ "Version" => "2.0",
44
+ "IssueInstant" => issue_instant,
45
+ "Destination" => settings.idp_sso_target_url,
46
+ "AssertionConsumerServiceIndex" => settings.acs_index
47
+ }
48
+ attributes["ForceAuthn"] = true if settings.force_authn?
49
+ attributes
50
+ end
51
+ end
52
+ # rubocop:enable Metrics/MethodLength
53
+
54
+ def issuer
55
+ @issuer ||=
56
+ begin
57
+ element = REXML::Element.new("saml:Issuer")
58
+ element.add_attributes(
59
+ "Format" => "urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
60
+ "NameQualifier" => settings.sp_entity_id
61
+ )
62
+ element.text = settings.sp_entity_id
63
+ element
64
+ end
65
+ end
66
+
67
+ def name_id_policy
68
+ @name_id_policy ||=
69
+ begin
70
+ element = REXML::Element.new("samlp:NameIDPolicy")
71
+ element.add_attributes(
72
+ "Format" => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
73
+ )
74
+ element
75
+ end
76
+ end
77
+
78
+ def requested_authn_context
79
+ @requested_authn_context ||=
80
+ begin
81
+ element = REXML::Element.new("samlp:RequestedAuthnContext")
82
+ element.add_attributes(
83
+ "Comparison" => Spid::MINIMUM_COMPARISON
84
+ )
85
+ element.add_element(authn_context_class_ref)
86
+ element
87
+ end
88
+ end
89
+
90
+ def authn_context_class_ref
91
+ @authn_context_class_ref ||=
92
+ begin
93
+ element = REXML::Element.new("saml:AuthnContextClassRef")
94
+ element.text = settings.authn_context
95
+ element
96
+ end
97
+ end
98
+
99
+ def issue_instant
100
+ @issue_instant ||= Time.now.utc.iso8601
101
+ end
102
+ end
103
+ end
104
+ end