mirror of
https://github.com/discourse/discourse.git
synced 2025-03-20 15:56:39 +08:00
FIX: validate parent category/subcategories permissions
See: https://meta.discourse.org/t/subcategories-do-not-inherit-permissions-from-parent-category/17174/23 for more details This ensures users with access to child category can always at least see parent
This commit is contained in:
parent
090e9c8432
commit
39522659a6
@ -99,7 +99,7 @@ const Category = RestModel.extend({
|
|||||||
color: this.get("color"),
|
color: this.get("color"),
|
||||||
text_color: this.get("text_color"),
|
text_color: this.get("text_color"),
|
||||||
secure: this.get("secure"),
|
secure: this.get("secure"),
|
||||||
permissions: this.get("permissionsForUpdate"),
|
permissions: this._permissionsForUpdate(),
|
||||||
auto_close_hours: this.get("auto_close_hours"),
|
auto_close_hours: this.get("auto_close_hours"),
|
||||||
auto_close_based_on_last_post: this.get(
|
auto_close_based_on_last_post: this.get(
|
||||||
"auto_close_based_on_last_post"
|
"auto_close_based_on_last_post"
|
||||||
@ -135,8 +135,8 @@ const Category = RestModel.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("permissions")
|
_permissionsForUpdate() {
|
||||||
permissionsForUpdate(permissions) {
|
const permissions = this.get("permissions");
|
||||||
let rval = {};
|
let rval = {};
|
||||||
permissions.forEach(p => (rval[p.group_name] = p.permission.id));
|
permissions.forEach(p => (rval[p.group_name] = p.permission.id));
|
||||||
return rval;
|
return rval;
|
||||||
|
@ -140,7 +140,7 @@ class CategoriesController < ApplicationController
|
|||||||
|
|
||||||
render_serialized(@category, CategorySerializer)
|
render_serialized(@category, CategorySerializer)
|
||||||
else
|
else
|
||||||
return render_json_error(@category) unless @category.save
|
return render_json_error(@category)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ class Category < ActiveRecord::Base
|
|||||||
|
|
||||||
validate :ensure_slug
|
validate :ensure_slug
|
||||||
|
|
||||||
|
validate :permissions_compatibility_validator
|
||||||
|
|
||||||
validates :auto_close_hours, numericality: { greater_than: 0, less_than_or_equal_to: 87600 }, allow_nil: true
|
validates :auto_close_hours, numericality: { greater_than: 0, less_than_or_equal_to: 87600 }, allow_nil: true
|
||||||
|
|
||||||
after_create :create_category_definition
|
after_create :create_category_definition
|
||||||
@ -631,6 +633,43 @@ class Category < ActiveRecord::Base
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def permissions_compatibility_validator
|
||||||
|
# when saving subcategories
|
||||||
|
if @permissions && parent_category_id.present?
|
||||||
|
return if parent_category.category_groups.empty?
|
||||||
|
|
||||||
|
parent_permissions = parent_category.category_groups.pluck(:group_id, :permission_type)
|
||||||
|
child_permissions = @permissions.empty? ? [[Group[:everyone].id, CategoryGroup.permission_types[:full]]] : @permissions
|
||||||
|
check_permissions_compatibility(parent_permissions, child_permissions)
|
||||||
|
|
||||||
|
# when saving parent category
|
||||||
|
elsif @permissions && subcategories.present?
|
||||||
|
return if @permissions.empty?
|
||||||
|
|
||||||
|
parent_permissions = @permissions
|
||||||
|
child_permissions = subcategories_permissions.uniq
|
||||||
|
|
||||||
|
check_permissions_compatibility(parent_permissions, child_permissions)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_permissions_compatibility(parent_permissions, child_permissions)
|
||||||
|
parent_groups = parent_permissions.map(&:first)
|
||||||
|
child_groups = child_permissions.map(&:first)
|
||||||
|
only_in_subcategory = child_groups - parent_groups
|
||||||
|
|
||||||
|
errors.add(:base, I18n.t("category.errors.permission_conflict")) if only_in_subcategory.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def subcategories_permissions
|
||||||
|
CategoryGroup.joins(:category)
|
||||||
|
.where(['categories.parent_category_id = ?', self.id])
|
||||||
|
.pluck(:group_id, :permission_type)
|
||||||
|
.uniq
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
@ -586,6 +586,7 @@ en:
|
|||||||
email_already_used_in_group: "'%{email}' is already used by the group '%{group_name}'."
|
email_already_used_in_group: "'%{email}' is already used by the group '%{group_name}'."
|
||||||
email_already_used_in_category: "'%{email}' is already used by the category '%{category_name}'."
|
email_already_used_in_category: "'%{email}' is already used by the category '%{category_name}'."
|
||||||
description_incomplete: "The category description post must have at least one paragraph."
|
description_incomplete: "The category description post must have at least one paragraph."
|
||||||
|
permission_conflict: "Subcategory permissions cannot be less restrictive than parent's."
|
||||||
cannot_delete:
|
cannot_delete:
|
||||||
uncategorized: "Can't delete Uncategorized"
|
uncategorized: "Can't delete Uncategorized"
|
||||||
has_subcategories: "Can't delete this category because it has sub-categories."
|
has_subcategories: "Can't delete this category because it has sub-categories."
|
||||||
|
@ -751,4 +751,77 @@ describe Category do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "validate permissions compatibility" do
|
||||||
|
let(:admin) { Fabricate(:admin) }
|
||||||
|
let(:group) { Fabricate(:group) }
|
||||||
|
let(:group2) { Fabricate(:group) }
|
||||||
|
let(:parent_category) { Fabricate(:category, name: "parent") }
|
||||||
|
let(:subcategory) { Fabricate(:category, name: "child1", parent_category_id: parent_category.id) }
|
||||||
|
let(:subcategory2) { Fabricate(:category, name: "child2", parent_category_id: parent_category.id) }
|
||||||
|
|
||||||
|
context "when changing subcategory permissions" do
|
||||||
|
it "it is not valid if permissions are less restrictive" do
|
||||||
|
parent_category.set_permissions(group => :readonly)
|
||||||
|
parent_category.save!
|
||||||
|
|
||||||
|
subcategory.set_permissions(group => :full, group2 => :readonly)
|
||||||
|
|
||||||
|
expect(subcategory.valid?).to eq(false)
|
||||||
|
expect(subcategory.errors.full_messages).to eq([I18n.t("category.errors.permission_conflict")])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if permissions are same or more restrictive" do
|
||||||
|
parent_category.set_permissions(group => :full, group2 => :create_post)
|
||||||
|
parent_category.save!
|
||||||
|
|
||||||
|
subcategory.set_permissions(group => :create_post, group2 => :full)
|
||||||
|
|
||||||
|
expect(subcategory.valid?).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if no permissions are set on parent" do
|
||||||
|
parent_category.set_permissions(everyone: :full)
|
||||||
|
parent_category.save!
|
||||||
|
|
||||||
|
subcategory.set_permissions(group => :create_post, group2 => :create_post)
|
||||||
|
|
||||||
|
expect(subcategory.valid?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when changing parent category permissions" do
|
||||||
|
it "it is not valid if subcategory permissions are less restrictive" do
|
||||||
|
subcategory.set_permissions(group => :create_post)
|
||||||
|
subcategory.save!
|
||||||
|
subcategory2.set_permissions(group => :create_post, group2 => :create_post)
|
||||||
|
subcategory2.save!
|
||||||
|
|
||||||
|
parent_category.set_permissions(group => :readonly)
|
||||||
|
|
||||||
|
expect(parent_category.valid?).to eq(false)
|
||||||
|
expect(parent_category.errors.full_messages).to eq([I18n.t("category.errors.permission_conflict")])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if subcategory permissions are same or more restrictive" do
|
||||||
|
subcategory.set_permissions(group => :create_post)
|
||||||
|
subcategory.save!
|
||||||
|
subcategory2.set_permissions(group => :create_post, group2 => :create_post)
|
||||||
|
subcategory2.save!
|
||||||
|
|
||||||
|
parent_category.set_permissions(group => :full, group2 => :create_post)
|
||||||
|
|
||||||
|
expect(parent_category.valid?).to eq(true)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid if no permissions set on parent" do
|
||||||
|
subcategory.set_permissions(group => :create_post)
|
||||||
|
subcategory.save
|
||||||
|
parent_category.set_permissions(everyone: :full)
|
||||||
|
|
||||||
|
expect(parent_category.valid?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user