Auth mechanism in Camel

HTTP is a stateless protocol which means each time a client retrieves a Web page, the client opens a separate connection to the web server.

Still there are following three ways to maintain session between weh client and web server. I will have a brief introduction to these ways to maintain session. And then I will introduce the implementation in my light weight pure Web server – Camel.

Cookies:

A web server can assign a set of key/value to each web client and the server can use these values to track session. For example, in Perl,

set cookie with Perl CGI module:

print "Set-Cookie: user=luohua; domain=abc.def.com; " .
"path=/cgi-bin/report.pl; expires=Mon, 12-Aug-2013 12:00:00 GMT\n";
print "Set-Cookie: passwd=xxxxxxxxxxx; domain=abc.def.com; " .
"path=/cgi-bin/report.pl; expires=Mon, 12-Aug-2013 12:00:00 GMT\n\n";

Check cookie with Perl CGI module:

@cookie = split(/; /, $ENV{'HTTP_COOKIE'});
foreach(@cookie)
{
($cookiename,$cookievalue) = split(/=/,$_) ;

}

The good point is, cookie is very convenient to maintain sessions through the site. However the weak point is that, some of the browsers/clients don’t support cookies or allow cookies.

Hidden Form Fields:

A web server can send a hidden HTML form with its required value:


<p><input type="hidden" name="username" value="Luohua"></p>
 <p><input type="hidden" name="password" value="xxxxxxxxx"></p>

These two entries mean that, when the form is submitted, the specified names and values are automatically included in the GET or POST method.

For example,

POST /firstapp/test.pl HTTP/1.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Host: localhost:8080
Referer: http://localhost:8080/firstapp/login.html
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Content-Length: 37
Content-Type: application/x-www-form-urlencoded

username=luohua&password=xxxxxx&Submit=submit

The good point is, you can use Hidden Form anywhere. However the weak point is that, you have to do Form Authentication page by page.

URL Rewriting:

Also you can append your session data on the end of each URL. For example,

http://luohuahuang.com/intro.html;sessionid=123456789

Session Object:

In JEE, JSP & Servelet use session object to maintain session status. For example, in Tomcat, Catalina use Session Manager to maintain session objects. Servelet instance can getSession() from HttpServletRequest interface. In the front tier, developer can just easily use  tags like <%@ page session=”1234567 %> in their jsp pages to get/set values.

Now come back to Camel’s session mechanism.

In my design, basically I use the way of Hidden Form Fields.

1. In the auth.xml, it will define the role/account and pages privileges.


<?xml version='1.0' encoding='utf-8'?>
 <!-- Camel Web Server -->
 <!-- The contents of this file will be loaded for each web application -->
 <Auth>
 <role rolename="administrator"/>
 <role rolename="update"/>
 <role rolename="view"/>
 <user username="admin" password="admin123" roles="administrator,view,update"/>
 <user username="luhuang" password="luhuang123" roles="view, update"/>
 <user username="homer" password="simpsons" roles="view"/>
 <user username="update1" password="update123"/>
 <user username="view1" password="view123"/>
 <user username="view2" password="view123"/>

<pagecontrol>
 <page path="/firstapp/auth_admin.pl">
 <roles>administrator</roles>
 <users>luhuang</users>
 </page>
 <page path="/firstapp/auth_update.pl">
 <roles>update</roles>
 <users>luhuang</users>
 </page>
 <page path="/firstapp/auth_view.pl">
 <roles>update</roles>
 <users>view1,view2</users>
 </page>
 <page path="/firstapp/welcome.html">
 <roles>update</roles>
 <users>view1,view2</users>
 </page>
 </pagecontrol>
 </Auth>

When a request comes, it will check the page privilege.

If the page is a public page (not indicated in the auth.xml), then authentication pass.

If the page is under prvs control and the user has logged in, then authentication pass.

If the page is under prvs control and the user has not yet log in, then redirect to login portal.

If username or password is wrong, then authentication fail.

If user has no privs to access the page, then authentication fail.

The main logic:

sub authAgent{
	my ($username, $password, $page) = @_;
	if (camelxml::isPageAuth($page)){
		if($username){
			my $pwd = camelxml::getPasswordByUsername($username);
			if ($pwd){
				if ($pwd eq $password) {
					if (camelxml::getAuthByUsernameAndPage($username, $page)){
						return AUTH;
					} else {
						return NOPRVS;
					}
				} else {
					return WRGACNT;
				}
			} else {
				return WRGACNT;
			}

		} else {
			return UNAUTH;
		}
	} else {
		return PUB;
	}
}

In the login portal, it will inherit the original url and redirect back if the authentication passes.

use strict;
use camelcgi;

my $cgiinst = camelcgi->new(PARAMS => $ARGV[0]);

print <<END_HTML;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Camel Server - Login Form</title>
<style type="text/css">
img {border: none;}
</style>
</head>
<body>
<p>*** Camel Server - Applications Login Portal ***</p>
<p>You are going to $cgiinst->{originaluri}. $cgiinst->{message}</p>
<form action="$cgiinst->{originaluri}" method="post">
<p>User name: &nbsp;&nbsp;
<input type="text" name="username" value="apps"/></p>
<p>Password: &nbsp;&nbsp;&nbsp;&nbsp;
<input type="password" name="password" /></p>
<p><input type="submit" name="Submit" value="Submit" /></p>
</form>
</body>
</html>
END_HTML

In the form, it will use Camel’s CGI to pass its original url automatically. More details about Camel CGI: Tricky way to instantiate a class in Perl

At last, it will pass the values in the way of Hidden form:


<form action="/firstapp/auth_update.pl" method="post">
 <p>Result: $test </p>
 <p><input type="text" name="test" value=""></p>
 <p><input type="hidden" name="username" value="$cgiinst->{username}"></p>
 <p><input type="hidden" name="password" value="$cgiinst->{password}"></p>
 <p><input type="submit" name="Submit" value="Submit" /></p>
 </form>

In this way, Camel can track client’s session information.

Let me conclude Camel’s session process here:

1. Define the account/roles and pages prvs in auth.xml. Camel session mechanism will help you track prvs.

2. If you want to pass username/password to other pages, then just inject below two lines in your form:


<p><input type="hidden" name="username" value="$cgiinst->{username}"></p>
 <p><input type="hidden" name="password" value="$cgiinst->{password}"></p>

# So light weight! 🙂