added bans

This commit is contained in:
albert
2010-02-19 17:30:11 -05:00
parent 09cca6f631
commit 703eb6a1b6
11 changed files with 478 additions and 11 deletions

53
app/models/ban.rb Normal file
View File

@@ -0,0 +1,53 @@
class Ban < ActiveRecord::Base
after_create :update_feedback
belongs_to :user
belongs_to :banner, :class_name => "User"
attr_accessible :reason, :duration, :user_id
validate :user_is_inferior
def self.is_user_banned?(user)
exists?(["user_id = ? AND expires_at > ?", user.id, Time.now])
end
def self.is_ip_banned?(ip_addr)
exists?(["ip_addr = ? AND expires_at > ?", ip_addr, Time.now])
end
def user_is_inferior
if user
if user.is_admin?
errors[:base] << "You can never ban an admin."
false
elsif user.is_moderator? && banner.is_admin?
true
elsif user.is_moderator?
errors[:base] << "Only admins can ban moderators."
false
elsif banner.is_admin? || banner.is_moderator?
true
else
errors[:base] << "No one else can ban."
false
end
end
end
def update_feedback
if user
feedback = user.feedback.build
feedback.is_positive = false
feedback.body = "Banned: #{reason}"
feedback.creator_id = banner_id
feedback.save
end
end
def duration=(dur)
self.expires_at = dur.to_i.days.from_now
@duration = dur
end
def duration
@duration
end
end

View File

@@ -12,6 +12,8 @@ class User < ActiveRecord::Base
validates_confirmation_of :password
before_save :encrypt_password
after_save :update_cache
before_create :normalize_level
has_many :feedback, :class_name => "UserFeedback", :dependent => :destroy
scope :named, lambda {|name| where(["lower(name) = ?", name])}
module NameMethods
@@ -92,10 +94,29 @@ class User < ActiveRecord::Base
end
end
module LevelMethods
def normalize_level
if is_admin?
self.is_moderator = true
self.is_janitor = true
self.is_contributor = true
self.is_privileged = true
elsif is_moderator?
self.is_janitor = true
self.is_privileged = true
elsif is_janitor?
self.is_privileged = true
elsif is_contributor?
self.is_privileged = true
end
end
end
include NameMethods
include PasswordMethods
extend AuthenticationMethods
include FavoriteMethods
include LevelMethods
def can_update?(object, foreign_key = :user_id)
is_moderator? || is_admin? || object.__send__(foreign_key) == id

View File

@@ -0,0 +1,5 @@
class UserFeedback < ActiveRecord::Base
set_table_name "user_feedback"
belongs_to :user
attr_accessible :body, :user_id, :is_positive
end

View File

@@ -1,10 +1,3 @@
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format
# (all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable %w( feedback )
end

View File

