KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > realm > RealmBase


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. 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
18
19 package org.apache.catalina.realm;
20
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.UnsupportedEncodingException JavaDoc;
26 import java.security.MessageDigest JavaDoc;
27 import java.security.NoSuchAlgorithmException JavaDoc;
28 import java.security.Principal JavaDoc;
29 import java.security.cert.X509Certificate JavaDoc;
30 import java.util.ArrayList JavaDoc;
31
32 import javax.management.Attribute JavaDoc;
33 import javax.management.MBeanRegistration JavaDoc;
34 import javax.management.MBeanServer JavaDoc;
35 import javax.management.ObjectName JavaDoc;
36 import javax.servlet.http.HttpServletResponse JavaDoc;
37
38 import org.apache.catalina.Container;
39 import org.apache.catalina.Context;
40 import org.apache.catalina.Lifecycle;
41 import org.apache.catalina.LifecycleException;
42 import org.apache.catalina.LifecycleListener;
43 import org.apache.catalina.Realm;
44 import org.apache.catalina.connector.Request;
45 import org.apache.catalina.connector.Response;
46 import org.apache.catalina.core.ContainerBase;
47 import org.apache.catalina.deploy.LoginConfig;
48 import org.apache.catalina.deploy.SecurityConstraint;
49 import org.apache.catalina.deploy.SecurityCollection;
50 import org.apache.catalina.util.HexUtils;
51 import org.apache.catalina.util.LifecycleSupport;
52 import org.apache.catalina.util.MD5Encoder;
53 import org.apache.catalina.util.StringManager;
54 import org.apache.commons.logging.Log;
55 import org.apache.commons.logging.LogFactory;
56 import org.apache.tomcat.util.modeler.Registry;
57
58 /**
59  * Simple implementation of <b>Realm</b> that reads an XML file to configure
60  * the valid users, passwords, and roles. The file format (and default file
61  * location) are identical to those currently supported by Tomcat 3.X.
62  *
63  * @author Craig R. McClanahan
64  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
65  */

66
67 public abstract class RealmBase
68     implements Lifecycle, Realm, MBeanRegistration JavaDoc {
69
70     private static Log log = LogFactory.getLog(RealmBase.class);
71
72     // ----------------------------------------------------- Instance Variables
73

74
75     /**
76      * The Container with which this Realm is associated.
77      */

78     protected Container container = null;
79
80
81     /**
82      * Container log
83      */

84     protected Log containerLog = null;
85
86
87     /**
88      * Digest algorithm used in storing passwords in a non-plaintext format.
89      * Valid values are those accepted for the algorithm name by the
90      * MessageDigest class, or <code>null</code> if no digesting should
91      * be performed.
92      */

93     protected String JavaDoc digest = null;
94
95     /**
96      * The encoding charset for the digest.
97      */

98     protected String JavaDoc digestEncoding = null;
99
100
101     /**
102      * Descriptive information about this Realm implementation.
103      */

104     protected static final String JavaDoc info =
105         "org.apache.catalina.realm.RealmBase/1.0";
106
107
108     /**
109      * The lifecycle event support for this component.
110      */

111     protected LifecycleSupport lifecycle = new LifecycleSupport(this);
112
113
114     /**
115      * The MessageDigest object for digesting user credentials (passwords).
116      */

117     protected MessageDigest JavaDoc md = null;
118
119
120     /**
121      * The MD5 helper object for this class.
122      */

123     protected static final MD5Encoder md5Encoder = new MD5Encoder();
124
125
126     /**
127      * MD5 message digest provider.
128      */

129     protected static MessageDigest JavaDoc md5Helper;
130
131
132     /**
133      * The string manager for this package.
134      */

135     protected static StringManager sm =
136         StringManager.getManager(Constants.Package);
137
138
139     /**
140      * Has this component been started?
141      */

142     protected boolean started = false;
143
144
145     /**
146      * The property change support for this component.
147      */

148     protected PropertyChangeSupport JavaDoc support = new PropertyChangeSupport JavaDoc(this);
149
150
151     /**
152      * Should we validate client certificate chains when they are presented?
153      */

154     protected boolean validate = true;
155
156     
157     /**
158      * The all role mode.
159      */

160     protected AllRolesMode allRolesMode = AllRolesMode.STRICT_MODE;
161     
162
163     // ------------------------------------------------------------- Properties
164

165
166     /**
167      * Return the Container with which this Realm has been associated.
168      */

169     public Container getContainer() {
170
171         return (container);
172
173     }
174
175
176     /**
177      * Set the Container with which this Realm has been associated.
178      *
179      * @param container The associated Container
180      */

181     public void setContainer(Container container) {
182
183         Container oldContainer = this.container;
184         this.container = container;
185         support.firePropertyChange("container", oldContainer, this.container);
186
187     }
188
189     /**
190      * Return the all roles mode.
191      */

192     public String JavaDoc getAllRolesMode() {
193
194         return allRolesMode.toString();
195
196     }
197
198
199     /**
200      * Set the all roles mode.
201      */

202     public void setAllRolesMode(String JavaDoc allRolesMode) {
203
204         this.allRolesMode = AllRolesMode.toMode(allRolesMode);
205
206     }
207
208     /**
209      * Return the digest algorithm used for storing credentials.
210      */

211     public String JavaDoc getDigest() {
212
213         return digest;
214
215     }
216
217
218     /**
219      * Set the digest algorithm used for storing credentials.
220      *
221      * @param digest The new digest algorithm
222      */

223     public void setDigest(String JavaDoc digest) {
224
225         this.digest = digest;
226
227     }
228
229     /**
230      * Returns the digest encoding charset.
231      *
232      * @return The charset (may be null) for platform default
233      */

234     public String JavaDoc getDigestEncoding() {
235         return digestEncoding;
236     }
237
238     /**
239      * Sets the digest encoding charset.
240      *
241      * @param charset The charset (null for platform default)
242      */

243     public void setDigestEncoding(String JavaDoc charset) {
244         digestEncoding = charset;
245     }
246
247     /**
248      * Return descriptive information about this Realm implementation and
249      * the corresponding version number, in the format
250      * <code>&lt;description&gt;/&lt;version&gt;</code>.
251      */

252     public String JavaDoc getInfo() {
253
254         return info;
255
256     }
257
258
259     /**
260      * Return the "validate certificate chains" flag.
261      */

262     public boolean getValidate() {
263
264         return (this.validate);
265
266     }
267
268
269     /**
270      * Set the "validate certificate chains" flag.
271      *
272      * @param validate The new validate certificate chains flag
273      */

274     public void setValidate(boolean validate) {
275
276         this.validate = validate;
277
278     }
279
280
281     // --------------------------------------------------------- Public Methods
282

283
284     
285     /**
286      * Add a property change listener to this component.
287      *
288      * @param listener The listener to add
289      */

290     public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
291
292         support.addPropertyChangeListener(listener);
293
294     }
295
296
297     /**
298      * Return the Principal associated with the specified username and
299      * credentials, if there is one; otherwise return <code>null</code>.
300      *
301      * @param username Username of the Principal to look up
302      * @param credentials Password or other credentials to use in
303      * authenticating this username
304      */

