/simply_authenticate

SimplyAuthenticate is a Rails plugin providing a relatively simple but complete role based authentication system.

Primary LanguageRubyMIT LicenseMIT

SimplyAuthenticate
==================

SimplyAuthenticate is a Rails plugin providing a relatively simple but on the other hand quite complete role based authentication system.


Requirements
============

Rails version 2.3+

Gem dependencies:
  redgreen (for testing) [OPTIONAL]
  sqlite3-ruby (for testing)
  unidecode (for a reliable .to_slug for user names)

Other plugins dependencies:
  ruby_extensions (.to_slug method for String class) (http://github.com/pjg/ruby_extensions)
  simply_settings (to store application settings concerning registration) (http://github.com/pjg/simply_settings) [OPTIONAL]


Installation
============

git submodule add git://github.com/pjg/simply_authenticate.git vendor/plugins/simply_authenticate
git commit -m "Add simply_authenticate plugin as a submodule"


script/generate model user
  class User < ActiveRecord::Base
    acts_as_authenticated
  end

db/migrate/xxxxxxxxxxxxxx_create_users.rb
  create_table :users do |t|
    t.string   :email,                     :limit => 120, :default => '',    :null => false
    t.string   :hashed_password,           :limit => 40,  :default => '',    :null => false
    t.string   :name,                      :limit => 30,  :default => ''
    t.string   :slug,                      :limit => 30,  :default => ''
    t.string   :gender,                    :limit => 1
    t.string   :salt,                      :limit => 10
    t.string   :activation_code,           :limit => 40
    t.datetime :activated_on
    t.boolean  :is_activated,                             :default => false, :null => false
    t.boolean  :is_blocked,                               :default => false, :null => false
    t.string   :autologin_token,           :limit => 40
    t.datetime :autologin_expires
    t.string   :new_email,                 :limit => 120
    t.string   :new_email_activation_code, :limit => 40
    t.integer  :login_count,                              :default => 0,     :null => false
    t.string   :current_ip,                :limit => 20
    t.string   :last_ip,                   :limit => 20
    t.string   :last_failed_ip,            :limit => 20
    t.datetime :current_logged_on
    t.datetime :last_logged_on
    t.datetime :last_failed_logged_on
    t.datetime :created_on
    t.datetime :updated_on
  end

  add_index :users, :autologin_token
  add_index :users, :email, :unique => true
  add_index :users, :slug


script/generate model role
  class Role < ActiveRecord::Base
    acts_as_authenticated_role
  end

db/migrate/xxxx_create_roles.rb
  create_table :roles do |t|
    t.string :name, :limit => 30, :default => '', :null => false
    t.string :slug, :limit => 30, :default => '', :null => false
  end

  add_index :roles, :slug, :unique => true


script/generate migration create_roles_users
  create_table :roles_users, :id => false do |t|
    t.integer :role_id, :null => false
    t.integer :user_id, :null => false
  end

  execute "ALTER TABLE `roles_users` ADD PRIMARY KEY `role_id_user_id` (`role_id`, `user_id`)"


script/generate migration add_default_roles
  # Add roles you'd like your system to have. Administrator and user roles are required
  def self.up
    Role.create(:slug => 'user', :name => 'Użytkownik')
    Role.create(:slug => 'administrator', :name => 'Administrator')
    Role.create(:slug => 'editor', :name => 'Redaktor')
    Role.create(:slug => 'moderator', :name => 'Moderator')
  end

  def self.down
    Role.find_by_slug('moderator').destroy
    Role.find_by_slug('editor').destroy
    Role.find_by_slug('administrator').destroy
    Role.find_by_slug('user').destroy
  end


run the migrations:
  rake db:migrate


script/generate controller users
  class UsersController < ApplicationController
    # certain filters for certain actions
    before_filter :login_required, :only => [:logout, :profile, :change_password, :change_email_address]
    before_filter :registration_allowed, :only => [:register] # OPTIONAL
    before_filter :password_change_allowed, :only => [:change_password] # OPTIONAL
    before_filter :password_reset_allowed, :only => [:forgot_password] # OPTIONAL
    before_filter :administrator_role_required, :only => [:index, :show, :edit]

    def register
      register_and_redirect_to_default
    end

    def activate_account
      activate_account_login_and_redirect_to_default
    end

    def send_activation_code
      send_activation_code_and_redirect_to_root
    end

    def login
      login_and_redirect_to_stored
    end

    def forgot_password
      reset_password_and_redirect_to_login
    end

    def profile
      show_or_edit_profile
    end

    def change_password
      change_password_and_redirect_to_profile
    end

    def change_email_address
      change_email_address_and_redirect_to_profile
    end

    def activate_new_email_address
      activate_new_email_address_and_redirect_to_default
    end

    def logout
      logout_and_redirect_to_default
    end

    # Administrative methods

    def index
      list_users
    end

    def show
      show_user
    end

    def edit
      edit_user
    end
  end


script/generate mailer Notifications
  class Notifications < ActionMailer::Base
    acts_as_authenticated_mailer
  end


config/initializers/simply_authenticate.rb
  SimplyAuthenticate::Settings.notifications[:application] = 'MyApplication'
  SimplyAuthenticate::Settings.notifications[:host] = 'myapplication.com'
  SimplyAuthenticate::Settings.notifications[:email] = 'info@myapplication.com'
  SimplyAuthenticate::Settings.legal_notice = 'Udostępniane dane osobowe są chronione zgodnie z Ustawą o ochronie danych osobowych i Ustawą o świadczeniu usług drogą elektroniczną. Administratorem bazy danych osobowych jest XXX z siedzibą w XXX przy ulicy XXX. Udostępniającemu przysługuje prawo do wglądu i zmiany swoich danych osobowych.'
  SimplyAuthenticate::Settings.legal_requirements_message = 'Oświadczam, że zostałem poinformowany o przysługujących mi prawach i wyrażam zgodę na przechowywanie i przetwarzanie moich danych osobowych przez XXX.'
  # you can see all of the things you can configure in the lib/simply_authenticate_settings.rb file


And finally add the login/logout links to the application.html.erb:

<% if logged_in? -%>
<%= link_to 'Panel użytkownika', panel_path %>
<%= link_to 'Wyloguj mnie »', logout_path %>
<% else -%>
<%= link_to 'Rejestracja', register_path if registration_allowed? %>
<%= link_to 'Logowanie »', login_path %>
<% end -%>


Optional steps
--------------

Install the simply_settings plugin (https://github.com/pjg/simply_settings) and add settings for the simply_authenticate plugin ('1' means true/allowed):

script/generate migration add_settings_for_the_simply_authenticate_plugin
  def self.up
    Setting.create(:slug => 'registration_allowed', :value => '1', :name => 'Możliwość rejestracji użytkowników?')
    Setting.create(:slug => 'password_change_allowed', :value => '1', :name => 'Użytkownik może zmienić swoje hasło?')
    Setting.create(:slug => 'password_reset_allowed', :value => '1', :name => 'Użytkownik może zresetować swoje hasło?')
  end

  def self.down
    Setting.find_by_slug('registration_allowed').destroy
    Setting.find_by_slug('password_change_allowed').destroy
    Setting.find_by_slug('password_reset_allowed').destroy
  end

rake db:migrate

--

It's best to have '/panel' action, where user will be redirected to (by default, using the SimplyAuthenticate::Settings.default_logged_in_redirect_to configuration option) after logging in:

routes.rb
  map.panel '/panel', :controller => 'site', :action => 'panel'

site_controller.rb:
  def panel
  end

--

The plugin comes with its own set of templates for actions defined in the UsersController. If you want to use your own template you can just create it in the app/views/users/ directory of your application and it should be picked up instead of the template from the plugin.
There is also one special partial: _detailed_profile.html.erb which is empty by default. You should create it if you'd like to display some additional info about the user in the profile. Like this:

app/views/users/_detailed_profile.html.erb
  <p>Added comments: <strong><%= @user.comments_count %></strong></p>

--

application.js (jQuery)

// CANCEL buttons from hrefs
$('a.cancel:contains("Anuluj")').each(function() {
  var redirect_to = $(this).attr('href')
  var button = $('<input type="button" class="cancel" value="Anuluj" />')
  button.click(function() {
    window.location = redirect_to
  })
  $(this).replaceWith(button)
})

// Focus the email field for the login/register form (but only if the user hasn't started typing yet)
$('input[value=""]#user_email').focus()


NAMED_SCOPES
============

The User model comes with some predefined named_scopes.

User.ordered
=> User.all(:order => 'name')

User.editors
=> User.all(:joins => :roles, :conditions => "roles.slug='editor'")
# works for all roles defined


MODEL METHODS
=============

The User model comes with some predefined dynamic methods:

User.first.is_editor?
User.first.editor?
# works for all roles defined


FILTERS
=======

There are some filters defined, which you can use in any controller, like this:

before_filter :login_required

before_filter :editor_role_required
# works for all roles defined


HELPERS
=======

There are some dynamic helpers available in the views:

logged_in?

editor?
# works for all roles defined


ADDING USERS FROM THE CONSOLE
=============================

user = User.new(:email => 'test@example.com', :password => 'test123', :password_confirmation => 'test123', :name => 'Test', :gender => 'm')
user.is_activated = true
user.activated_on = Time.now
user.roles << Role.find_by_slug('user')
user.save


TESTING
=======

If you'd like to run the tests included within this plugin you must have your application set up properly to work with this plugin. This means having: a user and role models (and corresponding databse tables) and the following plugins installed: simply_settings (http://github.com/pjg/simply_settings) and ruby_extensions (http://github.com/pjg/ruby_extensions).

You also need to have sqlite3 installed:

aptitude install sqlite3 libsqlite3-dev
gem install sqlite3-ruby


If you are running into errors like this one:

SQLite3::SQLException: index name already exists

you must have your indices named uniquely accross the database (sqlite3 limitation), like this:

add_index :users, ['slug'], :name => 'users_slug', :unique => true

notice the name prefix ('users_').


FAQ
===

Changing the paths of defined named routes
------------------------------------------

config/initializers/simply_authenticate.rb
  SimplyAuthenticate::Settings.route_names[:login_path] = 'login'
  ...

See: simply_authenticate/lib/simply_authenticate_settings.rb



Copyright (c) 2009-2011 Paweł Gościcki, released under the MIT license