Merge branch 'atomaka/feature/authorization' into 'master'

Add authorization via Pundit

Also includes some a refactor to null object pattern for guest users.

See merge request !24
This commit is contained in:
Andrew Tomaka 2015-12-23 20:47:12 +00:00
commit 1f9788309b
31 changed files with 678 additions and 231 deletions

View file

@ -4,4 +4,5 @@ test:
- apt-get install -y nodejs libqtwebkit-dev qt4-qmake sqlite3 libsqlite3-dev - apt-get install -y nodejs libqtwebkit-dev qt4-qmake sqlite3 libsqlite3-dev
- bundle install --path /cache - bundle install --path /cache
- bundle exec rake db:create RAILS_ENV=test - bundle exec rake db:create RAILS_ENV=test
- bundle exec rake db:test:prepare
- bundle exec rspec - bundle exec rspec

10
Gemfile
View file

@ -1,13 +1,5 @@
source 'https://rubygems.org' source 'https://rubygems.org'
def darwin_only(require_as)
RbConfig::CONFIG['host_os'] =~ /darwin/ && require_as
end
def linux_only(require_as)
RbConfig::CONFIG['host_os'] =~ /linux/ && require_as
end
gem 'rails', '4.2.3' gem 'rails', '4.2.3'
gem 'sqlite3' gem 'sqlite3'
@ -25,6 +17,8 @@ gem 'ancestry'
gem 'bcrypt' gem 'bcrypt'
gem 'pundit'
gem 'sdoc', '~> 0.4.0', group: :doc gem 'sdoc', '~> 0.4.0', group: :doc
group :development do group :development do

View file

@ -212,6 +212,8 @@ GEM
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
slop (~> 3.4) slop (~> 3.4)
pundit (1.0.1)
activesupport (>= 3.0.0)
quiet_assets (1.1.0) quiet_assets (1.1.0)
railties (>= 3.1, < 5.0) railties (>= 3.1, < 5.0)
rack (1.6.4) rack (1.6.4)
@ -387,6 +389,7 @@ DEPENDENCIES
launchy launchy
metric_fu metric_fu
pry pry
pundit
quiet_assets quiet_assets
rails (= 4.2.3) rails (= 4.2.3)
rspec-rails rspec-rails
@ -403,4 +406,4 @@ DEPENDENCIES
uglifier (>= 1.3.0) uglifier (>= 1.3.0)
BUNDLED WITH BUNDLED WITH
1.10.5 1.10.6

View file

@ -6,10 +6,15 @@ class ApplicationController < ActionController::Base
helper_method :current_session helper_method :current_session
helper_method :logged_in? helper_method :logged_in?
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private private
def current_user def current_user
@current_user ||= User.find(current_session[:user_id]) if current_session @current_user ||= User.find(current_session[:user_id]) if current_session
@current_user ||= GuestUser.new
end end
def current_session def current_session
@ -17,6 +22,11 @@ class ApplicationController < ActionController::Base
end end
def logged_in? def logged_in?
!!current_user current_user.registered?
end
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to(request.referrer || root_path)
end end
end end

View file

@ -3,22 +3,29 @@ class CommentsController < ApplicationController
before_filter :set_comment, only: [:show, :edit, :update, :destroy] before_filter :set_comment, only: [:show, :edit, :update, :destroy]
before_filter :set_post before_filter :set_post
before_filter :set_subcreddit before_filter :set_subcreddit
after_action :verify_authorized
def show def show
@comments = @comment @comments = @comment
.subtree .subtree
.includes(:post, :user) .includes(:post, :user)
.arrange(order: :created_at) .arrange(order: :created_at)
authorize @comment
end end
def new def new
@comment = Comment.new(params[:parent_id]) @comment = Comment.new(params[:parent_id])
authorize @comment
end end
def create def create
@comment = @post.comments.build comment_params @comment = @post.comments.build comment_params
@comment.user = current_user @comment.user = current_user
authorize @comment
if @comment.save if @comment.save
flash[:notice] = 'Comment saved' flash[:notice] = 'Comment saved'
else else
@ -29,9 +36,12 @@ class CommentsController < ApplicationController
end end
def edit def edit
authorize @comment
end end
def update def update
authorize @comment
if @comment.update comment_params if @comment.update comment_params
redirect_to subcreddit_post_path(@subcreddit, @post), redirect_to subcreddit_post_path(@subcreddit, @post),
notice: 'Comment updated' notice: 'Comment updated'
@ -41,6 +51,8 @@ class CommentsController < ApplicationController
end end
def destroy def destroy
authorize @comment
@comment.destroy @comment.destroy
redirect_to subcreddit_post_path(@subcreddit, @post), redirect_to subcreddit_post_path(@subcreddit, @post),
notice: 'Comment deleted' notice: 'Comment deleted'

