Home » geronimo-2.2-source-release » org.apache.geronimo.tomcat.realm » [javadoc | source]

    1   /**
    2    *
    3    * Copyright 2003-2004 The Apache Software Foundation
    4    *
    5    *  Licensed under the Apache License, Version 2.0 (the "License");
    6    *  you may not use this file except in compliance with the License.
    7    *  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   package org.apache.geronimo.tomcat.realm;
   18   
   19   import java.io.IOException;
   20   import java.security.AccessControlContext;
   21   import java.security.AccessControlException;
   22   import java.security.Principal;
   23   
   24   import javax.security.auth.Subject;
   25   import javax.security.auth.login.AccountExpiredException;
   26   import javax.security.auth.login.CredentialExpiredException;
   27   import javax.security.auth.login.FailedLoginException;
   28   import javax.security.auth.login.LoginContext;
   29   import javax.security.auth.login.LoginException;
   30   import javax.security.jacc.PolicyContext;
   31   import javax.security.jacc.PolicyContextException;
   32   import javax.security.jacc.WebResourcePermission;
   33   import javax.security.jacc.WebRoleRefPermission;
   34   import javax.security.jacc.WebUserDataPermission;
   35   import javax.servlet.http.HttpServletRequest;
   36   
   37   import org.apache.catalina.Context;
   38   import org.apache.catalina.LifecycleException;
   39   import org.apache.catalina.connector.Request;
   40   import org.apache.catalina.connector.Response;
   41   import org.apache.catalina.deploy.LoginConfig;
   42   import org.apache.catalina.deploy.SecurityConstraint;
   43   import org.apache.catalina.realm.JAASCallbackHandler;
   44   import org.apache.catalina.realm.JAASRealm;
   45   import org.apache.commons.logging.Log;
   46   import org.apache.commons.logging.LogFactory;
   47   import org.apache.geronimo.security.ContextManager;
   48   import org.apache.geronimo.security.jacc.PolicyContextHandlerContainerSubject;
   49   import org.apache.geronimo.tomcat.JAASTomcatPrincipal;
   50   
   51   
   52   public class TomcatGeronimoRealm extends JAASRealm {
   53   
   54       private static final Log log = LogFactory.getLog(TomcatGeronimoRealm.class);
   55   
   56   //    private Context context = null;
   57       private static ThreadLocal currentRequest = new ThreadLocal();
   58       
   59       private boolean enabled = false;
   60   
   61       /**
   62        * Descriptive information about this <code>Realm</code> implementation.
   63        */
   64       protected static final String info = "org.apache.geronimo.tomcat.TomcatGeronimoRealm/1.0";
   65   
   66       /**
   67        * Descriptive information about this <code>Realm</code> implementation.
   68        */
   69       protected static final String name = "TomcatGeronimoRealm";
   70   
   71       public TomcatGeronimoRealm() {
   72   
   73        }
   74   
   75       /**
   76        * Enforce any user data constraint required by the security constraint
   77        * guarding this request URI.  Return <code>true</code> if this constraint
   78        * was not violated and processing should continue, or <code>false</code>
   79        * if we have created a response already.
   80        *
   81        * @param request     Request we are processing
   82        * @param response    Response we are creating
   83        * @param constraints Security constraint being checked
   84        * @throws IOException if an input/output error occurs
   85        */
   86       public boolean hasUserDataPermission(Request request,
   87                                            Response response,
   88                                            SecurityConstraint[] constraints)
   89               throws IOException {
   90   
   91           //Get an authenticated subject, if there is one
   92           Subject subject = null;
   93           try {
   94   
   95               //We will use the PolicyContextHandlerContainerSubject.HANDLER_KEY to see if a user
   96               //has authenticated, since a request.getUserPrincipal() will not pick up the user
   97               //unless its using a cached session.
   98               subject = (Subject) PolicyContext.getContext(PolicyContextHandlerContainerSubject.HANDLER_KEY);
   99   
  100           } catch (PolicyContextException e) {
  101               log.error(e);
  102           }
  103   
  104           //If nothing has authenticated yet, do the normal
  105           if (subject == null)
  106               return super.hasUserDataPermission(request, response, constraints);
  107   
  108           ContextManager.setCurrentCaller(subject);
  109   
  110           try {
  111   
  112               AccessControlContext acc = ContextManager.getCurrentContext();
  113   
  114               /**
  115                * JACC v1.0 secion 4.1.1
  116                */
  117               WebUserDataPermission wudp = new WebUserDataPermission(request);
  118               acc.checkPermission(wudp);
  119   
  120           } catch (AccessControlException ace) {
  121               response.sendError(Response.SC_FORBIDDEN);
  122               return false;
  123           }
  124   
  125           return true;
  126       }
  127   
  128       /**
  129        * Perform access control based on the specified authorization constraint.
  130        * Return <code>true</code> if this constraint is satisfied and processing
  131        * should continue, or <code>false</code> otherwise.
  132        *
  133        * @param request    Request we are processing
  134        * @param response   Response we are creating
  135        * @param constraint Security constraint we are enforcing
  136        * @param context    The Context to which client of this class is attached.
  137        * @throws java.io.IOException if an input/output error occurs
  138        */
  139       public boolean hasResourcePermission(Request request,
  140                                            Response response,
  141                                            SecurityConstraint[] constraint,
  142                                            Context context)
  143               throws IOException {
  144   
  145           //Set the current request (for hasRole)
  146           currentRequest.set(request);
  147   
  148           // Specifically allow access to the form login and form error pages
  149           // and the "j_security_check" action
  150           LoginConfig config = context.getLoginConfig();
  151           if ((config != null) &&
  152               (org.apache.catalina.realm.Constants.FORM_METHOD.equals(config.getAuthMethod()))) {
  153               String requestURI = request.getDecodedRequestURI();
  154               String loginPage = context.getPath() + config.getLoginPage();
  155               if (loginPage.equals(requestURI)) {
  156                   if (log.isDebugEnabled())
  157                       log.debug(" Allow access to login page " + loginPage);
  158                   return (true);
  159               }
  160               String errorPage = context.getPath() + config.getErrorPage();
  161               if (errorPage.equals(requestURI)) {
  162                   if (log.isDebugEnabled())
  163                       log.debug(" Allow access to error page " + errorPage);
  164                   return (true);
  165               }
  166               if (requestURI.endsWith(org.apache.catalina.realm.Constants.FORM_ACTION)) {
  167                   if (log.isDebugEnabled())
  168                       log.debug(" Allow access to username/password submission");
  169                   return (true);
  170               }
  171           }
  172   
  173           // Which user principal have we already authenticated?
  174           Principal principal = request.getUserPrincipal();
  175   
  176           //If we have no principal, then we should use the default.
  177           if (principal == null) {
  178               return false;
  179           } else {
  180               ContextManager.setCurrentCaller(((JAASTomcatPrincipal) principal).getSubject());
  181           }
  182   
  183           try {
  184   
  185               AccessControlContext acc = ContextManager.getCurrentContext();
  186   
  187   
  188               /**
  189                * JACC v1.0 secion 4.1.2
  190                */
  191               acc.checkPermission(new WebResourcePermission(request));
  192   
  193           } catch (AccessControlException ace) {
  194               response.sendError(Response.SC_FORBIDDEN);
  195               return false;
  196           }
  197   
  198           return true;
  199   
  200       }
  201   
  202       private String getServletName(Request request) {
  203   
  204           String contextPath = ((HttpServletRequest) request.getRequest()).getContextPath();
  205           String requestURI = request.getDecodedRequestURI();
  206           String relativeURI = requestURI.substring(contextPath.length());
  207           String servletPath = relativeURI;
  208           String name = null;
  209           Context context = request.getContext();
  210   
  211           //Try exact match
  212           if (!(relativeURI.equals("/")))
  213               name = context.findServletMapping(relativeURI);
  214   
  215           //Try prefix match (i.e. xyz/* )
  216           if (name == null) {
  217               servletPath = relativeURI;
  218               while (true) {
  219                   name = context.findServletMapping(servletPath + "/*");
  220                   if (name != null) {
  221                       break;
  222                   }
  223                   int slash = servletPath.lastIndexOf('/');
  224                   if (slash < 0)
  225                       break;
  226                   servletPath = servletPath.substring(0, slash);
  227               }
  228           }
  229   
  230           //Try extension match (i.e. *.do )
  231           if (name == null) {
  232               int slash = relativeURI.lastIndexOf('/');
  233               if (slash >= 0) {
  234                   String last = relativeURI.substring(slash);
  235                   int period = last.lastIndexOf('.');
  236                   if (period >= 0) {
  237                       String pattern = "*" + last.substring(period);
  238                       name = context.findServletMapping(pattern);
  239                   }
  240               }
  241           }
  242   
  243           //Try default match
  244           if (name == null) {
  245               name = context.findServletMapping("/");
  246           }
  247   
  248           /**
  249            * JACC v1.0 secion B.19
  250            */
  251           if (name.equals("jsp")) {
  252               name = "";
  253           }
  254   
  255           return (name == null ? "" : name);
  256       }
  257   
  258       /**
  259        * Return <code>true</code> if the specified Principal has the specified
  260        * security role, within the context of this Realm; otherwise return
  261        * <code>false</code>.
  262        *
  263        * @param principal Principal for whom the role is to be checked
  264        * @param role      Security role to be checked
  265        */
  266       public boolean hasRole(Principal principal, String role) {
  267   
  268           if ((principal == null) || (role == null) || !(principal instanceof JAASTomcatPrincipal)) {
  269               return false;
  270           }
  271   
  272           Request request = (Request) currentRequest.get();
  273           if (currentRequest == null) {
  274               log.error("No currentRequest found.");
  275               return false;
  276           }
  277   
  278           String name = getServletName(request);
  279   
  280           //Set the caller
  281           ContextManager.setCurrentCaller(((JAASTomcatPrincipal) principal).getSubject());
  282   
  283           AccessControlContext acc = ContextManager.getCurrentContext();
  284   
  285           try {
  286               /**
  287                * JACC v1.0 secion 4.1.3
  288                */
  289               acc.checkPermission(new WebRoleRefPermission(name, role));
  290           } catch (AccessControlException e) {
  291               return false;
  292           }
  293   
  294           return true;
  295       }
  296   
  297       /**
  298        * Return the <code>Principal</code> associated with the specified
  299        * username and credentials, if there is one; otherwise return
  300        * <code>null</code>.
  301        * <p/>
  302        * If there are any errors with the JDBC connection, executing the query or
  303        * anything we return null (don't authenticate). This event is also logged,
  304        * and the connection will be closed so that a subsequent request will
  305        * automatically re-open it.
  306        *
  307        * @param username    Username of the <code>Principal</code> to look up
  308        * @param credentials Password or other credentials to use in authenticating this
  309        *                    username
  310        */
  311       public Principal authenticate(String username, String credentials) {
  312   
  313           // Establish a LoginContext to use for authentication
  314           try {
  315               LoginContext loginContext = null;
  316               if (appName == null)
  317                   appName = "Tomcat";
  318   
  319               if (log.isDebugEnabled())
  320                   log.debug(sm.getString("jaasRealm.beginLogin", username, appName));
  321   
  322               // What if the LoginModule is in the container class loader ?
  323               ClassLoader ocl = null;
  324   
  325               if (isUseContextClassLoader()) {
  326                   ocl = Thread.currentThread().getContextClassLoader();
  327                   Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
  328               }
  329   
  330               try {
  331                   loginContext = new LoginContext(appName, new JAASCallbackHandler(this, username, credentials));
  332               } catch (Throwable e) {
  333                   log.error(sm.getString("jaasRealm.unexpectedError"), e);
  334                   return (null);
  335               } finally {
  336                   if (isUseContextClassLoader()) {
  337                       Thread.currentThread().setContextClassLoader(ocl);
  338                   }
  339               }
  340   
  341               if (log.isDebugEnabled())
  342                   log.debug("Login context created " + username);
  343   
  344               // Negotiate a login via this LoginContext
  345               Subject subject = null;
  346               try {
  347                   loginContext.login();
  348                   Subject tempSubject = loginContext.getSubject();
  349                   if (tempSubject == null) {
  350                       if (log.isDebugEnabled())
  351                           log.debug(sm.getString("jaasRealm.failedLogin", username));
  352                       return (null);
  353                   }
  354   
  355                   subject = ContextManager.getServerSideSubject(tempSubject);
  356                   if (subject == null) {
  357                       if (log.isDebugEnabled())
  358                           log.debug(sm.getString("jaasRealm.failedLogin", username));
  359                       return (null);
  360                   }
  361   
  362                   ContextManager.setCurrentCaller(subject);
  363   
  364               } catch (AccountExpiredException e) {
  365                   if (log.isDebugEnabled())
  366                       log.debug(sm.getString("jaasRealm.accountExpired", username));
  367                   return (null);
  368               } catch (CredentialExpiredException e) {
  369                   if (log.isDebugEnabled())
  370                       log.debug(sm.getString("jaasRealm.credentialExpired", username));
  371                   return (null);
  372               } catch (FailedLoginException e) {
  373                   if (log.isDebugEnabled())
  374                       log.debug(sm.getString("jaasRealm.failedLogin", username));
  375                   return (null);
  376               } catch (LoginException e) {
  377                   log.warn(sm.getString("jaasRealm.loginException", username), e);
  378                   return (null);
  379               } catch (Throwable e) {
  380                   log.error(sm.getString("jaasRealm.unexpectedError"), e);
  381                   return (null);
  382               }
  383   
  384               if (log.isDebugEnabled())
  385                   log.debug(sm.getString("jaasRealm.loginContextCreated", username));
  386   
  387               // Return the appropriate Principal for this authenticated Subject
  388   /*            Principal principal = createPrincipal(username, subject);
  389               if (principal == null) {
  390                   log.debug(sm.getString("jaasRealm.authenticateFailure", username));
  391                   return (null);
  392               }
  393               if (log.isDebugEnabled()) {
  394                   log.debug(sm.getString("jaasRealm.authenticateSuccess", username));
  395               }
  396   */
  397               JAASTomcatPrincipal jaasPrincipal = new JAASTomcatPrincipal(username);
  398               jaasPrincipal.setSubject(subject);
  399   
  400               return (jaasPrincipal);
  401   
  402           } catch (Throwable t) {
  403               log.error("error ", t);
  404               return null;
  405           }
  406       }
  407   
  408   
  409       /**
  410        * Prepare for active use of the public methods of this <code>Component</code>.
  411        *
  412        * @throws org.apache.catalina.LifecycleException
  413        *          if this component detects a fatal error
  414        *          that prevents it from being started
  415        */
  416       public void start() throws LifecycleException {
  417   
  418           // Perform normal superclass initialization
  419           super.start();
  420   
  421       }
  422   
  423       /**
  424        * Gracefully shut down active use of the public methods of this <code>Component</code>.
  425        *
  426        * @throws LifecycleException if this component detects a fatal error
  427        *                            that needs to be reported
  428        */
  429       public void stop() throws LifecycleException {
  430   
  431           // Perform normal superclass finalization
  432           super.stop();
  433   
  434       }
  435   }

Home » geronimo-2.2-source-release » org.apache.geronimo.tomcat.realm » [javadoc | source]