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.
@@ -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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Mpassid
5
+ module MailInterceptors
6
+ autoload :GeneratedRecipientsInterceptor, "decidim/mpassid/mail_interceptors/generated_recipients_interceptor"
7
+ end
8
+ end
9
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "verification/metadata_collector"
4
+ require_relative "verification/manager"
5
+ require_relative "verification/engine"
@@ -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