001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.jetty;
019    
020    import java.security.AccessControlContext;
021    import java.security.AccessControlException;
022    import java.security.Principal;
023    import java.security.cert.X509Certificate;
024    import java.util.HashMap;
025    
026    import javax.security.auth.Subject;
027    import javax.security.auth.login.LoginContext;
028    import javax.security.auth.login.LoginException;
029    import javax.security.jacc.WebRoleRefPermission;
030    
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.apache.geronimo.security.ContextManager;
034    import org.apache.geronimo.security.realm.providers.CertificateCallbackHandler;
035    import org.apache.geronimo.security.realm.providers.ClearableCallbackHandler;
036    import org.apache.geronimo.security.realm.providers.PasswordCallbackHandler;
037    import org.mortbay.http.HttpRequest;
038    
039    
040    /**
041     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
042     */
043    public class InternalJAASJettyRealm {
044        private static Log log = LogFactory.getLog(InternalJAASJettyRealm.class);
045    
046        private final String securityRealmName;
047        private final HashMap userMap = new HashMap();
048        private int count = 1;
049    
050        public InternalJAASJettyRealm(String geronimoRealmName) {
051            this.securityRealmName = geronimoRealmName;
052        }
053    
054        public String getSecurityRealmName() {
055            return securityRealmName;
056        }
057    
058        public Principal getPrincipal(String username) {
059            return (Principal) userMap.get(username);
060        }
061    
062        public Principal authenticate(String username, Object credentials, HttpRequest request) {
063            try {
064                if ((username != null) && (!username.equals(""))) {
065    
066                    JAASJettyPrincipal userPrincipal = (JAASJettyPrincipal) userMap.get(username);
067    
068                    //user has been previously authenticated, but
069                    //re-authentication has been requested, so remove them
070                    if (userPrincipal != null) {
071                        userMap.remove(username);
072                    }
073    
074                    ClearableCallbackHandler callbackHandler;
075                    if (credentials instanceof char[]) {
076                        char[] password = (char[]) credentials;
077                        callbackHandler = new PasswordCallbackHandler(username, password);
078                    } else if (credentials instanceof String) {
079                        char[] password = ((String) credentials).toCharArray();
080                        callbackHandler = new PasswordCallbackHandler(username, password);
081                    } else if (credentials instanceof X509Certificate[]) {
082                        X509Certificate[] certs = (X509Certificate[]) credentials;
083                        if (certs.length < 1) {
084                            throw new LoginException("no certificates supplied");
085                        }
086                        callbackHandler = new CertificateCallbackHandler(certs[0]);
087                    } else {
088                        throw new LoginException("Cannot extract credentials from class: " + credentials.getClass().getName());
089                    }
090    
091                    //set up the login context
092                    LoginContext loginContext = new LoginContext(securityRealmName, callbackHandler);
093                    loginContext.login();
094                    callbackHandler.clear();
095    
096                    Subject subject = ContextManager.getServerSideSubject(loginContext.getSubject());
097                    //TODO use the run-as subject as nextCaller
098                    ContextManager.setCallers(subject, subject);
099                    ContextManager.setNextCaller(subject);
100    
101                    //login success
102                    userPrincipal = new JAASJettyPrincipal(username);
103                    userPrincipal.setSubject(subject);
104    
105                    userMap.put(username, userPrincipal);
106    
107                    return userPrincipal;
108                } else {
109                    log.debug("Login Failed - null userID");
110                    return null;
111                }
112    
113            } catch (LoginException e) {
114    //          log.warn("Login Failed", e);
115                log.debug("Login Failed", e);
116                return null;
117            }
118        }
119    
120        public void logout(Principal user) {
121            JAASJettyPrincipal principal = (JAASJettyPrincipal) user;
122    
123            userMap.remove(principal.getName());
124            ContextManager.unregisterSubject(principal.getSubject());
125        }
126    
127        public boolean reauthenticate(Principal user) {
128            // TODO This is not correct if auth can expire! We need to
129    
130            Subject subject = ((JAASJettyPrincipal) user).getSubject();
131            ContextManager.setCallers(subject, subject);
132    
133            // get the user out of the cache
134            return (userMap.get(user.getName()) != null);
135        }
136    
137        public void disassociate(Principal user) {
138            // do nothing
139        }
140    
141        public boolean isUserInRole(Principal user, String role) {
142            if (user == null || role == null) {
143                return false;
144            }
145    
146            AccessControlContext acc = ContextManager.getCurrentContext();
147            try {
148                // JACC v1.0 secion B.19
149                String servletName = JettyServletHolder.getCurrentServletName();
150                if (servletName.equals("jsp")) {
151                    servletName = "";
152                }
153                acc.checkPermission(new WebRoleRefPermission(servletName, role));
154            } catch (AccessControlException e) {
155                return false;
156            }
157            return true;
158        }
159    
160        public Principal pushRole(Principal user, String role) {
161            //handled by JettyServletHolder and its runAsSubject
162            return user;
163        }
164    
165        public Principal popRole(Principal user) {
166            return user;
167        }
168    
169        public void addUse() {
170            count++;
171        }
172    
173        public int removeUse() {
174            return count--;
175        }
176    
177    }