305     public Principal JavaDoc authenticate(String JavaDoc username, String JavaDoc credentials) {
306
307         String JavaDoc serverCredentials = getPassword(username);
308
309         boolean validated ;
310         if ( serverCredentials == null ) {
311             validated = false;
312         } else if(hasMessageDigest()) {
313             validated = serverCredentials.equalsIgnoreCase(digest(credentials));
314         } else {
315             validated = serverCredentials.equals(credentials);
316         }
317         if(! validated ) {
318             if (containerLog.isTraceEnabled()) {
319                 containerLog.trace(sm.getString("realmBase.authenticateFailure",
320                                                 username));
321             }
322             return null;
323         }
324         if (containerLog.isTraceEnabled()) {
325             containerLog.trace(sm.getString("realmBase.authenticateSuccess",
326                                             username));
327         }
328
329         return getPrincipal(username);
330     }
331
332
333     /**
334      * Return the Principal associated with the specified username and
335      * credentials, if there is one; otherwise return <code>null</code>.
336      *
337      * @param username Username of the Principal to look up
338      * @param credentials Password or other credentials to use in
339      * authenticating this username
340      */

341     public Principal JavaDoc authenticate(String JavaDoc username, byte[] credentials) {
342
343         return (authenticate(username, credentials.toString()));
344
345     }
346
347
348     /**
349      * Return the Principal associated with the specified username, which
350      * matches the digest calculated using the given parameters using the
351      * method described in RFC 2069; otherwise return <code>null</code>.
352      *
353      * @param username Username of the Principal to look up
354      * @param clientDigest Digest which has been submitted by the client
355      * @param nOnce Unique (or supposedly unique) token which has been used
356      * for this request
357      * @param realm Realm name
358      * @param md5a2 Second MD5 digest used to calculate the digest :
359      * MD5(Method + ":" + uri)
360      */

361     public Principal JavaDoc authenticate(String JavaDoc username, String JavaDoc clientDigest,
362                                   String JavaDoc nOnce, String JavaDoc nc, String JavaDoc cnonce,
363                                   String JavaDoc qop, String JavaDoc realm,
364                                   String JavaDoc md5a2) {
365
366         String JavaDoc md5a1 = getDigest(username, realm);
367         if (md5a1 == null)
368             return null;
369         String JavaDoc serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":"
370             + cnonce + ":" + qop + ":" + md5a2;
371
372         byte[] valueBytes = null;
373         if(getDigestEncoding() == null) {
374             valueBytes = serverDigestValue.getBytes();
375         } else {
376             try {
377                 valueBytes = serverDigestValue.getBytes(getDigestEncoding());
378             } catch (UnsupportedEncodingException JavaDoc uee) {
379                 log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
380                 throw new IllegalArgumentException JavaDoc(uee.getMessage());
381             }
382         }
383
384         String JavaDoc serverDigest = null;
385         // Bugzilla 32137
386
synchronized(md5Helper) {
387             serverDigest = md5Encoder.encode(md5Helper.digest(valueBytes));
388         }
389
390         if (log.isDebugEnabled()) {
391             log.debug("Digest : " + clientDigest + " Username:" + username
392                     + " ClientSigest:" + clientDigest + " nOnce:" + nOnce
393                     + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop
394                     + " realm:" + realm + "md5a2:" + md5a2
395                     + " Server digest:" + serverDigest);
396         }
397         
398         if (serverDigest.equals(clientDigest))
399             return getPrincipal(username);
400         else
401             return null;
402     }
403
404
405
406     /**
407      * Return the Principal associated with the specified chain of X509
408      * client certificates. If there is none, return <code>null</code>.
409      *
410      * @param certs Array of client certificates, with the first one in
411      * the array being the certificate of the client itself.
412      */

