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.lang.reflect.Constructor;
020import java.lang.reflect.Method;
021import java.security.Principal;
022import java.util.*;
023
024import org.apache.activemq.command.ActiveMQDestination;
025import org.apache.activemq.filter.DestinationMap;
026import org.apache.activemq.filter.DestinationMapEntry;
027
028/**
029 * Represents a destination based configuration of policies so that individual
030 * destinations or wildcard hierarchies of destinations can be configured using
031 * different policies. Each entry in the map represents the authorization ACLs
032 * for each operation.
033 *
034 *
035 */
036public class DefaultAuthorizationMap extends DestinationMap implements AuthorizationMap {
037
038    public static final String DEFAULT_GROUP_CLASS = "org.apache.activemq.jaas.GroupPrincipal";
039
040    private AuthorizationEntry defaultEntry;
041
042    private TempDestinationAuthorizationEntry tempDestinationAuthorizationEntry;
043
044    protected String groupClass = DEFAULT_GROUP_CLASS;
045
046    public DefaultAuthorizationMap() {
047    }
048
049    @SuppressWarnings("rawtypes")
050    public DefaultAuthorizationMap(List<DestinationMapEntry> authorizationEntries) {
051        setAuthorizationEntries(authorizationEntries);
052
053    }
054
055    public void setTempDestinationAuthorizationEntry(TempDestinationAuthorizationEntry tempDestinationAuthorizationEntry) {
056        this.tempDestinationAuthorizationEntry = tempDestinationAuthorizationEntry;
057    }
058
059    public TempDestinationAuthorizationEntry getTempDestinationAuthorizationEntry() {
060        return this.tempDestinationAuthorizationEntry;
061    }
062
063    @Override
064    public Set<Object> getTempDestinationAdminACLs() {
065        if (tempDestinationAuthorizationEntry != null) {
066            Set<Object> answer = new WildcardAwareSet<Object>();
067            answer.addAll(tempDestinationAuthorizationEntry.getAdminACLs());
068            return answer;
069        } else {
070            return null;
071        }
072    }
073
074    @Override
075    public Set<Object> getTempDestinationReadACLs() {
076        if (tempDestinationAuthorizationEntry != null) {
077            Set<Object> answer = new WildcardAwareSet<Object>();
078            answer.addAll(tempDestinationAuthorizationEntry.getReadACLs());
079            return answer;
080        } else {
081            return null;
082        }
083    }
084
085    @Override
086    public Set<Object> getTempDestinationWriteACLs() {
087        if (tempDestinationAuthorizationEntry != null) {
088            Set<Object> answer = new WildcardAwareSet<Object>();
089            answer.addAll(tempDestinationAuthorizationEntry.getWriteACLs());
090            return answer;
091        } else {
092            return null;
093        }
094    }
095
096    @Override
097    public Set<Object> getAdminACLs(ActiveMQDestination destination) {
098        Set<AuthorizationEntry> entries = getAllEntries(destination);
099        Set<Object> answer = new WildcardAwareSet<Object>();
100
101        // now lets go through each entry adding individual
102        for (Iterator<AuthorizationEntry> iter = entries.iterator(); iter.hasNext();) {
103            AuthorizationEntry entry = iter.next();
104            answer.addAll(entry.getAdminACLs());
105        }
106        return answer;
107    }
108
109    @Override
110    public Set<Object> getReadACLs(ActiveMQDestination destination) {
111        Set<AuthorizationEntry> entries = getAllEntries(destination);
112        Set<Object> answer = new WildcardAwareSet<Object>();
113
114        // now lets go through each entry adding individual
115        for (Iterator<AuthorizationEntry> iter = entries.iterator(); iter.hasNext();) {
116            AuthorizationEntry entry = iter.next();
117            answer.addAll(entry.getReadACLs());
118        }
119        return answer;
120    }
121
122    @Override
123    public Set<Object> getWriteACLs(ActiveMQDestination destination) {
124        Set<AuthorizationEntry> entries = getAllEntries(destination);
125        Set<Object> answer = new WildcardAwareSet<Object>();
126
127        // now lets go through each entry adding individual
128        for (Iterator<AuthorizationEntry> iter = entries.iterator(); iter.hasNext();) {
129            AuthorizationEntry entry = iter.next();
130            answer.addAll(entry.getWriteACLs());
131        }
132        return answer;
133    }
134
135    public AuthorizationEntry getEntryFor(ActiveMQDestination destination) {
136        AuthorizationEntry answer = (AuthorizationEntry)chooseValue(destination);
137        if (answer == null) {
138            answer = getDefaultEntry();
139        }
140        return answer;
141    }
142
143
144    /**
145     * Looks up the value(s) matching the given Destination key. For simple
146     * destinations this is typically a List of one single value, for wildcards
147     * or composite destinations this will typically be a Union of matching
148     * values.
149     *
150     * @param key the destination to lookup
151     * @return a Union of matching values or an empty list if there are no
152     *         matching values.
153     */
154    @Override
155    @SuppressWarnings("rawtypes")
156    public synchronized Set get(ActiveMQDestination key) {
157        if (key.isComposite()) {
158            ActiveMQDestination[] destinations = key.getCompositeDestinations();
159            Set answer = null;
160            for (int i = 0; i < destinations.length; i++) {
161                ActiveMQDestination childDestination = destinations[i];
162                answer = union(answer, get(childDestination));
163                if (answer == null  || answer.isEmpty()) {
164                    break;
165                }
166            }
167            return answer;
168        }
169
170        return findWildcardMatches(key, false);
171    }
172
173
174    /**
175     * Sets the individual entries on the authorization map
176     */
177    @SuppressWarnings("rawtypes")
178    public void setAuthorizationEntries(List<DestinationMapEntry> entries) {
179        super.setEntries(entries);
180    }
181
182    public AuthorizationEntry getDefaultEntry() {
183        return defaultEntry;
184    }
185
186    public void setDefaultEntry(AuthorizationEntry defaultEntry) {
187        this.defaultEntry = defaultEntry;
188    }
189
190    @Override
191    @SuppressWarnings("rawtypes")
192    protected Class<? extends DestinationMapEntry> getEntryClass() {
193        return AuthorizationEntry.class;
194    }
195
196    @SuppressWarnings("unchecked")
197    protected Set<AuthorizationEntry> getAllEntries(ActiveMQDestination destination) {
198        Set<AuthorizationEntry> entries = get(destination);
199        if (defaultEntry != null) {
200            entries.add(defaultEntry);
201        }
202        return entries;
203    }
204
205    public String getGroupClass() {
206        return groupClass;
207    }
208
209    public void setGroupClass(String groupClass) {
210        this.groupClass = groupClass;
211    }
212
213    final static String WILDCARD = "*";
214    public static Object createGroupPrincipal(String name, String groupClass) throws Exception {
215        if (WILDCARD.equals(name)) {
216            // simple match all group principal - match any name and class
217            return new Principal() {
218                @Override
219                public String getName() {
220                    return WILDCARD;
221                }
222                @Override
223                public boolean equals(Object other) {
224                    return true;
225                }
226
227                @Override
228                public int hashCode() {
229                    return WILDCARD.hashCode();
230                }
231            };
232        }
233        Object[] param = new Object[]{name};
234
235        Class<?> cls = Class.forName(groupClass);
236
237        Constructor<?>[] constructors = cls.getConstructors();
238        int i;
239        Object instance;
240        for (i = 0; i < constructors.length; i++) {
241            Class<?>[] paramTypes = constructors[i].getParameterTypes();
242            if (paramTypes.length == 1 && paramTypes[0].equals(String.class)) {
243                break;
244            }
245        }
246        if (i < constructors.length) {
247            instance = constructors[i].newInstance(param);
248        } else {
249            instance = cls.newInstance();
250            Method[] methods = cls.getMethods();
251            i = 0;
252            for (i = 0; i < methods.length; i++) {
253                Class<?>[] paramTypes = methods[i].getParameterTypes();
254                if (paramTypes.length == 1 && methods[i].getName().equals("setName") && paramTypes[0].equals(String.class)) {
255                    break;
256                }
257            }
258
259            if (i < methods.length) {
260                methods[i].invoke(instance, param);
261            } else {
262                throw new NoSuchMethodException();
263            }
264        }
265
266        return instance;
267    }
268
269    class WildcardAwareSet<T> extends HashSet<T> {
270        boolean hasWildcard = false;
271
272        @Override
273        public boolean contains(Object e) {
274            if (hasWildcard) {
275                return true;
276            } else {
277                return super.contains(e);
278            }
279        }
280
281        @Override
282        public boolean addAll(Collection<? extends T> collection) {
283            boolean modified = false;
284            Iterator<? extends T> e = collection.iterator();
285            while (e.hasNext()) {
286                final T item = e.next();
287                if (isWildcard(item)) {
288                    hasWildcard = true;
289                }
290                if (add(item)) {
291                    modified = true;
292                }
293            }
294            return modified;
295        }
296
297        private boolean isWildcard(T item) {
298            try {
299                if (item.getClass().getMethod("getName", new Class[]{}).invoke(item).equals("*")) {
300                    return true;
301                }
302            } catch (Exception ignored) {
303            }
304            return false;
305        }
306    }
307}