View file

@ -2,23 +2,32 @@
class PostsController < ApplicationController class PostsController < ApplicationController
before_filter :set_post, except: [:index, :new, :create] before_filter :set_post, except: [:index, :new, :create]
before_filter :set_subcreddit before_filter :set_subcreddit
after_action :verify_authorized
def index def index
@posts = Post.includes(:subcreddit, :user).all @posts = Post.includes(:subcreddit, :user).all
authorize Post
end end
def show def show
@comments = @post.comments.includes(:user).arrange(order: :created_at) @comments = @post.comments.includes(:user).arrange(order: :created_at)
authorize @post
end end
def new def new
@post = Post.new @post = Post.new
authorize @post
end end
def create def create
@post = @subcreddit.posts.build(post_params) @post = @subcreddit.posts.build(post_params)
@post.user = current_user @post.user = current_user
authorize @post
if @post.save if @post.save
redirect_to subcreddit_post_path(@subcreddit, @post), redirect_to subcreddit_post_path(@subcreddit, @post),
notice: 'Post created' notice: 'Post created'
@ -28,9 +37,12 @@ class PostsController < ApplicationController
end end
def edit def edit
authorize @post
end end
def update def update
authorize @post
if @post.update(post_params) if @post.update(post_params)
redirect_to subcreddit_post_path(@subcreddit, @post), redirect_to subcreddit_post_path(@subcreddit, @post),
notice: 'Post was updated' notice: 'Post was updated'
@ -40,6 +52,8 @@ class PostsController < ApplicationController
end end
def destroy def destroy
authorize @post
@post.destroy @post.destroy
redirect_to subcreddits_path(@subcreddit), notice: 'Post was deleted' redirect_to subcreddits_path(@subcreddit), notice: 'Post was deleted'

View file

@ -1,23 +1,32 @@
# controllers/subcreddits_controller.rb # controllers/subcreddits_controller.rb
class SubcredditsController < ApplicationController class SubcredditsController < ApplicationController
before_filter :set_subcreddit, only: [:show, :edit, :update] before_filter :set_subcreddit, only: [:show, :edit, :update]
after_action :verify_authorized
def index def index
@subcreddits = Subcreddit.all @subcreddits = Subcreddit.all
authorize Subcreddit
end end
def show def show
@posts = @subcreddit.posts @posts = @subcreddit.posts
authorize @subcreddit
end end
def new def new
@subcreddit = Subcreddit.new @subcreddit = Subcreddit.new
authorize @subcreddit
end end
def create def create
@subcreddit = Subcreddit.new(create_subcreddit_params) @subcreddit = Subcreddit.new(create_subcreddit_params)
@subcreddit.owner = current_user @subcreddit.owner = current_user
authorize @subcreddit
if @subcreddit.save if @subcreddit.save
redirect_to @subcreddit, notice: 'Subcreddit was created!' redirect_to @subcreddit, notice: 'Subcreddit was created!'
else else
@ -26,9 +35,12 @@ class SubcredditsController < ApplicationController
end end
def edit def edit
authorize @subcreddit
end end
def update def update
authorize @subcreddit
if @subcreddit.update(update_subcreddit_params) if @subcreddit.update(update_subcreddit_params)
redirect_to @subcreddit, notice: 'Subcreddit was updated!' redirect_to @subcreddit, notice: 'Subcreddit was updated!'
else else

6
app/models/guest_user.rb Normal file
View file

@ -0,0 +1,6 @@
# models/guest_user.rb
class GuestUser
def registered?
false
end
end

View file

@ -15,6 +15,10 @@ class User < ActiveRecord::Base
validates :username, presence: true, uniqueness: true, sluguuidless: true validates :username, presence: true, uniqueness: true, sluguuidless: true
validates :password, length: { minimum: 8 } validates :password, length: { minimum: 8 }
def registered?
true
end
private private
def downcase_email def downcase_email

View file

@ -0,0 +1,37 @@
# policies/application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
end

View file

