KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jonas > security > realm > web > catalina55 > JACC


1 /**
2  * JOnAS: Java(TM) Open Application Server
3  * Copyright (C) 2004-2005 Bull S.A.
4  * Contact: jonas-team@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  *
21  * --------------------------------------------------------------------------
22  * $Id: JACC.java,v 1.6 2005/06/08 09:58:17 benoitf Exp $
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.jonas.security.realm.web.catalina55;
27
28 import java.io.IOException JavaDoc;
29 import java.security.Principal JavaDoc;
30 import java.security.cert.X509Certificate JavaDoc;
31 import java.util.ArrayList JavaDoc;
32
33 import javax.servlet.http.HttpServletRequest JavaDoc;
34 import javax.servlet.http.HttpServletResponse JavaDoc;
35
36 import org.apache.catalina.Context;
37 import org.apache.catalina.connector.Request;
38 import org.apache.catalina.connector.Response;
39 import org.apache.catalina.LifecycleException;
40 import org.apache.catalina.deploy.LoginConfig;
41 import org.apache.catalina.deploy.SecurityConstraint;
42 import org.apache.catalina.realm.Constants;
43 import org.apache.catalina.realm.GenericPrincipal;
44 import org.apache.catalina.realm.RealmBase;
45 import org.apache.catalina.util.StringManager;
46
47 import org.objectweb.jonas.common.Log;
48 import org.objectweb.jonas.security.SecurityService;
49 import org.objectweb.jonas.security.realm.factory.JResource;
50 import org.objectweb.jonas.security.realm.factory.JResourceException;
51 import org.objectweb.jonas.security.realm.principals.User;
52 import org.objectweb.jonas.service.ServiceManager;
53 import org.objectweb.jonas.web.lib.PermissionManager;
54
55 import org.objectweb.security.context.SecurityContext;
56 import org.objectweb.security.context.SecurityCurrent;
57
58 import org.objectweb.util.monolog.api.BasicLevel;
59 import org.objectweb.util.monolog.api.Logger;
60
61 /**
62  * <p>
63  * Implementation of a Realm. (by a wrapper) Use any JOnAS realm by specifying
64  * the resource name This implementation manages the security with JACC
65  * specification It implements Cloneable to allow clones. Each context must have
66  * its own Realm. A realm can not be shared across different contexts or an
67  * engine. This is because each realm is associated to a permission manager
68  *
69  * Extends the Realmbase class of the Tomcat Server.
70  *
71  * @author Florent Benoit
72  */

