decidim-suomifi 0.18.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 +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +236 -0
- data/Rakefile +17 -0
- data/app/controllers/decidim/suomifi/omniauth_callbacks_controller.rb +267 -0
- data/app/controllers/decidim/suomifi/verification/authorizations_controller.rb +19 -0
- data/config/locales/en.yml +21 -0
- data/config/locales/fi.yml +20 -0
- data/config/locales/sv.yml +20 -0
- data/lib/decidim/suomifi.rb +141 -0
- data/lib/decidim/suomifi/engine.rb +101 -0
- data/lib/decidim/suomifi/mail_interceptors.rb +9 -0
- data/lib/decidim/suomifi/mail_interceptors/generated_recipients_interceptor.rb +25 -0
- data/lib/decidim/suomifi/test/cert_store.rb +26 -0
- data/lib/decidim/suomifi/test/runtime.rb +48 -0
- data/lib/decidim/suomifi/verification.rb +5 -0
- data/lib/decidim/suomifi/verification/engine.rb +43 -0
- data/lib/decidim/suomifi/verification/manager.rb +17 -0
- data/lib/decidim/suomifi/verification/metadata_collector.rb +110 -0
- data/lib/decidim/suomifi/version.rb +8 -0
- data/lib/generators/decidim/suomifi/install_generator.rb +135 -0
- data/lib/generators/templates/suomifi_initializer.rb +15 -0
- data/lib/generators/templates/suomifi_initializer_test.rb +3 -0
- data/lib/generators/templates/suomifi_localhost.crt +21 -0
- data/lib/generators/templates/suomifi_localhost.key +28 -0
- metadata +137 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Suomifi
|
5
|
+
module Verification
|
6
|
+
class AuthorizationsController < ::Decidim::ApplicationController
|
7
|
+
skip_before_action :store_current_location
|
8
|
+
|
9
|
+
def new
|
10
|
+
# Do enforce the permission here because it would cause
|
11
|
+
# re-authorizations not to work as the authorization already exists.
|
12
|
+
# In case the user wants to re-authorize themselves, they can just
|
13
|
+
# hit this endpoint again.
|
14
|
+
redirect_to decidim.user_suomifi_omniauth_authorize_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
decidim:
|
4
|
+
authorization_handlers:
|
5
|
+
suomifi_eid:
|
6
|
+
explanation: Identify yourself using the Suomi.fi e-Identification service.
|
7
|
+
name: Suomi.fi e-Identification
|
8
|
+
suomifi:
|
9
|
+
omniauth_callbacks:
|
10
|
+
failure:
|
11
|
+
already_authorized: Another user has already authorized themselves with the same identity.
|
12
|
+
conditions: The authentication request was not handled within an allowed timeframe. Please try again.
|
13
|
+
identity_bound_to_other_user: Another user has already been identified using this identity. Please sign out and sign in again directly using Suomi.fi.
|
14
|
+
session_expiration: Authentication session expired. Please try again.
|
15
|
+
success_status: Authentication failed or cancelled. Please try again.
|
16
|
+
verification:
|
17
|
+
authorizations:
|
18
|
+
create:
|
19
|
+
success: You have been successfully authorized through Suomi.fi
|
20
|
+
destroy:
|
21
|
+
success: Authorization sucessfully reset.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
fi:
|
2
|
+
decidim:
|
3
|
+
authorization_handlers:
|
4
|
+
suomifi_eid:
|
5
|
+
explanation: Tunnista itsesi Suomi.fi-tunnistuspalvelun avulla.
|
6
|
+
name: Suomi.fi tunnistus
|
7
|
+
suomifi:
|
8
|
+
omniauth_callbacks:
|
9
|
+
failure:
|
10
|
+
already_authorized: Toinen käyttäjä on tunnistanut itsensä jo samalla henkilöllisyydellä.
|
11
|
+
conditions: Tunnistuspyyntöä ei käsitelty sallitun aikarajan sisällä. Yritä uudestaan.
|
12
|
+
identity_bound_to_other_user: Toinen käyttäjä on jo tunnistanut itsensä tällä henkilöllisyydellä. Kirjaudu ulos ja kirjaudu uudestaan sisään käyttäen suoraan Suomi.fi-tunnistusta.
|
13
|
+
session_expiration: Tunnistusistunto vanhentui. Yritä uudestaan.
|
14
|
+
success_status: Tunnistus epäonnistui tai peruutettiin. Yritä uudestaan.
|
15
|
+
verification:
|
16
|
+
authorizations:
|
17
|
+
create:
|
18
|
+
success: Sinut on onnistuneesti tunnistettu Suomi.fi-palvelun avulla
|
19
|
+
destroy:
|
20
|
+
success: Varmennus tyhjennetty onnistuneesti.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
sv:
|
2
|
+
decidim:
|
3
|
+
authorization_handlers:
|
4
|
+
suomifi_eid:
|
5
|
+
explanation: Identifiera dig själv med Suomi.fi-identifikation.
|
6
|
+
name: Suomi.fi-identifikation
|
7
|
+
suomifi:
|
8
|
+
omniauth_callbacks:
|
9
|
+
failure:
|
10
|
+
already_authorized: En annan användare har redan godkänt sig med samma identitet.
|
11
|
+
conditions: Autentiseringsbegäran hanterades inte inom en tillåten tidsram. Var god försök igen.
|
12
|
+
identity_bound_to_other_user: En annan användare har redan identifierats med denna identitet. Logga ut och logga in igen direkt med Suomi.fi.
|
13
|
+
session_expiration: Autentiseringssessionen har gått ut. Var god försök igen.
|
14
|
+
success_status: Autentiseringen misslyckades eller avbröts. Var god försök igen.
|
15
|
+
verification:
|
16
|
+
authorizations:
|
17
|
+
create:
|
18
|
+
success: Du har godkänts med Suomi.fi
|
19
|
+
destroy:
|
20
|
+
success: Tillståndet återställs efterhand.
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "omniauth"
|
4
|
+
require "omniauth-suomifi"
|
5
|
+
require "henkilotunnus"
|
6
|
+
|
7
|
+
require_relative "suomifi/version"
|
8
|
+
require_relative "suomifi/engine"
|
9
|
+
require_relative "suomifi/verification"
|
10
|
+
require_relative "suomifi/mail_interceptors"
|
11
|
+
|
12
|
+
module Decidim
|
13
|
+
module Suomifi
|
14
|
+
include ActiveSupport::Configurable
|
15
|
+
|
16
|
+
@configured = false
|
17
|
+
|
18
|
+
# :production - For Suomi.fi production environment
|
19
|
+
# :test - For Suomi.fi test environment
|
20
|
+
config_accessor :mode, instance_reader: false
|
21
|
+
|
22
|
+
# :limited - Limited scope
|
23
|
+
# :medium_extensive - Medium-extensive scope
|
24
|
+
# :extensive - Extensive scope
|
25
|
+
config_accessor :scope_of_data do
|
26
|
+
:medium_extensive
|
27
|
+
end
|
28
|
+
|
29
|
+
# Defines the auto email domain in case the person's email address is not
|
30
|
+
# stored in the Suomi.fi database. In case this is defined, the user will
|
31
|
+
# be automatically assigned an email such as
|
32
|
+
# "suomifi-identifier@auto-email-domain.fi" upon their registration.
|
33
|
+
config_accessor :auto_email_domain
|
34
|
+
|
35
|
+
config_accessor :sp_entity_id, instance_reader: false
|
36
|
+
|
37
|
+
# The certificate string for the application
|
38
|
+
config_accessor :certificate, instance_reader: false
|
39
|
+
|
40
|
+
# The private key string for the application
|
41
|
+
config_accessor :private_key, instance_reader: false
|
42
|
+
|
43
|
+
# The certificate file for the application
|
44
|
+
config_accessor :certificate_file
|
45
|
+
|
46
|
+
# The private key file for the application
|
47
|
+
config_accessor :private_key_file
|
48
|
+
|
49
|
+
# Extra configuration for the omniauth strategy
|
50
|
+
config_accessor :extra do
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Allows customizing the authorization workflow e.g. for adding custom
|
55
|
+
# workflow options or configuring an action authorizer for the
|
56
|
+
# particular needs.
|
57
|
+
config_accessor :workflow_configurator do
|
58
|
+
lambda do |workflow|
|
59
|
+
# By default, expiration is set to 0 minutes which means it will
|
60
|
+
# never expire.
|
61
|
+
workflow.expires_in = 0.minutes
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Allows customizing how the authorization metadata gets collected from
|
66
|
+
# the SAML attributes passed from the authorization endpoint.
|
67
|
+
config_accessor :metadata_collector_class do
|
68
|
+
Decidim::Suomifi::Verification::MetadataCollector
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.configured?
|
72
|
+
@configured
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.configure
|
76
|
+
@configured = true
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.mode
|
81
|
+
return config.mode if config.mode
|
82
|
+
return :production unless Rails.application.secrets.omniauth
|
83
|
+
return :production unless Rails.application.secrets.omniauth[:suomifi]
|
84
|
+
|
85
|
+
# Read the mode from the secrets
|
86
|
+
secrets = Rails.application.secrets.omniauth[:suomifi]
|
87
|
+
secrets[:mode] == "test" ? :test : :production
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.sp_entity_id
|
91
|
+
return config.sp_entity_id if config.sp_entity_id
|
92
|
+
|
93
|
+
"#{application_host}/users/auth/suomifi/metadata"
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.certificate
|
97
|
+
return File.read(certificate_file) if certificate_file
|
98
|
+
|
99
|
+
config.certificate
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.private_key
|
103
|
+
return File.read(private_key_file) if private_key_file
|
104
|
+
|
105
|
+
config.private_key
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.omniauth_settings
|
109
|
+
settings = {
|
110
|
+
mode: mode,
|
111
|
+
scope_of_data: scope_of_data,
|
112
|
+
sp_entity_id: sp_entity_id,
|
113
|
+
certificate: certificate,
|
114
|
+
private_key: private_key
|
115
|
+
}
|
116
|
+
settings.merge!(config.extra) if config.extra.is_a?(Hash)
|
117
|
+
settings
|
118
|
+
end
|
119
|
+
|
120
|
+
# Used to determine the default service provider entity ID in case not
|
121
|
+
# specifically set by the `sp_entity_id` configuration option.
|
122
|
+
def self.application_host
|
123
|
+
conf = Rails.application.config
|
124
|
+
url_options = conf.action_controller.default_url_options
|
125
|
+
url_options = conf.action_mailer.default_url_options if !url_options || !url_options[:host]
|
126
|
+
url_options ||= {}
|
127
|
+
|
128
|
+
host = url_options[:host]
|
129
|
+
port = url_options[:port]
|
130
|
+
if host.blank?
|
131
|
+
# Default to local development environment
|
132
|
+
host = "http://localhost"
|
133
|
+
port ||= 3000
|
134
|
+
end
|
135
|
+
|
136
|
+
return "#{host}:#{port}" if port && ![80, 443].include?(port.to_i)
|
137
|
+
|
138
|
+
host
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Suomifi
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace Decidim::Suomifi
|
7
|
+
|
8
|
+
routes do
|
9
|
+
devise_scope :user do
|
10
|
+
# Manually map the SAML omniauth routes for Devise because the default
|
11
|
+
# routes are mounted by core Decidim. This is because we want to map
|
12
|
+
# these routes to the local callbacks controller instead of the
|
13
|
+
# Decidim core.
|
14
|
+
# See: https://git.io/fjDz1
|
15
|
+
match(
|
16
|
+
"/users/auth/suomifi",
|
17
|
+
to: "omniauth_callbacks#passthru",
|
18
|
+
as: "user_suomifi_omniauth_authorize",
|
19
|
+
via: [:get, :post]
|
20
|
+
)
|
21
|
+
|
22
|
+
match(
|
23
|
+
"/users/auth/suomifi/callback",
|
24
|
+
to: "omniauth_callbacks#suomifi",
|
25
|
+
as: "user_suomifi_omniauth_callback",
|
26
|
+
via: [:get, :post]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
initializer "decidim_suomifi.mount_routes", before: :add_routing_paths do
|
32
|
+
# Mount the engine routes to Decidim::Core::Engine because otherwise
|
33
|
+
# they would not get mounted properly. Note also that we need to prepend
|
34
|
+
# the routes in order for them to override Decidim's own routes for the
|
35
|
+
# "suomifi" authentication.
|
36
|
+
Decidim::Core::Engine.routes.prepend do
|
37
|
+
mount Decidim::Suomifi::Engine => "/"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
initializer "decidim_suomifi.setup", before: "devise.omniauth" do
|
42
|
+
next unless Decidim::Suomifi.configured?
|
43
|
+
|
44
|
+
# Configure the SAML OmniAuth strategy for Devise
|
45
|
+
::Devise.setup do |config|
|
46
|
+
config.omniauth(
|
47
|
+
:suomifi,
|
48
|
+
Decidim::Suomifi.omniauth_settings
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Customized version of Devise's OmniAuth failure app in order to handle
|
53
|
+
# the failures properly. Without this, the failure requests would end
|
54
|
+
# up in an ActionController::InvalidAuthenticityToken exception.
|
55
|
+
devise_failure_app = OmniAuth.config.on_failure
|
56
|
+
OmniAuth.config.on_failure = proc do |env|
|
57
|
+
if env["PATH_INFO"] =~ %r{^/users/auth/suomifi(/.*)?}
|
58
|
+
env["devise.mapping"] = ::Devise.mappings[:user]
|
59
|
+
Decidim::Suomifi::OmniauthCallbacksController.action(
|
60
|
+
:failure
|
61
|
+
).call(env)
|
62
|
+
else
|
63
|
+
# Call the default for others.
|
64
|
+
devise_failure_app.call(env)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
initializer "decidim_suomifi.omniauth_provider", after: :load_config_initializers do
|
70
|
+
next unless Decidim::Suomifi.configured?
|
71
|
+
|
72
|
+
Decidim::Suomifi::Engine.add_omniauth_provider
|
73
|
+
|
74
|
+
# This also needs to run as a callback for the reloader because
|
75
|
+
# otherwise the suomifi OmniAuth routes would not be added to the core
|
76
|
+
# engine because its routes are reloaded before e.g. the to_prepare hook
|
77
|
+
# runs in this engine. The OmniAuth provider needs to be added before
|
78
|
+
# the core routes are reloaded.
|
79
|
+
ActiveSupport::Reloader.to_run do
|
80
|
+
Decidim::Suomifi::Engine.add_omniauth_provider
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
initializer "decidim_suomifi.mail_interceptors" do
|
85
|
+
ActionMailer::Base.register_interceptor(
|
86
|
+
MailInterceptors::GeneratedRecipientsInterceptor
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.add_omniauth_provider
|
91
|
+
# Add :suomifi to the Decidim omniauth providers
|
92
|
+
providers = ::Decidim::User::OMNIAUTH_PROVIDERS
|
93
|
+
unless providers.include?(:suomifi)
|
94
|
+
providers << :suomifi
|
95
|
+
::Decidim::User.send(:remove_const, :OMNIAUTH_PROVIDERS)
|
96
|
+
::Decidim::User.const_set(:OMNIAUTH_PROVIDERS, providers)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Suomifi
|
5
|
+
module MailInterceptors
|
6
|
+
# Prevents sending emails to the auto-generated email addresses.
|
7
|
+
class GeneratedRecipientsInterceptor
|
8
|
+
def self.delivering_email(message)
|
9
|
+
return unless Decidim::Suomifi.auto_email_domain
|
10
|
+
|
11
|
+
# Regexp to match the auto-generated emails
|
12
|
+
regexp = /^suomifi-[a-z0-9]{32}@#{Decidim::Suomifi.auto_email_domain}$/
|
13
|
+
|
14
|
+
# Remove the auto-generated email from the message recipients
|
15
|
+
message.to = message.to.reject { |email| email =~ regexp } if message.to
|
16
|
+
message.cc = message.cc.reject { |email| email =~ regexp } if message.cc
|
17
|
+
message.bcc = message.bcc.reject { |email| email =~ regexp } if message.bcc
|
18
|
+
|
19
|
+
# Prevent delivery in case there are no recipients on the email
|
20
|
+
message.perform_deliveries = false if message.to.empty?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Suomifi
|
5
|
+
module Test
|
6
|
+
class CertStore
|
7
|
+
attr_reader :certificate, :private_key, :sign_certificate, :sign_private_key
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# Certificates
|
11
|
+
certgen = OmniAuth::Suomifi::Test::CertificateGenerator.new
|
12
|
+
@certificate = certgen.certificate
|
13
|
+
@private_key = certgen.private_key
|
14
|
+
|
15
|
+
# Use local certificate and private key for signing because otherwise the
|
16
|
+
# locally signed SAMLResponse's signature cannot be properly validated as
|
17
|
+
# we cannot sign it using the actual environments private key which is
|
18
|
+
# unknown.
|
19
|
+
sign_certgen = OmniAuth::Suomifi::Test::CertificateGenerator.new
|
20
|
+
@sign_certificate = sign_certgen.certificate
|
21
|
+
@sign_private_key = sign_certgen.private_key
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Suomifi
|
5
|
+
module Test
|
6
|
+
class Runtime
|
7
|
+
# Ability to stub the requests already in the control class
|
8
|
+
include WebMock::API
|
9
|
+
|
10
|
+
def self.initializer(&block)
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.initialize
|
15
|
+
new.instance_initialize(&@block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.load_app
|
19
|
+
engine_spec_dir = File.join(Dir.pwd, "spec")
|
20
|
+
|
21
|
+
require "#{Decidim::Dev.dummy_app_path}/config/environment"
|
22
|
+
|
23
|
+
Dir["#{engine_spec_dir}/shared/**/*.rb"].each { |f| require f }
|
24
|
+
|
25
|
+
require "paper_trail/frameworks/rspec"
|
26
|
+
|
27
|
+
require "decidim/dev/test/spec_helper"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.cert_store
|
31
|
+
@cert_store ||= CertStore.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def instance_initialize
|
35
|
+
yield self
|
36
|
+
|
37
|
+
# Setup the Suomi.fi OmniAuth strategy for Devise
|
38
|
+
# ::Devise.setup do |config|
|
39
|
+
# config.omniauth(
|
40
|
+
# :suomifi,
|
41
|
+
# Decidim::Suomifi.omniauth_settings
|
42
|
+
# )
|
43
|
+
# end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|