Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-3 of 3 total  RSS 

Overriding Tomcat Valve to return extended login failure status

See Shade Grown Code for more information.

ExtendedStatusSetter.java
package com.ofc.tomcat;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Interface flagging that the implementing Realm can set request
 * headers providing additional information about an authentication
 * failure.
 *
 * @author Nicholas Sushkin
 */
public interface ExtendedStatusSetter
{

    /**
     * The request attribute under which we forward an extended failure status message
     * (as an object of type String) to a login error page.
     */
    public static String LOGIN_FAILURE_MESSAGE_ATTR = 
        "com.ofc.tomcat.LOGIN_FAILURE_MESSAGE";
    
    public void setExtendedStatus(String username, HttpServletRequest request, HttpServletResponse response);
}


ExtendedStatusFormAuthenticator.java
package com.ofc.tomcat;

import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.Realm;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.RequestDispatcher;

/**
 * Adds extended authentication failure status to tomcat FormAuthenticator.
 *
 * @author Nicholas Sushkin
 */
public class ExtendedStatusFormAuthenticator extends FormAuthenticator
{
    /**
     * Descriptive information about this implementation.
     */
    protected static final String info =
        "com.ofc.tomcat.ExtendedStatusFormAuthenticator/1.0";

    private static Log log = LogFactory.getLog(ExtendedStatusFormAuthenticator.class);

    // ------------------------------------------------------------- Properties
    /**
     * Return descriptive information about this Valve implementation.
     */
    @Override
    public String getInfo() 
    {
        return info;
    }

    // ------------------------------------------------------------- Overridden behavior
    /**
     * Called to forward to the error page
     * 
     * @param request Request we are processing
     * @param response Response we are creating
     * @param config    Login configuration describing how authentication
     *              should be performed
     */
    @Override
    protected void forwardToErrorPage(Request request, Response response, LoginConfig config) 
    {
        Realm realm = context.getRealm();

        if (realm instanceof ExtendedStatusSetter)
        {
            log.debug("realm implements ExtendedStatusSetter, setting extended status for error page");
            String username = request.getParameter(Constants.FORM_USERNAME);
            ((ExtendedStatusSetter) realm).setExtendedStatus(username, request.getRequest(), response.getResponse());
        }
        else
        {
            log.debug("realm does not implement ExtendedStatusSetter, NOT setting extended status for error page");
        }

        RequestDispatcher disp =
            context.getServletContext().getRequestDispatcher
            (config.getErrorPage());
        try {
            disp.forward(request.getRequest(), response.getResponse());
            response.finishResponse();
        } catch (Throwable t) {
            log.warn("Unexpected error forwarding to error page", t);
        }
    }
}


Realm implementation will include the following
public class AccountLockoutDatasourceRealm extends DataSourceRealm implements ExtendedStatusSetter
{
    // ...

    public void setExtendedStatus(String username, HttpServletRequest request, HttpServletResponse response)
    {
        setMessage(request, "Account locked");
    }

    protected void setMessage(HttpServletRequest request, String message)
    {
        request.setAttribute(ExtendedStatusSetter.LOGIN_FAILURE_MESSAGE_ATTR, message);
    }
}

WWW-Authenticate example

// Shows how to use the WWW-Authenticate header to make login pages.You find a good tutorial at php.net
// (Source: http://codedump.jonasjohn.de/ - Public domain)

<?php

$login_successful = false;

// check user & pwd:
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])){

    $usr = $_SERVER['PHP_AUTH_USER'];
    $pwd = $_SERVER['PHP_AUTH_PW'];

    if ($usr == 'jonas' && $pwd == 'secret'){
        $login_successful = true;
    }
}

// login ok?
if (!$login_successful){

    // send 401 headers:
    // realm="something" will be shown in the login box 
    header('WWW-Authenticate: Basic realm="Secret page"');
    header('HTTP/1.0 401 Unauthorized');
    print "Login failed!\n";

}
else {
    // show secret page:
    print 'you reached the secret page!';
}
?>

Auto-login

Auto-login: http://www.onrails.org/articles/2006/02/18/auto-login

Posted by Daniel Wanja Sat, 18 Feb 2006 21:41:00 GMT

One of my midnight Rails projects is a “time tracking� application for which I needed auto-login. You know, the “Remember me� check box so that you don’t have to login each time you visit the application. I found a nice article written by Matt McCray describing how this was implemented for TaskThis.com at http://www.mattmccray.com/archives/category/software/rails/taskthis/. Even further he provides the full source code for the application. I didn’t take directly his auto_login.rb module but was greatly inspired by it. I also used the Login Engine Plugin that was not providing this feature, maybe this changed, so it could be simpler, but how simple implementing the auto-login can be. Note these are not the full classes just pertinent code extracts.

1. Remember me

When the user login and checks the “Remember me� checkbox, the :save_login parameter is set, the User instance remember_me method invoked and the :auth_token cookie set.
class AccountController < ApplicationController
  def login
    case @request.method
      when :post
      if @session[:user] = User.authenticate(@params[:user_login], @params[:user_password])
        flash['notice']  = "Login successful"
        if @params[:save_login] == "1"
          @session[:user].remember_me
          cookies[:auth_token] = { :value => @session[:user].remember_token , :expires => @session[:user].remember_token_expires }
        end
        redirect_back_or_default :controller => "time"
      else
        flash.now['notice']  = "Login unsuccessful"
        @login = @params[:user_login]
      end
    end
  end

  def logout
    @session[:user].forget_me if @session[:user]
    @session[:user] = nil
    cookies.delete :auth_token
  end
end

2. login_from_cookie

The next time the user visits the website the “login_from_cookie� filter is triggered. This method checks that the user is not logged in and that the :auth_token cookie is set. If that’s the case the user matching the :auth_token is searched and the token_expiration verified the the user is automatically logged in. Et voila! I guess auto_login would be more appropriate as method name.
class ApplicationController < ActionController::Base
   before_filter :login_from_cookie
   def login_from_cookie
      return unless cookies[:auth_token] && @session[:user].nil?
      user = User.find_by_remember_token(cookies[:auth_token]) 
      if user && !user.remember_token_expires.nil? && Time.now < user.remember_token_expires 
         @session[:user] = user
      end
   end
end

3. the User class

The User class has two methods to set and remove the token from the database. It’s pretty secure as from the token the user cannot be identified without having the salt, the email, and the token expiration, which is most unlikely to be recreated. It could be even more secure by just encrypting some random unique identifier. The only issue I encountered was that the user class always forces the password validation and encryption when saving. For now I just bypass validation and encryption when setting and clearing the remember_me token.
class User < ActiveRecord::Base
  def remember_me
    self.remember_token_expires = 2.weeks.from_now
    self.remember_token = Digest::SHA1.hexdigest("#{salt}--#{self.email}--#{self.remember_token_expires}")
    self.password = ""  # This bypasses password encryption, thus leaving password intact
    self.save_with_validation(false)
  end

  def forget_me
    self.remember_token_expires = nil
    self.remember_token = nil
    self.password = ""  # This bypasses password encryption, thus leaving password intact
    self.save_with_validation(false)
  end
end

« Newer Snippets
Older Snippets »
Showing 1-3 of 3 total  RSS