@@ -250,6 +250,41 @@ CREATE SEQUENCE artists_id_seq
ALTER SEQUENCE artists_id_seq OWNED BY artists.id;
--
-- Name: bans; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE bans (
id integer NOT NULL,
user_id integer,
ip_addr inet,
reason text NOT NULL,
banner_id integer NOT NULL,
expires_at timestamp without time zone NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
--
-- Name: bans_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE bans_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
--
-- Name: bans_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE bans_id_seq OWNED BY bans.id;
--
-- Name: comment_votes; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
@@ -1000,6 +1035,40 @@ CREATE SEQUENCE uploads_id_seq
ALTER SEQUENCE uploads_id_seq OWNED BY uploads.id;
--
-- Name: user_feedback; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE user_feedback (
id integer NOT NULL,
user_id integer NOT NULL,
creator_id integer NOT NULL,
is_positive boolean NOT NULL,
body text NOT NULL,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
--
-- Name: user_feedback_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE user_feedback_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
--
-- Name: user_feedback_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE user_feedback_id_seq OWNED BY user_feedback.id;
--
-- Name: users; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
@@ -1155,6 +1224,13 @@ ALTER TABLE artist_versions ALTER COLUMN id SET DEFAULT nextval('artist_versions
ALTER TABLE artists ALTER COLUMN id SET DEFAULT nextval('artists_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE bans ALTER COLUMN id SET DEFAULT nextval('bans_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1309,6 +1385,13 @@ ALTER TABLE unapprovals ALTER COLUMN id SET DEFAULT nextval('unapprovals_id_seq'
ALTER TABLE uploads ALTER COLUMN id SET DEFAULT nextval('uploads_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE user_feedback ALTER COLUMN id SET DEFAULT nextval('user_feedback_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1370,6 +1453,14 @@ ALTER TABLE ONLY artists
ADD CONSTRAINT artists_pkey PRIMARY KEY (id);
--
-- Name: bans_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY bans
ADD CONSTRAINT bans_pkey PRIMARY KEY (id);
--
-- Name: comment_votes_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
@@ -1546,6 +1637,14 @@ ALTER TABLE ONLY uploads
ADD CONSTRAINT uploads_pkey PRIMARY KEY (id);
--
-- Name: user_feedback_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY user_feedback
ADD CONSTRAINT user_feedback_pkey PRIMARY KEY (id);
--
-- Name: users_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
@@ -1647,6 +1746,27 @@ CREATE UNIQUE INDEX index_artists_on_name ON artists USING btree (name);
CREATE INDEX index_artists_on_other_names_index ON artists USING gin (other_names_index);
--
-- Name: index_bans_on_expires_at; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_bans_on_expires_at ON bans USING btree (expires_at);
--
-- Name: index_bans_on_ip_addr; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_bans_on_ip_addr ON bans USING btree (ip_addr);
--
-- Name: index_bans_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_bans_on_user_id ON bans USING btree (user_id);
--
-- Name: index_comment_votes_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
@@ -1962,6 +2082,13 @@ CREATE UNIQUE INDEX index_tags_on_name ON tags USING btree (name);
CREATE INDEX index_unapprovals_on_post_id ON unapprovals USING btree (post_id);
--
-- Name: index_user_feedback_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_user_feedback_on_user_id ON user_feedback USING btree (user_id);
--
-- Name: index_users_on_email; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
@@ -2086,4 +2213,8 @@ INSERT INTO schema_migrations (version) VALUES ('20100215223541');
INSERT INTO schema_migrations (version) VALUES ('20100215224629');
INSERT INTO schema_migrations (version) VALUES ('20100215224635');
INSERT INTO schema_migrations (version) VALUES ('20100215224635');
INSERT INTO schema_migrations (version) VALUES ('20100215225710');
INSERT INTO schema_migrations (version) VALUES ('20100215230642');

View File

@@ -0,0 +1,20 @@
class CreateBans < ActiveRecord::Migration
def self.up
create_table :bans do |t|
t.column :user_id, :integer
t.column :ip_addr, "inet"
t.column :reason, :text, :null => false
t.column :banner_id, :integer, :null => false
t.column :expires_at, :datetime, :null => false
t.timestamps
end
add_index :bans, :user_id
add_index :bans, :ip_addr
add_index :bans, :expires_at
end
def self.down
drop_table :bans
end
end

View File

@@ -0,0 +1,17 @@
class CreateUserFeedback < ActiveRecord::Migration
def self.up
create_table :user_feedback do |t|
t.column :user_id, :integer, :null => false
t.column :creator_id, :integer, :null => false
t.column :is_positive, :boolean, :null => false
t.column :body, :text, :null => false
t.timestamps
end
add_index :user_feedback, :user_id
end
def self.down
drop_table :user_feedback
end
end

6
test/factories/ban.rb Normal file
View File

@@ -0,0 +1,6 @@
Factory.define(:ban) do |f|
f.user {|x| x.association(:user)}
f.banner {|x| x.association(:admin_user)}
f.reason {Faker::Lorem.words}
f.duration 60
end

170
test/unit/ban_test.rb Normal file
View File

@@ -0,0 +1,170 @@
require File.dirname(__FILE__) + '/../test_helper'
class BanTest < ActiveSupport::TestCase
context "A ban" do
context "created by an admin" do
setup do
@banner = Factory.create(:admin_user)
end
teardown do
@banner = nil
end
should "not be valid against another admin" do
user = Factory.create(:admin_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
end
should "be valid against anyone who is not an admin" do
user = Factory.create(:moderator_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:janitor_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:contributor_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:privileged_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
end
end
context "created by a moderator" do
setup do
@banner = Factory.create(:moderator_user)
end
teardown do
@banner = nil
end
should "not be valid against an admin or moderator" do
user = Factory.create(:admin_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
user = Factory.create(:moderator_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
end
should "be valid against anyone who is not an admin or a moderator" do
user = Factory.create(:janitor_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:contributor_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:privileged_user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
user = Factory.create(:user)
ban = Factory.create(:ban, :user => user, :banner => @banner)
assert(ban.errors.empty?)
end
end
context "created by a janitor" do
setup do
@banner = Factory.create(:janitor_user)
end
teardown do
@banner = nil
end
should "always be invalid" do
user = Factory.create(:admin_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
user = Factory.create(:moderator_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
user = Factory.create(:janitor_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
user = Factory.create(:contributor_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
user = Factory.create(:privileged_user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
user = Factory.create(:user)
ban = Factory.build(:ban, :user => user, :banner => @banner)
ban.save
assert(ban.errors.any?)
end
end
should "initialize the expiration date" do
user = Factory.create(:user)
admin = Factory.create(:admin_user)
ban = Factory.create(:ban, :user => user, :banner => admin)
assert_not_nil(ban.expires_at)
end
should "update the user's feedback" do
user = Factory.create(:user)
admin = Factory.create(:admin_user)
assert(user.feedback.empty?)
ban = Factory.create(:ban, :user => user, :banner => admin)
assert(!user.feedback.empty?)
assert(!user.feedback.last.is_positive?)
end
end
context "Searching for a ban" do
context "by user id" do
should "not return expired bans" do
admin = Factory.create(:admin_user)
user = Factory.create(:user)
ban = Factory.create(:ban, :user => user, :banner => admin, :duration => -1)
assert(!Ban.is_user_banned?(user))
user = Factory.create(:user)
ban = Factory.create(:ban, :user => user, :banner => admin, :duration => 1)
assert(Ban.is_user_banned?(user))
end
end
context "by ip address" do
should "not return expired bans" do
admin = Factory.create(:admin_user)
ban = Factory.create(:ban, :ip_addr => "1.2.3.4", :banner => admin, :duration => -1)
assert(!Ban.is_ip_banned?("1.2.3.4"))
ban = Factory.create(:ban, :ip_addr => "5.6.7.8", :banner => admin, :duration => 1)
assert(Ban.is_ip_banned?("5.6.7.8"))
end
end
end
end

View File

@@ -0,0 +1,8 @@
require 'test_helper'
class UserFeedbackTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end

View File

@@ -14,6 +14,49 @@ class UserTest < ActiveSupport::TestCase
assert(!User.authenticate_hash(@user.name, "xxxx"), "Authentication should not have succeeded")
end
should "normalize its level" do
user = Factory.create(:user, :is_admin => true)
assert(user.is_moderator?)
assert(user.is_janitor?)
assert(user.is_contributor?)
assert(user.is_privileged?)
user = Factory.create(:user, :is_moderator => true)
assert(!user.is_admin?)
assert(user.is_moderator?)
assert(user.is_janitor?)
assert(!user.is_contributor?)
assert(user.is_privileged?)
user = Factory.create(:user, :is_janitor => true)
assert(!user.is_admin?)
assert(!user.is_moderator?)
assert(user.is_janitor?)
assert(!user.is_contributor?)
assert(user.is_privileged?)
user = Factory.create(:user, :is_contributor => true)
assert(!user.is_admin?)
assert(!user.is_moderator?)
assert(!user.is_janitor?)
assert(user.is_contributor?)
assert(user.is_privileged?)
user = Factory.create(:user, :is_privileged => true)
assert(!user.is_admin?)
assert(!user.is_moderator?)
assert(!user.is_janitor?)
assert(!user.is_contributor?)
assert(user.is_privileged?)
user = Factory.create(:user)
assert(!user.is_admin?)
assert(!user.is_moderator?)
assert(!user.is_janitor?)
assert(!user.is_contributor?)
assert(!user.is_privileged?)
end
context "name" do
should "be #{Danbooru.config.default_guest_name} given an invalid user id" do
assert_equal(Danbooru.config.default_guest_name, User.find_name(-1))