diff --git a/app/components/admin/groups/group_rename_component.html.erb b/app/components/admin/groups/group_rename_component.html.erb index 785c53214..440168d25 100644 --- a/app/components/admin/groups/group_rename_component.html.erb +++ b/app/components/admin/groups/group_rename_component.html.erb @@ -14,7 +14,7 @@ - <%= form_with model: [:admin, Current.tenant, @group] do |form| %> + <%= form_with model: [:admin, Current.tenant, @group], url: admin_tenant_groups_path(Current.tenant, @group) do |form| %>
diff --git a/app/components/admin/permissions/groups_list_row_component.html.erb b/app/components/admin/permissions/groups_list_row_component.html.erb index a06b6b7c6..9ac1df95f 100644 --- a/app/components/admin/permissions/groups_list_row_component.html.erb +++ b/app/components/admin/permissions/groups_list_row_component.html.erb @@ -5,14 +5,10 @@
- <% if @group.is_modifiable? %> - <%= render Admin::Groups::GroupRenameComponent.new(@group) if @group.is_modifiable? %> - <% else %> - <%= @group.name %> - <% end %> + <%= @group.name %>
- <%= t :user, count: @group.users.count %> + <%= t("user", count: @group.users.count) %>
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 11578d6d2..a466ed6db 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -3,40 +3,39 @@ class Admin::GroupsController < ApplicationController def index authorize([:admin, Group]) - @custom_groups = policy_scope([:admin, Group]).where(tenant_id: Current.tenant.id).where.not(group_type: %w[ALL USER]) - @system_groups = policy_scope([:admin, Group]).where(tenant_id: Current.tenant.id, group_type: %w[ALL USER]) + + @editable_groups = group_policy_scope.where(tenant_id: Current.tenant.id).editable + @non_editable_groups = group_policy_scope.where(tenant_id: Current.tenant.id).where.not(id: @editable_groups.pluck(:id)) end def show - authorize([:admin, @group]) - @other_tags = other_tags + authorize([:admin, @group], policy_class: Admin::GroupPolicy) end def new - @group = Current.tenant.groups.new - authorize([:admin, @group]) + @group = Current.tenant.custom_groups.new + authorize([:admin, @group], policy_class: Admin::GroupPolicy) end def edit - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) end def edit_members - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) end def show_members - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) end def edit_permissions - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) end def create - @group = Current.tenant.groups.new(group_params) - @group.group_type = 'CUSTOM' - authorize([:admin, @group]) + @group = Current.tenant.custom_groups.new(group_params) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) if @group.save redirect_to edit_members_admin_tenant_group_url(Current.tenant, @group, step: :new), notice: 'Group was successfully created' @@ -46,7 +45,7 @@ def create end def update - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) if @group.update(group_params) redirect_to admin_tenant_groups_url(Current.tenant), notice: 'Group was successfully updated' else @@ -55,20 +54,20 @@ def update end def destroy - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) @group.destroy redirect_to admin_tenant_groups_url(Current.tenant), notice: 'Group was successfully destroyed' end def search_non_members - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) return if params[:name_search].blank? @users = non_members_search_clause end def search_non_tags - authorize([:admin, @group]) + authorize([:admin, @group], policy_class: Admin::GroupPolicy) return if params[:name_search].blank? @tags = non_tags_search_clause @@ -77,7 +76,7 @@ def search_non_tags private def non_members_search_clause - policy_scope([:admin, User]) + user_policy_scope .where(tenant: Current.tenant.id) .where.not(id: User.joins(:group_memberships).where(group_memberships: { group_id: @group.id })) .where('unaccent(name) ILIKE unaccent(?)', "%#{params[:name_search]}%") @@ -85,7 +84,7 @@ def non_members_search_clause end def non_tags_search_clause - policy_scope([:admin, Tag]) + tag_policy_scope .where(tenant: Current.tenant.id) .where.not(id: Tag.joins(:tag_groups).where(tag_groups: { group_id: @group.id })) .where('unaccent(name) ILIKE unaccent(?)', "%#{params[:name_search]}%") @@ -93,10 +92,22 @@ def non_tags_search_clause end def set_group - @group = policy_scope([:admin, Group]).find(params[:id]) + @group = group_policy_scope.find(params[:id]) end def group_params - params.require(:group).permit(:name, :group_type) + params.require(:custom_group).permit(:name) + end + + def group_policy_scope + policy_scope([:admin, Group]) + end + + def user_policy_scope + policy_scope([:admin, User]) + end + + def tag_policy_scope + policy_scope([:admin, Tag]) end end diff --git a/app/models/admin_group.rb b/app/models/admin_group.rb new file mode 100644 index 000000000..6c5cd7c78 --- /dev/null +++ b/app/models/admin_group.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: groups +# +# id :bigint not null, primary key +# group_type :enum +# name :string not null +# type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# tenant_id :bigint not null +# +class AdminGroup < Group + def name + I18n.t("group.names.admin") + end +end diff --git a/app/models/all_group.rb b/app/models/all_group.rb new file mode 100644 index 000000000..06c77c9f7 --- /dev/null +++ b/app/models/all_group.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: groups +# +# id :bigint not null, primary key +# group_type :enum +# name :string not null +# type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# tenant_id :bigint not null +# +class AllGroup < Group + def name + I18n.t("group.names.all") + end +end diff --git a/app/models/audit_log.rb b/app/models/audit_log.rb index 6ec004c54..1289ca0b2 100644 --- a/app/models/audit_log.rb +++ b/app/models/audit_log.rb @@ -1,3 +1,22 @@ +# == Schema Information +# +# Table name: audit_logs +# +# id :bigint not null, primary key +# actor_name :string +# changeset :jsonb +# happened_at :datetime not null +# new_value :string +# previous_value :string +# thread_id_archived :integer +# thread_title :string +# type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# actor_id :bigint +# message_thread_id :bigint +# tenant_id :bigint +# require "csv" class AuditLog < ApplicationRecord diff --git a/app/models/custom_group.rb b/app/models/custom_group.rb new file mode 100644 index 000000000..cd83ccae9 --- /dev/null +++ b/app/models/custom_group.rb @@ -0,0 +1,14 @@ +# == Schema Information +# +# Table name: groups +# +# id :bigint not null, primary key +# group_type :enum +# name :string not null +# type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# tenant_id :bigint not null +# +class CustomGroup < Group +end diff --git a/app/models/group.rb b/app/models/group.rb index 4c473143c..0ce00e218 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -3,8 +3,9 @@ # Table name: groups # # id :bigint not null, primary key -# group_type :enum not null +# group_type :enum # name :string not null +# type :string not null # created_at :datetime not null # updated_at :datetime not null # tenant_id :bigint not null @@ -16,11 +17,26 @@ class Group < ApplicationRecord has_many :tag_groups, dependent: :destroy has_many :tags, through: :tag_groups + EDITABLE_GROUP_TYPES = %w[AdminGroup SignerGroup CustomGroup] + + scope :editable, -> { where(type: EDITABLE_GROUP_TYPES) } + validates_presence_of :name validates_uniqueness_of :name, scope: :tenant_id - validates :group_type, inclusion: { in: ['ALL', 'USER', 'ADMIN', 'CUSTOM'], allow_blank: false } - def is_modifiable? - !group_type.in? %w[ALL USER] + def editable? + type.in? EDITABLE_GROUP_TYPES + end + + def system? + !is_a?(CustomGroup) + end + + def destroyable? + !system? + end + + def renamable? + !system? end end diff --git a/app/models/group_membership.rb b/app/models/group_membership.rb index f1b1e4712..858b5b9c4 100644 --- a/app/models/group_membership.rb +++ b/app/models/group_membership.rb @@ -11,9 +11,4 @@ class GroupMembership < ApplicationRecord belongs_to :group belongs_to :user - - def group_membership_modifiable? - # can't be removed from default groups - "TENANT_NAME"_ALL group and named user default group "USER_NAME"_USER - !group.group_type.in? ['ALL', 'USER'] - end end diff --git a/app/models/signer_group.rb b/app/models/signer_group.rb new file mode 100644 index 000000000..a87ba561b --- /dev/null +++ b/app/models/signer_group.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: groups +# +# id :bigint not null, primary key +# group_type :enum +# name :string not null +# type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# tenant_id :bigint not null +# +class SignerGroup < Group + def name + I18n.t("group.names.signer") + end +end diff --git a/app/models/tag.rb b/app/models/tag.rb index bb0a62554..165d294ad 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -28,7 +28,7 @@ class Tag < ApplicationRecord scope :visible, -> { where(visible: true) } - after_create_commit ->(tag) { tag.mark_readable_by_groups(tag.tenant.admin_groups) } + after_create_commit ->(tag) { tag.mark_readable_by_groups([tag.tenant.admin_group]) } after_update_commit ->(tag) { EventBus.publish(:tag_renamed, tag) if previous_changes.key?("name") } after_destroy ->(tag) { EventBus.publish(:tag_destroyed, tag) } diff --git a/app/models/tenant.rb b/app/models/tenant.rb index 30c5a0d54..fb3f9d99d 100644 --- a/app/models/tenant.rb +++ b/app/models/tenant.rb @@ -11,11 +11,13 @@ class Tenant < ApplicationRecord has_many :users, dependent: :destroy has_many :groups, dependent: :destroy - has_one :all_group, -> { where(group_type: 'ALL') }, class_name: 'Group' - has_many :admin_groups, -> { where(group_type: 'ADMIN') }, class_name: 'Group' + has_one :all_group + has_one :signer_group + has_one :admin_group + has_many :custom_groups has_many :boxes, dependent: :destroy - has_many :automation_rules, class_name: 'Automation::Rule', dependent: :destroy + has_many :automation_rules, class_name: "Automation::Rule", dependent: :destroy has_many :tags, dependent: :destroy has_many :filters after_create :create_default_objects @@ -25,8 +27,9 @@ class Tenant < ApplicationRecord private def create_default_objects - groups.create!(name: 'all', group_type: 'ALL') - groups.create!(name: 'admins', group_type: 'ADMIN') + create_all_group!(name: "all") + create_admin_group!(name: "admins") + create_signer_group!(name: "signers") tags.create!(name: 'Drafty', system_name: Tag::DRAFT_SYSTEM_NAME, external: false, visible: true) tags.create!(name: 'Na prevzatie', system_name: 'delivery_notification', external: false, visible: true) end diff --git a/app/models/user.rb b/app/models/user.rb index 53d0d7286..7bf81e460 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,6 +11,7 @@ # class User < ApplicationRecord belongs_to :tenant + has_many :group_memberships, dependent: :destroy has_many :groups, through: :group_memberships has_many :own_tags, class_name: 'Tag', foreign_key: 'user_id', inverse_of: :owner, dependent: :nullify @@ -29,21 +30,21 @@ def site_admin? end def admin? - groups.exists?(group_type: 'ADMIN') + groups.exists?(type: "AdminGroup") end def user_group - groups.where(group_type: 'USER').first + groups.where(type: "UserGroup").first end private def delete_user_group - groups.destroy_by(group_type: 'USER') + user_group.destroy end def handle_default_groups - groups.create!(name: name, group_type: 'USER', tenant_id: tenant_id) + groups.create!(name: name, type: "UserGroup", tenant_id: tenant_id) group_memberships.create!(group: tenant.all_group) end end diff --git a/app/models/user_group.rb b/app/models/user_group.rb new file mode 100644 index 000000000..21f27c2b5 --- /dev/null +++ b/app/models/user_group.rb @@ -0,0 +1,14 @@ +# == Schema Information +# +# Table name: groups +# +# id :bigint not null, primary key +# group_type :enum +# name :string not null +# type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# tenant_id :bigint not null +# +class UserGroup < Group +end diff --git a/app/policies/admin/group_policy.rb b/app/policies/admin/group_policy.rb index b4b4ec855..9800bba87 100644 --- a/app/policies/admin/group_policy.rb +++ b/app/policies/admin/group_policy.rb @@ -35,6 +35,8 @@ def new? end def update? + return false unless @group.editable? + @user.site_admin? || @user.admin? end @@ -47,14 +49,16 @@ def edit_members? end def show_members? - update? + @user.site_admin? || @user.admin? end def edit_permissions? - update? + @user.site_admin? || @user.admin? end def destroy? + return false if @group.system? + @user.site_admin? || @user.admin? end diff --git a/app/views/admin/groups/_form.html.erb b/app/views/admin/groups/_form.html.erb index 9a9aa22b7..d203d8e38 100644 --- a/app/views/admin/groups/_form.html.erb +++ b/app/views/admin/groups/_form.html.erb @@ -13,10 +13,6 @@ <%= form.label :name %> <%= form.text_field :name, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %>
-
- <%= form.label :group_type %> - <%= form.select :group_type, ['CUSTOM', 'ADMIN'], class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %> -
<%= form.label :tenant_id %> <%= form.text_field :tenant_id, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %> diff --git a/app/views/admin/groups/_group.html.erb b/app/views/admin/groups/_group.html.erb index f3e86a6cf..98f25b147 100644 --- a/app/views/admin/groups/_group.html.erb +++ b/app/views/admin/groups/_group.html.erb @@ -4,8 +4,8 @@ <%= group.name %>

