/** * 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; }
/** 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); } }
/** 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(); } }
/** 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(); }
/** 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(); }