@ -0,0 +1,22 @@
# policies/comment_policy.rb
class CommentPolicy < ApplicationPolicy
def index?
true
end
def show?
true
end
def create?
user.registered?
end
def update?
record.user == user
end
def destroy?
record.user == user
end
end

View file

@ -0,0 +1,22 @@
# policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def index?
true
end
def show?
true
end
def create?
user.registered?
end
def update?
record.user == user
end
def destroy?
record.user == user
end
end

View file

@ -0,0 +1,18 @@
# policies/subcreddit_policy.rb
class SubcredditPolicy < ApplicationPolicy
def index?
true
end
def show?
true
end
def create?
user.registered?
end
def update?
record.owner == user
end
end

View file

@ -5,12 +5,14 @@
button.btn.btn-default type="button" button.btn.btn-default type="button"
span.glyphicon.glyphicon-search aria-hidden="true" span.glyphicon.glyphicon-search aria-hidden="true"
- if @subcreddit && @subcreddit.id - if policy(:post).new?
= link_to 'Submit a new link', new_subcreddit_post_path(@subcreddit), class: 'button btn btn-primary btn-block' - if @subcreddit && @subcreddit.id
= link_to 'Submit a new text post', new_subcreddit_post_path(@subcreddit), class: 'button btn btn-primary btn-block' = link_to 'Submit a new link', new_subcreddit_post_path(@subcreddit), class: 'button btn btn-primary btn-block'
= link_to 'Create your own subcreddit', new_subcreddit_path, class: 'button btn btn-primary btn-block' = link_to 'Submit a new text post', new_subcreddit_post_path(@subcreddit), class: 'button btn btn-primary btn-block'
- if policy(:subcreddit).new?
= link_to 'Create your own subcreddit', new_subcreddit_path, class: 'button btn btn-primary btn-block'
- if @subcreddit && @subcreddit.id - if @subcreddit && @subcreddit.id && policy(@subcreddit).edit?
.title Moderation Tools .title Moderation Tools
.box .box
ul ul

View file

@ -3,7 +3,8 @@
.alert.alert-info.in-page .alert.alert-info.in-page
p you are viewing a single comment's thread. p you are viewing a single comment's thread.
p #{link_to 'view the rest of the comments', subcreddit_post_path(@subcreddit, @post)} → p #{link_to 'view the rest of the comments', subcreddit_post_path(@subcreddit, @post)} →
= "Commenting as: #{current_user.username}" - if policy(:comment).new?
== render 'comments/form', subcreddit: @subcreddit, post: @post, comment: @post.comments.build, parent: @comment = "Commenting as: #{current_user.username}"
== render 'comments/form', subcreddit: @subcreddit, post: @post, comment: @post.comments.build, parent: @comment
.comments.contents .comments.contents
== nested_comments(@comments) == nested_comments(@comments)

View file

@ -4,7 +4,8 @@
.title= "all #{@post.comments_count} comments" .title= "all #{@post.comments_count} comments"
- else - else
.title= "no comments (yet)" .title= "no comments (yet)"
= "Commenting as: #{current_user.username}" - if policy(:comment).new?
== render 'comments/form', subcreddit: @subcreddit, post: @post, comment: @post.comments.build, parent: nil = "Commenting as: #{current_user.username}"
== render 'comments/form', subcreddit: @subcreddit, post: @post, comment: @post.comments.build, parent: nil
.comments.contents .comments.contents
== nested_comments(@comments) == nested_comments(@comments)

View file

@ -31,7 +31,7 @@ describe ApplicationController, type: :controller do
context 'when not logged in' do context 'when not logged in' do
it 'should return nil' do it 'should return nil' do
expect(controller.send(:current_user)).to be_nil expect(controller.send(:current_user)).to be_a(GuestUser)
end end
end end
end end

View file

