Thursday, December 19, 2013

Use Jetty to Add Login Passwords to a Web Application

Introduction

When I used Jetty 9.1 to serve my JSP application I was very annoyed that it was such a struggle to add password protection to the site.  I could not find any suitable examples, and the security documentation was confusing, obsolete, or incomplete.  By publishing what I did as an example, I hope to spare others the pain that I experienced.

My application is fairly straightforward.  The web site was built for data entry and retrieval for a local school district.  There is public index.html home page with links to the private index.jsp pages for each of the schools.  Each school has its own unique login and password, and each school can only view its own data.  A set of administrator pages has its own login; the administrator can also access the pages for each of the schools.  When a user first attempts to visit one of the protected pages, a dialog box asks for a user name and password:



The diagram below shows the layout of the important directories and files in the application.  Directories are blue, the web pages are white, and the files that need to be modified to add password protection are orange.



Define Login Service

First, define a login service in etc/jetty.xml.  The lines that I added are in bold.
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
    ...
    <Call name="addBean">
      <Arg>
        <New class="org.eclipse.jetty.security.HashLoginService">
          <Set name="name">Local School Data Entry</Set>
          <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
          <Set name="refreshInterval">0</Set>
        </New>
      </Arg>
   </Call>

</Configure>

Generate Encrypted Passwords

Jetty provides a command line utility to generate encrypted passwords.  To launch it, run this command from the root of your jetty installation, where xxx is replaced by the version number of your Jetty installation (in my case 9.1.0.v20131115):
java -cp lib/jetty-util-xxx.jar org.eclipse.jetty.util.security.Password
The command line utility replies with a message telling you that you need to supply additional information, namely a user name and a password.

Usage - java org.eclipse.jetty.security.Password [<user>] <password>If the password is ?, the user will be prompted for the password
Here I create passwords for school1, school2, and admin.  The commands that I typed are in bold.
java -cp lib/jetty-util-9.1.0.v20131115.jar org.eclipse.jetty.util.security.Password school1 pw1
pw1
OBF:1l1a20731kxs
MD5:6e6fdf956d04289354dcf1619e28fe77
CRYPT:scp3ZgA5Pj7Jw
java -cp lib/jetty-util-9.1.0.v20131115.jar org.eclipse.jetty.util.security.Password school2 pw2
pw2
OBF:1l8d20731l4x
MD5:6d5779b9b85bd4f11e44c9772e0de602
CRYPT:scMnwamsTauQY
java -cp lib/jetty-util-9.1.0.v20131115.jar org.eclipse.jetty.util.security.Password admin pwa
pwa

OBF:1uha20731ugg
MD5:07ba8a9247a0f91e025d8036a28c3b56
CRYPT:adQdI3DrVe70M

The Password utility prints the transformed password in various forms.  I used the CRYPT version and ignored the rest.

Add Passwords to Properties File

I added the passwords to the file etc/realm.properties under the root of the Jetty installation.  Jetty wants me to specify a role for each user, so I invented the roles School1_User, School2_User, and Admin_User:
school1: CRYPT:scp3ZgA5Pj7Jw,School1_User
school2: CRYPT:scMnwamsTauQY,School2_User
admin: CRYPT:adQdI3DrVe70M,Admin_User

Specify URLS With Security Constraints

My web application has one public page and a number of other pages that are password protected.  To specify which pages are password protected and who may access them, you'll edit your application's WEB-INF/web.xml file.

Note that the protected URLs are specified via a url-pattern.  The asterisk in the Admin pattern indicates that all web pages under Admin/ are protected.

The auth-constraint says who is authorized to view web pages with a particular security constraint.  Note that Admin_User may access the School1 and School2 pages as well as the Admin pages.  School1_User may only access School1 pages, and School2_User may only access School2's pages.
 <!-- Specify some URLs that have security constraints -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Local School Admin</web-resource-name>
            <url-pattern>/Admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>Admin_User</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>School1 Data Entry</web-resource-name>
            <url-pattern>/School1/index.jsp</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>School1_User</role-name>
            <role-name>Admin_User</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>School2 Data Entry</web-resource-name>
            <url-pattern>/School2/index.jsp</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>School2_User</role-name>
            <role-name>Admin_User</role-name>
        </auth-constraint>
    </security-constraint>

Specify the Authentication Method

The last thing to do is to specify the authentication method that Jetty will use to protect the web site.  This information is also specified in your application's WEB-INF/web.xml file.  BASIC authentication gives us the login dialog for free.  The realm-name must be the same as the name used in the section titled Define Login Service.
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Local School Data Entry</realm-name>
    </login-config>

No Logout

One of the limitations of the BASIC authentication method is that there is no way for users to log out.  Once a user is logged in, attempting to visit a page that is protected by a different login and password results in an error page rather than a login dialog.  To login in as a different user requires that a user first close the current browser window and open a new one.


HTTP ERROR 403
Problem accessing /myapp/School1/index.jsp. Reason:
!role

Powered by Jetty://