413     public Principal JavaDoc authenticate(X509Certificate JavaDoc certs[]) {
414
415         if ((certs == null) || (certs.length < 1))
416             return (null);
417
418         // Check the validity of each certificate in the chain
419
if (log.isDebugEnabled())
420             log.debug("Authenticating client certificate chain");
421         if (validate) {
422             for (int i = 0; i < certs.length; i++) {
423                 if (log.isDebugEnabled())
424                     log.debug(" Checking validity for '" +
425                         certs[i].getSubjectDN().getName() + "'");
426                 try {
427                     certs[i].checkValidity();
428                 } catch (Exception JavaDoc e) {
429                     if (log.isDebugEnabled())
430                         log.debug(" Validity exception", e);
431                     return (null);
432                 }
433             }
434         }
435
436         // Check the existence of the client Principal in our database
437
return (getPrincipal(certs[0]));
438
439     }
440
441     
442     /**
443      * Execute a periodic task, such as reloading, etc. This method will be
444      * invoked inside the classloading context of this container. Unexpected
445      * throwables will be caught and logged.
446      */

447     public void backgroundProcess() {
448     }
449
450
451     /**
452      * Return the SecurityConstraints configured to guard the request URI for
453      * this request, or <code>null</code> if there is no such constraint.
454      *
455      * @param request Request we are processing
456      * @param context Context the Request is mapped to
457      */

458     public SecurityConstraint [] findSecurityConstraints(Request request,
459                                                          Context context) {
460
461         ArrayList JavaDoc results = null;
462         // Are there any defined security constraints?
463
SecurityConstraint constraints[] = context.findConstraints();
464         if ((constraints == null) || (constraints.length == 0)) {
465             if (log.isDebugEnabled())
466                 log.debug(" No applicable constraints defined");
467             return (null);
468         }
469
470         // Check each defined security constraint
471
String JavaDoc uri = request.getRequestPathMB().toString();
472         
473         String JavaDoc method = request.getMethod();
474         int i;
475         boolean found = false;
476         for (i = 0; i < constraints.length; i++) {
477             SecurityCollection [] collection = constraints[i].findCollections();
478                      
479             // If collection is null, continue to avoid an NPE
480
// See Bugzilla 30624
481
if ( collection == null) {
482         continue;
483             }
484
485             if (log.isDebugEnabled()) {
486                 log.debug(" Checking constraint '" + constraints[i] +
487                     "' against " + method + " " + uri + " --> " +
488                     constraints[i].included(uri, method));
489         }
490
491             for(int j=0; j < collection.length; j++){
492                 String JavaDoc [] patterns = collection[j].findPatterns();
493  
494                 // If patterns is null, continue to avoid an NPE
495
// See Bugzilla 30624
496
if ( patterns == null) {
497             continue;
498                 }
499
500                 for(int k=0; k < patterns.length; k++) {
501                     if(uri.equals(patterns[k])) {
502                         found = true;
503                         if(collection[j].findMethod(method)) {
504                             if(results == null) {
505                                 results = new ArrayList JavaDoc();
506                             }
507                             results.add(constraints[i]);
508                         }
509                     }
510                 }
511             }
512         }
513
514         if(found) {
515             return resultsToArray(results);
516         }
517
518         int longest = -1;
519
520         for (i = 0; i < constraints.length; i++) {
521             SecurityCollection [] collection = constraints[i].findCollections();
522             
523             // If collection is null, continue to avoid an NPE
524
// See Bugzilla 30624
525
if ( collection == null) {
526         continue;
527             }
528
529             if (log.isDebugEnabled()) {
530                 log.debug(" Checking constraint '" + constraints[i] +
531                     "' against " + method + " " + uri + " --> " +
532                     constraints[i].included(uri, method));
533         }
534
535             for(int j=0; j < collection.length; j++){
536                 String JavaDoc [] patterns = collection[j].findPatterns();
537
538                 // If patterns is null, continue to avoid an NPE
539
// See Bugzilla 30624
540
if ( patterns == null) {
541             continue;
542                 }
543
544                 boolean matched = false;
545                 int length = -1;
546                 for(int k=0; k < patterns.length; k++) {
547                     String JavaDoc pattern = patterns[k];
548                     if(pattern.startsWith("/") && pattern.endsWith("/*") &&
549                        pattern.length() >= longest) {
550                             
551                         if(pattern.length() == 2) {
552                             matched = true;
553                             length = pattern.length();
554                         } else if(pattern.regionMatches(0,uri,0,
555                                                         pattern.length()-1) ||
556                                   (pattern.length()-2 == uri.length() &&
557                                    pattern.regionMatches(0,uri,0,
558                                                         pattern.length()-2))) {
559                             matched = true;
560                             length = pattern.length();
561                         }
562                     }
563                 }
564                 if(matched) {
565                     found = true;
566                     if(length > longest) {
567                         if(results != null) {
568                             results.clear();
569                         }
570                         longest = length;
571                     }
572                     if(collection[j].findMethod(method)) {
573                         if(results == null) {
574                             results = new ArrayList JavaDoc();
575                         }
576                         results.add(constraints[i]);
577                     }
578                 }
579             }
580         }
581
582         if(found) {
583             return resultsToArray(results);
584         }
585
586         for (i = 0; i < constraints.length; i++) {
587             SecurityCollection [] collection = constraints[i].findCollections();
588
589             // If collection is null, continue to avoid an NPE
590
// See Bugzilla 30624
591
if ( collection == null) {
592         continue;
593             }
594             
595             if (log.isDebugEnabled()) {
596                 log.debug(" Checking constraint '" + constraints[i] +
597                     "' against " + method + " " + uri + " --> " +
598                     constraints[i].included(uri, method));
599         }
600
601             boolean matched = false;
602             int pos = -1;
603             for(int j=0; j < collection.length; j++){
604                 String JavaDoc [] patterns = collection[j].findPatterns();
605
606                 // If patterns is null, continue to avoid an NPE
607
// See Bugzilla 30624
608
if ( patterns == null) {
609             continue;
610                 }
611
612                 for(int k=0; k < patterns.length && !matched; k++) {
613                     String JavaDoc pattern = patterns[k];
614                     if(pattern.startsWith("*.")){
615                         int slash = uri.lastIndexOf("/");
616                         int dot = uri.lastIndexOf(".");
617                         if(slash >= 0 && dot > slash &&
618                            dot != uri.length()-1 &&
619                            uri.length()-dot == pattern.length()-1) {
620                             if(pattern.regionMatches(1,uri,dot,uri.length()-dot)) {
621                                 matched = true;
622                                 pos = j;
623                             }
624                         }
625                     }
626                 }
627             }
628             if(matched) {
629                 found = true;
630                 if(collection[pos].findMethod(method)) {
631                     if(results == null) {
632                         results = new ArrayList JavaDoc();
633                     }
634                     results.add(constraints[i]);
635                 }
636             }
637         }
638
639         if(found) {
640             return resultsToArray(results);
641         }
642
643         for (i = 0; i < constraints.length; i++) {
644             SecurityCollection [] collection = constraints[i].findCollections();
645             
646             // If collection is null, continue to avoid an NPE
647
// See Bugzilla 30624
648
if ( collection == null) {
649         continue;
650             }
651
652             if (log.isDebugEnabled()) {
653                 log.debug(" Checking constraint '" + constraints[i] +
654                     "' against " + method + " " + uri + " --> " +
655                     constraints[i].included(uri, method));
656         }
657
658             for(int j=0; j < collection.length; j++){
659                 String JavaDoc [] patterns = collection[j].findPatterns();
660
661                 // If patterns is null, continue to avoid an NPE
662
// See Bugzilla 30624
663
if ( patterns == null) {
664             continue;
665                 }
666
667                 boolean matched = false;
668                 for(int k=0; k < patterns.length && !matched; k++) {
669                     String JavaDoc pattern = patterns[k];
670                     if(pattern.equals("/")){
671                         matched = true;
672                     }
673                 }
674                 if(matched) {
675                     if(results == null) {
676                         results = new ArrayList JavaDoc();
677                     }
678                     results.add(constraints[i]);
679                 }
680             }
681         }
682
683         if(results == null) {
684             // No applicable security constraint was found
685
if (log.isDebugEnabled())
686                 log.debug(" No applicable constraint located");
687         }
688         return resultsToArray(results);
689     }
690  
691     /**
692      * Convert an ArrayList to a SecurityContraint [].
693      */