@ -99,103 +99,111 @@ describe CommentsController, type: :controller do
end end
describe '#edit' do describe '#edit' do
let!(:comment) { create(:comment) } context 'when owner' do
before(:each) do let!(:comment) { create(:comment, user: user) }
get :edit, before(:each) do
id: comment, get :edit,
post_id: comment.post, id: comment,
subcreddit_id: comment.post.subcreddit post_id: comment.post,
end subcreddit_id: comment.post.subcreddit
context 'with valid comment' do
it 'should render :edit' do
expect(response).to render_template(:edit)
end end
it 'should assign correct Comment to @comment' do context 'with valid comment' do
expect(assigns(:comment)).to eq(comment) it 'should render :edit' do
expect(response).to render_template(:edit)
end
it 'should assign correct Comment to @comment' do
expect(assigns(:comment)).to eq(comment)
end
end end
end end
end end
context '#update' do context '#update' do
let!(:comment) { create(:comment) }
let(:data) { { content: 'Some edited comment content goes here' } } let(:data) { { content: 'Some edited comment content goes here' } }
context 'with valid data' do context 'with valid data' do
before(:each) do let!(:comment) { create(:comment, user: user) }
put :update,
id: comment, context 'when owner' do
post_id: comment.post, before(:each) do
subcreddit_id: comment.post.subcreddit, put :update,
comment: data id: comment,
post_id: comment.post,
subcreddit_id: comment.post.subcreddit,
comment: data
end
it 'should assign correct Comment to @comment' do
expect(assigns(:comment)).to eq(comment)
end
it 'should update the comment' do
comment.reload
expect(comment.content).to eq(data[:content])
end
it 'should redirect to the post' do
expect(response)
.to redirect_to(subcreddit_post_path(assigns(:post).subcreddit,
assigns(:post)))
end
it 'should display a notice flash message' do
expect(flash[:notice]).to be_present
end
end end
it 'should assign correct Comment to @comment' do context 'with invalid data' do
expect(assigns(:comment)).to eq(comment) before(:each) { data[:content] = '' }
end
it 'should update the comment' do it 'should render :edit' do
comment.reload put :update,
id: comment,
post_id: comment.post,
subcreddit_id: comment.post.subcreddit,
comment: data
expect(comment.content).to eq(data[:content]) expect(response).to render_template(:edit)
end end
it 'should redirect to the post' do
expect(response)
.to redirect_to(subcreddit_post_path(assigns(:post).subcreddit,
assigns(:post)))
end
it 'should display a notice flash message' do
expect(flash[:notice]).to be_present
end
end
context 'with invalid data' do
before(:each) { data[:content] = '' }
it 'should render :edit' do
put :update,
id: comment,
post_id: comment.post,
subcreddit_id: comment.post.subcreddit,
comment: data
expect(response).to render_template(:edit)
end end
end end
end end
context '#destroy' do context '#destroy' do
let!(:comment) { create(:comment) } context 'when owner' do
let!(:comment) { create(:comment, user: user) }
it 'should delete the post' do it 'should delete the post' do
delete :destroy, delete :destroy,
id: comment, id: comment,
post_id: comment.post, post_id: comment.post,
subcreddit_id: comment.post.subcreddit subcreddit_id: comment.post.subcreddit
comment.reload comment.reload
expect(comment.destroyed?).to be(true) expect(comment.destroyed?).to be(true)
end end
it 'should redirect to the post' do it 'should redirect to the post' do
delete :destroy, delete :destroy,
id: comment, id: comment,
post_id: comment.post, post_id: comment.post,
subcreddit_id: comment.post.subcreddit subcreddit_id: comment.post.subcreddit
expect(response).to redirect_to(subcreddit_post_path(assigns(:subcreddit), expect(response)
assigns(:post))) .to redirect_to(subcreddit_post_path(assigns(:subcreddit),
end assigns(:post)))
end
it 'should send a notice flash message' do it 'should send a notice flash message' do
delete :destroy, delete :destroy,
id: comment, id: comment,
post_id: comment.post, post_id: comment.post,
subcreddit_id: comment.post.subcreddit subcreddit_id: comment.post.subcreddit
expect(flash[:notice]).to be_present expect(flash[:notice]).to be_present
end
end end
end end
end end

View file

