Added two new views: user/login and user/passreq.
authorNguyễn Thái Ngọc Duy <[email protected]>
Thu, 6 Apr 2006 11:03:44 +0000 (6 18:03 +0700)
committerNguyễn Thái Ngọc Duy <[email protected]>
Thu, 6 Apr 2006 11:03:44 +0000 (6 18:03 +0700)
Added a new helper AuthenticatedSystem, used by application.rb to check authentication
Modified main.rhtml a bit because now we have a working auth system.

app/controllers/application.rb
app/controllers/user_controller.rb
app/models/user.rb
app/views/layouts/main.rhtml
app/views/user/login.rhtml [new file with mode: 0644]
app/views/user/new.rhtml
app/views/user/passreq.rhtml [new file with mode: 0644]
lib/authenticated_system.rb [new file with mode: 0644]
lib/authenticated_test_helper.rb [new file with mode: 0644]

index 537de40..bfaf0af 100644 (file)
@@ -1,4 +1,6 @@
 # Filters added to this controller will be run for all controllers in the application.
 # Likewise, all the methods added will be available for all controllers.
 class ApplicationController < ActionController::Base
-end
\ No newline at end of file
+  include AuthenticatedSystem
+  before_filter :logged_in?
+end
index c3a4f6e..0bf77fe 100644 (file)
@@ -6,10 +6,6 @@ class UserController < ApplicationController
       redirect_to :action => "rules"
    end
 
-   def rules
-      redirect_to :action => "rules"
-   end
-
    def list
       @users = User.find :all
    end
@@ -34,18 +30,6 @@ class UserController < ApplicationController
       @user = User.new(user_obj)
       @user.username = user_obj[:username]
       if @user.save
-         flash[:notice] = "User registered"
-        redirect_to :controller => "forum"
-      else
-         render :action => "new"
-      end
-   end
-
-   def create
-      user_obj = @params[:user]
-      @user = User.new(user_obj)
-      @user.username = user_obj[:username]
-      if @user.save
          @flash[:notice] = "User registered"
         redirect_to :controller => "forum", :action => "list"
       else
@@ -54,12 +38,17 @@ class UserController < ApplicationController
    end
 
    def logout
-      @flash[:notice] = "Logged out"
-      redirect_to :controller => "forum", :action => "list"
+      self.current_user = nil
+      flash[:notice] = "You have been logged out."
+      redirect_back_or_default :controller => "forum", :action => "list"
    end
 
    def login
-      @flash[:notice] = "Logged in"
-      redirect_to :controller => "forum", :action => "list"
+     return unless request.post?
+     self.current_user = User.authenticate(params[:username], params[:password])
+     if current_user
+        flash[:notice] = "Logged in successfully"
+       redirect_back_or_default :controller => "forum", :action => "list"
+     end
    end
 end
index a58c627..7b449e9 100644 (file)
@@ -1,9 +1,48 @@
 class User < ActiveRecord::Base
-   validates_presence_of :username, :email
-   validates_uniqueness_of :username, :email
-   validates_length_of :username, :minimum => 4
-   validates_length_of :password, :minimum => 6
-   validates_confirmation_of :password, :email
+   # Virtual attribute for the unencrypted password
+   attr_accessor :passwd
+
+   validates_presence_of :username
+   validates_length_of :username, :within => 3..40
+
+   validates_presence_of :passwd, :passwd_confirmation
+   validates_length_of :passwd, :within => 5..40
+   validates_confirmation_of :passwd
+   before_save :encrypt_password
+
+   validates_presence_of :email, :email_confirmation
+   validates_length_of :email,    :within => 3..100
+   validates_confirmation_of :email
    validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/
+
+   validates_uniqueness_of :username, :email
    attr_protected :group_id, :username
+
+   # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
+   def self.authenticate(username, passwd)
+     u = find_by_username(username) # need to get the salt
+     u && u.authenticated?(passwd) ? u : nil
+   end
+
+   # Encrypts some data with the salt.
+   def self.encrypt(passwd, salt)
+     Digest::SHA1.hexdigest("--#{salt}--#{passwd}--")
+   end
+
+   # Encrypts the password with the user salt
+   def encrypt(passwd)
+     self.class.encrypt(passwd, salt)
+   end
+
+   def authenticated?(passwd)
+     password == encrypt(passwd)
+   end
+
+   protected
+   # before filter 
+   def encrypt_password
+     return if passwd.blank?
+     self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{username}--") if new_record?
+     self.password = encrypt(passwd)
+   end
 end
