Hi,

JASPI make us to plug in other authentication module (for example, using openID 
or other mechanism to authenticate users) easier on Jetty for container-managed 
security. There is jetty-jaspi module in the Jetty source code base, but I 
think it has not been maintained for a long time.

By making a little fix on jetty-jaspi module, I make the JASPI works well on 
Jetty 8. I attach the changes. I hope if I did it well, it could be merged into 
the next Jetty 8 release.

To use JASPI:

1.       Creating a JASPI configuration XML file for your auth module:

<?xml version="1.0" encoding="UTF-8"?>

<jaspi xmlns="http://geronimo.apache.org/xml/ns/geronimo-jaspi";>



    <configProvider>

        <messageLayer>HTTP</messageLayer>

        <appContext>/ui</appContext>

        <description>description</description>

        <serverAuthConfig>

            
<authenticationContextID>authenticationContextID2</authenticationContextID>

            <protected>true</protected>

            <serverAuthContext>

                <serverAuthModule>

                    
<className>org.eclipse.jetty.security.jaspi.modules.FormAuthModule</className>

                    <options>

                       
org.eclipse.jetty.security.jaspi.modules.LoginPage=/secure/jaaslogin

                       
org.eclipse.jetty.security.jaspi.modules.ErrorPage=/secure/jaaserror

                    </options>

                </serverAuthModule>

            </serverAuthContext>

        </serverAuthConfig>

        <persistent>true</persistent>

    </configProvider>

</jaspi>
   The above using the Jetty built-in FormAuthModule, if you want to use the 
built-in Basic or Digest auth module, the <serverAuthModule> part should be:
                <serverAuthModule>
                    
<className>org.eclipse.jetty.security.jaspi.modules.DigestAuthModule</className>
                    <options>
                       
org.eclipse.jetty.security.jaspi.modules.RealmName=JAASRealm
                    </options>
                </serverAuthModule>
Or
                <serverAuthModule>
                    
<className>org.eclipse.jetty.security.jaspi.modules.BasicAuthModule</className>
                    <options>
                       
org.eclipse.jetty.security.jaspi.modules.RealmName=JAASRealm
                    </options>
                </serverAuthModule>

