decidim-mpassid 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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