class Post < ActiveRecord::Base
belongs_to :author
validate :author_is_admin
def author_is_admin
unless author.role == 'admin'
errors.add(:author_id, 'must be an admin')
end
end
end
class UsersController < ApplicationController
def update
user = User.find(params[:id])
unless user.supervisor == current_user
raise "Trying to edit unauthorized user!"
end
if params[:user][:role] == 'admin'
raise "Trying to set unauthorized value!"
end
user.update_attributes(params[:user])
end
end
Scope-based authorization solution for Ruby on Rails.
github.com/makandra/consul
Enums for ActiveRecord attributes and associations.
github.com/makandra/assignable_values
... but you can do it in plain Ruby, cancan, etc.
Post.where(:author_id => user.id)
class Power
include Consul::Power
def initialize(user)
@user = user
end
power :posts do
Post.where(:author_id => user.id)
end
end
Note how we are not storing permissions with the user.
Power.current = Power.new(user) Power.current.posts # => #<ActiveRecord::Relation> Power.current.posts? # => true or false Power.current.post?(post) # => true or false
class PostsController < ApplicationController
def show
@object = end_of_association_chain.find(params[:id])
end
def index
@collection = end_of_association_chain.all
end
def end_of_association_chain
Power.current.posts
end
end
class PostsController < ApplicationController
resource_controller
def end_of_association_chain
Power.current.posts
end
end
class PostsController < ApplicationController resource_controller power :posts, :as => :end_of_association_chain end
class Power
# ...
power :posts do
Post.where(:author_id => user.id)
end
power :updatable_posts do
posts.where(:published => false)
end
end
class PostsController < ApplicationController
resource_controller
def end_of_association_chain
if action_name == 'edit' || action_name == 'update'
Power.current.updatable_posts
else
Power.current.posts
end
end
end
class PostsController < ApplicationController resource_controller power :crud => :posts, :as => :end_of_association_chain end
class Power
def initialize(user)
@user = user
end
power :posts do
case role
when :admin then Post
when :author then Post.where(:author_id => user.id)
when :guest then Post.where(:open => true)
end
end
private
def role
@user.role.to_sym
end
end
class PostsController < ApplicationController resource_controller power :crud => :posts, :as => :end_of_association_chain end
Power repository.class Power
# ...
power :assignable_post_fields do
case role
when :admin then %w[subject body state]
when :author then %w[subject body]
end
end
end
class PostsController < ApplicationController
# ...
def update
@object = end_of_association_chain.find(params[:id])
@object.update_attributes!(object_params)
redirect_to @object
end
def object_params
params[:post].slice(*Power.current.assignable_post_fields)
end
end
class Post < ActiveRecord::Base
validates_inclusion_of :state,↲
:in => %w[draft delivered published]
end
class Post < ActiveRecord::Base
assignable_values_for :state do
%w[draft delivered published]
end
end
Power repositoryclass Post < ActiveRecord::Base
assignable_values_for :state, ↲
:through => lambda { Power.current }
end
class Post < ActiveRecord::Base authorize_values_for :state end
class Power
# ...
power :assignable_post_states do
case role
when :admin then %w[draft delivered published]
when :author then %w[draft delivered]
end
end
end
The authorization rule manifests as a validation in the Post model:
post = Post.new(:state => 'published') Power.current = Power.new(admin_user) post.assignable_states # => ['draft', 'delivered', 'published'] post.valid? # => true Power.current = Power.new(author_user) post.assignable_states # => ['draft', 'delivered'] post.valid? # => false
post = Post.new(:state => 'published') Power.current = Power.new(author_user) post.assignable_states # => ['draft', 'delivered'] post.valid? # => false Power.current = nil post.valid? # => true
class PostsController < ApplicationController resource_controller power :crud => :posts end
Again the controller didn't need to change.
class Post < ActiveRecord::Base
belongs_to :author
authorize_values_for :author
end
class Power
# ...
power :assignable_post_authors do
User.where(:locked => false)
end
end
What happens to a user's posts when I lock her account?
class Power
# ...
power :assignable_post_authors do
User.where(:locked => false)
end
end
class Post < ActiveRecord::Base
authorize_values_for :state
end
class Power
# ...
power :assignable_post_states do |post|
if user.supervisor_of?(post.author)
%w[draft pending published]
else
%w[draft pending]
end
end
end
Get to know makandra:
http://www.makandra.de
http://makandra.com
Talk to me afterwards or send me a message:
henning.koch@makandra.de
@triskweline
Replay this talk:
makandra.com/talks