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