spid-rails 0.1

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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +156 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/spid_rails_manifest.js +2 -0
  6. data/app/assets/javascripts/spid_rails/application.js +13 -0
  7. data/app/assets/javascripts/spid_rails/metadata.js +2 -0
  8. data/app/assets/stylesheets/spid_rails/application.css +15 -0
  9. data/app/assets/stylesheets/spid_rails/metadata.css +4 -0
  10. data/app/controllers/spid_rails/application_controller.rb +5 -0
  11. data/app/controllers/spid_rails/metadata_controller.rb +15 -0
  12. data/app/controllers/spid_rails/single_logout_operations_controller.rb +43 -0
  13. data/app/controllers/spid_rails/single_sign_ons_controller.rb +36 -0
  14. data/app/helpers/spid_rails/application_helper.rb +4 -0
  15. data/app/jobs/spid_rails/application_job.rb +4 -0
  16. data/app/mailers/spid_rails/application_mailer.rb +6 -0
  17. data/app/models/spid_rails/application_record.rb +5 -0
  18. data/app/models/spid_rails/certificate.rb +45 -0
  19. data/app/models/spid_rails/idp.rb +21 -0
  20. data/app/models/spid_rails/metadata.rb +76 -0
  21. data/app/models/spid_rails/settings.rb +93 -0
  22. data/app/models/spid_rails/settings/metadata.rb +11 -0
  23. data/app/models/spid_rails/settings/slo.rb +13 -0
  24. data/app/models/spid_rails/settings/sso.rb +17 -0
  25. data/app/models/spid_rails/slo_request.rb +22 -0
  26. data/app/models/spid_rails/slo_response.rb +27 -0
  27. data/app/models/spid_rails/sso_request.rb +46 -0
  28. data/app/models/spid_rails/sso_response.rb +31 -0
  29. data/app/views/layouts/spid_rails/application.html.erb +14 -0
  30. data/config/routes.rb +12 -0
  31. data/lib/generators/spid_rails/config_generator.rb +18 -0
  32. data/lib/generators/spid_rails/templates/spid_rails.rb +30 -0
  33. data/lib/spid_rails.rb +42 -0
  34. data/lib/spid_rails/engine.rb +8 -0
  35. data/lib/spid_rails/onelogin/rubysaml/authrequest.rb +79 -0
  36. data/lib/spid_rails/version.rb +3 -0
  37. data/lib/tasks/spid_rails_tasks.rake +4 -0
  38. metadata +128 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8aecb3b0eb7abdba6750c9bb4df29868dfa5f64b
