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.activemq.security;
018    
019    import java.security.Principal;
020    import java.util.Iterator;
021    import java.util.Set;
022    import java.util.concurrent.CopyOnWriteArrayList;
023    
024    import javax.security.auth.Subject;
025    import javax.security.auth.login.LoginContext;
026    
027    import org.apache.activemq.broker.Broker;
028    import org.apache.activemq.broker.BrokerFilter;
029    import org.apache.activemq.broker.ConnectionContext;
030    import org.apache.activemq.command.ConnectionInfo;
031    import org.apache.activemq.jaas.JassCredentialCallbackHandler;
032    
033    /**
034     * Logs a user in using JAAS.
035     * 
036     * 
037     */
038    public class JaasAuthenticationBroker extends BrokerFilter {
039    
040        private final String jassConfiguration;
041        private final CopyOnWriteArrayList<SecurityContext> securityContexts = new CopyOnWriteArrayList<SecurityContext>();
042    
043        public JaasAuthenticationBroker(Broker next, String jassConfiguration) {
044            super(next);
045            this.jassConfiguration = jassConfiguration;
046        }
047    
048        static class JaasSecurityContext extends SecurityContext {
049    
050            private final Subject subject;
051    
052            public JaasSecurityContext(String userName, Subject subject) {
053                super(userName);
054                this.subject = subject;
055            }
056    
057            public Set<Principal> getPrincipals() {
058                return subject.getPrincipals();
059            }
060    
061        }
062    
063        public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
064    
065            if (context.getSecurityContext() == null) {
066                // Set the TCCL since it seems JAAS needs it to find the login
067                // module classes.
068                ClassLoader original = Thread.currentThread().getContextClassLoader();
069                Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader());
070                try {
071                    // Do the login.
072                    try {
073                        JassCredentialCallbackHandler callback = new JassCredentialCallbackHandler(info
074                            .getUserName(), info.getPassword());
075                        LoginContext lc = new LoginContext(jassConfiguration, callback);
076                        lc.login();
077                        Subject subject = lc.getSubject();
078    
079                        SecurityContext s = new JaasSecurityContext(info.getUserName(), subject);
080                        context.setSecurityContext(s);
081                        securityContexts.add(s);
082                    } catch (Exception e) {
083                        throw (SecurityException)new SecurityException("User name [" + info.getUserName() + "] or password is invalid.")
084                            .initCause(e);
085                    }
086                } finally {
087                    Thread.currentThread().setContextClassLoader(original);
088                }
089            }
090            super.addConnection(context, info);
091        }
092    
093        public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error)
094            throws Exception {
095            super.removeConnection(context, info, error);
096            if (securityContexts.remove(context.getSecurityContext())) {
097                context.setSecurityContext(null);
098            }
099        }
100    
101        /**
102         * Previously logged in users may no longer have the same access anymore.
103         * Refresh all the logged into users.
104         */
105        public void refresh() {
106            for (Iterator<SecurityContext> iter = securityContexts.iterator(); iter.hasNext();) {
107                SecurityContext sc = iter.next();
108                sc.getAuthorizedReadDests().clear();
109                sc.getAuthorizedWriteDests().clear();
110            }
111        }
112    }