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