Alkasai says:

Here is how i did it. This is the OLD code in application_controller.rb:


def  authorize
        unless  User.find_by_id(session[:user_id]) 
            session[:original_uri]  =  request.request_uri
            #flash[:notice]  =  "You're loged in as #{params[:name]}" 
            redirect_to  :controller  =>  'admin' ,  :action  =>  'login'
        end
    end

And this is the NEW code in application_controller.rb:


def  authorize
        if User.count == 0 
            if request.path_parameters[:controller] == 'users' and request.path_parameters[:action] == 'create'
                #do nothing. let the users controoler verify that everything is correct 
            elsif !(request.path_parameters[:controller] == 'users' and request.path_parameters[:action] == 'new')
                flash[:notice] = "Please create Admin User" 
                redirect_to :controller => 'users' , :action => 'new'                
            end
        elsif  !User.find_by_id(session[:user_id])
            session[:original_uri]  =  request.request_uri
            redirect_to  :controller  =>  'admin' ,  :action  =>  'login'
        end
    end

This should redirect all the pages to ’/users/new’ (except the ’/store’ of cause), when there are no users in DB. However, only page that is not redirected for some reason is ’/admin/login’. Anybody knows why?

unknown

This is a quick and dirty solution, just creates a user with the given credentials when no user exists in the database. In login_controller.rb:


def login
  session[:user_id] = nil

  if request.post?
    if User.count.zero?
      user = User.create(:name => params[:name],
                         :password => params[:password],
                         :password_confirmation => params[:password])
    else
      user = User.authenticate(params[:name], params[:password])
    end

    if user
      session[:user_id] = user.id
      uri = session[:original_uri]
      session[:original_uri] = nil
      redirect_to(uri || { :action => "index" })
    else
      flash[:notice] = "Invalid user/password combination" 
    end
  end
end

Cosmin Lehene:

This will pass over authorize method like a valid login as long as no users are created in the database and will flash a warning notice permanently. After the first user is created it will ask for credentials.

In application.rb change the authorize method:

def authorize
  unless User.find_by_id(session[:user_id]) or User.count == 0
    session[:original_uri] = request.request_uri
    flash[:notice] = "Please login" 
    redirect_to(:controller => "login", :action => "login")
  end

  if User.count == 0 
    flash[:notice] = "Please create the first user account" 
  end
end
end

Of course, in order to get the required behavior, you have to modify the login action in login_controller.rb also:


def login
  session[:user_id] = nil
  if User.count == 0 
    redirect_to(:action => "add_user")
  elsif 
  [...]

k9d says:

I followed Cosmin’s example except I wanted the user to be redirect to the page where they can create the first user account. To achieve this I modified def authorize in application.rb to look more like this


#...
if User.count == 0 
  flash[:notice] = "Please create the first user account" 
  redirect_to(:controller => "login", :action => "add_user")
end
#...

All is well except the before_filter in login_controller.rb doesn’t allow the un-authorized user to see add_user. Is there a way to work around this problem?

Also, a handy mysql command if you need to manually clear out the last user, to Delete a row(s) from a table>


DELETE from [table name] where [field name] = 'whatever';

Matt says:

To allow an unauthorised user to get to the add_user page, check the path_parameters of the request:

def authorize
  unless User.find_by_id(session[:user_id]) or User.count == 0
    session[:original_uri] = request.request_uri
    flash[:notice] = "Please log in." 
    redirect_to(:controller=>"login", :action=>"login")
  end
  if User.count == 0 
    if request.path_parameters[:action]=="add_user" and request.path_parameters[:controller]=="login" 
      #As we are already on our way to the add_user action, do nothing here.
    else
      flash[:notice] = "Please create an account." 
      redirect_to(:controller=>"login", :action=>"add_user")
    end
  end
end

jwat says:

Matt’s code worked for me. However, as good programming practice, it makes more sense to write the logic for zero user counts as so:


def authorize
        unless User.find_by_id(session[:user_id]) or User.count == 0
            session[:original_uri] = request.request_uri
            flash[:notice] = "Please log in." 
            redirect_to(:controller=>"login", :action=>"login")
        end
        if (User.count == 0 and request.path_parameters['action'] != "add_user")
                flash[:notice] = "Please create an account." 
                redirect_to(:controller=>"login", :action=>"add_user")
        end
end

I suppose you can check for the controller params as well, but I didn’t bother.

h3. Dimitris says: All you need to do is go to application.rb and change the check in the authorize method to:

    unless User.find_by_id(session[:user_id]) || User.count == 0
In this way, authorization is bypassed if there are no users in the DB. As soon as you create a user, you’re asked to login and everything works as before. h3. Valerie says: Nice solution, Dimitris! I also added a flash notice, as Cosmin mentioned.

  def authorize
    flash[:notice] = "Please create an admin user" if User.count == 0
    unless User.find_by_id(session[:user_id]) || User.count == 0
      session[:original_uri] = request.request_uri
      flash[:notice] = "Please log in" 
      redirect_to :controller => 'admin', :action => 'login'
    end
  end

Grazybom

Here is my simple solution: admin_controller.rb


  def login
    if request.post?
      user = User.authenticate(params[:name], params[:password])
      if user
        session[:user_id] = user.id
        uri = session[:original_uri]
        session[:original_uri] = nil
        redirect_to(uri || { :action => "index" })
      elsif User.count.zero?
        flash[:notice] = "Create your first administrator account. Please, logout after you have created your new account and login with your new credentials." 
        redirect_to(:controller => "users", :action => "new")  
      else
        flash.now[:notice] = "Invalid user/password combination" 
      end
    end
  end

