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.util.Set;
020    import org.apache.activemq.broker.Broker;
021    import org.apache.activemq.broker.BrokerFilter;
022    import org.apache.activemq.broker.ConnectionContext;
023    import org.apache.activemq.broker.ProducerBrokerExchange;
024    import org.apache.activemq.broker.region.Destination;
025    import org.apache.activemq.broker.region.Subscription;
026    import org.apache.activemq.command.ActiveMQDestination;
027    import org.apache.activemq.command.ActiveMQQueue;
028    import org.apache.activemq.command.ActiveMQTopic;
029    import org.apache.activemq.command.ConsumerInfo;
030    import org.apache.activemq.command.DestinationInfo;
031    import org.apache.activemq.command.Message;
032    import org.apache.activemq.command.ProducerInfo;
033    
034    /**
035     * Verifies if a authenticated user can do an operation against the broker using
036     * an authorization map.
037     * 
038     * 
039     */
040    public class AuthorizationBroker extends BrokerFilter implements SecurityAdminMBean {
041    
042        private final AuthorizationMap authorizationMap;
043    
044        public AuthorizationBroker(Broker next, AuthorizationMap authorizationMap) {
045            super(next);
046            this.authorizationMap = authorizationMap;
047        }
048    
049        protected SecurityContext checkSecurityContext(ConnectionContext context) throws SecurityException {
050            final SecurityContext securityContext = context.getSecurityContext();
051            if (securityContext == null) {
052                throw new SecurityException("User is not authenticated.");
053            }
054            return securityContext;
055        }
056    
057        protected boolean checkDestinationAdmin(SecurityContext securityContext, ActiveMQDestination destination) {
058            Destination existing = this.getDestinationMap().get(destination);
059            if (existing != null) {
060                return true;
061            }
062    
063            if (!securityContext.isBrokerContext()) {
064                Set<?> allowedACLs = null;
065                if (!destination.isTemporary()) {
066                    allowedACLs = authorizationMap.getAdminACLs(destination);
067                } else {
068                    allowedACLs = authorizationMap.getTempDestinationAdminACLs();
069                }
070    
071                if (allowedACLs != null && !securityContext.isInOneOf(allowedACLs)) {
072                    return false;
073                }
074            }
075            return true;
076        }
077               
078        @Override
079        public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
080            final SecurityContext securityContext = checkSecurityContext(context);
081    
082            if (!checkDestinationAdmin(securityContext, info.getDestination())) {
083                throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to create: " + info.getDestination());
084            }
085    
086            super.addDestinationInfo(context, info);
087        }
088    
089        @Override
090        public Destination addDestination(ConnectionContext context, ActiveMQDestination destination,boolean create) throws Exception {
091            final SecurityContext securityContext = checkSecurityContext(context);
092            
093            if (!checkDestinationAdmin(securityContext, destination)) {
094                throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to create: " + destination);
095            }
096    
097            return super.addDestination(context, destination,create);
098        }
099    
100        @Override
101        public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Exception {
102            final SecurityContext securityContext = checkSecurityContext(context);
103    
104            if (!checkDestinationAdmin(securityContext, destination)) {
105                throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to remove: " + destination);
106            }
107    
108            super.removeDestination(context, destination, timeout);
109        }
110    
111        @Override
112        public void removeDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
113            final SecurityContext securityContext = checkSecurityContext(context);
114    
115            if (!checkDestinationAdmin(securityContext, info.getDestination())) {
116                throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to remove: " + info.getDestination());
117            }
118    
119            super.removeDestinationInfo(context, info);
120        }
121    
122        @Override
123        public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
124            final SecurityContext securityContext = checkSecurityContext(context);
125    
126            Set<?> allowedACLs = null;
127            if (!info.getDestination().isTemporary()) {
128                allowedACLs = authorizationMap.getReadACLs(info.getDestination());
129            } else {
130                allowedACLs = authorizationMap.getTempDestinationReadACLs();
131            }
132    
133            if (!securityContext.isBrokerContext() && allowedACLs != null && !securityContext.isInOneOf(allowedACLs)) {
134                throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to read from: " + info.getDestination());
135            }
136            securityContext.getAuthorizedReadDests().put(info.getDestination(), info.getDestination());
137    
138            /*
139             * Need to think about this a little more. We could do per message
140             * security checking to implement finer grained security checking. For
141             * example a user can only see messages with price>1000 . Perhaps this
142             * should just be another additional broker filter that installs this
143             * type of feature. If we did want to do that, then we would install a
144             * predicate. We should be careful since there may be an existing
145             * predicate already assigned and the consumer info may be sent to a
146             * remote broker, so it also needs to support being marshaled.
147             * info.setAdditionalPredicate(new BooleanExpression() { public boolean
148             * matches(MessageEvaluationContext message) throws JMSException { if(
149             * !subject.getAuthorizedReadDests().contains(message.getDestination()) ) {
150             * Set allowedACLs =
151             * authorizationMap.getReadACLs(message.getDestination());
152             * if(allowedACLs!=null && !subject.isInOneOf(allowedACLs)) return
153             * false; subject.getAuthorizedReadDests().put(message.getDestination(),
154             * message.getDestination()); } return true; } public Object
155             * evaluate(MessageEvaluationContext message) throws JMSException {
156             * return matches(message) ? Boolean.TRUE : Boolean.FALSE; } });
157             */
158    
159            return super.addConsumer(context, info);
160        }
161    
162        @Override
163        public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception {
164            final SecurityContext securityContext = checkSecurityContext(context);
165    
166            if (!securityContext.isBrokerContext() && info.getDestination() != null) {
167    
168                Set<?> allowedACLs = null;
169                if (!info.getDestination().isTemporary()) {
170                    allowedACLs = authorizationMap.getWriteACLs(info.getDestination());
171                } else {
172                    allowedACLs = authorizationMap.getTempDestinationWriteACLs();
173                }
174                if (allowedACLs != null && !securityContext.isInOneOf(allowedACLs)) {
175                    throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to write to: " + info.getDestination());
176                }
177                securityContext.getAuthorizedWriteDests().put(info.getDestination(), info.getDestination());
178            }
179    
180            super.addProducer(context, info);
181        }
182    
183        @Override
184        public void send(ProducerBrokerExchange producerExchange, Message messageSend) throws Exception {
185            final SecurityContext securityContext = checkSecurityContext(producerExchange.getConnectionContext());
186    
187            if (!securityContext.isBrokerContext() && !securityContext.getAuthorizedWriteDests().contains(messageSend.getDestination())) {
188    
189                Set<?> allowedACLs = null;
190                if (!messageSend.getDestination().isTemporary()) {
191                    allowedACLs = authorizationMap.getWriteACLs(messageSend.getDestination());
192                } else {
193                    allowedACLs = authorizationMap.getTempDestinationWriteACLs();
194                }
195    
196                if (allowedACLs != null && !securityContext.isInOneOf(allowedACLs)) {
197                    throw new SecurityException("User " + securityContext.getUserName() + " is not authorized to write to: " + messageSend.getDestination());
198                }
199                securityContext.getAuthorizedWriteDests().put(messageSend.getDestination(), messageSend.getDestination());
200            }
201    
202            super.send(producerExchange, messageSend);
203        }
204    
205        // SecurityAdminMBean interface
206        // -------------------------------------------------------------------------
207    
208        public void addQueueRole(String queue, String operation, String role) {
209            addDestinationRole(new ActiveMQQueue(queue), operation, role);
210        }
211    
212        public void addTopicRole(String topic, String operation, String role) {
213            addDestinationRole(new ActiveMQTopic(topic), operation, role);
214        }
215    
216        public void removeQueueRole(String queue, String operation, String role) {
217            removeDestinationRole(new ActiveMQQueue(queue), operation, role);
218        }
219    
220        public void removeTopicRole(String topic, String operation, String role) {
221            removeDestinationRole(new ActiveMQTopic(topic), operation, role);
222        }
223    
224        public void addDestinationRole(javax.jms.Destination destination, String operation, String role) {
225        }
226    
227        public void removeDestinationRole(javax.jms.Destination destination, String operation, String role) {
228        }
229    
230        public void addRole(String role) {
231        }
232    
233        public void addUserRole(String user, String role) {
234        }
235    
236        public void removeRole(String role) {
237        }
238    
239        public void removeUserRole(String user, String role) {
240        }
241    
242    }