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