4
+ data.tar.gz: 2ac156310a32d15632dffcfb48e8c88519357515
5
+ SHA512:
6
+ metadata.gz: 9a57490aa21db27b99b68ed58f4fc6985fca5d25ff7326ce532334eb10be361d82ebf876f0f2bf4204f56b1287fecbe47b41078c6744aba91f91c09db2a7125b
7
+ data.tar.gz: 672ffaccb4f92ebfab71a8fd1c04de0f526a54365935b860096d3b1e88ffa6f6097e983035b0c03afaa3ce3d3f6f867b56f99bc2445b1235a7ea40ce05535b03
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Alessandro Descovi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # spid-rails 0.1.1
2
+ Autenticazione SPID per Ruby on Rails.
3
+ Questa gemma si appoggia alla gemma [ruby-saml](https://github.com/onelogin/ruby-saml).
4
+
5
+ ## Cosa c'è e cosa manca
6
+ - [x] Creazione gemma
7
+ - [x] Applicazione demo funzionante con login tramite il server test idp.spid.gov.it
8
+ repository: https://github.com/rubynetti/rubynetti-rails
9
+ - [x] Login tramite redirect
10
+ - [ ] Login tramite post
11
+ - [ ] Sistema di testing automatico - WIP
12
+ - [X] Sistema di configurazione
13
+ - [ ] Integrazione con omniauth
14
+ - [ ] Integrazione o esempio di integrazione con devise
15
+
16
+
17
+ ## Installazione
18
+ All'interno del Gemfile indicare questa gemma:
19
+
20
+ ```ruby
21
+ gem 'spid-rails', require: 'spid_rails'
22
+ ```
23
+
24
+ Eseguire
25
+
26
+ ```bash
27
+ $ bundle
28
+ ```
29
+
30
+
31
+ ## Come si usa?
32
+ La gemma può essere aggiunta a qualunque applicazione Rails al fine di utilizzare il sistema di login Spid.
33
+ Il metadata generato può essere utilizzato per farsi accreditare e in seguito dialogare con qualunque Identity Provider Spid accreditato.
34
+
35
+
36
+ ### Configurazione
37
+
38
+ Per creare il file di configurazione:
39
+
40
+ ```bash
41
+ $ rails g spid_rails:config
42
+ ```
43
+
44
+ Il file viene aggiunto agli initializer dell'applicazione e permette il settaggio personalizzato del mount-point dell'engine e i relativi end-point per le procedure Spid di login, logout e visualizzazione del metadata del Service Provider.
45
+
46
+ Le restanti impostazioni permettono di configurare il percorso di sistema dove reperire la coppia chiave privata/certificato e il livello di crittografia per l'eventuale signature.
47
+
48
+ ```ruby
49
+ # config/initializers/spid_rails.rb
50
+
51
+ # Impostazioni di default dello Spid Engine
52
+
53
+ SpidRails.tap do |config|
54
+
55
+ # Mount point di Spid sull'applicazione
56
+ # default: 'spid'
57
+ # config.mount_point = 'spid'
58
+
59
+ # Url alla quale è disponibile il metadata del provider
60
+ # default: 'metadata'
61
+ # config.metadata_path = 'metadata'
62
+
63
+ # Url alla quale ricevere le risposte di autenticazione Saml
64
+ # default: 'sso'
65
+ # config.sso_path = 'sso'
66
+
67
+ # Url alla quale ricevere le risposte di logout Saml
68
+ # default: 'slo'
69
+ # config.slo_path = 'slo'
70
+
71
+ # Percorso relativo alla root dell'app
72
+ # al quale reperire la coppia chiave privata - certificato
73
+ # default: 'lib/.keys'
74
+ # config.keys_path = 'lib/.keys/'
75
+
76
+ # Livello di crittografia SHA per la generazione delle signature
77
+ # default: 256
78
+ # config.sha = 256
79
+
80
+ end
81
+ ```
82
+
83
+
84
+ ### Nelle view
85
+
86
+ Una volta installata la gemma, verranno creati una serie di helper utilizzabili nelle view e nei controller.
87
+
88
+ ```spid_rails.metadata_path``` e ```spid_rails.metadata_url``` restituiscono il percorso al quale è reperibile il metadata del Service Provider.
89
+ ```ruby
90
+ # Esempio di link al metadata del ServiceProvider
91
+ link_to "Metadata SP", spid_rails.metadata_path
92
+ ```
93
+
94
+
95
+ ```spid_rails.new_sso_path``` e ```spid_rails.new_sso_url``` restituiscono il percorso tramite il quale inizializzare una richiesa di autenticazione all'Identity Provider.
96
+ E' necessario fornire come parametro l'Idp cui indirizzare la richiesta, facoltativo il livello di autenticazione Spid (default: '1') e i bindings della richiesta all' Idp (default: ['redirect']).
97
+ ```ruby
98
+ # Esempio di link al login tramite l'Idp di test https:://idp.spid.gov.it
99
+ link_to "Login con Spid", spid_rails.new_sso_path(sso: { idp: :agid_test, spid_level: 2 })
100
+ ```
101
+
102
+ Gli Identity Provider attualmente supportati sono:
103
+ - 'aruba' : servizio Idp di Aruba Pec S.p.A.
104
+ - 'infocert' : servizio Idp di Infocert S.p.A
105
+ - 'namirial' : servizio Idp di Namirial S.p.A.
106
+ - 'poste' : servizio Idp di Poste Italiane S.p.A.
107
+ - 'spiditalia' : servizio Idp di REGISTER.IT S.p.A.
108
+ - 'sielte' : servizio Idp di Sielte S.p.A.
109
+ - 'tim' : servizio Idp di TI Trust Technologies S.r.l.
110
+ - 'agid_test' : servizio idp di test di Agid
111
+ - 'poste_test' : servizio Idp di test di Poste Italiane S.p.A.
112
+
113
+
114
+ ```spid_rails.new_slo_path``` e ```spid_rails.new_slo_url``` infine restituiscono il percorso tramite il quale inizializzare una richiesa di logout all'Identity Provider che ha autenticato la sessione corrente.
115
+ ```ruby
116
+ # Esempio di link al logout
117
+ link_to "Logout", spid_rails.new_slo_path
118
+ ```
119
+
120
+
121
+ ### Nei controller
122
+
123
+ Avvenuta con successo l'autenticazione e fino al logout della stessa vengono aggiunte alla sessione le seguenti variabili:
124
+
125
+ ```session[:sso_params]``` restituisce i parametri coi quali è stata effettuata l'ultima richiesta di autenticazione, in particolare l'idp
126
+
127
+ ```session[:spid_index]``` restituisce l'identificativo dell'attuale sessione Spid, viene utilizzato nella procedura di logout
128
+
129
+ ```session[:spid_login_time]``` l'istante in cui è avvenuto il login
130
+
131
+ E' inoltre possibile settare la variabile ```session[:spid_relay_state]```, contenente l'indirizzo al quale si vuole essere reindirizzati in caso l'autenticazione abbia successo
132
+
133
+ Un esempio rudimentale di verifica del login dell'utente all'interno di un'azione del controller potrebbe essere il seguente
134
+ ```ruby
135
+ # app/controllers/my_controller.rb
136
+ class MyController < Application controller
137
+ before_action :validate_spid_session
138
+
139
+ ...
140
+
141
+ private
142
+
143
+ def validate_spid_session
144
+ if session[:spid_index].blank?
145
+ session[:spid_relay_state] = request.path
146
+ redirect_to login_path
147
+ end
148
+ end
149
+
150
+ end
151
+ ```
152
+ ove login_path indirizzi alla pagina in cui è posizionato il pulsante Spid
153
+
154
+
155
+ ## License
156
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'SpidRails'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'test'
31
+ t.pattern = 'test/**/*_test.rb'
32
+ t.verbose = false
33
+ end
34
+
35
+
36
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/spid_rails .js
2
+ //= link_directory ../stylesheets/spid_rails .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,5 @@
1
+ module SpidRails
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ require_dependency "spid_rails/application_controller"
2
+
3
+ # Metadata del Service Provider
4
+ module SpidRails
5
+
6
+ class MetadataController < ApplicationController
7
+
8
+ def show
9
+ metadata = SpidRails::Metadata.create(host: main_app.root_url)
10
+ render xml: metadata.to_xml
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,43 @@
1
+ require_dependency "spid_rails/application_controller"
2
+
3
+ module SpidRails
4
+
5
+ class SingleLogoutOperationsController < ApplicationController
6
+ skip_before_action :verify_authenticity_token, only: :create
7
+
8
+ def new
9
+ logout_request = SpidRails::SloRequest.new(slo_params)
10
+ redirect_to logout_request.to_saml
11
+ session[:spid_slo_id] = logout_request.uuid
12
+ end
13
+
14
+ def create
15
+ logout_response = SpidRails::SloResponse.new(params[:SAMLResponse],
16
+ session[:spid_slo_id],
17
+ slo_params)
18
+ # TODO: approfondire validazione logout
19
+ destroy_spid_session
20
+ redirect_to main_app.root_path, notice: 'Logout utente eseguito con successo'
21
+ end
22
+
23
+ private
24
+
25
+ def slo_params
26
+ {
27
+ host: main_app.root_url,
28
+ idp: session[:sso_params]['idp'],
29
+ session_index: session[:spid_index]
30
+ }
31
+ end
32
+
33
+ def destroy_spid_session
34
+ session[:sso_params] = nil
35
+ session[:spid_index] = nil
36
+ session[:spid_slo_id] = nil
37
+ session[:spid_relay_state] = nil
38
+ session[:spid_login_time] = nil
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,36 @@
1
+ require_dependency "spid_rails/application_controller"
2
+
3
+ module SpidRails
4
+
5
+ class SingleSignOnsController < ApplicationController
6
+ skip_before_action :verify_authenticity_token, only: :create
7
+
8
+ def new
9
+ request = SpidRails::SsoRequest.new(sso_params)
10
+ redirect_to request.to_saml
11
+ session[:sso_params] = sso_params
12
+ end
13
+
14
+ def create
15
+ response = SpidRails::SsoResponse.new(params[:SAMLResponse], session[:sso_params])
16
+ if response.valid?
17
+ session[:spid_index] = response.session_index
18
+ session[:spid_login_time] = Time.now
19
+ redirect_to session[:relay_state] || main_app.root_path, notice: 'Utente autenticato con successo'
20
+ else
21
+ redirect_to main_app.root_path, notice: 'Autenticazione fallita'
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def sso_params
28
+ sso_params = params.require(:sso).permit(:idp, :spid_level, bindings: [])
29
+ sso_params[:host] = main_app.root_url
30
+ sso_params[:relay_state] = session[:spid_relay_state] || main_app.root_url
31
+ sso_params
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,4 @@
1
+ module SpidRails
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module SpidRails
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module SpidRails
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module SpidRails
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,45 @@
1
+ module SpidRails
2
+
3
+ class Certificate
4
+
5
+ def self.signature_algorithm sha
6
+ case sha.to_s
7
+ when '256'
8
+ XMLSecurity::Document::RSA_SHA256
9
+ when '384'
10
+ XMLSecurity::Document::RSA_SHA384
11
+ when '512'
12
+ XMLSecurity::Document::RSA_SHA512
13
+ end
14
+ end
15
+
16
+ def self.digest_algorithm sha
17
+ case sha.to_s
18
+ when '256'
19
+ XMLSecurity::Document::SHA256
20
+ when '384'
21
+ XMLSecurity::Document::SHA384
22
+ when '512'
23
+ XMLSecurity::Document::SHA512
24
+ end
25
+ end
26
+
27
+ def self.signature_algorithms
28
+ [
29
+ XMLSecurity::Document::RSA_SHA256,
30
+ XMLSecurity::Document::RSA_SHA384,
31
+ XMLSecurity::Document::RSA_SHA512,
32
+ ]
33
+ end
34
+
35
+ def self.digest_algorithms
36
+ [
37
+ XMLSecurity::Document::SHA256,
38
+ XMLSecurity::Document::SHA384,
39
+ XMLSecurity::Document::SHA512,
40
+ ]
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,21 @@
1
+ module SpidRails
2
+
3
+ class Idp
4
+
5
+ def self.metadata_urls
6
+ {
7
+ 'agid_test' => 'http://localhost:3000/metadata-idp-gov.xml',
8
+ 'aruba' => 'https://loginspid.aruba.it/metadata',
9
+ 'infocert' => 'https://identity.infocert.it/metadata/metadata.xml',
10
+ 'namirial' => 'https://idp.namirialtsp.com/idp/metadata',
11
+ 'poste' => 'http://posteid.poste.it/jod-fs/metadata/metadata.xml',
12
+ 'poste_test' => 'http://spidposte.test.poste.it/jod-fs/metadata/idp.xml',
13
+ 'spiditalia' => 'https://spid.register.it/login/metadata',
14
+ 'sielte' => 'https://identity.sieltecloud.it/simplesaml/metadata.xml',
15
+ 'tim' => 'https://login.id.tim.it/spid-services/MetadataBrowser/idp'
16
+ }
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,76 @@
1
+ module SpidRails
2
+
3
+ class Metadata
4
+ attr_accessor :settings
5
+
6
+ def self.create **settings
7
+ obj = self.new(**settings)
8
+ obj.save if obj.valid?
9
+ end
10
+
11
+ def initialize spid_params
12
+ spid_settings = SpidRails::Settings::Metadata.new(spid_params)
13
+ @settings = spid_settings.to_hash
14
+ end
15
+
16
+ def valid?
17
+ raise 'EntityID deve essere presente (impostare issuer)' if settings[:issuer].blank?
18
+ raise 'Signature deve essere presente (impostare private_key)' if settings[:private_key].blank?
19
+ raise 'Signature deve essere presente (impostare certificate)' if settings[:certificate].blank?
20
+ validate_signature_encryption
21
+ validate_digest_encryption
22
+ validate_key_size
23
+
24
+ true
25
+ end
26
+
27
+ def validate_signature_encryption
28
+ signature_algorithms = SpidRails::Certificate.signature_algorithms
29
+ if signature_algorithms.exclude?(settings[:security][:signature_method])
30
+ raise 'Signature deve essere presente (impostare encryption sha a 256, 384, 512)'
31
+ end
32
+ end
33
+
34
+ def validate_digest_encryption
35
+ digest_algorithms = SpidRails::Certificate.digest_algorithms
36
+ if digest_algorithms.exclude?(settings[:security][:digest_method])
37
+ raise 'Signature deve essere presente (impostare encryption sha a 256, 384, 512)'
38
+ end
39
+ end
40
+
41
+ def validate_key_size
42
+ key = OpenSSL::PKey::RSA.new settings[:private_key]
43
+ key_size = key.n.num_bytes * 8
44
+ if key_size < 1024
45
+ raise "Signature deve essere presente (impostare una chiave di almeno a 1024 bit"
46
+ end
47
+ end
48
+
49
+ def save
50
+ valid?
51
+ metadata = OneLogin::RubySaml::Metadata.new
52
+ saml_settings = OneLogin::RubySaml::Settings.new @settings
53
+ @to_xml = metadata.generate(saml_settings)
54
+ self
55
+ end
56
+
57
+ def to_xml
58
+ save and @to_xml
59
+ end
60
+
61
+
62
+ def self.xml_namespaces
63
+ {
64
+ saml: 'urn:oasis:names:tc:SAML:2.0:assertion',
65
+ samlp: 'urn:oasis:names:tc:SAML:2.0:protocol',
66
+ md: 'urn:oasis:names:tc:SAML:2.0:metadata',
67
+ ds: 'http://www.w3.org/2000/09/xmldsig#',
68
+ xenc: 'http://www.w3.org/2001/04/xmlenc#',
69
+ xs: 'http://www.w3.org/2001/XMLSchema'
70
+ }
71
+
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,93 @@
1
+ module SpidRails
2
+
3
+ class Settings
4
+
5
+
6
+ attr_accessor :host
7
+
8
+ attr_accessor :metadata_path
9
+
10
+ attr_accessor :sso_path
11
+
12
+ attr_accessor :slo_path
13
+
14
+ attr_accessor :keys_path
15
+
16
+ attr_accessor :sha
17
+
18
+ attr_accessor :idp
19
+
20
+ attr_accessor :bindings
21
+
22
+ attr_accessor :spid_level
23
+
24
+ attr_accessor :session_index
25
+
26
+ attr_accessor :relay_state
27
+
28
+
29
+ def initialize spid_params
30
+ @metadata_path = SpidRails.app_metadata_path
31
+ @sso_path = SpidRails.app_sso_path
32
+ @slo_path = SpidRails.app_slo_path
33
+ @keys_path = SpidRails.keys_path
34
+ @sha = SpidRails.sha
35
+ @bindings = [:redirect]
36
+ @spid_level = 1
37
+ spid_params.each do |k, v|
38
+ send("#{k}=", v)
39
+ end
40
+ end
41
+
42
+ def security_attributes
43
+ dig_alg = SpidRails::Certificate.digest_algorithm(@sha)
44
+ sig_alg = SpidRails::Certificate.signature_algorithm(@sha)
45
+ {
46
+ metadata_signed: true,
47
+ digest_method: dig_alg,
48
+ signature_method: sig_alg,
49
+ want_assertions_signed: true
50
+ }
51
+ end
52
+
53
+
54
+ def sp_attributes
55
+ {
56
+ issuer: host + metadata_path,
57
+ assertion_consumer_service_url: host + sso_path,
58
+ single_logout_service_url: host + slo_path,
59
+ private_key: File.read("#{Rails.root}/#{keys_path}/private_key.pem"),
60
+ certificate: File.read("#{Rails.root}/#{keys_path}/certificate.pem"),
61
+ security: security_attributes
62
+ }
63
+ end
64
+
65
+ def idp_attributes
66
+ idp_bindings = @bindings.map{ |b| self.class.saml_bindings[b] }
67
+ parser = OneLogin::RubySaml::IdpMetadataParser.new
68
+ parser.parse_remote_to_hash Idp.metadata_urls[@idp.to_s],
69
+ true,
70
+ sso_binding: idp_bindings
71
+ end
72
+
73
+ private
74
+
75
+ def authn_context
76
+ "https://www.spid.gov.it/SpidL#{@spid_level}"
77
+ end
78
+
79
+ def force_authn
80
+ return true if @spid_level != 1
81
+ end
82
+
83
+ # TODO spostare in utils
84
+ def self.saml_bindings
85
+ {
86
+ post: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
87
+ redirect: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
88
+ }
89
+ end
90
+
91
+ end
92
+
93
+ end
@@ -0,0 +1,11 @@
1
+ module SpidRails
2
+
3
+ class Settings::Metadata < Settings
4
+
5
+ def to_hash
6
+ sp_attributes
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ module SpidRails
2
+
3
+ class Settings::Slo < Settings
4
+
5
+ def to_hash
6
+ sso_attributes = sp_attributes.merge(idp_attributes)
7
+ sso_attributes[:sessionindex] = @session_index
8
+ sso_attributes
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,17 @@
1
+ module SpidRails
2
+
3
+ class Settings::Sso < Settings
4
+
5
+ def to_hash
6
+ sso_attributes = sp_attributes.merge(idp_attributes)
7
+ sso_attributes[:authn_context] = authn_context
8
+ sso_attributes[:authn_context_comparison] = 'minimum'
9
+ sso_attributes[:force_authn] = force_authn
10
+ sso_attributes[:protocol_binding] = self.class.saml_bindings[:post]
11
+ sso_attributes[:relay_state] = relay_state
12
+ sso_attributes
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,22 @@
1
+ module SpidRails
2
+
3
+ class SloRequest
4
+
5
+ def initialize slo_params
6
+ spid_settings = SpidRails::Settings::Slo.new(slo_params)
7
+ @settings = spid_settings.to_hash
8
+ @request = OneLogin::RubySaml::Logoutrequest.new
9
+ end
10
+
11
+ def uuid
12
+ @request.uuid
13
+ end
14
+
15
+ def to_saml
16
+ saml_settings = OneLogin::RubySaml::Settings.new(@settings)
17
+ @request.create(saml_settings)
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,27 @@
1
+ module SpidRails
2
+
3
+ class SloResponse
4
+
5
+ def initialize saml_response, slo_id, slo_params
6
+ spid_settings = SpidRails::Settings::Slo.new(slo_params)
7
+ settings = OneLogin::RubySaml::Settings.new(spid_settings.to_hash)
8
+ @response = OneLogin::RubySaml::Logoutresponse.new(saml_response,
9
+ settings,
10
+ matches_request_id: slo_id)
11
+ end
12
+
13
+ def valid?
14
+ @response.validate
15
+ end
16
+
17
+ def inspect
18
+ @response.inspect
19
+ end
20
+
21
+ def errors
22
+ @response.errors
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,46 @@
1
+ module SpidRails
2
+
3
+ class SsoRequest
4
+
5
+ attr_accessor :settings
6
+
7
+ def initialize spid_params
8
+ spid_settings = SpidRails::Settings::Sso.new(spid_params)
9
+ @settings = spid_settings.to_hash
10
+ end
11
+
12
+ def valid?
13
+ if settings[:idp_sso_target_url].blank?
14
+ raise "Destination deve essere presente (impostare idp_sso_target_url)"
15
+ end
16
+ if settings[:authn_context].last != '1' && settings[:force_authn] != true
17
+ raise "ForceAuthn deve essere presente per livelli di aitenticazione diversi da SPIDL1 (impostare force_authn a true)"
18
+ end
19
+ if settings[:authn_context_comparison] != 'minimum'
20
+ raise "AuthnContextComparison deve essere settato a 'minimum' (impostare authn_context_comparison a 'minimum')"
21
+ end
22
+ if settings[:protocol_binding] != SpidRails::Settings.saml_bindings[:post]
23
+ raise "Issuer deve contenere l'attributo ProtocolBinding con binding POST (impostare protocl_binding a ':post')"
24
+ end
25
+ end
26
+
27
+ def save
28
+ valid?
29
+ request = OneLogin::RubySaml::Authrequest.new
30
+ saml_settings = OneLogin::RubySaml::Settings.new @settings
31
+ @to_saml = request.create(saml_settings)
32
+ self
33
+ end
34
+
35
+ def to_saml
36
+ save and @to_saml
37
+ end
38
+
39
+ def self.create **settings
40
+ obj = self.new(**settings)
41
+ obj.save
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,31 @@
1
+ module SpidRails
2
+
3
+ class SsoResponse
4
+
5
+ def initialize saml_response, sso_params
6
+ response = OneLogin::RubySaml::Response.new(saml_response)
7
+ settings = SpidRails::Settings::Sso.new(sso_params)
8
+ saml_settings = OneLogin::RubySaml::Settings.new(settings.to_hash)
9
+ response.settings = saml_settings
10
+ @response = response
11
+ end
12
+
13
+ def valid?
14
+ @response.is_valid?
15
+ end
16
+
17
+ def inspect
18
+ @response.inspect
19
+ end
20
+
21
+ def session_index
22
+ @response.sessionindex
23
+ end
24
+
25
+ def errors
26
+ @response.errors
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Spid rails</title>
5
+ <%= stylesheet_link_tag "spid_rails/application", media: "all" %>
6
+ <%= javascript_include_tag "spid_rails/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,12 @@
1
+ Rails.application.routes.draw do
2
+ mount SpidRails::Engine, at: SpidRails.mount_point
3
+ end
4
+
5
+ SpidRails::Engine.routes.draw do
6
+ resource :metadata, only: :show,
7
+ path: SpidRails.metadata_path
8
+ resource :sso, only: [:new, :create], controller: :single_sign_ons,
9
+ path: SpidRails.sso_path
10
+ resource :slo, only: [:new, :create], controller: :single_logout_operations,
11
+ path: SpidRails.slo_path
12
+ end
@@ -0,0 +1,18 @@
1
+ module SpidRails
2
+ module Generators
3
+
4
+ # Chiamato tramite rails g spid_rails:config
5
+ class ConfigGenerator < Rails::Generators::Base
6
+
7
+ source_root File.expand_path("../templates", __FILE__)
8
+
9
+ desc "Crea il file di configurazione di spid (config/initializers/rumby_saml.rb)."
10
+
11
+ def create_initializer_file
12
+ template "spid_rails.rb", "./config/initializers/spid_rails.rb"
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,30 @@
1
+ # Impostazioni di default dello Spid Engine
2
+
3
+ SpidRails.tap do |config|
4
+
5
+ # Mount point di Spid sull'applicazione
6
+ # default: 'spid'
7
+ # config.mount_point = 'spid'
8
+
9
+ # Url alla quale è disponibile il metadata del provider
10
+ # default: 'metadata'
11
+ # config.metadata_path = 'metadata'
12
+
13
+ # Url alla quale ricevere le risposte di autenticazione Saml
14
+ # default: 'sso'
15
+ # config.sso_path = 'sso'
16
+
17
+ # Url alla quale ricevere le risposte di logout Saml
18
+ # default: 'slo'
19
+ # config.slo_path = 'slo'
20
+
21
+ # Percorso relativo alla root dell'app
22
+ # al quale reperire la coppia chiave privata - certificato
23
+ # default: 'lib/.keys'
24
+ # config.keys_path = 'lib/.keys/'
25
+
26
+ # Livello di crittografia SHA per la generazione delle signature
27
+ # default: 256
28
+ # config.sha = 256
29
+
30
+ end
data/lib/spid_rails.rb ADDED
@@ -0,0 +1,42 @@
1
+ require "spid_rails/engine"
2
+
3
+ module SpidRails
4
+
5
+ # Mount point di Spid sull'applicazione
6
+ mattr_accessor :mount_point
7
+ @@mount_point = 'spid'
8
+
9
+ # Url alla quale è disponibile il metadata del provider
10
+ mattr_accessor :metadata_path
11
+ @@metadata_path = 'metadata'
12
+
13
+ # Url alla quale ricevere le risposte di autenticazione Saml
14
+ mattr_accessor :sso_path
15
+ @@sso_path = 'sso'
16
+
17
+ # Url alla quale ricevere le risposte di logout Saml
18
+ mattr_accessor :slo_path
19
+ @@slo_path = 'slo'
20
+
21
+ # Percorso relativo alla root dell'app
22
+ # al quale reperire la coppia chiave privata - certificato
23
+ mattr_accessor :keys_path
24
+ @@keys_path = 'lib/.keys/'
25
+
26
+ # Livello di crittografia SHA per la generazione delle signature
27
+ mattr_accessor :sha
28
+ @@sha = 256
29
+
30
+ def self.app_metadata_path
31
+ "#{mount_point}/#{@@metadata_path}"
32
+ end
33
+
34
+ def self.app_sso_path
35
+ "#{mount_point}/#{@@sso_path}"
36
+ end
37
+
38
+ def self.app_slo_path
39
+ "#{mount_point}/#{@@slo_path}"
40
+ end
41
+
42
+ end
@@ -0,0 +1,8 @@
1
+ require 'onelogin/ruby-saml'
2
+ require 'spid_rails/onelogin/rubysaml/authrequest'
3
+
4
+ module SpidRails
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace SpidRails
7
+ end
8
+ end
@@ -0,0 +1,79 @@
1
+ # Necessario override della classe originaria della libreria,
2
+ # al fine di rendere conforme il nodo Issuer alle regole tecniche SPID,
3
+ # (aggiunte righe 32 e 33)
4
+
5
+ module OneLogin
6
+ module RubySaml
7
+ class Authrequest
8
+
9
+ def create_xml_document(settings)
10
+ time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
11
+
12
+ request_doc = XMLSecurity::Document.new
13
+ request_doc.uuid = uuid
14
+
15
+ root = request_doc.add_element "samlp:AuthnRequest", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol", "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
16
+ root.attributes['ID'] = uuid
17
+ root.attributes['IssueInstant'] = time
18
+ root.attributes['Version'] = "2.0"
19
+ root.attributes['Destination'] = settings.idp_sso_target_url unless settings.idp_sso_target_url.nil?
20
+ root.attributes['IsPassive'] = settings.passive unless settings.passive.nil?
21
+ root.attributes['ProtocolBinding'] = settings.protocol_binding unless settings.protocol_binding.nil?
22
+ root.attributes["AttributeConsumingServiceIndex"] = settings.attributes_index unless settings.attributes_index.nil?
23
+ root.attributes['ForceAuthn'] = settings.force_authn unless settings.force_authn.nil?
24
+
25
+ # Conditionally defined elements based on settings
26
+ if settings.assertion_consumer_service_url != nil
27
+ root.attributes["AssertionConsumerServiceURL"] = settings.assertion_consumer_service_url
28
+ end
29
+ #NameQualifier e Format da requisiti SPID
30
+ if settings.issuer != nil
31
+ issuer = root.add_element "saml:Issuer",{
32
+ "NameQualifier" => settings.issuer,
33
+ "Format" => 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'
34
+ }
35
+ issuer.text = settings.issuer
36
+ end
37
+ if settings.name_identifier_format != nil
38
+ root.add_element "samlp:NameIDPolicy", {
39
+ # Might want to make AllowCreate a setting?
40
+ "AllowCreate" => "true",
41
+ "Format" => settings.name_identifier_format
42
+ }
43
+ end
44
+
45
+ if settings.authn_context || settings.authn_context_decl_ref
46
+
47
+ if settings.authn_context_comparison != nil
48
+ comparison = settings.authn_context_comparison
49
+ else
50
+ comparison = 'exact'
51
+ end
52
+
53
+ requested_context = root.add_element "samlp:RequestedAuthnContext", {
54
+ "Comparison" => comparison,
55
+ }
56
+
57
+ if settings.authn_context != nil
58
+ authn_contexts_class_ref = settings.authn_context.is_a?(Array) ? settings.authn_context : [settings.authn_context]
59
+ authn_contexts_class_ref.each do |authn_context_class_ref|
60
+ class_ref = requested_context.add_element "saml:AuthnContextClassRef"
61
+ class_ref.text = authn_context_class_ref
62
+ end
63
+ end
64
+
65
+ if settings.authn_context_decl_ref != nil
66
+ authn_contexts_decl_refs = settings.authn_context_decl_ref.is_a?(Array) ? settings.authn_context_decl_ref : [settings.authn_context_decl_ref]
67
+ authn_contexts_decl_refs.each do |authn_context_decl_ref|
68
+ decl_ref = requested_context.add_element "saml:AuthnContextDeclRef"
69
+ decl_ref.text = authn_context_decl_ref
70
+ end
71
+ end
72
+ end
73
+
74
+ request_doc
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module SpidRails
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :spid_rails do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spid-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Alessandro Descovi, Giacomo Bertoldi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.1.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '5.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.1.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: ruby-saml
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 1.5.0
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 1.5.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ description: Soluzione per poter effettuare il login tramite SPID
62
+ email:
63
+ - descovi@gmail.com, bertoldi.giacomo@gmail.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - MIT-LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - app/assets/config/spid_rails_manifest.js
72
+ - app/assets/javascripts/spid_rails/application.js
73
+ - app/assets/javascripts/spid_rails/metadata.js
74
+ - app/assets/stylesheets/spid_rails/application.css
75
+ - app/assets/stylesheets/spid_rails/metadata.css
76
+ - app/controllers/spid_rails/application_controller.rb
77
+ - app/controllers/spid_rails/metadata_controller.rb
78
+ - app/controllers/spid_rails/single_logout_operations_controller.rb
79
+ - app/controllers/spid_rails/single_sign_ons_controller.rb
80
+ - app/helpers/spid_rails/application_helper.rb
81
+ - app/jobs/spid_rails/application_job.rb
82
+ - app/mailers/spid_rails/application_mailer.rb
83
+ - app/models/spid_rails/application_record.rb
84
+ - app/models/spid_rails/certificate.rb
85
+ - app/models/spid_rails/idp.rb
86
+ - app/models/spid_rails/metadata.rb
87
+ - app/models/spid_rails/settings.rb
88
+ - app/models/spid_rails/settings/metadata.rb
89
+ - app/models/spid_rails/settings/slo.rb
90
+ - app/models/spid_rails/settings/sso.rb
91
+ - app/models/spid_rails/slo_request.rb
92
+ - app/models/spid_rails/slo_response.rb
93
+ - app/models/spid_rails/sso_request.rb
94
+ - app/models/spid_rails/sso_response.rb
95
+ - app/views/layouts/spid_rails/application.html.erb
96
+ - config/routes.rb
97
+ - lib/generators/spid_rails/config_generator.rb
98
+ - lib/generators/spid_rails/templates/spid_rails.rb
99
+ - lib/spid_rails.rb
100
+ - lib/spid_rails/engine.rb
101
+ - lib/spid_rails/onelogin/rubysaml/authrequest.rb
102
+ - lib/spid_rails/version.rb
103
+ - lib/tasks/spid_rails_tasks.rake
104
+ homepage: https://github.com/italia/spid-rails
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 2.4.0
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.6.8
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: SPID, il Sistema Pubblico di Identità Digitale
128
+ test_files: []