001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.tomcat.realm;
018    
019    import org.apache.catalina.realm.JAASCallbackHandler;
020    import org.apache.catalina.realm.JAASRealm;
021    import org.apache.commons.logging.Log;
022    import org.apache.commons.logging.LogFactory;
023    import org.apache.geronimo.security.ContextManager;
024    import org.apache.geronimo.tomcat.JAASTomcatPrincipal;
025    
026    import javax.security.auth.Subject;
027    import javax.security.auth.login.*;
028    import java.security.Principal;
029    import java.util.ArrayList;
030    import java.util.Iterator;
031    import java.util.List;
032    
033    
034    /**
035     * @version $Rev: 565912 $ $Date: 2007-08-14 17:03:11 -0400 (Tue, 14 Aug 2007) $
036     */
037    public class TomcatJAASRealm extends JAASRealm implements Cloneable {
038        private static final Log log = LogFactory.getLog(TomcatJAASRealm.class);
039    
040        private static final String DEFAULT_NAME = "tomcat";
041    
042        /**
043         * Descriptive information about this <code>Realm</code> implementation.
044         */
045        protected static final String info = "org.apache.geronimo.tomcat.realm.TomcatJAASRealm/1.0";
046    
047        /**
048         * Descriptive information about this <code>Realm</code> implementation.
049         */
050        protected static final String name = "TomcatJAASRealm";
051    
052        public TomcatJAASRealm() {
053            super();
054        }
055    
056    
057        /**
058         * Return the <code>Principal</code> associated with the specified
059         * username and credentials, if there is one; otherwise return
060         * <code>null</code>.
061         * <p/>
062         * If there are any errors with the JDBC connection, executing the query or
063         * anything we return null (don't authenticate). This event is also logged,
064         * and the connection will be closed so that a subsequent request will
065         * automatically re-open it.
066         *
067         * @param username    Username of the <code>Principal</code> to look up
068         * @param credentials Password or other credentials to use in authenticating this
069         *                    username
070         */
071        public Principal authenticate(String username, String credentials) {
072    
073            // Establish a LoginContext to use for authentication
074            try {
075                LoginContext loginContext = null;
076                if (appName == null)
077                    appName = DEFAULT_NAME;
078    
079                if (log.isDebugEnabled())
080                    log.debug(sm.getString("jaasRealm.beginLogin", username, appName));
081    
082                // What if the LoginModule is in the container class loader ?
083                ClassLoader ocl = null;
084    
085                if (isUseContextClassLoader()) {
086                    ocl = Thread.currentThread().getContextClassLoader();
087                    Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
088                }
089    
090                try {
091                    loginContext = ContextManager.login(appName, new JAASCallbackHandler(this, username, credentials));
092                } catch (AccountExpiredException e) {
093                    if (log.isDebugEnabled())
094                        log.debug(sm.getString("jaasRealm.accountExpired", username));
095                    return (null);
096                } catch (CredentialExpiredException e) {
097                    if (log.isDebugEnabled())
098                        log.debug(sm.getString("jaasRealm.credentialExpired", username));
099                    return (null);
100                } catch (FailedLoginException e) {
101                    if (log.isDebugEnabled())
102                        log.debug(sm.getString("jaasRealm.failedLogin", username));
103                    return (null);
104                } catch (LoginException e) {
105                    log.warn(sm.getString("jaasRealm.loginException", username), e);
106                    return (null);
107                } catch (Throwable e) {
108                    log.error(sm.getString("jaasRealm.unexpectedError"), e);
109                    return (null);
110                } finally {
111                    if (isUseContextClassLoader()) {
112                        Thread.currentThread().setContextClassLoader(ocl);
113                    }
114                }
115    
116                if (log.isDebugEnabled())
117                    log.debug("Login context created " + username);
118    
119                // Negotiate a login via this LoginContext
120                Subject subject = loginContext.getSubject();
121                ContextManager.setCallers(subject, subject);
122                if (log.isDebugEnabled())
123                    log.debug(sm.getString("jaasRealm.loginContextCreated", username));
124    
125                // Return the appropriate Principal for this authenticated Subject
126                Principal principal = createPrincipal(username, subject);
127                if (principal == null) {
128                    log.debug(sm.getString("jaasRealm.authenticateFailure", username));
129                    return (null);
130                }
131                if (log.isDebugEnabled()) {
132                    log.debug(sm.getString("jaasRealm.authenticateSuccess", username));
133                }
134    
135                return (principal);
136    
137            } catch (Throwable t) {
138                log.error("error ", t);
139                return null;
140            }
141        }
142    
143        protected Principal createPrincipal(String username, Subject subject) {
144            // Prepare to scan the Principals for this Subject
145            //String password = null; // Will not be carried forward
146    
147            List roles = new ArrayList();
148            Principal userPrincipal = null;
149    
150            // Scan the Principals for this Subject
151            Iterator principals = subject.getPrincipals().iterator();
152            while (principals.hasNext()) {
153                Principal principal = (Principal) principals.next();
154    
155                String principalClass = principal.getClass().getName();
156    
157                if( log.isDebugEnabled() ) {
158                    log.debug(sm.getString("jaasRealm.checkPrincipal", principal, principalClass));
159                }
160    
161                if (userPrincipal == null && userClasses.contains(principalClass)) {
162                    userPrincipal = principal;
163                    if( log.isDebugEnabled() ) {
164                        log.debug(sm.getString("jaasRealm.userPrincipalSuccess", principal.getName()));
165                    }
166                }
167    
168                if (roleClasses.contains(principalClass)) {
169                    roles.add(principal.getName());
170                    if( log.isDebugEnabled() ) {
171                        log.debug(sm.getString("jaasRealm.rolePrincipalAdd", principal.getName()));
172                    }
173                }
174            }
175    
176            // Print failure message if needed
177            if (userPrincipal == null) {
178                if (log.isDebugEnabled()) {
179                    log.debug(sm.getString("jaasRealm.userPrincipalFailure"));
180                    log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
181                }
182            } else {
183                if (roles.size() == 0) {
184                    if (log.isDebugEnabled()) {
185                        log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
186                    }
187                }
188            }
189    
190            JAASTomcatPrincipal jaasPrincipal = new JAASTomcatPrincipal(username);
191            jaasPrincipal.setSubject(subject);
192            jaasPrincipal.setRoles(roles);
193    
194            // Return the resulting Principal for our authenticated user
195            return jaasPrincipal;
196        }
197    
198    
199        public Object clone() throws CloneNotSupportedException{
200            return super.clone();
201        }
202    
203    
204        public boolean hasRole(Principal principal, String role) {
205    
206            if ((principal == null) || (role == null) ||
207                !(principal instanceof JAASTomcatPrincipal))
208                return (false);
209    
210            JAASTomcatPrincipal jtp = (JAASTomcatPrincipal) principal;
211            if (jtp.getRoles().contains(role))
212                return true;
213    
214            return false;
215        }
216    
217    }