diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index e810806..971805b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,7 +13,7 @@ env:
jobs:
test:
name: Test
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
services:
postgres:
image: postgres:11
diff --git a/.gitignore b/.gitignore
index 6282880..87bc84f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,11 @@
/public/packs-test
/public/sw.js
/public/sw.js.map
+public/sw.js.LICENSE.txt
+public/sw.js.br
+public/sw.js.gz
+public/sw.js.map.br
+public/sw.js.map.gz
# Ignore node modules
/node_modules
diff --git a/Gemfile b/Gemfile
index d55f9ff..9616dc2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -14,6 +14,8 @@ gem "decidim", DECIDIM_VERSION
# gem "decidim-templates", DECIDIM_VERSION
gem "decidim-decidim_awesome"
+gem "decidim-direct_verifications", git: "https://github.com/Platoniq/decidim-verifications-direct_verifications"
+gem "decidim-kids", git: "https://github.com/AjuntamentdeBarcelona/decidim-module-kids"
gem "decidim-term_customizer", git: "https://github.com/mainio/decidim-module-term_customizer", branch: "main"
gem "bootsnap", "~> 1.3"
diff --git a/Gemfile.lock b/Gemfile.lock
index a301068..648eb35 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,22 @@
+GIT
+ remote: https://github.com/AjuntamentdeBarcelona/decidim-module-kids
+ revision: df16cf4de13c306348761d850d19815c8221874e
+ specs:
+ decidim-kids (0.2.0)
+ decidim-core (>= 0.28, < 0.29)
+ decidim-system (>= 0.28, < 0.29)
+ decidim-verifications (>= 0.28, < 0.29)
+ deface (>= 1.5)
+
+GIT
+ remote: https://github.com/Platoniq/decidim-verifications-direct_verifications
+ revision: f55f45c58a34b468f5eb753cc988233ec11d561c
+ specs:
+ decidim-direct_verifications (1.4.0)
+ decidim-admin (>= 0.28.0, < 0.29)
+ decidim-core (>= 0.28.0, < 0.29)
+ deface (~> 1.5)
+
GIT
remote: https://github.com/mainio/decidim-module-term_customizer
revision: 9133eea57ebfc4164b640efd1ac6b9ca1628c793
@@ -887,6 +906,8 @@ DEPENDENCIES
decidim (= 0.28.2)
decidim-decidim_awesome
decidim-dev (= 0.28.2)
+ decidim-direct_verifications!
+ decidim-kids!
decidim-term_customizer!
figaro (~> 1.2)
letter_opener_web (~> 2.0)
diff --git a/README.md b/README.md
index 7db0aaf..9afb84c 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,25 @@ Free Open-Source participatory democracy, citizen participation and open governm
This is the open-source repository for decidim_inspire, based on [Decidim](https://github.com/decidim/decidim).
+## Custom census authorization handler
+
+This authorization handler allows users to be directly verified with their birthdates by checking the records in a table.
+
+You need to create records for the model `Decidim::CustomCensusRecord`. For example:
+
+```ruby
+[
+ { email: "john.doe@example.org", date_of_birth: "1956-03-14" },
+ { email: "jane.smith@example.org", date_of_birth: "1998-12-06" }
+].each do |record|
+ Decidim::CustomCensusRecord.create(email: record[:email], metadata: { date_of_birth: record[:date_of_birth] })
+end
+```
+
+The verification will succeed if the user is in the census and introduces the same birthdate as the one in the database.
+
+This authorization handler will allow us to work with the [Decidim Kids](https://github.com/AjuntamentdeBarcelona/decidim-module-kids) module.
+
## Setting up the application
You will need to do some steps before having the app working properly once you have deployed it:
diff --git a/app/models/decidim/custom_census_record.rb b/app/models/decidim/custom_census_record.rb
new file mode 100644
index 0000000..ab37093
--- /dev/null
+++ b/app/models/decidim/custom_census_record.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Decidim
+ class CustomCensusRecord < ApplicationRecord
+ include Decidim::RecordEncryptor
+
+ validates :email, uniqueness: true
+
+ encrypt_attribute :metadata, type: :hash
+
+ def date_of_birth
+ metadata["date_of_birth"]
+ end
+ end
+end
diff --git a/app/services/custom_census_authorization_handler.rb b/app/services/custom_census_authorization_handler.rb
new file mode 100644
index 0000000..08119e6
--- /dev/null
+++ b/app/services/custom_census_authorization_handler.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+# Checks the authorization against the census for Barcelona.
+require "digest/md5"
+
+# This class performs a check against the census file in order to verify the citizen's residence.
+class CustomCensusAuthorizationHandler < Decidim::AuthorizationHandler
+ attribute :date_of_birth, Date
+
+ validates :date_of_birth, presence: true
+
+ validate :present_in_census
+
+ def metadata
+ super.merge(date_of_birth: parsed_date_of_birth)
+ end
+
+ def unique_id
+ Digest::MD5.hexdigest("#{user.email}-#{Rails.application.secrets.secret_key_base}")
+ end
+
+ private
+
+ def parsed_date_of_birth
+ @parsed_date_of_birth ||= date_of_birth&.strftime("%Y-%m-%d")
+ end
+
+ def present_in_census
+ record = Decidim::CustomCensusRecord.find_by(email: user.email)
+ return errors.add(:base, I18n.t("custom_census_authorization_handler.errors.not_found")) unless record
+
+ errors.add(:date_of_birth, I18n.t("custom_census_authorization_handler.errors.invalid_date_of_birth")) unless record.date_of_birth == parsed_date_of_birth
+ end
+end
diff --git a/app/views/custom_census_authorization/_form.html.erb b/app/views/custom_census_authorization/_form.html.erb
new file mode 100644
index 0000000..5aaef1b
--- /dev/null
+++ b/app/views/custom_census_authorization/_form.html.erb
@@ -0,0 +1,7 @@
+
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index 621c5a1..e106c5b 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -11,3 +11,5 @@ ignore_missing:
- layouts.decidim.footer.cc_by_license
- layouts.decidim.footer.decidim_logo
- layouts.decidim.footer.made_with_open_source
+ - custom_census_authorization.form.*
+ - custom_census_authorization_handler.errors.*
diff --git a/config/initializers/decidim.rb b/config/initializers/decidim.rb
index 253e436..1b35fa5 100644
--- a/config/initializers/decidim.rb
+++ b/config/initializers/decidim.rb
@@ -494,3 +494,9 @@
# Inform Decidim about the assets folder
Decidim.register_assets_path File.expand_path("app/packs", Rails.application.root)
+
+Decidim::Verifications.register_workflow(:custom_census_authorization_handler) do |auth|
+ auth.form = "CustomCensusAuthorizationHandler"
+ auth.renewable = true
+ auth.time_between_renewals = 1.day
+end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 63f1c3e..12b9d2a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1 +1,27 @@
en:
+ activemodel:
+ attributes:
+ custom_census_authorization_handler:
+ date_of_birth: Date of birth
+ custom_census_authorization:
+ form:
+ date_select:
+ day: Day
+ month: Month
+ year: Year
+ custom_census_authorization_handler:
+ errors:
+ not_found: The user is not present in the census
+ invalid_date_of_birth: The date of birth is not correct
+ decidim:
+ authorization_handlers:
+ custom_census_authorization_handler:
+ explanation: Verify against the custom census authorization handler
+ fields:
+ date_of_birth: Date of birth
+ name: Custom census
+ verifications:
+ authorizations:
+ first_login:
+ actions:
+ custom_census_authorization_handler: Verify against the custom census authorization handler
diff --git a/db/migrate/20241011083922_create_organization_minors_configs.decidim_kids.rb b/db/migrate/20241011083922_create_organization_minors_configs.decidim_kids.rb
new file mode 100644
index 0000000..2cbc978
--- /dev/null
+++ b/db/migrate/20241011083922_create_organization_minors_configs.decidim_kids.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+# This migration comes from decidim_kids (originally 20221017110422)
+
+class CreateOrganizationMinorsConfigs < ActiveRecord::Migration[5.2]
+ def change
+ create_table :decidim_kids_organization_configs do |t|
+ t.integer :decidim_organization_id, null: false, index: { name: "index_decidim_kids_organization" }
+ t.boolean :enable_minors_participation, null: false, default: false
+ t.integer :minimum_minor_age, null: false, default: 10
+ t.integer :maximum_minor_age, null: false, default: 13
+ t.string :minors_authorization
+ t.string :tutors_authorization
+ t.integer :maximum_minor_accounts, null: false, default: 3
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20241011083923_create_decidim_kids_minor_accounts.decidim_kids.rb b/db/migrate/20241011083923_create_decidim_kids_minor_accounts.decidim_kids.rb
new file mode 100644
index 0000000..d16f4bc
--- /dev/null
+++ b/db/migrate/20241011083923_create_decidim_kids_minor_accounts.decidim_kids.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+# This migration comes from decidim_kids (originally 20221024124523)
+
+class CreateDecidimKidsMinorAccounts < ActiveRecord::Migration[6.1]
+ def change
+ create_table :decidim_kids_minor_accounts do |t|
+ t.references :decidim_tutor, null: false, index: true, foreign_key: { to_table: "decidim_users" }
+ t.references :decidim_minor, null: false, index: true, foreign_key: { to_table: "decidim_users" }
+ t.timestamps
+ end
+
+ add_index :decidim_kids_minor_accounts, [:decidim_tutor_id, :decidim_minor_id], unique: true, name: "decidim_kids_minor_accounts_unique_tutor_and_minor_ids"
+ end
+end
diff --git a/db/migrate/20241011083924_create_decidim_kids_minor_data.decidim_kids.rb b/db/migrate/20241011083924_create_decidim_kids_minor_data.decidim_kids.rb
new file mode 100644
index 0000000..25d3962
--- /dev/null
+++ b/db/migrate/20241011083924_create_decidim_kids_minor_data.decidim_kids.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+# This migration comes from decidim_kids (originally 20221027211859)
+
+class CreateDecidimKidsMinorData < ActiveRecord::Migration[6.1]
+ def change
+ create_table :decidim_kids_minor_data do |t|
+ t.references :decidim_user, null: false, index: true
+ t.string :name # encrypted
+ t.string :birthday # encrypted
+ t.string :email # encrypted
+ t.jsonb :extra_data, null: false, default: {}
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20241011083925_create_impersonation_minor_logs.decidim_kids.rb b/db/migrate/20241011083925_create_impersonation_minor_logs.decidim_kids.rb
new file mode 100644
index 0000000..c4e1130
--- /dev/null
+++ b/db/migrate/20241011083925_create_impersonation_minor_logs.decidim_kids.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+# This migration comes from decidim_kids (originally 20221127103636)
+
+class CreateImpersonationMinorLogs < ActiveRecord::Migration[6.1]
+ def change
+ create_table :decidim_kids_impersonation_minor_logs do |t|
+ t.references :decidim_tutor, index: true
+ t.references :decidim_minor, index: true
+ t.datetime :started_at
+ t.datetime :ended_at
+ t.datetime :expired_at
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20241011083926_create_decidim_kids_participatory_spaces_minors_configs.decidim_kids.rb b/db/migrate/20241011083926_create_decidim_kids_participatory_spaces_minors_configs.decidim_kids.rb
new file mode 100644
index 0000000..228fbb8
--- /dev/null
+++ b/db/migrate/20241011083926_create_decidim_kids_participatory_spaces_minors_configs.decidim_kids.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+# This migration comes from decidim_kids (originally 20221220132306)
+
+class CreateDecidimKidsParticipatorySpacesMinorsConfigs < ActiveRecord::Migration[6.1]
+ def change
+ create_table :decidim_kids_participatory_spaces_minors_configs do |t|
+ t.string :access_type, null: false, default: "all"
+ t.string :authorization
+ t.integer :max_age, null: false, default: 16
+ t.references :participatory_space, polymorphic: true, index: { name: "index_minor_config_on_space_type_and_id" }
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20250115084854_create_decidim_custom_census_records.rb b/db/migrate/20250115084854_create_decidim_custom_census_records.rb
new file mode 100644
index 0000000..e78a952
--- /dev/null
+++ b/db/migrate/20250115084854_create_decidim_custom_census_records.rb
@@ -0,0 +1,13 @@
+class CreateDecidimCustomCensusRecords < ActiveRecord::Migration[6.1]
+ def change
+ create_table :decidim_custom_census_records do |t|
+ t.string :email, null: false
+ t.jsonb :metadata
+
+ Decidim::Authorization
+ t.timestamps
+ end
+
+ add_index :decidim_custom_census_records, :email, unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 505bb4c..1705c75 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2024_09_06_160616) do
+ActiveRecord::Schema.define(version: 2025_01_15_084854) do
# These are extensions that must be enabled in order to support this database
enable_extension "ltree"
@@ -550,6 +550,14 @@
t.index ["section_id"], name: "index_decidim_contextual_help_sections_on_section_id"
end
+ create_table "decidim_custom_census_records", force: :cascade do |t|
+ t.string "email", null: false
+ t.jsonb "metadata"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["email"], name: "index_decidim_custom_census_records_on_email", unique: true
+ end
+
create_table "decidim_debates_debates", id: :serial, force: :cascade do |t|
t.jsonb "title"
t.jsonb "description"
@@ -742,6 +750,63 @@
t.index ["decidim_user_id"], name: "index_decidim_impersonation_logs_on_decidim_user_id"
end
+ create_table "decidim_kids_impersonation_minor_logs", force: :cascade do |t|
+ t.bigint "decidim_tutor_id"
+ t.bigint "decidim_minor_id"
+ t.datetime "started_at"
+ t.datetime "ended_at"
+ t.datetime "expired_at"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["decidim_minor_id"], name: "index_decidim_kids_impersonation_minor_logs_on_decidim_minor_id"
+ t.index ["decidim_tutor_id"], name: "index_decidim_kids_impersonation_minor_logs_on_decidim_tutor_id"
+ end
+
+ create_table "decidim_kids_minor_accounts", force: :cascade do |t|
+ t.bigint "decidim_tutor_id", null: false
+ t.bigint "decidim_minor_id", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["decidim_minor_id"], name: "index_decidim_kids_minor_accounts_on_decidim_minor_id"
+ t.index ["decidim_tutor_id", "decidim_minor_id"], name: "decidim_kids_minor_accounts_unique_tutor_and_minor_ids", unique: true
+ t.index ["decidim_tutor_id"], name: "index_decidim_kids_minor_accounts_on_decidim_tutor_id"
+ end
+
+ create_table "decidim_kids_minor_data", force: :cascade do |t|
+ t.bigint "decidim_user_id", null: false
+ t.string "name"
+ t.string "birthday"
+ t.string "email"
+ t.jsonb "extra_data", default: {}, null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["decidim_user_id"], name: "index_decidim_kids_minor_data_on_decidim_user_id"
+ end
+
+ create_table "decidim_kids_organization_configs", force: :cascade do |t|
+ t.integer "decidim_organization_id", null: false
+ t.boolean "enable_minors_participation", default: false, null: false
+ t.integer "minimum_minor_age", default: 10, null: false
+ t.integer "maximum_minor_age", default: 13, null: false
+ t.string "minors_authorization"
+ t.string "tutors_authorization"
+ t.integer "maximum_minor_accounts", default: 3, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["decidim_organization_id"], name: "index_decidim_kids_organization"
+ end
+
+ create_table "decidim_kids_participatory_spaces_minors_configs", force: :cascade do |t|
+ t.string "access_type", default: "all", null: false
+ t.string "authorization"
+ t.integer "max_age", default: 16, null: false
+ t.string "participatory_space_type"
+ t.bigint "participatory_space_id"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["participatory_space_type", "participatory_space_id"], name: "index_minor_config_on_space_type_and_id"
+ end
+
create_table "decidim_meetings_agenda_items", force: :cascade do |t|
t.bigint "decidim_agenda_id"
t.integer "position"
@@ -1777,6 +1842,8 @@
add_foreign_key "decidim_editor_images", "decidim_organizations"
add_foreign_key "decidim_editor_images", "decidim_users", column: "decidim_author_id"
add_foreign_key "decidim_identities", "decidim_organizations"
+ add_foreign_key "decidim_kids_minor_accounts", "decidim_users", column: "decidim_minor_id"
+ add_foreign_key "decidim_kids_minor_accounts", "decidim_users", column: "decidim_tutor_id"
add_foreign_key "decidim_newsletters", "decidim_users", column: "author_id"
add_foreign_key "decidim_participatory_process_steps", "decidim_participatory_processes"
add_foreign_key "decidim_participatory_process_types", "decidim_organizations"