Add account management
This commit is contained in:
parent
882d0a51ed
commit
4f190f67f0
5
Gemfile
5
Gemfile
@ -4,13 +4,14 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
|||||||
ruby "3.1.2"
|
ruby "3.1.2"
|
||||||
|
|
||||||
gem "bootsnap", require: false
|
gem "bootsnap", require: false
|
||||||
|
gem "clearance"
|
||||||
|
gem "dotenv-rails"
|
||||||
gem "importmap-rails"
|
gem "importmap-rails"
|
||||||
gem "pg", "~> 1.1"
|
gem "pg", "~> 1.1"
|
||||||
gem "propshaft"
|
gem "propshaft"
|
||||||
gem "puma", "~> 5.0"
|
gem "puma", "~> 5.0"
|
||||||
gem "rails", "~> 7.0.3"
|
|
||||||
gem "dotenv-rails"
|
|
||||||
gem "pundit"
|
gem "pundit"
|
||||||
|
gem "rails", "~> 7.0.3"
|
||||||
gem "ransack"
|
gem "ransack"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
19
Gemfile.lock
19
Gemfile.lock
@ -66,10 +66,22 @@ GEM
|
|||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
|
argon2 (2.1.1)
|
||||||
|
ffi (~> 1.14)
|
||||||
|
ffi-compiler (~> 1.0)
|
||||||
|
bcrypt (3.1.18)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.12.0)
|
bootsnap (1.12.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
builder (3.2.4)
|
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)
|
concurrent-ruby (1.1.10)
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
debug (1.5.0)
|
debug (1.5.0)
|
||||||
@ -80,7 +92,13 @@ GEM
|
|||||||
dotenv-rails (2.7.6)
|
dotenv-rails (2.7.6)
|
||||||
dotenv (= 2.7.6)
|
dotenv (= 2.7.6)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
|
email_validator (2.2.3)
|
||||||
|
activemodel
|
||||||
erubi (1.10.0)
|
erubi (1.10.0)
|
||||||
|
ffi (1.15.5)
|
||||||
|
ffi-compiler (1.0.1)
|
||||||
|
ffi (>= 1.0.0)
|
||||||
|
rake
|
||||||
globalid (1.0.0)
|
globalid (1.0.0)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
i18n (1.10.0)
|
i18n (1.10.0)
|
||||||
@ -186,6 +204,7 @@ PLATFORMS
|
|||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
bootsnap
|
bootsnap
|
||||||
|
clearance
|
||||||
debug
|
debug
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
importmap-rails
|
importmap-rails
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
|
include Clearance::Controller
|
||||||
end
|
end
|
||||||
|
20
app/controllers/boards_controller.rb
Normal file
20
app/controllers/boards_controller.rb
Normal file
@ -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
|
@ -1,4 +1,5 @@
|
|||||||
class ContributionsController < ApplicationController
|
class ContributionsController < ApplicationController
|
||||||
|
before_action :require_login
|
||||||
before_action :set_member, only: %i[ new create ]
|
before_action :set_member, only: %i[ new create ]
|
||||||
before_action :set_contribution, only: %i[ edit update delete destroy ]
|
before_action :set_contribution, only: %i[ edit update delete destroy ]
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
class MembersController < ApplicationController
|
class MembersController < ApplicationController
|
||||||
|
before_action :require_login
|
||||||
before_action :set_member, only: %i[ show edit update delete destroy ]
|
before_action :set_member, only: %i[ show edit update delete destroy ]
|
||||||
helper_method :sort_params
|
helper_method :sort_params
|
||||||
|
|
||||||
|
9
app/guards/active_guard.rb
Normal file
9
app/guards/active_guard.rb
Normal file
@ -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
|
27
app/models/user.rb
Normal file
27
app/models/user.rb
Normal file
@ -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
|
19
app/views/boards/edit.html.erb
Normal file
19
app/views/boards/edit.html.erb
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<h1><%= t('boards.edit.title') %></h1>
|
||||||
|
|
||||||
|
<p><%= t('boards.edit.warning') %></p>
|
||||||
|
|
||||||
|
<%= form_with scope: :users, url: board_path, method: :put do |form| %>
|
||||||
|
<table class='lined'>
|
||||||
|
<% @users.each do |user| %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<%= form.email_field nil, value: user.email %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= form.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
8
app/views/clearance_mailer/change_password.html.erb
Normal file
8
app/views/clearance_mailer/change_password.html.erb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<p><%= t(".opening") %></p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= link_to t(".link_text", default: "Change my password"),
|
||||||
|
url_for([@user, :password, action: :edit, token: @user.confirmation_token]) %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><%= t(".closing") %></p>
|
5
app/views/clearance_mailer/change_password.text.erb
Normal file
5
app/views/clearance_mailer/change_password.text.erb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<%= t(".opening") %>
|
||||||
|
|
||||||
|
<%= url_for([@user, :password, action: :edit, token: @user.confirmation_token]) %>
|
||||||
|
|
||||||
|
<%= t(".closing") %>
|
@ -11,9 +11,19 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<% if signed_in? %>
|
||||||
<nav>
|
<nav>
|
||||||
<%= link_to t('navigation.members'), members_path %>
|
<ul>
|
||||||
|
<li><%= link_to t('navigation.members'), members_path %></li>
|
||||||
|
<li><%= link_to t('navigation.board'), edit_board_path %></li>
|
||||||
|
<li>
|
||||||
|
<%= button_to sign_out_path, method: :delete do %>
|
||||||
|
Sign out of <%= current_user.email %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
</body>
|
</body>
|
||||||
|
3
app/views/passwords/create.html.erb
Normal file
3
app/views/passwords/create.html.erb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div id="clearance" class="password-reset">
|
||||||
|
<p><%= t(".description") %></p>
|
||||||
|
</div>
|
18
app/views/passwords/edit.html.erb
Normal file
18
app/views/passwords/edit.html.erb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<div id="clearance" class="password-reset">
|
||||||
|
<h2><%= t(".title") %></h2>
|
||||||
|
|
||||||
|
<p><%= t(".description") %></p>
|
||||||
|
|
||||||
|
<%= form_for :password_reset,
|
||||||
|
url: [@user, :password, token: @user.confirmation_token],
|
||||||
|
html: { method: :put } do |form| %>
|
||||||
|
<div class="password-field">
|
||||||
|
<%= form.label :password %>
|
||||||
|
<%= form.password_field :password %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="submit-field">
|
||||||
|
<%= form.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
16
app/views/passwords/new.html.erb
Normal file
16
app/views/passwords/new.html.erb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<div id="clearance" class="password-reset">
|
||||||
|
<h2><%= t(".title") %></h2>
|
||||||
|
|
||||||
|
<p><%= t(".description") %></p>
|
||||||
|
|
||||||
|
<%= form_for :password, url: passwords_path do |form| %>
|
||||||
|
<div class="text-field">
|
||||||
|
<%= form.label :email %>
|
||||||
|
<%= form.email_field :email %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="submit-field">
|
||||||
|
<%= form.submit %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
28
app/views/sessions/_form.html.erb
Normal file
28
app/views/sessions/_form.html.erb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<%= form_for :session, url: session_path do |form| %>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<%= form.label :email %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= form.email_field :email %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><%= form.label :password %></td>
|
||||||
|
<td><%= form.password_field :password %></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="submit-field">
|
||||||
|
<%= form.submit %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="other-links">
|
||||||
|
<% if Clearance.configuration.allow_sign_up? %>
|
||||||
|
<%= link_to t(".sign_up"), sign_up_path %>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to t(".forgot_password"), new_password_path %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
5
app/views/sessions/new.html.erb
Normal file
5
app/views/sessions/new.html.erb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<div id="clearance" class="sign-in">
|
||||||
|
<h2><%= t(".title") %></h2>
|
||||||
|
|
||||||
|
<%= render partial: '/sessions/form' %>
|
||||||
|
</div>
|
@ -53,6 +53,10 @@ Rails.application.configure do
|
|||||||
enable_starttls_auto: true,
|
enable_starttls_auto: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.action_mailer.default_url_options = {
|
||||||
|
host: ENV['BASE_HOST'],
|
||||||
|
}
|
||||||
|
|
||||||
# Print deprecation notices to the Rails logger.
|
# Print deprecation notices to the Rails logger.
|
||||||
config.active_support.deprecation = :log
|
config.active_support.deprecation = :log
|
||||||
|
|
||||||
|
8
config/initializers/clearance.rb
Normal file
8
config/initializers/clearance.rb
Normal file
@ -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
|
64
config/locales/clearance.en.yml
Normal file
64
config/locales/clearance.en.yml
Normal file
@ -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
|
@ -101,3 +101,14 @@ pt:
|
|||||||
cancelled:
|
cancelled:
|
||||||
subject: "ANSOL - Inscrição cancelada"
|
subject: "ANSOL - Inscrição cancelada"
|
||||||
greetings: "Caro(a) %{display_name}"
|
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.
|
||||||
|
@ -3,6 +3,8 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
# root "articles#index"
|
# root "articles#index"
|
||||||
|
#
|
||||||
|
root to: redirect('/members')
|
||||||
|
|
||||||
resources :members do
|
resources :members do
|
||||||
member do
|
member do
|
||||||
@ -16,4 +18,6 @@ Rails.application.routes.draw do
|
|||||||
get :delete
|
get :delete
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resource :board, only: [:edit, :update]
|
||||||
end
|
end
|
||||||
|
15
db/migrate/20220625203020_create_users.rb
Normal file
15
db/migrate/20220625203020_create_users.rb
Normal file
@ -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
|
7
db/migrate/20220625210821_add_active_to_user.rb
Normal file
7
db/migrate/20220625210821_add_active_to_user.rb
Normal file
@ -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
|
15
db/schema.rb
generated
15
db/schema.rb
generated
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
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"
|
t.index ["member_id"], name: "index_notifications_on_member_id"
|
||||||
end
|
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 "contributions", "members"
|
||||||
add_foreign_key "notifications", "members"
|
add_foreign_key "notifications", "members"
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user