You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@activemq.apache.org by "Luis Miguel De Bello (Jira)" <ji...@apache.org> on 2021/01/14 19:59:00 UTC

[jira] [Created] (ARTEMIS-3068) Comparator implementation of HierarchicalObjectRepository issue

Luis Miguel De Bello created ARTEMIS-3068:
---------------------------------------------

             Summary: Comparator implementation of HierarchicalObjectRepository issue
                 Key: ARTEMIS-3068
                 URL: https://issues.apache.org/jira/browse/ARTEMIS-3068
             Project: ActiveMQ Artemis
          Issue Type: Bug
            Reporter: Luis Miguel De Bello


Hi, I am having an issue with the MatchComparator implemented in the
HierarchicalObjectRepository class.

Let me put an example on what's going on right now:

I have created a class called DynamicRolesSecuritySettingPlugin implementing
the interface SecuritySettingPlugin so that I can implement the
setSecurityRepository method. There, I am adding some roles to the
HierarchicalRepository based on a certificate. The HierarchicalRepository
type T is Set<Role>. To summarize, the HierarchicalObjectRepository ends up
having two different strings as key to the internal map (they are added with
the addMatch method):
- *.Provider.*.Agent.*.State
- uswest.Provider.RR.Agent.*.State

For the string "*.Provider.*.Agent.*.State", i end up setting a Role with
permissions to publish but not to consume.
For the string "uswest.Provider.RR.Agent.*.State", i end up setting a Role
with permissions to consume.

Now, when I create a consumer for a queue named
uswest.Provider.RR.Agent.1.State (there is already a queue created with that
name and with messages on it), I get an error on the consumer saying that I
don't have the permission 'CONSUME' for queue
uswest.Provider.RR.Agent.1.State.

So I went ahead and debugged the code. I ended up in the class
HierarchicalObjectRepository reading the getMatch method:

   @Override
   public T getMatch(final String match) {
      String modifiedMatch = matchModifier.modify(match);
      T cacheResult = cache.get(modifiedMatch);
      if (cacheResult != null) {
         return cacheResult;
      }
      lock.readLock().lock();
      try {
         T actualMatch;
         Map<String, Match&lt;T>> possibleMatches =
getPossibleMatches(modifiedMatch);
         Collection<Match&lt;T>> orderedMatches = sort(possibleMatches);
         actualMatch = merge(orderedMatches);
         T value = actualMatch != null ? actualMatch : defaultmatch;
         if (value != null) {
            cache.put(modifiedMatch, value);
         }
         return value;
      } finally {
         lock.readLock().unlock();
      }
   }

When I set a breakpoint in the getPossibleMatches I get the two entries
correctly (both "*.Provider.*.Agent.*.State" and
"uswest.Provider.RR.Agent.*.State"). Then when i see the sort method, i see
that I do get first the "*.Provider.*.Agent.*.State" instead of the
"uswest.Provider.RR.Agent.*State". As I said before, the role attached to
"*.Provider.*.Agent.*.State" does not have permission to consume on the
queue "uswest.Provider.RR.Agent.1.State" and that is the reason why I can't
consume.

When I looked at the comparator (MatchComparator) that decides which one
should be first in the list, I see the following code:

         if (o1.contains(anyWords) && !o2.contains(anyWords)) {
            return +1;
         } else if (!o1.contains(anyWords) && o2.contains(anyWords)) {
            return -1;
         } else if (o1.contains(anyWords) && o2.contains(anyWords)) {
            return o2.length() - o1.length();
         } else if (o1.contains(singleWord) && !o2.contains(singleWord)) {
            return +1;
         } else if (!o1.contains(singleWord) && o2.contains(singleWord)) {
            return -1;
         } else if (o1.contains(singleWord) && o2.contains(singleWord)) {
            String[] leftSplits = o1.split(quotedDelimiter);
            String[] rightSplits = o2.split(quotedDelimiter);
            for (int i = 0; i < leftSplits.length; i++) {
               String left = leftSplits[i];
               if (left.equals(singleWord)) {
                  if (rightSplits.length < i ||
!rightSplits[i].equals(singleWord)) {
                     return -1;
                  } else {
                     return +1;
                  }
               }
            }
         }
         return o1.length() - o2.length();

Since in this case both strings contains the "*" it goes through the last
else if and this is what ends up doing the comparation:
         
          String[] leftSplits = o1.split(quotedDelimiter);
          String[] rightSplits = o2.split(quotedDelimiter);
          for (int i = 0; i < leftSplits.length; i++) {
               String left = leftSplits[i];
               if (left.equals(singleWord)) {
                  if (rightSplits.length < i ||
!rightSplits[i].equals(singleWord)) {
                     return -1;
                  } else {
                     return +1;
                  }
               }
            }

This ends up returning the "*.Provider.*.Agent.*.State" when it shouldn't.
There is a more specific string for the queue
"uswest.Provider.RR.Agent.1.State".

It's not taking into account the amount of "*" present in the string.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)