0001: /*
0002: * Copyright 1999,2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.catalina.realm;
0018:
0019: import java.beans.PropertyChangeListener;
0020: import java.beans.PropertyChangeSupport;
0021: import java.io.IOException;
0022: import java.security.MessageDigest;
0023: import java.security.NoSuchAlgorithmException;
0024: import java.security.Principal;
0025: import java.security.cert.X509Certificate;
0026: import java.util.ArrayList;
0027:
0028: import javax.management.MBeanRegistration;
0029: import javax.management.MBeanServer;
0030: import javax.management.ObjectName;
0031: import javax.servlet.http.HttpServletRequest;
0032: import javax.servlet.http.HttpServletResponse;
0033:
0034: import org.apache.catalina.Container;
0035: import org.apache.catalina.Context;
0036: import org.apache.catalina.HttpRequest;
0037: import org.apache.catalina.HttpResponse;
0038: import org.apache.catalina.Lifecycle;
0039: import org.apache.catalina.LifecycleException;
0040: import org.apache.catalina.LifecycleListener;
0041: import org.apache.catalina.Logger;
0042: import org.apache.catalina.Realm;
0043: import org.apache.catalina.core.ContainerBase;
0044: import org.apache.catalina.deploy.LoginConfig;
0045: import org.apache.catalina.deploy.SecurityConstraint;
0046: import org.apache.catalina.deploy.SecurityCollection;
0047: import org.apache.catalina.util.HexUtils;
0048: import org.apache.catalina.util.LifecycleSupport;
0049: import org.apache.catalina.util.MD5Encoder;
0050: import org.apache.catalina.util.StringManager;
0051: import org.apache.commons.logging.Log;
0052: import org.apache.commons.logging.LogFactory;
0053: import org.apache.commons.modeler.Registry;
0054:
0055: /**
0056: * Simple implementation of <b>Realm</b> that reads an XML file to configure
0057: * the valid users, passwords, and roles. The file format (and default file
0058: * location) are identical to those currently supported by Tomcat 3.X.
0059: *
0060: * @author Craig R. McClanahan
0061: * @version $Revision: 1.33 $ $Date: 2004/05/26 15:51:25 $
0062: */
0063:
0064: public abstract class RealmBase implements Lifecycle, Realm,
0065: MBeanRegistration {
0066:
0067: private static Log log = LogFactory.getLog(RealmBase.class);
0068:
0069: // ----------------------------------------------------- Instance Variables
0070:
0071: /**
0072: * The Container with which this Realm is associated.
0073: */
0074: protected Container container = null;
0075:
0076: /**
0077: * The debugging detail level for this component.
0078: */
0079: protected int debug = 0;
0080:
0081: /**
0082: * Digest algorithm used in storing passwords in a non-plaintext format.
0083: * Valid values are those accepted for the algorithm name by the
0084: * MessageDigest class, or <code>null</code> if no digesting should
0085: * be performed.
0086: */
0087: protected String digest = null;
0088:
0089: /**
0090: * Descriptive information about this Realm implementation.
0091: */
0092: protected static final String info = "org.apache.catalina.realm.RealmBase/1.0";
0093:
0094: /**
0095: * The lifecycle event support for this component.
0096: */
0097: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
0098:
0099: /**
0100: * The MessageDigest object for digesting user credentials (passwords).
0101: */
0102: protected MessageDigest md = null;
0103:
0104: /**
0105: * The MD5 helper object for this class.
0106: */
0107: protected static final MD5Encoder md5Encoder = new MD5Encoder();
0108:
0109: /**
0110: * MD5 message digest provider.
0111: */
0112: protected static MessageDigest md5Helper;
0113:
0114: /**
0115: * The string manager for this package.
0116: */
0117: protected static StringManager sm = StringManager
0118: .getManager(Constants.Package);
0119:
0120: /**
0121: * Has this component been started?
0122: */
0123: protected boolean started = false;
0124:
0125: /**
0126: * The property change support for this component.
0127: */
0128: protected PropertyChangeSupport support = new PropertyChangeSupport(
0129: this );
0130:
0131: /**
0132: * Should we validate client certificate chains when they are presented?
0133: */
0134: protected boolean validate = true;
0135:
0136: // ------------------------------------------------------------- Properties
0137:
0138: /**
0139: * Return the Container with which this Realm has been associated.
0140: */
0141: public Container getContainer() {
0142:
0143: return (container);
0144:
0145: }
0146:
0147: /**
0148: * Set the Container with which this Realm has been associated.
0149: *
0150: * @param container The associated Container
0151: */
0152: public void setContainer(Container container) {
0153:
0154: Container oldContainer = this .container;
0155: this .container = container;
0156: support.firePropertyChange("container", oldContainer,
0157: this .container);
0158:
0159: }
0160:
0161: /**
0162: * Return the debugging detail level for this component.
0163: */
0164: public int getDebug() {
0165:
0166: return (this .debug);
0167:
0168: }
0169:
0170: /**
0171: * Set the debugging detail level for this component.
0172: *
0173: * @param debug The new debugging detail level
0174: */
0175: public void setDebug(int debug) {
0176:
0177: this .debug = debug;
0178:
0179: }
0180:
0181: /**
0182: * Return the digest algorithm used for storing credentials.
0183: */
0184: public String getDigest() {
0185:
0186: return digest;
0187:
0188: }
0189:
0190: /**
0191: * Set the digest algorithm used for storing credentials.
0192: *
0193: * @param digest The new digest algorithm
0194: */
0195: public void setDigest(String digest) {
0196:
0197: this .digest = digest;
0198:
0199: }
0200:
0201: /**
0202: * Return descriptive information about this Realm implementation and
0203: * the corresponding version number, in the format
0204: * <code><description>/<version></code>.
0205: */
0206: public String getInfo() {
0207:
0208: return info;
0209:
0210: }
0211:
0212: /**
0213: * Return the "validate certificate chains" flag.
0214: */
0215: public boolean getValidate() {
0216:
0217: return (this .validate);
0218:
0219: }
0220:
0221: /**
0222: * Set the "validate certificate chains" flag.
0223: *
0224: * @param validate The new validate certificate chains flag
0225: */
0226: public void setValidate(boolean validate) {
0227:
0228: this .validate = validate;
0229:
0230: }
0231:
0232: // --------------------------------------------------------- Public Methods
0233:
0234: /**
0235: * Add a property change listener to this component.
0236: *
0237: * @param listener The listener to add
0238: */
0239: public void addPropertyChangeListener(
0240: PropertyChangeListener listener) {
0241:
0242: support.addPropertyChangeListener(listener);
0243:
0244: }
0245:
0246: /**
0247: * Return the Principal associated with the specified username and
0248: * credentials, if there is one; otherwise return <code>null</code>.
0249: *
0250: * @param username Username of the Principal to look up
0251: * @param credentials Password or other credentials to use in
0252: * authenticating this username
0253: */
0254: public Principal authenticate(String username, String credentials) {
0255:
0256: String serverCredentials = getPassword(username);
0257:
0258: if ((serverCredentials == null)
0259: || (!serverCredentials.equals(credentials)))
0260: return null;
0261:
0262: return getPrincipal(username);
0263:
0264: }
0265:
0266: /**
0267: * Return the Principal associated with the specified username and
0268: * credentials, if there is one; otherwise return <code>null</code>.
0269: *
0270: * @param username Username of the Principal to look up
0271: * @param credentials Password or other credentials to use in
0272: * authenticating this username
0273: */
0274: public Principal authenticate(String username, byte[] credentials) {
0275:
0276: return (authenticate(username, credentials.toString()));
0277:
0278: }
0279:
0280: /**
0281: * Return the Principal associated with the specified username, which
0282: * matches the digest calculated using the given parameters using the
0283: * method described in RFC 2069; otherwise return <code>null</code>.
0284: *
0285: * @param username Username of the Principal to look up
0286: * @param clientDigest Digest which has been submitted by the client
0287: * @param nOnce Unique (or supposedly unique) token which has been used
0288: * for this request
0289: * @param realm Realm name
0290: * @param md5a2 Second MD5 digest used to calculate the digest :
0291: * MD5(Method + ":" + uri)
0292: */
0293: public Principal authenticate(String username, String clientDigest,
0294: String nOnce, String nc, String cnonce, String qop,
0295: String realm, String md5a2) {
0296:
0297: /*
0298: System.out.println("Digest : " + clientDigest);
0299:
0300: System.out.println("************ Digest info");
0301: System.out.println("Username:" + username);
0302: System.out.println("ClientSigest:" + clientDigest);
0303: System.out.println("nOnce:" + nOnce);
0304: System.out.println("nc:" + nc);
0305: System.out.println("cnonce:" + cnonce);
0306: System.out.println("qop:" + qop);
0307: System.out.println("realm:" + realm);
0308: System.out.println("md5a2:" + md5a2);
0309: */
0310:
0311: String md5a1 = getDigest(username, realm);
0312: if (md5a1 == null)
0313: return null;
0314: String serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":"
0315: + cnonce + ":" + qop + ":" + md5a2;
0316: String serverDigest = md5Encoder.encode(md5Helper
0317: .digest(serverDigestValue.getBytes()));
0318: //System.out.println("Server digest : " + serverDigest);
0319:
0320: if (serverDigest.equals(clientDigest))
0321: return getPrincipal(username);
0322: else
0323: return null;
0324: }
0325:
0326: /**
0327: * Return the Principal associated with the specified chain of X509
0328: * client certificates. If there is none, return <code>null</code>.
0329: *
0330: * @param certs Array of client certificates, with the first one in
0331: * the array being the certificate of the client itself.
0332: */
0333: public Principal authenticate(X509Certificate certs[]) {
0334:
0335: if ((certs == null) || (certs.length < 1))
0336: return (null);
0337:
0338: // Check the validity of each certificate in the chain
0339: if (log.isDebugEnabled())
0340: log.debug("Authenticating client certificate chain");
0341: if (validate) {
0342: for (int i = 0; i < certs.length; i++) {
0343: if (log.isDebugEnabled())
0344: log.debug(" Checking validity for '"
0345: + certs[i].getSubjectDN().getName() + "'");
0346: try {
0347: certs[i].checkValidity();
0348: } catch (Exception e) {
0349: if (log.isDebugEnabled())
0350: log.debug(" Validity exception", e);
0351: return (null);
0352: }
0353: }
0354: }
0355:
0356: // Check the existence of the client Principal in our database
0357: return (getPrincipal(certs[0].getSubjectDN().getName()));
0358:
0359: }
0360:
0361: /**
0362: * Return the SecurityConstraints configured to guard the request URI for
0363: * this request, or <code>null</code> if there is no such constraint.
0364: *
0365: * @param request Request we are processing
0366: * @param context Context the Request is mapped to
0367: */
0368: public SecurityConstraint[] findSecurityConstraints(
0369: HttpRequest request, Context context) {
0370:
0371: ArrayList results = null;
0372: // Are there any defined security constraints?
0373: SecurityConstraint constraints[] = context.findConstraints();
0374: if ((constraints == null) || (constraints.length == 0)) {
0375: if (log.isDebugEnabled())
0376: log.debug(" No applicable constraints defined");
0377: return (null);
0378: }
0379:
0380: // Check each defined security constraint
0381: HttpServletRequest hreq = (HttpServletRequest) request
0382: .getRequest();
0383: String uri = request.getRequestPathMB().toString();
0384:
0385: String method = hreq.getMethod();
0386: int i;
0387: boolean found = false;
0388: for (i = 0; i < constraints.length; i++) {
0389: SecurityCollection[] collection = constraints[i]
0390: .findCollections();
0391:
0392: if (log.isDebugEnabled())
0393: log.debug(" Checking constraint '" + constraints[i]
0394: + "' against " + method + " " + uri + " --> "
0395: + constraints[i].included(uri, method));
0396: for (int j = 0; j < collection.length; j++) {
0397: String[] patterns = collection[j].findPatterns();
0398: for (int k = 0; k < patterns.length; k++) {
0399: if (uri.equals(patterns[k])) {
0400: found = true;
0401: if (collection[j].findMethod(method)) {
0402: if (results == null) {
0403: results = new ArrayList();
0404: }
0405: results.add(constraints[i]);
0406: }
0407: }
0408: }
0409: }
0410: }
0411:
0412: if (found) {
0413: return resultsToArray(results);
0414: }
0415:
0416: int longest = -1;
0417:
0418: for (i = 0; i < constraints.length; i++) {
0419: SecurityCollection[] collection = constraints[i]
0420: .findCollections();
0421:
0422: if (log.isDebugEnabled())
0423: log.debug(" Checking constraint '" + constraints[i]
0424: + "' against " + method + " " + uri + " --> "
0425: + constraints[i].included(uri, method));
0426: for (int j = 0; j < collection.length; j++) {
0427: String[] patterns = collection[j].findPatterns();
0428: boolean matched = false;
0429: int length = -1;
0430: for (int k = 0; k < patterns.length; k++) {
0431: String pattern = patterns[k];
0432: if (pattern.startsWith("/")
0433: && pattern.endsWith("/*")
0434: && pattern.length() >= longest) {
0435:
0436: if (pattern.length() == 2) {
0437: matched = true;
0438: length = pattern.length();
0439: } else if (pattern.regionMatches(0, uri, 0,
0440: pattern.length() - 1)
0441: || (pattern.length() - 2 == uri
0442: .length() && pattern
0443: .regionMatches(0, uri, 0,
0444: pattern.length() - 2))) {
0445: matched = true;
0446: length = pattern.length();
0447: }
0448: }
0449: }
0450: if (matched) {
0451: found = true;
0452: if (length > longest) {
0453: if (results != null) {
0454: results.clear();
0455: }
0456: longest = length;
0457: }
0458: if (collection[j].findMethod(method)) {
0459: if (results == null) {
0460: results = new ArrayList();
0461: }
0462: results.add(constraints[i]);
0463: }
0464: }
0465: }
0466: }
0467:
0468: if (found) {
0469: return resultsToArray(results);
0470: }
0471:
0472: for (i = 0; i < constraints.length; i++) {
0473: SecurityCollection[] collection = constraints[i]
0474: .findCollections();
0475:
0476: if (log.isDebugEnabled())
0477: log.debug(" Checking constraint '" + constraints[i]
0478: + "' against " + method + " " + uri + " --> "
0479: + constraints[i].included(uri, method));
0480: boolean matched = false;
0481: int pos = -1;
0482: for (int j = 0; j < collection.length; j++) {
0483: String[] patterns = collection[j].findPatterns();
0484: for (int k = 0; k < patterns.length && !matched; k++) {
0485: String pattern = patterns[k];
0486: if (pattern.startsWith("*.")) {
0487: int slash = uri.lastIndexOf("/");
0488: int dot = uri.lastIndexOf(".");
0489: if (slash >= 0
0490: && dot > slash
0491: && dot != uri.length() - 1
0492: && uri.length() - dot == pattern
0493: .length() - 1) {
0494: if (pattern.regionMatches(1, uri, dot, uri
0495: .length()
0496: - dot)) {
0497: matched = true;
0498: pos = j;
0499: }
0500: }
0501: }
0502: }
0503: }
0504: if (matched) {
0505: found = true;
0506: if (collection[pos].findMethod(method)) {
0507: if (results == null) {
0508: results = new ArrayList();
0509: }
0510: results.add(constraints[i]);
0511: }
0512: }
0513: }
0514:
0515: if (found) {
0516: return resultsToArray(results);
0517: }
0518:
0519: for (i = 0; i < constraints.length; i++) {
0520: SecurityCollection[] collection = constraints[i]
0521: .findCollections();
0522:
0523: if (log.isDebugEnabled())
0524: log.debug(" Checking constraint '" + constraints[i]
0525: + "' against " + method + " " + uri + " --> "
0526: + constraints[i].included(uri, method));
0527: for (int j = 0; j < collection.length; j++) {
0528: String[] patterns = collection[j].findPatterns();
0529: boolean matched = false;
0530: for (int k = 0; k < patterns.length && !matched; k++) {
0531: String pattern = patterns[k];
0532: if (pattern.equals("/")) {
0533: matched = true;
0534: }
0535: }
0536: if (matched) {
0537: if (results == null) {
0538: results = new ArrayList();
0539: }
0540: results.add(constraints[i]);
0541: }
0542: }
0543: }
0544:
0545: if (results == null) {
0546: // No applicable security constraint was found
0547: if (log.isDebugEnabled())
0548: log.debug(" No applicable constraint located");
0549: }
0550: return resultsToArray(results);
0551: }
0552:
0553: /**
0554: * Convert an ArrayList to a SecurityContraint [].
0555: */
0556: private SecurityConstraint[] resultsToArray(ArrayList results) {
0557: if (results == null) {
0558: return null;
0559: }
0560: SecurityConstraint[] array = new SecurityConstraint[results
0561: .size()];
0562: results.toArray(array);
0563: return array;
0564: }
0565:
0566: /**
0567: * Perform access control based on the specified authorization constraint.
0568: * Return <code>true</code> if this constraint is satisfied and processing
0569: * should continue, or <code>false</code> otherwise.
0570: *
0571: * @param request Request we are processing
0572: * @param response Response we are creating
0573: * @param constraints Security constraint we are enforcing
0574: * @param context The Context to which client of this class is attached.
0575: *
0576: * @exception IOException if an input/output error occurs
0577: */
0578: public boolean hasResourcePermission(HttpRequest request,
0579: HttpResponse response, SecurityConstraint[] constraints,
0580: Context context) throws IOException {
0581:
0582: if (constraints == null || constraints.length == 0)
0583: return (true);
0584:
0585: // Specifically allow access to the form login and form error pages
0586: // and the "j_security_check" action
0587: LoginConfig config = context.getLoginConfig();
0588: if ((config != null)
0589: && (Constants.FORM_METHOD
0590: .equals(config.getAuthMethod()))) {
0591: String requestURI = request.getDecodedRequestURI();
0592: String loginPage = context.getPath()
0593: + config.getLoginPage();
0594: if (loginPage.equals(requestURI)) {
0595: if (log.isDebugEnabled())
0596: log.debug(" Allow access to login page "
0597: + loginPage);
0598: return (true);
0599: }
0600: String errorPage = context.getPath()
0601: + config.getErrorPage();
0602: if (errorPage.equals(requestURI)) {
0603: if (log.isDebugEnabled())
0604: log.debug(" Allow access to error page "
0605: + errorPage);
0606: return (true);
0607: }
0608: if (requestURI.endsWith(Constants.FORM_ACTION)) {
0609: if (log.isDebugEnabled())
0610: log
0611: .debug(" Allow access to username/password submission");
0612: return (true);
0613: }
0614: }
0615:
0616: // Which user principal have we already authenticated?
0617: Principal principal = ((HttpServletRequest) request
0618: .getRequest()).getUserPrincipal();
0619: for (int i = 0; i < constraints.length; i++) {
0620: SecurityConstraint constraint = constraints[i];
0621: String roles[] = constraint.findAuthRoles();
0622: if (roles == null)
0623: roles = new String[0];
0624:
0625: if (constraint.getAllRoles())
0626: return (true);
0627:
0628: if (log.isDebugEnabled())
0629: log.debug(" Checking roles " + principal);
0630:
0631: if (roles.length == 0) {
0632: if (constraint.getAuthConstraint()) {
0633: ((HttpServletResponse) response.getResponse())
0634: .sendError(
0635: HttpServletResponse.SC_FORBIDDEN,
0636: sm.getString("realmBase.forbidden"));
0637: if (log.isDebugEnabled())
0638: log.debug("No roles ");
0639: return (false); // No listed roles means no access at all
0640: } else {
0641: log.debug("Passing all access");
0642: return (true);
0643: }
0644: } else if (principal == null) {
0645: if (log.isDebugEnabled())
0646: log
0647: .debug(" No user authenticated, cannot grant access");
0648: ((HttpServletResponse) response.getResponse())
0649: .sendError(
0650: HttpServletResponse.SC_FORBIDDEN,
0651: sm
0652: .getString("realmBase.notAuthenticated"));
0653: return (false);
0654: }
0655:
0656: for (int j = 0; j < roles.length; j++) {
0657: if (hasRole(principal, roles[j]))
0658: return (true);
0659: if (log.isDebugEnabled())
0660: log.debug("No role found: " + roles[j]);
0661: }
0662: }
0663: // Return a "Forbidden" message denying access to this resource
0664: ((HttpServletResponse) response.getResponse()).sendError(
0665: HttpServletResponse.SC_FORBIDDEN, sm
0666: .getString("realmBase.forbidden"));
0667: return (false);
0668:
0669: }
0670:
0671: /**
0672: * Return <code>true</code> if the specified Principal has the specified
0673: * security role, within the context of this Realm; otherwise return
0674: * <code>false</code>. This method can be overridden by Realm
0675: * implementations, but the default is adequate when an instance of
0676: * <code>GenericPrincipal</code> is used to represent authenticated
0677: * Principals from this Realm.
0678: *
0679: * @param principal Principal for whom the role is to be checked
0680: * @param role Security role to be checked
0681: */
0682: public boolean hasRole(Principal principal, String role) {
0683:
0684: // Should be overriten in JAASRealm - to avoid pretty inefficient conversions
0685: if ((principal == null) || (role == null)
0686: || !(principal instanceof GenericPrincipal))
0687: return (false);
0688:
0689: GenericPrincipal gp = (GenericPrincipal) principal;
0690: if (!(gp.getRealm() == this )) {
0691: log.debug("Different realm " + this + " " + gp.getRealm());// return (false);
0692: }
0693: boolean result = gp.hasRole(role);
0694: if (log.isDebugEnabled()) {
0695: String name = principal.getName();
0696: if (result)
0697: log.debug(sm.getString("realmBase.hasRoleSuccess",
0698: name, role));
0699: else
0700: log.debug(sm.getString("realmBase.hasRoleFailure",
0701: name, role));
0702: }
0703: return (result);
0704:
0705: }
0706:
0707: /**
0708: * Enforce any user data constraint required by the security constraint
0709: * guarding this request URI. Return <code>true</code> if this constraint
0710: * was not violated and processing should continue, or <code>false</code>
0711: * if we have created a response already.
0712: *
0713: * @param request Request we are processing
0714: * @param response Response we are creating
0715: * @param constraints Security constraint being checked
0716: *
0717: * @exception IOException if an input/output error occurs
0718: */
0719: public boolean hasUserDataPermission(HttpRequest request,
0720: HttpResponse response, SecurityConstraint[] constraints)
0721: throws IOException {
0722:
0723: // Is there a relevant user data constraint?
0724: if (constraints == null || constraints.length == 0) {
0725: if (log.isDebugEnabled())
0726: log
0727: .debug(" No applicable security constraint defined");
0728: return (true);
0729: }
0730: for (int i = 0; i < constraints.length; i++) {
0731: SecurityConstraint constraint = constraints[i];
0732: String userConstraint = constraint.getUserConstraint();
0733: if (userConstraint == null) {
0734: if (log.isDebugEnabled())
0735: log
0736: .debug(" No applicable user data constraint defined");
0737: return (true);
0738: }
0739: if (userConstraint.equals(Constants.NONE_TRANSPORT)) {
0740: if (log.isDebugEnabled())
0741: log
0742: .debug(" User data constraint has no restrictions");
0743: return (true);
0744: }
0745:
0746: }
0747: // Validate the request against the user data constraint
0748: if (request.getRequest().isSecure()) {
0749: if (log.isDebugEnabled())
0750: log.debug(" User data constraint already satisfied");
0751: return (true);
0752: }
0753: // Initialize variables we need to determine the appropriate action
0754: HttpServletRequest hrequest = (HttpServletRequest) request
0755: .getRequest();
0756: HttpServletResponse hresponse = (HttpServletResponse) response
0757: .getResponse();
0758: int redirectPort = request.getConnector().getRedirectPort();
0759:
0760: // Is redirecting disabled?
0761: if (redirectPort <= 0) {
0762: if (log.isDebugEnabled())
0763: log.debug(" SSL redirect is disabled");
0764: hresponse.sendError(HttpServletResponse.SC_FORBIDDEN,
0765: hrequest.getRequestURI());
0766: return (false);
0767: }
0768:
0769: // Redirect to the corresponding SSL port
0770: StringBuffer file = new StringBuffer();
0771: String protocol = "https";
0772: String host = hrequest.getServerName();
0773: // Protocol
0774: file.append(protocol).append("://");
0775: // Host with port
0776: file.append(host).append(":").append(redirectPort);
0777: // URI
0778: file.append(hrequest.getRequestURI());
0779: String requestedSessionId = hrequest.getRequestedSessionId();
0780: if ((requestedSessionId != null)
0781: && hrequest.isRequestedSessionIdFromURL()) {
0782: file.append(";jsessionid=");
0783: file.append(requestedSessionId);
0784: }
0785: String queryString = hrequest.getQueryString();
0786: if (queryString != null) {
0787: file.append('?');
0788: file.append(queryString);
0789: }
0790: if (log.isDebugEnabled())
0791: log.debug(" Redirecting to " + file.toString());
0792: hresponse.sendRedirect(file.toString());
0793: return (false);
0794:
0795: }
0796:
0797: /**
0798: * Remove a property change listener from this component.
0799: *
0800: * @param listener The listener to remove
0801: */
0802: public void removePropertyChangeListener(
0803: PropertyChangeListener listener) {
0804:
0805: support.removePropertyChangeListener(listener);
0806:
0807: }
0808:
0809: // ------------------------------------------------------ Lifecycle Methods
0810:
0811: /**
0812: * Add a lifecycle event listener to this component.
0813: *
0814: * @param listener The listener to add
0815: */
0816: public void addLifecycleListener(LifecycleListener listener) {
0817:
0818: lifecycle.addLifecycleListener(listener);
0819:
0820: }
0821:
0822: /**
0823: * Get the lifecycle listeners associated with this lifecycle. If this
0824: * Lifecycle has no listeners registered, a zero-length array is returned.
0825: */
0826: public LifecycleListener[] findLifecycleListeners() {
0827:
0828: return lifecycle.findLifecycleListeners();
0829:
0830: }
0831:
0832: /**
0833: * Remove a lifecycle event listener from this component.
0834: *
0835: * @param listener The listener to remove
0836: */
0837: public void removeLifecycleListener(LifecycleListener listener) {
0838:
0839: lifecycle.removeLifecycleListener(listener);
0840:
0841: }
0842:
0843: /**
0844: * Prepare for the beginning of active use of the public methods of this
0845: * component. This method should be called before any of the public
0846: * methods of this component are utilized. It should also send a
0847: * LifecycleEvent of type START_EVENT to any registered listeners.
0848: *
0849: * @exception LifecycleException if this component detects a fatal error
0850: * that prevents this component from being used
0851: */
0852: public void start() throws LifecycleException {
0853:
0854: // Validate and update our current component state
0855: if (started) {
0856: log.info(sm.getString("realmBase.alreadyStarted"));
0857: return;
0858: }
0859: if (!initialized) {
0860: init();
0861: }
0862: lifecycle.fireLifecycleEvent(START_EVENT, null);
0863: started = true;
0864:
0865: // Create a MessageDigest instance for credentials, if desired
0866: if (digest != null) {
0867: try {
0868: md = MessageDigest.getInstance(digest);
0869: } catch (NoSuchAlgorithmException e) {
0870: throw new LifecycleException(sm.getString(
0871: "realmBase.algorithm", digest), e);
0872: }
0873: }
0874:
0875: }
0876:
0877: /**
0878: * Gracefully terminate the active use of the public methods of this
0879: * component. This method should be the last one called on a given
0880: * instance of this component. It should also send a LifecycleEvent
0881: * of type STOP_EVENT to any registered listeners.
0882: *
0883: * @exception LifecycleException if this component detects a fatal error
0884: * that needs to be reported
0885: */
0886: public void stop() throws LifecycleException {
0887:
0888: // Validate and update our current component state
0889: if (!started) {
0890: log.info(sm.getString("realmBase.notStarted"));
0891: return;
0892: }
0893: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
0894: started = false;
0895:
0896: // Clean up allocated resources
0897: md = null;
0898:
0899: destroy();
0900:
0901: }
0902:
0903: public void destroy() {
0904:
0905: // unregister this realm
0906: if (oname != null) {
0907: try {
0908: Registry.getRegistry(null, null).unregisterComponent(
0909: oname);
0910: log.debug("unregistering realm " + oname);
0911: } catch (Exception ex) {
0912: log.error("Can't unregister realm " + oname, ex);
0913: }
0914: }
0915:
0916: }
0917:
0918: // ------------------------------------------------------ Protected Methods
0919:
0920: /**
0921: * Digest the password using the specified algorithm and
0922: * convert the result to a corresponding hexadecimal string.
0923: * If exception, the plain credentials string is returned.
0924: *
0925: * <strong>IMPLEMENTATION NOTE</strong> - This implementation is
0926: * synchronized because it reuses the MessageDigest instance.
0927: * This should be faster than cloning the instance on every request.
0928: *
0929: * @param credentials Password or other credentials to use in
0930: * authenticating this username
0931: */
0932: protected String digest(String credentials) {
0933:
0934: // If no MessageDigest instance is specified, return unchanged
0935: if (hasMessageDigest() == false)
0936: return (credentials);
0937:
0938: // Digest the user credentials and return as hexadecimal
0939: synchronized (this ) {
0940: try {
0941: md.reset();
0942: md.update(credentials.getBytes());
0943: return (HexUtils.convert(md.digest()));
0944: } catch (Exception e) {
0945: log.error(sm.getString("realmBase.digest"), e);
0946: return (credentials);
0947: }
0948: }
0949:
0950: }
0951:
0952: protected boolean hasMessageDigest() {
0953: return !(md == null);
0954: }
0955:
0956: /**
0957: * Return the digest associated with given principal's user name.
0958: */
0959: protected String getDigest(String username, String realmName) {
0960: if (md5Helper == null) {
0961: try {
0962: md5Helper = MessageDigest.getInstance("MD5");
0963: } catch (NoSuchAlgorithmException e) {
0964: e.printStackTrace();
0965: throw new IllegalStateException();
0966: }
0967: }
0968: String digestValue = username + ":" + realmName + ":"
0969: + getPassword(username);
0970: byte[] digest = md5Helper.digest(digestValue.getBytes());
0971: return md5Encoder.encode(digest);
0972: }
0973:
0974: /**
0975: * Return a short name for this Realm implementation, for use in
0976: * log messages.
0977: */
0978: protected abstract String getName();
0979:
0980: /**
0981: * Return the password associated with the given principal's user name.
0982: */
0983: protected abstract String getPassword(String username);
0984:
0985: /**
0986: * Return the Principal associated with the given user name.
0987: */
0988: protected abstract Principal getPrincipal(String username);
0989:
0990: /**
0991: * Log a message on the Logger associated with our Container (if any)
0992: *
0993: * @param message Message to be logged
0994: */
0995: protected void log(String message) {
0996:
0997: Logger logger = null;
0998: String name = null;
0999: if (container != null) {
1000: logger = container.getLogger();
1001: name = container.getName();
1002: }
1003:
1004: if (logger != null) {
1005: logger.log(getName() + "[" + name + "]: " + message);
1006: } else {
1007: System.out
1008: .println(getName() + "[" + name + "]: " + message);
1009: }
1010:
1011: }
1012:
1013: /**
1014: * Log a message on the Logger associated with our Container (if any)
1015: *
1016: * @param message Message to be logged
1017: * @param throwable Associated exception
1018: */
1019: protected void log(String message, Throwable throwable) {
1020:
1021: Logger logger = null;
1022: String name = null;
1023: if (container != null) {
1024: logger = container.getLogger();
1025: name = container.getName();
1026: }
1027:
1028: if (logger != null) {
1029: logger.log(getName() + "[" + name + "]: " + message,
1030: throwable);
1031: } else {
1032: System.out
1033: .println(getName() + "[" + name + "]: " + message);
1034: throwable.printStackTrace(System.out);
1035: }
1036: }
1037:
1038: // --------------------------------------------------------- Static Methods
1039:
1040: /**
1041: * Digest password using the algorithm especificied and
1042: * convert the result to a corresponding hex string.
1043: * If exception, the plain credentials string is returned
1044: *
1045: * @param credentials Password or other credentials to use in
1046: * authenticating this username
1047: * @param algorithm Algorithm used to do th digest
1048: */
1049: public final static String Digest(String credentials,
1050: String algorithm) {
1051:
1052: try {
1053: // Obtain a new message digest with "digest" encryption
1054: MessageDigest md = (MessageDigest) MessageDigest
1055: .getInstance(algorithm).clone();
1056: // encode the credentials
1057: md.update(credentials.getBytes());
1058: // Digest the credentials and return as hexadecimal
1059: return (HexUtils.convert(md.digest()));
1060: } catch (Exception ex) {
1061: ex.printStackTrace();
1062: return credentials;
1063: }
1064:
1065: }
1066:
1067: /**
1068: * Digest password using the algorithm especificied and
1069: * convert the result to a corresponding hex string.
1070: * If exception, the plain credentials string is returned
1071: */
1072: public static void main(String args[]) {
1073:
1074: if (args.length > 2 && args[0].equalsIgnoreCase("-a")) {
1075: for (int i = 2; i < args.length; i++) {
1076: System.out.print(args[i] + ":");
1077: System.out.println(Digest(args[i], args[1]));
1078: }
1079: } else {
1080: System.out
1081: .println("Usage: RealmBase -a <algorithm> <credentials>");
1082: }
1083:
1084: }
1085:
1086: // -------------------- JMX and Registration --------------------
1087: protected String type;
1088: protected String domain;
1089: protected String host;
1090: protected String path;
1091: protected ObjectName oname;
1092: protected ObjectName controller;
1093: protected MBeanServer mserver;
1094:
1095: public ObjectName getController() {
1096: return controller;
1097: }
1098:
1099: public void setController(ObjectName controller) {
1100: this .controller = controller;
1101: }
1102:
1103: public ObjectName getObjectName() {
1104: return oname;
1105: }
1106:
1107: public String getDomain() {
1108: return domain;
1109: }
1110:
1111: public String getType() {
1112: return type;
1113: }
1114:
1115: public ObjectName preRegister(MBeanServer server, ObjectName name)
1116: throws Exception {
1117: oname = name;
1118: mserver = server;
1119: domain = name.getDomain();
1120:
1121: type = name.getKeyProperty("type");
1122: host = name.getKeyProperty("host");
1123: path = name.getKeyProperty("path");
1124:
1125: return name;
1126: }
1127:
1128: public void postRegister(Boolean registrationDone) {
1129: }
1130:
1131: public void preDeregister() throws Exception {
1132: }
1133:
1134: public void postDeregister() {
1135: }
1136:
1137: protected boolean initialized = false;
1138:
1139: public void init() {
1140: if (initialized && container != null)
1141: return;
1142:
1143: initialized = true;
1144: if (container == null) {
1145: ObjectName parent = null;
1146: // Register with the parent
1147: try {
1148: if (host == null) {
1149: // global
1150: parent = new ObjectName(domain + ":type=Engine");
1151: } else if (path == null) {
1152: parent = new ObjectName(domain + ":type=Host,host="
1153: + host);
1154: } else {
1155: parent = new ObjectName(domain
1156: + ":j2eeType=WebModule,name=//" + host
1157: + path);
1158: }
1159: if (mserver.isRegistered(parent)) {
1160: log.debug("Register with " + parent);
1161: mserver
1162: .invoke(
1163: parent,
1164: "setRealm",
1165: new Object[] { this },
1166: new String[] { "org.apache.catalina.Realm" });
1167: }
1168: } catch (Exception e) {
1169: log.info("Parent not available yet: " + parent);
1170: }
1171: }
1172:
1173: if (oname == null) {
1174: // register
1175: try {
1176: ContainerBase cb = (ContainerBase) container;
1177: oname = new ObjectName(cb.getDomain() + ":type=Realm"
1178: + cb.getContainerSuffix());
1179: Registry.getRegistry(null, null).registerComponent(
1180: this , oname, null);
1181: log.debug("Register Realm " + oname);
1182: } catch (Throwable e) {
1183: log.error("Can't register " + oname, e);
1184: }
1185: }
1186:
1187: }
1188:
1189: }
|