694     private SecurityConstraint [] resultsToArray(ArrayList JavaDoc results) {
695         if(results == null) {
696             return null;
697         }
698         SecurityConstraint [] array = new SecurityConstraint[results.size()];
699         results.toArray(array);
700         return array;
701     }
702
703     
704     /**
705      * Perform access control based on the specified authorization constraint.
706      * Return <code>true</code> if this constraint is satisfied and processing
707      * should continue, or <code>false</code> otherwise.
708      *
709      * @param request Request we are processing
710      * @param response Response we are creating
711      * @param constraints Security constraint we are enforcing
712      * @param context The Context to which client of this class is attached.
713      *
714      * @exception IOException if an input/output error occurs
715      */

716     public boolean hasResourcePermission(Request request,
717                                          Response JavaDoc response,
718                                          SecurityConstraint []constraints,
719                                          Context context)
720         throws IOException JavaDoc {
721
722         if (constraints == null || constraints.length == 0)
723             return (true);
724
725         // Specifically allow access to the form login and form error pages
726
// and the "j_security_check" action
727
LoginConfig config = context.getLoginConfig();
728         if ((config != null) &&
729             (Constants.FORM_METHOD.equals(config.getAuthMethod()))) {
730             String JavaDoc requestURI = request.getRequestPathMB().toString();
731             String JavaDoc loginPage = config.getLoginPage();
732             if (loginPage.equals(requestURI)) {
733                 if (log.isDebugEnabled())
734                     log.debug(" Allow access to login page " + loginPage);
735                 return (true);
736             }
737             String JavaDoc errorPage = config.getErrorPage();
738             if (errorPage.equals(requestURI)) {
739                 if (log.isDebugEnabled())
740                     log.debug(" Allow access to error page " + errorPage);
741                 return (true);
742             }
743             if (requestURI.endsWith(Constants.FORM_ACTION)) {
744                 if (log.isDebugEnabled())
745                     log.debug(" Allow access to username/password submission");
746                 return (true);
747             }
748         }
749
750         // Which user principal have we already authenticated?
751
Principal JavaDoc principal = request.getPrincipal();
752         boolean status = false;
753         boolean denyfromall = false;
754         for(int i=0; i < constraints.length; i++) {
755             SecurityConstraint constraint = constraints[i];
756
757             String JavaDoc roles[];
758             if (constraint.getAllRoles()) {
759                 // * means all roles defined in web.xml
760
roles = request.getContext().findSecurityRoles();
761             } else {
762                 roles = constraint.findAuthRoles();
763             }
764
765             if (roles == null)
766                 roles = new String JavaDoc[0];
767
768             if (log.isDebugEnabled())
769                 log.debug(" Checking roles " + principal);
770
771             if (roles.length == 0 && !constraint.getAllRoles()) {
772                 if(constraint.getAuthConstraint()) {
773                     if( log.isDebugEnabled() )
774                         log.debug("No roles ");
775                     status = false; // No listed roles means no access at all
776
denyfromall = true;
777                 } else {
778                     if(log.isDebugEnabled())
779                         log.debug("Passing all access");
780                     return (true);
781                 }
782             } else if (principal == null) {
783                 if (log.isDebugEnabled())
784                     log.debug(" No user authenticated, cannot grant access");
785                 status = false;
786             } else if(!denyfromall) {
787
788                 for (int j = 0; j < roles.length; j++) {
789                     if (hasRole(principal, roles[j]))
790                         status = true;
791                     if( log.isDebugEnabled() )
792                         log.debug( "No role found: " + roles[j]);
793                 }
794             }
795         }
796
797         if (allRolesMode != AllRolesMode.STRICT_MODE && !status && principal != null) {
798             if (log.isDebugEnabled()) {
799                 log.debug("Checking for all roles mode: " + allRolesMode);
800             }
801             // Check for an all roles(role-name="*")
802
for (int i = 0; i < constraints.length; i++) {
803                 SecurityConstraint constraint = constraints[i];
804                 String JavaDoc roles[];
805                 // If the all roles mode exists, sets
806
if (constraint.getAllRoles()) {
807                     if (allRolesMode == AllRolesMode.AUTH_ONLY_MODE) {
808                         if (log.isDebugEnabled()) {
809                             log.debug("Granting access for role-name=*, auth-only");
810                         }
811                         status = true;
812                         break;
813                     }
814                     
815                     // For AllRolesMode.STRICT_AUTH_ONLY_MODE there must be zero roles
816
roles = request.getContext().findSecurityRoles();
817                     if (roles.length == 0 && allRolesMode == AllRolesMode.STRICT_AUTH_ONLY_MODE) {
818                         if (log.isDebugEnabled()) {
819                             log.debug("Granting access for role-name=*, strict auth-only");
820                         }
821                         status = true;
822                         break;
823                     }
824                 }
825             }
826         }
827         
828         // Return a "Forbidden" message denying access to this resource
829
if(!status) {
830             response.sendError
831                 (HttpServletResponse.SC_FORBIDDEN,
832                  sm.getString("realmBase.forbidden"));
833         }
834         return status;
835
836     }
837     
838     
839     /**
840      * Return <code>true</code> if the specified Principal has the specified
841      * security role, within the context of this Realm; otherwise return
842      * <code>false</code>. This method can be overridden by Realm
843      * implementations, but the default is adequate when an instance of
844      * <code>GenericPrincipal</code> is used to represent authenticated
845      * Principals from this Realm.
846      *
847      * @param principal Principal for whom the role is to be checked
848      * @param role Security role to be checked
849      */

