Finished adding the ability for admins to create new users.

This commit is contained in:
Ben 2024-04-03 18:32:15 -05:00
parent 1d5646e61c
commit 867deebf20
12 changed files with 167 additions and 8 deletions

View File

@ -30,6 +30,8 @@ gem 'devise'
gem 'rolify'
gem 'cancancan'
gem 'webpacker'
gem 'kaminari'

View File

@ -84,6 +84,7 @@ GEM
bootsnap (1.17.0)
msgpack (~> 1.2)
builder (3.2.4)
cancancan (3.5.0)
capybara (3.39.2)
addressable
matrix
@ -291,6 +292,7 @@ PLATFORMS
DEPENDENCIES
bootsnap
cancancan
capybara
debug
devise

View File

@ -0,0 +1,26 @@
class AdminController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource class: User
def new_user
@user = User.new
end
def create_user
@user = User.new(user_params)
if @user.save
# Add role to the user here if needed e.g., user.add_role :new_role
redirect_to admin_users_path, notice: 'User was successfully created.'
else
render :new_user
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
# Add other fields as needed
end
end

View File

@ -1,3 +1,7 @@
class ApplicationController < ActionController::Base
before_action :authenticate_user!
end
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, alert: "Access denied."
end
end

View File

@ -2,6 +2,7 @@ class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user, only: [:edit, :update, :destroy]
before_action :require_admin
load_and_authorize_resource
def index
@users = User.all
@ -10,8 +11,20 @@ class UsersController < ApplicationController
def edit
end
def create
@user = User.new(user_params)
if @user.save
assign_roles(@user)
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
def update
if @user.update(user_params)
# Assumes @user is already set from a before_action callback
if @user.update(user_params.except(:roles))
update_user_roles(@user, user_params[:roles])
redirect_to users_path, notice: 'User was successfully updated.'
else
render :edit
@ -30,7 +43,7 @@ class UsersController < ApplicationController
end
def user_params
params.require(:user).permit(:email, :admin)
params.require(:user).permit(:email, :password, :password_confirmation, roles: [])
end
def require_admin
@ -38,4 +51,25 @@ class UsersController < ApplicationController
redirect_to root_path, alert: 'Only admins are allowed to access this section.'
end
end
def assign_roles(user)
user.roles.delete_all # Clear all roles before reassigning to prevent duplicates
# Assuming roles are passed as an array of role names from the form
# and that the form sends an empty string if no roles are selected.
selected_roles = params[:user][:roles].reject(&:blank?)
selected_roles.each do |role_name|
user.add_role(role_name) unless user.has_role?(role_name)
end
end
def update_user_roles(user, roles_names)
user.roles.delete_all # Remove existing roles
roles_names.each do |role_name|
user.add_role(role_name) unless role_name.blank?
end
end
end

View File

@ -0,0 +1,2 @@
module AdminHelper
end

16
app/models/ability.rb Normal file
View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
can :create, User if user.has_role? :admin
else
can :read, :all
# Define other abilities for other roles
end
end
end

View File

@ -0,0 +1,52 @@
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 class="mb-3 text-center">Create New User</h2>
<%= form_for(@user, url: admin_users_path, html: { class: 'needs-validation', novalidate: true }) do |f| %>
<div class="mb-3">
<%= f.label :email, class: 'form-label' %>
<%= f.email_field :email, class: 'form-control', placeholder: 'Enter email', required: true %>
</div>
<div class="mb-3">
<%= f.label :password, class: 'form-label' %>
<%= f.password_field :password, class: 'form-control', placeholder: 'Password', required: true %>
</div>
<div class="mb-3">
<%= f.label :password_confirmation, "Confirm Password", class: 'form-label' %>
<%= f.password_field :password_confirmation, class: 'form-control', placeholder: 'Confirm Password', required: true %>
</div>
<div class="actions text-center">
<%= f.submit "Create User", class: 'btn btn-dark' %>
</div>
<% end %>
</div>
</div>
</div>
<%# This is to indicate to the User if the passwords didn't match %>
<script>
document.addEventListener("DOMContentLoaded", function() {
const password = document.querySelector('#user_password');
const confirmPassword = document.querySelector('#user_password_confirmation');
const messageContainer = document.createElement('div');
messageContainer.style.color = 'red';
// Append the messageContainer below the confirmPassword field
confirmPassword.parentNode.insertBefore(messageContainer, confirmPassword.nextSibling);
function validatePassword(){
if(password.value !== confirmPassword.value) {
messageContainer.textContent = "Passwords need to match!";
} else {
messageContainer.textContent = "";
}
}
password.onchange = validatePassword;
confirmPassword.onkeyup = validatePassword;
});
</script>

View File

@ -1,4 +1,5 @@
<%= form_with(model: user, local: true, html: { class: 'needs-validation', novalidate: true }) do |form| %>
<!-- Display errors, if any -->
<% if user.errors.any? %>
<div id="error_explanation" class="alert alert-danger" role="alert">
<h4><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h4>
@ -10,16 +11,19 @@
</div>
<% end %>
<!-- Email field -->
<div class="mb-3">
<%= form.label :email, class: 'form-label' %>
<%= form.email_field :email, id: :user_email, class: 'form-control' %>
</div>
<div class="mb-3 form-check">
<%= form.check_box :admin, class: 'form-check-input', id: :user_admin %>
<%= form.label :admin, class: 'form-check-label' %>
<!-- Role selection -->
<div class="mb-3">
<%= form.label :roles, 'Assign Role', class: 'form-label' %>
<%= form.select :roles, options_for_select(Role.pluck(:name), user.roles.pluck(:name)), {}, { multiple: true, class: 'form-control' } %>
</div>
<!-- Submit button -->
<div class="actions">
<%= form.submit 'Save', class: 'btn btn-dark' %>
</div>

View File

@ -16,7 +16,7 @@
<% @users.each do |user| %>
<tr>
<td><%= user.email %></td>
<td><%= user.admin? ? 'Yes' : 'No' %></td>
<td><%= user.has_role?(:admin) ? 'Yes' : 'No' %></td> <!-- Assuming you're using Rolify for role management -->
<td>
<%= link_to edit_user_path(user), class: 'btn btn-info btn-sm' do %>
<i class="bi bi-pencil-fill" style="color: white;"></i>
@ -31,9 +31,15 @@
</table>
</div>
</div>
<!-- Optionally, include action buttons or links here -->
<div class="row">
<div class="col-12 d-flex justify-content-between mb-4">
<!-- Button for Admins to add a new user -->
<% if can?(:create, User) %> <!-- Checks if the current user has the permission to create new users -->
<%= link_to 'Add New User', new_admin_user_path, class: "btn btn-dark" %>
<% end %>
<%= link_to 'Back to Home', root_path, class: "btn btn-secondary" %> <!-- Adjust as needed -->
</div>
</div>

View File

@ -84,6 +84,10 @@ Rails.application.routes.draw do
resources :forms
resources :users
# Custom route for admin to create a new user
get 'admin/users/new', to: 'admin#new_user', as: :new_admin_user
post 'admin/users', to: 'admin#create_user', as: :admin_users

View File

@ -0,0 +1,7 @@
require "test_helper"
class AdminControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end