- Group type: - <%= group.group_type %> + Type: + <%= group.type.to_s %>

Tenant: diff --git a/app/views/admin/groups/index.html.erb b/app/views/admin/groups/index.html.erb index f71494a6c..7e091c0c1 100644 --- a/app/views/admin/groups/index.html.erb +++ b/app/views/admin/groups/index.html.erb @@ -1 +1 @@ -<%= render Admin::Groups::GroupsListComponent.new(custom_groups: @custom_groups, system_groups: @system_groups) %> +<%= render Admin::Groups::GroupsListComponent.new(editable_groups: @editable_groups, non_editable_groups: @non_editable_groups) %> diff --git a/app/views/admin/groups/show.html.erb b/app/views/admin/groups/show.html.erb index 94ba4829b..324ae5e4a 100644 --- a/app/views/admin/groups/show.html.erb +++ b/app/views/admin/groups/show.html.erb @@ -1 +1 @@ -<%= render Admin::Groups::GroupFormComponent.new(group: @group, step: :show)%> +<%= render Admin::Groups::GroupFormComponent.new(group: @group, readonly: true)%> diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 950c6b690..3b25971c4 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -239,6 +239,11 @@ sk: "Automation::BoxCondition": "je" "Automation::AddMessageThreadTagAction": "Pridaj štítok na vlákno" "Automation::AddTagAction": "Pridaj štítok" + group: + names: + all: "Všetci" + admin: "Administrátori" + signer: "Podpisovatelia" title: "Názov" tag_editing_in_message_threads: one: "Úprava štítkov v jednom vlákne" diff --git a/db/migrate/20231127215006_convert_groups_to_sti.rb b/db/migrate/20231127215006_convert_groups_to_sti.rb new file mode 100644 index 000000000..b91ba4d1c --- /dev/null +++ b/db/migrate/20231127215006_convert_groups_to_sti.rb @@ -0,0 +1,28 @@ +class ConvertGroupsToSti < ActiveRecord::Migration[7.0] + def up + add_column :groups, :type, :string, null: true + + Group.find_each do |group| + type = case group.group_type + when "ALL" + "AllGroup" + when "ADMIN" + "AdminGroup" + when "USER" + "UserGroup" + when "CUSTOM" + "CustomGroup" + end + + group.update_column(:type, type) + end + + change_column_null :groups, :type, false + change_column_null :groups, :group_type, true + end + + def down + change_column_null :groups, :group_type, false + remove_column :groups, :type + end +end diff --git a/db/migrate/20231127223506_generate_signing_groups_for_existing_tenants.rb b/db/migrate/20231127223506_generate_signing_groups_for_existing_tenants.rb new file mode 100644 index 000000000..a11b1a7fc --- /dev/null +++ b/db/migrate/20231127223506_generate_signing_groups_for_existing_tenants.rb @@ -0,0 +1,11 @@ +class GenerateSigningGroupsForExistingTenants < ActiveRecord::Migration[7.0] + def up + Tenant.find_each do |tenant| + tenant.create_signer_group!(name: "signers") + end + end + + def down + SignerGroup.destroy_all + end +end diff --git a/db/schema.rb b/db/schema.rb index a64d6eb8d..87bdaf763 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_11_23_085142) do +ActiveRecord::Schema[7.0].define(version: 2023_11_27_223506) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -268,9 +268,10 @@ create_table "groups", force: :cascade do |t| t.string "name", null: false t.bigint "tenant_id", null: false - t.enum "group_type", null: false, enum_type: "group_type" + t.enum "group_type", enum_type: "group_type" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "type", null: false t.index ["tenant_id"], name: "index_groups_on_tenant_id" end diff --git a/test/fixtures/groups.yml b/test/fixtures/groups.yml index 50a9de531..9044e1228 100644 --- a/test/fixtures/groups.yml +++ b/test/fixtures/groups.yml @@ -2,15 +2,20 @@ all: name: all - group_type: ALL + type: AllGroup tenant: ssd admins: name: admins - group_type: ADMIN + type: AdminGroup tenant: ssd basic_user: name: "Basic user" - group_type: USER + type: UserGroup tenant: ssd + +solver_admin: + name: admins + type: AdminGroup + tenant: solver diff --git a/test/models/tag_test.rb b/test/models/tag_test.rb index dda6ca210..dd3cf2e2a 100644 --- a/test/models/tag_test.rb +++ b/test/models/tag_test.rb @@ -5,6 +5,6 @@ class TagTest < ActiveSupport::TestCase tenant = tenants(:ssd) tag = Tag.create(name: 'New tag', tenant: tenant) - assert tenant.admin_groups == tag.groups + assert tag.groups == [tenant.admin_group] end end