73 public class JACC extends RealmBase implements Cloneable JavaDoc {
74
75     /**
76      * Descriptive information about this Realm implementation.
77      */

78     private static final String JavaDoc NAME = "JACC_Catalina55";
79
80     /**
81      * Descriptive information about this Realm implementation.
82      */

83     private static final String JavaDoc INFO = "org.objectweb.jonas.security.realm.JRealmJACCCatalina50/1.0";
84
85     /**
86      * The string manager for this package.
87      */

88     private static StringManager sm = StringManager.getManager(Constants.Package);
89
90     /**
91      * The logger used in JOnAS
92      */

93     private static Logger logger = null;
94
95     /**
96      * The resource we will use to authenticate users and identify associated
97      * roles.
98      */

99     private JResource jResource = null;
100
101     /**
102      * The name of the resource
103      */

104     private String JavaDoc resourceName = null;
105
106     /**
107      * Reference to the JOnAS security service
108      */

109     private SecurityService securityService = null;
110
111     /**
112      * Permission manager used by this realm for JACC permissions
113      */

114     private PermissionManager permissionManager = null;
115
116     /**
117      * Last request that has been send to hasUserDataPermission or
118      * hasResourcePermission methods Used in hasRole to know the current servlet
119      * name.
120      * This request is store in a local thread.
121      */

122     private ThreadLocal JavaDoc lastRequestThread = new ThreadLocal JavaDoc();
123
124     /**
125      * Context (used to retrieve web.xml informations)
126      */

127     private Context context = null;
128
129     /**
130      * Name of this Realm for traces
131      */

132     private String JavaDoc realmName = NAME;
133
134
135     /**
136      * Return descriptive information about this Realm implementation and the
137      * corresponding version number, in the format
138      * <code>&lt;description&gt;/&lt;version&gt;</code>.
139      *
140      * @return the info.
141      */

142     public String JavaDoc getInfo() {
143         return INFO;
144     }
145
146     /**
147      * Return the resource name we will be using.
148      *
149      * @return the resource name.
150      */

151     public String JavaDoc getResourceName() {
152         return resourceName;
153     }
154
155     /**
156      * Set the resource name we will be using.
157      *
158      * @param resourceName The new resource name
159      */

160     public void setResourceName(String JavaDoc resourceName) {
161         this.resourceName = resourceName;
162
163     }
164
165     /**
166      * Set the permission manager used by this realm
167      *
168      * @param permissionManager the permission manager to use
169      */

170     public void setPermissionManager(PermissionManager permissionManager) {
171         this.permissionManager = permissionManager;
172
173     }
174
175     /**
176      * Return the SecurityConstraints configured to guard the request URI for
177      * this request, or <code>null</code> if there is no such constraint.
178      *
179      * @param request Request we are processing
180      * @param context Context the Request is mapped to
181      * @return security constraints configured to guard the request URI
182      */

183     public SecurityConstraint[] findSecurityConstraints(Request JavaDoc request, Context context) {
184         // Use super Method
185
return super.findSecurityConstraints(request, context);
186     }
187
188     /**
189      * Perform access control based on the specified authorization constraint.
190      *
191      * @return <code>true</code> if this constraint is satisfied and
192      * processing should continue, or <code>false</code> otherwise.
193      *
194      * @param request Request we are processing
195      * @param response Response we are creating
196      * @param constraints Security constraint we are enforcing
197      * @param context The Context to which client of this class is attached.
198      *
199      * @exception IOException if an input/output error occurs
200      */

201     public boolean hasResourcePermission(Request JavaDoc request, Response JavaDoc response, SecurityConstraint[] constraints,
202             Context context) throws IOException JavaDoc {
203
204         // Update request
205
lastRequestThread.set(request);
206
207         // --- Use code of RealmBase for the Login / Error pages
208

209         // Specifically allow access to the form login and form error pages
210
// and the "j_security_check" action
211
LoginConfig config = context.getLoginConfig();
212         if (config != null && Constants.FORM_METHOD.equals(config.getAuthMethod())) {
213             String JavaDoc requestURI = request.getDecodedRequestURI();
214             String JavaDoc loginPage = context.getPath() + config.getLoginPage();
215             if (loginPage.equals(requestURI)) {
216                 if (logger.isLoggable(BasicLevel.DEBUG)) {
217                     logger.log(BasicLevel.DEBUG, realmName + "Allow access to login page " + loginPage);
218                 }
219                 return true;
220             }
221
222             String JavaDoc errorPage = context.getPath() + config.getErrorPage();
223             if (errorPage.equals(requestURI)) {
224                 if (logger.isLoggable(BasicLevel.DEBUG)) {
225                     logger.log(BasicLevel.DEBUG, realmName + "Allow access to error page " + errorPage);
226                 }
227                 return true;
228             }
229             if (requestURI.endsWith(Constants.FORM_ACTION)) {
230                 if (logger.isLoggable(BasicLevel.DEBUG)) {
231                     logger.log(BasicLevel.DEBUG, realmName + "Allow access to username/password submission");
232                 }
233                 return true;
234             }
235         }
236
237         // Which user principal have we already authenticated?
238
Principal JavaDoc principal = request.getUserPrincipal();
239
240         // --- End of code from RealmBase class
241

242         String JavaDoc[] roles = null;
243         String JavaDoc principalName = null;
244         if (principal instanceof GenericPrincipal) {
245             roles = ((GenericPrincipal) principal).getRoles();
246             principalName = principal.getName();
247         }
248
249         if (permissionManager == null) {
250             logger.log(BasicLevel.ERROR, realmName + "No permission manager is set. It means that you are using this realm without using the JOnAS deployer but only Tomcat.");
251             return false;
252         }
253
254         boolean hasResourcePermission = permissionManager.checkWebResourcePermission(request, principalName, roles);
255
256         // Need to send HTTP status code as invoke() method of Authenticator
257
// expect that it is
258
// done by the realm.
259
if (!hasResourcePermission) {
260             // Return a "Forbidden" message denying access to this resource
261
response.sendError(HttpServletResponse.SC_FORBIDDEN, sm
262                     .getString("realmBase.forbidden"));
263         }
264
265         return hasResourcePermission;
266     }
267
268     /**
269      * @return <code>true</code> if the specified Principal has the specified
270      * security role, within the context of this Realm; otherwise return
271      * <code>false</code>. This method can be overridden by Realm
272      * implementations, but the default is adequate when an instance of
273      * <code>GenericPrincipal</code> is used to represent
274      * authenticated Principals from this Realm.
275      *
276      * @param principal Principal for whom the role is to be checked
277      * @param role Security role to be checked
278      */

279     public boolean hasRole(Principal JavaDoc principal, String JavaDoc role) {
280
281         if ((principal == null) || (role == null) || !(principal instanceof GenericPrincipal)) {
282             return false;
283         }
284
285         if (logger.isLoggable(BasicLevel.DEBUG)) {
286             logger.log(BasicLevel.DEBUG, realmName + "Principal = " + principal);
287             logger.log(BasicLevel.DEBUG, realmName + "Role = " + role);
288         }
289
290         if (context == null) {
291             logger.log(BasicLevel.ERROR, realmName + "Cannot find a servlet name for isUserInRole() as no context was found");
292             return false;
293         }
294
295         Request JavaDoc req = (Request JavaDoc) lastRequestThread.get();
296
297         if (req == null) {
298             logger.log(BasicLevel.ERROR, realmName + "Cannot find a servlet name for isUserInRole(). No previous request !");
299             return false;
300         }
301         String JavaDoc servletName = findServletName(req);
302         String JavaDoc[] roles = null;
303         String JavaDoc principalName = null;
304
305         if (principal instanceof GenericPrincipal) {
306             roles = ((GenericPrincipal) principal).getRoles();
307             principalName = principal.getName();
308         }
309
310         if (permissionManager == null) {
311             logger.log(BasicLevel.ERROR, realmName + "No permission manager is set. It means that you are using this realm without using the JOnAS deployer but only Tomcat.");
312             return false;
313         }
314
315         boolean hasRole = permissionManager.checkWebRoleRefPermission(req, servletName, principalName, roles, role);
316         return hasRole;
317     }
318
319     /**
320      * @return the name of the servlet (or "" if it's a JSP for example) This
321      * servlet name is used to build JACC permission for the hasRole
322      * method
323      *
324      * @param request the servlet request with which we have to find
325      * servlet name
326      */

327     private String JavaDoc findServletName(Request JavaDoc request) {
328
329         // Pattern of the user (remove path)
330
String JavaDoc userPattern = request.getRequestURI().substring(request.getContextPath().length());
331
332         if (logger.isLoggable(BasicLevel.DEBUG)) {
333             logger.log(BasicLevel.DEBUG, realmName + "User Pattern = " + userPattern);
334         }
335
336         String JavaDoc servletName = "";
337         String JavaDoc[] patterns = context.findServletMappings();
338         boolean foundServlet = false;
339         String JavaDoc pattern = "";
340         int i = 0;
341
342         // Try to search servlet name
343
while ((i < patterns.length) && !foundServlet) {
344             pattern = patterns[i];
345             if (logger.isLoggable(BasicLevel.DEBUG)) {
346                 logger.log(BasicLevel.DEBUG, realmName + "Pattern found = " + pattern);
347                 logger.log(BasicLevel.DEBUG, realmName + "Servlet name for pattern = " + context.findServletMapping(pattern));
348             }
349
350             // Extension pattern and ends with extension
351
if (pattern.startsWith("*.") && userPattern.endsWith(pattern.substring(1))) {
352                 foundServlet = true;
353                 continue;
354             }
355
356             // Exact Servlet name (ie pattern = /ServletName and userPattern =
357
// /ServletName)
358
if (pattern.equals(userPattern)) {
359                 foundServlet = true;
360                 continue;
361             }
362
363             i++;
364         }
365
366         if (foundServlet) {
367             servletName = context.findServletMapping(pattern);
368             // JSP case. servlet name must be empty as required by JACC
369
// specification
370
if (servletName.equals("jsp")) {
371                 servletName = "";
372             }
373             if (logger.isLoggable(BasicLevel.DEBUG)) {
374                 logger.log(BasicLevel.DEBUG, realmName + "Found servlet name = " + servletName);
375             }
376         }
377         return servletName;
378     }
379
380     /**
381      * Enforce any user data constraint required by the security constraint
382      * guarding this request URI.
383      *
384      * @return <code>true</code> if this constraint was not violated and
385      * processing should continue, or <code>false</code> if we have
386      * created a response already.
387      *
388      * @param request Request we are processing
389      * @param response Response we are creating
390      * @param constraints Security constraints being checked
391      *
392      * @exception IOException if an input/output error occurs
393      */

394     public boolean hasUserDataPermission(Request JavaDoc request, Response JavaDoc response, SecurityConstraint[] constraints)
395             throws IOException JavaDoc {
396
397         // Update request
398
lastRequestThread.set(request);
399
400         // ---- Start of copy from RealmBase class ---
401

402         // Validate the request against the user data constraint
403
if (request.getRequest().isSecure()) {
404             if (logger.isLoggable(BasicLevel.DEBUG)) {
405                 logger.log(BasicLevel.DEBUG, realmName + "User data constraint already satisfied");
406             }
407             return true;
408         }
409
410         // Which user principal have we already authenticated?
411
Principal JavaDoc principal = ((HttpServletRequest JavaDoc) request).getUserPrincipal();
412
413         // ---- End of copy from RealmBase class ---
414

415         String JavaDoc[] roles = null;
416         String JavaDoc principalName = null;
417         if (principal instanceof GenericPrincipal) {
418             roles = ((GenericPrincipal) principal).getRoles();
419             principalName = principal.getName();
420         }
421
422         // ---- Start of copy from RealmBase class ---
423
for (int i = 0; i < constraints.length; i++) {
424             SecurityConstraint constraint = constraints[i];
425             // Use redirect only if it the transport protocol is integral or
426
// confidential
427
String JavaDoc userConstraint = constraint.getUserConstraint();
428
429             // Redirect only if the constraint is INTEGRAL or CONFIDENTIAL !
430
if (userConstraint != null
431                     && (userConstraint.equals(Constants.INTEGRAL_TRANSPORT) || userConstraint
432                             .equals(Constants.CONFIDENTIAL_TRANSPORT))) {
433                 // Initialize variables we need to determine the appropriate action
434
int redirectPort = request.getConnector().getRedirectPort();
435
436                 // Is redirecting disabled?
437
if (redirectPort <= 0) {
438                     if (logger.isLoggable(BasicLevel.DEBUG)) {
439                         logger.log(BasicLevel.DEBUG, realmName + "SSL redirect is disabled");
440                     }
441                     response.sendError(HttpServletResponse.SC_FORBIDDEN, request.getRequestURI());
442                     return false;
443                 }
444
445                 // Redirect to the corresponding SSL port
446
StringBuffer JavaDoc file = new StringBuffer JavaDoc();
447                 String JavaDoc protocol = "https";
448                 String JavaDoc host = request.getServerName();
449                 // Protocol
450
file.append(protocol).append("://");
451                 // Host with port
452
file.append(host).append(":").append(redirectPort);
453                 // URI
454
file.append(request.getRequestURI());
455                 String JavaDoc requestedSessionId = request.getRequestedSessionId();
456                 if ((requestedSessionId != null) && request.isRequestedSessionIdFromURL()) {
457                     file.append(";jsessionid=");
458                     file.append(requestedSessionId);
459                 }
460                 String JavaDoc queryString = request.getQueryString();
461                 if (queryString != null) {
462                     file.append('?');
463                     file.append(queryString);
464                 }
465
466                 if (logger.isLoggable(BasicLevel.DEBUG)) {
467                     logger.log(BasicLevel.DEBUG, realmName + "Redirecting to " + file.toString());
468                 }
469                 response.sendRedirect(file.toString());
470
471                 return false;
472             }
473         }
474
475         // ---- End of copy from RealmBase ---
476
if (permissionManager == null) {
477             logger.log(BasicLevel.ERROR, realmName + "No permission manager is set. It means that you are using this realm without using the JOnAS deployer but only Tomcat.");
478             return false;
479         }
480
481         // If Transport protocol is NONE :
482
boolean hasUserDataPermission = permissionManager.checkWebUserDataPermission(request, principalName, roles);
483         return hasUserDataPermission;
484
485     }
486
487     /**
488      * Return the Principal associated with the specified username and
489      * credentials, if there is one; otherwise return <code>null</code>.
490      *
491      * @param username Username of the Principal to look up
492      * @param credentials Password or other credentials to use in authenticating
493      * this username
494      * @return the principal associated
495      */

496     public Principal JavaDoc authenticate(String JavaDoc username, String JavaDoc credentials) {
497
498         // No authentication can be made with a null username
499
if (username == null) {
500             if (logger.isLoggable(BasicLevel.DEBUG)) {
501                 logger.log(BasicLevel.DEBUG, realmName + "No username so no authentication");
502             }
503             return null;
504         }
505
506         // Does a user with this username exist?
507
User user = null;
508         try {
509             user = jResource.findUser(username);
510         } catch (Exception JavaDoc jre) {
511             // could not retrieve user
512
logger.log(BasicLevel.ERROR, realmName + "Can not find the user : " + jre.getMessage());
513             return null;
514         }
515
516         // User was not found
517
if (user == null) {
518             if (logger.isLoggable(BasicLevel.DEBUG)) {
519                 logger.log(BasicLevel.DEBUG, realmName + "User " + username + " not found.");
520             }
521             return null;
522         }
523
524         boolean validated = jResource.isValidUser(user, credentials);
525         if (!validated) {
526             logger.log(BasicLevel.ERROR, realmName + "The password for the user " + username + " is not valid");
527             return null;
528         }
529
530         ArrayList JavaDoc combinedRoles = null;
531         try {
532             combinedRoles = jResource.getArrayListCombinedRoles(user);
533         } catch (JResourceException jre) {
534             logger.log(BasicLevel.ERROR, realmName + jre.getMessage(), jre);
535             return null;
536         }
537
538         GenericPrincipal principal = new GenericPrincipal(this, user.getName(), user.getPassword(), combinedRoles);
539         SecurityContext ctx = new SecurityContext(principal.getName(), combinedRoles);
540         SecurityCurrent current = SecurityCurrent.getCurrent();
541         current.setSecurityContext(ctx);
542
543         return principal;
544     }
545
546     /**
547      * Return the Principal associated with the specified chain of X509 client
548      * certificates. If there is none, return <code>null</code>.
549      *
550      * @param cert Array of client certificates, with the first one in the array
551      * being the certificate of the client itself.
552      * @return the associated Principal
553      */

554     public Principal JavaDoc authenticate(X509Certificate JavaDoc[] cert) {
555         String JavaDoc dn = cert[0].getSubjectDN().getName();
556         return authenticate(dn, "tomcat");
557     }
558
559     /**
560      * Return a short name for this Realm implementation.
561      *
562      * @return the name
563      */

564     protected String JavaDoc getName() {
565         return NAME;
566     }
567
568     /**
569      * Return the password associated with the given principal's user name.
570      *
571      * @param username the given principal's user name.
572      * @return the password associated.
573      */

574     protected String JavaDoc getPassword(String JavaDoc username) {
575         return null;
576     }
577
578     /**
579      * Return the Principal associated with the given user name.
580      *
581      * @param username the given principal's user name.
582      * @return the Principal associated
583      */

584     protected Principal JavaDoc getPrincipal(String JavaDoc username) {
585         return null;
586     }
587
588     /**
589      * Set the context of this Realm. This is used to retrieve xml information
590      * of the web.xml file
591      *
592      * @param context Context for this realm
593      */

594     public void setContext(Context context) {
595         this.context = context;
596         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
597         sb.append("[");
598         sb.append(NAME);
599         sb.append(":");
600         sb.append(resourceName);
601         sb.append(":");
602         if (context != null) {
603             sb.append(context.getName());
604         }
605         sb.append("] ");
606         this.realmName = sb.toString();
607     }
608
609     /**
610      * Prepare for active use of the public methods of this Component.
611      *
612      * @exception LifecycleException if this component detects a fatal error
613      * that prevents it from being started
614      */

615     public synchronized void start() throws LifecycleException {
616
617         if (logger == null) {
618             logger = Log.getLogger(Log.JONAS_SECURITY_PREFIX);
619         }
620
621         // Get the Security Service
622
try {
623             securityService = (SecurityService) ServiceManager.getInstance().getSecurityService();
624         } catch (Exception JavaDoc e) {
625             // Can't retrieve Security service
626
throw new LifecycleException("can't retrieve Security service");
627         }
628
629         // Get the resource from the security service
630
jResource = securityService.getJResource(resourceName);
631         if (jResource == null) {
632             throw new LifecycleException("Can't retrieve resource '" + resourceName + "' from the security service");
633         }
634
635         // Perform normal superclass initialization
636
super.start();
637
638     }
639
640     /**
641      * Gracefully shut down active use of the public methods of this Component.
642      *
643      * @exception LifecycleException if this component detects a fatal error
644      * that needs to be reported
645      */

646     public synchronized void stop() throws LifecycleException {
647         // Perform normal superclass finalization
648
super.stop();
649
650         // Release reference to our resource
651
jResource = null;
652     }
653
654     /**
655      * Log a message on the Logger associated with our Container (if any)
656      *
657      * @param message Message to be logged
658      */

659     protected void log(String JavaDoc message) {
660         if (logger.isLoggable(BasicLevel.DEBUG)) {
661             logger.log(BasicLevel.DEBUG, message);
662         }
663     }
664
665     /**
666      * Creates and returns a copy of this object.
667      *
668      * @return copy of this object.
669      * @throws CloneNotSupportedException if the copy fails
670      */

671     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
672         JACC jRealm = new JACC();
673         jRealm.setResourceName(resourceName);
674         return jRealm;
675     }
676
677     /**
678      * @return the permission manager used by this realm.
679      */

680     public PermissionManager getPermissionManager() {
681         return permissionManager;
682     }
683
684
685 }
Popular Tags