Security - activescaffold/active_scaffold GitHub Wiki

If you want to deploy ActiveScaffold in production and expose it to possibly untrusted users, you can take advantage of its security layer to protect your data. The security layer works by paying attention to methods that you can define on your models and controllers. With these methods you may restrict access by taking into account any of the following, as appropriate: the ActiveRecord model, the database record, the current/intended action, and the current user. You may also restrict access at various levels of granularity, forbidding access to an entire action, an entire record, or just a column on the record.

Disclaimer: The security layer in ActiveScaffold is new. Please, please, please test your security setup! If your data is critical and you want complete peace of mind, you must test it yourself. Make sure you have been as specific as possible, and that you have tested to make sure your security methods are being used.

Note about CanCan: If you install the CanCan authorization package, ActiveScaffold will use the CanCan bridge and not the native ActiveScaffold security authorization methods described below. See CanCan in this wiki.

Settings

security.current_user_method global

Names a method on the controller that should return the current_user object, when available. The default is :current_user, which fits nicely with acts_as_authenticated, etc.

class ApplicationController < ActionController::Base

  ActiveScaffold.defaults do |config|
    config.security.current_user_method = :current_login
  end

  protected

  def current_login
    @session[:user_id] ? User.find(@session[:user_id]) : nil
  end
end

security.default_permission global

A boolean value for what a security check should return in the absence of a relevant method. The default is true, which lets ActiveScaffold work out of the box. If you need to be security conscious in your application, you should consider setting this to false so that nothing works until you permit it.

class ApplicationController < ActionController::Base

  ActiveScaffold.set_defaults do |config|
    config.security.default_permission = false
  end
end

Controller Method: Restricting an Action

You may restrict an entire action (in the ActiveScaffold plugin sense) by defining a method on your controller in the format #{action_name}_authorized?. All of the actions are set up with before_filters that check these controller methods. The default behavior of each controller method is to query the scaffold’s ActiveRecord model (see Model Methods below). The model is usually the best place to put a security method, but only methods defined in the controller have access to both session data and the params-hash.

Example:

class PostController
  active_scaffold :posts

  protected

  # only authenticated users are authorized to create records
  def create_authorized?
    # you can also check e.g. :params[:nested] or active_scaffold_constraints[:parent]
    self.logged_in?
  end

  #index action
  def list_authorized?
    current_user.some_boolean_property
  end

end

Since version 3, update_authorized? and delete_authorized? has an optional argument, record. Be sure you override them as an optional argument (record = nil). If you want to use that argument, remember that record can be nil, because methods will be called with no record too.

Model Methods: Restricting Anything Else

On your model object you may define methods (none of which accept any arguments) in any of four formats, depending on your need for granularity.

The formats are:

  • #{column_name}_authorized_for_#{crud_type}?
  • #{column_name}_authorized?
  • authorized_for_#{crud_type}?
  • authorized_for_#{action_name}?

Hopefully these methods are for the most part intuitive. Crud_type is one of :create, :read, :update, and :delete. Column_name is not necessarily the name of a database field. It’s the name of whatever column ActiveScaffold is working with, which means it may be a virtual column or it may be an association column.

You may combine methods, and ActiveScaffold will use them intelligently. That is, #{column_name}_authorized_for_#{crud_action}? has priority, so when authorized_for? is called with both crud_type and column, it checks only #{column_name}_authorized_for_#{crud_type}?. If #{column_name}_authorized_for_#{crud_type}? isn’t defined, ActiveScaffold will check both #{column_name}_authorized? and authorized_for_#{crud_type}?.

For example, if you define authorized_for_update? and username_authorized_for_update? methods, to check permissions for a inplace edit in username column, authorized_for?(:action => :update, :column => :username) is called and ActiveScaffold checks only username_authorized_for_update?. But to check permissions for other column, ActiveScaffold will use authorized_for_update? because we haven’t defined #{column}_authorized_for_update? neither #{column}_authorized?.

Most methods are defined on the instance level (e.g. def authorized_for_update?), but e.g. def self.authorized_for_create? is defined as a class method, because there is no record against which the create-permissions can be checked. Thus, authorized_for_create? is never called on a record.

authorized_for_read?, authorized_for_update?, authorized_for_delete? and authorized_for_#{action_name}? are called as class and instance methods to display action links:

  • if class method disallows, the action link is not displayed for any record.
  • if class method allows, the action link is displayed for all records, but disabled for records which disallow it.

authorized_for_read?, authorized_for_update? and authorized_for_delete? are called as class and instance methods for security checks in the controller:

  • if class method of the model disallows, a before_filter avoids executing the action without a database query.
  • after loading the record, its model’s instance method is called.

self.authorized_for_create? is used for both the create action and action links with create crud type, so it must be used to disallow creating a record, because it will disallow access to new and create actions.

Instance methods can return a string with reason for disallowing action, if string is returning to disallow actions, config.security.not_authorized_reason must be enabled in ActiveScaffold.set_defaults block:

# application_controller or initializer
ActiveScaffold.set_defaults do |config|
    config.security.not_authorized_reason = true
end

# model
def authorized_for_update?
  return 'reason for not authorized' unless authorized
  true
end

Tips for Defining Model Security Methods

ActiveScaffold’s permissions system actually makes the current user available to your records via the :current_user method. This lets you actually pay attention to who’s logged in and what their roles/permissions are. You should still be prepared to handle the current_user.nil? scenario, though. The current_user may not exist for anonymous users, during cron scripts, or if the model is accessed outside of a request/response cycle.

def authorized_for_delete?
  # anonymous users may never destroy these/this records
  return false unless current_user
  # unless it's an existing record and a 'permanent' flag has been thrown
  return (self.permanent == false)
end  

What Does NOT Happen

We have tried to design these security methods in a way that lets us spin them off into a new plugin at some future date. The reason they are not a separate plugin yet is because they function passively. That is, they only function when ActiveScaffold is polite enough to care! So please be aware: you can still mess up your data with these security methods in place if you bypass ActiveScaffold (with script/console, for example).

⚠️ **GitHub.com Fallback** ⚠️