001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JAAS.java 10642 2007-06-14 14:35:14Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.security.realm.web.catalina55;
025:
026: import java.security.Principal;
027: import java.security.acl.Group;
028: import java.security.cert.X509Certificate;
029: import java.util.ArrayList;
030: import java.util.Enumeration;
031: import java.util.Iterator;
032:
033: import javax.security.auth.Subject;
034: import javax.security.auth.login.AccountExpiredException;
035: import javax.security.auth.login.CredentialExpiredException;
036: import javax.security.auth.login.FailedLoginException;
037: import javax.security.auth.login.LoginContext;
038: import javax.security.auth.login.LoginException;
039:
040: import org.objectweb.util.monolog.api.BasicLevel;
041: import org.objectweb.util.monolog.api.Logger;
042:
043: import org.objectweb.jonas.common.Log;
044: import org.objectweb.jonas.security.auth.callback.NoInputCallbackHandler;
045:
046: import org.apache.catalina.LifecycleException;
047: import org.apache.catalina.realm.GenericPrincipal;
048: import org.apache.catalina.realm.RealmBase;
049:
050: import org.objectweb.security.context.SecurityContext;
051: import org.objectweb.security.context.SecurityCurrent;
052:
053: /**
054: * <p>Implementation of a JAAS Realm. (by a wrapper)
055: * It uses the entry JAAS_CONFIG_NAME from the JAAS config file
056: * @author Florent Benoit
057: * @author Alexandre Thaveau (JAAS support with JOnAS)
058: * @author Marc-Antoine Bourgeot (JAAS support with JOnAS)
059: */
060: public class JAAS extends RealmBase {
061:
062: /**
063: * Descriptive information about this Realm implementation.
064: */
065: private static final String NAME = "JRealmJAASCatalina50";
066:
067: /**
068: * Descriptive information about this Realm implementation.
069: */
070: private static final String INFO = "org.objectweb.jonas.security.realm.JRealmJAASCatalina50/1.0";
071:
072: /**
073: * Name used in the JAAS config file
074: */
075: private static final String JAAS_CONFIG_NAME = "tomcat";
076:
077: /**
078: * The logger used in JOnAS
079: */
080: private static Logger logger = null;
081:
082: /**
083: * User Certificate
084: */
085: private X509Certificate cert = null;
086:
087: /**
088: * User will be transformed in uppercase for the authentication
089: */
090: private String useUpperCaseUser = null;
091:
092: /**
093: * Return descriptive information about this Realm implementation and
094: * the corresponding version number, in the format
095: * <code><description>/<version></code>.
096: * @return the info.
097: */
098: public String getInfo() {
099: return INFO;
100: }
101:
102: /**
103: * Return the Principal associated with the specified X.509 certificate Distinguished Name
104: * if there is one; otherwise return <code>null</code>.
105: * @param cert X.509 Certificate of the user
106: * @return the principal associated
107: */
108: public Principal authenticate(X509Certificate[] cert) {
109: String headerCertificate = "##DN##";
110: //concat the header certificate and replace the caracters
111: String dn = headerCertificate.concat(cert[0].getSubjectDN()
112: .getName().replace('=', '#').replace(',', '%').replace(
113: ' ', '$'));
114: this .cert = cert[0];
115: return authenticate(dn, "");
116: }
117:
118: /**
119: * Return the Principal associated with the specified username and
120: * credentials, if there is one; otherwise return <code>null</code>.
121: *
122: * @param username Username of the Principal to look up
123: * @param credentials Password or other credentials to use in
124: * authenticating this username
125: * @return the principal associated
126: */
127: public Principal authenticate(String username, String credentials) {
128:
129: // Transform username in uppercase if option is enabled
130: if (useUpperCaseUser != null && useUpperCaseUser.equals("true")) {
131: username = username.toUpperCase();
132: }
133:
134: // No authentication can be made with a null username
135: if (username == null) {
136: log("No username so no authentication");
137: return null;
138: }
139: // Establish a LoginContext to use for authentication
140: LoginContext loginContext = null;
141: try {
142: loginContext = new LoginContext(JAAS_CONFIG_NAME,
143: new NoInputCallbackHandler(username, credentials,
144: this .cert));
145: } catch (LoginException e) {
146: logger.log(BasicLevel.ERROR, "loginException for user '"
147: + username + "'", e);
148: return null;
149: }
150: // Negotiate a login via this LoginContext
151: Subject subject = null;
152: try {
153: loginContext.login();
154: subject = loginContext.getSubject();
155: if (subject == null) {
156: if (logger.isLoggable(BasicLevel.ERROR)) {
157: logger.log(BasicLevel.ERROR,
158: "failedLoginlogin for user :" + username);
159: }
160: return null;
161: }
162: } catch (AccountExpiredException e) {
163: if (logger.isLoggable(BasicLevel.ERROR)) {
164: logger.log(BasicLevel.ERROR,
165: "accountExpired for user :" + username, e);
166: }
167: return null;
168: } catch (CredentialExpiredException e) {
169: if (logger.isLoggable(BasicLevel.ERROR)) {
170: logger.log(BasicLevel.ERROR,
171: "credentialExpired for user :" + username, e);
172: }
173: return null;
174: } catch (FailedLoginException e) {
175: if (logger.isLoggable(BasicLevel.ERROR)) {
176: logger.log(BasicLevel.ERROR, "failedLogin for user :"
177: + username, e);
178: }
179: return null;
180: } catch (LoginException e) {
181: if (logger.isLoggable(BasicLevel.ERROR)) {
182: logger.log(BasicLevel.ERROR,
183: "loginException for user :" + username, e);
184: }
185: return null;
186: }
187:
188: // Get credentials iterators from the subject
189: Iterator credentialsIterator = subject.getPrivateCredentials()
190: .iterator();
191: String credential = (String) credentialsIterator.next();
192:
193: // Retrieve first principal name found (without groups)
194: Iterator iterator = subject.getPrincipals(Principal.class)
195: .iterator();
196: String userName = null;
197: while (iterator.hasNext() && (userName == null)) {
198: Principal principal = (Principal) iterator.next();
199: if (!(principal instanceof Group)) {
200: userName = principal.getName();
201: }
202: }
203:
204: // No name --> error
205: if (userName == null) {
206: logger.log(BasicLevel.ERROR,
207: "No Username found in the subject");
208: return null;
209: }
210:
211: // Retrieve all roles of the user (Roles are members of the Group.class)
212: iterator = subject.getPrincipals(Group.class).iterator();
213: ArrayList roles = new ArrayList();
214: while (iterator.hasNext()) {
215: Group group = (Group) iterator.next();
216: Enumeration e = group.members();
217: while (e.hasMoreElements()) {
218: Principal p = (Principal) e.nextElement();
219: roles.add(p.getName());
220: }
221: }
222:
223: GenericPrincipal principal = new GenericPrincipal(this ,
224: userName, credential, roles);
225: //instanciation of the security context
226: SecurityContext ctx = new SecurityContext(userName, roles);
227: SecurityCurrent current = SecurityCurrent.getCurrent();
228: current.setSecurityContext(ctx);
229:
230: return principal;
231: }
232:
233: /**
234: * Return a short name for this Realm implementation.
235: * @return the name
236: */
237: protected String getName() {
238: return NAME;
239: }
240:
241: /**
242: * Return the password associated with the given principal's user name.
243: * @param username the given principal's user name.
244: * @return the password associated.
245: */
246: protected String getPassword(String username) {
247: return null;
248: }
249:
250: /**
251: * Return the Principal associated with the given user name.
252: * @param username the given principal's user name.
253: * @return the Principal associated
254: */
255: protected Principal getPrincipal(String username) {
256: return null;
257: }
258:
259: /**
260: * Prepare for active use of the public methods of this Component.
261: *
262: * @exception LifecycleException if this component detects a fatal error
263: * that prevents it from being started
264: */
265: public synchronized void start() throws LifecycleException {
266:
267: if (logger == null) {
268: logger = Log.getLogger(Log.JONAS_SECURITY_PREFIX);
269: }
270:
271: // Perform normal superclass initialization
272: super .start();
273:
274: }
275:
276: /**
277: * Gracefully shut down active use of the public methods of this Component.
278: *
279: * @exception LifecycleException if this component detects a fatal error
280: * that needs to be reported
281: */
282: public synchronized void stop() throws LifecycleException {
283: // Perform normal superclass finalization
284: super .stop();
285: }
286:
287: /**
288: * Log a message on the Logger associated with our Container (if any)
289: *
290: * @param message Message to be logged
291: */
292: protected void log(String message) {
293: if (logger.isLoggable(BasicLevel.DEBUG)) {
294: logger.log(BasicLevel.DEBUG, message);
295: }
296: }
297:
298: /**
299: * Redirect to setUseUpperCaseUserName() method.
300: * @param useUpperCaseUser boolean value : "true" or "false"
301: */
302: public void setUseUpperCaseUser(String useUpperCaseUser) {
303: setUseUpperCaseUserName(useUpperCaseUser);
304: }
305:
306: /**
307: * Authentication will be made with a username in uppercase
308: * @param useUpperCaseUser boolean value : "true" or "false"
309: */
310: public void setUseUpperCaseUserName(String useUpperCaseUser) {
311: this .useUpperCaseUser = useUpperCaseUser;
312: if (logger.isLoggable(BasicLevel.DEBUG)) {
313: logger.log(BasicLevel.DEBUG,
314: "Option setUseUpperCaseUserName set to "
315: + useUpperCaseUser);
316: }
317: }
318:
319: }
|