index 6f23ef5..9fb0f8c 100644 (file)
                        <li id="navindex"><%= link_to "User list", :controller => "user", :action => "list" %>
                        <li id="navindex"><%= link_to "Rules", :controller => "user", :action => "list" %>
                        <li id="navindex"><%= link_to "Search", :controller => "search" %>
-                       <li id="navindex"><%= link_to "Register", :controller => "user", :action => "register" %>
-                       <li id="navindex"><%= link_to "Login", :controller => "user", :action => "login" %>
+<% if @current_user %>
                        <li id="navindex"><%= link_to "Profile", :controller => "user", :action => "profile" %>
                        <li id="navindex"><%= link_to "Logout", :controller => "user", :action => "logout" %>
+<% else %>
+                       <li id="navindex"><%= link_to "Register", :controller => "user", :action => "register" %>
+                       <li id="navindex"><%= link_to "Login", :controller => "user", :action => "login" %>
+<% end %>
                </div>
                <!--pun_status-->
                <div id="brdwelcome" class="inbox">
diff --git a/app/views/user/login.rhtml b/app/views/user/login.rhtml
new file mode 100644 (file)
index 0000000..9bc65f8
--- /dev/null
@@ -0,0 +1,22 @@
+<div class="blockform">
+       <h2><span>Login</span></h2>
+       <div class="box">
+               <form id="login" method="post" action="<%= url_for :controller => "user", :action => "login" %>" onsubmit="return process_form(this)">
+                       <div class="inform">
+                               <fieldset>
+                                       <legend>Login legend</legend>
+                                               <div class="infldset">
+                                                       <input type="hidden" name="form_sent" value="1" />
+                                                       <input type="hidden" name="redirect_url" value="<?php echo $redirect_url ?>" />
+                                                       <label class="conl"><strong>Username</strong><br /><input type="text" name="username" size="25" maxlength="25" tabindex="1" /><br /></label>
+                                                       <label class="conl"><strong>Password</strong><br /><input type="password" name="password" size="16" maxlength="16" tabindex="2" /><br /></label>
+                                                       <p class="clearb">Login info</p>
+                                                       <p><%= link_to "Not registered", :controller => "user", :action => "register" %>&nbsp;&nbsp;
+                                                       <%= link_to "Forgotten pass", :controller => "user", :action => "passreq" %></p>
+                                               </div>
+                               </fieldset>
+                       </div>
+                       <p><input type="submit" name="login" value="Login" tabindex="3" /></p>
+               </form>
+       </div>
+</div>
index 1e1106e..fcd07cd 100644 (file)
@@ -21,8 +21,8 @@
                                <fieldset>
                                        <legend>Pass legend 1</legend>
                                        <div class="infldset">
-                                               <label class="conl"><strong>Password</strong><br /><input type="password" name="user[password]" size="16" maxlength="16" /><br /></label>
-                                               <label class="conl"><strong>Confirm pass</strong><br /><input type="password" name="user[password_confirmation]" size="16" maxlength="16" /><br /></label>
+                                               <label class="conl"><strong>Password</strong><br /><input type="password" name="user[passwd]" size="16" maxlength="16" /><br /></label>
+                                               <label class="conl"><strong>Confirm pass</strong><br /><input type="password" name="user[passwd_confirmation]" size="16" maxlength="16" /><br /></label>
                                                <p class="clearb">Pass info</p>
                                        </div>
                                </fieldset>