@ -94,22 +94,23 @@ describe PostsController, type: :controller do
end end
context '#edit' do context '#edit' do
let!(:post) { create(:post) } context 'when owner' do
before(:each) { get :edit, id: post, subcreddit_id: post.subcreddit } let!(:post) { create(:post, user: user) }
before(:each) { get :edit, id: post, subcreddit_id: post.subcreddit }
context 'with valid post' do context 'with valid post' do
it 'should render :edit' do it 'should render :edit' do
expect(response).to render_template(:edit) expect(response).to render_template(:edit)
end end
it 'should assign correct Post to @post' do it 'should assign correct Post to @post' do
expect(assigns(:post)).to eq(post) expect(assigns(:post)).to eq(post)
end
end end
end end
end end
context '#update' do context '#update' do
let!(:post) { create(:post) }
let(:data) do let(:data) do
{ {
title: 'New title', title: 'New title',
@ -117,61 +118,67 @@ describe PostsController, type: :controller do
} }
end end
context 'with valid data' do context 'when owner' do
before(:each) do let!(:post) { create(:post, user: user) }
put :update, id: post, subcreddit_id: post.subcreddit, post: data
context 'with valid data' do
before(:each) do
put :update, id: post, subcreddit_id: post.subcreddit, post: data
end
it 'should assign correct Post to @post' do
expect(assigns(:post)).to eq(post)
end
it 'should update the post' do
post.reload
expect(post.title).to eq(data[:title])
expect(post.content).to eq(data[:content])
end
it 'should redirect to the post' do
expect(response)
.to redirect_to(subcreddit_post_path(assigns(:post).subcreddit,
assigns(:post)))
end
it 'should display a notice flash message' do
expect(flash[:notice]).to be_present
end
end end
it 'should assign correct Post to @post' do context 'with invalid data' do
expect(assigns(:post)).to eq(post) before(:each) { data[:title] = '' }
end
it 'should update the post' do it 'should render :edit' do
post.reload put :update, id: post, subcreddit_id: post.subcreddit, post: data
expect(post.title).to eq(data[:title]) expect(response).to render_template(:edit)
expect(post.content).to eq(data[:content]) end
end
it 'should redirect to the post' do
expect(response)
.to redirect_to(subcreddit_post_path(assigns(:post).subcreddit,
assigns(:post)))
end
it 'should display a notice flash message' do
expect(flash[:notice]).to be_present
end
end
context 'with invalid data' do
before(:each) { data[:title] = '' }
it 'should render :edit' do
put :update, id: post, subcreddit_id: post.subcreddit, post: data
expect(response).to render_template(:edit)
end end
end end
end end
context '#destroy' do context '#destroy' do
let!(:post) { create(:post, subcreddit: subcreddit) } context 'when owner' do
let!(:post) { create(:post, user: user, subcreddit: subcreddit) }
it 'should delete the post' do it 'should delete the post' do
expect { delete :destroy, subcreddit_id: subcreddit, id: post } expect { delete :destroy, subcreddit_id: subcreddit, id: post }
.to change(Post, :count).by(-1) .to change(Post, :count).by(-1)
end end
it 'should redirect to the subcreddit index' do it 'should redirect to the subcreddit index' do
expect(delete :destroy, subcreddit_id: subcreddit, id: post) expect(delete :destroy, subcreddit_id: subcreddit, id: post)
.to redirect_to(subcreddits_path(subcreddit)) .to redirect_to(subcreddits_path(subcreddit))
end end
it 'should flash notify that the post was deleted' do it 'should flash notify that the post was deleted' do
delete :destroy, subcreddit_id: subcreddit, id: post delete :destroy, subcreddit_id: subcreddit, id: post
expect(flash[:notice]).to be_present expect(flash[:notice]).to be_present
end
end end
end end
end end

View file

