From 2ddd26ad8ea83e02828993124f4b0f68c914fe2e Mon Sep 17 00:00:00 2001 From: Rainer Dema Date: Wed, 7 Jun 2023 13:41:42 +0200 Subject: [PATCH 1/4] Add Migrations for Role and Permission Set management This commit introduces database schema changes to extend the role and permission management capabilities of Solidus. These changes are derived from the solidus_user_roles gem and are designed to provide more granular control over user roles and permissions. Changes include: * Create Spree Permission Sets: Introduced a new Spree::PermissionSet table in the database. This table will store sets of permissions, each with a name and set identifier, that can be assigned to user roles. * Create Spree Roles Permissions: Introduced a new Spree::RolePermission table in the database. This table will store the associations between roles and permission sets. It includes foreign key references to the Spree::Role and Spree::PermissionSet tables. --- .../20230607100048_create_spree_permission_sets.rb | 11 +++++++++++ .../20230607100109_create_spree_roles_permissions.rb | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 core/db/migrate/20230607100048_create_spree_permission_sets.rb create mode 100644 core/db/migrate/20230607100109_create_spree_roles_permissions.rb diff --git a/core/db/migrate/20230607100048_create_spree_permission_sets.rb b/core/db/migrate/20230607100048_create_spree_permission_sets.rb new file mode 100644 index 00000000000..360275630cc --- /dev/null +++ b/core/db/migrate/20230607100048_create_spree_permission_sets.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class CreateSpreePermissionSets < ActiveRecord::Migration[7.0] + def change + create_table :spree_permission_sets do |t| + t.string :name + t.string :group + t.timestamps + end + end +end diff --git a/core/db/migrate/20230607100109_create_spree_roles_permissions.rb b/core/db/migrate/20230607100109_create_spree_roles_permissions.rb new file mode 100644 index 00000000000..e2f0e08da0f --- /dev/null +++ b/core/db/migrate/20230607100109_create_spree_roles_permissions.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class CreateSpreeRolesPermissions < ActiveRecord::Migration[7.0] + def change + create_table :spree_role_permissions do |t| + t.references :role + t.references :permission_set + t.timestamps + end + end +end From d13296f4e1dc01679a732a08b4e37ee8516a4150 Mon Sep 17 00:00:00 2001 From: Rainer Dema Date: Wed, 7 Jun 2023 13:41:49 +0200 Subject: [PATCH 2/4] Enhance Role model for better role management This commit expands the functionality of the `Spree::Role` model to provide more granular and efficient control over user roles and permissions. * Spree::RolePermission * Spree::Role enhancements * Spree::PermissionSet These enhancements are a part of our broader initiative to improve the flexibility and extensibility of role and permission management in Solidus, adapting functionality from the `solidus_user_roles` gem. --- core/app/models/spree/permission_set.rb | 10 ++++++++++ core/app/models/spree/role.rb | 16 ++++++++++++++++ core/app/models/spree/role_permission.rb | 8 ++++++++ core/spec/models/spree/role_spec.rb | 24 ++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 core/app/models/spree/permission_set.rb create mode 100644 core/app/models/spree/role_permission.rb create mode 100644 core/spec/models/spree/role_spec.rb diff --git a/core/app/models/spree/permission_set.rb b/core/app/models/spree/permission_set.rb new file mode 100644 index 00000000000..07a28c19cc7 --- /dev/null +++ b/core/app/models/spree/permission_set.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Spree + class PermissionSet < Spree::Base + has_many :role_permissions + has_many :roles, through: :role_permissions + + validates :name, :group, presence: true + end +end diff --git a/core/app/models/spree/role.rb b/core/app/models/spree/role.rb index 3e2d7d1a234..d5371c935b0 100644 --- a/core/app/models/spree/role.rb +++ b/core/app/models/spree/role.rb @@ -2,13 +2,29 @@ module Spree class Role < Spree::Base + RESERVED_ROLES = ['admin', 'default'].freeze + has_many :role_users, class_name: "Spree::RoleUser", dependent: :destroy + has_many :role_permissions, dependent: :destroy + has_many :permission_sets, through: :role_permissions has_many :users, through: :role_users + scope :non_base_roles, -> { where.not(name: RESERVED_ROLES) } + validates_uniqueness_of :name, case_sensitive: true + validates :name, uniqueness: true + after_save :assign_permissions def admin? name == "admin" end + + def permission_sets_constantized + permission_sets.map(&:name).map(&:constantize) + end + + def assign_permissions + ::Spree::Config.roles.assign_permissions name, permission_sets_constantized + end end end diff --git a/core/app/models/spree/role_permission.rb b/core/app/models/spree/role_permission.rb new file mode 100644 index 00000000000..a194df5b2f0 --- /dev/null +++ b/core/app/models/spree/role_permission.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Spree + class RolePermission < Spree::Base + belongs_to :role + belongs_to :permission_set + end +end diff --git a/core/spec/models/spree/role_spec.rb b/core/spec/models/spree/role_spec.rb new file mode 100644 index 00000000000..4433600bf9d --- /dev/null +++ b/core/spec/models/spree/role_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Spree::Role, type: :model do + describe '.non_base_roles' do + subject do + Spree::Role.non_base_roles + end + + context 'when there is a custom role' do + let(:role) { create(:role, name: 'custom role') } + let(:admin_role) { create(:admin_role) } + let(:default_role) { create(:role, name: 'default') } + + it { is_expected.to include(role) } + it { is_expected.not_to include(admin_role, default_role) } + end + + context 'when there is no custom roles' do + it { is_expected.to be_empty } + end + end +end From cc9eb18411c7e2283667303580993460b5c2655e Mon Sep 17 00:00:00 2001 From: Enzo Haguiwara Date: Tue, 22 Aug 2023 10:46:25 -0300 Subject: [PATCH 3/4] Add task to import current permissions This commit introduces a simple rake task that imports the Permission Sets to the DB, then iterates over the defined permissions on AppConfiguration to create the RolePermissions. --- .../import_existing_permission_sets.rake | 30 ++++++++++ .../import_existing_permission_sets_spec.rb | 56 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 core/lib/tasks/solidus/import_existing_permission_sets.rake create mode 100644 core/spec/lib/tasks/solidus/import_existing_permission_sets_spec.rb diff --git a/core/lib/tasks/solidus/import_existing_permission_sets.rake b/core/lib/tasks/solidus/import_existing_permission_sets.rake new file mode 100644 index 00000000000..675aad0b78d --- /dev/null +++ b/core/lib/tasks/solidus/import_existing_permission_sets.rake @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +namespace :solidus do + desc "Import existing permission sets to role permissions table" + task import_existing_permission_sets: :environment do + Zeitwerk::Loader.eager_load_all unless Rails.env.test? + + ActiveRecord::Base.transaction do + Spree::PermissionSets::Base.descendants.each do |permission| + Spree::PermissionSet.find_or_create_by(name: permission.to_s, group: permission.to_s.split("PermissionSets::").last.gsub(/Display|Management/i, "")) + end + + Spree::AppConfiguration.new.roles.roles.each do |role_name, role_config| + role_config.permission_sets.each do |set| + role = Spree::Role.find_or_create_by(name: role_name) + permission_set = Spree::PermissionSet.find_by(name: set.name) + + if permission_set + Spree::RolePermission.find_or_create_by!( + role: role, + permission_set: permission_set + ) + else + puts "#{set} was not found." + end + end + end + end + end +end diff --git a/core/spec/lib/tasks/solidus/import_existing_permission_sets_spec.rb b/core/spec/lib/tasks/solidus/import_existing_permission_sets_spec.rb new file mode 100644 index 00000000000..0baf1b888fd --- /dev/null +++ b/core/spec/lib/tasks/solidus/import_existing_permission_sets_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'rails_helper' + +path = Spree::Core::Engine.root.join('lib/tasks/solidus/import_existing_permission_sets.rake') + +RSpec.describe 'solidus' do + describe 'import_existing_permission_sets' do + include_context( + 'rake', + task_path: path, + task_name: 'solidus:import_existing_permission_sets' + ) + + it 'creates permission sets' do + expect(Spree::PermissionSet.pluck(:name)).to eq([]) + + task.invoke + + expect(Spree::PermissionSet.pluck(:name)).to eq(Spree::PermissionSets::Base.subclasses.map(&:to_s)) + end + + context 'when there is a custom role' do + let(:role_name) { :customer_service } + let(:permissions) { ['Spree::PermissionSets::OrderDisplay', 'Spree::PermissionSets::UserDisplay', 'Spree::PermissionSets::ProductDisplay'] } + + before do + roles = Spree::RoleConfiguration.new.tap do |role| + role.assign_permissions :default, ['Spree::PermissionSets::DefaultCustomer'] + role.assign_permissions :admin, ['Spree::PermissionSets::SuperUser'] + role.assign_permissions role_name, permissions + end + + allow_any_instance_of(Spree::AppConfiguration).to receive(:roles).and_return(roles) + end + + it 'creates the new role with permissions' do + expect(Spree::Role.find_by(name: role_name.to_s)).not_to be_present + + task.invoke + + role = Spree::Role.find_by(name: role_name.to_s) + expect(role).to be_present + expect(role.permission_sets.pluck(:name)).to match_array(permissions) + end + end + + context 'when permission set is not found' do + it 'prints out the missing permission set' do + allow(Spree::PermissionSet).to receive(:find_by).and_return(nil) + + expect { task.invoke }.to output(a_string_including('Spree::PermissionSets::DefaultCustomer')).to_stdout + end + end + end +end From f4e0fd25c647bc8fbe294bcbd27293e4280f6a64 Mon Sep 17 00:00:00 2001 From: Enzo Haguiwara Date: Thu, 31 Aug 2023 16:52:48 -0300 Subject: [PATCH 4/4] Assign roles from database on app configuration --- core/lib/spree/app_configuration.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/lib/spree/app_configuration.rb b/core/lib/spree/app_configuration.rb index 49baffcc554..ee1c7a9522a 100644 --- a/core/lib/spree/app_configuration.rb +++ b/core/lib/spree/app_configuration.rb @@ -605,6 +605,10 @@ def roles @roles ||= Spree::RoleConfiguration.new.tap do |roles| roles.assign_permissions :default, ['Spree::PermissionSets::DefaultCustomer'] roles.assign_permissions :admin, ['Spree::PermissionSets::SuperUser'] + + Spree::Role.non_base_roles.each do |role| + roles.assign_permissions role.name, role.permission_sets_constantized + end end end