/**
   * Perform access control based on the specified authorization constraint. Return <code>true
   * </code> if this constraint is satisfied and processing should continue, or <code>false</code>
   * otherwise.
   *
   * @param request Request we are processing
   * @param response Response we are creating
   * @param constraint Security constraint we are enforcing
   * @exception IOException if an input/output error occurs
   */
  protected boolean accessControl(
      HttpRequest request, HttpResponse response, SecurityConstraint constraint)
      throws IOException {

    if (constraint == null) return (true);

    // Specifically allow access to the form login and form error pages
    // and the "j_security_check" action
    LoginConfig config = context.getLoginConfig();
    if ((config != null) && (Constants.FORM_METHOD.equals(config.getAuthMethod()))) {
      String requestURI = request.getDecodedRequestURI();
      String loginPage = context.getPath() + config.getLoginPage();
      if (loginPage.equals(requestURI)) {
        if (debug >= 1) log(" Allow access to login page " + loginPage);
        return (true);
      }
      String errorPage = context.getPath() + config.getErrorPage();
      if (errorPage.equals(requestURI)) {
        if (debug >= 1) log(" Allow access to error page " + errorPage);
        return (true);
      }
      if (requestURI.endsWith(Constants.FORM_ACTION)) {
        if (debug >= 1) log(" Allow access to username/password submission");
        return (true);
      }
    }

    // Which user principal have we already authenticated?
    Principal principal = ((HttpServletRequest) request.getRequest()).getUserPrincipal();
    if (principal == null) {
      if (debug >= 2) log("  No user authenticated, cannot grant access");
      ((HttpServletResponse) response.getResponse())
          .sendError(
              HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
              sm.getString("authenticator.notAuthenticated"));
      return (false);
    }

    // Check each role included in this constraint
    Realm realm = context.getRealm();
    String roles[] = constraint.findAuthRoles();
    if (roles == null) roles = new String[0];

    if (constraint.getAllRoles()) return (true);
    if ((roles.length == 0) && (constraint.getAuthConstraint())) {
      ((HttpServletResponse) response.getResponse())
          .sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("authenticator.forbidden"));
      return (false); // No listed roles means no access at all
    }
    for (int i = 0; i < roles.length; i++) {
      if (realm.hasRole(principal, roles[i])) return (true);
    }

    // Return a "Forbidden" message denying access to this resource
    ((HttpServletResponse) response.getResponse())
        .sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("authenticator.forbidden"));
    return (false);
  }
  /**
   * Bind the specified value with the specified context attribute name, replacing any existing
   * value for that name.
   *
   * @param name Attribute name to be bound
   * @param value New attribute value to be bound
   */
  public void setAttribute(String name, Object value) {

    // Name cannot be null
    if (name == null)
      throw new IllegalArgumentException(sm.getString("applicationContext.setAttribute.namenull"));

    // Null value is the same as removeAttribute()
    if (value == null) {
      removeAttribute(name);
      return;
    }

    Object oldValue = null;
    boolean replaced = false;

    // Add or replace the specified attribute
    synchronized (attributes) {
      // Check for read only attribute
      if (readOnlyAttributes.containsKey(name)) return;
      oldValue = attributes.get(name);
      if (oldValue != null) replaced = true;
      attributes.put(name, value);
    }

    // Notify interested application event listeners
    Object listeners[] = context.getApplicationListeners();
    if ((listeners == null) || (listeners.length == 0)) return;
    ServletContextAttributeEvent event = null;
    if (replaced)
      event = new ServletContextAttributeEvent(context.getServletContext(), name, oldValue);
    else event = new ServletContextAttributeEvent(context.getServletContext(), name, value);

    for (int i = 0; i < listeners.length; i++) {
      if (!(listeners[i] instanceof ServletContextAttributeListener)) continue;
      ServletContextAttributeListener listener = (ServletContextAttributeListener) listeners[i];
      try {
        if (replaced) {
          context.fireContainerEvent("beforeContextAttributeReplaced", listener);
          listener.attributeReplaced(event);
          context.fireContainerEvent("afterContextAttributeReplaced", listener);
        } else {
          context.fireContainerEvent("beforeContextAttributeAdded", listener);
          listener.attributeAdded(event);
          context.fireContainerEvent("afterContextAttributeAdded", listener);
        }
      } catch (Throwable t) {
        if (replaced) context.fireContainerEvent("afterContextAttributeReplaced", listener);
        else context.fireContainerEvent("afterContextAttributeAdded", listener);
        // FIXME - should we do anything besides log these?
        log(sm.getString("applicationContext.attributeEvent"), t);
      }
    }
  }
  /**
   * Prepare for the beginning of active use of the public methods of this component. This method
   * should be called after <code>configure()</code>, and before any of the public methods of the
   * component are utilized.
   *
   * @exception LifecycleException if this component detects a fatal error that prevents this
   *     component from being used
   */
  public void start() throws LifecycleException {

    // Validate and update our current component state
    if (started) {
      throw new LifecycleException(sm.getString("requestFilterValve.alreadyStarted"));
    }
    if (!allowValid || !denyValid) {
      throw new LifecycleException(sm.getString("requestFilterValve.configInvalid"));
    }
    lifecycle.fireLifecycleEvent(START_EVENT, null);
    started = true;
  }