@ -97,24 +97,25 @@ describe SubcredditsController, type: :controller do
describe '#edit' do describe '#edit' do
context 'with valid subcreddit' do context 'with valid subcreddit' do
let(:subcreddit) { create(:subcreddit) } context 'when owner' do
let(:subcreddit) { create(:subcreddit, owner: user) }
it 'should assign @subcreddit to the existing subcreddit' do it 'should assign @subcreddit to the existing subcreddit' do
get :edit, id: subcreddit get :edit, id: subcreddit
expect(assigns(:subcreddit)).to eq(subcreddit) expect(assigns(:subcreddit)).to eq(subcreddit)
end end
it 'should render :edit' do it 'should render :edit' do
get :edit, id: subcreddit get :edit, id: subcreddit
expect(response).to render_template(:edit) expect(response).to render_template(:edit)
end
end end
end end
end end
describe '#update' do describe '#update' do
let(:subcreddit) { create(:subcreddit) }
let(:data) do let(:data) do
{ {
closed: '1' closed: '1'
@ -122,39 +123,43 @@ describe SubcredditsController, type: :controller do
end end
context 'wth valid data' do context 'wth valid data' do
it 'should assign @subcreddit to the existing subcreddit' do context 'when owner' do
put :update, id: subcreddit, subcreddit: data let(:subcreddit) { create(:subcreddit, owner: user) }
expect(assigns(:subcreddit)).to eq(subcreddit) it 'should assign @subcreddit to the existing subcreddit' do
end put :update, id: subcreddit, subcreddit: data
it 'should update the subcreddit' do expect(assigns(:subcreddit)).to eq(subcreddit)
put :update, id: subcreddit, subcreddit: data end
subcreddit.reload
expect(subcreddit.closed_at).to_not eq(nil) it 'should update the subcreddit' do
end put :update, id: subcreddit, subcreddit: data
subcreddit.reload
it 'should redirect to the subcreddit' do expect(subcreddit.closed_at).to_not eq(nil)
put :update, id: subcreddit, subcreddit: data end
expect(response).to redirect_to(subcreddit_url(subcreddit)) it 'should redirect to the subcreddit' do
end put :update, id: subcreddit, subcreddit: data
it 'should display a notice flash message' do expect(response).to redirect_to(subcreddit_url(subcreddit))
put :update, id: subcreddit, subcreddit: data end
expect(flash[:notice]).to be_present it 'should display a notice flash message' do
end put :update, id: subcreddit, subcreddit: data
end
context 'with invalid data' do expect(flash[:notice]).to be_present
before(:each) { data[:closed] = 'bad' } end
it 'should render :edit' do context 'with invalid data' do
put :update, id: subcreddit, subcreddit: data before(:each) { data[:closed] = 'bad' }
expect(response).to render_template(:edit) it 'should render :edit' do
put :update, id: subcreddit, subcreddit: data
expect(response).to render_template(:edit)
end
end
end end
end end
end end

View file

@ -3,28 +3,54 @@ require 'rails_helper'
describe 'Edit Comment', type: :feature do describe 'Edit Comment', type: :feature do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:post) { create(:post) } let!(:post) { create(:post) }
let!(:comment) { create(:comment, post: post, user: user) }
context 'when signed in' do context 'when signed in' do
let(:content) { 'Some different data' } let!(:comment) { create(:comment, post: post, user: user) }
before(:each) { signin(user: user) } before(:each) { signin(user: user) }
context 'with valid data' do context 'when owner' do
before(:each) do let(:content) { 'Some different data' }
context 'with valid data' do
before(:each) do
visit edit_subcreddit_post_comment_path(post.subcreddit,
post,
comment)
fill_in :comment_content, with: content
click_button 'Update Comment'
end
it 'should notify that the comment was edited' do
expect(page).to have_content('updated')
end
it 'should update the comment' do
expect(page).to have_content(content)
end
end
end
context 'when not owner' do
let!(:comment) { create(:comment, post: post) }
it 'should not allow editing of comment' do
visit edit_subcreddit_post_comment_path(post.subcreddit, post, comment) visit edit_subcreddit_post_comment_path(post.subcreddit, post, comment)
fill_in :comment_content, with: content expect(page).to have_content('not authorized')
click_button 'Update Comment'
end
it 'should notify that the comment was edited' do
expect(page).to have_content('updated')
end
it 'should update the comment' do
expect(page).to have_content(content)
end end
end end
end end
context 'when not signed in' do
let!(:comment) { create(:comment, post: post) }
it 'should not allow editing of comment' do
visit edit_subcreddit_post_comment_path(post.subcreddit, post, comment)
expect(page).to have_content('not authorized')
end
end
end end

View file

@ -1,33 +1,58 @@
require 'rails_helper' require 'rails_helper'
describe 'Edit Post', type: :feature do describe 'Edit Post', type: :feature do
let!(:subcreddit) { create(:subcreddit) }
context 'when signed in' do context 'when signed in' do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:subcreddit) { create(:subcreddit) }
let!(:post) { create(:post, subcreddit: subcreddit, user: user) }
let(:new_post) { build_stubbed(:post) } let(:new_post) { build_stubbed(:post) }
before(:each) { signin(user: user) } before(:each) { signin(user: user) }
context 'with valid data' do context 'when owner' do
before(:each) do let!(:post) { create(:post, subcreddit: subcreddit, user: user) }
context 'with valid data' do
before(:each) do
visit edit_subcreddit_post_path(subcreddit, post)
fill_in :post_title, with: new_post.title
fill_in :post_link, with: new_post.link
fill_in :post_content, with: new_post.content
click_button 'Update Post'
end
it 'should notify that the post was edited' do
expect(page).to have_content('updated')
end
it 'should show the updated post' do
expect(page).to have_content(new_post.title)
expect(page).to have_content(new_post.content)
end
end
end
context 'when not owner' do
let!(:post) { create(:post, subcreddit: subcreddit) }
it 'should notify user that they cannot edit' do
visit edit_subcreddit_post_path(subcreddit, post) visit edit_subcreddit_post_path(subcreddit, post)
fill_in :post_title, with: new_post.title expect(page).to have_content 'not authorized'
fill_in :post_link, with: new_post.link
fill_in :post_content, with: new_post.content
click_button 'Update Post'
end
it 'should notify that the post was edited' do
expect(page).to have_content('updated')
end
it 'should show the updated post' do
expect(page).to have_content(new_post.title)
expect(page).to have_content(new_post.content)
end end
end end
end end
context 'when not signed in' do
let!(:user) { GuestUser.new }
let!(:post) { create(:post, subcreddit: subcreddit) }
it 'should notify user they cannot edit' do
visit edit_subcreddit_post_path(subcreddit, post)
expect(page).to have_content 'not authorized'
end
end
end end

