You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by rk...@apache.org on 2012/12/01 00:09:37 UTC

svn commit: r1415882 - in /oozie/trunk: ./ core/src/main/java/org/apache/oozie/test/ core/src/test/java/org/apache/oozie/test/ distro/ docs/src/site/twiki/ hadooplibs/hadoop-3/ login/ login/src/ login/src/main/ login/src/main/java/ login/src/main/java/...

Author: rkanter
Date: Fri Nov 30 23:09:35 2012
New Revision: 1415882

URL: http://svn.apache.org/viewvc?rev=1415882&view=rev
Log:
OOZIE-1103 Create example using AltKerberosAuthenticationHandler (rkanter)

Added:
    oozie/trunk/login/
    oozie/trunk/login/README.txt
    oozie/trunk/login/pom.xml
    oozie/trunk/login/src/
    oozie/trunk/login/src/main/
    oozie/trunk/login/src/main/java/
    oozie/trunk/login/src/main/java/org/
    oozie/trunk/login/src/main/java/org/apache/
    oozie/trunk/login/src/main/java/org/apache/oozie/
    oozie/trunk/login/src/main/java/org/apache/oozie/authentication/
    oozie/trunk/login/src/main/java/org/apache/oozie/authentication/AltKerberosAuthenticationHandler.java
    oozie/trunk/login/src/main/java/org/apache/oozie/authentication/ExampleAltAuthenticationHandler.java
    oozie/trunk/login/src/main/java/org/apache/oozie/servlet/
    oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/
    oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LDAPLoginServlet.java
    oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LoginServlet.java
    oozie/trunk/login/src/main/resources/
    oozie/trunk/login/src/main/resources/login-page-template.html
    oozie/trunk/login/src/main/webapp/
    oozie/trunk/login/src/main/webapp/META-INF/
    oozie/trunk/login/src/main/webapp/META-INF/context.xml
    oozie/trunk/login/src/main/webapp/WEB-INF/
    oozie/trunk/login/src/main/webapp/WEB-INF/web.xml
    oozie/trunk/login/src/test/
    oozie/trunk/login/src/test/java/
    oozie/trunk/login/src/test/java/org/
    oozie/trunk/login/src/test/java/org/apache/
    oozie/trunk/login/src/test/java/org/apache/oozie/
    oozie/trunk/login/src/test/java/org/apache/oozie/authentication/
    oozie/trunk/login/src/test/java/org/apache/oozie/authentication/TestExampleAltAuthenticationHandler.java
    oozie/trunk/login/src/test/java/org/apache/oozie/servlet/
    oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/
    oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLDAPLoginServlet.java
    oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLoginServlet.java
Modified:
    oozie/trunk/core/src/main/java/org/apache/oozie/test/EmbeddedServletContainer.java
    oozie/trunk/core/src/test/java/org/apache/oozie/test/XTestCase.java
    oozie/trunk/distro/pom.xml
    oozie/trunk/docs/src/site/twiki/ENG_Custom_Authentication.twiki
    oozie/trunk/hadooplibs/hadoop-3/pom.xml
    oozie/trunk/pom.xml
    oozie/trunk/release-log.txt
    oozie/trunk/src/main/assemblies/distro.xml

Modified: oozie/trunk/core/src/main/java/org/apache/oozie/test/EmbeddedServletContainer.java
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/test/EmbeddedServletContainer.java?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/core/src/main/java/org/apache/oozie/test/EmbeddedServletContainer.java (original)
+++ oozie/trunk/core/src/main/java/org/apache/oozie/test/EmbeddedServletContainer.java Fri Nov 30 23:09:35 2012
@@ -24,6 +24,7 @@ import org.mortbay.jetty.servlet.Context
 
 import java.net.InetAddress;
 import java.net.ServerSocket;
