decidim-trusted_ids 0.12.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +344 -0
  4. data/Rakefile +40 -0
  5. data/app/commands/concerns/decidim/trusted_ids/system/create_organization_override.rb +22 -0
  6. data/app/commands/concerns/decidim/trusted_ids/system/needs_census_config.rb +29 -0
  7. data/app/commands/concerns/decidim/trusted_ids/system/update_organization_override.rb +21 -0
  8. data/app/controllers/concerns/decidim/trusted_ids/admin/impersonations_controller_override.rb +31 -0
  9. data/app/controllers/concerns/decidim/trusted_ids/check_existing_authorizations.rb +40 -0
  10. data/app/controllers/concerns/decidim/trusted_ids/check_omniauth_email_on_login.rb +25 -0
  11. data/app/controllers/concerns/decidim/trusted_ids/needs_trusted_ids_snippets.rb +26 -0
  12. data/app/events/decidim/trusted_ids/verifications/invalid_notification.rb +10 -0
  13. data/app/events/decidim/trusted_ids/verifications/success_notification.rb +33 -0
  14. data/app/forms/concerns/decidim/trusted_ids/system/organization_form_override.rb +93 -0
  15. data/app/forms/decidim/trusted_ids/verifications/trusted_ids_handler.rb +65 -0
  16. data/app/forms/decidim/via_oberta/verifications/via_oberta_handler.rb +103 -0
  17. data/app/jobs/decidim/trusted_ids/application_job.rb +8 -0
  18. data/app/jobs/decidim/trusted_ids/omniauth_verification_job.rb +76 -0
  19. data/app/jobs/decidim/trusted_ids/system/propagate_census_config_job.rb +29 -0
  20. data/app/jobs/decidim/trusted_ids/system/propagate_census_settings_job.rb +29 -0
  21. data/app/models/concerns/decidim/trusted_ids/authorization_override.rb +21 -0
  22. data/app/models/concerns/decidim/trusted_ids/organization_override.rb +17 -0
  23. data/app/models/decidim/trusted_ids/organization_config.rb +20 -0
  24. data/app/overrides/decidim/devise/sessions/new/add_trusted_id_buttons.html.erb.deface +3 -0
  25. data/app/overrides/decidim/devise/sessions/new/wrap_default_login_form.html.erb.deface +4 -0
  26. data/app/overrides/decidim/devise/shared/_omniauth_buttons/replace_button_content.html.erb.deface +17 -0
  27. data/app/overrides/decidim/devise/shared/_omniauth_buttons/skip_trusted_ids_provider.html.erb.deface +3 -0
  28. data/app/overrides/decidim/system/organizations/_advanced_settings/settings_form.html.erb.deface +3 -0
  29. data/app/overrides/decidim/system/organizations/_omniauth_provider/add_icon_placeholder.html.erb.deface +3 -0
  30. data/app/overrides/layouts/decidim/_head/add_trusted_ids_tags.html.erb.deface +11 -0
  31. data/app/packs/entrypoints/decidim_trusted_ids.js +3 -0
  32. data/app/packs/images/idcat_mobil-icon.svg +8 -0
  33. data/app/packs/images/valid-icon.png +0 -0
  34. data/app/packs/stylesheets/decidim/trusted_ids/oauth_icons.scss +75 -0
  35. data/app/services/decidim/via_oberta/api/request.rb +156 -0
  36. data/app/services/decidim/via_oberta/api/response.rb +67 -0
  37. data/app/views/decidim/trusted_ids/devise/sessions/_login_box.html.erb +30 -0
  38. data/app/views/decidim/trusted_ids/system/organizations/_icon_path_placeholder.html.erb +10 -0
  39. data/app/views/decidim/trusted_ids/system/organizations/_settings_form.html.erb +25 -0
  40. data/app/views/decidim/trusted_ids/verifications/_form.html.erb +39 -0
  41. data/app/views/decidim/via_oberta/verifications/_form.html.erb +56 -0
  42. data/config/assets.rb +16 -0
  43. data/config/i18n-tasks.yml +12 -0
  44. data/config/locales/ca.yml +132 -0
  45. data/config/locales/de.yml +168 -0
  46. data/config/locales/en.yml +169 -0
  47. data/config/locales/es.yml +132 -0
  48. data/config/locales/fr.yml +168 -0
  49. data/config/locales/it.yml +168 -0
  50. data/config/locales/oc.yml +132 -0
  51. data/config/locales/zz_fallbacks.rb +45 -0
  52. data/db/migrate/20230718082548_create_organization_trusted_ids_config.rb +14 -0
  53. data/lib/decidim/trusted_ids/engine.rb +181 -0
  54. data/lib/decidim/trusted_ids/test/factories.rb +21 -0
  55. data/lib/decidim/trusted_ids/verifications.rb +10 -0
  56. data/lib/decidim/trusted_ids/version.rb +10 -0
  57. data/lib/decidim/trusted_ids.rb +69 -0
  58. data/lib/omniauth/strategies/valid.rb +134 -0
  59. data/lib/omniauth/strategies.rb +7 -0
  60. data/lib/tasks/decidim_trusted_ids_upgrade_tasks.rake +5 -0
  61. data/package-lock.json +29154 -0
  62. data/package.json +43 -0
  63. metadata +200 -0
