From 867deebf209ab3c797caf408ce6b44b9edb3b57d Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 3 Apr 2024 18:32:15 -0500 Subject: [PATCH] Finished adding the ability for admins to create new users. --- Gemfile | 2 + Gemfile.lock | 2 + app/controllers/admin_controller.rb | 26 ++++++++++++ app/controllers/application_controller.rb | 6 ++- app/controllers/users_controller.rb | 38 ++++++++++++++++- app/helpers/admin_helper.rb | 2 + app/models/ability.rb | 16 +++++++ app/views/admin/new_user.html.erb | 52 +++++++++++++++++++++++ app/views/users/_form.html.erb | 10 +++-- app/views/users/index.html.erb | 10 ++++- config/routes.rb | 4 ++ test/controllers/admin_controller_test.rb | 7 +++ 12 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 app/controllers/admin_controller.rb create mode 100644 app/helpers/admin_helper.rb create mode 100644 app/models/ability.rb create mode 100644 app/views/admin/new_user.html.erb create mode 100644 test/controllers/admin_controller_test.rb diff --git a/Gemfile b/Gemfile index e9284df..ffe46f5 100644 --- a/Gemfile +++ b/Gemfile @@ -30,6 +30,8 @@ gem 'devise' gem 'rolify' +gem 'cancancan' + gem 'webpacker' gem 'kaminari' diff --git a/Gemfile.lock b/Gemfile.lock index 466bd18..dd38dd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb new file mode 100644 index 0000000..3bc151a --- /dev/null +++ b/app/controllers/admin_controller.rb @@ -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 + \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cd7f6aa..eb02814 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -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 + \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d1920fe..3a25598 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -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 diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb new file mode 100644 index 0000000..d5c6d35 --- /dev/null +++ b/app/helpers/admin_helper.rb @@ -0,0 +1,2 @@ +module AdminHelper +end diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 0000000..7a26de4 --- /dev/null +++ b/app/models/ability.rb @@ -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 diff --git a/app/views/admin/new_user.html.erb b/app/views/admin/new_user.html.erb new file mode 100644 index 0000000..486d9c7 --- /dev/null +++ b/app/views/admin/new_user.html.erb @@ -0,0 +1,52 @@ +
+
+
+

Create New User

+ + <%= form_for(@user, url: admin_users_path, html: { class: 'needs-validation', novalidate: true }) do |f| %> +
+ <%= f.label :email, class: 'form-label' %> + <%= f.email_field :email, class: 'form-control', placeholder: 'Enter email', required: true %> +
+ +
+ <%= f.label :password, class: 'form-label' %> + <%= f.password_field :password, class: 'form-control', placeholder: 'Password', required: true %> +
+ +
+ <%= f.label :password_confirmation, "Confirm Password", class: 'form-label' %> + <%= f.password_field :password_confirmation, class: 'form-control', placeholder: 'Confirm Password', required: true %> +
+ +
+ <%= f.submit "Create User", class: 'btn btn-dark' %> +
+ <% end %> +
+
+
+ +<%# This is to indicate to the User if the passwords didn't match %> + diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index 66e6d90..34a4a45 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -1,4 +1,5 @@ <%= form_with(model: user, local: true, html: { class: 'needs-validation', novalidate: true }) do |form| %> + <% if user.errors.any? %> <% end %> +
<%= form.label :email, class: 'form-label' %> <%= form.email_field :email, id: :user_email, class: 'form-control' %>
-
- <%= form.check_box :admin, class: 'form-check-input', id: :user_admin %> - <%= form.label :admin, class: 'form-check-label' %> + +
+ <%= 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' } %>
+
<%= form.submit 'Save', class: 'btn btn-dark' %>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 1f214fa..6ec33d9 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -16,7 +16,7 @@ <% @users.each do |user| %> <%= user.email %> - <%= user.admin? ? 'Yes' : 'No' %> + <%= user.has_role?(:admin) ? 'Yes' : 'No' %> <%= link_to edit_user_path(user), class: 'btn btn-info btn-sm' do %> @@ -31,9 +31,15 @@
- + +
+ + <% if can?(:create, User) %> + <%= 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" %>
diff --git a/config/routes.rb b/config/routes.rb index e020cfd..a04d0da 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/test/controllers/admin_controller_test.rb b/test/controllers/admin_controller_test.rb new file mode 100644 index 0000000..f891499 --- /dev/null +++ b/test/controllers/admin_controller_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class AdminControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end