h1. Create default Guest user
class Guest def create Guest.new end def has_role? role role == :guest end def has_roles? *roles role == :guest end endh2. Add to ApplicationController
# http://stackoverflow.com/questions/4275058/using-devise-with-guest-users def current_user super || Guest.create # role defaults to 'guest' in the model. end def user_signed_in? current_user && !current_user.new_record? end def user_session user_signed_in? ? super : session endh1. Control who can create new users
$ mkdir app/controllers/users $ touch app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController before_filter :check_permissions, :only => [:new, :create, :cancel] skip_before_filter :require_no_authentication def check_permissions authorize! :create, resource end endThe check permissions method is really simple. It calls the CanCan method, authorize!, and checks if the current user can create users. We use resource here because devise uses resource to refer to the model that can be authenticated. Also notice how I removed the require_no_authentication filter, a Devise filter which allows access to actions without authentication.
# replace devise_for :users with: devise_for :users, :controllers => { :registrations => "users/registrations" }At this point if you hit the users/sign_up page when not logged in, you will notice that a CanCan::AccessDenied is thrown. This exception is thrown anytime permission is denied so you should customize it to your liking. I put the handler in my ApplicationController:
class ApplicationController < ActionController::Base ... rescue_from CanCan::AccessDenied do |exception| flash[:error] = exception.message redirect_to root_url end ... end
class UsersController < ApplicationController before_filter :get_user, :only => [:index,:new,:edit] before_filter :accessible_roles, :only => [:new, :edit, :show, :update, :create] load_and_authorize_resource :only => [:show,:new,:destroy,:edit,:update] # GET /users # GET /users.xml # GET /users.json HTML and AJAX #----------------------------------------------------------------------- def index @users = User.accessible_by(current_ability, :index).limit(20) respond_to do |format| format.json { render :json => @users } format.xml { render :xml => @users } format.html end end # GET /users/new # GET /users/new.xml # GET /users/new.json HTML AND AJAX #------------------------------------------------------------------- def new respond_to do |format| format.json { render :json => @user } format.xml { render :xml => @user } format.html end end # GET /users/1 # GET /users/1.xml # GET /users/1.json HTML AND AJAX #------------------------------------------------------------------- def show respond_to do |format| format.json { render :json => @user } format.xml { render :xml => @user } format.html end rescue ActiveRecord::RecordNotFound respond_to_not_found(:json, :xml, :html) end # GET /users/1/edit # GET /users/1/edit.xml # GET /users/1/edit.json HTML AND AJAX #------------------------------------------------------------------- def edit respond_to do |format| format.json { render :json => @user } format.xml { render :xml => @user } format.html end rescue ActiveRecord::RecordNotFound respond_to_not_found(:json, :xml, :html) end # DELETE /users/1 # DELETE /users/1.xml # DELETE /users/1.json HTML AND AJAX #------------------------------------------------------------------- def destroy @user.destroy! respond_to do |format| format.json { respond_to_destroy(:ajax) } format.xml { head :ok } format.html { respond_to_destroy(:html) } end rescue ActiveRecord::RecordNotFound respond_to_not_found(:json, :xml, :html) end # POST /users # POST /users.xml # POST /users.json HTML AND AJAX #----------------------------------------------------------------- def create @user = User.new(params[:user]) if @user.save respond_to do |format| format.json { render :json => @user.to_json, :status => 200 } format.xml { head :ok } format.html { redirect_to :action => :index } end else respond_to do |format| format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder format.xml { head :ok } format.html { render :action => :new, :status => :unprocessable_entity } end end end ... # PUT /users/1 # PUT /users/1.xml # PUT /users/1.json HTML AND AJAX #---------------------------------------------------------------------------- def update if params[:user][:password].blank? [:password,:password_confirmation,:current_password].collect{|p| params[:user].delete(p) } else @user.errors[:base] << "The password you entered is incorrect" unless @user.valid_password?(params[:user][:current_password]) end respond_to do |format| if @user.errors[:base].empty? and @user.update_attributes(params[:user]) flash[:notice] = "Your account has been updated" format.json { render :json => @user.to_json, :status => 200 } format.xml { head :ok } format.html { render :action => :edit } else format.json { render :text => "Could not update user", :status => :unprocessable_entity } #placeholder format.xml { render :xml => @user.errors, :status => :unprocessable_entity } format.html { render :action => :edit, :status => :unprocessable_entity } end end rescue respond_to_not_found(:js, :xml, :html) end # FILTERS # Get roles accessible by the current user #---------------------------------------------------- def accessible_roles @accessible_roles = Role.accessible_by(current_ability,:read) end # Make the current user object available to views #---------------------------------------- def get_user @current_user = current_user end endh2. Views
h3. partial – _user_fields.html.erbRegister User
<%= form_for(@user) do |f| %> <%= error_messages(@user,"Could not register user") %> <%= render :partial => 'user_fields', :locals => { :f => f } %><%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Register" %>
<% end %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.label :email %>
<%= f.text_field :email %>
<% if can? :read, Role %><%= f.label :role %>
h3. Show User<%= @user == @current_user ? "Your Account Settings" : "Edit User" %>
<%= form_for(@user, :html => { :method => :put }) do |f| %> <%= error_messages(@user,"Could not update user") %> <%= render :partial => 'user_fields', :locals => { :f => f } %><%= f.label :password %> (leave blank if you don't want to change it)
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.label :current_password %> (we need your current password to confirm your changes)
<%= f.password_field :current_password %>
<%= f.submit "Update" %>
<% end %> <%= link_to "Back", :back %>
<%= @user.name %>
<%= link_to_if(can?(:update,@user), "Edit", edit_user_path(@user)) %> | <%= link_to_if(can?(:delete, @user), "Delete", user_path(@user), :confirm => "Are you sure?", :method => :delete) {} %>
Email: | <%= @user.email %> |
Role: | <%= @user.roles.first.name %> |
Created at: | <%= @user.created_at %> |
Last Sign In: | <%= @user.last_sign_in_at %> |
Sign In Count: | <%= @user.sign_in_count %> |
if user.role? :admin can :see_timestamps, User elsif user.role? :normal can :see_timestamps, User, :id => user.id endh3. Recaptcha
class RegistrationsController < Devise::RegistrationsController def create if verify_recaptcha super else build_resource clean_up_passwords(resource) flash[:error] = "There was an error with the recaptcha code below. Please re-enter the code and click submit." render_with_scope :new end end . . . . endh3. Recaptcha with Omniauth
class RegistrationsController < Devise::RegistrationsController def create if session[:omniauth] == nil #OmniAuth if verify_recaptcha super session[:omniauth] = nil unless @user.new_record? #OmniAuth else build_resource clean_up_passwords(resource) flash[:error] = "There was an error with the recaptcha code below. Please re-enter the code and click submit." render_with_scope :new end else super session[:omniauth] = nil unless @user.new_record? #OmniAuth end endh2. login with username Customizing The Login Requirements Our application currently uses an email address and password to log users in, but if we want to change it so that it asks for a username instead of an email address then we can do so fairly easily. The first thing we need to do is create a username column in the User table, which we can do by generating a migration. $ rails generate migration add_username_to_users username:string That done we’ll run the migration. $ rake db:migrate As we only have one user in the database we can quickly log in to the Rails console and set a value for the username attribute for that user. $ rails c Loading development environment (Rails 3.0.0.beta2) ruby-1.8.7-p249 > User.first.update_attribute(:username, "eifion") => true Now that we’ve modified the database we need to modify devise’s configuration file, uncommenting the config.authentication_keys line and changing its value from :email to :username. /config/initializers/devise.rb 1. config.authentication_keys = [ :username ] config.authentication_keys = [ :username ] With this value set devise will use the username field as the authentication key. We’ll also have to modify the sign in form so that it has a username field instead of the email field. /app/views/devise/sessions/new.html.erb 1. <% title "Sign In" %> 2. 3. <%= form_for(resource_name, resource, :url => session_path(resource_name)) do |f| %> 4.