Beispiel #4
0
  /** Process a "start" event for this Host. */
  public void start() {

    if (log.isDebugEnabled()) log.debug(sm.getString("hostConfig.start"));

    try {
      ObjectName hostON = new ObjectName(host.getObjectName());
      oname = new ObjectName(hostON.getDomain() + ":type=Deployer,host=" + host.getName());
      Registry.getRegistry(null, null).registerComponent(this, oname, this.getClass().getName());
    } catch (Exception e) {
      log.error(sm.getString("hostConfig.jmx.register", oname), e);
    }

    if (host.getDeployOnStartup()) deployApps();
  }
  /**
   * Return <code>true</code> if the specified Principal has the specified security role, within the
   * context of this Realm; otherwise return <code>false</code>. This method can be overridden by
   * Realm implementations, but the default is adequate when an instance of <code>GenericPrincipal
   * </code> is used to represent authenticated Principals from this Realm.
   *
   * @param principal Principal for whom the role is to be checked
   * @param role Security role to be checked
   */
  public boolean hasRole(Principal principal, String role) {

    if ((principal == null) || (role == null) || !(principal instanceof GenericPrincipal))
      return (false);
    GenericPrincipal gp = (GenericPrincipal) principal;
    if (!(gp.getRealm() == this)) return (false);
    boolean result = gp.hasRole(role);
    if (debug >= 2) {
      String name = principal.getName();
      if (result) log(sm.getString("realmBase.hasRoleSuccess", name, role));
      else log(sm.getString("realmBase.hasRoleFailure", name, role));
    }
    return (result);
  }
  /**
   * Close the specified database connection.
   *
   * @param dbConnection The connection to be closed
   */
  protected void close(Connection dbConnection) {

    // Do nothing if the database connection is already closed
    if (dbConnection == null) return;

    // Close our prepared statements (if any)
    try {
      preparedCredentials.close();
    } catch (Throwable f) {;
    }
    try {
      preparedRoles.close();
    } catch (Throwable f) {;
    }

    // Close this database connection, and log any errors
    try {
      dbConnection.close();
    } catch (SQLException e) {
      log(sm.getString("jdbcRealm.close"), e); // Just log it here
    }

    // Release resources associated with the closed connection
    this.dbConnection = null;
    this.preparedCredentials = null;
    this.preparedRoles = null;
  }
  /**
   * Return an array of regular expression objects initialized from the specified argument, which
   * must be <code>null</code> or a comma-delimited list of regular expression patterns.
   *
   * @param list The comma-separated list of patterns
   * @exception IllegalArgumentException if one of the patterns has invalid syntax
   */
  protected Pattern[] precalculate(String list) {

    if (list == null) return (new Pattern[0]);
    list = list.trim();
    if (list.length() < 1) return (new Pattern[0]);
    list += ",";

    ArrayList reList = new ArrayList();
    while (list.length() > 0) {
      int comma = list.indexOf(',');
      if (comma < 0) break;
      String pattern = list.substring(0, comma).trim();
      try {
        reList.add(Pattern.compile(pattern));
      } catch (PatternSyntaxException e) {
        IllegalArgumentException iae =
            new IllegalArgumentException(sm.getString("requestFilterValve.syntax", pattern));
        iae.initCause(e);
        throw iae;
      }
      list = list.substring(comma + 1);
    }

    Pattern reArray[] = new Pattern[reList.size()];
    return ((Pattern[]) reList.toArray(reArray));
  }
  /**
   * Return the Principal associated with the specified username and credentials, if there is one;
   * otherwise return <code>null</code>.
   *
   * <p>If there are any errors with the JDBC connection, executing the query or anything we return
   * null (don't authenticate). This event is also logged, and the connection will be closed so that
   * a subsequent request will automatically re-open it.
   *
   * @param username Username of the Principal to look up
   * @param credentials Password or other credentials to use in authenticating this username
   */
  public Principal authenticate(String username, String credentials) {

    Connection dbConnection = null;

    try {

      // Ensure that we have an open database connection
      dbConnection = open();

      // Acquire a Principal object for this user
      Principal principal = authenticate(dbConnection, username, credentials);

      // Release the database connection we just used
      release(dbConnection);

      // Return the Principal (if any)
      return (principal);

    } catch (SQLException e) {

      // Log the problem for posterity
      log(sm.getString("jdbcRealm.exception"), e);

      // Close the connection so that it gets reopened next time
      if (dbConnection != null) close(dbConnection);

      // Return "not authenticated" for this request
      return (null);
    }
  }