+import java.util.Map;
 
 /**
  * An embedded servlet container for testing purposes. <p/> It provides reduced functionality, it supports only
@@ -56,9 +57,25 @@ public class EmbeddedServletContainer {
      * @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at
      * the end.
      * @param servletClass servlet class
+     * @param initParams a mapping of init parameters for the servlet, or null
+     */
+    public void addServletEndpoint(String servletPath, Class servletClass, Map<String, String> initParams) {
+        ServletHolder s = new ServletHolder(servletClass);
+        context.addServlet(s, servletPath);
+        if (initParams != null) {
+            s.setInitParameters(initParams);
+        }
+    }
+
+    /**
+     * Add a servlet to the container.
+     *
+     * @param servletPath servlet path for the servlet, it should be prefixed with '/", it may contain a wild card at
+     * the end.
+     * @param servletClass servlet class
      */
     public void addServletEndpoint(String servletPath, Class servletClass) {
-        context.addServlet(new ServletHolder(servletClass), servletPath);
+        addServletEndpoint(servletPath, servletClass, null);
     }
 
     /**

Modified: oozie/trunk/core/src/test/java/org/apache/oozie/test/XTestCase.java
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/test/XTestCase.java?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/core/src/test/java/org/apache/oozie/test/XTestCase.java (original)
+++ oozie/trunk/core/src/test/java/org/apache/oozie/test/XTestCase.java Fri Nov 30 23:09:35 2012
@@ -61,6 +61,7 @@ import org.apache.oozie.store.StoreExcep
 import org.apache.oozie.util.IOUtils;
 import org.apache.oozie.util.ParamChecker;
 import org.apache.oozie.util.XLog;
+import org.junit.Assert;
 
 /**
  * Base JUnit <code>TestCase</code> subclass used by all Oozie testcases.
@@ -253,6 +254,12 @@ public abstract class XTestCase extends 
         File source = (customOozieSite.startsWith("/"))
                       ? new File(customOozieSite) : new File(OOZIE_SRC_DIR, customOozieSite);
         source = source.getAbsoluteFile();
+        // If we can't find it, try using the class loader (useful if we're using XTestCase from outside core)
+        if (!source.exists()) {
+            source = new File(getClass().getClassLoader().getResource(oozieTestDB + "-oozie-site.xml").getPath());
+            source = source.getAbsoluteFile();
+        }
+        // If we still can't find it, then exit
         if (!source.exists()) {
             System.err.println();
             System.err.println(XLog.format("Custom configuration file for testing does no exist [{0}]",

Modified: oozie/trunk/distro/pom.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/distro/pom.xml?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/distro/pom.xml (original)
+++ oozie/trunk/distro/pom.xml Fri Nov 30 23:09:35 2012
@@ -139,6 +139,21 @@
                 </dependency>
              </dependencies>
         </profile>
+        <profile>
+            <id>loginServerExample</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.apache.oozie</groupId>
+                    <artifactId>oozie-login</artifactId>
+                    <version>${project.version}</version>
+                    <scope>compile</scope>
+                    <type>war</type>
+                </dependency>
+             </dependencies>
+        </profile>
     </profiles>
 </project>
 

Modified: oozie/trunk/docs/src/site/twiki/ENG_Custom_Authentication.twiki
URL: http://svn.apache.org/viewvc/oozie/trunk/docs/src/site/twiki/ENG_Custom_Authentication.twiki?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/docs/src/site/twiki/ENG_Custom_Authentication.twiki (original)
+++ oozie/trunk/docs/src/site/twiki/ENG_Custom_Authentication.twiki Fri Nov 30 23:09:35 2012
@@ -19,6 +19,7 @@ The following authenticators are provide
 
    * KerberosAuthenticationHandler   : the authenticator handler implements the Kerberos SPNEGO authentication mechanism for HTTP.
    * PseudoAuthenticationHandler     : the authenticator handler provides a pseudo authentication mechanism that accepts the user name specified as a query string parameter.
+   * AltKerberosAuthenticationHandler: the authenticator handler allows for Kerberos SPNEGO authentication for non-browsers and an alternate form of authentication for browsers.  A subclass must implement the alternate authentication (see [[ENG_Custom_Authentication#LoginServerExample][Example Login Server]])
 
 3. =org.apache.hadoop.security.authentication.server.AuthenticationFilter:= A servlet filter enables protecting web application resources with different authentication mechanisms provided by AuthenticationHandler. To enable the filter, web application resources file (ex. web.xml) needs to include the a filter class derived from =AuthenticationFilter=.
 
@@ -140,6 +141,144 @@ protected AuthenticationToken getToken(H
       }
 </verbatim>
 
+#LoginServerExample
+---++ Login Server Example
+
+---+++ Overview
+
+The Login Server Example is a web application that is an example of how to create a login server for Oozie.  It provides two example
+servlets: LoginServlet and LDAPLoginServlet.  The LoginServlet example is very primitive and simply authenticates users whose
+username and password match (e.g. user=foo and pass=foo).  The LDAPLoginServlet example can be configured against an LDAP server to
+authenticate users from that LDAP server.  Once authenticated, both example servlets write the username to a cookie that Oozie
+checks via the ExampleAltAuthenticationHandler (which uses that cookie for authentication for browsers but Kerberos otherwise).
+
+The LoginServlet and LDAPLoginServlet are run from a separate WAR file called oozie-login.war; its web.xml can be used to configure
+which servlet is used as well as some additional properties. The ExampleAltAuthenticationHandler is run as part of the Oozie server
+but is built as a separate jar: oozie-login.jar.
+
+---+++ ExampleAltAuthenticationHandler
+
+This is a subclass of the abstract AltKerberosAuthenticationHandler, which is an AuthenticationHandler that allows for a "mixed"
+mode of authentication.  When a non-browser is used, Kerberos will be used for authentication; when a browser is used, some other
+authentication method will be used.  In the case of ExampleAltAuthenticationHandler, the other authentication method is to look for
+a cookie named =oozie.web.login.auth= and create an AuthenticationToken using the value of the cookie as the username.  If the
+cookie cannot be found, it will redirect the browser to a page where the user can (presumably) login to a server that can
+authenticate the user and create the cookie.  As this is obviously a very primitive method of authentication that is not secure, it
+should NOT be used in production; it is only provided as an example of how the AltKerberosAuthenticationHandler can be used.
+
+To reiterate: %RED%ExampleAltAuthenticationHandler IS NOT SECURE -- DO NOT USE IT IN A PRODUCTION ENVIRONMENT%ENDCOLOR%
+
+To use the ExampleAltAuthenticationHandler, make at least the following two changes to your oozie-site.xml.  All of the existing
+Kerberos-related settings are still applicable (for when a non-browser is used) so make sure to configure them appropriately.
+<verbatim>
+   <property>
+        <name>oozie.authentication.type</name>
+        <value>org.apache.oozie.authentication.ExampleAltAuthenticationHandler</value>
+   </property>
+   <property>
+        <name>oozie.service.HadoopAccessorService.kerberos.enabled</name>
+        <value>true</value>
+    </property>
+</verbatim>
+Note: The ExampleAltAuthenticationHandler is included in the oozie-login.jar file and not normally included with Oozie core.
+Additionally, you can configure which user-agents AltKerberosAuthenticationHandler (and thus ExampleAltAuthenticationHandler)
+consider to be non-browsers by setting the following property in oozie-site.xml to a comma separated list.  When any of the values
+in this property are contained in the user-agent of the request, Kerberos will be used; otherwise, the alternate authentication will
+be used.
+<verbatim>
+   <property>
+        <name>alt-kerberos.non-browser.user-agents</name>
+        <value>java,curl,wget,perl</value>
+   </property>
+</verbatim>
+The above values, which are the default, will cause a user-agent such as "java" (the user-agent used by Java programs) to use
+Kerberos.  Note that this would also match with user-agents such as "java6" and "I am not a JaVa program".
+
+When the ExampleAltAuthenticationHandler cannot find the =oozie.web.login.auth= cookie, it will redirect the user to another URL,
+which can be configured by setting the following property in oozie-site.xml.  Typically, this URL should take the user to a server
+where they can login to acquire the cookie and then get redirected back to the Oozie web console (the Login Server Example does this
+and will be explained in more detail later).
+<verbatim>
+    <property>
+        <name>oozie.authentication.ExampleAltAuthenticationHandler.redirect.url</name>
+        <value>http://localhost:11000/oozie-login/?backurl={0}</value>
+    </property>
+</verbatim>
+The above value, which is the default, will cause the user to be redirected to the Login Server Example if its running in the same
+tomcat as Oozie and on the default port.  If ={0}= appears anywhere in this URL, it will be replaced by the URL of Oozie's web
+console so that the Login Server Example can know where to send the user back while staying independent of Oozie.
+
+---+++ LoginServlet
+
+This is a web servlet that gets bundled in the oozie-login.war web application.  It is a very primitive example of a login server
+implementation that is compatible with the ExampleAltAuthenticationHandler.  When users visit this servlet, they are shown a simple
+login page that allows them to enter their username and password.  It authenticates them if their username and password are the same
+(e.g. user=foo and pass=foo), which is not secure and should not be used in production; it is only provided as an example.
+
+To reiterate: %RED%LoginServlet IS NOT SECURE -- DO NOT USE IT IN A PRODUCTION ENVIRONMENT%ENDCOLOR%
+
+Sending it a GET request returns the login page; the =backurl= parameter is required (so it knows where to redirect the user back to
+once they are authenticated), but there is also an optional =username= parameter that will pre-populate the username field if given.
+
+Sending it a POST request will also return the login page, but only if an error occurs (e.g. invalid username or password).  As with
+the GET request, the =backurl= parameter is required, but now the =username= and =password= parameters are also required.  If they
+match, the LoginServlet will write the =oozie.web.login.auth= cookie containing the username and redirect the user to the =backurl=,
+which is presumably the Oozie web console.
+
+The login page can be configured to look differently by changing the following parameter in the web.xml in the oozie-login.war file
+(or in the login/src/main/webapp/WEB-INF/ directory before building it).  The file needs to be located in the
+login/src/main/resources/ directory and should contain ={0}= for where an error message can go, ={1}= for where the username
+included with a GET request will go, and ={2}= for where the =backurl= goes.
+<verbatim>
+    <init-param>
+        <param-name>login.page.template</param-name>
+        <param-value>login-page-template.html</param-value>
+    </init-param>
+</verbatim>
+The above value, which is the default, is a basic html page that has fields for the username and password and meets the previously
+stated requirements.
+
+---+++ LDAPLoginServlet
+
+This is a second web servlet that gets bundled in the oozie-login.war web application.  It inherits from the LoginServlet, so the
+previous configuration information (i.e. login.page.template) still applies to this servlet.  The only difference between the
+LDAPLoginServlet and the LoginServlet, is that the LDAPLoginServlet is configured against an LDAP server to provide the
+authentication instead of simply checking that the username and password are equal.  As before, this is not secure and should not be
+used in production; it is only provided as an example.
+
+To reiterate: %RED%LDAPLoginServlet IS NOT SECURE -- DO NOT USE IT IN A PRODUCTION ENVIRONMENT%ENDCOLOR%
+
+The oozie-login.war web application is configured to use LoginServlet by default.  To switch it to use the LDAPLoginServlet, you
+have to change the following line in the web.xml from:
+<verbatim>
+    <servlet-class>org.apache.oozie.servlet.login.LoginServlet</servlet-class>
+to:
+    <servlet-class>org.apache.oozie.servlet.login.LDAPLoginServlet</servlet-class>
+</verbatim>
+
+There are three additional parameters related to LDAP that you should configure in the web.xml:
+<verbatim>
+    <init-param>
+        <param-name>ldap.provider.url</param-name>
+        <param-value>ldap://localhost:389</param-value>
+    </init-param>
+    <init-param>
+        <param-name>ldap.context.factory</param-name>
+        <param-value>com.sun.jndi.ldap.LdapCtxFactory</param-value>
+    </init-param>
+    <init-param>
+        <param-name>ldap.security.authentication</param-name>
+        <param-value>simple</param-value>
+    </init-param>
+</verbatim>
+The ldap.provider.url is the LDAP provider URL to use, the ldap.context.factory is the LDAP context factory to use, and the
+ldap.security.authentication is the LDAP security authentication type to use.
+
+---+++ Building and Deploying
+The README.txt file in the =login= directory contains instructions on how to build and deploy the Login Server Example
+
+
+
 [[index][::Go back to Oozie Documentation Index::]]
 
 </noautolink>
\ No newline at end of file

Modified: oozie/trunk/hadooplibs/hadoop-3/pom.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/hadooplibs/hadoop-3/pom.xml?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/hadooplibs/hadoop-3/pom.xml (original)
+++ oozie/trunk/hadooplibs/hadoop-3/pom.xml Fri Nov 30 23:09:35 2012
@@ -38,6 +38,16 @@
             <artifactId>hadoop-client</artifactId>
             <version>3.0.0-SNAPSHOT</version>
             <scope>compile</scope>
+            <exclusions>
+                <exclusion>
+                    <artifactId>jasper-runtime</artifactId>
+                    <groupId>tomcat</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jsp-api</artifactId>
+                    <groupId>javax.servlet.jsp</groupId>
+                </exclusion>
+            </exclusions>
         </dependency>
     </dependencies>
 

Added: oozie/trunk/login/README.txt
URL: http://svn.apache.org/viewvc/oozie/trunk/login/README.txt?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/README.txt (added)
+++ oozie/trunk/login/README.txt Fri Nov 30 23:09:35 2012
@@ -0,0 +1,118 @@
+
+Login Server Example
+====================
+
+---------------------------------
+What is the Login Server Example?
+---------------------------------
+The Login Server Example is a web application that is an example of how to create a login server for Oozie.  It provides two example
+servlets: LoginServlet and LDAPLoginServlet.  The LoginServlet example is very primitive and simply authenticates users whose
+username and password match (e.g. user=foo and pass=foo).  The LDAPLoginServlet example can be configured against an LDAP server to
+authenticate users from that LDAP server.  Onces authenticated, both example servlets write the username to a cookie that Oozie
+checks via the ExampleAltAuthenticationHandler (which uses that cookie for authentication for browsers but Kerberos otherwise).
+
+The LoginServlet and LDAPLoginServlet are run from a separate WAR file called oozie-login.war; its web.xml can be used to configure
+which servlet is used as well as some additional properties. The ExampleAltAuthenticationHandler is run as part of Oozie.
+
+More details on the Login Server Example and the three classes can be found on the "Creating Custom Authentication" page of the
+Oozie Documentation.
+
+ExampleAltAuthenticationHandler, LoginServlet, and LDAPLoginServlet ARE NOT SECURE
+                                                                    -- THEY SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT
+
+--------------------------------------------------------------------------------------
+How to build and launch the Login Server Example as part of entire oozie package build
+--------------------------------------------------------------------------------------
+
+1. run mkdistro.sh on top-level directory
+---------------------
+bin/mkdistro.sh -P loginServerExample
+---------------------
+[NOTE]
+The Login Server Example is not included in the build by default, hence the need to specify a maven profile (-P loginServerExample).
+This maven profile causes two additional files to be built: oozie-login.war (contains the oozie login server example) and
+oozie-login.jar (contains the AuthenticationHandler to use with the oozie login server example)
+
+2. move to output directory
+--------------------
+cd distro/target/oozie-<version>-distro/oozie-<version>
+--------------------
+
+3-(a). copy the war file to oozie-server/webapps/
+--------------------
+cp oozie-login.war ./oozie-server/webapps
+--------------------
+[NOTE]
+Method (a) only gives you the login server; to also make the AuthenticationHandler available to the Oozie server, use method (b)
+
+or
+
+3-(b). create /libext and copy the war and jar files to it
+--------------------
+mkdir libext
+cp oozie-login.war ./libext
+cp oozie-login.jar ./libext
+--------------------
+[NOTE]
+bin/oozie-setup.sh is implemented such that oozie-login.war is automatically picked up and deployed to oozie server
+
+4. start oozie server (using bin/oozie-setup.sh and bin/oozie-start.sh) and check through browser
+---------------------
+http://localhost:11000/oozie-login
+---------------------
+[NOTE]
+Using default port number, which is 11000. Tomcat server may fail to start if another application already using the same port.
+Please make sure the port is not being used.
+
+
+---------------------------------------------------------------------------
+How to build and launch Login Server Example only (not whole package build)
+---------------------------------------------------------------------------
+This is to launch the Login Server Example using a web server that you are already running (without using bundled tomcat).
+This is suitable for when you want to host this application on the existing tomcat instance. You need to copy the war file into
+the webapp directory of the existing tomcat.
+
+1. build the Login Server Example and create war file
+---------------------
+// Assuming you are at the top level directory
+mvn clean package -P loginServerExample -Dtest=TestExampleAltAuthenticationHandler,TestLoginServlet,TestLDAPLoginServlet
+---------------------
+[NOTE]
+This must be done from the top level directory because oozie-core is a dependency on the Login Server Example. To skip all tests,
+replace the -Dtest=... with -DskipTests.
+
+2. copy war file to webapps directory of web server
+---------------------
+cp login/target/oozie-login.war <webserver-installed-directory>/webapps/
+---------------------
+
+3. start web server
+---------------------
+<webserver-installed-directory>/bin/startup.sh
+---------------------
+[NOTE]
+name of start script might be different in your web-server, please change accordingly
+
+4. check through browser
+---------------------
+http://localhost:8080/oozie-login
+---------------------
+[NOTE]
+port number might not be 8080 in your web-server setting (usually it's default in tomcat), please change accordingly
+
+5. stop web server
+---------------------
+<webserver-installed-directory>/bin/shutdown.sh
+---------------------
+[NOTE] name of shutdown script might be different in your web-server, please change accordingly
+
+
+=====================================================================
+
+If you have any questions/issues, please send an email to:
+
+user@oozie.apache.org
+
+Subscribe using the link:
+
+http://oozie.apache.org/mail-lists.html

Added: oozie/trunk/login/pom.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/login/pom.xml?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/pom.xml (added)
+++ oozie/trunk/login/pom.xml Fri Nov 30 23:09:35 2012
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.oozie</groupId>
+        <artifactId>oozie-main</artifactId>
+        <version>3.4.0-SNAPSHOT</version>
+    </parent>
+    <groupId>org.apache.oozie</groupId>
+    <artifactId>oozie-login</artifactId>
+    <version>3.4.0-SNAPSHOT</version>
+    <description>Apache Oozie Login</description>
+    <name>Apache Oozie Login</name>
+    <packaging>war</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.oozie</groupId>
+            <artifactId>oozie-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.oozie</groupId>
+            <artifactId>oozie-core</artifactId>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-auth</artifactId>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.oozie</groupId>
+            <artifactId>oozie-hadoop</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- ApacheDS needed for LDAP unit tests -->
+        <dependency>
+            <groupId>org.apache.directory.server</groupId>
+            <artifactId>apacheds-server-unit</artifactId>
+            <version>1.0.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+           <groupId>org.apache.oozie</groupId>
+           <artifactId>oozie-hadoop-test</artifactId>
+           <scope>test</scope>
+       </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>oozie-login</finalName>
+        <plugins>
+            <!-- Make the oozie-login.jar too -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>make-a-jar</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                 <groupId>org.apache.rat</groupId>
+                 <artifactId>apache-rat-plugin</artifactId>
+                 <configuration>
+                     <excludeSubProjects>false</excludeSubProjects>
+                     <excludes>
+                         <!-- excluding all as the root POM does the full check-->
+                         <exclude>**</exclude>
+                     </excludes>
+                 </configuration>
+             </plugin>
+             <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>../src/main/assemblies/empty.xml</descriptor>
+                    </descriptors>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+

Added: oozie/trunk/login/src/main/java/org/apache/oozie/authentication/AltKerberosAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/java/org/apache/oozie/authentication/AltKerberosAuthenticationHandler.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/java/org/apache/oozie/authentication/AltKerberosAuthenticationHandler.java (added)
+++ oozie/trunk/login/src/main/java/org/apache/oozie/authentication/AltKerberosAuthenticationHandler.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.authentication;
+
+import java.io.IOException;
+import java.util.Properties;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
+
+// TODO: Delete this class when hadoop's AltKerberosAuthenticationHandler becomes available in a hadoop release
+/**
+ * The {@link AltKerberosAuthenticationHandler} behaves exactly the same way as the KerberosAuthenticationHandler, except that it
+ * allows for an alternative form of authentication for browsers while still using Kerberos for Java access. This is an abstract
+ * class that should be subclassed to allow a developer to implement their own custom authentication for browser access. The
+ * alternateAuthenticate method will be called whenever a request comes from a browser.
+ * <p/>
+ */
+public abstract class AltKerberosAuthenticationHandler extends KerberosAuthenticationHandler {
+
+    /**
+     * Constant that identifies the authentication mechanism.
+     */
+    public static final String TYPE = "alt-kerberos";
+
+    /**
+     * Constant for the configuration property that indicates which user agents are not considered browsers (comma separated)
+     */
+    public static final String NON_BROWSER_USER_AGENTS = TYPE + ".non-browser.user-agents";
+    private static final String NON_BROWSER_USER_AGENTS_DEFAULT = "java,curl,wget,perl";
+
+    private String[] nonBrowserUserAgents;
+
+    /**
+     * Returns the authentication type of the authentication handler, 'alt-kerberos'.
+     * <p/>
+     *
+     * @return the authentication type of the authentication handler, 'alt-kerberos'.
+     */
+    @Override
+    public String getType() {
+        return TYPE;
+    }
+
+    @Override
+    public void init(Properties config) throws ServletException {
+        super.init(config);
+
+        nonBrowserUserAgents = config.getProperty(NON_BROWSER_USER_AGENTS, NON_BROWSER_USER_AGENTS_DEFAULT).split("\\W*,\\W*");
+        for (int i = 0; i < nonBrowserUserAgents.length; i++) {
+            nonBrowserUserAgents[i] = nonBrowserUserAgents[i].toLowerCase();
+        }
+    }
+
+  /**
+     * It enforces the the Kerberos SPNEGO authentication sequence returning an {@link AuthenticationToken} only after the Kerberos
+     * SPNEGO sequence has completed successfully (in the case of Java access) and only after the custom authentication implemented
+     * by the subclass in alternateAuthenticate has completed successfully (in the case of browser access).
+     * <p/>
+     *
+     * @param request the HTTP client request.
+     * @param response the HTTP client response.
+     *
+     * @return an authentication token if the request is authorized or null
+     *
+     * @throws IOException thrown if an IO error occurred
+     * @throws AuthenticationException thrown if an authentication error occurred
+     */
+    @Override
+    public AuthenticationToken authenticate(HttpServletRequest request, HttpServletResponse response)
+            throws IOException, AuthenticationException {
+        AuthenticationToken token;
+        if (isBrowser(request.getHeader("User-Agent"))) {
+            token = alternateAuthenticate(request, response);
+        }
+        else {
+            token = super.authenticate(request, response);
+        }
+        return token;
+    }
+
+    /**
+     * This method parses the User-Agent String and returns whether or not it refers to a browser. If its not a browser, then
+     * Kerberos authentication will be used; if it is a browser, alternateAuthenticate from the subclass will be used.
+     * <p/>
+     * A User-Agent String is considered to be a browser if it does not contain any of the values from
+     * alt-kerberos.non-browser.user-agents; the default behavior is to consider everything a browser unless it contains one of:
+     * "java", "curl", "wget", or "perl". Subclasses can optionally override this method to use different behavior.
+     *
+     * @param userAgent The User-Agent String, or null if there isn't one
+     * @return true if the User-Agent String refers to a browser, false if not
+     */
+    protected boolean isBrowser(String userAgent) {
+        if (userAgent == null) {
+            return false;
+        }
+        userAgent = userAgent.toLowerCase();
+        boolean isBrowser = true;
+        for (String nonBrowserUserAgent : nonBrowserUserAgents) {
+            if (userAgent.contains(nonBrowserUserAgent)) {
+                isBrowser = false;
+                break;
+            }
+        }
+        return isBrowser;
+    }
+
+    /**
+     * Subclasses should implement this method to provide the custom authentication to be used for browsers.
+     *
+     * @param request the HTTP client request.
+     * @param response the HTTP client response.
+     * @return an authentication token if the request is authorized, or null
+     * @throws IOException thrown if an IO error occurs
+     * @throws AuthenticationException thrown if an authentication error occurs
+     */
+    public abstract AuthenticationToken alternateAuthenticate(HttpServletRequest request, HttpServletResponse response)
+            throws IOException, AuthenticationException;
+}