Here I use the implementation of JASPI of geronimo-jaspi 2.0-SNAPSHOT from 
geronimo-jaspi (https://github.com/apache/geronimo-jaspi). you could use 
geronimo-jaspi 1.1.1 release, but you might need to implements 
javax.security.auth.message.config.AuthConfigProvider (and not 
javax.security.auth.message.module.ServerAuthModule) and configure it in the 
above XML file.


2.       Configuring your AppContext to use JaspiAuthenticatorFactory. I 
configured it in jetty-web.xml file:

<Configure class="org.eclipse.jetty.webapp.WebAppContext">

  <Set name="securityHandler">

      <New class="org.eclipse.jetty.security.ConstraintSecurityHandler">

         <Set name="loginService">

            <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">

              <Set name="name">JAASRealm</Set>

              <Set name="loginModuleName">jaas</Set>

            </New>

         </Set>



         <Set name="authenticatorFactory">

             <New 
class="org.eclipse.jetty.security.jaspi.JaspiAuthenticatorFactory" />

         </Set>

      </New>

  </Set>

</Configure>


3.       When launching Jetty, using 
-Dorg.apache.geronimo.jaspic.configurationFile to tell geronimo-jaspi where to 
find the JASPI configuration file. The following is the jetty-maven-plugin 
configuration (my JASPI configuration file is form-test-jaspi-2.xml):

      <plugin>

        <groupId>org.mortbay.jetty</groupId>

        <artifactId>jetty-maven-plugin</artifactId>

        <version>${jetty.version}</version>

       <configuration>

           <scanIntervalSeconds>10</scanIntervalSeconds>

           <webAppConfig>

              <contextPath>/ui</contextPath>

              <parentLoaderPriority>true</parentLoaderPriority>

          </webAppConfig>



          <systemProperties>

             <systemProperty>

               <name>java.security.auth.login.config</name>

               <value>./conf/jetty/jaas.conf</value>

            </systemProperty>

            <systemProperty>

              <name>org.apache.geronimo.jaspic.configurationFile</name>

              <value>./conf/jaspi/form-test-jaspi-2.xml</value>

            </systemProperty>

        </configuration>

        <dependencies>

           <dependency>

              <groupId>org.eclipse.jetty</groupId>

              <artifactId>jetty-jaspi</artifactId>

              <version>${jetty.version}</version>

           </dependency>

           <dependency>

              <groupId>org.apache.geronimo.components</groupId>

              <artifactId>geronimo-jaspi</artifactId>

              <version>2.0-SNAPSHOT</version>

           </dependency>

         </dependencies>

      </plugin>
In my test, I use my own JAAS login module, which configured in jaas.conf. you 
could use Jetty built-in login module as described in 
http://wiki.eclipse.org/Jetty/Feature/JAAS.

Q: what I cannot know if I did it correctly is the changes to 
JaspiAuthenticator. I removed the following lines at the beginning of 
validateRequest():
        if (_allowLazyAuthentication && !mandatory)
            return _deferred;
and add the following:
                     if ( !isMandatory( messageInfo ) )
                               return _deferred ;
after "if (authStatus == AuthStatus.SUCCESS)" at line 114. This make me have to 
add the following in validateRequest() of FormAuthModule class:
            Authentication authentication = 
((org.eclipse.jetty.server.Request)request).getAuthentication() ;
            if (authentication instanceof Authentication.Deferred )
                return AuthStatus.SEND_SUCCESS;
or the unprotected resources won't be handled properly. This makes the auth 
module to have to know how Authentication.Deferred works, it is not an easier 
to use dependency. This might be that I do not understand 
DeferredAuthentication class clearly. Your advice is appreciated.

Thanks.


diff --git 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
index dbdb0b4..5915568 100644
--- 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
+++ 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
@@ -20,6 +20,7 @@ import java.util.Set;
 import javax.security.auth.Subject;
 import javax.security.auth.message.AuthException;
 import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
 import javax.security.auth.message.callback.CallerPrincipalCallback;
 import javax.security.auth.message.callback.GroupPrincipalCallback;
 import javax.security.auth.message.config.ServerAuthConfig;
@@ -79,8 +80,8 @@ public class JaspiAuthenticator implements Authenticator
 
     public Authentication validateRequest(ServletRequest request, 
ServletResponse response, boolean mandatory) throws ServerAuthException
     {
-        if (_allowLazyAuthentication && !mandatory)
-            return _deferred;
+        //** if (_allowLazyAuthentication && !mandatory)
+        //    return _deferred;
         
         JaspiMessageInfo info = new JaspiMessageInfo(request, response, 
mandatory);
         request.setAttribute("org.eclipse.jetty.security.jaspi.info",info);
@@ -94,6 +95,13 @@ public class JaspiAuthenticator implements Authenticator
         if (info==null) throw new NullPointerException("MeesageInfo from 
request missing: " + req);
         return secureResponse(info,validatedUser);
     }
+
+    protected boolean isMandatory(MessageInfo messageInfo)
+    {
+        String mandatory = (String) 
messageInfo.getMap().get(JaspiMessageInfo.MANDATORY_KEY);
+        if (mandatory == null) return false;
+        return Boolean.valueOf(mandatory);
+    }
     
     public Authentication validateRequest(JaspiMessageInfo messageInfo) throws 
ServerAuthException
     {
@@ -113,6 +121,10 @@ public class JaspiAuthenticator implements Authenticator
             
             if (authStatus == AuthStatus.SUCCESS)
             {
+               // **
+               if ( !isMandatory( messageInfo ) )
+                       return _deferred ;
+               
             Set<UserIdentity> ids = 
clientSubject.getPrivateCredentials(UserIdentity.class);
                 UserIdentity userIdentity;
                 if (ids.size() > 0)
diff --git 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
index 3501509..11dd3a4 100644
--- 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
+++ 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java
@@ -4,11 +4,11 @@
 // All rights reserved. This program and the accompanying materials
 // are made available under the terms of the Eclipse Public License v1.0
 // and Apache License v2.0 which accompanies this distribution.
-// The Eclipse Public License is available at 
+// The Eclipse Public License is available at
 // http://www.eclipse.org/legal/epl-v10.html
 // The Apache License v2.0 is available at
 // http://www.opensource.org/licenses/apache2.0.php
-// You may elect to redistribute this code under either of these licenses. 
+// You may elect to redistribute this code under either of these licenses.
 // ========================================================================
 
 package org.eclipse.jetty.security.jaspi;
@@ -41,10 +41,10 @@ public class JaspiAuthenticatorFactory extends 
DefaultAuthenticatorFactory
     private static final Logger LOG = 
Log.getLogger(JaspiAuthenticatorFactory.class);
 
     private static String MESSAGE_LAYER = "HTTP";
-    
+
     private Subject _serviceSubject;
     private String _serverName;
-    
+
 
     /* ------------------------------------------------------------ */
     /**
@@ -86,7 +86,7 @@ public class JaspiAuthenticatorFactory extends 
DefaultAuthenticatorFactory
     public Authenticator getAuthenticator(Server server, ServletContext 
context, AuthConfiguration configuration, IdentityService identityService, 
LoginService loginService)
     {
         Authenticator authenticator=null;
-        try 
+        try
         {
             AuthConfigFactory authConfigFactory = 
AuthConfigFactory.getFactory();
             RegistrationListener listener = new RegistrationListener()
@@ -97,9 +97,12 @@ public class JaspiAuthenticatorFactory extends 
DefaultAuthenticatorFactory
 
             Subject serviceSubject=findServiceSubject(server);
             String serverName=findServerName(server,serviceSubject);
-            
-            
-            String appContext = serverName + " " + context.getContextPath();
+
+
+            // Spce. section 3.2 P27: AppContextID ::= hostname blank 
context-path
+            // but here we simplify it by only using the contextPath as the 
app context id.
+            //** String appContext = serverName + " " + 
context.getContextPath();
+            String appContext = context.getContextPath();
             AuthConfigProvider authConfigProvider = 
authConfigFactory.getConfigProvider(MESSAGE_LAYER,appContext,listener);
             if (authConfigProvider != null)
             {
@@ -114,8 +117,8 @@ public class JaspiAuthenticatorFactory extends 
DefaultAuthenticatorFactory
                                 serviceSubject,true, identityService);
                 }
             }
-        } 
-        catch (AuthException e) 
+        }
+        catch (AuthException e)
         {
             LOG.warn(e);
         }
@@ -124,7 +127,7 @@ public class JaspiAuthenticatorFactory extends 
DefaultAuthenticatorFactory
 
     /* ------------------------------------------------------------ */
     /** Find a service Subject.
-     * If {@link #setServiceSubject(Subject)} has not been used to 
+     * If {@link #setServiceSubject(Subject)} has not been used to
      * set a subject, then the {@link Server#getBeans(Class)} method is
      * used to look for a Subject.
      */
@@ -154,7 +157,7 @@ public class JaspiAuthenticatorFactory extends 
DefaultAuthenticatorFactory
             if (principals!=null && !principals.isEmpty())
                 return principals.iterator().next().getName();
         }
-        
+
         return "server";
     }
 }
diff --git 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
index d11a8d9..df6f5b7 100644
--- 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
+++ 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/ServletCallbackHandler.java
@@ -94,6 +94,9 @@ public class ServletCallbackHandler implements CallbackHandler
 
                     
credentialValidationCallback.getSubject().getPrincipals().addAll(user.getSubject().getPrincipals());
                     
credentialValidationCallback.getSubject().getPrivateCredentials().add(user);
+                    
+                    //*** 
+                    
credentialValidationCallback.getSubject().getPrivateCredentials().add( 
loginCallback ) ;
                 }
             }
             // server to jaspi communication