850     public boolean hasRole(Principal JavaDoc principal, String JavaDoc role) {
851
852         // Should be overriten in JAASRealm - to avoid pretty inefficient conversions
853
if ((principal == null) || (role == null) ||
854             !(principal instanceof GenericPrincipal))
855             return (false);
856
857         GenericPrincipal gp = (GenericPrincipal) principal;
858         if (!(gp.getRealm() == this)) {
859             if(log.isDebugEnabled())
860                 log.debug("Different realm " + this + " " + gp.getRealm());// return (false);
861
}
862         boolean result = gp.hasRole(role);
863         if (log.isDebugEnabled()) {
864             String JavaDoc name = principal.getName();
865             if (result)
866                 log.debug(sm.getString("realmBase.hasRoleSuccess", name, role));
867             else
868                 log.debug(sm.getString("realmBase.hasRoleFailure", name, role));
869         }
870         return (result);
871
872     }
873
874     
875     /**
876      * Enforce any user data constraint required by the security constraint
877      * guarding this request URI. Return <code>true</code> if this constraint
878      * was not violated and processing should continue, or <code>false</code>
879      * if we have created a response already.
880      *
881      * @param request Request we are processing
882      * @param response Response we are creating
883      * @param constraints Security constraint being checked
884      *
885      * @exception IOException if an input/output error occurs
886      */

