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