From f8121dd2c99c9e4d9ab0c54646951fea76fae17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Bol=C3=ADvar?= Date: Mon, 13 Jan 2025 16:47:43 +0100 Subject: [PATCH 1/2] Add custom census authorization handler --- .gitignore | 2 + README.md | 16 ++++++++ .../custom_census_authorization_handler.rb | 38 +++++++++++++++++++ .../_form.html.erb | 7 ++++ config/i18n-tasks.yml | 2 + config/initializers/decidim.rb | 6 +++ config/locales/en.yml | 26 +++++++++++++ 7 files changed, 97 insertions(+) create mode 100644 app/services/custom_census_authorization_handler.rb create mode 100644 app/views/custom_census_authorization/_form.html.erb diff --git a/.gitignore b/.gitignore index 87bc84f..5ad4e7a 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,5 @@ tailwind.config.js Capfile config/deploy.rb config/deploy + +lib/assets/custom_census.csv diff --git a/README.md b/README.md index 7db0aaf..affc35c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,22 @@ 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 census in a CSV file. + +You need to create a CSV file `lib/asses/census.csv` with two columns: `email` and `birthdate`. For example: + +```csv +email,date_of_birth +john.doe@example.org,1956-03-14 +jane.smith@example.org,1998-12-06 +``` + +The verification will succeed if the user is in the census and introduces the same birthdate as the one in the CSV. + +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/services/custom_census_authorization_handler.rb b/app/services/custom_census_authorization_handler.rb new file mode 100644 index 0000000..9f29d1f --- /dev/null +++ b/app/services/custom_census_authorization_handler.rb @@ -0,0 +1,38 @@ +# 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 + result = census.find { |row| row["email"] == user.email } + return errors.add(:base, I18n.t("custom_census_authorization_handler.errors.not_found")) unless result + + errors.add(:date_of_birth, I18n.t("custom_census_authorization_handler.errors.invalid_date_of_birth")) unless result["date_of_birth"] == parsed_date_of_birth + end + + def census + @census ||= CSV.read(Rails.root.join("lib/assets/custom_census.csv"), headers: true) + 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 @@ +
+
+ <%= form.date_select :date_of_birth, start_year: 1900, end_year: 1.year.ago.year, default: 20.years.ago, prompt: { day: t(".date_select.day"), month: t(".date_select.month"), year: t(".date_select.year") } %> +
+ + <%= form.hidden_field :handler_name %> +
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 From 87faba7abaf8998352f415f7edb3bd8fbde0bca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Bol=C3=ADvar?= Date: Wed, 15 Jan 2025 10:44:44 +0100 Subject: [PATCH 2/2] Store census in the database instead of in a CSV --- .gitignore | 2 -- README.md | 17 ++++++++++------- app/models/decidim/custom_census_record.rb | 15 +++++++++++++++ .../custom_census_authorization_handler.rb | 10 +++------- ...4854_create_decidim_custom_census_records.rb | 13 +++++++++++++ db/schema.rb | 10 +++++++++- 6 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 app/models/decidim/custom_census_record.rb create mode 100644 db/migrate/20250115084854_create_decidim_custom_census_records.rb diff --git a/.gitignore b/.gitignore index 5ad4e7a..87bc84f 100644 --- a/.gitignore +++ b/.gitignore @@ -60,5 +60,3 @@ tailwind.config.js Capfile config/deploy.rb config/deploy - -lib/assets/custom_census.csv diff --git a/README.md b/README.md index affc35c..9afb84c 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,20 @@ This is the open-source repository for decidim_inspire, based on [Decidim](https ## Custom census authorization handler -This authorization handler allows users to be directly verified with their birthdates by checking the census in a CSV file. +This authorization handler allows users to be directly verified with their birthdates by checking the records in a table. -You need to create a CSV file `lib/asses/census.csv` with two columns: `email` and `birthdate`. For example: +You need to create records for the model `Decidim::CustomCensusRecord`. For example: -```csv -email,date_of_birth -john.doe@example.org,1956-03-14 -jane.smith@example.org,1998-12-06 +```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 CSV. +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. 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 index 9f29d1f..08119e6 100644 --- a/app/services/custom_census_authorization_handler.rb +++ b/app/services/custom_census_authorization_handler.rb @@ -26,13 +26,9 @@ def parsed_date_of_birth end def present_in_census - result = census.find { |row| row["email"] == user.email } - return errors.add(:base, I18n.t("custom_census_authorization_handler.errors.not_found")) unless result + 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 result["date_of_birth"] == parsed_date_of_birth - end - - def census - @census ||= CSV.read(Rails.root.join("lib/assets/custom_census.csv"), headers: true) + 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/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 0b6318c..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_10_11_083926) 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"