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