887     public boolean hasUserDataPermission(Request request,
888                                          Response JavaDoc response,
889                                          SecurityConstraint []constraints)
890         throws IOException JavaDoc {
891
892         // Is there a relevant user data constraint?
893
if (constraints == null || constraints.length == 0) {
894             if (log.isDebugEnabled())
895                 log.debug(" No applicable security constraint defined");
896             return (true);
897         }
898         for(int i=0; i < constraints.length; i++) {
899             SecurityConstraint constraint = constraints[i];
900             String JavaDoc userConstraint = constraint.getUserConstraint();
901             if (userConstraint == null) {
902                 if (log.isDebugEnabled())
903                     log.debug(" No applicable user data constraint defined");
904                 return (true);
905             }
906             if (userConstraint.equals(Constants.NONE_TRANSPORT)) {
907                 if (log.isDebugEnabled())
908                     log.debug(" User data constraint has no restrictions");
909                 return (true);
910             }
911
912         }
913         // Validate the request against the user data constraint
914
if (request.getRequest().isSecure()) {
915             if (log.isDebugEnabled())
916                 log.debug(" User data constraint already satisfied");
917             return (true);
918         }
919         // Initialize variables we need to determine the appropriate action
920
int redirectPort = request.getConnector().getRedirectPort();
921
922         // Is redirecting disabled?
923
if (redirectPort <= 0) {
924             if (log.isDebugEnabled())
925                 log.debug(" SSL redirect is disabled");
926             response.sendError
927                 (HttpServletResponse.SC_FORBIDDEN,
928                  request.getRequestURI());
929             return (false);
930         }
931
932         // Redirect to the corresponding SSL port
933
StringBuffer JavaDoc file = new StringBuffer JavaDoc();
934         String JavaDoc protocol = "https";
935         String JavaDoc host = request.getServerName();
936         // Protocol
937
file.append(protocol).append("://").append(host);
938         // Host with port
939
if(redirectPort != 443) {
940             file.append(":").append(redirectPort);
941         }
942         // URI
943
file.append(request.getRequestURI());
944         String JavaDoc requestedSessionId = request.getRequestedSessionId();
945         if ((requestedSessionId != null) &&
946             request.isRequestedSessionIdFromURL()) {
947             file.append(";jsessionid=");
948             file.append(requestedSessionId);
949         }
950         String JavaDoc queryString = request.getQueryString();
951         if (queryString != null) {
952             file.append('?');
953             file.append(queryString);
954         }
955         if (log.isDebugEnabled())
956             log.debug(" Redirecting to " + file.toString());
957         response.sendRedirect(file.toString());
958         return (false);
959
960     }
961     
962     
963     /**
964      * Remove a property change listener from this component.
965      *
966      * @param listener The listener to remove
967      */

968     public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
969
970         support.removePropertyChangeListener(listener);
971
972     }
973
974
975     // ------------------------------------------------------ Lifecycle Methods
976

977
978     /**
979      * Add a lifecycle event listener to this component.
980      *
981      * @param listener The listener to add
982      */

983     public void addLifecycleListener(LifecycleListener listener) {
984
985         lifecycle.addLifecycleListener(listener);
986
987     }
988
989
990     /**
991      * Get the lifecycle listeners associated with this lifecycle. If this
992      * Lifecycle has no listeners registered, a zero-length array is returned.
993      */

994     public LifecycleListener[] findLifecycleListeners() {
995
996         return lifecycle.findLifecycleListeners();
997
998     }
999
1000
1001    /**
1002     * Remove a lifecycle event listener from this component.
1003     *
1004     * @param listener The listener to remove
1005     */

1006    public void removeLifecycleListener(LifecycleListener listener) {
1007
1008        lifecycle.removeLifecycleListener(listener);
1009
1010    }
1011
1012    /**
1013     * Prepare for the beginning of active use of the public methods of this
1014     * component. This method should be called before any of the public
1015     * methods of this component are utilized. It should also send a
1016     * LifecycleEvent of type START_EVENT to any registered listeners.
1017     *
1018     * @exception LifecycleException if this component detects a fatal error
1019     * that prevents this component from being used
1020     */

1021    public void start() throws LifecycleException {
1022
1023        // Validate and update our current component state
1024
if (started) {
1025            if(log.isInfoEnabled())
1026                log.info(sm.getString("realmBase.alreadyStarted"));
1027            return;
1028        }
1029        if( !initialized ) {
1030            init();
1031        }
1032        lifecycle.fireLifecycleEvent(START_EVENT, null);
1033        started = true;
1034
1035        // Create a MessageDigest instance for credentials, if desired
1036
if (digest != null) {
1037            try {
1038                md = MessageDigest.getInstance(digest);
1039            } catch (NoSuchAlgorithmException JavaDoc e) {
1040                throw new LifecycleException
1041                    (sm.getString("realmBase.algorithm", digest), e);
1042            }
1043        }
1044
1045    }
1046
1047
1048    /**
1049     * Gracefully terminate the active use of the public methods of this
1050     * component. This method should be the last one called on a given
1051     * instance of this component. It should also send a LifecycleEvent
1052     * of type STOP_EVENT to any registered listeners.
1053     *
1054     * @exception LifecycleException if this component detects a fatal error
1055     * that needs to be reported
1056     */

1057    public void stop()
1058        throws LifecycleException {
1059
1060        // Validate and update our current component state
1061
if (!started) {
1062            if(log.isInfoEnabled())
1063                log.info(sm.getString("realmBase.notStarted"));
1064            return;
1065        }
1066        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
1067        started = false;
1068
1069        // Clean up allocated resources
1070
md = null;
1071        
1072        destroy();
1073    
1074    }
1075    
1076    public void destroy() {
1077    
1078        // unregister this realm
1079
if ( oname!=null ) {
1080            try {
1081                Registry.getRegistry(null, null).unregisterComponent(oname);
1082                if(log.isDebugEnabled())
1083                    log.debug( "unregistering realm " + oname );
1084            } catch( Exception JavaDoc ex ) {
1085                log.error( "Can't unregister realm " + oname, ex);
1086            }
1087        }
1088          
1089    }
1090
1091    // ------------------------------------------------------ Protected Methods
1092

1093
1094    /**
1095     * Digest the password using the specified algorithm and
1096     * convert the result to a corresponding hexadecimal string.
1097     * If exception, the plain credentials string is returned.
1098     *
1099     * @param credentials Password or other credentials to use in
1100     * authenticating this username
1101     */