Added: oozie/trunk/login/src/main/java/org/apache/oozie/authentication/ExampleAltAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/java/org/apache/oozie/authentication/ExampleAltAuthenticationHandler.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/java/org/apache/oozie/authentication/ExampleAltAuthenticationHandler.java (added)
+++ oozie/trunk/login/src/main/java/org/apache/oozie/authentication/ExampleAltAuthenticationHandler.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,127 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.authentication;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.text.MessageFormat;
+import java.util.Properties;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+// TODO: Switch to subclassing hadoop's AltKerberosAuthenticationHandler when it becomes available in a hadoop release
+//import org.apache.hadoop.security.authentication.server.AltKerberosAuthenticationHandler;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.oozie.service.Services;
+
+/**
+ * This class provides an implementation of the {@link AltKerberosAuthenticationHandler} as a simple example.  It is meant to be
+ * used with the Login Server Example.  The alternate authentication offered by this class is to check for a cookie named
+ * "oozie.web.login.auth" and use its value as the username.  More information can be found in the README.txt for the Login Server
+ * Example.  Note that this implementation is NOT SECURE and should not be used in production.
+ */
+public class ExampleAltAuthenticationHandler extends AltKerberosAuthenticationHandler {
+
+    /**
+     * Constant for the configuration property that indicates the redirect URL to send unauthenticated users to the Login Server.
+     * It can include {0}, which will be replaced by the Oozie web console URL.
+     */
+    private static final String REDIRECT_URL = "oozie.authentication.ExampleAltAuthenticationHandler.redirect.url";
+    private static final String REDIRECT_URL_DEFAULT = "http://localhost:11000/oozie-login/?backurl={0}";
+
+    private String redirectURL;
+
+    @Override
+    public void init(Properties config) throws ServletException {
+        super.init(config);
+
+        Configuration conf = Services.get().getConf();
+        redirectURL = conf.get(REDIRECT_URL, REDIRECT_URL_DEFAULT);
+    }
+
+    /**
+     * Implementation of the custom authentication.  It looks for the "oozie.web.login.auth" cookie and if it exists, returns an
+     * AuthenticationToken with the cookie's value as the username.  Otherwise, it will redirect the user to the login server via
+     * the REDIRECT_URL.
+     *
+     * @param request the HTTP client request.
+     * @param response the HTTP client response.
+     * @return an authentication token if the request is authorized, or null
+     * @throws IOException thrown if an IO error occurs
+     * @throws AuthenticationException thrown if an authentication error occurs
+     */
+    @Override
+    public AuthenticationToken alternateAuthenticate(HttpServletRequest request, HttpServletResponse response)
+            throws IOException, AuthenticationException {
+        AuthenticationToken token = null;
+        Cookie[] cookies = request.getCookies();
+        Cookie authCookie = verifyAndExtractAltAuth(cookies);
+        String altAuthUserName = getAltAuthUserName(authCookie);
+        // Authenticated
+        if (altAuthUserName != null) {
+            token = new AuthenticationToken(altAuthUserName, altAuthUserName, getType());
+        }
+        // Not Authenticated
+        else {
+            StringBuffer sb = request.getRequestURL();
+            if (request.getQueryString() != null) {
+                sb.append("?").append(request.getQueryString());
+            }
+            String url = MessageFormat.format(redirectURL, URLEncoder.encode(sb.toString(), "ISO-8859-1"));
+            url = response.encodeRedirectURL(url);
+            response.sendRedirect(url);
+        }
+        return token;
+    }
+
+    /**
+     * Verifies and extracts the "oozie.web.login.auth" Cookie from the passed in cookies.  Note that this implementation doesn't
+     * actually do any verification, but a subclass can override it to do so.
+     *
+     * @param cookies The cookies from a request.
+     * @return The "oozie.web.login.auth" cookie or null
+     */
+    protected Cookie verifyAndExtractAltAuth(Cookie[] cookies) {
+        if (cookies == null) {
+            return null;
+        }
+        for (Cookie cookie : cookies) {
+            if (cookie.getName().equals("oozie.web.login.auth")) {
+                // Here the cookie should be verified for integrity/authenticity from the login service
+                return cookie;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the username from the "oozie.web.login.auth" cookie.
+     *
+     * @param authCookie The "oozie.web.login.auth" cookie
+     * @return The username from the cookie or null if the cookie is null
+     */
+    protected String getAltAuthUserName(Cookie authCookie) {
+        if (authCookie == null) {
+            return null;
+        }
+        return authCookie.getValue();
+    }
+}

Added: oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LDAPLoginServlet.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LDAPLoginServlet.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LDAPLoginServlet.java (added)
+++ oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LDAPLoginServlet.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.servlet.login;
+
+import java.util.Hashtable;
+import javax.naming.Context;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.servlet.ServletException;
+
+/**
+ * This class provides an LDAP example Login Servlet to be used with the ExampleAltAuthenticationHandler.  It provides a login page
+ * to the user and checks that the username and password are are able to login to the configured LDAP server and writes the username
+ * to a cookie named "oozie.web.login.auth".  Once authenticated, it will send the user to the "backurl".  More information can be
+ * found in the README.txt for the Login Server Example.  Note that this implementation is NOT SECURE and should not be used in
+ * production.
+ */
+public class LDAPLoginServlet extends LoginServlet {
+
+    /**
+     * Constant for the configuration property that indicates LDAP provider url to use.  Note that this is configured in the web.xml
+     * file.
+     */
+    public static final String LDAP_PROVIDER_URL_KEY = "ldap.provider.url";
+    private static final String LDAP_PROVIDER_URL_DEFAULT = "ldap://localhost:389";
+    private String ldapProviderUrl;
+
+    /**
+     * Constant for the configuration property that indicates LDAP context factory to use.  Note that this is configured in the
+     * web.xml file.
+     */
+    public static final String LDAP_CONTEXT_FACTORY_KEY = "ldap.context.factory";
+    private static final String LDAP_CONTEXT_FACTORY_DEFAULT = "com.sun.jndi.ldap.LdapCtxFactory";
+    private String ldapContextFactory;
+
+    /**
+     * Constant for the configuration property that indicates LDAP security authentication type to use.  Note that this is
+     * configured in the web.xml file.
+     */
+    public static final String LDAP_SECURITY_AUTHENTICATION_KEY = "ldap.security.authentication";
+    private static final String LDAP_SECURITY_AUTHENTICATION_DEFAULT = "simple";
+    private String ldapSecurityAuthentication;
+
+    @Override
+    public void init() throws ServletException {
+        super.init();
+
+        ldapProviderUrl = getInitParameter(LDAP_PROVIDER_URL_KEY);
+        if (ldapProviderUrl == null) {
+            ldapProviderUrl = LDAP_PROVIDER_URL_DEFAULT;
+        }
+
+        ldapContextFactory = getInitParameter(LDAP_CONTEXT_FACTORY_KEY);
+        if (ldapContextFactory == null) {
+            ldapContextFactory = LDAP_CONTEXT_FACTORY_DEFAULT;
+        }
+
+        ldapSecurityAuthentication = getInitParameter(LDAP_SECURITY_AUTHENTICATION_KEY);
+        if (ldapSecurityAuthentication == null) {
+            ldapSecurityAuthentication = LDAP_SECURITY_AUTHENTICATION_DEFAULT;
+        }
+    }
+
+    /**
+     * This method is overridden from LoginServlet to verify the password by attempting to use the username and password to login to
+     * the configured LDAP server.
+     *
+     * @param username The username
+     * @param password The password
+     * @return true if verified, false if not
+     */
+    @Override
+    protected boolean verifyPassword(String username, String password) {
+        boolean result = false;
+        try {
+            Hashtable<String, String> env = new Hashtable<String, String>();
+            env.put(Context.INITIAL_CONTEXT_FACTORY, ldapContextFactory);
+            env.put(Context.PROVIDER_URL, ldapProviderUrl);
+            env.put(Context.SECURITY_AUTHENTICATION, ldapSecurityAuthentication);
+            env.put(Context.SECURITY_PRINCIPAL, username);
+            env.put(Context.SECURITY_CREDENTIALS, password);
+            DirContext ctx = new InitialDirContext(env);
+            if (ctx != null) {
+                ctx.close();
+                result = true;
+            }
+        } catch (Exception e) {
+            result = false;
+        }
+        return result;
+    }
+}

Added: oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LoginServlet.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LoginServlet.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LoginServlet.java (added)
+++ oozie/trunk/login/src/main/java/org/apache/oozie/servlet/login/LoginServlet.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.servlet.login;
+
+import java.io.*;
+import java.text.MessageFormat;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * This class provides a basic example Login Servlet to be used with the ExampleAltAuthenticationHandler.  It provides a login page
+ * to the user and simply checks that the username and password are equal (e.g. user=foo pass=foo) and writes the username to a
+ * cookie named "oozie.web.login.auth".  Once authenticated, it will send the user to the "backurl".  More information can be found
+ * in the README.txt for the Login Server Example.  Note that this implementation is NOT SECURE and should not be used in
+ * production.
+ */
+public class LoginServlet extends HttpServlet {
+
+    /**
+     * Constant for the configuration property that indicates the login page html to use.  The file needs to be located in the
+     * login/src/main/resources/ directory and should contain {0} for where an error message can go, {1} for where the username
+     * included with a GET request will go, and {2} for where the "backurl" goes.  Note that this is configured in the web.xml file.
+     */
+    public static final String LOGIN_PAGE_TEMPLATE_KEY = "login.page.template";
+    private static final String LOGIN_PAGE_TEMPLATE_DEFAULT = "login-page-template.html";
+    private String loginPageTemplate;
+
+    private static final String USERNAME = "username";
+    private static final String PASSWORD = "password";
+    private static final String BACKURL = "backurl";
+
+    @Override
+    public void init() throws ServletException {
+        // Read in the login page html
+        String loginPageTemplateName = getInitParameter(LOGIN_PAGE_TEMPLATE_KEY);
+        if (loginPageTemplateName == null) {
+            loginPageTemplateName = LOGIN_PAGE_TEMPLATE_DEFAULT;
+        }
+        InputStream is = getClass().getClassLoader().getResourceAsStream(loginPageTemplateName);
+        if (is == null) {
+            throw new ServletException("Could not find resource [" + loginPageTemplateName + "]");
+        }
+        try {
+            StringBuilder sb = new StringBuilder();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = br.readLine();
+            while (line != null) {
+                sb.append(line).append("\n");
+                line = br.readLine();
+            }
+            br.close();
+            loginPageTemplate = sb.toString();
+        } catch (IOException ex) {
+            throw new ServletException("Could not read resource [" + loginPageTemplateName + "]");
+        }
+    }
+
+    protected void renderLoginPage(String message, String username, String backUrl, HttpServletResponse resp)
+            throws ServletException, IOException {
+        resp.setContentType("text/html");
+        Writer writer = resp.getWriter();
+        writer.write(MessageFormat.format(loginPageTemplate, message, username, backUrl));
+        writer.close();
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        // Check for the optional username parameter
+        String username = req.getParameter(USERNAME);
+        if (username == null) {
+            username = "";
+        }
+        // Check for the required backurl parameter
+        String backUrl = req.getParameter(BACKURL);
+        if (backUrl == null || backUrl.trim().isEmpty()) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing or invalid '" + BACKURL + "' parameter");
+        }
+        else {
+            renderLoginPage("", username, backUrl, resp);
+        }
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        String backUrl = req.getParameter(BACKURL);
+        // Check for the required backurl parameter
+        if (backUrl == null || backUrl.trim().isEmpty()) {
+            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing or invalid '" + BACKURL + "' parameter");
+        } else {
+            // Check for the requried username and password parameters
+            String username = req.getParameter(USERNAME);
+            String password = req.getParameter(PASSWORD);
+            if (username == null || username.trim().isEmpty()) {
+                renderLoginPage("<font color=\"red\">Error: Invalid Username or Password</font><br>", "", backUrl, resp);
+            }
+            else if (password == null || password.trim().isEmpty()) {
+                renderLoginPage("<font color=\"red\">Error: Invalid Username or Password</font><br>", username, backUrl, resp);
+            }
+            // Verify that the username and password are correct
+            else if (verifyPassword(username, password)) {
+                // If so, write the "oozie.web.login.auth" cookie and redirect back to the backurl
+                writeCookie(resp, username);
+                resp.sendRedirect(backUrl);
+            } else {
+                renderLoginPage("<font color=\"red\">Error: Invalid Username or Password</font><br>", username, backUrl, resp);
+            }
+        }
+    }
+
+    /**
+     * Verify that the given username and password are correct.  In this implementation, they are correct when they are equal, but
+     * a subclass can override this to provide a more complex/secure mechanism.
+     *
+     * @param username The username
+     * @param password The password
+     * @return true if verified, false if not
+     */
+    protected boolean verifyPassword(String username, String password) {
+        return (username.equals(password));
+    }
+
+    /**
+     * Write the "oozie.web.login.auth" cookie containing the username.  A subclass can override this to include more information
+     * into the cookie; though this will likely break compatibility with the ExampleAltAuthenticationHandler, so it would have to
+     * be extended as well.
+     *
+     * @param resp The response
+     * @param username The username
+     */
+    protected void writeCookie(HttpServletResponse resp, String username) {
+        Cookie cookie = new Cookie("oozie.web.login.auth", username);
+        cookie.setPath("/");
+        resp.addCookie(cookie);
+    }
+}

Added: oozie/trunk/login/src/main/resources/login-page-template.html
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/resources/login-page-template.html?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/resources/login-page-template.html (added)
+++ oozie/trunk/login/src/main/resources/login-page-template.html Fri Nov 30 23:09:35 2012
@@ -0,0 +1,36 @@
+<html>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+  <title>Oozie Web Console Login</title>
+</head>
+<body>
+<img src="../oozie/oozie_50x.png" height="30"/>
+<form name="input" action="/oozie-login/" method="POST">
+  <fieldset>
+    <legend>Login to Oozie Web Console:</legend>
+    {0}
+    Username: <input type="text" size="30" name="username" value="{1}"><br>
+    Password: <input type="password" size="30" name="password"><br>
+    <input type="hidden" name="backurl" value="{2}">
+    <input type="submit" value="Login">
+  </fieldset>
+</form>
+</body>
+</html>
\ No newline at end of file

Added: oozie/trunk/login/src/main/webapp/META-INF/context.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/webapp/META-INF/context.xml?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/webapp/META-INF/context.xml (added)
+++ oozie/trunk/login/src/main/webapp/META-INF/context.xml Fri Nov 30 23:09:35 2012
@@ -0,0 +1,19 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<Context path="/oozie-login">
+</Context>

Added: oozie/trunk/login/src/main/webapp/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/main/webapp/WEB-INF/web.xml?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/main/webapp/WEB-INF/web.xml (added)
+++ oozie/trunk/login/src/main/webapp/WEB-INF/web.xml Fri Nov 30 23:09:35 2012
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+    <display-name>Oozie Login</display-name>
+
+    <servlet>
+        <servlet-name>login</servlet-name>
+        <display-name>Oozie Login</display-name>
+        <!-- Replace the following with "org.apache.oozie.servlet.login.LDAPLoginServlet" to use LDAP -->
+        <servlet-class>org.apache.oozie.servlet.login.LoginServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+
+        <!-- Uncomment to change the default values
+        <init-param>
+            <param-name>login.page.template</param-name>
+            <param-value>login-page-template.html</param-value>
+            <description>
+                Set the login page template used for both LoginServlet and LDAPLoginServlet. The file needs to be located in the
+                login/src/main/resources/ directory and should contain {0} for where error message go, {1} for where the username
+                included with a GET request goes, and {2} for where the "backurl" goes (redirects the user back to the oozie web
+                console).
+            </description>
+        </init-param>
+        -->
+        <!--
+        <init-param>
+            <param-name>ldap.provider.url</param-name>
+            <param-value>ldap://localhost:389</param-value>
+            <description>The LDAP provider URL for the LDAPLoginServlet</description>
+        </init-param>
+        <init-param>
+            <param-name>ldap.context.factory</param-name>
+            <param-value>com.sun.jndi.ldap.LdapCtxFactory</param-value>
+            <description>The LDAP context factory for the LDAPLoginServlet</description>
+        </init-param>
+        <init-param>
+            <param-name>ldap.security.authentication</param-name>
+            <param-value>simple</param-value>
+            <description>The security authentication type for the LDAPLoginServlet</description>
+        </init-param>
+        -->
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>login</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+
+</web-app>

Added: oozie/trunk/login/src/test/java/org/apache/oozie/authentication/TestExampleAltAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/test/java/org/apache/oozie/authentication/TestExampleAltAuthenticationHandler.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/test/java/org/apache/oozie/authentication/TestExampleAltAuthenticationHandler.java (added)
+++ oozie/trunk/login/src/test/java/org/apache/oozie/authentication/TestExampleAltAuthenticationHandler.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.authentication;
+
+import java.net.URLEncoder;
+import java.text.MessageFormat;
+import java.util.Properties;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.oozie.service.Services;
+import org.apache.oozie.test.XTestCase;
+import org.mockito.Mockito;
+
+public class TestExampleAltAuthenticationHandler extends XTestCase {
+
+    private ExampleAltAuthenticationHandler handler;
+    private final String redirectUrl = "http://foo:11000/oozie-login/?backurl={0}";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        new Services().init();
+        Services.get().getConf().set("oozie.authentication.ExampleAltAuthenticationHandler.redirect.url", redirectUrl);
+        handler = new ExampleAltAuthenticationHandler();
+        Properties props = new Properties();
+        props.setProperty(ExampleAltAuthenticationHandler.PRINCIPAL, getOoziePrincipal());
+        props.setProperty(ExampleAltAuthenticationHandler.KEYTAB, getKeytabFile());
+        try {
+          handler.init(props);
+        } catch (Exception ex) {
+          handler = null;
+          throw ex;
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (handler != null) {
+            handler.destroy();
+            handler = null;
+        }
+        Services.get().destroy();
+        super.tearDown();
+    }
+
+    public void testRedirect() throws Exception {
+        String oozieBaseUrl = Services.get().getConf().get("oozie.base.url");
+        String resolvedRedirectUrl = MessageFormat.format(redirectUrl, URLEncoder.encode(oozieBaseUrl, "ISO-8859-1"));
+
+        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
+
+        // A User-Agent without "java", "curl", "wget", or "perl" (default) in it is considered to be a browser
+        Mockito.when(request.getHeader("User-Agent")).thenReturn("Some Browser");
+        // Pretend the request URL is from oozie.base.url
+        Mockito.when(request.getRequestURL()).thenReturn(new StringBuffer(oozieBaseUrl));
+
+        // The HttpServletResponse needs to return the encoded redirect url
+        Mockito.when(response.encodeRedirectURL(resolvedRedirectUrl)).thenReturn(resolvedRedirectUrl);
+
+        handler.authenticate(request, response);
+        Mockito.verify(response).sendRedirect(resolvedRedirectUrl);
+    }
+
+    public void testAuthenticateCookie() throws Exception {
+        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
+
+        // A User-Agent without "java" in it is considered to be a browser
+        Mockito.when(request.getHeader("User-Agent")).thenReturn("Some Browser");
+
+        // We need the request to return the auth cookie
+        Cookie[] cookies = {new Cookie("some.other.cookie", "someValue"),
+                            new Cookie("oozie.web.login.auth", "someUser")};
+        Mockito.when(request.getCookies()).thenReturn(cookies);
+
+        AuthenticationToken token = handler.authenticate(request, response);
+        assertEquals("someUser", token.getUserName());
+        assertEquals("someUser", token.getName());
+        assertEquals("alt-kerberos", token.getType());
+    }
+}

Added: oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLDAPLoginServlet.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLDAPLoginServlet.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLDAPLoginServlet.java (added)
+++ oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLDAPLoginServlet.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,165 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.servlet.login;
+
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.unit.AbstractServerTest;
+
+// LDAP stuff based on https://cwiki.apache.org/DIRxSRVx10/using-apacheds-for-unit-tests.html
+// The default admin user for Apache DS is "uid=admin,ou=system" and password is "secret"
+public class TestLDAPLoginServlet extends AbstractServerTest {
+
+    // We need to subclass the AbstractServerTest to get the LDAP stuff, so we'll have to do a wrapper to inherit the
+    // TestLoginServlet tests instead of subclassing it
+    TestLoginServlet tls = new TestLoginServlet() {
+        @Override
+        protected Class getServletClass() {
+            // Make the TestLoginServlet use LDAPLoginServlet instead of LoginServlet
+            return LDAPLoginServlet.class;
+        }
+
+        @Override
+        protected Map<String, String> getInitParameters() {
+            // Configure for LDAP tests
+            HashMap<String, String> initParams = new HashMap<String, String>();
+            initParams.put("ldap.provider.url", "o=test");
+            initParams.put("ldap.context.factory", "org.apache.directory.server.jndi.ServerContextFactory");
+            return initParams;
+        }
+    };
+
+    @Override
+    public void setUp() throws Exception {
+        // Add partition 'test'
+        MutablePartitionConfiguration pcfg = new MutablePartitionConfiguration();
+        pcfg.setName("test");
+        pcfg.setSuffix("o=test");
+
+        // Create some indices
+        Set<String> indexedAttrs = new HashSet<String>();
+        indexedAttrs.add("objectClass");
+        indexedAttrs.add("o");
+        pcfg.setIndexedAttributes(indexedAttrs);
+
+        // Create a first entry associated to the partition
+        Attributes attrs = new BasicAttributes(true);
+
+        // First, the objectClass attribute
+        Attribute attr = new BasicAttribute("objectClass");
+        attr.add("top");
+        attr.add("organization");
+        attrs.put(attr);
+
+        // The the 'Organization' attribute
+        attr = new BasicAttribute("o");
+        attr.add("test");
+        attrs.put(attr);
+
+        // Associate this entry to the partition
+        pcfg.setContextEntry(attrs);
+
+        // As we can create more than one partition, we must store
+        // each created partition in a Set before initialization
+        Set<MutablePartitionConfiguration> pcfgs = new HashSet<MutablePartitionConfiguration>();
+        pcfgs.add(pcfg);
+
+        configuration.setContextPartitionConfigurations(pcfgs);
+
+        // Create a working directory
+        File workingDirectory = new File("server-work");
+        configuration.setWorkingDirectory(workingDirectory);
+
+        // Now, let's call the super class which is responsible for the
+        // partitions creation
+        super.setUp();
+
+        // setUp the TestLoginServlet
+        tls.setUp();
+    }
+
+    public void testGetMissingBackurl() throws Exception {
+        tls.testGetMissingBackurl();
+    }
+
+    public void testGetSuccess() throws Exception {
+        tls.testGetSuccess();
+    }
+
+    public void testPostMissingBackurl() throws Exception {
+        tls.testPostMissingBackurl();
+    }
+
+    public void testPostMissingUsernamePassword() throws Exception {
+        tls.testPostMissingUsernamePassword();
+    }
+
+    public void testPostInvalidUsernamePassword() throws Exception {
+        // Valid username, invalid password
+        URL url = new URL(tls.container.getServletURL("/")
+                + "?backurl=http://foo:11000/oozie&username=uid=admin,ou=system&password=bar");
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        String html = tls.getHTML(conn);
+        assertEquals(MessageFormat.format(TestLoginServlet.loginPageTemplate,
+                "<font color=\"red\">Error: Invalid Username or Password</font><br>",
+                "uid=admin,ou=system", "http://foo:11000/oozie"), html);
+
+        // InValid username, valid password
+        url = new URL(tls.container.getServletURL("/")
+                + "?backurl=http://foo:11000/oozie&username=foo&password=secret");
+        conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        html = tls.getHTML(conn);
+        assertEquals(MessageFormat.format(TestLoginServlet.loginPageTemplate,
+                "<font color=\"red\">Error: Invalid Username or Password</font><br>", "foo", "http://foo:11000/oozie"), html);
+    }
+
+    public void testPostSuccess() throws Exception {
+        // Now that its actually going to work successfully, the backurl needs to go somewhere real; about:blank provides a
+        // convinient location that doesn't require internet access or another servlet running locally
+        URL url = new URL(tls.container.getServletURL("/") + "?backurl=about:blank&username=uid=admin,ou=system&password=secret");
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_FOUND, conn.getResponseCode());
+        String cookies = tls.getCookies(conn);
+        assertTrue(cookies.contains("oozie.web.login.auth=uid=admin,ou=system"));
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // tear down the TestLoginServlet
+        tls.tearDown();
+
+        super.tearDown();
+    }
+}

Added: oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLoginServlet.java
URL: http://svn.apache.org/viewvc/oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLoginServlet.java?rev=1415882&view=auto
==============================================================================
--- oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLoginServlet.java (added)
+++ oozie/trunk/login/src/test/java/org/apache/oozie/servlet/login/TestLoginServlet.java Fri Nov 30 23:09:35 2012
@@ -0,0 +1,195 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.oozie.servlet.login;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+import junit.framework.TestCase;
+import org.apache.oozie.test.EmbeddedServletContainer;
+
+public class TestLoginServlet extends TestCase {
+
+    protected EmbeddedServletContainer container;
+    protected static String loginPageTemplate;
+
+    static {
+        try {
+            StringBuilder sb = new StringBuilder();
+            InputStream is = new FileInputStream(new File("src/main/resources/login-page-template.html"));
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = br.readLine();
+            while (line != null) {
+                sb.append(line).append("\n");
+                line = br.readLine();
+            }
+            br.close();
+            loginPageTemplate = sb.toString();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+            fail("Unable to read login-page-template.html");
+        }
+    }
+
+    protected Class getServletClass() {
+        return LoginServlet.class;
+    }
+
+    protected Map<String, String> getInitParameters() {
+        return null;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        container = new EmbeddedServletContainer("oozie-login");
+        container.addServletEndpoint("/", getServletClass(), getInitParameters());
+        container.start();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (container != null) {
+            container.stop();
+        }
+        super.tearDown();
+    }
+
+    public void testGetMissingBackurl() throws Exception {
+        URL url = new URL(container.getServletURL("/"));
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("GET");
+        assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+        assertEquals("missing or invalid 'backurl' parameter", conn.getResponseMessage());
+    }
+
+    public void testGetSuccess() throws Exception {
+        URL url = new URL(container.getServletURL("/") + "?backurl=http://foo:11000/oozie");
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("GET");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        String html = getHTML(conn);
+        assertEquals(MessageFormat.format(loginPageTemplate, "", "", "http://foo:11000/oozie"), html);
+
+        // With optional username parameter
+        url = new URL(container.getServletURL("/") + "?backurl=http://foo:11000/oozie&username=foo");
+        conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("GET");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        html = getHTML(conn);
+        assertEquals(MessageFormat.format(loginPageTemplate, "", "foo", "http://foo:11000/oozie"), html);
+    }
+
+    public void testPostMissingBackurl() throws Exception {
+        // Missing all
+        URL url = new URL(container.getServletURL("/"));
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+        assertEquals("missing or invalid 'backurl' parameter", conn.getResponseMessage());
+
+        // Missing only backurl
+        url = new URL(container.getServletURL("/") + "?username=foo&password=bar");
+        conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+        assertEquals("missing or invalid 'backurl' parameter", conn.getResponseMessage());
+    }
+
+    public void testPostMissingUsernamePassword() throws Exception {
+        // Missing password
+        URL url = new URL(container.getServletURL("/") + "?backurl=http://foo:11000/oozie&username=foo");
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        String html = getHTML(conn);
+        assertEquals(MessageFormat.format(loginPageTemplate,
+                "<font color=\"red\">Error: Invalid Username or Password</font><br>", "foo", "http://foo:11000/oozie"), html);
+
+        // Missing username
+        url = new URL(container.getServletURL("/") + "?backurl=http://foo:11000/oozie&password=bar");
+        conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        html = getHTML(conn);
+        assertEquals(MessageFormat.format(loginPageTemplate,
+                "<font color=\"red\">Error: Invalid Username or Password</font><br>", "", "http://foo:11000/oozie"), html);
+
+        // Missing both
+        url = new URL(container.getServletURL("/") + "?backurl=http://foo:11000/oozie");
+        conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        html = getHTML(conn);
+        assertEquals(MessageFormat.format(loginPageTemplate,
+                "<font color=\"red\">Error: Invalid Username or Password</font><br>", "", "http://foo:11000/oozie"), html);
+    }
+
+    public void testPostInvalidUsernamePassword() throws Exception {
+        URL url = new URL(container.getServletURL("/") + "?backurl=http://foo:11000/oozie&username=foo&password=bar");
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_OK, conn.getResponseCode());
+        String html = getHTML(conn);
+        assertEquals(MessageFormat.format(loginPageTemplate,
+                "<font color=\"red\">Error: Invalid Username or Password</font><br>", "foo", "http://foo:11000/oozie"), html);
+    }
+
+    public void testPostSuccess() throws Exception {
+        // Now that its actually going to work successfully, the backurl needs to go somewhere real; about:blank provides a
+        // convinient location that doesn't require internet access or another servlet running locally
+        URL url = new URL(container.getServletURL("/") + "?backurl=about:blank&username=foo&password=foo");
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        conn.setRequestMethod("POST");
+        assertEquals(HttpServletResponse.SC_FOUND, conn.getResponseCode());
+        String cookies = getCookies(conn);
+        assertTrue(cookies.contains("oozie.web.login.auth=foo"));
+    }
+
+    protected String getHTML(HttpURLConnection conn) throws Exception {
+        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+        String line;
+        StringBuilder htmlBuilder = new StringBuilder();
+        while ((line = br.readLine()) != null) {
+            htmlBuilder.append(line);
+            htmlBuilder.append("\n");
+        }
+        br.close();
+        return htmlBuilder.toString();
+    }
+
+    protected String getCookies(HttpURLConnection conn) throws Exception {
+        Map<String, List<String>> headers = conn.getHeaderFields();
+        for (String key : headers.keySet()) {
+            if (key != null && key.equals("Set-Cookie")) {
+                List<String> cookies = headers.get(key);
+                return cookies.get(0);
+            }
+        }
+        return null;
+    }
+}