diff --git 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
index bd66f60..247ae50 100644
--- 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
+++ 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java
@@ -90,7 +90,8 @@ public class BaseAuthModule implements ServerAuthModule, 
ServerAuthContext
     public AuthStatus secureResponse(MessageInfo messageInfo, Subject 
serviceSubject) throws AuthException
     {
         // servlets do not need secured responses
-        return AuthStatus.SUCCESS;
+        //** return AuthStatus.SUCCESS;
+        return AuthStatus.SEND_SUCCESS ;
     }
 
     public AuthStatus validateRequest(MessageInfo messageInfo, Subject 
clientSubject, Subject serviceSubject) throws AuthException
diff --git 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
index ab46f0a..efd67b5 100644
--- 
a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
+++ 
b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
@@ -4,11 +4,11 @@
 // All rights reserved. This program and the accompanying materials
 // are made available under the terms of the Eclipse Public License v1.0
 // and Apache License v2.0 which accompanies this distribution.
-// The Eclipse Public License is available at 
+// The Eclipse Public License is available at
 // http://www.eclipse.org/legal/epl-v10.html
 // The Apache License v2.0 is available at
 // http://www.opensource.org/licenses/apache2.0.php
-// You may elect to redistribute this code under either of these licenses. 
+// You may elect to redistribute this code under either of these licenses.
 // ========================================================================
 
 package org.eclipse.jetty.security.jaspi.modules;