View file

@ -1,9 +1,10 @@
require 'rails_helper' require 'rails_helper'
describe 'New Post', type: :feature do describe 'New Post', type: :feature do
let!(:subcreddit) { create(:subcreddit) }
context 'when signed in' do context 'when signed in' do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:subcreddit) { create(:subcreddit) }
let!(:post) { build(:post, subcreddit: subcreddit) } let!(:post) { build(:post, subcreddit: subcreddit) }
before(:each) { signin(user: user) } before(:each) { signin(user: user) }
@ -46,4 +47,15 @@ describe 'New Post', type: :feature do
end end
end end
end end
context 'when not signed in' do
let!(:user) { GuestUser.new }
let!(:post) { create(:post, subcreddit: subcreddit) }
it 'should notify user they cannot create' do
visit new_subcreddit_post_path(subcreddit)
expect(page).to have_content 'not authorized'
end
end
end end

View file

@ -4,36 +4,60 @@ describe 'Edit Subcreddit', type: :feature do
before(:each) { signout } before(:each) { signout }
context 'when logged in' do context 'when logged in' do
context 'when board is open' do let!(:user) { create(:user) }
let!(:subcreddit) { create(:subcreddit) } before(:each) { signin(user: user) }
before(:each) do
visit subcreddits_path context 'when user is owner' do
click_link 'Edit' let!(:subcreddit) { create(:subcreddit, owner: user) }
check :subcreddit_closed
click_button 'Update Subcreddit' context 'when board is open' do
before(:each) do
visit subcreddits_path
click_link 'Edit'
check :subcreddit_closed
click_button 'Update Subcreddit'
end
it 'should be notified the subcreddit was updated' do
expect(page).to have_content('updated')
end
it 'should close the board when closed is checked' do
expect(page).to have_content('closed')
end
end end
it 'should be notified the subcreddit was updated' do context 'when board is closed' do
expect(page).to have_content('updated') let!(:subcreddit) do
end create(:subcreddit, owner: user, closed_at: Time.now)
end
it 'should close the board when closed is checked' do before(:each) do
expect(page).to have_content('closed') visit subcreddits_path
click_link 'Edit'
uncheck :subcreddit_closed
click_button 'Update Subcreddit'
end
it 'should open the board when closed is checked' do
expect(page).to_not have_content('closed')
end
end end
end end
context 'when board is closed' do context 'when not owner' do
let!(:subcreddit) { create(:subcreddit, closed_at: Time.now) } let!(:subcreddit) { create(:subcreddit) }
before(:each) do
it 'should not allow editing of subcreddit' do
visit subcreddits_path visit subcreddits_path
click_link 'Edit' click_link 'Edit'
uncheck :subcreddit_closed
click_button 'Update Subcreddit'
end
it 'should open the board when closed is checked' do expect(page).to have_content('not authorized')
expect(page).to_not have_content('closed')
end end
end end
end end
context 'when not logged in' do
end
end end

View file

@ -4,7 +4,11 @@ describe 'New Subcreddit', type: :feature do
before(:each) { signout } before(:each) { signout }
context 'when not signed in' do context 'when not signed in' do
it 'should not be able to create a new subcreddit' it 'should not be able to create a new subcreddit' do
visit new_subcreddit_path
expect(page).to have_content('not authorized')
end
end end
context 'when signed in' do context 'when signed in' do