1102    protected String JavaDoc digest(String JavaDoc credentials) {
1103
1104        // If no MessageDigest instance is specified, return unchanged
1105
if (hasMessageDigest() == false)
1106            return (credentials);
1107
1108        // Digest the user credentials and return as hexadecimal
1109
synchronized (this) {
1110            try {
1111                md.reset();
1112    
1113                byte[] bytes = null;
1114                if(getDigestEncoding() == null) {
1115                    bytes = credentials.getBytes();
1116                } else {
1117                    try {
1118                        bytes = credentials.getBytes(getDigestEncoding());
1119                    } catch (UnsupportedEncodingException JavaDoc uee) {
1120                        log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
1121                        throw new IllegalArgumentException JavaDoc(uee.getMessage());
1122                    }
1123                }
1124                md.update(bytes);
1125
1126                return (HexUtils.convert(md.digest()));
1127            } catch (Exception JavaDoc e) {
1128                log.error(sm.getString("realmBase.digest"), e);
1129                return (credentials);
1130            }
1131        }
1132
1133    }
1134
1135    protected boolean hasMessageDigest() {
1136        return !(md == null);
1137    }
1138
1139    /**
1140     * Return the digest associated with given principal's user name.
1141     */

1142    protected String JavaDoc getDigest(String JavaDoc username, String JavaDoc realmName) {
1143        if (md5Helper == null) {
1144            try {
1145                md5Helper = MessageDigest.getInstance("MD5");
1146            } catch (NoSuchAlgorithmException JavaDoc e) {
1147                log.error("Couldn't get MD5 digest: ", e);
1148                throw new IllegalStateException JavaDoc(e.getMessage());
1149            }
1150        }
1151
1152        if (hasMessageDigest()) {
1153            // Use pre-generated digest
1154
return getPassword(username);
1155        }
1156        
1157        String JavaDoc digestValue = username + ":" + realmName + ":"
1158            + getPassword(username);
1159
1160        byte[] valueBytes = null;
1161        if(getDigestEncoding() == null) {
1162            valueBytes = digestValue.getBytes();
1163        } else {
1164            try {
1165                valueBytes = digestValue.getBytes(getDigestEncoding());
1166            } catch (UnsupportedEncodingException JavaDoc uee) {
1167                log.error("Illegal digestEncoding: " + getDigestEncoding(), uee);
1168                throw new IllegalArgumentException JavaDoc(uee.getMessage());
1169            }
1170        }
1171
1172        byte[] digest = null;
1173        // Bugzilla 32137
1174
synchronized(md5Helper) {
1175            digest = md5Helper.digest(valueBytes);
1176        }
1177
1178        return md5Encoder.encode(digest);
1179    }
1180
1181
1182    /**
1183     * Return a short name for this Realm implementation, for use in
1184     * log messages.
1185     */

1186    protected abstract String JavaDoc getName();
1187
1188
1189    /**
1190     * Return the password associated with the given principal's user name.
1191     */

1192    protected abstract String JavaDoc getPassword(String JavaDoc username);
1193
1194
1195    /**
1196     * Return the Principal associated with the given certificate.
1197     */

1198    protected Principal JavaDoc getPrincipal(X509Certificate JavaDoc usercert) {
1199        return(getPrincipal(usercert.getSubjectDN().getName()));
1200    }
1201    
1202
1203    /**
1204     * Return the Principal associated with the given user name.
1205     */

1206    protected abstract Principal JavaDoc getPrincipal(String JavaDoc username);
1207
1208
1209    // --------------------------------------------------------- Static Methods
1210

1211
1212    /**
1213     * Digest password using the algorithm especificied and
1214     * convert the result to a corresponding hex string.
1215     * If exception, the plain credentials string is returned
1216     *
1217     * @param credentials Password or other credentials to use in
1218     * authenticating this username
1219     * @param algorithm Algorithm used to do the digest
1220     * @param encoding Character encoding of the string to digest
1221     */

1222    public final static String JavaDoc Digest(String JavaDoc credentials, String JavaDoc algorithm,
1223                                      String JavaDoc encoding) {
1224
1225        try {
1226            // Obtain a new message digest with "digest" encryption
1227
MessageDigest JavaDoc md =
1228                (MessageDigest JavaDoc) MessageDigest.getInstance(algorithm).clone();
1229
1230            // encode the credentials
1231
// Should use the digestEncoding, but that's not a static field
1232
if (encoding == null) {
1233                md.update(credentials.getBytes());
1234            } else {
1235                md.update(credentials.getBytes(encoding));
1236            }
1237
1238            // Digest the credentials and return as hexadecimal
1239
return (HexUtils.convert(md.digest()));
1240        } catch(Exception JavaDoc ex) {
1241            log.error(ex);
1242            return credentials;
1243        }
1244
1245    }
1246
1247
1248    /**
1249     * Digest password using the algorithm especificied and
1250     * convert the result to a corresponding hex string.
1251     * If exception, the plain credentials string is returned
1252     */