Beispiel #9
0
  /** Deploy WAR files. */
  protected void deployWARs(File appBase, String[] files) {

    if (files == null) return;

    for (int i = 0; i < files.length; i++) {

      if (files[i].equalsIgnoreCase("META-INF")) continue;
      if (files[i].equalsIgnoreCase("WEB-INF")) continue;
      File dir = new File(appBase, files[i]);
      if (files[i].toLowerCase().endsWith(".war")
          && dir.isFile()
          && !invalidWars.contains(files[i])) {

        // Calculate the context path and make sure it is unique
        String contextPath = "/" + files[i].replace('#', '/');
        int period = contextPath.lastIndexOf(".");
        contextPath = contextPath.substring(0, period);

        // Check for WARs with /../ /./ or similar sequences in the name
        if (!validateContextPath(appBase, contextPath)) {
          log.error(sm.getString("hostConfig.illegalWarName", files[i]));
          invalidWars.add(files[i]);
          continue;
        }

        if (contextPath.equals("/ROOT")) contextPath = "";

        if (isServiced(contextPath)) continue;

        String file = files[i];

        deployWAR(contextPath, dir, file);
      }
    }
  }
  /**
   * Prepare for the beginning of active use of the public methods of this component. This method
   * should be called before any of the public methods of this component are utilized. It should
   * also send a LifecycleEvent of type START_EVENT to any registered listeners.
   *
   * @throws LifecycleException if this component detects a fatal error that prevents this component
   *     from being used
   */
  public void start() throws LifecycleException {

    // Validate and update our current component state
    if (started) throw new LifecycleException(sm.getString("realmBase.alreadyStarted"));
    lifecycle.fireLifecycleEvent(START_EVENT, null);
    started = true;

    // Create a MessageDigest instance for credentials, if desired
    if (digest != null) {
      try {
        md = MessageDigest.getInstance(digest);
      } catch (NoSuchAlgorithmException e) {
        throw new LifecycleException(sm.getString("realmBase.algorithm", digest), e);
      }
    }
  }
  /**
   * Invoke a pre-startup initialization. This is used to allow connectors to bind to restricted
   * ports under Unix operating environments.
   */
  public void initialize() throws LifecycleException {
    if (initialized) {
      log.info(sm.getString("standardServer.initialize.initialized"));
      return;
    }
    lifecycle.fireLifecycleEvent(INIT_EVENT, null);
    initialized = true;

    if (oname == null) {
      try {
        oname = new ObjectName("Catalina:type=Server");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);
      } catch (Exception e) {
        log.error("Error registering ", e);
      }
    }

    // Register global String cache
    try {
      ObjectName oname2 = new ObjectName(oname.getDomain() + ":type=StringCache");
      Registry.getRegistry(null, null).registerComponent(new StringCache(), oname2, null);
    } catch (Exception e) {
      log.error("Error registering ", e);
    }

    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
      services[i].initialize();
    }
  }