View file

@ -0,0 +1,15 @@
require 'rails_helper'
describe ApplicationPolicy do
subject { ApplicationPolicy.new(user, object) }
let(:user) { create(:user) }
let(:object) { double('Object') }
it { should_not grant(:index) }
it { should_not grant(:show) }
it { should_not grant(:new) }
it { should_not grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end

View file

@ -0,0 +1,48 @@
require 'rails_helper'
describe CommentPolicy do
subject { CommentPolicy.new(user, comment) }
context 'when user is a guest' do
let(:comment) { create(:comment) }
let(:user) { GuestUser.new }
it { should grant(:index) }
it { should grant(:show) }
it { should_not grant(:new) }
it { should_not grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end
context 'when user is registered' do
let(:user) { create(:user) }
context 'when not owner' do
let(:comment) { create(:comment) }
it { should grant(:index) }
it { should grant(:show) }
it { should grant(:new) }
it { should grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end
context 'when owner' do
let(:comment) { create(:comment, user: user) }
it { should grant(:index) }
it { should grant(:show) }
it { should grant(:new) }
it { should grant(:create) }
it { should grant(:edit) }
it { should grant(:update) }
it { should grant(:destroy) }
end
end
end

View file

@ -0,0 +1,48 @@
require 'rails_helper'
describe PostPolicy do
subject { PostPolicy.new(user, post) }
context 'when user is a guest' do
let(:post) { create(:post) }
let(:user) { GuestUser.new }
it { should grant(:index) }
it { should grant(:show) }
it { should_not grant(:new) }
it { should_not grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end
context 'when user is registered' do
let(:user) { create(:user) }
context 'when not owner' do
let(:post) { create(:post) }
it { should grant(:index) }
it { should grant(:show) }
it { should grant(:new) }
it { should grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end
context 'when owner' do
let(:post) { create(:post, user: user) }
it { should grant(:index) }
it { should grant(:show) }
it { should grant(:new) }
it { should grant(:create) }
it { should grant(:edit) }
it { should grant(:update) }
it { should grant(:destroy) }
end
end
end

View file

@ -0,0 +1,49 @@
require 'rails_helper'
describe SubcredditPolicy do
subject { SubcredditPolicy.new(user, subcreddit) }
context 'when user is a guest' do
let(:subcreddit) { create(:subcreddit) }
let(:user) { GuestUser.new }
it { should grant(:index) }
it { should grant(:show) }
it { should_not grant(:new) }
it { should_not grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end
context 'when user is registered' do
let(:user) { create(:user) }
context 'when not owner' do
let(:subcreddit) { create(:subcreddit) }
it { should grant(:index) }
it { should grant(:show) }
it { should grant(:new) }
it { should grant(:create) }
it { should_not grant(:edit) }
it { should_not grant(:update) }
it { should_not grant(:destroy) }
end
context 'when owner' do
let(:subcreddit) { create(:subcreddit, owner: user) }
it { should grant(:index) }
it { should grant(:show) }
it { should grant(:new) }
it { should grant(:create) }
it { should grant(:edit) }
it { should grant(:update) }
it { should_not grant(:destroy) }
end
end
end

View file

@ -12,11 +12,13 @@ require 'rspec/rails'
require 'shoulda/matchers' require 'shoulda/matchers'
require 'capybara/rails' require 'capybara/rails'
require 'capybara/rspec' require 'capybara/rspec'
require 'pundit/rspec'
require 'support/capybara' require 'support/capybara'
require 'support/database_cleaner' require 'support/database_cleaner'
require 'support/factory_girl' require 'support/factory_girl'
require 'support/helpers' require 'support/helpers'
require 'support/pundit_matcher'
ActiveRecord::Migration.maintain_test_schema! ActiveRecord::Migration.maintain_test_schema!

View file

@ -0,0 +1,15 @@
RSpec::Matchers.define :grant do |action|
match do |policy|
policy.public_send("#{action}?")
end
failure_message do |policy|
"#{policy.class} does not permit #{action} on #{policy.record} "
+ "for #{policy.user.inspect}."
end
failure_message_when_negated do |policy|
"#{policy.class} does not forbid #{action} on #{policy.record} "
+ "for #{policy.user.inspect}."
end
end