data/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # Decidim :: Trusted IDs
2
+
3
+ [![[CI] Lint](https://github.com/ConsorciAOC-PRJ/decidim-module-trusted-ids/actions/workflows/lint.yml/badge.svg)](https://github.com/ConsorciAOC-PRJ/decidim-module-trusted-ids/actions/workflows/lint.yml)
4
+ [![[CI] Test](https://github.com/ConsorciAOC-PRJ/decidim-module-trusted-ids/actions/workflows/test.yml/badge.svg)](https://github.com/ConsorciAOC-PRJ/decidim-module-trusted-ids/actions/workflows/test.yml)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/9cf5fb91121f322a50c6/maintainability)](https://codeclimate.com/github/ConsorciAOC-PRJ/decidim-module-trusted-ids/maintainability)
6
+ [![Codecov](https://codecov.io/gh/ConsorciAOC-PRJ/decidim-module-trusted-ids/branch/main/graph/badge.svg)](https://codecov.io/gh/ConsorciAOC-PRJ/decidim-module-trusted-ids)
7
+ [![Gem Version](https://badge.fury.io/rb/decidim-trusted-ids.svg)](https://badge.fury.io/rb/decidim-trusted-ids)
8
+
9
+ Translations:
10
+
11
+ [![Translations](https://badges.awesome-crowdin.com/translation-14246854-583683-update.png)](https://crowdin.com/project/decidim-trusted-ids)
12
+
13
+ This module is an evolution of the original [IdCat Mòbil](https://github.com/gencat/decidim-module-idcat_mobil) that was funded by the Department d'Exteriors of [Generalitat de Catalunya](http://gencat.cat) and developed by [CodiTramuntana](http://coditramuntana.com/).
14
+
15
+ On of the goals of this module is to decouple the authentication method from the IdCat Mòbil and pursue a more agnostic with a registry of providers. It also implements additional user options for extended verification methods using Via Oberta (or other providers) with improved user's control over personal data management.
16
+
17
+ The main goal, though, is to provide an, opinionated, easy to use, and secure authentication method for Decidim with **strong authentication systems**. That is, OAuth authentication systems that provide a unique identifier for each user. Preferably from official entities. Each user logged using this system will be verified automatically using the integrated verification method that this plugin provides, and will save the unique identifier as metadata.
18
+
19
+ Later on, this metadata will be used to connect to an additional, configurable, API to retrieve more information about the user. This information will be used to verify the user's identity with more granularity (for instance, restrict user actions to certain user properties, such as which census belongs to).
20
+
21
+ Finally, GDPR regulations are very present in this workflow, so user consent is a must. This plugins adds some additional steps to the registration process to give the user more control over the data that is being used, and to give the user the ability to revoke consent at any time.
22
+
23
+ ### Workflow:
24
+
25
+ ![Workflow](docs/workflow.png)
26
+
27
+ As is shown in the previous workflow, this module implements two stages for user registration:
28
+
29
+ ##### First stage:
30
+
31
+ - **A OAuth 2.0 authentication (login & register) method that is configurable**. At the moment the default provider is `valid` (is a built-in identity validator from the [AOC](https://www.aoc.cat/) consortium. However, it is possible to add other (external) providers, not necessarily available in this plugin. PRs are welcome to add OAuth registration/login in this plugin itself if they come from official sources. See the [CONTRIBUTING](CONTRIBUTING.md) file for more information.
32
+
33
+ - **Automatic creation of the first authorization with OAuth metadata**. This authorization will be used to verify the user's identity in the second stage. It saves some data from the OAuth provider, such as the unique identifier, the provider name, and the expiration date of the authorization or other. The saved data is configurable in this plugin. This data should be the one necessary to authenticate the users, without their intervention to an external census API provider in the second stage.
34
+
35
+ ##### Second stage:
36
+
37
+ - A second authorization ("Census authorization") can be issued to verify the user's identity. This authorization will be used to connect to an external API to retrieve more information about the user (mainly if belongs to a particular census). This authorization is optional and configurable in this plugin. It can be disabled if not needed.
38
+
39
+ - The default census authorization uses [Via Oberta](https://www.aoc.cat/serveis-aoc/via-oberta/) but others can be used instead (either internal or external). If you want to incorporate a new provider see the [CONTRIBUTING](CONTRIBUTING.md) file for more information.
40
+
41
+ - Once the user has obtained the census authorization, you can use it to ensure that the user belongs to a particular census. This increases the security and avoids spoofing attacks (if the second authorization methods does not uses user inputs).
42
+
43
+
44
+ ## Installation
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ ```ruby
49
+ gem "decidim-trusted_ids"
50
+ ```
51
+
52
+ Or, if you want to stay up to date with the latest changes use this line instead:
53
+
54
+ ```ruby
55
+ gem 'decidim-trusted_ids', git: "https://github.com/ConsorciAOC-PRJ/decidim-module-trusted-ids"
56
+ ```
57
+
58
+ And then execute:
59
+
60
+ ```bash
61
+ bundle
62
+ bin/rails decidim:upgrade
63
+ bin/rails db:migrate
64
+ ```
65
+
66
+ > **EXPERTS ONLY**
67
+ >
68
+ > Under the hood, when running `bundle exec rails decidim:upgrade` the `decidim-trusted_ids` gem will run the following (that can also be run manually if you consider):
69
+ >
70
+ > ```bash
71
+ > bin/rails decidim_trusted_ids:install:migrations
72
+ > ```
73
+
74
+ ## Using OAuth in Decidim
75
+
76
+ This module adds a new authentication method to Decidim that allows users to log in and register using an OAuth2 provider. This provider must be configured in the `ENV` variables or through an initializer.
77
+
78
+ One important thing to note is that, in order to use an OAuth authentication system, you need on the provider to register and authorize the return url of your Decidim instance. This is usually done by adding the URL of your Decidim instance to the list of authorized URLs in the OAuth provider's settings.
79
+
80
+ By default this plugin uses the `valid` provider, which is a built-in identity validator from the [AOC](https://www.aoc.cat/) consortium.
81
+
82
+ This means that you need to enable the following return url in the OAuth provider:
83
+
84
+ ```text
85
+ https://your-decidim-instance.com/users/auth/valid/callback
86
+ ```
87
+
88
+ If you are using a different provider, you should replace `valid` with the name of your provider.
89
+
90
+ ## Usage
91
+
92
+ This plugin comes prepared to be used solely with `ENV` variables, but you can also configure it through an initializer.
93
+
94
+ By default, you can use these variables to configure the module:
95
+
96
+ Environment variable | Description | Default value
97
+ --- | --- | ---
98
+ `OMNIAUTH_PROVIDER` | The OAuth2 provider to use. Currently only `valid` is built-in in this plugin. Note that this word will be used as a prefix (in uppercase) for all the omniauth values defined after this. If you use a different provider, say `foo`, nexts ENV vars will start with `FOO_` instead of `VALID_` | `valid`
99
+ `CUSTOM_LOGIN_SCREEN` | Whether to use a custom login screen or the default Decidim login screen. | `true` (only if omniauth is enabled)
100
+ `OMNIAUTH_ENABLED_BY_DEFAULT` | Whether the OAuth2 login is enabled by default. If false must be enabled in system. | `true` (if `VALID_CLIENT_ID` is present)
101
+ `OMNIAUTH_GLOBAL_ATTRIBUTES` | Attributes sent to the Omniauth provider that are not writeable in the `/system` admin. Separated by spaces. | `site icon_path scope`
102
+ `VALID_CLIENT_ID` | The OAuth2 client ID. Note that the prefix `VALID` is because `OMNIAUTH_PROVIDER` is set to "valid". Other values will require to name this variable accordingly (for instance `FOO_CLIENT_ID`). **IF this variable is empty, no OAuth login will be used**. | `nil`
103
+ `VALID_CLIENT_SECRET` | The OAuth2 client secret. | `nil`
104
+ `VALID_SITE` | The OAuth2 site. | `nil`
105
+ `VALID_ICON` | The icon used for the login button. | `media/images/valid-icon.png`
106
+ `VALID_SCOPE` | The OAuth2 scope that returns the necessary fields for registration (some OAuth method might override this making it unnecessary). | `autenticacio_usuari`
107
+ `VERIFICATION_EXPIRATION_TIME` | In seconds, how long the authorizations will be valid. Use zero or empty to prevent expiration. | `7776000` (90 days)
108
+ `SEND_VERIFICATION_NOTIFICATIONS` | Whether to send notifications to users when they are verified or the verification process fails. | `true`
109
+ `CENSUS_AUTHORIZATION_HANDLER` | The authorization handler to use for the census authorization. Currently only `via_oberta_handler` is built-in in this plugin. | `via_oberta_handler`
110
+ `CENSUS_AUTHORIZATION_FORM` | The authorization form to use for the census authorization. This is an standard [authorization form](https://docs.decidim.org/en/develop/customize/authorizations) and it should be responsible for the actions of connecting to a census API and handle any user interaction that might require. Currently only `ViaObertaHandler` is built-in in this plugin. | `Decidim::ViaOberta::Verifications::ViaObertaHandler`
111
+ `CENSUS_AUTHORIZATION_ENV` | The environment variable that will be used to store the census authorization. In the case of Via Oberta, calls to the proper URL api. | `production`
112
+ `CENSUS_AUTHORIZATION_API_URL` | The URL of the census API. By default it is empty, if defined, overrides the pre-defined URL obtained from the previous ENV var (if the census authorization is created this way). | `nil`
113
+ `CENSUS_AUTHORIZATION_SYSTEM_ATTRIBUTES` | This var defines which attributes need to be configured at the [/system](https://docs.decidim.org/en/v0.27/admin/system.html) multi-tenant super admin page. These might be secret properties that can be used by the census authorization but might vary from tenant to tenant. Each value must be a word, separated by spaces. See the [screenshots section](#screenshots). | `nif` `ine` `municipal_code` `province_code` `organization_name`
114
+ `VIA_OBERTA_PURPOSE` | The purpose of Via Oberta request.
115
+
116
+ In` `addition, metadata obtained from the OAuth provider that need to be stored in the `trusted_ids_handler` authorization can be configured through next variables.
117
+
118
+ Note that these ENVs variables work the same way as the previous `VALID_*` vars.
119
+ If the provider is `foo`, it should start with `FOO_METADATA_*`.
120
+
121
+ Any ENV var starting as `VALID_METADATA_SUFFIX` will make the plugin to save a metadata attribute called `suffix` as part of the authorization metadata encrypted hash.
122
+
123
+ As the the returned JSON from a successful OAuth login/registration might follow a different structure, you can configure the names of the fields that will be used to extract the metadata. This is done by using each word inside the value of the ENV var (separated by spaces) as a level in the JSON structure. For instance, if you have a return OAuth JSON data like this:
124
+
125
+ ```json
126
+ {
127
+ "uid": "12345678Z",
128
+ "provider": "valid",
129
+ "credentials": {
130
+ "expires_at": "2020-12-31T23:59:59Z"
131
+ },
132
+ "info": {
133
+ "identifier_type": "NIF",
134
+ "method": "idcat_mobil"
135
+ },
136
+ "assurance_level": "high"
137
+ }
138
+ ```
139
+
140
+ You can configure the ENV vars like this:
141
+
142
+ Environment variable | Value
143
+ --- | ---
144
+ `VALID_METADATA_EXPIRES_AT` | `credentials expires_at`
145
+ `VALID_METADATA_IDENTIFIER_TYPE` | `info identifier_type`
146
+ `VALID_METADATA_METHOD` | `info method`
147
+ `VALID_METADATA_ASSURANCE_LEVEL` | `info assurance_level`
148
+
149
+ And this will store in the `trusted_ids_handler` authorization metadata the following values:
150
+
151
+ ```json
152
+ {
153
+ "uid": "12345678Z",
154
+ "provider": "valid",
155
+ "extra" {
156
+ "expires_at": "2020-12-31T23:59:59Z",
157
+ "identifier_type": "NIF",
158
+ "method": "idcat_mobil",
159
+ "assurance_level": "high"
160
+ }
161
+ }
162
+ ```
163
+
164
+ Note that the `uid` and `provider` fields are always stored, and the `extra` field is used to store any other metadata.
165
+
166
+ ### Via initializer
167
+
168
+ If you are using a different source for your settings, you can also configure this module through an initializer.
169
+
170
+ For instance, create a file `config/initializers/decidim_trusted_ids.rb` with the following content:
171
+
172
+ ```ruby
173
+ Decidim::TrustedIds.configure do |config|
174
+ # The name of the omniauth provider, must be registered in Decidim.
175
+ config.omniauth_provider = "valid"
176
+ config.omniauth = {
177
+ enabled: true,
178
+ client_id: "my-client-id",
179
+ client_secret: "my-client-secret",
180
+ site: "https://valid.aoc.cat",
181
+ scope: "autenticacio_usuari",
182
+ icon: "media/images/valid-icon.png"
183
+ },
184
+ ...
185
+ end
186
+ ```
187
+
188
+ For the complete list of available options, see the [trusted_ids](lib/decidim/trusted_ids.rb) file.
189
+
190
+ ### Screenshots
191
+
192
+ - If system attributes are defined, it will available in the system configuration page:
193
+ ![System configuration](docs/system.png)
194
+
195
+ - If the census authorization is created, it will be available in the census authorization page.
196
+ This is how the Via Oberta Handler looks like:
197
+ ![Census authorization](docs/viaoberta.png)
198
+
199
+ - If the login screen is customized, it will look like this:
200
+ ![Login screen](docs/login.png)
201
+
202
+ ## Decidim core overrides
203
+
204
+ This module customizes Decidim core behavior through Deface overrides (for views) and module-based form extensions.
205
+
206
+ ### Views
207
+
208
+ - **`app/overrides/decidim/devise/sessions/new/add_trusted_id_buttons.html.erb.deface`** — Replaces the core call that renders omniauth buttons with `decidim/trusted_ids/devise/sessions/login_box`, showing the VÀLid login box on the login page.
209
+
210
+ - **`app/overrides/decidim/devise/sessions/new/wrap_default_login_form.html.erb.deface`** — Replaces the core `decidim_form_for` line to conditionally hide the default login form when the custom VÀLid login screen is enabled.
211
+
212
+ - **`app/overrides/decidim/devise/shared/_omniauth_buttons/skip_trusted_ids_provider.html.erb.deface`** — Inserts a guard to skip rendering the VÀLid provider in the core omniauth buttons loop on the sessions/new page when the custom login screen is enabled, avoiding duplicated buttons.
213
+
214
+ - **`app/overrides/decidim/devise/shared/_omniauth_buttons/replace_button_content.html.erb.deface`** — Replaces the core `oauth_icon provider` call for button content. For the VÀLid provider, it resolves `icon_path` from organization/module settings and falls back to `media/images/valid-icon.png`; for other providers it keeps `oauth_icon provider`. It rescues `Shakapacker::Manifest::MissingEntryError` to prevent crashes if the asset is missing.
215
+
216
+ - **`app/overrides/decidim/system/organizations/_omniauth_provider/add_icon_placeholder.html.erb.deface`** — Replaces the `icon_path` form row rendering in system settings with a module partial that adds a default-placeholder hint.
217
+
218
+ - **`app/overrides/decidim/system/organizations/_advanced_settings/settings_form.html.erb.deface`** — Inserts the trusted IDs settings partial at the top of the organization advanced settings panel.
219
+
220
+ - **`app/overrides/layouts/decidim/_head/add_trusted_ids_tags.html.erb.deface`** — Appends the `decidim_trusted_ids` stylesheet pack in the Decidim head layout so trusted IDs styles are available on pages that do not load them by default.
221
+
222
+ ### Forms
223
+
224
+ - **`Decidim::TrustedIds::System::OrganizationFormOverride`** — Included into `Decidim::System::RegisterOrganizationForm` and `Decidim::System::UpdateOrganizationForm`. Adds:
225
+ - Census configuration fields (expiration days, TOS, census settings).
226
+ - `validate_icon_path`: validates that if an `icon_path` is provided for the VÀLid provider, it must follow the `media/images/filename.ext` format and the file must exist in the module's `app/packs/images/` directory. This is future-proof: any new icon added there will automatically pass validation. Blank values are always accepted — on save, the encrypted default path is stored automatically.
227
+ - `EncryptedOmniauthSettingsOverride` (prepended module): when the VÀLid provider is being enabled and `icon_path` is blank, sets the default path (`media/images/valid-icon.png`) via the jsonb sub-attribute setter before calling `super`, so the core encrypts it and stores it in the database. This prevents `external_icon("")` from being called and crashing. Uses `prepend` so `super` correctly calls the core's `encrypted_omniauth_settings`.
228
+
229
+ ## Contributing
230
+
231
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ConsorciAOC-PRJ/decidim-module-trusted-ids.
232
+
233
+ If you want to create your own authorization or OmniAuth methods, make sure to read the [CONTRIBUTING.md](CONTRIBUTING.md) file first.
234
+
235
+ ### Developing
236
+
237
+ To start contributing to this project, first:
238
+
239
+ - Install the basic dependencies (such as Ruby and PostgreSQL)
240
+ - Clone this repository
241
+
242
+ Decidim's main repository also provides a Docker configuration file if you
243
+ prefer to use Docker instead of installing the dependencies locally on your
244
+ machine.
245
+
246
+ You can create the development app by running the following commands after
247
+ cloning this project:
248
+
249
+ ```bash
250
+ bundle
251
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake development_app
252
+ ```
253
+
254
+ Note that the database user has to have rights to create and drop a database in
255
+ order to create the dummy test app database.
256
+
257
+ Then to test how the module works in Decidim, start the development server:
258
+
259
+ ```bash
260
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bin/rails s
261
+ ```
262
+
263
+ Note that `bin/rails` is a convenient wrapper around the command `cd development_app; bundle exec rails`.
264
+
265
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
266
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
267
+ can add the environment variables to the root directory of the project in a file
268
+ named `.rbenv-vars`. If these are defined for the environment, you can omit
269
+ defining these in the commands shown above.
270
+
271
+ #### Webpacker notes
272
+
273
+ As latests versions of Decidim, this repository uses Shakapacker for Rails. This means that compilation
274
+ of assets is required every time a Javascript or CSS file is modified. Usually, this happens
275
+ automatically, but in some cases (specially when actively changes that type of files) you want to
276
+ speed up the process.
277
+
278
+ To do that, start in a separate terminal than the one with `bin/rails s`, and BEFORE it, the following command:
279
+
280
+ ```bash
281
+ bin/shakapacker-dev-server
282
+ ```
283
+
284
+ #### Code Styling
285
+
286
+ Please follow the code styling defined by the different linters that ensure we
287
+ are all talking with the same language collaborating on the same project. This
288
+ project is set to follow the same rules that Decidim itself follows.
289
+
290
+ [Rubocop](https://rubocop.readthedocs.io/) linter is used for the Ruby language.
291
+
292
+ You can run the code styling checks by running the following commands from the
293
+ console:
294
+
295
+ ```bash
296
+ bundle exec rubocop
297
+ ```
298
+
299
+ To ease up following the style guide, you should install the plugin to your
300
+ favorite editor, such as:
301
+
302
+ - Sublime Text - [Sublime RuboCop](https://github.com/pderichs/sublime_rubocop)
303
+ - Visual Studio Code - [Rubocop for Visual Studio Code](https://github.com/misogi/vscode-ruby-rubocop)
304
+
305
+ ### Testing
306
+
307
+ To run the tests run the following in the gem development path:
308
+
309
+ ```bash
310
+ bundle
311
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake test_app
312
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rspec
313
+ ```
314
+
315
+ Note that the database user has to have rights to create and drop a database in
316
+ order to create the dummy test app database.
317
+
318
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
319
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
320
+ can add these environment variables to the root directory of the project in a
321
+ file named `.rbenv-vars`. In this case, you can omit defining these in the
322
+ commands shown above.
323
+
324
+ ### Test code coverage
325
+
326
+ Running tests automatically generates a code coverage report. To generate the complete report run all the tests using this command:
327
+
328
+ ```bash
329
+ bundle exec rspec
330
+ ```
331
+
332
+ This will generate a folder named `coverage` in the project root which contains
333
+ the code coverage report.
334
+
335
+ ### Localization
336
+
337
+ If you would like to see this module in your own language, you can help with its
338
+ translation at Crowdin:
339
+
340
+ https://crowdin.com/project/decidim-trusted-ids
341
+
342
+ ## License
343
+
344
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+
5
+ def install_module(path)
6
+ Dir.chdir(path) do
7
+ system("bundle exec rake decidim_trusted_ids:install:migrations")
8
+ system("bundle exec rake db:migrate")
9
+ end
10
+ end
11
+
12
+ def seed_db(path)
13
+ Dir.chdir(path) do
14
+ system("bundle exec rake db:seed")
15
+ end
16
+ end
17
+
18
+ desc "Generates a dummy app for testing"
19
+ task test_app: "decidim:generate_external_test_app" do
20
+ ENV["RAILS_ENV"] = "test"
21
+ install_module("spec/decidim_dummy_app")
22
+ end
23
+
24
+ desc "Generates a development app."
25
+ task :development_app do
26
+ Bundler.with_original_env do
27
+ generate_decidim_app(
28
+ "development_app",
29
+ "--app_name",
30
+ "#{base_app_name}_development_app",
31
+ "--path",
32
+ "..",
33
+ "--recreate_db",
34
+ "--demo"
35
+ )
36
+ end
37
+
38
+ install_module("development_app")
39
+ seed_db("development_app")
40
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TrustedIds
5
+ module System
6
+ module CreateOrganizationOverride
7
+ extend ActiveSupport::Concern
8
+ include NeedsCensusConfig
9
+
10
+ included do
11
+ alias_method :trusted_ids_original_create_organization, :create_organization
12
+
13
+ def create_organization
14
+ organization = trusted_ids_original_create_organization
15
+ save_census_config!(organization)
16
+ organization
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TrustedIds
5
+ module System
6
+ module NeedsCensusConfig
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ def save_census_config!(organization)
11
+ conf = Decidim::TrustedIds::OrganizationConfig.find_or_initialize_by(organization: organization)
12
+ conf.handler = TrustedIds.census_authorization[:handler]
13
+ return if conf.handler.blank?
14
+
15
+ conf.settings = form.trusted_ids_census_settings
16
+ conf.tos = form.trusted_ids_census_tos
17
+ conf.expiration_days = form.trusted_ids_census_expiration_days
18
+ conf.save!
19
+
20
+ propagate = []
21
+ propagate << :expiration_days if form.census_expiration_apply_all_tenants
22
+ propagate << :tos if form.census_tos_apply_all_tenants
23
+ PropagateCensusConfigJob.perform_later(conf.id, propagate) if propagate.any?
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TrustedIds
5
+ module System
6
+ module UpdateOrganizationOverride
7
+ extend ActiveSupport::Concern
8
+ include NeedsCensusConfig
9
+
10
+ included do
11
+ alias_method :trusted_ids_original_save_organization, :save_organization
12
+
13
+ def save_organization
14
+ trusted_ids_original_save_organization
15
+ save_census_config!(organization)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module TrustedIds
7
+ module Admin
8
+ module ImpersonationsControllerOverride
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ # normalize_provider_name is used in this builtin authorizations
13
+ helper Decidim::OmniauthHelper
14
+
15
+ # the internal 1st step verification shouldn't be used for impersonations as it is linked with an OAuth login
16
+ def other_available_authorization_handlers
17
+ Decidim::Verifications::Adapter.from_collection(
18
+ current_organization.available_authorization_handlers - [handler_name, "trusted_ids_handler"]
19
+ )
20
+ end
21
+
22
+ def available_authorization_handlers
23
+ Decidim::Verifications::Adapter.from_collection(
24
+ current_organization.available_authorization_handlers - ["trusted_ids_handler"]
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module TrustedIds
7
+ module CheckExistingAuthorizations
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ # rubocop:disable Rails/LexicallyScopedActionFilter
12
+ before_action :check_existing_authorizations, only: [:new, :create]
13
+ before_action :check_parent_census_authorizations, only: [:new, :create]
14
+ # rubocop:enable Rails/LexicallyScopedActionFilter
15
+
16
+ private
17
+
18
+ def check_existing_authorizations
19
+ return unless authorization&.granted?
20
+
21
+ flash[:alert] = I18n.t("decidim.verifications.authorizations.errors.already_verified")
22
+ redirect_to decidim.account_path
23
+ end
24
+
25
+ def check_parent_census_authorizations
26
+ return unless handler_name.to_s == Decidim::TrustedIds.census_authorization[:handler].to_s
27
+ return if trusted_ids_authorization&.granted?
28
+
29
+ flash[:alert] = I18n.t("decidim.verifications.authorizations.errors.pending_authorization",
30
+ handler_name: I18n.t("decidim.authorization_handlers.trusted_ids_handler.name"))
31
+ redirect_to decidim.account_path
32
+ end
33
+
34
+ def trusted_ids_authorization
35
+ @trusted_ids_authorization ||= Decidim::Authorization.find_by(user: current_user, name: "trusted_ids_handler")
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module TrustedIds
7
+ module CheckOmniauthEmailOnLogin
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ before_action :prevent_omniauth_login, only: Decidim::TrustedIds.omniauth_provider
12
+
13
+ private
14
+
15
+ def prevent_omniauth_login
16
+ return unless user_signed_in?
17
+ return if current_user.email == verified_email
18
+
19
+ flash[:alert] = I18n.t("decidim.trusted_ids.sessions.different_omniauth_emails")
20
+ redirect_back fallback_location: account_path
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module TrustedIds
7
+ module NeedsTrustedIdsSnippets
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ helper_method :snippets
12
+ end
13
+
14
+ def snippets
15
+ @snippets ||= Decidim::Snippets.new
16
+
17
+ unless @snippets.any?(:trusted_ids_global)
18
+ @snippets.add(:trusted_ids_global, ActionController::Base.helpers.stylesheet_pack_tag("decidim_trusted_ids"))
19
+ @snippets.add(:head, @snippets.for(:trusted_ids_global))
20
+ end
21
+
22
+ @snippets
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TrustedIds
5
+ module Verifications
6
+ class InvalidNotification < SuccessNotification
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module TrustedIds
5
+ module Verifications
6
+ class SuccessNotification < Decidim::Events::SimpleEvent
7
+ i18n_attributes :handler_name
8
+
9
+ def resource_path
10
+ Decidim::Verifications::Engine.routes.url_helpers.new_authorization_path(handler: authorization&.name)
11
+ end
12
+
13
+ def resource_url
14
+ Decidim::Verifications::Engine.routes.url_helpers.new_authorization_url(handler: authorization&.name, host: organization.host)
15
+ end
16
+
17
+ def handler_name
18
+ I18n.t("decidim.authorization_handlers.#{authorization&.name}.name")
19
+ end
20
+
21
+ def resource_title
22
+ handler_name
23
+ end
24
+
25
+ def authorization
26
+ return unless resource.is_a? Decidim::Authorization
27
+
28
+ @authorization ||= resource
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end