Incline
Rails is a great framework and is very general in design. Inclines are railed devices designed to get from the bottom of a hill to the top. There are two left in Pittsburgh, I can’t speak for the rest of the world. The point is they are designed to do one thing and they do it pretty well.
This library builds on the Rails framework to quickly get to a certain point. That certain point is an application with a fairly simple layout, built-in authentication and authorization, and quite a few helper methods bolted onto the various Rails classes (e.g. - ActiveRecord::Base, ActionView::Base, ActionController::Base, etc).
The scaffold generator has also been updated to be more useful out of the box by scaffolding with bootstrap themed views and the DataTables (datatables.net) library being used for the index views.
Incline is currently locked to Rails 4.2, specifically 4.2.8 or higher. It has not been tested against Rails 5.
Installation
The easiest way to get started with Incline:
$ gem install incline
$ new_incline_app my_app
This will create an application depending on Incline, configure the application, generate a database, and initialize a local git repository for the application.
You could also manually perform the necessary steps by creating a rails app and adding Incline to your Gemfile.
gem 'incline'
Followed by running bundle and then the ‘incline:install’ generator.
$ bundle
$ bundle exec rails generate incline:install
Usage
Where to get started. If you use ‘new_incline_app’ or the ‘incline:install’ generator, your Rails app will now be setup with a basic, yet powerful, security model. All users are either standard users or system admins. There must always be at least one system admin. A system admin can promote other users to system admin or demote them back to standard users. A system admin has automatic access to everything that does not require an anonymous user. Standard users gain access through Access Groups. Users are defined in the Incline::User model.
Access Groups are completely customizable. You can create however many you want. Each access group can contain any number of other access groups or users. Any user who is a member of an access group added to another access group is also a member of the other access group. For instance. Group A includes Group B, so members of Group B are automatically members of Group A. Access Groups are defined in the Incline::AccessGroup model.
The final part of the security model is the Action Security objects. Each routed action in the application has an Action Security object. The object asks the controller for any hard settings for the action. The hard settings would be ‘allow_anon’ (grants access to everyone), ‘require_anon’ (requires an anonymous user), and ‘require_admin’ (requires a system admin). If any of the hard settings are flagged, ‘require_admin’ takes precedence, followed by ‘require_anon’ and finally ‘allow_anon’. Access Groups can be added to Action Security objects with no hard settings. Action Security objects are defined in the Incline::ActionSecurity model.
class FirstController < ActionController::Base
allow_anon :index, :show
require_anon :new, :create
require_admin :edit, :update, :destroy
end
class SecondController < ActionController::Base
require_admin true
end
When a user requests an action, the controller will end up taking one of these 10 actions:
-
If ‘require_admin’ is set and no user is logged in, redirect to login page.
-
If ‘require_admin’ is set and a non-admin is logged in, redirect to root page with error.
-
If ‘require_admin’ is set and an admin is logged in, grant access.
-
If ‘require_anon’ is set and no user is logged in, grant access.
-
If ‘require_anon’ is set and any user is logged in, redirect to root page with error.
-
If ‘allow_anon’ is set for the action, grant access.
-
If no user is logged in, redirect to login page.
-
If no groups are assign to the action, grant access.
-
If the user is a member of at least one group assigned to the action, grant access.
-
Redirect to root page with error.
There are ways to override this behavior, and the Incline::UsersController actually does by redefining the #valid_user? method. The Incline::UsersController allows the user to review and edit their own details, and it allows system admins to add new users. Without overriding this method, users would not be able to view or update their own details, and system admins would not be able to add new users since they are obviously already logged in.
When #valid_user? or #authorize! are redefined by a controller, the Action Security model will take notice of that fact and mark them as non-standard. Non-standard controllers will be treated the same when defining security, but may not behave the same as standard controllers. Going back to the Incline::UsersController, it doesn’t behave the way the standard security model expects because of the exceptions created for a few actions.
If you do decide to redefine security for a controller, redefine #valid_user? and leave #authorize! alone unless you have a good reason. At the end of your #valid_user? method, you should call super to allow the standard security model to handle any non-exception cases. The #valid_user? method should return true or redirect as appropriate and return false.
def valid_user?
action = params[:action].to_sym
# In order to delete resources, the user must be a member of 'super user' or 'can delete'.
if action == :destroy && !current_user.has_any_group?('super user', 'can delete')
redirect_to root_url
return false
end
# Members of the 'super user' group are granted access to all actions of this controller.
return true if current_user.has_any_group?('super user')
# Fall back on standard processing for everything else.
super
end
So now you’ve seen how to setup security for your controllers and actions, how do you best access them? With a menu of course. Two such files are included in any application that has had the ‘incline:install’ generator run.
- app/views/layouts/incline/_app_menu_anon.html.erb
-
This file defines the menu made available to everyone. It is a standard ERB file and therefore can contain embedded Ruby to customize the menu even more. For instance you may only want a link displayed to a resource if current_user.anonymous? is true.
- app/views/layouts/incline/_app_menu_authenticated.html.erb
-
This file defines the menu made available to users logged into the system. Just like the anonymous file, it is a standard ERB file and you can use Ruby to customize the menu more.
reCAPTCHA support is built into the library. To enable the support, you have to define the “recaptcha_public” and “recaptcha_private” values to be your site key and secret key from Google. If these are both defined, the gem will enable the reCAPTCHA functionality and force users to complete the challenges. If either of these are blank, the gem disables the reCAPTCHA functionality and silently passes the challenges.
You can use reCAPTCHA in two ways. The first way is via a validated model attribute and then using the form builder to include the necessary code in the form.
class MyModel < ActiveRecord::Base
attr_accessor :recaptcha_field
validates :recaptcha_field, 'incline/recaptcha' => true
...
end
<%= form_for @my_model do |f| %>
...
<%= f.recaptcha :recaptcha_field %>
<% end %>
Nice and simple.
The second way reCAPTCHA can be used is by explicitly including the challenge on a form and then explicitly checking the result on submission.
<%= form_for @another_model do |f| %>
...
<%= Incline::Recaptcha.add %>
<% end %>
def update
unless Incline::Recaptcha.verify(request)
@another_model.errors[:base] << 'You must complete the reCAPTCHA challenge.'
end
end
Still pretty simple.
Email support is also built into the library. This is of course configuring email for use with SMTP. SMTP allows for a more consistent experience across platforms, so I generally find myself adding SMTP configuration to an application. Incline handles that configuration for you by reading from a simple to use ‘email.yml’ file. The default ‘email.yml’ from the ‘incline:install’ generator includes all of the necessary configuration information and shows how to utilize ‘secrets.yml’ to store the login credentials.
(The ‘incline:install’ generator also adds a line to ‘.gitignore’ to exclude ‘secrets.yml’ from the git repo.)
For email, generally port 465 is used with the ssl parameter set to true and port 587 is used with start_tls parameter set to true. It is not recommended to utilize SMTP without some form of encryption.
If for some reason you don’t want to use the SMTP configuration support, simply delete the ‘email.yml’ file from your configuration directory.
Migrations and seeds from the Incline gem are included into your application automatically. They do not get copied into your application and you should not copy them local since they may be overridden in the future. You shouldn’t add onto Incline models directly unless you are sure about what you are doing. A future Incline upgrade may conflict with fields you add on. Instead of adding on, you may want to define a related model.
class CreateUserDetails < ActiveRecord::Migration
def change
create_table :user_details do |t|
t.integer :user_id, null: false
t.string :full_name
t.string :address1
t.string :address2
t.string :city
t.string :state
t.string :zip_code
t.string :phone_number
end
add_index :user_details, :user_id, :unique => true
end
end
class UserDetails < ActiveRecord::Base
belongs_to :user, class_name: 'Incline::User'
::Incline::User.has_one :details, :class_name => 'UserDetail', :foreign_key => 'user_id', :dependent => :destroy
end
License
This gem is released under the MIT license.