Showing posts with label jetty. Show all posts
Showing posts with label jetty. Show all posts

Friday, July 16, 2010

JEE Authentication: Login Errors, Roles, Access Denied, and Logout

In a previous post around LDAP authentication using Jetty, I had some unfinished business. Here I'll deal with login errors, restrict the login to a given role, deal with subsequent access denied scenarios, display the currently logged-in user name, and provide log-out functionality.

The web.xml I'd started with already specified a login-error page, but I was simply pointing it to the same page as the login form. This results in that page simply refreshing without any indication to the user of why that happened. What we want is some kind of message displayed that indicates the given username or password was not valid.

Now, I could simply copy the login.jsp to another JSP, say login-error.jsp, add a message to that page and alter the web.xml to specify that new page on login error:
<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>ldap</realm-name>
    <form-login-config>
        <form-login-page>/faces/login/login.jsp</form-login-page>
        <form-error-page>/faces/login/login-error.jsp</form-error-page>
    </form-login-config>
</login-config>
But now both login.jsp and login-error.jsp contain the same FORM snippet. Now if I next want to specify yet another login page to which the user is directed on an "access denied" error (which we'll deal with momentarily), and I'm still afflicted with copy-paste fever, I'll have the same login form in three places. Let's factor it out instead into a JSP snippet named loginform.jsp:
<form method=post action="j_security_check">
    <label for="j_username" style="font-weight:bold">Username</label>
    <input type="text" name="j_username" id="j_username" style="margin-left:10px"/>
    <label for="j_password" style="font-weight:bold">Password</label>
    <input type="password" name="j_password" id="j_password" style="margin-left:10px"/>
    <input type="submit" value="Log In"/>
</form>
The usual login page now looks like this:
<div style="margin-top:25px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Please Log In</h3>
    <%@include file="loginform.jsp"%>
</div>
And I'll provide an "access denied" page that looks like this:
<div style="margin-top:25px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Please Log In</h3>
    <div style="color:red;font-weight:bold;">Authentication failed. User is not in Required Role.</div>
    <%@include file="loginform.jsp"%>
</div>
Configuring HTTP-403 responses (i.e. access denied) to navigate to this page is done like so:
<error-page>
    <error-code>403</error-code>
    <location>/login/accessDenied.jsp</location>
</error-page>
Access denied problems will occur if a given user is not in the expected role. So far, the web.xml has granted authorization to all roles by virtue of the wild-card for the role-name. We can restrict that by naming a role instead:
<security-constraint>
    <web-resource-collection>
        <web-resource-name>Protected Resources</web-resource-name>
        <url-pattern>*.iface</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>
            CONFIDENTIAL
        </transport-guarantee>
    </user-data-constraint>
</security-constraint>
Now, once a user provides his/her credentials at the login form, these are first checked by the LDAP module (configured as a Jetty realm, as per the previous post); if those are valid, the user is next confirmed to be assigned the admin role. If that is the case, all is well and navigation will proceed as configured by the JSF navigation rule (again, please see the previous post). If the credentials are not valid, the user will be redirected to the login-error page, this time with an informative error message about the login problem. If the credentials are good but the user is not assigned the admin role, the user will be redirected to the access-denied page, again with a informative message.

Displaying the current username is a simple matter of leveraging the built-in getRemoteUser(), provided by HttpServletRequest. Since, after logging in, I've transitioned into a JSF application - and because I'm adverse to using JSP scriptlets to accomplish use of that getter once I'm in JSF - I simply provide a getter in one of my JSF managed beans that fetches the HTTP request and returns the user name:
public String getUserName() {
    return getServletRequest().getRemoteUser();
}
...referencing it, as usual, with the JSF expression language:

User: #{svh.userName}

Finally, I'll provide log-out functionality. First, a command link (done with the IceFaces framework):
<ice:commandLink 
    action="logout" immediate="true" value="Logout" 
    style="margin-left:5px;color:blue;font-size:medium"/>
The logout action is mapped with a navigation rule:
<navigation-rule>
    <description>Logout</description>
    <from-view-id>/*</from-view-id>
    <navigation-case>
        <from-outcome>logout</from-outcome>
        <to-view-id>/login/logout.jsp</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>
...taking us to the logout.jsp page, which invalidates the session and invites the user to log back in:
<% session.invalidate(); %>

<div style="margin-top:5px; margin-left:25%">
    <h2 style="text-decoration:underline; color:blue;margin-left:-10px">Management App</h2>
    <h3>Logout Succeeded</h3>
    <p>
        You are now logged out of the Management UI.
    </p>
    <a href="/index.jsp" style="text-decoration:underline">Return to Login page.</a>
</div>
The index.jsp redirects to the application's JSF-based home page:
<html>
    <head>
        <title>Management UI</title>
    </head>
    <body>
    <%
        String redirectURL = "./index.iface";
        response.sendRedirect(redirectURL);
    %>
    </body>
</html>
And, as mentioned in the first post, all iface resources are protected by a security constraint, so this will redirect to the login page.

Wednesday, July 14, 2010

LDAP Authentication with Jetty

If you noticed nothing else but the titles of my last two posts, you might suspect that I went down the Spring-Security road and back-pedaled to a standard JAAS approach. You would be correct.

As it turned out - to my surprise and with great disappointment - I found Spring Security to be impenetrable. Now, Spring makes a lot of things in my life easier, and in fact that's the only reason I use it. But when it becomes a tangled snarl of undocumented opaqueness, and especially when I read that even an expert in Acegi had trouble with it (see the Wrap-Up in that article), I decide to find another way.

Here are my basic building blocks for a JAAS approach with a JSF application in Jetty. First, I configure my web.xml with an authorization constraint:
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected Resources</web-resource-name>
            <url-pattern>*.iface</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>
                CONFIDENTIAL
            </transport-guarantee>
        </user-data-constraint>
    </security-constraint>
My web.xml has already mapped an IceFaces PersistentFacesServlet to *.iface, so that's what the URL pattern is about. My initial naive attempt was to use a URL pattern of /*, but that's much too broad - it will preclude e.g. loading image resources as part of your login page. That login page is also configured in the web.xml:
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>ldap</realm-name>
        <form-login-config>
            <form-login-page>/faces/login/login.jsp</form-login-page>
            <form-error-page>/faces/login/login.jsp</form-error-page>
        </form-login-config>
    </login-config>
The login page (./login/login.jsp) is dirt-simple so far. I'm only working on basic functionality at this point, and there's nothing pretty about it:
    <form method=post action="j_security_check">
        <label for="j_username">Username</label>
        <input type="text" name="j_username" id="j_username"/>
        <br/>
        <label for="j_password">Password</label>
        <input type="password" name="j_password" id="j_password"/>
        <br/>
        <input type="submit" value="Login"/>
    </form>
The realm-name portion in the web.xml references the container environment; configuring the realm is delegated to the container in JEE. My container is (embedded) Jetty, and I configure the Jetty realm via a simple entry in my jetty.xml file:
    <Call name="addUserRealm">
        <Arg>
            <New class="org.mortbay.jetty.plus.jaas.JAASUserRealm">
                <Set name="name">ldap</Set>
                <Set name="LoginModuleName">ldapmodule</Set>
            </New>
        </Arg>
    </Call>
By the way, I followed the Jetty tutorial on JAAS to make all of this happen. My previous post mentioned a gotcha in that article around the LDAP Login Module package name. Depending on which version of Jetty you're using, you may need to make the change discussed there.

In either event, the Jetty realm configuration references a LoginModuleName of "ldapmodule", and as per standard JAAS, this configuration is captured in a file (in my case, a file named ./etc/ldap.conf) referenced by the JVM argument -Djava.security.auth.login.config=etc/ldap.conf. That file is basically a replica of the ldaploginmodule example in the Jetty tutorial (again, except for the package name of the LdapLoginModule class), configured of course with the proper schema references and credentials for my LDAP environment.

Finally, I configure a navigation rule so JSF will take me to my target page after successful login:
    <navigation-rule>
        <description>After Login</description>
        <from-view-id>/login/login.jsp</from-view-id>
        <navigation-case>
            <to-view-id>/index.jsp</to-view-id>
            <redirect/>
        </navigation-case>
    </navigation-rule>
Note that this a bare-bones scaffold for authentication. I have yet to deal with login failures, roles, JSF-messaging for the user, and the like.

Tip: Package Name Correction for Jetty LdapLoginModule

If you're following the Jetty tutorial for establishing JAAS authentication, and you have copied the sample configuration file for the LDAP module, you may come across this error message after you enter your username and password on your login page:

javax.security.auth.login.LoginException: unable to find LoginModule class: org.mortbay.jetty.plus.jaas.spi.LdapLoginModule

The reason is that, at least for the version of Jetty I'm using (6.1.21), the package name has changed to org.mortbay.jetty.plus.jaas.ldap.LdapLoginModule. Make that change in the ldaploginmodule sample in that tutorial and you'll solve this problem (assuming of course you have all the needed dependencies).

For your reference, here's the complete dependency list I'm using for a JSF-based app running with embedded Jetty 6.1.21:
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-jetty</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-plus</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-ldap-jaas</artifactId>
            <version>${jettyVersion}</version>
        </dependency>

Wednesday, June 30, 2010

Followup: Basic Embedded Jetty in Cygwin

In a recent post, I described how to get a basic Jetty web application going, using the embedded approach. Since then, I've reproduced it in a Cygwin environment, and here I'll comment on that exercise.

As it turns out, specifying this type of Java startup in Cygwin (or in Windows XP per se) will not work:

java -server -Dbasedir=/usr/local/mywebapp/war -cp /usr/local/mywebapp/war/WEB-INF/lib/* com.mybiz.MyJettyWebServer

That's because the wild-card expression apparently is not supported in a DOS-based environment - even if I enclose the above classpath in quotes. Instead, I'd need to provide a semi-colon-delimited list (not colon-separated - I'm in XP) of all jars under ./WEB-INF/lib. This is not the kind of thing I'd like to do; maintaining that kind of list would be a headache as the webapp evolves. Additionally, keep in mind that I've told Jetty to start up with an exploded warfile location:

        WebAppContext appContext = new WebAppContext();
        File warPath = new File(System.getProperty("basedir"));
        appContext.setWar(warPath.getAbsolutePath());
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[]{ appContext, new DefaultHandler() });
        jetty.setHandler(handlers);
        jetty.start();

This will result in a web-level classloader to load all the jars under WEB-INF/lib, which is arguably redundant, since I'm explicitly setting my classpath to the same thing. That in turn will cause loader constraint violations when running the webapp in an IDE such as Intellij, if the run configuration you're using there points to the same classpath (since that application-level classloader loads the classes first, and then the webapp-level classloader tries to do the same thing). I'll defer solving the Intellij problem for now, and just address basic command line startup.

Given maintenance cost concerns, I'm motivated to load the minimal number of jars needed to get Jetty going, then allow it to load the rest of what it needs from WEB-INF/lib. In my particular setup, that minimal set includes my application jar and three Jetty jars:

MyApp-1.0.jar
jetty-6.1.21.jar
jetty-util-6.1.21.jar
servlet-api-2.5-20081211.jar

I figured out this minimal set by just trying to start up the WebServer class and seeing what classdef-not-found problems I had - then searching for the necessary jar by setting up a bash function that I can reuse:

findclass () { find . -name '*.jar' -o -type f |xargs -i bash -c "jar -tvf {}| tr / . | grep -i "$@" && echo {}"; }

...and subsequently invoking it like this:

findclass <dot-delimited-classname>

Once I have all my dependencies figured out, I can invoke my Jetty program with that minimal set, and rely on the webapp-level classloader to do the rest when the embedded Jetty webserver starts:

java -server -Dbasedir=/usr/local/mywebapp/war -cp "MyApp-1.0.jar;jetty-6.1.21.jar;jetty-util-6.1.21.jar;servlet-api-2.5-20081211.jar" com.mybiz.MyJettyWebServer

Note that I've wrapped the classpath in quotes, and, as mentioned, used semi-colons instead of colons.

Wednesday, June 23, 2010

Basic Embedded Jetty Setup: JSF 1.2 Webapp

Here are some basic code snippets I've used (to-date ... subject to change) to get an IceFaces-based (JSF 1.2) webapp deployed as an embedded Jetty webapp - using Maven for building. The artifacts include a configuration file, a minimal bootstrap class, the pom and the file used as the maven assembly-plugin descriptor.

The configuration file is jetty.xml, and doesn't require much. It lives under the ./etc directory of my project:

<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure id="Server" class="org.mortbay.jetty.Server">

    <Set name="ThreadPool">
        <New class="org.mortbay.thread.QueuedThreadPool">
            <!-- initial threads set to 10 -->
            <Set name="minThreads">10</Set>
            <!-- the thread pool will grow only up to 200 -->
            <Set name="maxThreads">200</Set>
            <!-- indicates that having 20 and below, the pool will be considered low on threads -->
            <Set name="lowThreads">20</Set>
            <!-- The number of queued jobs (or idle threads) needed before the thread pool is grown (or shrunk) -->
            <Set name="SpawnOrShrinkAt">2</Set>
        </New>
    </Set>

    <Call name="addConnector">
        <Arg>
            <New class="org.mortbay.jetty.nio.SelectChannelConnector">
                <!-- the ip address or domain to bind -->
                <Set name="host">
                    <SystemProperty name="jetty.host"/>
                </Set>
                <!-- the port to use/bind, defaults to 8080 if property not set -->
                <Set name="port">
                    <SystemProperty name="jetty.port" default="8080"/>
                </Set>
                <!-- the time in milliseconds when a connection is considered idle -->
                <Set name="maxIdleTime">300000</Set>
                <!-- the number of acceptors (their job is to accept the connection and dispatch to thread pool) -->
                <Set name="Acceptors">2</Set>
                <!-- should the connection statistics be turned on? (Not advisable in production) -->
                <Set name="statsOn">false</Set>
                <!-- the confidential port -->
                <Set name="confidentialPort">8443</Set>
                <!-- indicates the minimum number of connections when the server is considered low on resources -->
                <Set name="lowResourcesConnections">5000</Set>
                <!-- when low on resources, this indicates the maximum time a connection must be idle to not be closed -->
                <Set name="lowResourcesMaxIdleTime">5000</Set>
            </New>
        </Arg>
    </Call>

    <!-- Stops the server when ctrl+c is pressed (registers to Runtime.addShutdownHook) -->
    <Set name="stopAtShutdown">true</Set>
    <!-- send the server version in the response header? -->
    <Set name="sendServerVersion">true</Set>
    <!-- send the date header in the response header? -->
    <Set name="sendDateHeader">true</Set>
    <!-- allows requests(prior to shutdown) to finish gracefully -->
    <Set name="gracefulShutdown">1000</Set>

</Configure>

This configuration file is referenced by the bootstrap class, using it to configure Jetty. This class also sets the context for the webapp, points to the top-level directory of the exploded WAR content, sets a webapp context and a default context as handlers for Jetty, and starts up the webserver:

public class MyJettyWebServer {

    public static void main(String[] args) throws Exception {

        Server jetty = new Server();

        // configure Jetty by pointing to config file(s)
        String[] configFiles = { "etc/jetty.xml" };
        for (String configFile : configFiles) {
            XmlConfiguration configuration = new XmlConfiguration(new File(configFile).toURI().toURL());
            configuration.configure(jetty);
        }

        // set the context for the webapp
        WebAppContext appContext = new WebAppContext();
        appContext.setContextPath("/mycontext");

        // point to the top-level directory of the exploded WAR content
        File warPath = new File(System.getProperty("basedir"));
        appContext.setWar(warPath.getAbsolutePath());

        // set a webapp context and a default context as handlers for Jetty
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[]{ appContext, new DefaultHandler() });
        jetty.setHandler(handlers);

        // start up the webserver
        jetty.start();
    }
}


The pom specifies IceFaces 1.8.2, being careful to exclude the EL API jar wherever that's brought in transitively, as per http://www.icefaces.org/docs/v1_8_0/htmlguide/devguide/appendixA.html; and it specifies Jetty and Log4J artifacts. Note that JSP support is explicitly specified:

    ....
    <!-- use JAR packaging for embedded Jetty -->
    <packaging>jar</packaging>
    ....
    <dependencies>
        <dependency>
        <dependency>
            <groupId>org.icefaces</groupId>
            <artifactId>icefaces</artifactId>
            <version>1.8.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.icefaces</groupId>
            <artifactId>icefaces-comps</artifactId>
            <version>1.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.icefaces</groupId>
            <artifactId>icefaces-facelets</artifactId>
            <version>1.8.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>1.2_12</version>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>1.2_12</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>6.1.21</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>6.1.21</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-jetty</artifactId>
            <version>6.1.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4jVersion}</version>
        </dependency>
    </dependencies>
</project>

The assembly-plugin descriptor has a few things worth mentioning. In the dependency sets, I lay down my dependencies in the standard webapp location, ./WEB-INF/lib:

        <dependencySet>
            <unpack>false</unpack>
            <scope>runtime</scope>
            <outputDirectory>war/WEB-INF/lib</outputDirectory>
        </dependencySet>

I also specify several destinations for my dev-time files to deal with Jetty-related stuff:

        <!--
        Jetty deployment: configuration file location
        -->
        <fileSet>
            <directory>etc</directory>
            <outputDirectory>/usr/local/mywebapp/etc</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>
        <!--
        Jetty deployment: webapp deployment location - hmmm, this one might not be needed...
        -->
        <fileSet>
            <directory>webapps</directory>
            <outputDirectory>/usr/local/mywebapp/webapps</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>
        <!--
        Jetty deployment: production-time exploded warfile location
        -->
        <fileSet>
            <directory>src/main/webapp</directory>
            <outputDirectory>/usr/local/mywebapp/war</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>

Finally, to start the program:

java -server -Dbasedir=/usr/local/mywebapp/war -cp /usr/local/mywebapp/war/WEB-INF/lib/* com.mybiz.MyJettyWebServer

Browse to localhost:8080 and your webapp should appear.

You'll notice I did not use the maven-jetty plugin (nor the jetty-maven plugin - yes, there are two different ones, each with different names, schemas and behaviors). The good news about the plugin is that it shields you from much of the configuration/deployment exercises you'll need; that's also the bad news. I needed to understand explicitly what dependencies, etc. I'd need for production, so I chose to do things manually. The good news around this is that I've done most of the heavy lifting to grease the skids for future embedded-Jetty exercises.

References

Jetty 6.x Wiki
Jetty 7x, 8.x Wiki

Tuesday, June 22, 2010

Ternary Expressions Problem with JSF 1.2

This is a followup on my previous post that offered a workaround to this error message, as seen when working with JSF 1.2:

javax.el.ELException: Error Parsing: 
Caused by: com.sun.el.parser.ParseException: Encountered ":text"

In my first post, I failed to find the root cause, so I took the path of least resistance in the interest of the project schedule (using the heavy-handed approach of two separate panel groups, backing the condition out to the "rendered" attribute of each group). As it turns out, the root cause is lack of white space around the colon, as explained in this article from Oracle. That is, given an expression:

#{isThisTrue?doThis:doThat}

...and depending on what the deployment environment is, you'll either notice nothing or bump into this error message. When that first post was written, I was deploying to Glassfish 8.x - and I needed to use the workaround described above. Recently, I deployed to JBoss 4.x and later to 5.1, and here I did not notice anything - but then I redeployed to embedded Jetty 6.1.x, and the problem re-appeared. This is probably because JBoss shielded me from needing the magic combination of JSF, JSP, Facelets, JSTL and etc. to make things work, but vanilla Jetty does not.

In either event, you might have better luck than I with determining the dependency mix, including tweaking the web.xml to configure the correct expression factory, trying various permutations of Jetty's JSP libraries, and etc. (aka "time sink"); but, more simply, the fix is to change the problematic expression (well, actually it's a bug in JSF 1.2) to this:

#{isThisTrue?doThis : doThat}

This time, I googled for the right thing ("JSF 1.2 ternary parse exception") and, by now, the article from Oracle had finally been published (it wasn't there until after I originally bailed out with my workaround).