-
Notifications
You must be signed in to change notification settings - Fork 330
Security
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.
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.set_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
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
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 end
On your model object you may define methods (none of which accept any arguments) in any of three formats, depending on your need for granularity.
The formats are:
- #{column_name}_authorized_for_#{crud_action}?
- #{column_name}_authorized?
- authorized_for_#{crud_action}?
Hopefully these methods are for the most part intuitive. Crud_action 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 action and column, it checks only #{column_name}_authorized_for_#{crud_action}?
. If #{column_name}_authorized_for_#{crud_action}?
isn’t defined, ActiveScaffold will check both #{column_name}_authorized?
and authorized_for_#{crud_action}?
.
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_for?
.
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
and authorized_for_delete
are called as class and instance methods:
Displaying 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.
Security checks in the controller:
– if class method disallows, a before_filter avoids executing the action without a database query.
– after loading the record, the 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.
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.
The second tip is that even though the methods are always at the instance level, the current record isn’t always meaningful. To distinguish whether the specific record matters for the security check, use the :existing_record_check?
method.
def authorized_for_delete? # anonymous users may never destroy these/this records return false unless current_user # and logged-in users are usually authorized to destroy records return true unless existing_record_check? # unless it's an existing record and a 'permanent' flag has been thrown return (self.permanent == false) end
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).