001    /**
002     *
003     * Copyright 2003-2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  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: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $
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 = new LoginContext(appName, new JAASCallbackHandler(this, username, credentials));
092                } catch (Throwable e) {
093                    log.error(sm.getString("jaasRealm.unexpectedError"), e);
094                    return (null);
095                } finally {
096                    if (isUseContextClassLoader()) {
097                        Thread.currentThread().setContextClassLoader(ocl);
098                    }
099                }
100    
101                if (log.isDebugEnabled())
102                    log.debug("Login context created " + username);
103    
104                // Negotiate a login via this LoginContext
105                Subject subject = null;
106                try {
107                    loginContext.login();
108                    Subject tempSubject = loginContext.getSubject();
109                    if (tempSubject == null) {
110                        if (log.isDebugEnabled())
111                            log.debug(sm.getString("jaasRealm.failedLogin", username));
112                        return (null);
113                    }
114    
115                    subject = ContextManager.getServerSideSubject(tempSubject);
116                    if (subject == null) {
117                        if (log.isDebugEnabled())
118                            log.debug(sm.getString("jaasRealm.failedLogin", username));
119                        return (null);
120                    }
121    
122                } catch (AccountExpiredException e) {
123                    if (log.isDebugEnabled())
124                        log.debug(sm.getString("jaasRealm.accountExpired", username));
125                    return (null);
126                } catch (CredentialExpiredException e) {
127                    if (log.isDebugEnabled())
128                        log.debug(sm.getString("jaasRealm.credentialExpired", username));
129                    return (null);
130                } catch (FailedLoginException e) {
131                    if (log.isDebugEnabled())
132                        log.debug(sm.getString("jaasRealm.failedLogin", username));
133                    return (null);
134                } catch (LoginException e) {
135                    log.warn(sm.getString("jaasRealm.loginException", username), e);
136                    return (null);
137                } catch (Throwable e) {
138                    log.error(sm.getString("jaasRealm.unexpectedError"), e);
139                    return (null);
140                }
141    
142                if (log.isDebugEnabled())
143                    log.debug(sm.getString("jaasRealm.loginContextCreated", username));
144    
145                // Return the appropriate Principal for this authenticated Subject
146                Principal principal = createPrincipal(username, subject);
147                if (principal == null) {
148                    log.debug(sm.getString("jaasRealm.authenticateFailure", username));
149                    return (null);
150                }
151                if (log.isDebugEnabled()) {
152                    log.debug(sm.getString("jaasRealm.authenticateSuccess", username));
153                }
154    
155                return (principal);
156    
157            } catch (Throwable t) {
158                log.error("error ", t);
159                return null;
160            }
161        }
162    
163        protected Principal createPrincipal(String username, Subject subject) {
164            // Prepare to scan the Principals for this Subject
165            String password = null; // Will not be carried forward
166    
167            List roles = new ArrayList();
168            Principal userPrincipal = null;
169    
170            // Scan the Principals for this Subject
171            Iterator principals = subject.getPrincipals().iterator();
172            while (principals.hasNext()) {
173                Principal principal = (Principal) principals.next();
174    
175                String principalClass = principal.getClass().getName();
176    
177                if( log.isDebugEnabled() ) {
178                    log.debug(sm.getString("jaasRealm.checkPrincipal", principal, principalClass));
179                }
180    
181                if (userPrincipal == null && userClasses.contains(principalClass)) {
182                    userPrincipal = principal;
183                    if( log.isDebugEnabled() ) {
184                        log.debug(sm.getString("jaasRealm.userPrincipalSuccess", principal.getName()));
185                    }
186                }
187    
188                if (roleClasses.contains(principalClass)) {
189                    roles.add(principal.getName());
190                    if( log.isDebugEnabled() ) {
191                        log.debug(sm.getString("jaasRealm.rolePrincipalAdd", principal.getName()));
192                    }
193                }
194            }
195    
196            // Print failure message if needed
197            if (userPrincipal == null) {
198                if (log.isDebugEnabled()) {
199                    log.debug(sm.getString("jaasRealm.userPrincipalFailure"));
200                    log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
201                }
202            } else {
203                if (roles.size() == 0) {
204                    if (log.isDebugEnabled()) {
205                        log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
206                    }
207                }
208            }
209    
210            JAASTomcatPrincipal jaasPrincipal = new JAASTomcatPrincipal(username);
211            jaasPrincipal.setSubject(subject);
212            jaasPrincipal.setRoles(roles);
213    
214            // Return the resulting Principal for our authenticated user
215            return jaasPrincipal;
216        }
217    
218    
219        public Object clone() throws CloneNotSupportedException{
220            return super.clone();
221        }
222    
223    
224        public boolean hasRole(Principal principal, String role) {
225    
226            if ((principal == null) || (role == null) ||
227                !(principal instanceof JAASTomcatPrincipal))
228                return (false);
229    
230            JAASTomcatPrincipal jtp = (JAASTomcatPrincipal) principal;
231            if (jtp.getRoles().contains(role))
232                return true;
233    
234            return false;
235        }
236    
237    }