decidim-mpassid 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 +207 -0
- data/Rakefile +17 -0
- data/app/controllers/decidim/mpassid/omniauth_callbacks_controller.rb +247 -0
- data/app/controllers/decidim/mpassid/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/mpassid.rb +105 -0
- data/lib/decidim/mpassid/engine.rb +101 -0
- data/lib/decidim/mpassid/mail_interceptors.rb +9 -0
- data/lib/decidim/mpassid/mail_interceptors/generated_recipients_interceptor.rb +25 -0
- data/lib/decidim/mpassid/test/cert_store.rb +21 -0
- data/lib/decidim/mpassid/test/runtime.rb +48 -0
- data/lib/decidim/mpassid/verification.rb +5 -0
- data/lib/decidim/mpassid/verification/engine.rb +43 -0
- data/lib/decidim/mpassid/verification/manager.rb +17 -0
- data/lib/decidim/mpassid/verification/metadata_collector.rb +58 -0
- data/lib/decidim/mpassid/version.rb +8 -0
- data/lib/generators/decidim/mpassid/install_generator.rb +127 -0
- data/lib/generators/templates/mpassid_initializer.rb +10 -0
- data/lib/generators/templates/mpassid_initializer_test.rb +3 -0
- metadata +107 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Mpassid
|
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_mpassid_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
|
+
mpassid_nids:
|
6
|
+
explanation: Identify yourself using the MPASSid identity service.
|
7
|
+
name: MPASSid identity
|
8
|
+
mpassid:
|
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 MPASSid.
|
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 MPASSid
|
20
|
+
destroy:
|
21
|
+
success: Authorization sucessfully reset.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
fi:
|
2
|
+
decidim:
|
3
|
+
authorization_handlers:
|
4
|
+
mpassid_nids:
|
5
|
+
explanation: Tunnista itsesi MPASSid-tunnistuspalvelun avulla.
|
6
|
+
name: MPASSid tunnistus
|
7
|
+
mpassid:
|
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 MPASSid-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 MPASSid-palvelun avulla
|
19
|
+
destroy:
|
20
|
+
success: Varmennus tyhjennetty onnistuneesti.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
sv:
|
2
|
+
decidim:
|
3
|
+
authorization_handlers:
|
4
|
+
mpassid_nids:
|
5
|
+
explanation: Identifiera dig själv med MPASSid-identifikation.
|
6
|
+
name: MPASSid-identifikation
|
7
|
+
mpassid:
|
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 MPASSid.
|
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 MPASSid
|
19
|
+
destroy:
|
20
|
+
success: Tillståndet återställs efterhand.
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "omniauth"
|
4
|
+
require "omniauth-mpassid"
|
5
|
+
|
6
|
+
require_relative "mpassid/version"
|
7
|
+
require_relative "mpassid/engine"
|
8
|
+
require_relative "mpassid/verification"
|
9
|
+
require_relative "mpassid/mail_interceptors"
|
10
|
+
|
11
|
+
module Decidim
|
12
|
+
module Mpassid
|
13
|
+
include ActiveSupport::Configurable
|
14
|
+
|
15
|
+
@configured = false
|
16
|
+
|
17
|
+
# :production - For MPASSid production environment
|
18
|
+
# :test - For MPASSid test environment
|
19
|
+
config_accessor :mode, instance_reader: false
|
20
|
+
|
21
|
+
# Defines the auto email domain to generate verified email addresses upon
|
22
|
+
# the user's registration automatically that have format similar to
|
23
|
+
# "mpassid-identifier@auto-email-domain.fi"
|
24
|
+
config_accessor :auto_email_domain
|
25
|
+
|
26
|
+
config_accessor :sp_entity_id, instance_reader: false
|
27
|
+
|
28
|
+
# Extra configuration for the omniauth strategy
|
29
|
+
config_accessor :extra do
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Allows customizing the authorization workflow e.g. for adding custom
|
34
|
+
# workflow options or configuring an action authorizer for the
|
35
|
+
# particular needs.
|
36
|
+
config_accessor :workflow_configurator do
|
37
|
+
lambda do |workflow|
|
38
|
+
# By default, expiration is set to 0 minutes which means it will
|
39
|
+
# never expire.
|
40
|
+
workflow.expires_in = 0.minutes
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Allows customizing how the authorization metadata gets collected from
|
45
|
+
# the SAML attributes passed from the authorization endpoint.
|
46
|
+
config_accessor :metadata_collector_class do
|
47
|
+
Decidim::Mpassid::Verification::MetadataCollector
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.configured?
|
51
|
+
@configured
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.configure
|
55
|
+
@configured = true
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.mode
|
60
|
+
return config.mode if config.mode
|
61
|
+
return :production unless Rails.application.secrets.omniauth
|
62
|
+
return :production unless Rails.application.secrets.omniauth[:mpassid]
|
63
|
+
|
64
|
+
# Read the mode from the secrets
|
65
|
+
secrets = Rails.application.secrets.omniauth[:mpassid]
|
66
|
+
secrets[:mode] == "test" ? :test : :production
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.sp_entity_id
|
70
|
+
return config.sp_entity_id if config.sp_entity_id
|
71
|
+
|
72
|
+
"#{application_host}/users/auth/mpassid/metadata"
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.omniauth_settings
|
76
|
+
settings = {
|
77
|
+
mode: mode,
|
78
|
+
sp_entity_id: sp_entity_id
|
79
|
+
}
|
80
|
+
settings.merge!(config.extra) if config.extra.is_a?(Hash)
|
81
|
+
settings
|
82
|
+
end
|
83
|
+
|
84
|
+
# Used to determine the default service provider entity ID in case not
|
85
|
+
# specifically set by the `sp_entity_id` configuration option.
|
86
|
+
def self.application_host
|
87
|
+
conf = Rails.application.config
|
88
|
+
url_options = conf.action_controller.default_url_options
|
89
|
+
url_options = conf.action_mailer.default_url_options if !url_options || !url_options[:host]
|
90
|
+
url_options ||= {}
|
91
|
+
|
92
|
+
host = url_options[:host]
|
93
|
+
port = url_options[:port]
|
94
|
+
if host.blank?
|
95
|
+
# Default to local development environment
|
96
|
+
host = "http://localhost"
|
97
|
+
port ||= 3000
|
98
|
+
end
|
99
|
+
|
100
|
+
return "#{host}:#{port}" if port && ![80, 443].include?(port.to_i)
|
101
|
+
|
102
|
+
host
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Mpassid
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace Decidim::Mpassid
|
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/mpassid",
|
17
|
+
to: "omniauth_callbacks#passthru",
|
18
|
+
as: "user_mpassid_omniauth_authorize",
|
19
|
+
via: [:get, :post]
|
20
|
+
)
|
21
|
+
|
22
|
+
match(
|
23
|
+
"/users/auth/mpassid/callback",
|
24
|
+
to: "omniauth_callbacks#mpassid",
|
25
|
+
as: "user_mpassid_omniauth_callback",
|
26
|
+
via: [:get, :post]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
initializer "decidim_mpassid.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
|
+
# "mpassid" authentication.
|
36
|
+
Decidim::Core::Engine.routes.prepend do
|
37
|
+
mount Decidim::Mpassid::Engine => "/"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
initializer "decidim_mpassid.setup", before: "devise.omniauth" do
|
42
|
+
next unless Decidim::Mpassid.configured?
|
43
|
+
|
44
|
+
# Configure the SAML OmniAuth strategy for Devise
|
45
|
+
::Devise.setup do |config|
|
46
|
+
config.omniauth(
|
47
|
+
:mpassid,
|
48
|
+
Decidim::Mpassid.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/mpassid(/.*)?}
|
58
|
+
env["devise.mapping"] = ::Devise.mappings[:user]
|
59
|
+
Decidim::Mpassid::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_mpassid.omniauth_provider", after: :load_config_initializers do
|
70
|
+
next unless Decidim::Mpassid.configured?
|
71
|
+
|
72
|
+
Decidim::Mpassid::Engine.add_omniauth_provider
|
73
|
+
|
74
|
+
# This also needs to run as a callback for the reloader because
|
75
|
+
# otherwise the mpassid 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::Mpassid::Engine.add_omniauth_provider
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
initializer "decidim_mpassid.mail_interceptors" do
|
85
|
+
ActionMailer::Base.register_interceptor(
|
86
|
+
MailInterceptors::GeneratedRecipientsInterceptor
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.add_omniauth_provider
|
91
|
+
# Add :mpassid to the Decidim omniauth providers
|
92
|
+
providers = ::Decidim::User::OMNIAUTH_PROVIDERS
|
93
|
+
unless providers.include?(:mpassid)
|
94
|
+
providers << :mpassid
|
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 Mpassid
|
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::Mpassid.auto_email_domain
|
10
|
+
|
11
|
+
# Regexp to match the auto-generated emails
|
12
|
+
regexp = /^mpassid-[a-z0-9]{32}@#{Decidim::Mpassid.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,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Mpassid
|
5
|
+
module Test
|
6
|
+
class CertStore
|
7
|
+
attr_reader :sign_certificate, :sign_private_key
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# Use local certificate and private key for signing because otherwise
|
11
|
+
# the locally signed SAMLResponse's signature cannot be properly
|
12
|
+
# validated as we cannot sign it using the actual environments private
|
13
|
+
# key which is unknown.
|
14
|
+
sign_certgen = OmniAuth::MPASSid::Test::CertificateGenerator.new
|
15
|
+
@sign_certificate = sign_certgen.certificate
|
16
|
+
@sign_private_key = sign_certgen.private_key
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Mpassid
|
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 MPASSid OmniAuth strategy for Devise
|
38
|
+
# ::Devise.setup do |config|
|
39
|
+
# config.omniauth(
|
40
|
+
# :mpassid,
|
41
|
+
# Decidim::Mpassid.omniauth_settings
|
42
|
+
# )
|
43
|
+
# end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Mpassid
|
5
|
+
module Verification
|
6
|
+
# This is an engine that performs user authorization.
|
7
|
+
class Engine < ::Rails::Engine
|
8
|
+
isolate_namespace Decidim::Mpassid::Verification
|
9
|
+
|
10
|
+
paths["db/migrate"] = nil
|
11
|
+
paths["lib/tasks"] = nil
|
12
|
+
|
13
|
+
routes do
|
14
|
+
resource :authorizations, only: [:new], as: :authorization
|
15
|
+
|
16
|
+
root to: "authorizations#new"
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "decidim_mpassid.verification_workflow", after: :load_config_initializers do
|
20
|
+
next unless Decidim::Mpassid.configured?
|
21
|
+
|
22
|
+
# We cannot use the name `:mpassid` for the verification workflow
|
23
|
+
# because otherwise the route namespace (decidim_mpassid) would
|
24
|
+
# conflict with the main engine controlling the authentication flows.
|
25
|
+
# The main problem that this would bring is that the root path for
|
26
|
+
# this engine would not be found.
|
27
|
+
Decidim::Verifications.register_workflow(:mpassid_nids) do |workflow|
|
28
|
+
workflow.engine = Decidim::Mpassid::Verification::Engine
|
29
|
+
|
30
|
+
Decidim::Mpassid::Verification::Manager.configure_workflow(workflow)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_seed
|
35
|
+
# Enable the `:mpassid_nids` authorization
|
36
|
+
org = Decidim::Organization.first
|
37
|
+
org.available_authorizations << :mpassid_nids
|
38
|
+
org.save!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|