and application.rb


  def authorize
    unless User.find_by_id(session[:user_id]) or User.count.zero?
      session[:original_uri] = request.request_uri
      flash[:notice] = "Please log in" 
      redirect_to :controller => 'admin', :action => 'login'
    end
  end

Even though, each action is available via get request untill the first account is created.

m1kee

I didn’t like how Grazybom’s solution allowed full access to the entire site as long as there wasn’t any user account created.

Here’s my solution:

app/controllers/application.rb

def authorize
  unless User.find_by_id(session[:user_id]) || User.count.zero?
    session[:original_uri] = request.request_uri
    flash[:notice] = "Please log in" 
    redirect_to(:controller => 'admin', :action => 'login')
  end

  if (User.count.zero?)
    if !(request.path_parameters[:controller] == 'users' and request.path_parameters[:action] == 'new')
      if !(request.path_parameters[:controller] == 'users' and request.path_parameters[:action] == 'create')
        redirect_to(:controller => 'admin', :action => 'login')
      end
    end
  end
end
#...
app/controllers/admin_controller.rb

class AdminController < ApplicationController
  def login
    if User.count.zero?
      redirect_to(:controller => 'users', :action => 'new')
    end
#...

As an addition, to ensure user account creation:

app/controllers/store_controller.rb

class StoreController < ApplicationController
  before_filter :find_cart, :except => :empty_cart

  def index
    if (User.count.zero?)
      redirect_to(:controller => 'admin', :action => 'login')
    end
#...

AndriySand

Hi! I resolved this problem this way. Now I work with Rails 1.8.02 First I commented the method after_destroy in order be able to delete last user.

# def after_destroy
   # if User.count.zero?
     # raise "Can't delete last user" 
   # end
 # end      
Then I wrote ‘except ‘ in ‘login_ controller’ this way in order be able to add user to non-administrator person only once.

before_filter :authorize,:except =>  if User.count == 0 
:add_user 
else 
:login
 end 
After I added couple strings of code in ‘authorize’ method in application.rb and this method looks like this

private

  def authorize
    if  User.count == 0  
      flash[:notice] = "Please create first admin" 
      redirect_to(:controller => "login", :action => "add_user")
  else
unless User.find_by_id(session[:user_id])
      flash[:notice] = "Please log in" 
      redirect_to(:controller => "login", :action => "login")    
end
end
end
end
Good luck to every one!

Pablo says

I’ve solved this problem in very few lines


class ApplicationController < ActionController::Base
  layout "store" 
  before_filter :create_first_admin
  before_filter :authorize, :except => :login
...
    protected
    def create_first_admin
        flash[:notice] = %q{There are currently no users in the database.
        Please create one administrator user} if User.count == 0

        ignore1 = (controller_name+action_name != "usersnew")
        ignore2 = !request.post? || controller_name != "users" 

        if User.count == 0 && ignore1 && ignore2
            session[:user_id] = nil;
            redirect_to :controller => :users, :action => :new
        end 
    end
...
end

Only problem is, I didn’t know how to use the :except option in before_filter so that I can also say that the users controller must be ignored and hence I did it on my own in the function (this is the only part I don’t really like).

I decided that not even the store should be available when there are any user in the database. What is the point of browsing products, adding stuff in the cart, check it out when there is no one that will take your order?

illbzo1 says

Reading through a few of the methods shown above, here’s what I came up with.

First, I add a skip_before_filter method to :users_controller. We don’t want to be redirected while accessing the :new method, or while using it to :create a user.


skip_before_filter :authorize, :only => [:new, :create]

Next we update application_controller. Using an unless/or combination, we can check for either a logged in user, or an absence of users, then apply methods depending on that result.

def authorize
  unless User.find_by_id(session[:user_id]) or User.count.zero?
    redirect_to login_url, :notice => "Please log in" 
  end
  if User.count.zero?
    redirect_to new_user_path, :notice => "Please create a user" 
  end
end
end

MarkB says

I solved this similarly to illbzo1 above. I added


skip_before_filter :authorize, :only => [:new, :create]

to the top of users_controller.rb so that I didn’t get a redirect loop and added the below code to the respond_to block of the create method after the save of the record which checks if there is now only one user in the database and if so, sets the session id to to be that user ID so that the authorize filter will allow the redirect.


        if User.count == 1
          session[:user_id] = User.find_by_name(@user.name).id
        end

I then altered the authorize method in application_controller.rb as below to check for an empty User table and redirect if there is.


  def authorize
    if User.count.zero?
      redirect_to new_user_path, notice: "Please create the first user account" 
    else
      unless User.find_by_id(session[:user_id])
        redirect_to login_url, notice: "Please log in" 
      end
    end
  end

John Lane says

I did this by just creating a user if none exists. The user is created with whatever is entered in the login box:


class SessionsController < ApplicationController
  skip_before_filter :authorize
  def new
  end

  def create
    if User.count == 0
      user = User.new(name: params[:name],
                      password_digest: BCrypt::Password.create(params[:password]))

      user.save
    else
      user = User.find_by_name(params[:name])
    end
    if user and user.authenticate(params[:password]) 
      session[:user_id] = user.id
      redirect_to admin_url
    else
      redirect_to login_url, alert: "Invalid user/password combination" 
    end
  end

  def destroy
    session[:user_id] = nil
    redirect_to store_url, notice: "Logged out" 
  end
end