@@ -37,6 +37,8 @@ import org.eclipse.jetty.http.security.Constraint;
 import org.eclipse.jetty.http.security.Password;
 import org.eclipse.jetty.security.CrossContextPsuedoSession;
 import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
+import org.eclipse.jetty.server.Authentication;
+import org.eclipse.jetty.server.UserIdentity;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.URIUtil;
 import org.eclipse.jetty.util.log.Log;
@@ -89,7 +91,7 @@ public class FormAuthModule extends BaseAuthModule
         setErrorPage(errorPage);
     }
 
-    public FormAuthModule(CallbackHandler callbackHandler, 
CrossContextPsuedoSession<UserInfo> ssoSource, 
+    public FormAuthModule(CallbackHandler callbackHandler, 
CrossContextPsuedoSession<UserInfo> ssoSource,
                           String loginPage, String errorPage)
     {
         super(callbackHandler);
@@ -99,8 +101,8 @@ public class FormAuthModule extends BaseAuthModule
     }
 
     @Override
-    public void initialize(MessagePolicy requestPolicy, MessagePolicy 
responsePolicy, 
-                           CallbackHandler handler, Map options) 
+    public void initialize(MessagePolicy requestPolicy, MessagePolicy 
responsePolicy,
+                           CallbackHandler handler, Map options)
     throws AuthException
     {
         super.initialize(requestPolicy, responsePolicy, handler, options);
@@ -149,7 +151,10 @@ public class FormAuthModule extends BaseAuthModule
         HttpServletRequest request = (HttpServletRequest) 
messageInfo.getRequestMessage();
         HttpServletResponse response = (HttpServletResponse) 
messageInfo.getResponseMessage();
         HttpSession session = request.getSession(isMandatory(messageInfo));
-        String uri = request.getPathInfo();
+
+        //** String uri = request.getPathInfo();
+        String uri = request.getRequestURI();
+
         // not mandatory and not authenticated
         if (session == null || isLoginOrErrorPage(uri)) return 
AuthStatus.SUCCESS;
 
@@ -192,15 +197,19 @@ public class FormAuthModule extends BaseAuthModule
                 }
                 // TODO is this correct response if isMandatory false??? Can
                 // that occur?
-                return AuthStatus.SEND_FAILURE;
+                //** return AuthStatus.SEND_FAILURE;
+                return AuthStatus.SEND_CONTINUE;
             }
             // Check if the session is already authenticated.
             FormCredential form_cred = (FormCredential) 
session.getAttribute(__J_AUTHENTICATED);
 
             if (form_cred != null)
