content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.activemq.security;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Method;
  import java.security.Principal;
  import java.util.*;
  
  import org.apache.activemq.command.ActiveMQDestination;
  import org.apache.activemq.filter.DestinationMap;
  import org.apache.activemq.filter.DestinationMapEntry;
  
  /**
   * Represents a destination based configuration of policies so that individual
   * destinations or wildcard hierarchies of destinations can be configured using
   * different policies. Each entry in the map represents the authorization ACLs
   * for each operation.
   *
   *
   */
  public class DefaultAuthorizationMap extends DestinationMap implements AuthorizationMap {
  
      public static final String DEFAULT_GROUP_CLASS = "org.apache.activemq.jaas.GroupPrincipal";
  
      private AuthorizationEntry defaultEntry;
  
      private TempDestinationAuthorizationEntry tempDestinationAuthorizationEntry;
  
      protected String groupClass = DEFAULT_GROUP_CLASS;
  
      public DefaultAuthorizationMap() {
      }
  
      @SuppressWarnings("rawtypes")
      public DefaultAuthorizationMap(List<DestinationMapEntry> authorizationEntries) {
          setAuthorizationEntries(authorizationEntries);
  
      }
  
      public void setTempDestinationAuthorizationEntry(TempDestinationAuthorizationEntry tempDestinationAuthorizationEntry) {
          this.tempDestinationAuthorizationEntry = tempDestinationAuthorizationEntry;
      }
  
      public TempDestinationAuthorizationEntry getTempDestinationAuthorizationEntry() {
          return this.tempDestinationAuthorizationEntry;
      }
  
      @Override
      public Set<Object> getTempDestinationAdminACLs() {
          if (tempDestinationAuthorizationEntry != null) {
              Set<Object> answer = new WildcardAwareSet<Object>();
              answer.addAll(tempDestinationAuthorizationEntry.getAdminACLs());
              return answer;
          } else {
              return null;
          }
      }
  
      @Override
      public Set<Object> getTempDestinationReadACLs() {
          if (tempDestinationAuthorizationEntry != null) {
              Set<Object> answer = new WildcardAwareSet<Object>();
              answer.addAll(tempDestinationAuthorizationEntry.getReadACLs());
              return answer;
          } else {
              return null;
          }
      }
  
      @Override
      public Set<Object> getTempDestinationWriteACLs() {
          if (tempDestinationAuthorizationEntry != null) {
              Set<Object> answer = new WildcardAwareSet<Object>();
              answer.addAll(tempDestinationAuthorizationEntry.getWriteACLs());
              return answer;
          } else {
              return null;
          }
      }
  
      @Override
      public Set<Object> getAdminACLs(ActiveMQDestination destination) {
          Set<AuthorizationEntry> entries = getAllEntries(destination);
          Set<Object> answer = new WildcardAwareSet<Object>();
  
          // now lets go through each entry adding individual
          for (Iterator<AuthorizationEntry> iter = entries.iterator(); iter.hasNext();) {
              AuthorizationEntry entry = iter.next();
              answer.addAll(entry.getAdminACLs());
          }
          return answer;
      }
  
      @Override
      public Set<Object> getReadACLs(ActiveMQDestination destination) {
          Set<AuthorizationEntry> entries = getAllEntries(destination);
          Set<Object> answer = new WildcardAwareSet<Object>();
  
          // now lets go through each entry adding individual
          for (Iterator<AuthorizationEntry> iter = entries.iterator(); iter.hasNext();) {
              AuthorizationEntry entry = iter.next();
              answer.addAll(entry.getReadACLs());
          }
          return answer;
      }
  
      @Override
      public Set<Object> getWriteACLs(ActiveMQDestination destination) {
          Set<AuthorizationEntry> entries = getAllEntries(destination);
          Set<Object> answer = new WildcardAwareSet<Object>();
  
          // now lets go through each entry adding individual
          for (Iterator<AuthorizationEntry> iter = entries.iterator(); iter.hasNext();) {
              AuthorizationEntry entry = iter.next();
              answer.addAll(entry.getWriteACLs());
          }
          return answer;
      }
  
      public AuthorizationEntry getEntryFor(ActiveMQDestination destination) {
          AuthorizationEntry answer = (AuthorizationEntry)chooseValue(destination);
          if (answer == null) {
              answer = getDefaultEntry();
          }
          return answer;
      }
  
  
      /**
       * Looks up the value(s) matching the given Destination key. For simple
       * destinations this is typically a List of one single value, for wildcards
       * or composite destinations this will typically be a Union of matching
       * values.
       *
       * @param key the destination to lookup
       * @return a Union of matching values or an empty list if there are no
       *         matching values.
       */
      @Override
      @SuppressWarnings("rawtypes")
      public synchronized Set get(ActiveMQDestination key) {
          if (key.isComposite()) {
              ActiveMQDestination[] destinations = key.getCompositeDestinations();
              Set answer = null;
              for (int i = 0; i < destinations.length; i++) {
                  ActiveMQDestination childDestination = destinations[i];
                  answer = union(answer, get(childDestination));
                  if (answer == null  || answer.isEmpty()) {
                      break;
                  }
              }
              return answer;
          }
  
          return findWildcardMatches(key, false);
      }
  
  
      /**
       * Sets the individual entries on the authorization map
       */
      @SuppressWarnings("rawtypes")
      public void setAuthorizationEntries(List<DestinationMapEntry> entries) {
          super.setEntries(entries);
      }
  
      public AuthorizationEntry getDefaultEntry() {
          return defaultEntry;
      }
  
      public void setDefaultEntry(AuthorizationEntry defaultEntry) {
          this.defaultEntry = defaultEntry;
      }
  
      @Override
      @SuppressWarnings("rawtypes")
      protected Class<? extends DestinationMapEntry> getEntryClass() {
          return AuthorizationEntry.class;
      }
  
      @SuppressWarnings("unchecked")
      protected Set<AuthorizationEntry> getAllEntries(ActiveMQDestination destination) {
          Set<AuthorizationEntry> entries = get(destination);
          if (defaultEntry != null) {
              entries.add(defaultEntry);
          }
          return entries;
      }
  
      public String getGroupClass() {
          return groupClass;
      }
  
      public void setGroupClass(String groupClass) {
          this.groupClass = groupClass;
      }
  
      final static String WILDCARD = "*";
      public static Object createGroupPrincipal(String name, String groupClass) throws Exception {
          if (WILDCARD.equals(name)) {
              // simple match all group principal - match any name and class
              return new Principal() {
                  @Override
                  public String getName() {
                      return WILDCARD;
                  }
                  @Override
                  public boolean equals(Object other) {
                      return true;
                  }
  
                  @Override
                  public int hashCode() {
                      return WILDCARD.hashCode();
                  }
              };
          }
          Object[] param = new Object[]{name};
  
          Class<?> cls = Class.forName(groupClass);
  
          Constructor<?>[] constructors = cls.getConstructors();
          int i;
          Object instance;
          for (i = 0; i < constructors.length; i++) {
              Class<?>[] paramTypes = constructors[i].getParameterTypes();
              if (paramTypes.length == 1 && paramTypes[0].equals(String.class)) {
                  break;
              }
          }
          if (i < constructors.length) {
              instance = constructors[i].newInstance(param);
          } else {
              instance = cls.getConstructor().newInstance();
              Method[] methods = cls.getMethods();
              i = 0;
              for (i = 0; i < methods.length; i++) {
                  Class<?>[] paramTypes = methods[i].getParameterTypes();
                  if (paramTypes.length == 1 && methods[i].getName().equals("setName") && paramTypes[0].equals(String.class)) {
                      break;
                  }
              }
  
              if (i < methods.length) {
                  methods[i].invoke(instance, param);
              } else {
                  throw new NoSuchMethodException();
              }
          }
  
          return instance;
      }
  
      class WildcardAwareSet<T> extends HashSet<T> {
          boolean hasWildcard = false;
  
          @Override
          public boolean contains(Object e) {
              if (hasWildcard) {
                  return true;
              } else {
                  return super.contains(e);
              }
          }
  
          @Override
          public boolean addAll(Collection<? extends T> collection) {
              boolean modified = false;
              Iterator<? extends T> e = collection.iterator();
              while (e.hasNext()) {
                  final T item = e.next();
                  if (isWildcard(item)) {
                      hasWildcard = true;
                  }
                  if (add(item)) {
                      modified = true;
                  }
              }
              return modified;
          }
  
          private boolean isWildcard(T item) {
              try {
                  if (item.getClass().getMethod("getName", new Class[]{}).invoke(item).equals("*")) {
                      return true;
                  }
              } catch (Exception ignored) {
              }
              return false;
          }
      }
  }
  