diff --git a/app/views/user/passreq.rhtml b/app/views/user/passreq.rhtml
new file mode 100644 (file)
index 0000000..7c96ffd
--- /dev/null
@@ -0,0 +1,18 @@
+<div class="blockform">
+       <h2><span>Request pass</span></h2>
+       <div class="box">
+               <form id="request_pass" method="post" action="<%= url_for :controller => "user", :action => "login" %>" onsubmit="this.request_pass.disabled=true;if(process_form(this)){return true;}else{this.request_pass.disabled=false;return false;}">
+                       <div class="inform">
+                               <fieldset>
+                                       <legend>Request pass legend</legend>
+                                       <div class="infldset">
+                                               <input type="hidden" name="form_sent" value="1" />
+                                               <input id="req_email" type="text" name="req_email" size="50" maxlength="50" />
+                                               <p>Request pass info</p>
+                                       </div>
+                               </fieldset>
+                       </div>
+                       <p><input type="submit" name="request_pass" value="Submit" /><a href="javascript:history.go(-1)">Go back</a></p>
+               </form>
+       </div>
+</div>
diff --git a/lib/authenticated_system.rb b/lib/authenticated_system.rb
new file mode 100644 (file)
index 0000000..e9a8421
--- /dev/null
@@ -0,0 +1,113 @@
+module AuthenticatedSystem
+  protected
+  def logged_in?
+    current_user
+  end
+
+  # Accesses the current user from the session.
+  def current_user
+    @current_user ||= session[:user] ? User.find_by_id(session[:user]) : nil
+  end
+
+  # Store the given user in the session.
+  def current_user=(new_user)
+    session[:user] = new_user.nil? ? nil : new_user.id
+    @current_user = new_user
+  end
+
+  # Check if the user is authorized.
+  #
+  # Override this method in your controllers if you want to restrict access
+  # to only a few actions or if you want to check if the user
+  # has the correct rights.
+  #
+  # Example:
+  #
+  #  # only allow nonbobs
+  #  def authorize?(user)
+  #    user.login != "bob"
+  #  end
+  def authorized?(user)
+     true
+  end
+
+  # Check whether or not to protect an action.
+  #
+  # Override this method in your controllers if you only want to protect
+  # certain actions.
+  #
+  # Example:
+  #
+  #  # don't protect the login and the about method
+  #  def protect?(action)
+  #    if ['action', 'about'].include?(action)
+  #       return false
+  #    else
+  #       return true
+  #    end
+  #  end
+  def protect?(action)
+    true
+  end
+
+  # Filter method to enforce a login requirement.
+  #
+  # To require logins for all actions, use this in your controllers:
+  #
+  #   before_filter :login_required
+  #
+  # To require logins for specific actions, use this in your controllers:
+  #
+  #   before_filter :login_required, :only => [ :edit, :update ]
+  #
+  # To skip this in a subclassed controller:
+  #
+  #   skip_before_filter :login_required
+  #
+  def login_required
+    # Skip this filter if the requested action is not protected
+    return true unless protect?(action_name)
+
+    # Check if user is logged in and authorized
+    return true if logged_in? and authorized?(current_user)
+
+    # Store current location so that we can redirect back after login
+    store_location
+
+    # Call access_denied for an appropriate redirect and stop the filter
+    # chain here
+    access_denied and return false
+  end
+
+  # Redirect as appropriate when an access request fails.
+  #
+  # The default action is to redirect to the login screen.
+  #
+  # Override this method in your controllers if you want to have special
+  # behavior in case the user is not authorized
+  # to access the requested action.  For example, a popup window might
+  # simply close itself.
+  def access_denied
+    redirect_to :controller => 'user', :action => 'login'
+  end  
+
+  # Store the URI of the current request in the session.
+  #
+  # We can return to this location by calling #redirect_back_or_default.
+  def store_location
+    session[:return_to] = request.request_uri
+  end
+
+  # Redirect to the URI stored by the most recent store_location call or
+  # to the passed default.
+  def redirect_back_or_default(default)
+    session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default)
+    session[:return_to] = nil
+  end
+
+  # Inclusion hook to make #current_user and #logged_in?
+  # available as ActionView helper methods.
+  def self.included(base)
+    base.send :helper_method, :current_user, :logged_in?
+  end
+end
diff --git a/lib/authenticated_test_helper.rb b/lib/authenticated_test_helper.rb
new file mode 100644 (file)
index 0000000..eb036a6
--- /dev/null
@@ -0,0 +1,49 @@
+module AuthenticatedTestHelper
+  # Sets the current u in the session from the u fixtures.
+  def login_as(user)
+    @request.session[:user] = us(user).id
+  end
+
+  # Assert the block redirects to the login
+  # 
+  #   assert_requires_login(:bob) { get :edit, :id => 1 }
+  #
+  def assert_requires_login(user = nil, &block)
+    login_as(user) if user
+    block.call
+    assert_redirected_to :controller => 'user', :action => 'login'
+  end
+
+  # Assert the block accepts the login
+  # 
+  #   assert_accepts_login(:bob) { get :edit, :id => 1 }
+  #
+  # Accepts anonymous logins:
+  #
+  #   assert_accepts_login { get :list }
+  #
+  def assert_accepts_login(user = nil, &block)
+    login_as(user) if user
+    block.call
+    assert_response :success
+  end
+
+  # http://project.ioni.st/post/217#post-217
+  #
+  #  def test_new_publication
+  #    assert_difference(Publication, :count) do
+  #      post :create, :publication => {...}
+  #      # ...
+  #    end
+  #  end
+  # 
+  def assert_difference(object, method = nil, difference = 1)
+    initial_value = object.send(method)
+    yield
+    assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
+  end
+
+  def assert_no_difference(object, method, &block)
+    assert_difference object, method, 0, &block
+  end
+end