feat: add user deletion functionality

- Implement user destroy action in UsersController
- Add admin check for user deletion
- Update user view to include delete link for admins
- Add migration to add admin attribute to users
- Update tests to cover new admin functionality

This commit introduces the ability for admin users to delete
other users from the system. It includes necessary checks to
ensure that only admins can perform this action, along with
updates to the user interface and tests to validate the
new behavior.
This commit is contained in:
songtianlun 2025-01-05 18:27:13 +08:00
parent 022eae3029
commit f2c7d02eed
8 changed files with 69 additions and 7 deletions

View File

@ -1,7 +1,8 @@
class UsersController < ApplicationController class UsersController < ApplicationController
include SessionsHelper include SessionsHelper
before_action :logged_in_user, only: [ :index, :edit, :update ] before_action :logged_in_user, only: [ :index, :edit, :update, :destroy ]
before_action :correct_user, only: [ :edit, :update ] before_action :correct_user, only: [ :edit, :update ]
before_action :admin_user, only: [ :destroy ]
def index def index
# @users = User.all # @users = User.all
@ -46,6 +47,12 @@ class UsersController < ApplicationController
end end
end end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private private
def user_params def user_params
@ -65,4 +72,8 @@ class UsersController < ApplicationController
@user = User.find(params[:id]) @user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user) redirect_to(root_url) unless current_user?(@user)
end end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end end

View File

@ -1,4 +1,11 @@
<li> <li>
<%= gravatar_for user, size: 50 %> <%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %> <%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user,
data: {
turbo_method: :delete,
confirm: "You sure?"
} %>
<% end %>
</li> </li>

View File

@ -0,0 +1,5 @@
class AddAdminToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :admin, :boolean, default: false
end
end

3
db/schema.rb generated
View File

@ -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[8.0].define(version: 2025_01_02_072521) do ActiveRecord::Schema[8.0].define(version: 2025_01_05_095157) do
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.string "name" t.string "name"
t.string "email" t.string "email"
@ -18,6 +18,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_072521) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.string "password_digest" t.string "password_digest"
t.string "remember_digest" t.string "remember_digest"
t.boolean "admin", default: false
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
end end
end end

View File

@ -11,7 +11,8 @@
User.create!(name: "Example User", User.create!(name: "Example User",
email: "example@example.com", email: "example@example.com",
password: "foobar", password: "foobar",
password_confirmation: "foobar") password_confirmation: "foobar",
admin: true)
99.times do |n| 99.times do |n|
name = Faker::Name.name name = Faker::Name.name

View File

@ -35,4 +35,30 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
assert flash.empty? assert flash.empty?
assert_redirected_to root_url assert_redirected_to root_url
end end
test "should not allow the admin attribute to be edited via the web" do
log_in_as(@other_user)
assert_not @other_user.admin?
patch user_path(@other_user), params: {
user: { password: "password",
password_confirmation: "password",
admin: true }
}
assert_not @other_user.reload.admin?
end
test "should redirect destroy when not logged in" do
assert_no_difference "User.count" do
delete user_path(@user)
end
assert_redirected_to login_url
end
test "should redirect destroy when logged in as a non-admin" do
log_in_as(@other_user)
assert_no_difference "User.count" do
delete user_path(@user)
end
assert_redirected_to root_url
end
end end

View File

@ -7,6 +7,7 @@ one:
michael: michael:
name: Michael Example name: Michael Example
email: michael@example.com email: michael@example.com
admin: true
password_digest: <%= User.digest('password') %> password_digest: <%= User.digest('password') %>
archer: archer:

View File

@ -2,16 +2,26 @@ require "test_helper"
class UsersIndexTest < ActionDispatch::IntegrationTest class UsersIndexTest < ActionDispatch::IntegrationTest
def setup def setup
@user = users(:michael) @admin = users(:michael)
@non_admin = users(:archer)
end end
test "index including pagination" do test "index including pagination and delete links" do
log_in_as(@user) log_in_as(@admin)
get users_path get users_path
assert_template "users/index" assert_template "users/index"
assert_select "ul.pagination" assert_select "ul.pagination"
User.page(1).each do |user|
first_page_of_users = User.page(1)
first_page_of_users.each do |user|
assert_select "a[href=?]", user_path(user), text: user.name assert_select "a[href=?]", user_path(user), text: user.name
unless user == @admin
assert_select "a[href=?]", user_path(user), text: "delete"
end
end
assert_difference "User.count", -1 do
delete user_path(@non_admin)
end end
end end
end end