iloveeyjafjalla opened a new issue, #11406:
URL: https://github.com/apache/rocketmq/issues/11406

   ### Before Creating the Bug Report
   
   - [x] I found a bug, not just asking a question, which should be created in 
[GitHub Discussions](https://github.com/apache/rocketmq/discussions).
   - [x] I have searched the [GitHub 
Issues](https://github.com/apache/rocketmq/issues) and [GitHub 
Discussions](https://github.com/apache/rocketmq/discussions) of this repository 
and believe that this is not a duplicate.
   - [x] I have confirmed that this bug belongs to the current repository, not 
other repositories of RocketMQ.
   
   ### Runtime platform environment
   
   OS: any (broker with ACL 2.0 authorization enabled: 
`authorizationEnabled=true`, 
`authorizationMetadataProvider=...LocalAuthorizationMetadataProvider`)
   
   ### RocketMQ version
   
   branch: develop
   Git commit id: a7edeecfb (the file touched below is unchanged by that commit)
   
   ### JDK Version
   
   Compiler: Temurin 1.8.0_504 (target 1.8, same as CI)
   
   ### Describe the Bug
   
   Retry topics are authorized through the consumer group they belong to: the 
authorization context builder maps `%RETRY%<group>` to the `Group:<group>` 
resource in every request path that reads a caller-supplied topic, and PR 
#11009 ("Map retry topics to consumer-group resources for `VIEW_MESSAGE_BY_ID` 
authorization") extended that rule to `VIEW_MESSAGE_BY_ID` on purpose.
   
   `QUERY_MESSAGE` is the remaining read path that does not do it. 
`DefaultAuthorizationContextBuilder` builds the context as a **topic** resource 
for that request code:
   
   ```java
   case RequestCode.QUERY_MESSAGE:
       topic = Resource.ofTopic(requireResource(fields.get(TOPIC), "topic"));
       result.add(DefaultAuthorizationContext.of(subject, topic, 
Arrays.asList(Action.SUB, Action.GET), sourceIp));
       break;
   ```
   
   while the sibling paths that read a topic with the very same `SUB, GET` 
actions all map retry topics to the group, for example:
   
   ```java
   case RequestCode.VIEW_MESSAGE_BY_ID:
       String viewTopic = requireResource(fields.get(TOPIC), "topic");
       Resource viewResource = NamespaceUtil.isRetryTopic(viewTopic)
           ? Resource.ofGroup(viewTopic) : Resource.ofTopic(viewTopic);
       result.add(DefaultAuthorizationContext.of(subject, viewResource, 
Action.GET, sourceIp));
       break;
   ```
   
   (`GET_MAX_OFFSET`, `SEARCH_OFFSET_BY_TIMESTAMP`, `GET_ROUTEINFO_BY_TOPIC`, 
`PULL_MESSAGE`, `LITE_PULL_MESSAGE`, `QUERY_CONSUMER_OFFSET`, 
`UPDATE_CONSUMER_OFFSET`, `LOCK_BATCH_MQ`, `UNLOCK_BATCH_MQ` and `HEART_BEAT` 
behave the same way.)
   
   Because `Resource.isMatch` refuses to match two different resource types 
(`auth/src/main/java/org/apache/rocketmq/auth/authorization/model/Resource.java:111-113`),
 a role that grants `Group:<group>` with `SUB`/`GET` can read, pull and view 
the retry messages of that group but can never query them by key: the built 
resource `Topic:%RETRY%<group>` only matches a policy that also grants the 
internal retry topic as a topic, which is exactly the kind of grant the group 
convention avoids.
   
   ### Steps to Reproduce
   
   1. Enable ACL 2.0 authorization on a broker and create a role/user that is 
granted `Group:myGroup` (actions `SUB`, `GET`), i.e. the permission that lets 
the same user pull and view that group's retry messages.
   2. Run `mqadmin queryMsgByKey -t %RETRY%myGroup -k <someKey> -n <namesrv>` 
as that user (client path: `QueryMsgByKeySubCommand` → 
`MQClientAPIImpl.queryMessage` → `RequestCode.QUERY_MESSAGE`).
   3. At the unit level the same step is reproduced deterministically by 
building the request:
   
   ```java
   QueryMessageRequestHeader header = new QueryMessageRequestHeader();
   header.setTopic("%RETRY%myGroup");
   List<DefaultAuthorizationContext> result = 
builder.build(channelHandlerContext,
       remotingRequest(RequestCode.QUERY_MESSAGE, header, null));
   // observed:  Topic:%RETRY%myGroup
   // expected:  Group:myGroup   (the same resource the sibling read paths 
produce)
   ```
   
   ### What Did You Expect to See?
   
   The query is authorized by the `Group:myGroup` grant, like every other read 
of a retry topic: the context is `Group:myGroup` with actions `SUB, GET`, so 
the request reaches the broker and returns the matching messages.
   
   ### What Did You See Instead?
   
   The context is `Topic:%RETRY%myGroup`, which no group-level policy can 
match, so the request is rejected with `NO_PERMISSION` (`CODE: 1` / `DESC: 
Authentication failed`/authorization denial) even though the caller is 
authorized for the retry topic's consumer group. The only way to make the 
command work is to grant the internal retry topic as a topic resource, which 
contradicts the convention the other paths follow.
   
   ### Additional Context
   
   Minimal fix: give the `QUERY_MESSAGE` case the same retry-topic mapping its 
sibling cases already use, i.e.
   
   ```java
   case RequestCode.QUERY_MESSAGE:
       String queryMessageTopic = requireResource(fields.get(TOPIC), "topic");
       Resource queryMessageResource = 
NamespaceUtil.isRetryTopic(queryMessageTopic)
           ? Resource.ofGroup(queryMessageTopic) : 
Resource.ofTopic(queryMessageTopic);
       result.add(DefaultAuthorizationContext.of(subject, queryMessageResource, 
Arrays.asList(Action.SUB, Action.GET), sourceIp));
       break;
   ```
   
   Non-retry topics are unaffected, and the request codes that also read a 
topic but cannot legitimately carry a retry topic (`SEND_REPLY_MESSAGE`, 
`SEND_REPLY_MESSAGE_V2`, `RECALL_MESSAGE`, `END_TRANSACTION`, 
`QUERY_ASSIGNMENT`, `SET_MESSAGE_REQUEST_MODE`, 
`UPDATE_AND_CREATE_STATIC_TOPIC`) are left as they are. `BATCH_ACK_MESSAGE` and 
`CHECK_CLIENT_CONFIG` additionally authorize the consumer group of the same 
request and are out of scope here.
   
   I will open a PR with this change plus a unit test in 
`DefaultAuthorizationContextBuilderTest` (red before the change: `expected: 
Group:myGroup but was: Topic:%RETRY%myGroup`).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to