Modified: oozie/trunk/pom.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/pom.xml?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/pom.xml (original)
+++ oozie/trunk/pom.xml Fri Nov 30 23:09:35 2012
@@ -924,5 +924,14 @@
                <hadoop.auth.version>3.0.0-SNAPSHOT</hadoop.auth.version>
             </properties>
         </profile>
+        <profile>
+            <id>loginServerExample</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <modules>
+                <module>login</module>
+            </modules>
+        </profile>
     </profiles>
 </project>

Modified: oozie/trunk/release-log.txt
URL: http://svn.apache.org/viewvc/oozie/trunk/release-log.txt?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/release-log.txt (original)
+++ oozie/trunk/release-log.txt Fri Nov 30 23:09:35 2012
@@ -1,5 +1,6 @@
 -- Oozie 3.4.0 release (trunk - unreleased)
 
+OOZIE-1103 Create example using AltKerberosAuthenticationHandler (rkanter)
 OOZIE-1106 latest and future function do not work correctly when oozie processing timezone is non UTC (rohini via tucu)
 OOZIE-1102 Update Oozie README.txt to have the TLP mailing list and links (jaoki via rkanter)
 OOZIE-1096 Update wfgen README.txt to have the TLP mailing list (jun aoki via rkanter)

Modified: oozie/trunk/src/main/assemblies/distro.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/src/main/assemblies/distro.xml?rev=1415882&r1=1415881&r2=1415882&view=diff
==============================================================================
--- oozie/trunk/src/main/assemblies/distro.xml (original)
+++ oozie/trunk/src/main/assemblies/distro.xml Fri Nov 30 23:09:35 2012
@@ -107,6 +107,16 @@
             </includes>
             <fileMode>0555</fileMode>
         </fileSet>
+        <!--  Oozie Login Server Example war and jar -->
+         <fileSet>
+            <directory>${basedir}/../login/target</directory>
+            <outputDirectory>/</outputDirectory>
+            <includes>
+                <include>oozie-login.war</include>
+                <include>oozie-login.jar</include>
+            </includes>
+            <fileMode>0555</fileMode>
+        </fileSet>
 
     </fileSets>
     <files>