Skip to content

Commit 9dd8744

Browse files
author
Ryan Bigg
committed
Section 8.5.3: Restrict creating tickets to only users who have permissions to do it
1 parent d5d7df6 commit 9dd8744

File tree

10 files changed

+108
-0
lines changed

10 files changed

+108
-0
lines changed

ticketee/Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ gem 'bootstrap-sass', '~> 3.3'
2727
gem 'font-awesome-rails', '~> 4.2'
2828
gem 'simple_form', '3.1.0'
2929
gem 'devise', '~> 3.4.1'
30+
gem 'pundit', '~> 0.3.0'
3031

3132
# Use ActiveModel has_secure_password
3233
# gem 'bcrypt', '~> 3.1.7'

ticketee/Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ GEM
103103
nokogiri (1.6.5)
104104
mini_portile (~> 0.6.0)
105105
orm_adapter (0.5.0)
106+
pundit (0.3.0)
107+
activesupport (>= 3.0.0)
106108
rack (1.6.0.beta2)
107109
rack-test (0.6.2)
108110
rack (>= 1.0)
@@ -208,6 +210,7 @@ DEPENDENCIES
208210
font-awesome-rails (~> 4.2)
209211
jbuilder (~> 2.0)
210212
jquery-rails (~> 4.0.0.beta2)
213+
pundit (~> 0.3.0)
211214
rails (= 4.2.0.rc1)
212215
rspec-rails (~> 3.1.0)
213216
sass-rails (~> 5.0.0.beta1)

ticketee/app/controllers/application_controller.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
class ApplicationController < ActionController::Base
2+
include Pundit
3+
4+
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
25
# Prevent CSRF attacks by raising an exception.
36
# For APIs, you may want to use :null_session instead.
47
protect_from_forgery with: :exception
@@ -17,4 +20,9 @@ def authorize_admin!
1720
redirect_to root_path
1821
end
1922
end
23+
24+
def user_not_authorized
25+
flash[:alert] = "You don't have permission to do that."
26+
redirect_to(root_path)
27+
end
2028
end

ticketee/app/controllers/tickets_controller.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ class TicketsController < ApplicationController
44
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
55

66
def new
7+
authorize @project, :write?
78
@ticket = @project.tickets.build
89
end
910

1011
def create
12+
authorize @project, :write?
1113
@ticket = @project.tickets.build(ticket_params)
1214
@ticket.author = current_user
1315

ticketee/app/models/user.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ class User < ActiveRecord::Base
44
devise :database_authenticatable, :registerable,
55
:recoverable, :rememberable, :trackable, :validatable
66

7+
has_many :permissions
8+
79
def to_s
810
"#{email} (#{admin? ? "Admin" : "User"})"
911
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
class ApplicationPolicy
2+
attr_reader :user, :record
3+
4+
def initialize(user, record)
5+
@user = user
6+
@record = record
7+
end
8+
9+
def index?
10+
false
11+
end
12+
13+
def show?
14+
scope.where(:id => record.id).exists?
15+
end
16+
17+
def create?
18+
false
19+
end
20+
21+
def new?
22+
create?
23+
end
24+
25+
def update?
26+
false
27+
end
28+
29+
def edit?
30+
update?
31+
end
32+
33+
def destroy?
34+
false
35+
end
36+
37+
def scope
38+
Pundit.policy_scope!(user, record.class)
39+
end
40+
41+
class Scope
42+
attr_reader :user, :scope
43+
44+
def initialize(user, scope)
45+
@user = user
46+
@scope = scope
47+
end
48+
49+
def resolve
50+
scope
51+
end
52+
end
53+
end
54+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class ProjectPolicy < ApplicationPolicy
2+
attr_reader :user, :project
3+
4+
def initialize(user, project)
5+
@user = user
6+
@project = project
7+
end
8+
9+
def write?
10+
user.permissions.exists?(thing: project, action: :write)
11+
end
12+
end

ticketee/spec/controllers/tickets_controller_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,29 @@
2020
expect(flash[:alert]).to eql("The project you were looking " +
2121
"for could not be found.")
2222
end
23+
24+
context "with permission to read the project" do
25+
before do
26+
define_permission!(user, "read", project)
27+
end
28+
29+
context "but without permission to write" do
30+
def assert_no_permission!
31+
expect(response).to redirect_to(root_path)
32+
message = "You don't have permission to do that."
33+
expect(flash[:alert]).to eql(message)
34+
end
35+
36+
it "cannot begin to create a ticket" do
37+
get :new, project_id: project.id
38+
assert_no_permission!
39+
end
40+
41+
it "cannot create a ticket without permission" do
42+
post :create, project_id: project.id
43+
assert_no_permission!
44+
end
45+
end
46+
end
2347
end
2448
end

ticketee/spec/features/creating_tickets_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
login_as(user)
77
project = FactoryGirl.create(:project, name: "Internet Explorer")
88
define_permission!(user, "read", project)
9+
define_permission!(user, "write", project)
910

1011
visit '/'
1112
click_link project.name

ticketee/spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require "pundit/rspec"
12
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
23
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
34
# The generated `.rspec` file contains `--require spec_helper` which will cause this

0 commit comments

Comments
 (0)