-            {
-                boolean success = tryLogin(messageInfo, clientSubject, 
response, session, form_cred._jUserName, new Password(new 
String(form_cred._jPassword)));
-                if (success) { return AuthStatus.SUCCESS; }
+            {   //**
+                //** boolean success = tryLogin(messageInfo, clientSubject, 
response, session, form_cred._jUserName, new Password(new 
String(form_cred._jPassword)));
+               clientSubject.getPrivateCredentials().addAll( 
form_cred._subject.getPrivateCredentials( UserIdentity.class) ) ;
+                //** if (success) { return AuthStatus.SUCCESS; }
+                return AuthStatus.SUCCESS;
+
                 // CallbackHandler loginCallbackHandler = new
                 // UserPasswordCallbackHandler(form_cred._jUserName,
                 // form_cred._jPassword);
@@ -299,13 +308,24 @@ public class FormAuthModule extends BaseAuthModule
             // TODO verify this is correct action
                 return AuthStatus.SUCCESS;
 
+            //** Don't auth the Deferred authentication.
+            Authentication authentication = 
((org.eclipse.jetty.server.Request)request).getAuthentication() ;
+            if (authentication instanceof Authentication.Deferred )
+                return AuthStatus.SEND_SUCCESS;
+
             // redirect to login page
-            if (request.getQueryString() != null) uri += "?" + 
request.getQueryString();
-            session.setAttribute(__J_URI, request.getScheme() + "://"
-                                          + request.getServerName()
-                                          + ":"
-                                          + request.getServerPort()
-                                          + 
URIUtil.addPaths(request.getContextPath(), uri));
+            // ** if (request.getQueryString() != null) uri += "?" + 
request.getQueryString();
+            // ** session.setAttribute(__J_URI, request.getScheme() + "://"
+            // **                               + request.getServerName()
+            // **                               + ":"
+            // **                               + request.getServerPort()
+            // **                               + 
URIUtil.addPaths(request.getContextPath(), uri));
+
+            // **
+            StringBuffer buf = request.getRequestURL();
+            if (request.getQueryString() != null)
+                buf.append("?").append(request.getQueryString());
+            session.setAttribute(__J_URI, buf.toString());
             response.setContentLength(0);
             
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),
 _formLoginPage)));
             return AuthStatus.SEND_CONTINUE;
@@ -321,9 +341,9 @@ public class FormAuthModule extends BaseAuthModule
 
     }
 
-    private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject, 
-                             HttpServletResponse response, HttpSession 
session, 
-                             String username, Password password) 
+    private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject,
+                             HttpServletResponse response, HttpSession session,
+                             String username, Password password)
     throws AuthException, IOException, UnsupportedCallbackException
     {
         if (login(clientSubject, username, password, Constraint.__FORM_AUTH, 
messageInfo))
@@ -333,7 +353,7 @@ public class FormAuthModule extends BaseAuthModule
             if (!loginCallbacks.isEmpty())
             {
                 LoginCallbackImpl loginCallback = 
loginCallbacks.iterator().next();
-                FormCredential form_cred = new FormCredential(username, 
pwdChars, loginCallback.getUserPrincipal());
+                FormCredential form_cred = new FormCredential(username, 
pwdChars, loginCallback.getUserPrincipal(), loginCallback.getSubject() );
 
                 session.setAttribute(__J_AUTHENTICATED, form_cred);
             }
@@ -394,11 +414,17 @@ public class FormAuthModule extends BaseAuthModule
 
         transient Principal _userPrincipal;
 
-        private FormCredential(String _jUserName, char[] _jPassword, Principal 
_userPrincipal)
+        //**
+        transient Subject _subject ;
+
+        private FormCredential(String _jUserName, char[] _jPassword, Principal 
_userPrincipal, Subject subject)
         {
             this._jUserName = _jUserName;
             this._jPassword = _jPassword;
             this._userPrincipal = _userPrincipal;
+
+            //**
+            this._subject = subject ;
         }
 
         public void valueBound(HttpSessionBindingEvent event)
_______________________________________________
jetty-users mailing list
jetty-users@eclipse.org
https://dev.eclipse.org/mailman/listinfo/jetty-users

Reply via email to