diff --git a/Gemfile b/Gemfile
index 120ac9d..cd1b2bf 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,13 +4,14 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.1.2"
gem "bootsnap", require: false
+gem "clearance"
+gem "dotenv-rails"
gem "importmap-rails"
gem "pg", "~> 1.1"
gem "propshaft"
gem "puma", "~> 5.0"
-gem "rails", "~> 7.0.3"
-gem "dotenv-rails"
gem "pundit"
+gem "rails", "~> 7.0.3"
gem "ransack"
group :development, :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index 8f490b1..daf3448 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -66,10 +66,22 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
+ argon2 (2.1.1)
+ ffi (~> 1.14)
+ ffi-compiler (~> 1.0)
+ bcrypt (3.1.18)
bindex (0.8.1)
bootsnap (1.12.0)
msgpack (~> 1.2)
builder (3.2.4)
+ clearance (2.6.0)
+ actionmailer (>= 5.0)
+ activemodel (>= 5.0)
+ activerecord (>= 5.0)
+ argon2 (~> 2.0, >= 2.0.2)
+ bcrypt (>= 3.1.1)
+ email_validator (~> 2.0)
+ railties (>= 5.0)
concurrent-ruby (1.1.10)
crass (1.0.6)
debug (1.5.0)
@@ -80,7 +92,13 @@ GEM
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
+ email_validator (2.2.3)
+ activemodel
erubi (1.10.0)
+ ffi (1.15.5)
+ ffi-compiler (1.0.1)
+ ffi (>= 1.0.0)
+ rake
globalid (1.0.0)
activesupport (>= 5.0)
i18n (1.10.0)
@@ -186,6 +204,7 @@ PLATFORMS
DEPENDENCIES
bootsnap
+ clearance
debug
dotenv-rails
importmap-rails
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 09705d1..789d46b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,2 +1,3 @@
class ApplicationController < ActionController::Base
+ include Clearance::Controller
end
diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb
new file mode 100644
index 0000000..564557d
--- /dev/null
+++ b/app/controllers/boards_controller.rb
@@ -0,0 +1,20 @@
+class BoardsController < ApplicationController
+ before_action :require_login
+
+ def edit
+ @users = User.where(active: true)
+ @users += Array.new(5 - @users.size) { User.new }
+ end
+
+ def update
+ User.update_board_members(board_params)
+
+ redirect_to edit_board_path
+ end
+
+ private
+
+ def board_params
+ params.require('users')
+ end
+end
diff --git a/app/controllers/contributions_controller.rb b/app/controllers/contributions_controller.rb
index 15d812c..47c26b4 100644
--- a/app/controllers/contributions_controller.rb
+++ b/app/controllers/contributions_controller.rb
@@ -1,4 +1,5 @@
class ContributionsController < ApplicationController
+ before_action :require_login
before_action :set_member, only: %i[ new create ]
before_action :set_contribution, only: %i[ edit update delete destroy ]
diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb
index a69ad47..00282d0 100644
--- a/app/controllers/members_controller.rb
+++ b/app/controllers/members_controller.rb
@@ -1,4 +1,5 @@
class MembersController < ApplicationController
+ before_action :require_login
before_action :set_member, only: %i[ show edit update delete destroy ]
helper_method :sort_params
diff --git a/app/guards/active_guard.rb b/app/guards/active_guard.rb
new file mode 100644
index 0000000..5e9fc1a
--- /dev/null
+++ b/app/guards/active_guard.rb
@@ -0,0 +1,9 @@
+class ActiveGuard < Clearance::SignInGuard
+ def call
+ if signed_in? && !current_user.active
+ failure(t('sessions.new.deactivated_error'))
+ else
+ next_guard
+ end
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
new file mode 100644
index 0000000..2d96452
--- /dev/null
+++ b/app/models/user.rb
@@ -0,0 +1,27 @@
+class User < ApplicationRecord
+ include Clearance::User
+
+ def self.update_board_members(new_emails)
+ where(active: true).each do |user|
+ if !new_emails.include?(user.email)
+ user.update(active: false)
+
+ # TODO: notify board member of deactivation
+ end
+ end
+
+ new_emails.each do |email|
+ user = User.find_by(email: email)
+
+ if user
+ if !user.active?
+ # TODO: notify board member of activation
+ user.update(active: true, password: SecureRandom.hex(32))
+ end
+ else
+ # TODO: notify board member of activation
+ User.create(email: email, password: SecureRandom.hex(32))
+ end
+ end
+ end
+end
diff --git a/app/views/boards/edit.html.erb b/app/views/boards/edit.html.erb
new file mode 100644
index 0000000..3c5c2c0
--- /dev/null
+++ b/app/views/boards/edit.html.erb
@@ -0,0 +1,19 @@
+
<%= t('boards.edit.title') %>
+
+<%= t('boards.edit.warning') %>
+
+<%= form_with scope: :users, url: board_path, method: :put do |form| %>
+
+ <% @users.each do |user| %>
+
+
+ <%= form.email_field nil, value: user.email %>
+ |
+
+ <% end %>
+
+
+
+ <%= form.submit %>
+
+<% end %>
diff --git a/app/views/clearance_mailer/change_password.html.erb b/app/views/clearance_mailer/change_password.html.erb
new file mode 100644
index 0000000..228c8c2
--- /dev/null
+++ b/app/views/clearance_mailer/change_password.html.erb
@@ -0,0 +1,8 @@
+<%= t(".opening") %>
+
+
+ <%= link_to t(".link_text", default: "Change my password"),
+ url_for([@user, :password, action: :edit, token: @user.confirmation_token]) %>
+
+
+<%= t(".closing") %>
diff --git a/app/views/clearance_mailer/change_password.text.erb b/app/views/clearance_mailer/change_password.text.erb
new file mode 100644
index 0000000..5a5d2d5
--- /dev/null
+++ b/app/views/clearance_mailer/change_password.text.erb
@@ -0,0 +1,5 @@
+<%= t(".opening") %>
+
+<%= url_for([@user, :password, action: :edit, token: @user.confirmation_token]) %>
+
+<%= t(".closing") %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index ae94ca1..5bb562a 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -11,9 +11,19 @@
-
+ <% if signed_in? %>
+
+ <% end %>
<%= yield %>
diff --git a/app/views/passwords/create.html.erb b/app/views/passwords/create.html.erb
new file mode 100644
index 0000000..1215156
--- /dev/null
+++ b/app/views/passwords/create.html.erb
@@ -0,0 +1,3 @@
+
+
<%= t(".description") %>
+
diff --git a/app/views/passwords/edit.html.erb b/app/views/passwords/edit.html.erb
new file mode 100644
index 0000000..584e809
--- /dev/null
+++ b/app/views/passwords/edit.html.erb
@@ -0,0 +1,18 @@
+
+
<%= t(".title") %>
+
+
<%= t(".description") %>
+
+ <%= form_for :password_reset,
+ url: [@user, :password, token: @user.confirmation_token],
+ html: { method: :put } do |form| %>
+
+ <%= form.label :password %>
+ <%= form.password_field :password %>
+
+
+
+ <%= form.submit %>
+
+ <% end %>
+
diff --git a/app/views/passwords/new.html.erb b/app/views/passwords/new.html.erb
new file mode 100644
index 0000000..4c2e12f
--- /dev/null
+++ b/app/views/passwords/new.html.erb
@@ -0,0 +1,16 @@
+
+
<%= t(".title") %>
+
+
<%= t(".description") %>
+
+ <%= form_for :password, url: passwords_path do |form| %>
+
+ <%= form.label :email %>
+ <%= form.email_field :email %>
+
+
+
+ <%= form.submit %>
+
+ <% end %>
+
diff --git a/app/views/sessions/_form.html.erb b/app/views/sessions/_form.html.erb
new file mode 100644
index 0000000..fd02376
--- /dev/null
+++ b/app/views/sessions/_form.html.erb
@@ -0,0 +1,28 @@
+<%= form_for :session, url: session_path do |form| %>
+
+
+
+ <%= form.label :email %>
+ |
+
+ <%= form.email_field :email %>
+ |
+
+
+
+ <%= form.label :password %> |
+ <%= form.password_field :password %> |
+
+
+
+
+ <%= form.submit %>
+
+
+
+ <% if Clearance.configuration.allow_sign_up? %>
+ <%= link_to t(".sign_up"), sign_up_path %>
+ <% end %>
+ <%= link_to t(".forgot_password"), new_password_path %>
+
+<% end %>
diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb
new file mode 100644
index 0000000..3ba090f
--- /dev/null
+++ b/app/views/sessions/new.html.erb
@@ -0,0 +1,5 @@
+
+
<%= t(".title") %>
+
+ <%= render partial: '/sessions/form' %>
+
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 1317d9a..3ac9faf 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -53,6 +53,10 @@ Rails.application.configure do
enable_starttls_auto: true,
}
+ config.action_mailer.default_url_options = {
+ host: ENV['BASE_HOST'],
+ }
+
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
diff --git a/config/initializers/clearance.rb b/config/initializers/clearance.rb
new file mode 100644
index 0000000..c4ea458
--- /dev/null
+++ b/config/initializers/clearance.rb
@@ -0,0 +1,8 @@
+Clearance.configure do |config|
+ config.allow_sign_up = false
+ config.mailer_sender = ENV['SMTP_FROM_ADDRESS']
+ config.rotate_csrf_on_sign_in = true
+ config.secure_cookie = true
+ config.signed_cookie = true
+ config.sign_in_guards = ["ActiveGuard"]
+end
diff --git a/config/locales/clearance.en.yml b/config/locales/clearance.en.yml
new file mode 100644
index 0000000..f302a64
--- /dev/null
+++ b/config/locales/clearance.en.yml
@@ -0,0 +1,64 @@
+---
+en:
+ clearance:
+ models:
+ clearance_mailer:
+ change_password: Change your password
+ clearance_mailer:
+ change_password:
+ closing: If you didn't request this, ignore this email. Your password has
+ not been changed.
+ link_text: Change my password
+ opening: "Someone, hopefully you, requested we send you a link to change
+ your password:"
+ flashes:
+ failure_after_create: Bad email or password.
+ failure_after_update: Password can't be blank.
+ failure_when_forbidden: Please double check the URL or try submitting
+ the form again.
+ failure_when_not_signed_in: Please sign in to continue.
+ failure_when_missing_email: Email can't be blank.
+ helpers:
+ label:
+ password:
+ email: Email address
+ password_reset:
+ password: Choose password
+ session:
+ password: Password
+ user:
+ password: Password
+ submit:
+ password:
+ submit: Reset password
+ password_reset:
+ submit: Save this password
+ session:
+ submit: Sign in
+ user:
+ create: Sign up
+ layouts:
+ application:
+ sign_in: Sign in
+ sign_out: Sign out
+ passwords:
+ create:
+ description: You will receive an email within the next few minutes. It
+ contains instructions for changing your password.
+ edit:
+ description: Your password has been reset. Choose a new password below.
+ title: Change your password
+ new:
+ description: To be emailed a link to reset your password, please enter
+ your email address.
+ title: Reset your password
+ sessions:
+ form:
+ forgot_password: Forgot password?
+ sign_up: Sign up
+ new:
+ title: Sign in
+ users:
+ new:
+ sign_in: Sign in
+ title: Sign up
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index b6fa5e6..04fd018 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -101,3 +101,14 @@ pt:
cancelled:
subject: "ANSOL - Inscrição cancelada"
greetings: "Caro(a) %{display_name}"
+ sessions:
+ new:
+ title: "Entrar"
+ attributes:
+ email: "oi"
+ boards:
+ edit:
+ title: "Actualizar membros da direcção"
+ warning: |
+ Atenção: qualquer endereço que ficar omisso verá a sua conta suspensa e
+ deixará de poder aceder a este sistema.
diff --git a/config/routes.rb b/config/routes.rb
index f21fa29..e0380c7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,8 @@ Rails.application.routes.draw do
# Defines the root path route ("/")
# root "articles#index"
+ #
+ root to: redirect('/members')
resources :members do
member do
@@ -16,4 +18,6 @@ Rails.application.routes.draw do
get :delete
end
end
+
+ resource :board, only: [:edit, :update]
end
diff --git a/db/migrate/20220625203020_create_users.rb b/db/migrate/20220625203020_create_users.rb
new file mode 100644
index 0000000..a93b986
--- /dev/null
+++ b/db/migrate/20220625203020_create_users.rb
@@ -0,0 +1,15 @@
+class CreateUsers < ActiveRecord::Migration[7.0]
+ def change
+ create_table :users, id: :uuid do |t|
+ t.timestamps null: false
+ t.string :email, null: false
+ t.string :encrypted_password, limit: 128, null: false
+ t.string :confirmation_token, limit: 128
+ t.string :remember_token, limit: 128, null: false
+ end
+
+ add_index :users, :email
+ add_index :users, :confirmation_token, unique: true
+ add_index :users, :remember_token, unique: true
+ end
+end
diff --git a/db/migrate/20220625210821_add_active_to_user.rb b/db/migrate/20220625210821_add_active_to_user.rb
new file mode 100644
index 0000000..2a12143
--- /dev/null
+++ b/db/migrate/20220625210821_add_active_to_user.rb
@@ -0,0 +1,7 @@
+class AddActiveToUser < ActiveRecord::Migration[7.0]
+ def change
+ change_table :users do |t|
+ t.boolean :active, null: false, default: true
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index acaad17..e66b68e 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[7.0].define(version: 2022_06_25_181824) do
+ActiveRecord::Schema[7.0].define(version: 2022_06_25_210821) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
@@ -56,6 +56,19 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_25_181824) do
t.index ["member_id"], name: "index_notifications_on_member_id"
end
+ create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "email", null: false
+ t.string "encrypted_password", limit: 128, null: false
+ t.string "confirmation_token", limit: 128
+ t.string "remember_token", limit: 128, null: false
+ t.boolean "active", default: true, null: false
+ t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
+ t.index ["email"], name: "index_users_on_email"
+ t.index ["remember_token"], name: "index_users_on_remember_token", unique: true
+ end
+
add_foreign_key "contributions", "members"
add_foreign_key "notifications", "members"
end