1253    public static void main(String JavaDoc args[]) {
1254
1255        String JavaDoc encoding = null;
1256        int firstCredentialArg = 2;
1257        
1258        if (args.length > 4 && args[2].equalsIgnoreCase("-e")) {
1259            encoding = args[3];
1260            firstCredentialArg = 4;
1261        }
1262        
1263        if(args.length > firstCredentialArg && args[0].equalsIgnoreCase("-a")) {
1264            for(int i=firstCredentialArg; i < args.length ; i++){
1265                System.out.print(args[i]+":");
1266                System.out.println(Digest(args[i], args[1], encoding));
1267            }
1268        } else {
1269            System.out.println
1270                ("Usage: RealmBase -a <algorithm> [-e <encoding>] <credentials>");
1271        }
1272
1273    }
1274
1275
1276    // -------------------- JMX and Registration --------------------
1277
protected String JavaDoc type;
1278    protected String JavaDoc domain;
1279    protected String JavaDoc host;
1280    protected String JavaDoc path;
1281    protected ObjectName JavaDoc oname;
1282    protected ObjectName JavaDoc controller;
1283    protected MBeanServer JavaDoc mserver;
1284
1285    public ObjectName JavaDoc getController() {
1286        return controller;
1287    }
1288
1289    public void setController(ObjectName JavaDoc controller) {
1290        this.controller = controller;
1291    }
1292
1293    public ObjectName JavaDoc getObjectName() {
1294        return oname;
1295    }
1296
1297    public String JavaDoc getDomain() {
1298        return domain;
1299    }
1300
1301    public String JavaDoc getType() {
1302        return type;
1303    }
1304
1305    public ObjectName JavaDoc preRegister(MBeanServer JavaDoc server,
1306                                  ObjectName JavaDoc name) throws Exception JavaDoc {
1307        oname=name;
1308        mserver=server;
1309        domain=name.getDomain();
1310
1311        type=name.getKeyProperty("type");
1312        host=name.getKeyProperty("host");
1313        path=name.getKeyProperty("path");
1314
1315        return name;
1316    }
1317
1318    public void postRegister(Boolean JavaDoc registrationDone) {
1319    }
1320
1321    public void preDeregister() throws Exception JavaDoc {
1322    }
1323
1324    public void postDeregister() {
1325    }
1326
1327    protected boolean initialized=false;
1328    
1329    public void init() {
1330        this.containerLog = container.getLogger();
1331        if( initialized && container != null ) return;
1332        
1333        initialized=true;
1334        if( container== null ) {
1335            ObjectName JavaDoc parent=null;
1336            // Register with the parent
1337
try {
1338                if( host == null ) {
1339                    // global
1340
parent=new ObjectName JavaDoc(domain +":type=Engine");
1341                } else if( path==null ) {
1342                    parent=new ObjectName JavaDoc(domain +
1343                            ":type=Host,host=" + host);
1344                } else {
1345                    parent=new ObjectName JavaDoc(domain +":j2eeType=WebModule,name=//" +
1346                            host + path);
1347                }
1348                if( mserver.isRegistered(parent )) {
1349                    if(log.isDebugEnabled())
1350                        log.debug("Register with " + parent);
1351                    mserver.setAttribute(parent, new Attribute JavaDoc("realm", this));
1352                }
1353            } catch (Exception JavaDoc e) {
1354                log.error("Parent not available yet: " + parent);
1355            }
1356        }
1357        
1358        if( oname==null ) {
1359            // register
1360
try {
1361                ContainerBase cb=(ContainerBase)container;
1362                oname=new ObjectName JavaDoc(cb.getDomain()+":type=Realm" + cb.getContainerSuffix());
1363                Registry.getRegistry(null, null).registerComponent(this, oname, null );
1364                if(log.isDebugEnabled())
1365                    log.debug("Register Realm "+oname);
1366            } catch (Throwable JavaDoc e) {
1367                log.error( "Can't register " + oname, e);
1368            }
1369        }
1370
1371    }
1372
1373
1374    protected static class AllRolesMode {
1375        
1376        private String JavaDoc name;
1377        /** Use the strict servlet spec interpretation which requires that the user
1378         * have one of the web-app/security-role/role-name
1379         */

1380        public static final AllRolesMode STRICT_MODE = new AllRolesMode("strict");
1381        /** Allow any authenticated user
1382         */

1383        public static final AllRolesMode AUTH_ONLY_MODE = new AllRolesMode("authOnly");
1384        /** Allow any authenticated user only if there are no web-app/security-roles
1385         */

1386        public static final AllRolesMode STRICT_AUTH_ONLY_MODE = new AllRolesMode("strictAuthOnly");
1387        
1388        static AllRolesMode toMode(String JavaDoc name)
1389        {
1390            AllRolesMode mode;
1391            if( name.equalsIgnoreCase(STRICT_MODE.name) )
1392                mode = STRICT_MODE;
1393            else if( name.equalsIgnoreCase(AUTH_ONLY_MODE.name) )
1394                mode = AUTH_ONLY_MODE;
1395            else if( name.equalsIgnoreCase(STRICT_AUTH_ONLY_MODE.name) )
1396                mode = STRICT_AUTH_ONLY_MODE;
1397            else
1398                throw new IllegalStateException JavaDoc("Unknown mode, must be one of: strict, authOnly, strictAuthOnly");
1399            return mode;
1400        }
1401        
1402        private AllRolesMode(String JavaDoc name)
1403        {
1404            this.name = name;
1405        }
1406        
1407        public boolean equals(Object JavaDoc o)
1408        {
1409            boolean equals = false;
1410            if( o instanceof AllRolesMode )
1411            {
1412                AllRolesMode mode = (AllRolesMode) o;
1413                equals = name.equals(mode.name);
1414            }
1415            return equals;
1416        }
1417        public int hashCode()
1418        {
1419            return name.hashCode();
1420        }
1421        public String JavaDoc toString()
1422        {
1423            return name;
1424        }
1425    }
1426
1427}
1428
Popular Tags