diff --git a/app/assets/javascripts/discourse/controllers/edit_category_controller.js b/app/assets/javascripts/discourse/controllers/edit_category_controller.js index 2035fa4a537..5786c0cbedf 100644 --- a/app/assets/javascripts/discourse/controllers/edit_category_controller.js +++ b/app/assets/javascripts/discourse/controllers/edit_category_controller.js @@ -101,14 +101,12 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M return false; }, - addGroup: function(){ - this.get('model').addGroup(this.get("selectedGroup")); + addPermission: function(group, permission_id){ + this.get('model').addPermission({group_name: group + "", permission: Discourse.PermissionType.create({id: permission_id})}); }, - removeGroup: function(group){ - // OBVIOUS, Ember treats this as Ember.String, we need a real string here - group = group + ""; - this.get('model').removeGroup(group); + removePermission: function(permission){ + this.get('model').removePermission(permission); }, saveCategory: function() { diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index b7d755ccab8..4c63b01ce54 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -11,9 +11,21 @@ Discourse.Category = Discourse.Model.extend({ init: function() { this._super(); this.set("availableGroups", Em.A(this.get("available_groups"))); - this.set("groups", Em.A(this.groups)); + this.set("permissions", Em.A(_.map(this.group_permissions, function(elem){ + return { + group_name: elem.group_name, + permission: Discourse.PermissionType.create({id: elem.permission_type}) + }; + }))); }, + availablePermissions: function(){ + return [ Discourse.PermissionType.create({id: Discourse.PermissionType.FULL}), + Discourse.PermissionType.create({id: Discourse.PermissionType.CREATE_POST}), + Discourse.PermissionType.create({id: Discourse.PermissionType.READONLY}) + ]; + }.property(), + searchContext: function() { return ({ type: 'category', id: this.get('id'), category: this }); }.property('id'), @@ -37,15 +49,20 @@ Discourse.Category = Discourse.Model.extend({ } return Discourse.ajax(url, { - data: { + contentType: 'application/json', + dataType: 'json', + data: JSON.stringify({ name: this.get('name'), color: this.get('color'), text_color: this.get('text_color'), hotness: this.get('hotness'), secure: this.get('secure'), - group_names: this.get('groups').join(","), + permissions: _.map( + this.get('permissions'), function(p){ + return { group_name: p.group_name, permission_type: p.permission.id}; + }), auto_close_days: this.get('auto_close_days') - }, + }), type: this.get('id') ? 'PUT' : 'POST' }); }, @@ -54,22 +71,30 @@ Discourse.Category = Discourse.Model.extend({ return Discourse.ajax("/categories/" + (this.get('slug') || this.get('id')), { type: 'DELETE' }); }, - addGroup: function(group){ - this.get("groups").addObject(group); - this.get("availableGroups").removeObject(group); + addPermission: function(permission){ + this.get("permissions").addObject(permission); + this.get("availableGroups").removeObject(permission.group_name); }, - removeGroup: function(group){ - this.get("groups").removeObject(group); - this.get("availableGroups").addObject(group); + removePermission: function(permission){ + this.get("permissions").removeObject(permission); + this.get("availableGroups").addObject(permission.group_name); }, // note, this is used in a data attribute, data attributes get downcased // to avoid confusion later on using this naming here. description_text: function(){ return $("
" + this.get("description") + "
").text(); - }.property("description") + }.property("description"), + + permissions: function(){ + return Em.A([ + {group_name: "everyone", permission: Discourse.PermissionType.create({id: 1})}, + {group_name: "admins", permission: Discourse.PermissionType.create({id: 2}) }, + {group_name: "crap", permission: Discourse.PermissionType.create({id: 3}) } + ]); + }.property() }); diff --git a/app/assets/javascripts/discourse/models/permission_type.js b/app/assets/javascripts/discourse/models/permission_type.js new file mode 100644 index 00000000000..a37ba818c88 --- /dev/null +++ b/app/assets/javascripts/discourse/models/permission_type.js @@ -0,0 +1,23 @@ + +Discourse.PermissionType = Discourse.Model.extend({ + description: function(){ + var key = ""; + + switch(this.get("id")){ + case 1: + key = "full"; + break; + case 2: + key = "create_post"; + break; + case 3: + key = "readonly"; + break; + } + return I18n.t("permission_types." + key); + }.property("id") +}); + +Discourse.PermissionType.FULL = 1; +Discourse.PermissionType.CREATE_POST = 2; +Discourse.PermissionType.READONLY = 3; diff --git a/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars b/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars index 6cf2c695b0b..a15f34b4353 100644 --- a/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars +++ b/app/assets/javascripts/discourse/templates/modal/edit_category.js.handlebars @@ -60,25 +60,18 @@ {{#unless isUncategorized}}
- - {{#if secure}} -
- -
    - {{#each groups}} -
  • - {{this}} - -
  • - {{/each}} -
- {{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}} - -
- {{/if}} + + {{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}} + {{view Ember.Select optionValuePath="content.id" optionLabelPath="content.description" contentBinding="availablePermissions" valueBinding="selectedPermission"}} +
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fc1f7ef2c92..0b1bdc7ca75 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -102,7 +102,7 @@ class ApplicationController < ActionController::Base guardian.current_user.sync_notification_channel_position end - store_preloaded("site", Site.cached_json(current_user)) + store_preloaded("site", Site.cached_json(guardian)) if current_user.present? store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false))) diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 65256a61762..de5c1e83389 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -53,7 +53,7 @@ class CategoriesController < ApplicationController end def category_param_keys - [required_param_keys, :hotness, :secure, :group_names, :auto_close_days].flatten! + [required_param_keys, :hotness, :read_restricted, :permissions, :auto_close_days].flatten! end def category_params diff --git a/app/models/category.rb b/app/models/category.rb index 85943642c8d..d4a7594ef30 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -22,6 +22,7 @@ class Category < ActiveRecord::Base before_validation :ensure_slug after_save :invalidate_site_cache + before_save :apply_permissions after_create :create_category_definition after_create :publish_categories_list after_destroy :invalidate_site_cache @@ -34,16 +35,49 @@ class Category < ActiveRecord::Base scope :secured, ->(guardian = nil) { ids = guardian.secure_category_ids if guardian if ids.present? - where("NOT categories.secure or categories.id in (:cats)", cats: ids) + where("NOT categories.read_restricted or categories.id in (:cats)", cats: ids) else - where("NOT categories.secure") + where("NOT categories.read_restricted") end } + scope :topic_create_allowed, ->(guardian) { + scoped_to_permissions(guardian, [:full]) + } + + scope :post_create_allowed, ->(guardian) { + scoped_to_permissions(guardian, [:create_post, :full]) + } delegate :post_template, to: 'self.class' attr_accessor :displayable_topics + + def self.scoped_to_permissions(guardian, permission_types) + if guardian && guardian.is_staff? + scoped + else + permission_types = permission_types.map{ |permission_type| + CategoryGroup.permission_types[permission_type] + } + where("categories.id in ( + SELECT c.id FROM categories c + WHERE ( + NOT c.read_restricted AND + NOT EXISTS( + SELECT 1 FROM category_groups cg WHERE cg.category_id = categories.id ) + ) OR EXISTS( + SELECT 1 FROM category_groups cg + WHERE permission_type in (?) AND + cg.category_id = categories.id AND + group_id IN ( + SELECT g.group_id FROM group_users g where g.user_id = ? + ) + ) + )", permission_types,(!guardian || guardian.user.blank?) ? -1 : guardian.user.id) + end + end + # Internal: Update category stats: # of topics in past year, month, week for # all categories. def self.update_stats @@ -119,28 +153,65 @@ class Category < ActiveRecord::Base end end - def deny(group) - if group == :all - self.secure = true - end + # will reset permission on a topic to a particular + # set. + # + # Available permissions are, :full, :create_post, :readonly + # hash can be: + # + # :everyone => :full - everyone has everything + # :everyone => :readonly, :staff => :full + # 7 => 1 # you can pass a group_id and permission id + def set_permissions(permissions) + self.read_restricted, @permissions = Category.resolve_permissions(permissions) + + # Ideally we can just call .clear here, but it runs SQL, we only want to run it + # on save. end - def allow(group) - if group == :all - self.secure = false - # this is kind of annoying, there should be a clean way of queuing this stuff - category_groups.destroy_all unless new_record? - else - groups.push(group) + def apply_permissions + if @permissions + category_groups.destroy_all + @permissions.each do |group_id, permission_type| + category_groups.build(group_id: group_id, permission_type: permission_type) + end + @permissions = nil end end def secure_group_ids - if self.secure + if self.read_restricted? groups.pluck("groups.id") end end + + def self.resolve_permissions(permissions) + read_restricted = true + + everyone = Group::AUTO_GROUPS[:everyone] + full = CategoryGroup.permission_types[:full] + + mapped = permissions.map do |group,permission| + group = group.id if Group === group + + # subtle, using Group[] ensures the group exists in the DB + group = Group[group].id unless Fixnum === group + permission = CategoryGroup.permission_types[permission] unless Fixnum === permission + + [group, permission] + end + + mapped.each do |group, permission| + if group == everyone && permission == full + return [false, []] + end + + read_restricted = false if group == everyone + end + + [read_restricted, mapped] + end end # == Schema Information @@ -162,7 +233,7 @@ end # description :text # text_color :string(6) default("FFFFFF"), not null # hotness :float default(5.0), not null -# secure :boolean default(FALSE), not null +# read_restricted :boolean default(FALSE), not null # auto_close_days :float # # Indexes diff --git a/app/models/category_group.rb b/app/models/category_group.rb index 679c062e9ee..49cf5172bb5 100644 --- a/app/models/category_group.rb +++ b/app/models/category_group.rb @@ -1,16 +1,22 @@ class CategoryGroup < ActiveRecord::Base belongs_to :category belongs_to :group + + def self.permission_types + @permission_types ||= Enum.new(:full, :create_post, :readonly) + end + end # == Schema Information # # Table name: category_groups # -# id :integer not null, primary key -# category_id :integer not null -# group_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# category_id :integer not null +# group_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# permission_type :integer default(1) # diff --git a/app/models/group.rb b/app/models/group.rb index 3d45987b0e0..f6fc1623f6d 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -10,6 +10,7 @@ class Group < ActiveRecord::Base validate :name_format_validator AUTO_GROUPS = { + :everyone => 0, :admins => 1, :moderators => 2, :staff => 3, @@ -34,6 +35,10 @@ class Group < ActiveRecord::Base group.save! end + # the everyone group is special, it can include non-users so there is no + # way to have the membership in a table + return group if name == :everyone + group.name = I18n.t("groups.default_names.#{name}") # don't allow shoddy localization to break this diff --git a/app/models/invite.rb b/app/models/invite.rb index 9a5acc73b0b..e64ecaaffba 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -59,6 +59,7 @@ end # created_at :datetime not null # updated_at :datetime not null # deleted_at :datetime +# deleted_by_id :integer # # Indexes # diff --git a/app/models/post.rb b/app/models/post.rb index c2d4901f121..ad33a08609c 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -412,6 +412,8 @@ end # percent_rank :float default(1.0) # notify_user_count :integer default(0), not null # like_score :integer default(0), not null +# deleted_by_id :integer +# nuked_user :boolean default(FALSE) # # Indexes # diff --git a/app/models/site.rb b/app/models/site.rb index e94871d2404..60acda463d3 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -27,7 +27,7 @@ class Site def categories Category - .secured(@guardian) + .topic_create_allowed(@guardian) .latest .includes(:topic_only_relative_url) end diff --git a/app/models/topic.rb b/app/models/topic.rb index f2702df10bc..d57e539de71 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -103,9 +103,9 @@ class Topic < ActiveRecord::Base # Query conditions condition = if ids.present? - ["NOT c.secure or c.id in (:cats)", cats: ids] + ["NOT c.read_restricted or c.id in (:cats)", cats: ids] else - ["NOT c.secure"] + ["NOT c.read_restricted"] end where("category_id IS NULL OR category_id IN ( @@ -629,8 +629,8 @@ class Topic < ActiveRecord::Base self end - def secure_category? - category && category.secure + def read_restricted_category? + category && category.read_restricted end private @@ -692,6 +692,7 @@ end # auto_close_at :datetime # auto_close_user_id :integer # auto_close_started_at :datetime +# deleted_by_id :integer # # Indexes # diff --git a/app/models/topic_tracking_state.rb b/app/models/topic_tracking_state.rb index b6f816a65e8..507f3bc2553 100644 --- a/app/models/topic_tracking_state.rb +++ b/app/models/topic_tracking_state.rb @@ -113,11 +113,11 @@ class TopicTrackingState ((#{unread}) OR (#{new})) AND (topics.visible OR u.admin OR u.moderator) AND topics.deleted_at IS NULL AND - ( category_id IS NULL OR NOT c.secure OR category_id IN ( + ( category_id IS NULL OR NOT c.read_restricted OR category_id IN ( SELECT c2.id FROM categories c2 JOIN category_groups cg ON cg.category_id = c2.id JOIN group_users gu ON gu.user_id = u.id AND cg.group_id = gu.group_id - WHERE c2.secure ) + WHERE c2.read_restricted ) ) SQL diff --git a/app/models/user.rb b/app/models/user.rb index 77eddf65609..59e84962f1b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -486,10 +486,14 @@ class User < ActiveRecord::Base end def secure_category_ids - cats = self.staff? ? Category.select(:id).where(secure: true) : secure_categories.select('categories.id') + cats = self.staff? ? Category.select(:id).where(read_restricted: true) : secure_categories.select('categories.id') cats.map { |c| c.id }.sort end + def topic_create_allowed_category_ids + Category.topic_create_allowed(self.id).select(:id) + end + # Flag all posts from a user as spam def flag_linked_posts_as_spam admin = Discourse.system_user @@ -660,6 +664,7 @@ end # topic_reply_count :integer default(0), not null # blocked :boolean default(FALSE) # dynamic_favicon :boolean default(FALSE), not null +# title :string(255) # # Indexes # diff --git a/app/models/user_action.rb b/app/models/user_action.rb index 595ed3e4bc1..6f7f2536420 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -179,7 +179,7 @@ ORDER BY p.created_at desc # move into Topic perhaps group_ids = nil - if topic && topic.category && topic.category.secure + if topic && topic.category && topic.category.read_restricted group_ids = topic.category.groups.pluck("groups.id") end @@ -232,11 +232,11 @@ ORDER BY p.created_at desc unless guardian.is_staff? allowed = guardian.secure_category_ids if allowed.present? - builder.where("( c.secure IS NULL OR - c.secure = 'f' OR - (c.secure = 't' and c.id in (:cats)) )", cats: guardian.secure_category_ids ) + builder.where("( c.read_restricted IS NULL OR + NOT c.read_restricted OR + (c.read_restricted and c.id in (:cats)) )", cats: guardian.secure_category_ids ) else - builder.where("(c.secure IS NULL OR c.secure = 'f')") + builder.where("(c.read_restricted IS NULL OR NOT c.read_restricted)") end end end diff --git a/app/serializers/basic_category_serializer.rb b/app/serializers/basic_category_serializer.rb index 1ef1907448a..18e166c6899 100644 --- a/app/serializers/basic_category_serializer.rb +++ b/app/serializers/basic_category_serializer.rb @@ -9,6 +9,6 @@ class BasicCategorySerializer < ApplicationSerializer :description, :topic_url, :hotness, - :secure + :read_restricted end diff --git a/app/serializers/category_serializer.rb b/app/serializers/category_serializer.rb index 95c851c28b7..03a484e1278 100644 --- a/app/serializers/category_serializer.rb +++ b/app/serializers/category_serializer.rb @@ -1,13 +1,24 @@ class CategorySerializer < BasicCategorySerializer - attributes :secure, :groups, :available_groups, :auto_close_days + attributes :read_restricted, :groups, :available_groups, :auto_close_days, :group_permissions - def groups - @groups ||= object.groups.order("name").all.map(&:name) + def group_permissions + @group_permissions ||= begin + perms = object.category_groups.joins(:group).order("groups.name").map do |cg| + { + permission_type: cg.permission_type, + group_name: cg.group.name + } + end + if perms.length == 0 && !object.read_restricted + perms << {permission_type: CategoryGroup.permission_types[:full], group_name: :everyone} + end + perms + end end def available_groups - Group.order("name").map(&:name) - groups + Group.order("name").pluck(:name) - group_permissions.map{|g| g[:group_name]} end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 58faf0b70ea..8ad56b92b1e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -999,6 +999,11 @@ en: browser_update: 'Unfortunately, your browser is too old to work on this Discourse forum. Please upgrade your browser.' + permission_types: + full: "Create Topics, Create Posts and Read" + create_post: "Create Posts and Read" + readonly: "Read Only" + # This section is exported to the javascript for i18n in the admin section admin_js: type_to_filter: "type to filter..." @@ -1245,3 +1250,4 @@ en: title: 'Settings' reset: 'reset to default' none: 'none' + diff --git a/db/migrate/20130712041133_add_permission_type_to_category_groups.rb b/db/migrate/20130712041133_add_permission_type_to_category_groups.rb new file mode 100644 index 00000000000..ae227562e3c --- /dev/null +++ b/db/migrate/20130712041133_add_permission_type_to_category_groups.rb @@ -0,0 +1,9 @@ +class AddPermissionTypeToCategoryGroups < ActiveRecord::Migration + def change + # 1 is full permissions + add_column :category_groups, :permission_type, :integer, default: 1 + + # secure is ambiguous after this change, it should be read_restricted + rename_column :categories, :secure, :read_restricted + end +end diff --git a/lib/guardian.rb b/lib/guardian.rb index f4949aaf911..a58e791b8b0 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -7,6 +7,7 @@ class Guardian def staff?; false; end def approved?; false; end def secure_category_ids; []; end + def topic_create_allowed_category_ids; []; end def has_trust_level?(level); false; end end @@ -328,7 +329,7 @@ class Guardian topic.deleted_at.nil? && # not secure, or I can see it - (not(topic.secure_category?) || can_see_category?(topic.category)) && + (not(topic.read_restricted_category?) || can_see_category?(topic.category)) && # not private, or I am allowed (or an admin) (not(topic.private_message?) || authenticated? && (topic.all_allowed_users.where(id: @user.id).exists? || is_admin?)) @@ -340,7 +341,7 @@ class Guardian end def can_see_category?(category) - not(category.secure) || secure_category_ids.include?(category.id) + not(category.read_restricted) || secure_category_ids.include?(category.id) end def can_vote?(post, opts={}) @@ -378,6 +379,10 @@ class Guardian @secure_category_ids ||= @user.secure_category_ids end + def topic_create_allowed_category_ids + @topic_create_allowed_category_ids ||= @user.topic_create_allowed_category_ids + end + private def is_my_own?(obj) diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 9bbbd50f77d..18fffcc2613 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -116,7 +116,7 @@ class PostCreator protected def secure_group_ids(topic) - @secure_group_ids ||= if topic.category && topic.category.secure? + @secure_group_ids ||= if topic.category && topic.category.read_restricted? topic.category.secure_group_ids end end diff --git a/lib/search.rb b/lib/search.rb index 033a0acbb0e..d68b06c70a8 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -160,9 +160,9 @@ class Search .order("topics.bumped_at DESC") if secure_category_ids.present? - posts = posts.where("(categories.id IS NULL) OR (NOT categories.secure) OR (categories.id IN (?))", secure_category_ids) + posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted) OR (categories.id IN (?))", secure_category_ids) else - posts = posts.where("(categories.id IS NULL) OR (NOT categories.secure)") + posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted)") end posts.limit(limit) end diff --git a/lib/sql_builder.rb b/lib/sql_builder.rb index c06539dcd00..9858144eaa7 100644 --- a/lib/sql_builder.rb +++ b/lib/sql_builder.rb @@ -18,9 +18,9 @@ class SqlBuilder def secure_category(secure_category_ids, category_alias = 'c') if secure_category_ids.present? - where("NOT COALESCE(" << category_alias << ".secure, false) OR " << category_alias << ".id IN (:secure_category_ids)", secure_category_ids: secure_category_ids) + where("NOT COALESCE(" << category_alias << ".read_restricted, false) OR " << category_alias << ".id IN (:secure_category_ids)", secure_category_ids: secure_category_ids) else - where("NOT COALESCE(" << category_alias << ".secure, false)") + where("NOT COALESCE(" << category_alias << ".read_restricted, false)") end self end diff --git a/lib/topic_query.rb b/lib/topic_query.rb index eaaedd72956..dc01917a035 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -239,9 +239,9 @@ class TopicQuery unless @user && @user.moderator? category_ids = @user.secure_category_ids if @user if category_ids.present? - result = result.where('categories.secure IS NULL OR categories.secure = ? OR categories.id IN (?)', false, category_ids) + result = result.where('categories.read_restricted IS NULL OR categories.read_restricted = ? OR categories.id IN (?)', false, category_ids) else - result = result.where('categories.secure IS NULL OR categories.secure = ?', false) + result = result.where('categories.read_restricted IS NULL OR categories.read_restricted = ?', false) end end diff --git a/spec/components/category_list_spec.rb b/spec/components/category_list_spec.rb index a6eb8515eac..537788a3dd6 100644 --- a/spec/components/category_list_spec.rb +++ b/spec/components/category_list_spec.rb @@ -41,8 +41,7 @@ describe CategoryList do cat = Fabricate(:category) topic = Fabricate(:topic, category: cat) - cat.deny(:all) - cat.allow(Group[:admins]) + cat.set_permissions(:admins => :full) cat.save CategoryList.new(Guardian.new admin).categories.count.should == 1 diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 4278df2b897..63f707fb23d 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -215,8 +215,9 @@ describe Guardian do it 'correctly handles groups' do group = Fabricate(:group) - category = Fabricate(:category, secure: true) - category.allow(group) + category = Fabricate(:category, read_restricted: true) + category.set_permissions(group => :full) + category.save topic = Fabricate(:topic, category: category) diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 3289db853b6..98f29bb0180 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -62,8 +62,7 @@ describe PostCreator do admin = Fabricate(:admin) cat = Fabricate(:category) - cat.deny(:all) - cat.allow(Group[:admins]) + cat.set_permissions(:admins => :full) cat.save created_post = nil diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 82868b408aa..0d5a204f1c0 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -171,8 +171,7 @@ describe Search do topic.category_id = category.id topic.save - category.deny(:all) - category.allow(Group[:staff]) + category.set_permissions(:staff => :full) category.save result(nil).should_not be_present @@ -211,7 +210,7 @@ describe Search do r[:title].should == category.name r[:url].should == "/category/#{category.slug}" - category.deny(:all) + category.set_permissions({}) category.save result.should_not be_present diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb index 9433e4d74b1..7ae551735f4 100644 --- a/spec/components/topic_query_spec.rb +++ b/spec/components/topic_query_spec.rb @@ -14,9 +14,8 @@ describe TopicQuery do context 'secure category' do it "filters categories out correctly" do category = Fabricate(:category) - category.deny(:all) group = Fabricate(:group) - category.allow(group) + category.set_permissions(group => :full) category.save topic = Fabricate(:topic, category: category) diff --git a/spec/controllers/categories_controller_spec.rb b/spec/controllers/categories_controller_spec.rb index fba156c5223..b71fecdd2ab 100644 --- a/spec/controllers/categories_controller_spec.rb +++ b/spec/controllers/categories_controller_spec.rb @@ -134,11 +134,11 @@ describe CategoriesController do describe 'success' do before do - # might as well test this as well - @category.allow(Group[:admins]) + # might as well test this while at it + @category.set_permissions(:admins => :full) @category.save - xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff', group_names: Group[:staff].name, secure: 'true' + xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff', group_names: Group[:staff].name, read_restricted: 'true' @category.reload end @@ -146,7 +146,7 @@ describe CategoriesController do @category.name.should == 'science' @category.color.should == '000' @category.text_color.should == '0ff' - @category.secure?.should be_true + @category.read_restricted?.should be_true @category.groups.count.should == 1 end end diff --git a/spec/models/category_featured_topic_spec.rb b/spec/models/category_featured_topic_spec.rb index e4d7d1045c2..bde98f0ee8b 100644 --- a/spec/models/category_featured_topic_spec.rb +++ b/spec/models/category_featured_topic_spec.rb @@ -15,8 +15,7 @@ describe CategoryFeaturedTopic do # so much dancing, I am thinking fixures make sense here. user.change_trust_level!(:basic) - category.deny(:all) - category.allow(Group[:trust_level_1]) + category.set_permissions(:trust_level_1 => :full) category.save uncategorized_post = PostCreator.create(user, raw: "this is my new post 123 post", title: "hello world") diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 0c3f8d12e6b..f2d69c37536 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -18,6 +18,57 @@ describe Category do it { should have_many :category_featured_topics } it { should have_many :featured_topics } + + describe "resolve_permissions" do + it "can determine read_restricted" do + read_restricted, resolved = Category.resolve_permissions(:everyone => :full) + + read_restricted.should be_false + resolved.should == [] + end + end + + describe "topic_create_allowed and post_create_allowed" do + it "works" do + default_category = Fabricate(:category) + full_category = Fabricate(:category) + can_post_category = Fabricate(:category) + can_read_category = Fabricate(:category) + + + user = Fabricate(:user) + group = Fabricate(:group) + group.add(user) + group.save + + admin = Fabricate(:admin) + + full_category.set_permissions(group => :full) + full_category.save + + can_post_category.set_permissions(group => :create_post) + can_post_category.save + + can_read_category.set_permissions(group => :readonly) + can_read_category.save + + guardian = Guardian.new(admin) + Category.topic_create_allowed(guardian).count.should == 4 + Category.post_create_allowed(guardian).count.should == 4 + Category.secured(guardian).count.should == 4 + + guardian = Guardian.new(user) + Category.secured(guardian).count.should == 4 + Category.post_create_allowed(guardian).count.should == 3 + Category.topic_create_allowed(guardian).count.should == 2 # explicitly allowed once, default allowed once + end + + end + + describe "post_create_allowed" do + + end + describe "security" do let(:category) { Fabricate(:category) } let(:category_2) { Fabricate(:category) } @@ -25,20 +76,20 @@ describe Category do let(:group) { Fabricate(:group) } it "secures categories correctly" do - category.secure?.should be_false + category.read_restricted?.should be_false - category.deny(:all) - category.secure?.should be_true + category.set_permissions({}) + category.read_restricted?.should be_true - category.allow(:all) - category.secure?.should be_false + category.set_permissions(:everyone => :full) + category.read_restricted?.should be_false user.secure_categories.should be_empty group.add(user) group.save - category.allow(group) + category.set_permissions(group.id => :full) category.save user.reload @@ -47,13 +98,13 @@ describe Category do it "lists all secured categories correctly" do group.add(user) - category.allow(group) + category.set_permissions(group.id => :full) + category.save + category_2.set_permissions(group.id => :full) + category_2.save - Category.secured.should == [category] - - category_2.allow(group) - - Category.secured.should =~ [category, category_2] + Category.secured.should =~ [] + Category.secured(Guardian.new(user)).should =~ [category, category_2] end end diff --git a/spec/models/site_spec.rb b/spec/models/site_spec.rb new file mode 100644 index 00000000000..a4ffd55a3fb --- /dev/null +++ b/spec/models/site_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' +require_dependency 'site' + +describe Site do + it "omits categories users can not write to from the category list" do + category = Fabricate(:category) + user = Fabricate(:user) + + Site.new(Guardian.new(user)).categories.count.should == 1 + + category.set_permissions(:everyone => :create_post) + category.save + + Site.new(Guardian.new(user)).categories.count.should == 0 + end +end diff --git a/spec/models/topic_link_spec.rb b/spec/models/topic_link_spec.rb index 29d7422a29a..d2d960afb4c 100644 --- a/spec/models/topic_link_spec.rb +++ b/spec/models/topic_link_spec.rb @@ -258,8 +258,7 @@ describe TopicLink do TopicLink.topic_summary(Guardian.new, post.topic_id).count.should == 1 TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 1 - category.deny(:all) - category.allow(Group[:staff]) + category.set_permissions(:staff => :full) category.save admin = Fabricate(:admin) diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index fef82230605..9b13a30c3ad 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -192,7 +192,7 @@ describe Topic do context "secure categories" do let(:user) { Fabricate(:user) } - let(:category) { Fabricate(:category, secure: true) } + let(:category) { Fabricate(:category, read_restricted: true) } before do topic.category = category @@ -1263,7 +1263,7 @@ describe Topic do describe 'secured' do it 'can remove secure groups' do - category = Fabricate(:category, secure: true) + category = Fabricate(:category, read_restricted: true) topic = Fabricate(:topic, category: category) Topic.secured(Guardian.new(nil)).count.should == 0 @@ -1280,17 +1280,17 @@ describe Topic do let(:category){ Category.new } it "is true if the category is secure" do - category.stubs(:secure).returns(true) - Topic.new(:category => category).should be_secure_category + category.stubs(:read_restricted).returns(true) + Topic.new(:category => category).should be_read_restricted_category end it "is false if the category is not secure" do - category.stubs(:secure).returns(false) - Topic.new(:category => category).should_not be_secure_category + category.stubs(:read_restricted).returns(false) + Topic.new(:category => category).should_not be_read_restricted_category end it "is false if there is no category" do - Topic.new(:category => nil).should_not be_secure_category + Topic.new(:category => nil).should_not be_read_restricted_category end end diff --git a/spec/models/topic_tracking_state_spec.rb b/spec/models/topic_tracking_state_spec.rb index af9348736f7..aabfda32929 100644 --- a/spec/models/topic_tracking_state_spec.rb +++ b/spec/models/topic_tracking_state_spec.rb @@ -51,7 +51,7 @@ describe TopicTrackingState do row.user_id.should == post.user_id # when we have no permission to see a category, don't show its stats - category = Fabricate(:category, secure: true) + category = Fabricate(:category, read_restricted: true) post.topic.category_id = category.id post.topic.save diff --git a/spec/models/user_action_spec.rb b/spec/models/user_action_spec.rb index 8a1e5b7dcf9..2f5bdea4dba 100644 --- a/spec/models/user_action_spec.rb +++ b/spec/models/user_action_spec.rb @@ -68,7 +68,7 @@ describe UserAction do # groups - category = Fabricate(:category, secure: true) + category = Fabricate(:category, read_restricted: true) public_topic.recover! public_topic.category = category @@ -82,7 +82,7 @@ describe UserAction do group.add(u) group.save - category.allow(group) + category.set_permissions(group => :full) category.save stats_for_user(u).should == [UserAction::NEW_TOPIC]