Beispiel #12
0
  /** Undeploy all deployed applications. */
  protected void undeployApps() {

    if (log.isDebugEnabled()) log.debug(sm.getString("hostConfig.undeploying"));

    // Soft undeploy all contexts we have deployed
    DeployedApplication[] apps =
        (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
    for (int i = 0; i < apps.length; i++) {
      try {
        host.removeChild(host.findChild(apps[i].name));
      } catch (Throwable t) {
        log.warn(sm.getString("hostConfig.context.remove", apps[i].name), t);
      }
    }

    deployed.clear();
  }
  /** Set the specified environment entries in the naming context. */
  public void removeEnvironment(String name) {

    try {
      envCtx.unbind(name);
    } catch (NamingException e) {
      log(sm.getString("naming.unbindFailed", e));
    }
  }
  /** Set the specified resources in the naming context. */
  public void removeResourceLink(String name) {

    try {
      envCtx.unbind(name);
    } catch (NamingException e) {
      log(sm.getString("naming.unbindFailed", e));
    }
  }
  /**
   * This method is the simplified version of the similar method in
   * org.apache.catalina.connector.http.HttpProcessor. However, this method only parses some "easy"
   * headers, such as "cookie", "content-length", and "content-type", and ignore other headers.
   *
   * @param input The input stream connected to our socket
   * @exception IOException if an input/output error occurs
   * @exception ServletException if a parsing error occurs
   */
  private void parseHeaders(SocketInputStream input) throws IOException, ServletException {
    while (true) {
      HttpHeader header = new HttpHeader();
      ;

      // Read the next header
      input.readHeader(header);
      if (header.nameEnd == 0) {
        if (header.valueEnd == 0) {
          return;
        } else {
          throw new ServletException(sm.getString("httpProcessor.parseHeaders.colon"));
        }
      }

      String name = new String(header.name, 0, header.nameEnd);
      String value = new String(header.value, 0, header.valueEnd);
      request.addHeader(name, value);
      // do something for some headers, ignore others.
      if (name.equals("cookie")) {
        Cookie cookies[] = RequestUtil.parseCookieHeader(value);
        for (int i = 0; i < cookies.length; i++) {
          if (cookies[i].getName().equals("jsessionid")) {
            // Override anything requested in the URL
            if (!request.isRequestedSessionIdFromCookie()) {
              // Accept only the first session id cookie
              request.setRequestedSessionId(cookies[i].getValue());
              request.setRequestedSessionCookie(true);
              request.setRequestedSessionURL(false);
            }
          }
          request.addCookie(cookies[i]);
        }
      } else if (name.equals("content-length")) {
        int n = -1;
        try {
          n = Integer.parseInt(value);
        } catch (Exception e) {
          throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength"));
        }
        request.setContentLength(n);
      } else if (name.equals("content-type")) {
        request.setContentType(value);
      }
    } // end while
  }
  /**
   * Start the background thread we will use for request processing.
   *
   * @exception LifecycleException if a fatal startup error occurs
   */
  public void start() throws LifecycleException {

    if (started) throw new LifecycleException(sm.getString("httpProcessor.alreadyStarted"));
    lifecycle.fireLifecycleEvent(START_EVENT, null);
    started = true;

    threadStart();
  }
Beispiel #17
0
  /** Process a "stop" event for this Host. */
  public void stop() {

    if (log.isDebugEnabled()) log.debug(sm.getString("hostConfig.stop"));

    undeployApps();

    if (oname != null) {
      try {
        Registry.getRegistry(null, null).unregisterComponent(oname);
      } catch (Exception e) {
        log.error(sm.getString("hostConfig.jmx.unregister", oname), e);
      }
    }
    oname = null;
    appBase = null;
    configBase = null;
  }
  /**
   * Stop the background thread we will use for request processing.
   *
   * @exception LifecycleException if a fatal shutdown error occurs
   */
  public void stop() throws LifecycleException {

    if (!started) throw new LifecycleException(sm.getString("httpProcessor.notStarted"));
    lifecycle.fireLifecycleEvent(STOP_EVENT, null);
    started = false;

    threadStop();
  }
  /**
   * Set the Container to which this Valve is attached.
   *
   * @param container The container to which we are attached
   */
  public void setContainer(Container container) {

    if (!(container instanceof Context))
      throw new IllegalArgumentException(sm.getString("authenticator.notContext"));

    super.setContainer(container);
    this.context = (Context) container;
  }
  /**
   * Gracefully terminate the active use of the public methods of this component. This method should
   * be the last one called on a given instance of this component.
   *
   * @exception LifecycleException if this component detects a fatal error that needs to be reported
   */
  public void stop() throws LifecycleException {

    // Validate and update our current component state
    if (!started) throw new LifecycleException(sm.getString("authenticator.notStarted"));
    lifecycle.fireLifecycleEvent(STOP_EVENT, null);
    started = false;

    sso = null;
  }
  /** Start the background processing thread. */
  private void threadStart() {

    log(sm.getString("httpProcessor.starting"));

    thread = new Thread(this, threadName);
    thread.setDaemon(true);
    thread.start();

    if (debug >= 1) log(" Background thread has been started");
  }
  /**
   * Gracefully terminate the active use of the public methods of this component. This method should
   * be the last one called on a given instance of this component. It should also send a
   * LifecycleEvent of type STOP_EVENT to any registered listeners.
   *
   * @throws LifecycleException if this component detects a fatal error that needs to be reported
   */
  public void stop() throws LifecycleException {

    // Validate and update our current component state
    if (!started) throw new LifecycleException(sm.getString("realmBase.notStarted"));
    lifecycle.fireLifecycleEvent(STOP_EVENT, null);
    started = false;

    // Clean up allocated resources
    md = null;
  }
  /**
   * Return the Principal associated with the specified username and credentials, if there is one;
   * otherwise return <code>null</code>.
   *
   * @param dbConnection The database connection to be used
   * @param username Username of the Principal to look up
   * @param credentials Password or other credentials to use in authenticating this username
   * @throws SQLException if a database error occurs
   */
  public synchronized Principal authenticate(
      Connection dbConnection, String username, String credentials) throws SQLException {

    // Look up the user's credentials
    String dbCredentials = null;
    PreparedStatement stmt = credentials(dbConnection, username);
    ResultSet rs = stmt.executeQuery();
    while (rs.next()) {
      dbCredentials = rs.getString(1).trim();
    }
    rs.close();
    if (dbCredentials == null) {
      return (null);
    }

    // Validate the user's credentials
    boolean validated = false;
    if (hasMessageDigest()) {
      // Hex hashes should be compared case-insensitive
      validated = (digest(credentials).equalsIgnoreCase(dbCredentials));
    } else validated = (digest(credentials).equals(dbCredentials));

    if (validated) {
      if (debug >= 2) log(sm.getString("jdbcRealm.authenticateSuccess", username));
    } else {
      if (debug >= 2) log(sm.getString("jdbcRealm.authenticateFailure", username));
      return (null);
    }

    // Accumulate the user's roles
    ArrayList list = new ArrayList();
    stmt = roles(dbConnection, username);
    rs = stmt.executeQuery();
    while (rs.next()) {
      list.add(rs.getString(1).trim());
    }
    rs.close();
    dbConnection.commit();

    // Create and return a suitable Principal for this user
    return (new GenericPrincipal(this, username, credentials, list));
  }
  /**
   * Prepare for active use of the public methods of this Component.
   *
   * @throws LifecycleException if this component detects a fatal error that prevents it from being
   *     started
   */
  public void start() throws LifecycleException {

    // Validate that we can open our connection
    try {
      open();
    } catch (SQLException e) {
      throw new LifecycleException(sm.getString("jdbcRealm.open"), e);
    }

    // Perform normal superclass initialization
    super.start();
  }
  /**
   * Return the Principal associated with the specified username and credentials, if there is one;
   * otherwise return <code>null</code>.
   *
   * @param username Username of the Principal to look up
   * @param credentials Password or other credentials to use in authenticating this username
   */
  public Principal authenticate(String username, String credentials) {

    GenericPrincipal principal = (GenericPrincipal) principals.get(username);

    boolean validated = false;
    if (principal != null) {
      if (hasMessageDigest()) {
        // Hex hashes should be compared case-insensitive
        validated = (digest(credentials).equalsIgnoreCase(principal.getPassword()));
      } else {
        validated = (digest(credentials).equals(principal.getPassword()));
      }
    }

    if (validated) {
      if (debug >= 2) log(sm.getString("memoryRealm.authenticateSuccess", username));
      return (principal);
    } else {
      if (debug >= 2) log(sm.getString("memoryRealm.authenticateFailure", username));
      return (null);
    }
  }
  /**
   * Read and return a single byte from this input stream, or -1 if end of file has been
   * encountered.
   *
   * @exception IOException if an input/output error occurs
   */
  public int read() throws IOException {

    // Has this stream been closed?
    if (closed) throw new IOException(sm.getString("requestStream.read.closed"));

    // Have we read the specified content length already?
    if ((length >= 0) && (count >= length)) return (-1); // End of file indicator

    // Read and count the next byte, then return it
    int b = stream.read();
    if (b >= 0) count++;
    return (b);
  }
  /** Set the specified EJBs in the naming context. */
  public void addEjb(ContextEjb ejb) {

    // Create a reference to the EJB.
    Reference ref = new EjbRef(ejb.getType(), ejb.getHome(), ejb.getRemote(), ejb.getLink());
    // Adding the additional parameters, if any
    addAdditionalParameters(ejb.getNamingResources(), ref, ejb.getName());
    try {
      createSubcontexts(envCtx, ejb.getName());
      envCtx.bind(ejb.getName(), ref);
    } catch (NamingException e) {
      log(sm.getString("naming.bindFailed", e));
    }
  }
  /**
   * Close this input stream. No physical level I-O is performed, but any further attempt to read
   * from this stream will throw an IOException. If a content length has been set but not all of the
   * bytes have yet been consumed, the remaining bytes will be swallowed.
   */
  public void close() throws IOException {

    if (closed) throw new IOException(sm.getString("requestStream.close.closed"));

    if (length > 0) {
      while (count < length) {
        int b = read();
        if (b < 0) break;
      }
    }

    closed = true;
  }
  /** Set the specified resource link in the naming context. */
  public void addResourceLink(ContextResourceLink resourceLink) {

    // Create a reference to the resource.
    Reference ref = new ResourceLinkRef(resourceLink.getType(), resourceLink.getGlobal());
    // Adding the additional parameters, if any
    addAdditionalParameters(resourceLink.getNamingResources(), ref, resourceLink.getName());
    try {
      if (debug >= 2) log("  Adding resource link " + resourceLink.getName());
      createSubcontexts(envCtx, resourceLink.getName());
      envCtx.bind(resourceLink.getName(), ref);
    } catch (NamingException e) {
      log(sm.getString("naming.bindFailed", e));
    }
  }
  /**
   * Prepare for active use of the public methods of this Component.
   *
   * @exception LifecycleException if this component detects a fatal error that prevents it from
   *     being started
   */
  public synchronized void start() throws LifecycleException {

    // Validate the existence of our database file
    File file = new File(pathname);
    if (!file.isAbsolute()) file = new File(System.getProperty("catalina.base"), pathname);
    if (!file.exists() || !file.canRead())
      throw new LifecycleException(sm.getString("memoryRealm.loadExist", file.getAbsolutePath()));

    // Load the contents of the database file
    if (debug >= 1) log(sm.getString("memoryRealm.loadPath", file.getAbsolutePath()));
    Digester digester = getDigester();
    try {
      synchronized (digester) {
        digester.push(this);
        digester.parse(file);
      }
    } catch (Exception e) {
      throw new LifecycleException("memoryRealm.readXml", e);
    }

    // Perform normal superclass initialization
    super.start();
  }