From 40fd76f356ad37f8989e0d245864261965fa1031 Mon Sep 17 00:00:00 2001 From: Raul Gracia Date: Wed, 4 Oct 2023 06:18:04 +0100 Subject: [PATCH 1/2] Adding roles to the application --- Gemfile | 1 + Gemfile.lock | 2 ++ .../system_admin/users_controller.rb | 16 +++++++++-- app/models/role.rb | 27 +++++++++++++++++++ app/models/user.rb | 3 ++- app/views/system_admin/users/_form.html.erb | 1 + config/initializers/rolify.rb | 10 +++++++ .../20231003024901_rolify_create_roles.rb | 18 +++++++++++++ db/schema.rb | 20 +++++++++++++- lib/tasks/roles.rake | 8 ++++++ spec/factories/roles.rb | 16 +++++++++++ spec/models/role_spec.rb | 16 +++++++++++ 12 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 app/models/role.rb create mode 100644 config/initializers/rolify.rb create mode 100644 db/migrate/20231003024901_rolify_create_roles.rb create mode 100644 lib/tasks/roles.rake create mode 100644 spec/factories/roles.rb create mode 100644 spec/models/role_spec.rb diff --git a/Gemfile b/Gemfile index e9fd37cf..07cbd9b2 100644 --- a/Gemfile +++ b/Gemfile @@ -41,6 +41,7 @@ gem "flipper-ui" gem "httparty", "~> 0.21" gem "invisible_captcha" gem "omniauth-azure-activedirectory-v2" +gem "rolify" gem "sentry-rails", "~> 5.11" group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 7171c789..fc87f53e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -339,6 +339,7 @@ GEM actionpack (>= 5.2) railties (>= 5.2) rexml (3.2.6) + rolify (6.0.1) rspec-core (3.12.2) rspec-support (~> 3.12.0) rspec-expectations (3.12.3) @@ -499,6 +500,7 @@ DEPENDENCIES pry-byebug puma (~> 6.4) rails (~> 7.0.8) + rolify rspec-rails rubocop-govuk rubocop-performance diff --git a/app/controllers/system_admin/users_controller.rb b/app/controllers/system_admin/users_controller.rb index 86167fa5..07c3d000 100644 --- a/app/controllers/system_admin/users_controller.rb +++ b/app/controllers/system_admin/users_controller.rb @@ -8,12 +8,19 @@ def index def new @user = User.new + @roles = Role.all end - def edit; end + def edit + @roles = Role.all + end def create @user = User.new(user_params) + @user.roles = [] + params[:user][:role_ids].each do |role_id| + @user.add_role(Role.find(role_id).name) if role_id.present? + end if @user.save redirect_to(users_path, notice: t("users.create.success")) @@ -24,6 +31,11 @@ def create def update if @user.update(user_params) + @user.roles = [] + params[:user][:role_ids].each do |role_id| + @user.add_role(Role.find(role_id).name) if role_id.present? + end + @user.save redirect_to(users_path, notice: t("users.update.success")) else render(:edit, status: :unprocessable_entity) @@ -45,7 +57,7 @@ def set_user # Only allow a list of trusted parameters through. def user_params - params.require(:user).permit(:email) + params.require(:user).permit(:email, :role_ids) end end end diff --git a/app/models/role.rb b/app/models/role.rb new file mode 100644 index 00000000..9742cba5 --- /dev/null +++ b/app/models/role.rb @@ -0,0 +1,27 @@ +# == Schema Information +# +# Table name: roles +# +# id :bigint not null, primary key +# name :string +# resource_type :string +# created_at :datetime not null +# updated_at :datetime not null +# resource_id :bigint +# +class Role < ApplicationRecord + ROLES_LIST = [:spectator, :servant, :manager, :admin, :super_admin] + + has_and_belongs_to_many :users, :join_table => :users_roles + + belongs_to :resource, + :polymorphic => true, + :optional => true + + + validates :resource_type, + :inclusion => { :in => Rolify.resource_types }, + :allow_nil => true + + scopify +end diff --git a/app/models/user.rb b/app/models/user.rb index 651f8a11..afc69f33 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,6 +8,7 @@ # updated_at :datetime not null # class User < ApplicationRecord + rolify audited devise :omniauthable, omniauth_providers: %i[azure_activedirectory_v2] -end +end \ No newline at end of file diff --git a/app/views/system_admin/users/_form.html.erb b/app/views/system_admin/users/_form.html.erb index 34dfaaf2..44eebb8a 100644 --- a/app/views/system_admin/users/_form.html.erb +++ b/app/views/system_admin/users/_form.html.erb @@ -2,6 +2,7 @@ <%= f.govuk_error_summary %> <%= f.govuk_text_field :email, label: { text: 'Email' }, hint: { text: 'The email address of the user' } %> + <%= f.govuk_collection_check_boxes :role_ids, @roles, :id, ->(r){r.name.humanize}, legend: { text: "Roles" } %> <%= f.govuk_submit("Save") %> <% end %> diff --git a/config/initializers/rolify.rb b/config/initializers/rolify.rb new file mode 100644 index 00000000..bbf8fe26 --- /dev/null +++ b/config/initializers/rolify.rb @@ -0,0 +1,10 @@ +Rolify.configure do |config| + # By default ORM adapter is ActiveRecord. uncomment to use mongoid + # config.use_mongoid + + # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false + # config.use_dynamic_shortcuts + + # Configuration to remove roles from database once the last resource is removed. Default is: true + # config.remove_role_if_empty = false +end diff --git a/db/migrate/20231003024901_rolify_create_roles.rb b/db/migrate/20231003024901_rolify_create_roles.rb new file mode 100644 index 00000000..438b3bae --- /dev/null +++ b/db/migrate/20231003024901_rolify_create_roles.rb @@ -0,0 +1,18 @@ +class RolifyCreateRoles < ActiveRecord::Migration[7.0] + def change + create_table(:roles) do |t| + t.string :name + t.references :resource, :polymorphic => true + + t.timestamps + end + + create_table(:users_roles, :id => false) do |t| + t.references :user + t.references :role + end + + add_index(:roles, [ :name, :resource_type, :resource_id ]) + add_index(:users_roles, [ :user_id, :role_id ]) + end +end diff --git a/db/schema.rb b/db/schema.rb index 2a356c60..22705777 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: 2023_09_15_100841) do +ActiveRecord::Schema[7.0].define(version: 2023_10_03_024901) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "plpgsql" @@ -166,6 +166,16 @@ t.index ["application_id"], name: "index_qa_statuses_on_application_id" end + create_table "roles", force: :cascade do |t| + t.string "name" + t.string "resource_type" + t.bigint "resource_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id" + t.index ["resource_type", "resource_id"], name: "index_roles_on_resource" + end + create_table "schools", force: :cascade do |t| t.string "name" t.string "headteacher_name" @@ -179,6 +189,14 @@ t.datetime "updated_at", null: false end + create_table "users_roles", id: false, force: :cascade do |t| + t.bigint "user_id" + t.bigint "role_id" + t.index ["role_id"], name: "index_users_roles_on_role_id" + t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id" + t.index ["user_id"], name: "index_users_roles_on_user_id" + end + add_foreign_key "applicants", "schools" add_foreign_key "applications", "applicants" add_foreign_key "qa_statuses", "applications" diff --git a/lib/tasks/roles.rake b/lib/tasks/roles.rake new file mode 100644 index 00000000..60a92268 --- /dev/null +++ b/lib/tasks/roles.rake @@ -0,0 +1,8 @@ +namespace :roles do + desc "Create default roles" + task create_defaults: :environment do + Role::ROLES_LIST.each do |role_name| + Role.find_or_create_by(name: role_name) + end + end +end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb new file mode 100644 index 00000000..0c215ab0 --- /dev/null +++ b/spec/factories/roles.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: roles +# +# id :bigint not null, primary key +# name :string +# resource_type :string +# created_at :datetime not null +# updated_at :datetime not null +# resource_id :bigint +# +FactoryBot.define do + factory :role do + + end +end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb new file mode 100644 index 00000000..b7d00b6a --- /dev/null +++ b/spec/models/role_spec.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: roles +# +# id :bigint not null, primary key +# name :string +# resource_type :string +# created_at :datetime not null +# updated_at :datetime not null +# resource_id :bigint +# +require 'rails_helper' + +RSpec.describe Role, type: :model do + it { is_expected.to have_db_column(:name) } +end From b367e010fda893397ccdb8cc1f1b5ace0cef5bad Mon Sep 17 00:00:00 2001 From: Raul Gracia Date: Wed, 4 Oct 2023 09:01:47 +0100 Subject: [PATCH 2/2] Fix rubocop offenses --- app/controllers/system_admin/users_controller.rb | 4 ++-- app/models/role.rb | 15 +++++++-------- app/models/user.rb | 2 +- config/initializers/rolify.rb | 2 +- db/migrate/20231003024901_rolify_create_roles.rb | 10 +++++----- spec/factories/roles.rb | 2 +- spec/models/role_spec.rb | 4 ++-- 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/controllers/system_admin/users_controller.rb b/app/controllers/system_admin/users_controller.rb index 07c3d000..3fb15920 100644 --- a/app/controllers/system_admin/users_controller.rb +++ b/app/controllers/system_admin/users_controller.rb @@ -17,7 +17,7 @@ def edit def create @user = User.new(user_params) - @user.roles = [] + @user.roles = [] params[:user][:role_ids].each do |role_id| @user.add_role(Role.find(role_id).name) if role_id.present? end @@ -31,7 +31,7 @@ def create def update if @user.update(user_params) - @user.roles = [] + @user.roles = [] params[:user][:role_ids].each do |role_id| @user.add_role(Role.find(role_id).name) if role_id.present? end diff --git a/app/models/role.rb b/app/models/role.rb index 9742cba5..a6c51029 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -10,18 +10,17 @@ # resource_id :bigint # class Role < ApplicationRecord - ROLES_LIST = [:spectator, :servant, :manager, :admin, :super_admin] + ROLES_LIST = %i[spectator servant manager admin super_admin].freeze + + has_many :users, through: :users_roles - has_and_belongs_to_many :users, :join_table => :users_roles - belongs_to :resource, - :polymorphic => true, - :optional => true - + polymorphic: true, + optional: true validates :resource_type, - :inclusion => { :in => Rolify.resource_types }, - :allow_nil => true + inclusion: { in: Rolify.resource_types }, + allow_nil: true scopify end diff --git a/app/models/user.rb b/app/models/user.rb index afc69f33..4b336863 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,4 +11,4 @@ class User < ApplicationRecord rolify audited devise :omniauthable, omniauth_providers: %i[azure_activedirectory_v2] -end \ No newline at end of file +end diff --git a/config/initializers/rolify.rb b/config/initializers/rolify.rb index bbf8fe26..da8907ef 100644 --- a/config/initializers/rolify.rb +++ b/config/initializers/rolify.rb @@ -4,7 +4,7 @@ # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false # config.use_dynamic_shortcuts - + # Configuration to remove roles from database once the last resource is removed. Default is: true # config.remove_role_if_empty = false end diff --git a/db/migrate/20231003024901_rolify_create_roles.rb b/db/migrate/20231003024901_rolify_create_roles.rb index 438b3bae..e7ca9c07 100644 --- a/db/migrate/20231003024901_rolify_create_roles.rb +++ b/db/migrate/20231003024901_rolify_create_roles.rb @@ -2,17 +2,17 @@ class RolifyCreateRoles < ActiveRecord::Migration[7.0] def change create_table(:roles) do |t| t.string :name - t.references :resource, :polymorphic => true + t.references :resource, polymorphic: true t.timestamps end - create_table(:users_roles, :id => false) do |t| + create_table(:users_roles, id: false) do |t| t.references :user t.references :role end - - add_index(:roles, [ :name, :resource_type, :resource_id ]) - add_index(:users_roles, [ :user_id, :role_id ]) + + add_index(:roles, %i[name resource_type resource_id]) + add_index(:users_roles, %i[user_id role_id]) end end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb index 0c215ab0..93fe95f7 100644 --- a/spec/factories/roles.rb +++ b/spec/factories/roles.rb @@ -11,6 +11,6 @@ # FactoryBot.define do factory :role do - + name { "Admin" } end end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index b7d00b6a..a9cd1e51 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -9,8 +9,8 @@ # updated_at :datetime not null # resource_id :bigint # -require 'rails_helper' +require "rails_helper" -RSpec.describe Role, type: :model do +RSpec.describe Role do it { is_expected.to have_db_column(:name) } end