Ruby on Rails
ACLController

A User’s permissions are added to their session when they authenticate. Permissions are of the form {controller}/{method}. For example:

posts/add
posts/edit

The Controller supports ‘guest perms’ which do not require any permissions. Other methods require the user to have the permission string before the action is performed. If the user is unauthorized, they are returned to the requesting page.

Any other method will require the user to have that permission. Permissions are kept as part of their session. Any changes made to the User’s permissions will not take effect until they login again. The filters added to this controller will be run for all controllers in the application. Likewise all the methods added be available for all controllers.


require 'action_controller'
require 'application_helper'
require 'user'

class ACLController < ActionController::Base
  # This is a secured controller from which others should derive. They are a limited set
  # of methods which are callable by anyone.  They are defined in the @guest_perms array.
  # By default, #list and #show are unsecured.
  # Example:
  #   class PostsController < AbstractApplicationController
  #      def initialize
  #        super
  #       @guest_perms = [ "posts/summary","posts/show"]
  #      end
  #    end
  #
  include ApplicationHelper

  layout "shared/base" 
  before_filter :authorize

  def initialize
    @sections    ||= %w/Users Groups Roles Permissions/
    @pretty_name ||= controller_name
    @guest_perms ||= [ "#{@pretty_plural.downcase}/list",
        "#{@pretty_plural.downcase}/show"]
    @class_object||= Object.const_get(@pretty_name)

    @uri_name = @pretty_plural.downcase
    @related  = @class_object.reflect_on_all_associations.collect{|x|x.name.to_s}
    @edit_uri = "#{@pretty_plural.downcase}/edit" 
  end

  def list
  end

  def show
  end

  def new
  end

  def edit
  end

  def update
  end

  def destroy
  end

  def create
  end
  protected

  # Authorizes the user for an action.
  # This works in conjunction with the LoginController.
  # The LoginController loads the User object.
  def authorize
    #return true
    required_perm = "%s/%s" % [ @params["controller"], @params["action"] ]
    unless @guest_perms.include? required_perm

      unless @session["user"]
        flash['error'] = "Request not permitted unless logged in." 
        @session["return_to"] = @session["prev_uri"]
        redirect_to(:controller => "login")
        return false
      end

      unless @session["user"].authorized? required_perm
        logger.info("Guest perms are #{@guest_perms.join(';')}")
        flash['notice'] = "You are not authorized for #{required_perm}." 
        redirect_to_path @session["prev_uri"]
        return false
      end

    end
    #@session["prev_uri"] = @request.env["REQUEST_URI"]
    @session["prev_uri"] = @request.request_uri
    return true
  end

end

The User model defines authorized?. It determines if the user has the permission string.


class User < ActiveRecord::Base
  # Return true/false if User is authorized for resource.
  def authorized?(resource)
    return permission_strings.include?(resource)
  end
  # Load permission strings 
  def permission_strings
    a = []
    groups.each{|g| g.roles.each{|r| r.permissions.each{|p| a<< p.name }}}
    a
  end
end


If you’re trying to get this to work with the LoginGenerator and the database models as described in AccessControlListExample,
try LoginGeneratorAccessControlList or :

The following changes worked for me (not well tested yet, though):

In the User model, change:

In the ACL controller, change:

In the controller that’s using the ACL (ie: PostsController), change:

This should do it, but I’m sure I’m overlooking some things. Also, it would be good to throw a message in the flash to display on the login screen in case the user needs the priviledges.



Feedback:

Should the example PostsController inherit from ACLController? Should be, this is fixed in the bottom section

The permission_strings method is doing N*M queries where you might be able to do just 1 query using Permission.find_by_sql?



eric at snowmoon com

Looking over this it does not seem like ”the ruby way”. A few things jump out at me. First off you now have a random controller out there in your web app that does nothing meaningful for the site itself. Secondly how permissions are assigned seems akward as well.

The way I would do something like this is…

Cheers



I don’t understand all of what you are suggesting, but my LoginGeneratorACLSystem module does away with the ACLController and a lot of stuff that isn’t needed when using LoginGenerator. -JamesHillyerd

Where does Groups come from ? (I just removed it and it works but I’m just wondering if somebody should remove it from this web page since it says it is based on AccessControlListExample, but there is no Groups in this example)

i’m trying to add this ACL but then i got this error message “uninitialized constant Base”.
can anyone help me to fix this up? i think this
might related to the model, but i dunno. tq in advanced!