class CandidatePolicy < ApplicationPolicy
# ...
def permitted_attributes
if user.manager?
[:name, :salary, :equity]
else
[:name]
end
end
end
class CandidatesController < ApplicationController
# ...
def update
# ...
if @candidate.update_attributes(candidate_attributes)
# ...
end
private
def candidate_attributes
params.require(:candidate)
.permit(policy(@candidate).permitted_attributes)
end
end
describe "users/show" do
before(:each) do
user = assign(:user, build_stubbed(:user))
controller.stub(:current_user).and_return user
end
it "renders the destroy action" do
# New Rspec syntax
allow(view).to receive(:policy).and_return double(edit?: false, destroy?: true)
render
expect(rendered).to match 'Destroy'
end
it "does not render the destroy action" do
# Old Rspec syntax
view.stub(:policy).and_return double(edit?: false, destroy?: false)
render
expect(rendered).to_not match 'Destroy'
end
end
module PunditViewPolicy
extend ActiveSupport::Concern
included do
before do
controller.singleton_class.class_eval do
def policy(instance)
Class.new do
def method_missing(*args, &block); true; end
end.new
end
helper_method :policy
end
end
end
end
RSpec.configure do |config|
config.include PunditViewPolicy, type: :view
end
describe NotePolicy do
subject { NotePolicy.new(current_user, note) }
context "for a user" do
let(:current_user) { build_stubbed :user }
context "creating a new note" do
let(:note) { Note.new }
it { should permit(:new) }
it { should permit(:create) }
end
context "with a note someone else created" do
let(:note) { build_stubbed :note, user: build(:user) }
it { should permit(:show) }
it { should_not permit(:edit) }
it { should_not permit(:update) }
it { should_not permit(:destroy) }
end
context "with a note that I created" do
let(:note) { build_stubbed :note, user: current_user }
it { should permit(:show) }
it { should permit(:edit) }
it { should permit(:update) }
it { should permit(:destroy) }
end
end
end
# Styled for brevity...
class NotePolicy < ApplicationPolicy
def show? ; true; end
def create? ; true; end
def update? ; record.user == user; end
def destroy?; record.user == user; end
end
# And a top-level policy for setting defaults.
class ApplicationPolicy
attr_reader :user, # User performing the action
:record # Instance upon which action is performed
def initialize(user, record)
raise Pundit::NotAuthorizedError, "Must be signed in." unless user
@user = user
@record = record
end
def index? ; false; end
def show? ; scope.where(id: record.id).exists?; end
def new? ; create?; end
def create? ; false; end
def edit? ; update?; end
def update? ; false; end
def destroy?; false; end
def scope
Pundit.policy_scope!(user, record.class)
end
end
/ Truncated for brevity.
- @candidates.each do |candidate|
tr
td= link_to_if policy(candidate).show?, candidate.name, candidate_path(candidate)
td
- if policy(candidate).edit?
= link_to 'Edit', edit_candidate_path(candidate)
- if policy(candidate).destroy?
= link_to 'Destroy', candidate_path(candidate), method: :delete
- if policy(Candidate.new).create?
= link_to 'New Candidate', new_candidate_path
class CandidatesController < ApplicationController
# This controller defines some custom actions.
after_filter :verify_authorized, except: [:search, :index, :dashboard]
after_filter :verify_policy_scoped, only: [:search, :index, :dashboard]
def dashboard
# policy_scope returns a scope configured according to the
# CandidatePolicy.
@candidates = policy_scope(Candidate)
@candidates.includes(stages: [:notes]).references(:stages)
end
def show
@candidate = Candidate.find(params[:id])
authorize @candidate
end
def star
@candidate = Candidate.find(params[:id])
# If the user can update a candidate, they can also "star" them.
authorize @candidate, :update?
@candidate.update_attribute(:starred, true)
render partial: 'candidates/star', locals: { candidate: @candidate }
end
# ...
end
class ApplicationController < ActionController::Base
include Pundit
# Verify that controller actions are authorized. Optional, but good.
after_filter :verify_authorized, except: :index
after_filter :verify_policy_scoped, only: :index
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
flash[:error] = "You are not authorized to perform this action."
redirect_to request.headers["Referer"] || root_path
end
# ...
end