Repository: apache/rocketmq
Branch: develop
Commit: ebf159541817
Files: 2627
Total size: 16.4 MB
Directory structure:
gitextract_8f2xr136/
├── .asf.yaml
├── .bazelrc
├── .bazelversion
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── doc.yml
│ │ ├── enhancement_request.yml
│ │ └── feature_request.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── asf-deploy-settings.xml
│ └── workflows/
│ ├── bazel.yml
│ ├── codeql_analysis.yml
│ ├── coverage.yml
│ ├── integration-test.yml
│ ├── license-checker.yaml
│ ├── maven.yaml
│ ├── misspell_check.yml
│ ├── pr-ci.yml
│ ├── pr-e2e-test.yml
│ ├── push-ci.yml
│ ├── rerun-workflow.yml
│ ├── snapshot-automation.yml
│ └── stale.yml
├── .gitignore
├── .licenserc.yaml
├── BUILD.bazel
├── BUILDING
├── CONTRIBUTING.md
├── LICENSE
├── MODULE.bazel
├── NOTICE
├── README.md
├── WORKSPACE
├── auth/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── auth/
│ │ ├── authentication/
│ │ │ ├── AuthenticationEvaluator.java
│ │ │ ├── builder/
│ │ │ │ ├── AuthenticationContextBuilder.java
│ │ │ │ └── DefaultAuthenticationContextBuilder.java
│ │ │ ├── chain/
│ │ │ │ └── DefaultAuthenticationHandler.java
│ │ │ ├── context/
│ │ │ │ ├── AuthenticationContext.java
│ │ │ │ └── DefaultAuthenticationContext.java
│ │ │ ├── enums/
│ │ │ │ ├── SubjectType.java
│ │ │ │ ├── UserStatus.java
│ │ │ │ └── UserType.java
│ │ │ ├── exception/
│ │ │ │ └── AuthenticationException.java
│ │ │ ├── factory/
│ │ │ │ └── AuthenticationFactory.java
│ │ │ ├── manager/
│ │ │ │ ├── AuthenticationMetadataManager.java
│ │ │ │ └── AuthenticationMetadataManagerImpl.java
│ │ │ ├── model/
│ │ │ │ ├── Subject.java
│ │ │ │ └── User.java
│ │ │ ├── provider/
│ │ │ │ ├── AuthenticationMetadataProvider.java
│ │ │ │ ├── AuthenticationProvider.java
│ │ │ │ ├── DefaultAuthenticationProvider.java
│ │ │ │ └── LocalAuthenticationMetadataProvider.java
│ │ │ └── strategy/
│ │ │ ├── AbstractAuthenticationStrategy.java
│ │ │ ├── AuthenticationStrategy.java
│ │ │ ├── StatefulAuthenticationStrategy.java
│ │ │ └── StatelessAuthenticationStrategy.java
│ │ ├── authorization/
│ │ │ ├── AuthorizationEvaluator.java
│ │ │ ├── builder/
│ │ │ │ ├── AuthorizationContextBuilder.java
│ │ │ │ └── DefaultAuthorizationContextBuilder.java
│ │ │ ├── chain/
│ │ │ │ ├── AclAuthorizationHandler.java
│ │ │ │ └── UserAuthorizationHandler.java
│ │ │ ├── context/
│ │ │ │ ├── AuthorizationContext.java
│ │ │ │ └── DefaultAuthorizationContext.java
│ │ │ ├── enums/
│ │ │ │ ├── Decision.java
│ │ │ │ └── PolicyType.java
│ │ │ ├── exception/
│ │ │ │ └── AuthorizationException.java
│ │ │ ├── factory/
│ │ │ │ └── AuthorizationFactory.java
│ │ │ ├── manager/
│ │ │ │ ├── AuthorizationMetadataManager.java
│ │ │ │ └── AuthorizationMetadataManagerImpl.java
│ │ │ ├── model/
│ │ │ │ ├── Acl.java
│ │ │ │ ├── Environment.java
│ │ │ │ ├── Policy.java
│ │ │ │ ├── PolicyEntry.java
│ │ │ │ ├── RequestContext.java
│ │ │ │ └── Resource.java
│ │ │ ├── provider/
│ │ │ │ ├── AuthorizationMetadataProvider.java
│ │ │ │ ├── AuthorizationProvider.java
│ │ │ │ ├── DefaultAuthorizationProvider.java
│ │ │ │ └── LocalAuthorizationMetadataProvider.java
│ │ │ └── strategy/
│ │ │ ├── AbstractAuthorizationStrategy.java
│ │ │ ├── AuthorizationStrategy.java
│ │ │ ├── StatefulAuthorizationStrategy.java
│ │ │ └── StatelessAuthorizationStrategy.java
│ │ ├── config/
│ │ │ └── AuthConfig.java
│ │ └── migration/
│ │ ├── AuthMigrator.java
│ │ └── v1/
│ │ ├── AccessResource.java
│ │ ├── AclConfig.java
│ │ ├── PlainAccessConfig.java
│ │ ├── PlainAccessData.java
│ │ ├── PlainAccessResource.java
│ │ └── PlainPermissionManager.java
│ └── test/
│ └── java/
│ └── org/
│ └── apache/
│ └── rocketmq/
│ └── auth/
│ ├── authentication/
│ │ ├── AuthenticationEvaluatorTest.java
│ │ ├── builder/
│ │ │ └── DefaultAuthenticationContextBuilderTest.java
│ │ ├── manager/
│ │ │ └── AuthenticationMetadataManagerTest.java
│ │ └── provider/
│ │ └── LocalAuthenticationMetadataProviderTest.java
│ ├── authorization/
│ │ ├── AuthorizationEvaluatorTest.java
│ │ ├── builder/
│ │ │ └── DefaultAuthorizationContextBuilderTest.java
│ │ ├── chain/
│ │ │ ├── AclAuthorizationHandlerTest.java
│ │ │ └── UserAuthorizationHandlerTest.java
│ │ ├── manager/
│ │ │ └── AuthorizationMetadataManagerTest.java
│ │ ├── model/
│ │ │ └── ResourceTest.java
│ │ ├── provider/
│ │ │ └── LocalAuthorizationMetadataProviderTest.java
│ │ └── strategy/
│ │ └── StatefulAuthorizationStrategyTest.java
│ ├── helper/
│ │ └── AuthTestHelper.java
│ └── migration/
│ └── AuthMigratorTest.java
├── bazel/
│ ├── BUILD.bazel
│ └── GenTestRules.bzl
├── broker/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── apache/
│ │ │ └── rocketmq/
│ │ │ └── broker/
│ │ │ ├── BrokerController.java
│ │ │ ├── BrokerPathConfigHelper.java
│ │ │ ├── BrokerPreOnlineService.java
│ │ │ ├── BrokerStartup.java
│ │ │ ├── ConfigContext.java
│ │ │ ├── ShutdownHook.java
│ │ │ ├── auth/
│ │ │ │ ├── converter/
│ │ │ │ │ ├── AclConverter.java
│ │ │ │ │ └── UserConverter.java
│ │ │ │ └── pipeline/
│ │ │ │ ├── AuthenticationPipeline.java
│ │ │ │ └── AuthorizationPipeline.java
│ │ │ ├── client/
│ │ │ │ ├── ClientChannelAttributeHelper.java
│ │ │ │ ├── ClientChannelInfo.java
│ │ │ │ ├── ClientHousekeepingService.java
│ │ │ │ ├── ConsumerGroupEvent.java
│ │ │ │ ├── ConsumerGroupInfo.java
│ │ │ │ ├── ConsumerIdsChangeListener.java
│ │ │ │ ├── ConsumerManager.java
│ │ │ │ ├── DefaultConsumerIdsChangeListener.java
│ │ │ │ ├── ProducerChangeListener.java
│ │ │ │ ├── ProducerGroupEvent.java
│ │ │ │ ├── ProducerManager.java
│ │ │ │ ├── net/
│ │ │ │ │ └── Broker2Client.java
│ │ │ │ └── rebalance/
│ │ │ │ └── RebalanceLockManager.java
│ │ │ ├── coldctr/
│ │ │ │ ├── ColdCtrStrategy.java
│ │ │ │ ├── ColdDataCgCtrService.java
│ │ │ │ ├── ColdDataPullRequestHoldService.java
│ │ │ │ ├── PIDAdaptiveColdCtrStrategy.java
│ │ │ │ └── SimpleColdCtrStrategy.java
│ │ │ ├── config/
│ │ │ │ ├── v1/
│ │ │ │ │ ├── RocksDBConfigManager.java
│ │ │ │ │ ├── RocksDBConsumerOffsetManager.java
│ │ │ │ │ ├── RocksDBLmqSubscriptionGroupManager.java
│ │ │ │ │ ├── RocksDBLmqTopicConfigManager.java
│ │ │ │ │ ├── RocksDBOffsetSerializeWrapper.java
│ │ │ │ │ ├── RocksDBSubscriptionGroupManager.java
│ │ │ │ │ └── RocksDBTopicConfigManager.java
│ │ │ │ └── v2/
│ │ │ │ ├── ConfigHelper.java
│ │ │ │ ├── ConfigStorage.java
│ │ │ │ ├── ConsumerOffsetManagerV2.java
│ │ │ │ ├── RecordPrefix.java
│ │ │ │ ├── SerializationType.java
│ │ │ │ ├── SubscriptionGroupManagerV2.java
│ │ │ │ ├── TableId.java
│ │ │ │ ├── TablePrefix.java
│ │ │ │ ├── TopicConfigManagerV2.java
│ │ │ │ └── package-info.java
│ │ │ ├── controller/
│ │ │ │ └── ReplicasManager.java
│ │ │ ├── dledger/
│ │ │ │ └── DLedgerRoleChangeHandler.java
│ │ │ ├── failover/
│ │ │ │ └── EscapeBridge.java
│ │ │ ├── filter/
│ │ │ │ ├── CommitLogDispatcherCalcBitMap.java
│ │ │ │ ├── ConsumerFilterData.java
│ │ │ │ ├── ConsumerFilterManager.java
│ │ │ │ ├── ExpressionForRetryMessageFilter.java
│ │ │ │ ├── ExpressionMessageFilter.java
│ │ │ │ └── MessageEvaluationContext.java
│ │ │ ├── latency/
│ │ │ │ └── BrokerFastFailure.java
│ │ │ ├── lite/
│ │ │ │ ├── AbstractLiteLifecycleManager.java
│ │ │ │ ├── LiteCtlListener.java
│ │ │ │ ├── LiteEventDispatcher.java
│ │ │ │ ├── LiteLifecycleManager.java
│ │ │ │ ├── LiteMetadataUtil.java
│ │ │ │ ├── LiteQuotaException.java
│ │ │ │ ├── LiteSharding.java
│ │ │ │ ├── LiteShardingImpl.java
│ │ │ │ ├── LiteSubscriptionRegistry.java
│ │ │ │ ├── LiteSubscriptionRegistryImpl.java
│ │ │ │ └── RocksDBLiteLifecycleManager.java
│ │ │ ├── loadbalance/
│ │ │ │ └── MessageRequestModeManager.java
│ │ │ ├── longpolling/
│ │ │ │ ├── LmqPullRequestHoldService.java
│ │ │ │ ├── ManyPullRequest.java
│ │ │ │ ├── NotificationRequest.java
│ │ │ │ ├── NotifyMessageArrivingListener.java
│ │ │ │ ├── PollingHeader.java
│ │ │ │ ├── PollingResult.java
│ │ │ │ ├── PopCommandCallback.java
│ │ │ │ ├── PopLiteLongPollingService.java
│ │ │ │ ├── PopLongPollingService.java
│ │ │ │ ├── PopRequest.java
│ │ │ │ ├── PullRequest.java
│ │ │ │ └── PullRequestHoldService.java
│ │ │ ├── metrics/
│ │ │ │ ├── BrokerMetricsConstant.java
│ │ │ │ ├── BrokerMetricsManager.java
│ │ │ │ ├── ConsumerAttr.java
│ │ │ │ ├── ConsumerLagCalculator.java
│ │ │ │ ├── InvocationStatus.java
│ │ │ │ ├── LiteConsumerLagCalculator.java
│ │ │ │ ├── PopMetricsConstant.java
│ │ │ │ ├── PopMetricsManager.java
│ │ │ │ ├── PopReviveMessageType.java
│ │ │ │ └── ProducerAttr.java
│ │ │ ├── mqtrace/
│ │ │ │ ├── ConsumeMessageContext.java
│ │ │ │ ├── ConsumeMessageHook.java
│ │ │ │ ├── SendMessageContext.java
│ │ │ │ └── SendMessageHook.java
│ │ │ ├── offset/
│ │ │ │ ├── BroadcastOffsetManager.java
│ │ │ │ ├── BroadcastOffsetStore.java
│ │ │ │ ├── ConsumerOffsetManager.java
│ │ │ │ ├── LmqConsumerOffsetManager.java
│ │ │ │ └── MemoryConsumerOrderInfoManager.java
│ │ │ ├── out/
│ │ │ │ └── BrokerOuterAPI.java
│ │ │ ├── pagecache/
│ │ │ │ ├── ManyMessageTransfer.java
│ │ │ │ ├── OneMessageTransfer.java
│ │ │ │ └── QueryMessageTransfer.java
│ │ │ ├── plugin/
│ │ │ │ ├── BrokerAttachedPlugin.java
│ │ │ │ └── PullMessageResultHandler.java
│ │ │ ├── pop/
│ │ │ │ ├── PopConsumerCache.java
│ │ │ │ ├── PopConsumerContext.java
│ │ │ │ ├── PopConsumerKVStore.java
│ │ │ │ ├── PopConsumerLockService.java
│ │ │ │ ├── PopConsumerRecord.java
│ │ │ │ ├── PopConsumerRocksdbStore.java
│ │ │ │ ├── PopConsumerService.java
│ │ │ │ └── orderly/
│ │ │ │ ├── ConsumerOrderInfoManager.java
│ │ │ │ ├── QueueLevelConsumerManager.java
│ │ │ │ └── QueueLevelConsumerOrderInfoLockManager.java
│ │ │ ├── processor/
│ │ │ │ ├── AbstractSendMessageProcessor.java
│ │ │ │ ├── AckMessageProcessor.java
│ │ │ │ ├── AdminBrokerProcessor.java
│ │ │ │ ├── ChangeInvisibleTimeProcessor.java
│ │ │ │ ├── ClientManageProcessor.java
│ │ │ │ ├── ConsumerManageProcessor.java
│ │ │ │ ├── DefaultPullMessageResultHandler.java
│ │ │ │ ├── EndTransactionProcessor.java
│ │ │ │ ├── LiteManagerProcessor.java
│ │ │ │ ├── LiteSubscriptionCtlProcessor.java
│ │ │ │ ├── NotificationProcessor.java
│ │ │ │ ├── PeekMessageProcessor.java
│ │ │ │ ├── PollingInfoProcessor.java
│ │ │ │ ├── PopBufferMergeService.java
│ │ │ │ ├── PopInflightMessageCounter.java
│ │ │ │ ├── PopLiteMessageProcessor.java
│ │ │ │ ├── PopMessageProcessor.java
│ │ │ │ ├── PopReviveService.java
│ │ │ │ ├── PullMessageProcessor.java
│ │ │ │ ├── QueryAssignmentProcessor.java
│ │ │ │ ├── QueryMessageProcessor.java
│ │ │ │ ├── RecallMessageProcessor.java
│ │ │ │ ├── ReplyMessageProcessor.java
│ │ │ │ ├── SendMessageCallback.java
│ │ │ │ └── SendMessageProcessor.java
│ │ │ ├── schedule/
│ │ │ │ ├── DelayOffsetSerializeWrapper.java
│ │ │ │ └── ScheduleMessageService.java
│ │ │ ├── slave/
│ │ │ │ └── SlaveSynchronize.java
│ │ │ ├── subscription/
│ │ │ │ ├── LmqSubscriptionGroupManager.java
│ │ │ │ └── SubscriptionGroupManager.java
│ │ │ ├── topic/
│ │ │ │ ├── LmqTopicConfigManager.java
│ │ │ │ ├── TopicConfigManager.java
│ │ │ │ ├── TopicQueueMappingCleanService.java
│ │ │ │ ├── TopicQueueMappingManager.java
│ │ │ │ └── TopicRouteInfoManager.java
│ │ │ ├── transaction/
│ │ │ │ ├── AbstractTransactionalMessageCheckListener.java
│ │ │ │ ├── OperationResult.java
│ │ │ │ ├── TransactionMetrics.java
│ │ │ │ ├── TransactionMetricsFlushService.java
│ │ │ │ ├── TransactionalMessageCheckService.java
│ │ │ │ ├── TransactionalMessageService.java
│ │ │ │ ├── queue/
│ │ │ │ │ ├── DefaultTransactionalMessageCheckListener.java
│ │ │ │ │ ├── GetResult.java
│ │ │ │ │ ├── MessageQueueOpContext.java
│ │ │ │ │ ├── TransactionalMessageBridge.java
│ │ │ │ │ ├── TransactionalMessageServiceImpl.java
│ │ │ │ │ ├── TransactionalMessageUtil.java
│ │ │ │ │ └── TransactionalOpBatchService.java
│ │ │ │ └── rocksdb/
│ │ │ │ └── TransactionalMessageRocksDBService.java
│ │ │ └── util/
│ │ │ ├── HookUtils.java
│ │ │ └── PositiveAtomicCounter.java
│ │ └── resources/
│ │ ├── rmq.broker.logback.xml
│ │ └── transaction.sql
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── broker/
│ │ ├── BrokerControllerTest.java
│ │ ├── BrokerOuterAPITest.java
│ │ ├── BrokerPathConfigHelperTest.java
│ │ ├── BrokerShutdownTest.java
│ │ ├── BrokerStartupTest.java
│ │ ├── RocksDBConfigManagerTest.java
│ │ ├── client/
│ │ │ ├── ConsumerManagerScannerTest.java
│ │ │ ├── ConsumerManagerTest.java
│ │ │ ├── ProducerManagerTest.java
│ │ │ ├── net/
│ │ │ │ └── Broker2ClientTest.java
│ │ │ └── rebalance/
│ │ │ └── RebalanceLockManagerTest.java
│ │ ├── coldctr/
│ │ │ └── ColdDataCgCtrServiceTest.java
│ │ ├── config/
│ │ │ ├── v1/
│ │ │ │ ├── RocksDBConsumerOffsetManagerMigrationTest.java
│ │ │ │ ├── RocksDBSubscriptionGroupManagerMigrationTest.java
│ │ │ │ ├── RocksDBSubscriptionGroupManagerTest.java
│ │ │ │ ├── RocksDBTopicConfigManagerMigrationTest.java
│ │ │ │ └── RocksDBTopicConfigManagerTest.java
│ │ │ └── v2/
│ │ │ ├── ConsumerOffsetManagerV2Test.java
│ │ │ ├── SubscriptionGroupManagerV2Test.java
│ │ │ └── TopicConfigManagerV2Test.java
│ │ ├── controller/
│ │ │ ├── ReplicasManagerRegisterTest.java
│ │ │ └── ReplicasManagerTest.java
│ │ ├── failover/
│ │ │ └── EscapeBridgeTest.java
│ │ ├── filter/
│ │ │ ├── CommitLogDispatcherCalcBitMapTest.java
│ │ │ ├── ConsumerFilterManagerTest.java
│ │ │ └── MessageStoreWithFilterTest.java
│ │ ├── latency/
│ │ │ └── BrokerFastFailureTest.java
│ │ ├── lite/
│ │ │ ├── AbstractLiteLifecycleManagerTest.java
│ │ │ ├── LiteEventDispatcherTest.java
│ │ │ ├── LiteLifecycleManagerTest.java
│ │ │ ├── LiteShardingImplTest.java
│ │ │ ├── LiteSubscriptionRegistryImplTest.java
│ │ │ ├── LiteTestUtil.java
│ │ │ └── RocksDBLiteLifecycleManagerTest.java
│ │ ├── longpolling/
│ │ │ ├── PopLiteLongPollingServiceTest.java
│ │ │ ├── PopLongPollingServiceTest.java
│ │ │ └── PullRequestHoldServiceTest.java
│ │ ├── metrics/
│ │ │ ├── BrokerMetricsManagerTest.java
│ │ │ └── LiteConsumerLagCalculatorTest.java
│ │ ├── offset/
│ │ │ ├── BroadcastOffsetManagerTest.java
│ │ │ ├── BroadcastOffsetStoreTest.java
│ │ │ ├── ConsumerOffsetManagerTest.java
│ │ │ ├── LmqConsumerOffsetManagerTest.java
│ │ │ ├── RocksDBConsumerOffsetManagerTest.java
│ │ │ ├── RocksDBLmqConsumerOffsetManagerTest.java
│ │ │ ├── RocksDBOffsetSerializeWrapperTest.java
│ │ │ └── RocksdbTransferOffsetAndCqTest.java
│ │ ├── pagecache/
│ │ │ ├── ManyMessageTransferTest.java
│ │ │ ├── OneMessageTransferTest.java
│ │ │ └── QueryMessageTransferTest.java
│ │ ├── pop/
│ │ │ ├── PopConsumerCacheTest.java
│ │ │ ├── PopConsumerContextTest.java
│ │ │ ├── PopConsumerLockServiceTest.java
│ │ │ ├── PopConsumerRecordTest.java
│ │ │ ├── PopConsumerRocksdbStoreTest.java
│ │ │ ├── PopConsumerServiceTest.java
│ │ │ └── orderly/
│ │ │ ├── ConsumerOrderInfoManagerLockFreeNotifyTest.java
│ │ │ └── ConsumerOrderInfoManagerTest.java
│ │ ├── processor/
│ │ │ ├── AckMessageProcessorTest.java
│ │ │ ├── AdminBrokerProcessorTest.java
│ │ │ ├── ChangeInvisibleTimeProcessorTest.java
│ │ │ ├── ClientManageProcessorTest.java
│ │ │ ├── ConsumerManageProcessorTest.java
│ │ │ ├── EndTransactionProcessorTest.java
│ │ │ ├── LiteManagerProcessorTest.java
│ │ │ ├── LiteSubscriptionCtlProcessorTest.java
│ │ │ ├── PeekMessageProcessorTest.java
│ │ │ ├── PopBufferMergeServiceTest.java
│ │ │ ├── PopInflightMessageCounterTest.java
│ │ │ ├── PopLiteMessageProcessorTest.java
│ │ │ ├── PopMessageProcessorTest.java
│ │ │ ├── PopReviveServiceTest.java
│ │ │ ├── PullMessageProcessorTest.java
│ │ │ ├── QueryAssignmentProcessorTest.java
│ │ │ ├── QueryMessageProcessorTest.java
│ │ │ ├── RecallMessageProcessorTest.java
│ │ │ ├── ReplyMessageProcessorTest.java
│ │ │ └── SendMessageProcessorTest.java
│ │ ├── schedule/
│ │ │ └── ScheduleMessageServiceTest.java
│ │ ├── slave/
│ │ │ ├── SlaveSynchronizeAtomicTest.java
│ │ │ └── SlaveSynchronizeTest.java
│ │ ├── subscription/
│ │ │ ├── ForbiddenTest.java
│ │ │ ├── RocksdbGroupConfigTransferTest.java
│ │ │ └── SubscriptionGroupManagerTest.java
│ │ ├── topic/
│ │ │ ├── RocksdbTopicConfigManagerTest.java
│ │ │ ├── RocksdbTopicConfigTransferTest.java
│ │ │ ├── TopicConfigManagerTest.java
│ │ │ ├── TopicQueueMappingCleanServiceTest.java
│ │ │ └── TopicQueueMappingManagerTest.java
│ │ ├── transaction/
│ │ │ └── queue/
│ │ │ ├── DefaultTransactionalMessageCheckListenerTest.java
│ │ │ ├── TransactionMetricsTest.java
│ │ │ ├── TransactionalMessageBridgeTest.java
│ │ │ ├── TransactionalMessageServiceImplTest.java
│ │ │ └── TransactionalMessageUtilTest.java
│ │ └── util/
│ │ ├── HookUtilsTest.java
│ │ ├── LogTransactionalMessageCheckListener.java
│ │ ├── ServiceProviderTest.java
│ │ └── TransactionalMessageServiceImpl.java
│ └── resources/
│ ├── META-INF/
│ │ └── service/
│ │ ├── org.apache.rocketmq.broker.transaction.AbstractTransactionalMessageCheckListener
│ │ └── org.apache.rocketmq.broker.transaction.TransactionalMessageService
│ └── rmq.logback-test.xml
├── client/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── apache/
│ │ │ └── rocketmq/
│ │ │ ├── acl/
│ │ │ │ └── common/
│ │ │ │ ├── AclClientRPCHook.java
│ │ │ │ ├── AclConstants.java
│ │ │ │ ├── AclException.java
│ │ │ │ ├── AclSigner.java
│ │ │ │ ├── AclUtils.java
│ │ │ │ ├── Permission.java
│ │ │ │ ├── SessionCredentials.java
│ │ │ │ └── SigningAlgorithm.java
│ │ │ └── client/
│ │ │ ├── AccessChannel.java
│ │ │ ├── ClientConfig.java
│ │ │ ├── MQAdmin.java
│ │ │ ├── MQHelper.java
│ │ │ ├── MqClientAdmin.java
│ │ │ ├── QueryResult.java
│ │ │ ├── Validators.java
│ │ │ ├── admin/
│ │ │ │ └── MQAdminExtInner.java
│ │ │ ├── common/
│ │ │ │ ├── ClientErrorCode.java
│ │ │ │ ├── NameserverAccessConfig.java
│ │ │ │ └── ThreadLocalIndex.java
│ │ │ ├── consumer/
│ │ │ │ ├── AckCallback.java
│ │ │ │ ├── AckResult.java
│ │ │ │ ├── AckStatus.java
│ │ │ │ ├── AllocateMessageQueueStrategy.java
│ │ │ │ ├── DefaultLitePullConsumer.java
│ │ │ │ ├── DefaultMQPullConsumer.java
│ │ │ │ ├── DefaultMQPushConsumer.java
│ │ │ │ ├── LitePullConsumer.java
│ │ │ │ ├── MQConsumer.java
│ │ │ │ ├── MQPullConsumer.java
│ │ │ │ ├── MQPullConsumerScheduleService.java
│ │ │ │ ├── MQPushConsumer.java
│ │ │ │ ├── MessageQueueListener.java
│ │ │ │ ├── MessageSelector.java
│ │ │ │ ├── NotifyResult.java
│ │ │ │ ├── PopCallback.java
│ │ │ │ ├── PopResult.java
│ │ │ │ ├── PopStatus.java
│ │ │ │ ├── PullCallback.java
│ │ │ │ ├── PullResult.java
│ │ │ │ ├── PullStatus.java
│ │ │ │ ├── PullTaskCallback.java
│ │ │ │ ├── PullTaskContext.java
│ │ │ │ ├── TopicMessageQueueChangeListener.java
│ │ │ │ ├── listener/
│ │ │ │ │ ├── ConsumeConcurrentlyContext.java
│ │ │ │ │ ├── ConsumeConcurrentlyStatus.java
│ │ │ │ │ ├── ConsumeOrderlyContext.java
│ │ │ │ │ ├── ConsumeOrderlyStatus.java
│ │ │ │ │ ├── ConsumeReturnType.java
│ │ │ │ │ ├── MessageListener.java
│ │ │ │ │ ├── MessageListenerConcurrently.java
│ │ │ │ │ └── MessageListenerOrderly.java
│ │ │ │ ├── rebalance/
│ │ │ │ │ ├── AbstractAllocateMessageQueueStrategy.java
│ │ │ │ │ ├── AllocateMachineRoomNearby.java
│ │ │ │ │ ├── AllocateMessageQueueAveragely.java
│ │ │ │ │ ├── AllocateMessageQueueAveragelyByCircle.java
│ │ │ │ │ ├── AllocateMessageQueueByConfig.java
│ │ │ │ │ ├── AllocateMessageQueueByMachineRoom.java
│ │ │ │ │ └── AllocateMessageQueueConsistentHash.java
│ │ │ │ └── store/
│ │ │ │ ├── ControllableOffset.java
│ │ │ │ ├── LocalFileOffsetStore.java
│ │ │ │ ├── OffsetSerializeWrapper.java
│ │ │ │ ├── OffsetStore.java
│ │ │ │ ├── ReadOffsetType.java
│ │ │ │ └── RemoteBrokerOffsetStore.java
│ │ │ ├── exception/
│ │ │ │ ├── MQBrokerException.java
│ │ │ │ ├── MQClientException.java
│ │ │ │ ├── OffsetNotFoundException.java
│ │ │ │ └── RequestTimeoutException.java
│ │ │ ├── hook/
│ │ │ │ ├── CheckForbiddenContext.java
│ │ │ │ ├── CheckForbiddenHook.java
│ │ │ │ ├── ConsumeMessageContext.java
│ │ │ │ ├── ConsumeMessageHook.java
│ │ │ │ ├── EndTransactionContext.java
│ │ │ │ ├── EndTransactionHook.java
│ │ │ │ ├── FilterMessageContext.java
│ │ │ │ ├── FilterMessageHook.java
│ │ │ │ ├── SendMessageContext.java
│ │ │ │ └── SendMessageHook.java
│ │ │ ├── impl/
│ │ │ │ ├── ClientRemotingProcessor.java
│ │ │ │ ├── CommunicationMode.java
│ │ │ │ ├── FindBrokerResult.java
│ │ │ │ ├── MQAdminImpl.java
│ │ │ │ ├── MQClientAPIImpl.java
│ │ │ │ ├── MQClientManager.java
│ │ │ │ ├── admin/
│ │ │ │ │ └── MqClientAdminImpl.java
│ │ │ │ ├── consumer/
│ │ │ │ │ ├── AssignedMessageQueue.java
│ │ │ │ │ ├── ConsumeMessageConcurrentlyService.java
│ │ │ │ │ ├── ConsumeMessageOrderlyService.java
│ │ │ │ │ ├── ConsumeMessagePopConcurrentlyService.java
│ │ │ │ │ ├── ConsumeMessagePopOrderlyService.java
│ │ │ │ │ ├── ConsumeMessageService.java
│ │ │ │ │ ├── DefaultLitePullConsumerImpl.java
│ │ │ │ │ ├── DefaultMQPullConsumerImpl.java
│ │ │ │ │ ├── DefaultMQPushConsumerImpl.java
│ │ │ │ │ ├── MQConsumerInner.java
│ │ │ │ │ ├── MessageQueueLock.java
│ │ │ │ │ ├── MessageRequest.java
│ │ │ │ │ ├── PopProcessQueue.java
│ │ │ │ │ ├── PopRequest.java
│ │ │ │ │ ├── ProcessQueue.java
│ │ │ │ │ ├── PullAPIWrapper.java
│ │ │ │ │ ├── PullMessageService.java
│ │ │ │ │ ├── PullRequest.java
│ │ │ │ │ ├── PullResultExt.java
│ │ │ │ │ ├── RebalanceImpl.java
│ │ │ │ │ ├── RebalanceLitePullImpl.java
│ │ │ │ │ ├── RebalancePullImpl.java
│ │ │ │ │ ├── RebalancePushImpl.java
│ │ │ │ │ └── RebalanceService.java
│ │ │ │ ├── factory/
│ │ │ │ │ └── MQClientInstance.java
│ │ │ │ ├── mqclient/
│ │ │ │ │ ├── DoNothingClientRemotingProcessor.java
│ │ │ │ │ ├── MQClientAPIExt.java
│ │ │ │ │ └── MQClientAPIFactory.java
│ │ │ │ └── producer/
│ │ │ │ ├── DefaultMQProducerImpl.java
│ │ │ │ ├── MQProducerInner.java
│ │ │ │ └── TopicPublishInfo.java
│ │ │ ├── latency/
│ │ │ │ ├── LatencyFaultTolerance.java
│ │ │ │ ├── LatencyFaultToleranceImpl.java
│ │ │ │ ├── MQFaultStrategy.java
│ │ │ │ ├── Resolver.java
│ │ │ │ └── ServiceDetector.java
│ │ │ ├── lock/
│ │ │ │ └── ReadWriteCASLock.java
│ │ │ ├── producer/
│ │ │ │ ├── DefaultMQProducer.java
│ │ │ │ ├── LocalTransactionState.java
│ │ │ │ ├── MQProducer.java
│ │ │ │ ├── MessageQueueSelector.java
│ │ │ │ ├── ProduceAccumulator.java
│ │ │ │ ├── RequestCallback.java
│ │ │ │ ├── RequestFutureHolder.java
│ │ │ │ ├── RequestResponseFuture.java
│ │ │ │ ├── SendCallback.java
│ │ │ │ ├── SendResult.java
│ │ │ │ ├── SendStatus.java
│ │ │ │ ├── TransactionCheckListener.java
│ │ │ │ ├── TransactionListener.java
│ │ │ │ ├── TransactionMQProducer.java
│ │ │ │ ├── TransactionSendResult.java
│ │ │ │ └── selector/
│ │ │ │ ├── SelectMessageQueueByHash.java
│ │ │ │ ├── SelectMessageQueueByMachineRoom.java
│ │ │ │ └── SelectMessageQueueByRandom.java
│ │ │ ├── rpchook/
│ │ │ │ └── NamespaceRpcHook.java
│ │ │ ├── stat/
│ │ │ │ └── ConsumerStatsManager.java
│ │ │ ├── trace/
│ │ │ │ ├── AsyncTraceDispatcher.java
│ │ │ │ ├── TraceBean.java
│ │ │ │ ├── TraceConstants.java
│ │ │ │ ├── TraceContext.java
│ │ │ │ ├── TraceDataEncoder.java
│ │ │ │ ├── TraceDispatcher.java
│ │ │ │ ├── TraceDispatcherType.java
│ │ │ │ ├── TraceTransferBean.java
│ │ │ │ ├── TraceType.java
│ │ │ │ ├── TraceView.java
│ │ │ │ └── hook/
│ │ │ │ ├── ConsumeMessageOpenTracingHookImpl.java
│ │ │ │ ├── ConsumeMessageTraceHookImpl.java
│ │ │ │ ├── DefaultRecallMessageTraceHook.java
│ │ │ │ ├── EndTransactionOpenTracingHookImpl.java
│ │ │ │ ├── EndTransactionTraceHookImpl.java
│ │ │ │ ├── SendMessageOpenTracingHookImpl.java
│ │ │ │ └── SendMessageTraceHookImpl.java
│ │ │ └── utils/
│ │ │ └── MessageUtil.java
│ │ └── resources/
│ │ └── rmq.client.logback.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ ├── acl/
│ │ │ └── common/
│ │ │ ├── AclClientRPCHookTest.java
│ │ │ ├── AclSignerTest.java
│ │ │ ├── AclUtilsTest.java
│ │ │ ├── PermissionTest.java
│ │ │ └── SessionCredentialsTest.java
│ │ └── client/
│ │ ├── ClientConfigTest.java
│ │ ├── ValidatorsTest.java
│ │ ├── common/
│ │ │ └── ThreadLocalIndexTest.java
│ │ ├── consumer/
│ │ │ ├── DefaultLitePullConsumerTest.java
│ │ │ ├── DefaultMQPullConsumerTest.java
│ │ │ ├── DefaultMQPushConsumerTest.java
│ │ │ ├── rebalance/
│ │ │ │ ├── AllocateMachineRoomNearByTest.java
│ │ │ │ ├── AllocateMessageQueueAveragelyByCircleTest.java
│ │ │ │ ├── AllocateMessageQueueAveragelyTest.java
│ │ │ │ ├── AllocateMessageQueueByConfigTest.java
│ │ │ │ ├── AllocateMessageQueueByMachineRoomTest.java
│ │ │ │ └── AllocateMessageQueueConsitentHashTest.java
│ │ │ └── store/
│ │ │ ├── ControllableOffsetTest.java
│ │ │ ├── LocalFileOffsetStoreTest.java
│ │ │ └── RemoteBrokerOffsetStoreTest.java
│ │ ├── impl/
│ │ │ ├── ClientRemotingProcessorTest.java
│ │ │ ├── MQAdminImplTest.java
│ │ │ ├── MQClientAPIImplTest.java
│ │ │ ├── admin/
│ │ │ │ └── MqClientAdminImplTest.java
│ │ │ ├── consumer/
│ │ │ │ ├── ConsumeMessageConcurrentlyServiceTest.java
│ │ │ │ ├── ConsumeMessageOrderlyServiceTest.java
│ │ │ │ ├── ConsumeMessagePopConcurrentlyServiceTest.java
│ │ │ │ ├── ConsumeMessagePopOrderlyServiceTest.java
│ │ │ │ ├── DefaultLitePullConsumerImplTest.java
│ │ │ │ ├── DefaultMQPushConsumerImplTest.java
│ │ │ │ ├── PopProcessQueueTest.java
│ │ │ │ ├── ProcessQueueTest.java
│ │ │ │ ├── PullAPIWrapperTest.java
│ │ │ │ ├── PullMessageServiceTest.java
│ │ │ │ ├── RebalanceLitePullImplTest.java
│ │ │ │ └── RebalancePushImplTest.java
│ │ │ ├── factory/
│ │ │ │ └── MQClientInstanceTest.java
│ │ │ └── mqclient/
│ │ │ ├── MQClientAPIExtTest.java
│ │ │ └── MQClientAPITest.java
│ │ ├── latency/
│ │ │ └── LatencyFaultToleranceImplTest.java
│ │ ├── producer/
│ │ │ ├── DefaultMQProducerTest.java
│ │ │ ├── ProduceAccumulatorTest.java
│ │ │ ├── RequestResponseFutureTest.java
│ │ │ ├── SendResultTest.java
│ │ │ └── selector/
│ │ │ ├── DefaultMQProducerImplTest.java
│ │ │ ├── SelectMessageQueueByHashTest.java
│ │ │ ├── SelectMessageQueueByRandomTest.java
│ │ │ └── SelectMessageQueueRetryTest.java
│ │ ├── rpchook/
│ │ │ └── NamespaceRpcHookTest.java
│ │ ├── trace/
│ │ │ ├── DefaultMQConsumerWithOpenTracingTest.java
│ │ │ ├── DefaultMQConsumerWithTraceTest.java
│ │ │ ├── DefaultMQLitePullConsumerWithTraceTest.java
│ │ │ ├── DefaultMQProducerWithOpenTracingTest.java
│ │ │ ├── DefaultMQProducerWithTraceTest.java
│ │ │ ├── TraceDataEncoderTest.java
│ │ │ ├── TraceViewTest.java
│ │ │ ├── TransactionMQProducerWithOpenTracingTest.java
│ │ │ └── TransactionMQProducerWithTraceTest.java
│ │ └── utils/
│ │ └── MessageUtilsTest.java
│ └── resources/
│ ├── acl_hook/
│ │ └── plain_acl.yml
│ ├── conf/
│ │ └── plain_acl_incomplete.yml
│ ├── org/
│ │ └── powermock/
│ │ └── extensions/
│ │ └── configuration.properties
│ └── rmq.logback-test.xml
├── common/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── apache/
│ │ │ └── rocketmq/
│ │ │ └── common/
│ │ │ ├── AbortProcessException.java
│ │ │ ├── BoundaryType.java
│ │ │ ├── BrokerConfig.java
│ │ │ ├── BrokerConfigSingleton.java
│ │ │ ├── BrokerIdentity.java
│ │ │ ├── CheckRocksdbCqWriteResult.java
│ │ │ ├── ConfigManager.java
│ │ │ ├── ControllerConfig.java
│ │ │ ├── CountDownLatch2.java
│ │ │ ├── JraftConfig.java
│ │ │ ├── KeyBuilder.java
│ │ │ ├── LifecycleAwareServiceThread.java
│ │ │ ├── LockCallback.java
│ │ │ ├── MQVersion.java
│ │ │ ├── MixAll.java
│ │ │ ├── ObjectCreator.java
│ │ │ ├── OrderedConsumptionLevel.java
│ │ │ ├── Pair.java
│ │ │ ├── PopAckConstants.java
│ │ │ ├── ServiceState.java
│ │ │ ├── ServiceThread.java
│ │ │ ├── SubscriptionGroupAttributes.java
│ │ │ ├── SystemClock.java
│ │ │ ├── ThreadFactoryImpl.java
│ │ │ ├── TopicAttributes.java
│ │ │ ├── TopicConfig.java
│ │ │ ├── TopicFilterType.java
│ │ │ ├── TopicQueueId.java
│ │ │ ├── UnlockCallback.java
│ │ │ ├── UtilAll.java
│ │ │ ├── action/
│ │ │ │ ├── Action.java
│ │ │ │ └── RocketMQAction.java
│ │ │ ├── annotation/
│ │ │ │ └── ImportantField.java
│ │ │ ├── attribute/
│ │ │ │ ├── Attribute.java
│ │ │ │ ├── AttributeParser.java
│ │ │ │ ├── AttributeUtil.java
│ │ │ │ ├── BooleanAttribute.java
│ │ │ │ ├── CQType.java
│ │ │ │ ├── CleanupPolicy.java
│ │ │ │ ├── EnumAttribute.java
│ │ │ │ ├── LiteSubModel.java
│ │ │ │ ├── LongRangeAttribute.java
│ │ │ │ ├── StringAttribute.java
│ │ │ │ └── TopicMessageType.java
│ │ │ ├── chain/
│ │ │ │ ├── Handler.java
│ │ │ │ └── HandlerChain.java
│ │ │ ├── coldctr/
│ │ │ │ └── AccAndTimeStamp.java
│ │ │ ├── compression/
│ │ │ │ ├── CompressionType.java
│ │ │ │ ├── Compressor.java
│ │ │ │ ├── CompressorFactory.java
│ │ │ │ ├── Lz4Compressor.java
│ │ │ │ ├── ZlibCompressor.java
│ │ │ │ └── ZstdCompressor.java
│ │ │ ├── config/
│ │ │ │ ├── AbstractRocksDBStorage.java
│ │ │ │ ├── ConfigHelper.java
│ │ │ │ ├── ConfigManagerVersion.java
│ │ │ │ └── ConfigRocksDBStorage.java
│ │ │ ├── consistenthash/
│ │ │ │ ├── ConsistentHashRouter.java
│ │ │ │ ├── HashFunction.java
│ │ │ │ ├── Node.java
│ │ │ │ └── VirtualNode.java
│ │ │ ├── constant/
│ │ │ │ ├── CommonConstants.java
│ │ │ │ ├── ConsumeInitMode.java
│ │ │ │ ├── DBMsgConstants.java
│ │ │ │ ├── FIleReadaheadMode.java
│ │ │ │ ├── GrpcConstants.java
│ │ │ │ ├── HAProxyConstants.java
│ │ │ │ ├── LoggerName.java
│ │ │ │ └── PermName.java
│ │ │ ├── consumer/
│ │ │ │ ├── ConsumeFromWhere.java
│ │ │ │ └── ReceiptHandle.java
│ │ │ ├── entity/
│ │ │ │ ├── ClientGroup.java
│ │ │ │ └── TopicGroup.java
│ │ │ ├── fastjson/
│ │ │ │ └── GenericMapSuperclassDeserializer.java
│ │ │ ├── filter/
│ │ │ │ ├── ExpressionType.java
│ │ │ │ ├── FilterContext.java
│ │ │ │ ├── MessageFilter.java
│ │ │ │ └── impl/
│ │ │ │ ├── Op.java
│ │ │ │ ├── Operand.java
│ │ │ │ ├── Operator.java
│ │ │ │ ├── PolishExpr.java
│ │ │ │ └── Type.java
│ │ │ ├── future/
│ │ │ │ └── FutureTaskExt.java
│ │ │ ├── help/
│ │ │ │ └── FAQUrl.java
│ │ │ ├── hook/
│ │ │ │ └── FilterCheckHook.java
│ │ │ ├── lite/
│ │ │ │ ├── LiteLagInfo.java
│ │ │ │ ├── LiteSubscription.java
│ │ │ │ ├── LiteSubscriptionAction.java
│ │ │ │ ├── LiteSubscriptionDTO.java
│ │ │ │ ├── LiteUtil.java
│ │ │ │ └── OffsetOption.java
│ │ │ ├── logging/
│ │ │ │ ├── DefaultJoranConfiguratorExt.java
│ │ │ │ └── JoranConfiguratorExt.java
│ │ │ ├── message/
│ │ │ │ ├── Message.java
│ │ │ │ ├── MessageAccessor.java
│ │ │ │ ├── MessageBatch.java
│ │ │ │ ├── MessageClientExt.java
│ │ │ │ ├── MessageClientIDSetter.java
│ │ │ │ ├── MessageConst.java
│ │ │ │ ├── MessageDecoder.java
│ │ │ │ ├── MessageExt.java
│ │ │ │ ├── MessageExtBatch.java
│ │ │ │ ├── MessageExtBrokerInner.java
│ │ │ │ ├── MessageId.java
│ │ │ │ ├── MessageQueue.java
│ │ │ │ ├── MessageQueueAssignment.java
│ │ │ │ ├── MessageQueueForC.java
│ │ │ │ ├── MessageRequestMode.java
│ │ │ │ ├── MessageType.java
│ │ │ │ └── MessageVersion.java
│ │ │ ├── metrics/
│ │ │ │ ├── MetricsExporterType.java
│ │ │ │ ├── NopLongCounter.java
│ │ │ │ ├── NopLongHistogram.java
│ │ │ │ ├── NopLongUpDownCounter.java
│ │ │ │ ├── NopObservableDoubleGauge.java
│ │ │ │ └── NopObservableLongGauge.java
│ │ │ ├── namesrv/
│ │ │ │ ├── DefaultTopAddressing.java
│ │ │ │ ├── NameServerUpdateCallback.java
│ │ │ │ ├── NamesrvConfig.java
│ │ │ │ ├── NamesrvUtil.java
│ │ │ │ └── TopAddressing.java
│ │ │ ├── producer/
│ │ │ │ └── RecallMessageHandle.java
│ │ │ ├── queue/
│ │ │ │ ├── ConcurrentTreeMap.java
│ │ │ │ └── RoundQueue.java
│ │ │ ├── resource/
│ │ │ │ ├── ResourcePattern.java
│ │ │ │ ├── ResourceType.java
│ │ │ │ └── RocketMQResource.java
│ │ │ ├── running/
│ │ │ │ └── RunningStats.java
│ │ │ ├── state/
│ │ │ │ └── StateEventListener.java
│ │ │ ├── statistics/
│ │ │ │ ├── FutureHolder.java
│ │ │ │ ├── Interceptor.java
│ │ │ │ ├── StatisticsBrief.java
│ │ │ │ ├── StatisticsBriefInterceptor.java
│ │ │ │ ├── StatisticsItem.java
│ │ │ │ ├── StatisticsItemFormatter.java
│ │ │ │ ├── StatisticsItemPrinter.java
│ │ │ │ ├── StatisticsItemScheduledIncrementPrinter.java
│ │ │ │ ├── StatisticsItemScheduledPrinter.java
│ │ │ │ ├── StatisticsItemStateGetter.java
│ │ │ │ ├── StatisticsKindMeta.java
│ │ │ │ └── StatisticsManager.java
│ │ │ ├── stats/
│ │ │ │ ├── MomentStatsItem.java
│ │ │ │ ├── MomentStatsItemSet.java
│ │ │ │ ├── RTStatsItem.java
│ │ │ │ ├── Stats.java
│ │ │ │ ├── StatsItem.java
│ │ │ │ ├── StatsItemSet.java
│ │ │ │ └── StatsSnapshot.java
│ │ │ ├── sysflag/
│ │ │ │ ├── MessageSysFlag.java
│ │ │ │ ├── PullSysFlag.java
│ │ │ │ ├── SubscriptionSysFlag.java
│ │ │ │ └── TopicSysFlag.java
│ │ │ ├── thread/
│ │ │ │ ├── FutureTaskExtThreadPoolExecutor.java
│ │ │ │ ├── ThreadPoolMonitor.java
│ │ │ │ ├── ThreadPoolQueueSizeMonitor.java
│ │ │ │ ├── ThreadPoolStatusMonitor.java
│ │ │ │ └── ThreadPoolWrapper.java
│ │ │ ├── topic/
│ │ │ │ └── TopicValidator.java
│ │ │ └── utils/
│ │ │ ├── AbstractStartAndShutdown.java
│ │ │ ├── AsyncShutdownHelper.java
│ │ │ ├── BinaryUtil.java
│ │ │ ├── ChannelUtil.java
│ │ │ ├── CheckpointFile.java
│ │ │ ├── CleanupPolicyUtils.java
│ │ │ ├── ConcurrentHashMapUtils.java
│ │ │ ├── CorrelationIdUtil.java
│ │ │ ├── DataConverter.java
│ │ │ ├── ExceptionUtils.java
│ │ │ ├── FastJsonSerializer.java
│ │ │ ├── FutureUtils.java
│ │ │ ├── HttpTinyClient.java
│ │ │ ├── IOTinyUtils.java
│ │ │ ├── IPAddressUtils.java
│ │ │ ├── MessageUtils.java
│ │ │ ├── NameServerAddressUtils.java
│ │ │ ├── NetworkUtil.java
│ │ │ ├── PositiveAtomicCounter.java
│ │ │ ├── QueueTypeUtils.java
│ │ │ ├── Serializer.java
│ │ │ ├── ServiceProvider.java
│ │ │ ├── Shutdown.java
│ │ │ ├── Start.java
│ │ │ ├── StartAndShutdown.java
│ │ │ └── ThreadUtils.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── services/
│ │ └── org.apache.rocketmq.logging.ch.qos.logback.classic.spi.Configurator
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── common/
│ │ ├── BrokerConfigSingletonTest.java
│ │ ├── BrokerConfigTest.java
│ │ ├── ConfigManagerTest.java
│ │ ├── CountDownLatch2Test.java
│ │ ├── KeyBuilderTest.java
│ │ ├── MQVersionTest.java
│ │ ├── MessageBatchTest.java
│ │ ├── MessageEncodeDecodeTest.java
│ │ ├── MessageExtBrokerInnerTest.java
│ │ ├── MixAllTest.java
│ │ ├── NetworkUtilTest.java
│ │ ├── ServiceThreadTest.java
│ │ ├── TopicConfigTest.java
│ │ ├── UtilAllTest.java
│ │ ├── action/
│ │ │ ├── ActionTest.java
│ │ │ └── RocketMQActionTest.java
│ │ ├── attribute/
│ │ │ ├── AttributeParserTest.java
│ │ │ ├── AttributeTest.java
│ │ │ ├── AttributeUtilTest.java
│ │ │ ├── BooleanAttributeTest.java
│ │ │ ├── CQTypeTest.java
│ │ │ ├── CleanupPolicyTest.java
│ │ │ ├── EnumAttributeTest.java
│ │ │ ├── LongRangeAttributeTest.java
│ │ │ └── TopicMessageTypeTest.java
│ │ ├── chain/
│ │ │ └── HandlerChainTest.java
│ │ ├── coldctr/
│ │ │ └── AccAndTimeStampTest.java
│ │ ├── compression/
│ │ │ ├── CompressionTest.java
│ │ │ ├── CompressionTypeTest.java
│ │ │ ├── CompressorFactoryTest.java
│ │ │ ├── Lz4CompressorTest.java
│ │ │ ├── ZlibCompressorTest.java
│ │ │ └── ZstdCompressorTest.java
│ │ ├── config/
│ │ │ └── ConfigHelperTest.java
│ │ ├── consumer/
│ │ │ └── ReceiptHandleTest.java
│ │ ├── fastjson/
│ │ │ └── GenericMapSuperclassDeserializerTest.java
│ │ ├── help/
│ │ │ └── FAQUrlTest.java
│ │ ├── message/
│ │ │ ├── MessageClientIDSetterTest.java
│ │ │ ├── MessageDecoderTest.java
│ │ │ └── MessageTest.java
│ │ ├── producer/
│ │ │ └── RecallMessageHandleTest.java
│ │ ├── stats/
│ │ │ └── StatsItemSetTest.java
│ │ ├── sysflag/
│ │ │ ├── CompressionFlagTest.java
│ │ │ └── PullSysFlagTest.java
│ │ ├── topic/
│ │ │ └── TopicValidatorTest.java
│ │ └── utils/
│ │ ├── ConcurrentHashMapUtilsTest.java
│ │ ├── IOTinyUtilsTest.java
│ │ ├── IPAddressUtilsTest.java
│ │ ├── LiteUtilTest.java
│ │ └── NameServerAddressUtilsTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── container/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── container/
│ │ ├── BrokerBootHook.java
│ │ ├── BrokerContainer.java
│ │ ├── BrokerContainerConfig.java
│ │ ├── BrokerContainerProcessor.java
│ │ ├── BrokerContainerStartup.java
│ │ ├── ContainerClientHouseKeepingService.java
│ │ ├── IBrokerContainer.java
│ │ ├── InnerBrokerController.java
│ │ └── InnerSalveBrokerController.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── container/
│ │ ├── BrokerContainerExtensibilityTest.java
│ │ ├── BrokerContainerStartupTest.java
│ │ ├── BrokerContainerTest.java
│ │ └── BrokerPreOnlineServiceTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── controller/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── apache/
│ │ │ └── rocketmq/
│ │ │ └── controller/
│ │ │ ├── BrokerHeartbeatManager.java
│ │ │ ├── BrokerHousekeepingService.java
│ │ │ ├── Controller.java
│ │ │ ├── ControllerManager.java
│ │ │ ├── ControllerStartup.java
│ │ │ ├── elect/
│ │ │ │ ├── ElectPolicy.java
│ │ │ │ └── impl/
│ │ │ │ └── DefaultElectPolicy.java
│ │ │ ├── helper/
│ │ │ │ ├── BrokerLifecycleListener.java
│ │ │ │ ├── BrokerLiveInfoGetter.java
│ │ │ │ └── BrokerValidPredicate.java
│ │ │ ├── impl/
│ │ │ │ ├── DLedgerController.java
│ │ │ │ ├── DLedgerControllerStateMachine.java
│ │ │ │ ├── JRaftController.java
│ │ │ │ ├── JRaftControllerStateMachine.java
│ │ │ │ ├── closure/
│ │ │ │ │ └── ControllerClosure.java
│ │ │ │ ├── event/
│ │ │ │ │ ├── AlterSyncStateSetEvent.java
│ │ │ │ │ ├── ApplyBrokerIdEvent.java
│ │ │ │ │ ├── CleanBrokerDataEvent.java
│ │ │ │ │ ├── ControllerResult.java
│ │ │ │ │ ├── ElectMasterEvent.java
│ │ │ │ │ ├── EventMessage.java
│ │ │ │ │ ├── EventSerializer.java
│ │ │ │ │ ├── EventType.java
│ │ │ │ │ ├── ListEventSerializer.java
│ │ │ │ │ └── UpdateBrokerAddressEvent.java
│ │ │ │ ├── heartbeat/
│ │ │ │ │ ├── BrokerIdentityInfo.java
│ │ │ │ │ ├── BrokerLiveInfo.java
│ │ │ │ │ ├── DefaultBrokerHeartbeatManager.java
│ │ │ │ │ └── RaftBrokerHeartBeatManager.java
│ │ │ │ ├── manager/
│ │ │ │ │ ├── BrokerReplicaInfo.java
│ │ │ │ │ ├── RaftReplicasInfoManager.java
│ │ │ │ │ ├── ReplicasInfoManager.java
│ │ │ │ │ └── SyncStateInfo.java
│ │ │ │ └── task/
│ │ │ │ ├── BrokerCloseChannelRequest.java
│ │ │ │ ├── BrokerCloseChannelResponse.java
│ │ │ │ ├── CheckNotActiveBrokerRequest.java
│ │ │ │ ├── CheckNotActiveBrokerResponse.java
│ │ │ │ ├── GetBrokerLiveInfoRequest.java
│ │ │ │ ├── GetBrokerLiveInfoResponse.java
│ │ │ │ ├── GetSyncStateDataRequest.java
│ │ │ │ ├── RaftBrokerHeartBeatEventRequest.java
│ │ │ │ └── RaftBrokerHeartBeatEventResponse.java
│ │ │ ├── metrics/
│ │ │ │ ├── ControllerMetricsConstant.java
│ │ │ │ └── ControllerMetricsManager.java
│ │ │ └── processor/
│ │ │ └── ControllerRequestProcessor.java
│ │ └── resources/
│ │ └── rmq.controller.logback.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── controller/
│ │ ├── ControllerManagerTest.java
│ │ ├── ControllerRequestProcessorTest.java
│ │ ├── ControllerTestBase.java
│ │ └── impl/
│ │ ├── DLedgerControllerTest.java
│ │ ├── DefaultBrokerHeartbeatManagerTest.java
│ │ ├── RaftBrokerHeartBeatManagerTest.java
│ │ ├── event/
│ │ │ ├── EventSerializerTest.java
│ │ │ └── ListEventSerializerTest.java
│ │ ├── heartbeat/
│ │ │ └── RaftBrokerHeartBeatManagerTest.java
│ │ └── manager/
│ │ ├── RaftReplicasInfoManagerTest.java
│ │ └── ReplicasInfoManagerTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── dev/
│ └── merge_rocketmq_pr.py
├── distribution/
│ ├── LICENSE-BIN
│ ├── NOTICE-BIN
│ ├── benchmark/
│ │ ├── batchproducer.sh
│ │ ├── consumer.sh
│ │ ├── producer.sh
│ │ ├── runclass.sh
│ │ ├── shutdown.sh
│ │ └── tproducer.sh
│ ├── bin/
│ │ ├── README.md
│ │ ├── cachedog.sh
│ │ ├── cleancache.sh
│ │ ├── cleancache.v1.sh
│ │ ├── controller/
│ │ │ ├── fast-try-independent-deployment.cmd
│ │ │ ├── fast-try-independent-deployment.sh
│ │ │ ├── fast-try-namesrv-plugin.cmd
│ │ │ ├── fast-try-namesrv-plugin.sh
│ │ │ ├── fast-try.cmd
│ │ │ └── fast-try.sh
│ │ ├── dledger/
│ │ │ └── fast-try.sh
│ │ ├── export.sh
│ │ ├── mqadmin
│ │ ├── mqadmin.cmd
│ │ ├── mqbroker
│ │ ├── mqbroker.cmd
│ │ ├── mqbroker.numanode0
│ │ ├── mqbroker.numanode1
│ │ ├── mqbroker.numanode2
│ │ ├── mqbroker.numanode3
│ │ ├── mqbrokercontainer
│ │ ├── mqcontroller
│ │ ├── mqcontroller.cmd
│ │ ├── mqnamesrv
│ │ ├── mqnamesrv.cmd
│ │ ├── mqproxy
│ │ ├── mqproxy.cmd
│ │ ├── mqshutdown
│ │ ├── mqshutdown.cmd
│ │ ├── os.sh
│ │ ├── play.cmd
│ │ ├── play.sh
│ │ ├── runbroker.cmd
│ │ ├── runbroker.sh
│ │ ├── runserver.cmd
│ │ ├── runserver.sh
│ │ ├── setcache.sh
│ │ ├── startfsrv.sh
│ │ ├── tools.cmd
│ │ └── tools.sh
│ ├── conf/
│ │ ├── 2m-2s-async/
│ │ │ ├── broker-a-s.properties
│ │ │ ├── broker-a.properties
│ │ │ ├── broker-b-s.properties
│ │ │ └── broker-b.properties
│ │ ├── 2m-2s-sync/
│ │ │ ├── broker-a-s.properties
│ │ │ ├── broker-a.properties
│ │ │ ├── broker-b-s.properties
│ │ │ └── broker-b.properties
│ │ ├── 2m-noslave/
│ │ │ ├── broker-a.properties
│ │ │ ├── broker-b.properties
│ │ │ └── broker-trace.properties
│ │ ├── broker.conf
│ │ ├── container/
│ │ │ └── 2container-2m-2s/
│ │ │ ├── broker-a-in-container1.conf
│ │ │ ├── broker-a-in-container2.conf
│ │ │ ├── broker-b-in-container1.conf
│ │ │ ├── broker-b-in-container2.conf
│ │ │ ├── broker-container1.conf
│ │ │ ├── broker-container2.conf
│ │ │ └── nameserver.conf
│ │ ├── controller/
│ │ │ ├── cluster-3n-independent/
│ │ │ │ ├── controller-n0.conf
│ │ │ │ ├── controller-n1.conf
│ │ │ │ └── controller-n2.conf
│ │ │ ├── cluster-3n-namesrv-plugin/
│ │ │ │ ├── namesrv-n0.conf
│ │ │ │ ├── namesrv-n1.conf
│ │ │ │ └── namesrv-n2.conf
│ │ │ ├── controller-standalone.conf
│ │ │ └── quick-start/
│ │ │ ├── broker-n0.conf
│ │ │ ├── broker-n1.conf
│ │ │ └── namesrv.conf
│ │ ├── dledger/
│ │ │ ├── broker-n0.conf
│ │ │ ├── broker-n1.conf
│ │ │ └── broker-n2.conf
│ │ ├── rmq-proxy.json
│ │ └── tools.yml
│ ├── pom.xml
│ ├── release-client.xml
│ └── release.xml
├── docs/
│ ├── cn/
│ │ ├── BrokerContainer.md
│ │ ├── Configuration_System.md
│ │ ├── Configuration_TLS.md
│ │ ├── Debug_In_Idea.md
│ │ ├── Deployment.md
│ │ ├── Example_Batch.md
│ │ ├── Example_Compaction_Topic_cn.md
│ │ ├── Example_CreateTopic.md
│ │ ├── Example_Delay.md
│ │ ├── Example_LMQ.md
│ │ ├── Example_Simple_cn.md
│ │ ├── FAQ.md
│ │ ├── QuorumACK.md
│ │ ├── README.md
│ │ ├── RocketMQ_Example.md
│ │ ├── SlaveActingMasterMode.md
│ │ ├── acl/
│ │ │ ├── RocketMQ_Multiple_ACL_Files_设计.md
│ │ │ └── user_guide.md
│ │ ├── architecture.md
│ │ ├── best_practice.md
│ │ ├── client/
│ │ │ └── java/
│ │ │ ├── API_Reference_ DefaultPullConsumer.md
│ │ │ └── API_Reference_DefaultMQProducer.md
│ │ ├── concept.md
│ │ ├── controller/
│ │ │ ├── deploy.md
│ │ │ ├── design.md
│ │ │ ├── persistent_unique_broker_id.md
│ │ │ └── quick_start.md
│ │ ├── design.md
│ │ ├── dledger/
│ │ │ ├── deploy_guide.md
│ │ │ └── quick_start.md
│ │ ├── features.md
│ │ ├── msg_trace/
│ │ │ └── user_guide.md
│ │ ├── operation.md
│ │ ├── proxy/
│ │ │ └── deploy_guide.md
│ │ ├── rpc_request.md
│ │ └── statictopic/
│ │ ├── RocketMQ_Static_Topic_Logic_Queue_设计.md
│ │ └── The_Scope_Of_Static_Topic.md
│ └── en/
│ ├── CLITools.md
│ ├── Concept.md
│ ├── Configuration_Client.md
│ ├── Configuration_System.md
│ ├── Configuration_TLS.md
│ ├── Debug_In_Idea.md
│ ├── Deployment.md
│ ├── Design_Filter.md
│ ├── Design_LoadBlancing.md
│ ├── Design_Query.md
│ ├── Design_Remoting.md
│ ├── Design_Store.md
│ ├── Design_Trancation.md
│ ├── Example_Batch.md
│ ├── Example_Compaction_Topic.md
│ ├── Example_CreateTopic.md
│ ├── Example_Delay.md
│ ├── Example_Filter.md
│ ├── Example_OpenMessaging.md
│ ├── Example_Orderly.md
│ ├── Example_Simple.md
│ ├── Example_Transaction.md
│ ├── FAQ.md
│ ├── Feature.md
│ ├── Operations_Broker.md
│ ├── Operations_Consumer.md
│ ├── Operations_Producer.md
│ ├── Operations_Trace.md
│ ├── QuorumACK.md
│ ├── README.md
│ ├── RocketMQ_Example.md
│ ├── Troubleshoopting.md
│ ├── acl/
│ │ └── Operations_ACL.md
│ ├── architecture.md
│ ├── best_practice.md
│ ├── client/
│ │ └── java/
│ │ └── API_Reference_DefaultMQProducer.md
│ ├── controller/
│ │ ├── deploy.md
│ │ ├── design.md
│ │ ├── persistent_unique_broker_id.md
│ │ └── quick_start.md
│ ├── design.md
│ ├── dledger/
│ │ ├── deploy_guide.md
│ │ └── quick_start.md
│ ├── msg_trace/
│ │ └── user_guide.md
│ ├── operation.md
│ └── proxy/
│ └── deploy_guide.md
├── example/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── org/
│ └── apache/
│ └── rocketmq/
│ └── example/
│ ├── batch/
│ │ ├── SimpleBatchProducer.java
│ │ └── SplitBatchProducer.java
│ ├── benchmark/
│ │ ├── AclClient.java
│ │ ├── BatchProducer.java
│ │ ├── Consumer.java
│ │ ├── Producer.java
│ │ ├── TransactionProducer.java
│ │ └── timer/
│ │ ├── TimerConsumer.java
│ │ └── TimerProducer.java
│ ├── broadcast/
│ │ └── PushConsumer.java
│ ├── filter/
│ │ ├── SqlFilterConsumer.java
│ │ ├── SqlFilterProducer.java
│ │ ├── TagFilterConsumer.java
│ │ └── TagFilterProducer.java
│ ├── lmq/
│ │ ├── LMQProducer.java
│ │ ├── LMQPullConsumer.java
│ │ ├── LMQPushConsumer.java
│ │ └── LMQPushPopConsumer.java
│ ├── namespace/
│ │ ├── ProducerWithNamespace.java
│ │ ├── PullConsumerWithNamespace.java
│ │ └── PushConsumerWithNamespace.java
│ ├── openmessaging/
│ │ ├── SimpleProducer.java
│ │ ├── SimplePullConsumer.java
│ │ └── SimplePushConsumer.java
│ ├── operation/
│ │ ├── Consumer.java
│ │ └── Producer.java
│ ├── ordermessage/
│ │ ├── Consumer.java
│ │ └── Producer.java
│ ├── quickstart/
│ │ ├── Consumer.java
│ │ └── Producer.java
│ ├── rpc/
│ │ ├── AsyncRequestProducer.java
│ │ ├── RequestProducer.java
│ │ └── ResponseConsumer.java
│ ├── schedule/
│ │ ├── ScheduledMessageConsumer.java
│ │ ├── ScheduledMessageProducer.java
│ │ ├── TimerMessageConsumer.java
│ │ └── TimerMessageProducer.java
│ ├── simple/
│ │ ├── AclClient.java
│ │ ├── AsyncProducer.java
│ │ ├── CachedQueue.java
│ │ ├── LitePullConsumerAssign.java
│ │ ├── LitePullConsumerAssignWithSubExpression.java
│ │ ├── LitePullConsumerSubscribe.java
│ │ ├── OnewayProducer.java
│ │ ├── PopConsumer.java
│ │ ├── Producer.java
│ │ ├── PullConsumer.java
│ │ ├── PullScheduleService.java
│ │ ├── PushConsumer.java
│ │ └── RandomAsyncCommit.java
│ ├── tracemessage/
│ │ ├── OpenTracingProducer.java
│ │ ├── OpenTracingPushConsumer.java
│ │ ├── OpenTracingTransactionProducer.java
│ │ ├── TraceProducer.java
│ │ └── TracePushConsumer.java
│ └── transaction/
│ ├── TransactionListenerImpl.java
│ └── TransactionProducer.java
├── filter/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── filter/
│ │ ├── FilterFactory.java
│ │ ├── FilterSpi.java
│ │ ├── SqlFilter.java
│ │ ├── constant/
│ │ │ └── UnaryType.java
│ │ ├── expression/
│ │ │ ├── BinaryExpression.java
│ │ │ ├── BooleanConstantExpression.java
│ │ │ ├── BooleanExpression.java
│ │ │ ├── ComparisonExpression.java
│ │ │ ├── ConstantExpression.java
│ │ │ ├── EmptyEvaluationContext.java
│ │ │ ├── EvaluationContext.java
│ │ │ ├── Expression.java
│ │ │ ├── LogicExpression.java
│ │ │ ├── MQFilterException.java
│ │ │ ├── NowExpression.java
│ │ │ ├── PropertyExpression.java
│ │ │ ├── UnaryExpression.java
│ │ │ └── UnaryInExpression.java
│ │ ├── parser/
│ │ │ ├── ParseException.java
│ │ │ ├── SelectorParser.java
│ │ │ ├── SelectorParser.jj
│ │ │ ├── SelectorParserConstants.java
│ │ │ ├── SelectorParserTokenManager.java
│ │ │ ├── SimpleCharStream.java
│ │ │ ├── Token.java
│ │ │ └── TokenMgrError.java
│ │ └── util/
│ │ ├── BitsArray.java
│ │ ├── BloomFilter.java
│ │ └── BloomFilterData.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── filter/
│ │ ├── BitsArrayTest.java
│ │ ├── BloomFilterTest.java
│ │ ├── ExpressionTest.java
│ │ ├── FilterSpiTest.java
│ │ └── ParserTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── namesrv/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── apache/
│ │ │ └── rocketmq/
│ │ │ └── namesrv/
│ │ │ ├── NamesrvController.java
│ │ │ ├── NamesrvStartup.java
│ │ │ ├── kvconfig/
│ │ │ │ ├── KVConfigManager.java
│ │ │ │ └── KVConfigSerializeWrapper.java
│ │ │ ├── processor/
│ │ │ │ ├── ClientRequestProcessor.java
│ │ │ │ ├── ClusterTestRequestProcessor.java
│ │ │ │ └── DefaultRequestProcessor.java
│ │ │ ├── route/
│ │ │ │ └── ZoneRouteRPCHook.java
│ │ │ └── routeinfo/
│ │ │ ├── BatchUnregistrationService.java
│ │ │ ├── BrokerHousekeepingService.java
│ │ │ └── RouteInfoManager.java
│ │ └── resources/
│ │ └── rmq.namesrv.logback.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── namesrv/
│ │ ├── NameServerInstanceTest.java
│ │ ├── NamesrvControllerTest.java
│ │ ├── NamesrvStartupTest.java
│ │ ├── kvconfig/
│ │ │ ├── KVConfigManagerTest.java
│ │ │ └── KVConfigSerializeWrapperTest.java
│ │ ├── processor/
│ │ │ ├── ClientRequestProcessorTest.java
│ │ │ ├── ClusterTestRequestProcessorTest.java
│ │ │ └── RequestProcessorTest.java
│ │ ├── route/
│ │ │ ├── ZoneRouteRPCHookMoreTest.java
│ │ │ └── ZoneRouteRPCHookTest.java
│ │ └── routeinfo/
│ │ ├── BrokerHousekeepingServiceTest.java
│ │ ├── GetRouteInfoBenchmark.java
│ │ ├── RegisterBrokerBenchmark.java
│ │ ├── RouteInfoManagerBrokerPermTest.java
│ │ ├── RouteInfoManagerBrokerRegisterTest.java
│ │ ├── RouteInfoManagerNewTest.java
│ │ ├── RouteInfoManagerStaticRegisterTest.java
│ │ ├── RouteInfoManagerTest.java
│ │ └── RouteInfoManagerTestBase.java
│ └── resources/
│ └── rmq.logback-test.xml
├── openmessaging/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── io/
│ │ └── openmessaging/
│ │ └── rocketmq/
│ │ ├── MessagingAccessPointImpl.java
│ │ ├── config/
│ │ │ └── ClientConfig.java
│ │ ├── consumer/
│ │ │ ├── LocalMessageCache.java
│ │ │ ├── PullConsumerImpl.java
│ │ │ └── PushConsumerImpl.java
│ │ ├── domain/
│ │ │ ├── BytesMessageImpl.java
│ │ │ ├── ConsumeRequest.java
│ │ │ ├── NonStandardKeys.java
│ │ │ ├── RocketMQConstants.java
│ │ │ └── SendResultImpl.java
│ │ ├── producer/
│ │ │ ├── AbstractOMSProducer.java
│ │ │ └── ProducerImpl.java
│ │ ├── promise/
│ │ │ ├── DefaultPromise.java
│ │ │ └── FutureState.java
│ │ └── utils/
│ │ ├── BeanUtils.java
│ │ └── OMSUtil.java
│ └── test/
│ ├── java/
│ │ └── io/
│ │ └── openmessaging/
│ │ └── rocketmq/
│ │ ├── consumer/
│ │ │ ├── LocalMessageCacheTest.java
│ │ │ ├── PullConsumerImplTest.java
│ │ │ └── PushConsumerImplTest.java
│ │ ├── producer/
│ │ │ └── ProducerImplTest.java
│ │ ├── promise/
│ │ │ └── DefaultPromiseTest.java
│ │ └── utils/
│ │ └── BeanUtilsTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── pom.xml
├── proxy/
│ ├── BUILD.bazel
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── apache/
│ │ │ └── rocketmq/
│ │ │ └── proxy/
│ │ │ ├── CommandLineArgument.java
│ │ │ ├── ProxyMode.java
│ │ │ ├── ProxyStartup.java
│ │ │ ├── auth/
│ │ │ │ ├── ProxyAuthenticationMetadataProvider.java
│ │ │ │ └── ProxyAuthorizationMetadataProvider.java
│ │ │ ├── common/
│ │ │ │ ├── AbstractCacheLoader.java
│ │ │ │ ├── Address.java
│ │ │ │ ├── ContextVariable.java
│ │ │ │ ├── MessageReceiptHandle.java
│ │ │ │ ├── ProxyContext.java
│ │ │ │ ├── ProxyException.java
│ │ │ │ ├── ProxyExceptionCode.java
│ │ │ │ ├── ReceiptHandleGroup.java
│ │ │ │ ├── ReceiptHandleGroupKey.java
│ │ │ │ ├── RenewEvent.java
│ │ │ │ ├── RenewStrategyPolicy.java
│ │ │ │ ├── channel/
│ │ │ │ │ └── ChannelHelper.java
│ │ │ │ └── utils/
│ │ │ │ ├── FilterUtils.java
│ │ │ │ ├── GrpcUtils.java
│ │ │ │ └── ProxyUtils.java
│ │ │ ├── config/
│ │ │ │ ├── ConfigFile.java
│ │ │ │ ├── Configuration.java
│ │ │ │ ├── ConfigurationManager.java
│ │ │ │ ├── MetricCollectorMode.java
│ │ │ │ └── ProxyConfig.java
│ │ │ ├── grpc/
│ │ │ │ ├── GrpcServer.java
│ │ │ │ ├── GrpcServerBuilder.java
│ │ │ │ ├── ProxyAndTlsProtocolNegotiator.java
│ │ │ │ ├── constant/
│ │ │ │ │ └── AttributeKeys.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── ContextInterceptor.java
│ │ │ │ │ ├── GlobalExceptionInterceptor.java
│ │ │ │ │ ├── HeaderInterceptor.java
│ │ │ │ │ └── RequestMapping.java
│ │ │ │ ├── pipeline/
│ │ │ │ │ ├── AuthenticationPipeline.java
│ │ │ │ │ ├── AuthorizationPipeline.java
│ │ │ │ │ ├── ContextInitPipeline.java
│ │ │ │ │ └── RequestPipeline.java
│ │ │ │ └── v2/
│ │ │ │ ├── AbstractMessagingActivity.java
│ │ │ │ ├── ContextStreamObserver.java
│ │ │ │ ├── DefaultGrpcMessagingActivity.java
│ │ │ │ ├── GrpcMessagingActivity.java
│ │ │ │ ├── GrpcMessagingApplication.java
│ │ │ │ ├── channel/
│ │ │ │ │ ├── GrpcChannelManager.java
│ │ │ │ │ └── GrpcClientChannel.java
│ │ │ │ ├── client/
│ │ │ │ │ └── ClientActivity.java
│ │ │ │ ├── common/
│ │ │ │ │ ├── GrpcClientSettingsManager.java
│ │ │ │ │ ├── GrpcConverter.java
│ │ │ │ │ ├── GrpcProxyException.java
│ │ │ │ │ ├── GrpcValidator.java
│ │ │ │ │ ├── ResponseBuilder.java
│ │ │ │ │ └── ResponseWriter.java
│ │ │ │ ├── consumer/
│ │ │ │ │ ├── AckMessageActivity.java
│ │ │ │ │ ├── ChangeInvisibleDurationActivity.java
│ │ │ │ │ ├── PopMessageResultFilterImpl.java
│ │ │ │ │ ├── ReceiveMessageActivity.java
│ │ │ │ │ └── ReceiveMessageResponseStreamWriter.java
│ │ │ │ ├── producer/
│ │ │ │ │ ├── ForwardMessageToDLQActivity.java
│ │ │ │ │ ├── RecallMessageActivity.java
│ │ │ │ │ └── SendMessageActivity.java
│ │ │ │ ├── route/
│ │ │ │ │ └── RouteActivity.java
│ │ │ │ └── transaction/
│ │ │ │ └── EndTransactionActivity.java
│ │ │ ├── metrics/
│ │ │ │ ├── ProxyMetricsConstant.java
│ │ │ │ └── ProxyMetricsManager.java
│ │ │ ├── processor/
│ │ │ │ ├── AbstractProcessor.java
│ │ │ │ ├── BatchAckResult.java
│ │ │ │ ├── ClientProcessor.java
│ │ │ │ ├── ConsumerProcessor.java
│ │ │ │ ├── DefaultMessagingProcessor.java
│ │ │ │ ├── MessagingProcessor.java
│ │ │ │ ├── PopMessageResultFilter.java
│ │ │ │ ├── ProducerProcessor.java
│ │ │ │ ├── QueueSelector.java
│ │ │ │ ├── ReceiptHandleProcessor.java
│ │ │ │ ├── RequestBrokerProcessor.java
│ │ │ │ ├── TransactionProcessor.java
│ │ │ │ ├── TransactionStatus.java
│ │ │ │ ├── channel/
│ │ │ │ │ ├── ChannelExtendAttributeGetter.java
│ │ │ │ │ ├── ChannelProtocolType.java
│ │ │ │ │ ├── RemoteChannel.java
│ │ │ │ │ ├── RemoteChannelConverter.java
│ │ │ │ │ └── RemoteChannelSerializer.java
│ │ │ │ └── validator/
│ │ │ │ ├── DefaultTopicMessageTypeValidator.java
│ │ │ │ └── TopicMessageTypeValidator.java
│ │ │ ├── remoting/
│ │ │ │ ├── ClientHousekeepingService.java
│ │ │ │ ├── MultiProtocolRemotingServer.java
│ │ │ │ ├── MultiProtocolTlsHelper.java
│ │ │ │ ├── RemotingProtocolServer.java
│ │ │ │ ├── RemotingProxyOutClient.java
│ │ │ │ ├── activity/
│ │ │ │ │ ├── AbstractRemotingActivity.java
│ │ │ │ │ ├── AckMessageActivity.java
│ │ │ │ │ ├── ChangeInvisibleTimeActivity.java
│ │ │ │ │ ├── ClientManagerActivity.java
│ │ │ │ │ ├── ConsumerManagerActivity.java
│ │ │ │ │ ├── GetTopicRouteActivity.java
│ │ │ │ │ ├── PopMessageActivity.java
│ │ │ │ │ ├── PullMessageActivity.java
│ │ │ │ │ ├── RecallMessageActivity.java
│ │ │ │ │ ├── SendMessageActivity.java
│ │ │ │ │ └── TransactionActivity.java
│ │ │ │ ├── channel/
│ │ │ │ │ ├── RemotingChannel.java
│ │ │ │ │ └── RemotingChannelManager.java
│ │ │ │ ├── common/
│ │ │ │ │ └── RemotingConverter.java
│ │ │ │ ├── pipeline/
│ │ │ │ │ ├── AuthenticationPipeline.java
│ │ │ │ │ ├── AuthorizationPipeline.java
│ │ │ │ │ ├── ContextInitPipeline.java
│ │ │ │ │ └── RequestPipeline.java
│ │ │ │ └── protocol/
│ │ │ │ ├── ProtocolHandler.java
│ │ │ │ ├── ProtocolNegotiationHandler.java
│ │ │ │ ├── http2proxy/
│ │ │ │ │ ├── HAProxyMessageForwarder.java
│ │ │ │ │ ├── Http2ProtocolProxyHandler.java
│ │ │ │ │ ├── Http2ProxyBackendHandler.java
│ │ │ │ │ └── Http2ProxyFrontendHandler.java
│ │ │ │ └── remoting/
│ │ │ │ └── RemotingProtocolHandler.java
│ │ │ └── service/
│ │ │ ├── ClusterServiceManager.java
│ │ │ ├── LocalServiceManager.java
│ │ │ ├── ServiceManager.java
│ │ │ ├── ServiceManagerFactory.java
│ │ │ ├── admin/
│ │ │ │ ├── AdminService.java
│ │ │ │ └── DefaultAdminService.java
│ │ │ ├── cert/
│ │ │ │ └── TlsCertificateManager.java
│ │ │ ├── channel/
│ │ │ │ ├── ChannelManager.java
│ │ │ │ ├── InvocationChannel.java
│ │ │ │ ├── InvocationContext.java
│ │ │ │ ├── InvocationContextInterface.java
│ │ │ │ ├── SimpleChannel.java
│ │ │ │ └── SimpleChannelHandlerContext.java
│ │ │ ├── client/
│ │ │ │ ├── ClusterConsumerManager.java
│ │ │ │ └── ProxyClientRemotingProcessor.java
│ │ │ ├── lite/
│ │ │ │ └── LiteSubscriptionService.java
│ │ │ ├── message/
│ │ │ │ ├── ClusterMessageService.java
│ │ │ │ ├── LocalMessageService.java
│ │ │ │ ├── LocalRemotingCommand.java
│ │ │ │ ├── MessageService.java
│ │ │ │ └── ReceiptHandleMessage.java
│ │ │ ├── metadata/
│ │ │ │ ├── ClusterMetadataService.java
│ │ │ │ ├── LocalMetadataService.java
│ │ │ │ └── MetadataService.java
│ │ │ ├── receipt/
│ │ │ │ ├── DefaultReceiptHandleManager.java
│ │ │ │ └── ReceiptHandleManager.java
│ │ │ ├── relay/
│ │ │ │ ├── AbstractProxyRelayService.java
│ │ │ │ ├── ClusterProxyRelayService.java
│ │ │ │ ├── LocalProxyRelayService.java
│ │ │ │ ├── ProxyChannel.java
│ │ │ │ ├── ProxyRelayResult.java
│ │ │ │ ├── ProxyRelayService.java
│ │ │ │ └── RelayData.java
│ │ │ ├── route/
│ │ │ │ ├── AddressableMessageQueue.java
│ │ │ │ ├── ClusterTopicRouteService.java
│ │ │ │ ├── DefaultMessageQueuePriorityProvider.java
│ │ │ │ ├── LocalTopicRouteService.java
│ │ │ │ ├── MessageQueuePenalizer.java
│ │ │ │ ├── MessageQueuePriorityProvider.java
│ │ │ │ ├── MessageQueueSelector.java
│ │ │ │ ├── MessageQueueView.java
│ │ │ │ ├── ProxyTopicRouteData.java
│ │ │ │ ├── TopicRouteHelper.java
│ │ │ │ ├── TopicRouteService.java
│ │ │ │ └── TopicRouteWrapper.java
│ │ │ ├── sysmessage/
│ │ │ │ ├── AbstractSystemMessageSyncer.java
│ │ │ │ ├── HeartbeatSyncer.java
│ │ │ │ ├── HeartbeatSyncerData.java
│ │ │ │ └── HeartbeatType.java
│ │ │ └── transaction/
│ │ │ ├── AbstractTransactionService.java
│ │ │ ├── ClusterTransactionService.java
│ │ │ ├── EndTransactionRequestData.java
│ │ │ ├── LocalTransactionService.java
│ │ │ ├── TransactionData.java
│ │ │ ├── TransactionDataManager.java
│ │ │ └── TransactionService.java
│ │ └── resources/
│ │ └── rmq.proxy.logback.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── proxy/
│ │ ├── ProxyStartupTest.java
│ │ ├── common/
│ │ │ ├── AddressTest.java
│ │ │ ├── ReceiptHandleGroupTest.java
│ │ │ ├── RenewStrategyPolicyTest.java
│ │ │ └── utils/
│ │ │ └── FilterUtilTest.java
│ │ ├── config/
│ │ │ ├── ConfigurationManagerTest.java
│ │ │ ├── ConfigurationTest.java
│ │ │ ├── InitConfigTest.java
│ │ │ └── MetricCollectorModeTest.java
│ │ ├── grpc/
│ │ │ ├── ProxyAndTlsProtocolNegotiatorTest.java
│ │ │ └── v2/
│ │ │ ├── AbstractMessagingActivityTest.java
│ │ │ ├── BaseActivityTest.java
│ │ │ ├── GrpcMessagingApplicationTest.java
│ │ │ ├── channel/
│ │ │ │ └── GrpcClientChannelTest.java
│ │ │ ├── client/
│ │ │ │ └── ClientActivityTest.java
│ │ │ ├── common/
│ │ │ │ ├── GrpcClientSettingsManagerTest.java
│ │ │ │ ├── GrpcConverterTest.java
│ │ │ │ └── GrpcValidatorTest.java
│ │ │ ├── consumer/
│ │ │ │ ├── AckMessageActivityTest.java
│ │ │ │ ├── ChangeInvisibleDurationActivityTest.java
│ │ │ │ ├── ReceiveMessageActivityTest.java
│ │ │ │ └── ReceiveMessageResponseStreamWriterTest.java
│ │ │ ├── producer/
│ │ │ │ ├── ForwardMessageToDLQActivityTest.java
│ │ │ │ ├── RecallMessageActivityTest.java
│ │ │ │ └── SendMessageActivityTest.java
│ │ │ ├── route/
│ │ │ │ └── RouteActivityTest.java
│ │ │ └── transaction/
│ │ │ └── EndTransactionActivityTest.java
│ │ ├── processor/
│ │ │ ├── BaseProcessorTest.java
│ │ │ ├── ClientProcessorTest.java
│ │ │ ├── ConsumerProcessorTest.java
│ │ │ ├── ProducerProcessorTest.java
│ │ │ ├── ReceiptHandleProcessorTest.java
│ │ │ ├── TransactionProcessorTest.java
│ │ │ └── channel/
│ │ │ └── RemoteChannelTest.java
│ │ ├── remoting/
│ │ │ ├── activity/
│ │ │ │ ├── AbstractRemotingActivityTest.java
│ │ │ │ ├── GetTopicRouteActivityTest.java
│ │ │ │ ├── PullMessageActivityTest.java
│ │ │ │ ├── RecallMessageActivityTest.java
│ │ │ │ └── SendMessageActivityTest.java
│ │ │ ├── channel/
│ │ │ │ ├── RemotingChannelManagerTest.java
│ │ │ │ └── RemotingChannelTest.java
│ │ │ └── protocol/
│ │ │ └── http2proxy/
│ │ │ ├── HAProxyMessageForwarderTest.java
│ │ │ └── Http2ProtocolProxyHandlerTest.java
│ │ └── service/
│ │ ├── BaseServiceTest.java
│ │ ├── admin/
│ │ │ └── DefaultAdminServiceTest.java
│ │ ├── cert/
│ │ │ └── TlsCertificateManagerTest.java
│ │ ├── lite/
│ │ │ └── LiteSubscriptionServiceTest.java
│ │ ├── message/
│ │ │ ├── ClusterMessageServiceTest.java
│ │ │ └── LocalMessageServiceTest.java
│ │ ├── metadata/
│ │ │ └── ClusterMetadataServiceTest.java
│ │ ├── mqclient/
│ │ │ ├── MQClientAPIExtTest.java
│ │ │ └── ProxyClientRemotingProcessorTest.java
│ │ ├── receipt/
│ │ │ └── DefaultReceiptHandleManagerTest.java
│ │ ├── relay/
│ │ │ ├── LocalProxyRelayServiceTest.java
│ │ │ └── ProxyChannelTest.java
│ │ ├── route/
│ │ │ ├── ClusterTopicRouteServiceTest.java
│ │ │ ├── LocalTopicRouteServiceTest.java
│ │ │ ├── MessageQueuePenalizerTest.java
│ │ │ ├── MessageQueuePriorityProviderTest.java
│ │ │ └── MessageQueueSelectorTest.java
│ │ ├── sysmessage/
│ │ │ └── HeartbeatSyncerTest.java
│ │ └── transaction/
│ │ ├── AbstractTransactionServiceTest.java
│ │ ├── ClusterTransactionServiceTest.java
│ │ └── TransactionDataManagerTest.java
│ └── resources/
│ ├── certs/
│ │ ├── client.key
│ │ ├── client.pem
│ │ ├── server.key
│ │ └── server.pem
│ ├── mockito-extensions/
│ │ └── org.mockito.plugins.MockMaker
│ ├── rmq-proxy-home/
│ │ └── conf/
│ │ ├── broker.conf
│ │ ├── logback_proxy.xml
│ │ └── rmq-proxy.json
│ └── rmq.logback-test.xml
├── remoting/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── remoting/
│ │ ├── ChannelEventListener.java
│ │ ├── CommandCallback.java
│ │ ├── CommandCustomHeader.java
│ │ ├── Configuration.java
│ │ ├── InvokeCallback.java
│ │ ├── RPCHook.java
│ │ ├── RemotingClient.java
│ │ ├── RemotingServer.java
│ │ ├── RemotingService.java
│ │ ├── annotation/
│ │ │ ├── CFNotNull.java
│ │ │ └── CFNullable.java
│ │ ├── common/
│ │ │ ├── HeartbeatV2Result.java
│ │ │ ├── RemotingHelper.java
│ │ │ ├── SemaphoreReleaseOnlyOnce.java
│ │ │ ├── ServiceThread.java
│ │ │ └── TlsMode.java
│ │ ├── exception/
│ │ │ ├── RemotingCommandException.java
│ │ │ ├── RemotingConnectException.java
│ │ │ ├── RemotingException.java
│ │ │ ├── RemotingSendRequestException.java
│ │ │ ├── RemotingTimeoutException.java
│ │ │ └── RemotingTooMuchRequestException.java
│ │ ├── metrics/
│ │ │ ├── RemotingMetricsConstant.java
│ │ │ └── RemotingMetricsManager.java
│ │ ├── netty/
│ │ │ ├── AttributeKeys.java
│ │ │ ├── FileRegionEncoder.java
│ │ │ ├── NettyClientConfig.java
│ │ │ ├── NettyDecoder.java
│ │ │ ├── NettyEncoder.java
│ │ │ ├── NettyEvent.java
│ │ │ ├── NettyEventType.java
│ │ │ ├── NettyLogger.java
│ │ │ ├── NettyRemotingAbstract.java
│ │ │ ├── NettyRemotingClient.java
│ │ │ ├── NettyRemotingServer.java
│ │ │ ├── NettyRequestProcessor.java
│ │ │ ├── NettyServerConfig.java
│ │ │ ├── NettySystemConfig.java
│ │ │ ├── RemotingCodeDistributionHandler.java
│ │ │ ├── RemotingResponseCallback.java
│ │ │ ├── RequestTask.java
│ │ │ ├── ResponseFuture.java
│ │ │ ├── TlsHelper.java
│ │ │ └── TlsSystemConfig.java
│ │ ├── pipeline/
│ │ │ └── RequestPipeline.java
│ │ ├── protocol/
│ │ │ ├── BitSetSerializerDeserializer.java
│ │ │ ├── BrokerSyncInfo.java
│ │ │ ├── DataVersion.java
│ │ │ ├── EpochEntry.java
│ │ │ ├── FastCodesHeader.java
│ │ │ ├── ForbiddenType.java
│ │ │ ├── LanguageCode.java
│ │ │ ├── MQProtosHelper.java
│ │ │ ├── NamespaceUtil.java
│ │ │ ├── RemotingCommand.java
│ │ │ ├── RemotingCommandType.java
│ │ │ ├── RemotingSerializable.java
│ │ │ ├── RemotingSysResponseCode.java
│ │ │ ├── RequestCode.java
│ │ │ ├── RequestHeaderRegistry.java
│ │ │ ├── RequestSource.java
│ │ │ ├── RequestType.java
│ │ │ ├── ResponseCode.java
│ │ │ ├── RocketMQSerializable.java
│ │ │ ├── SerializeType.java
│ │ │ ├── admin/
│ │ │ │ ├── ConsumeStats.java
│ │ │ │ ├── OffsetWrapper.java
│ │ │ │ ├── RollbackStats.java
│ │ │ │ ├── TopicOffset.java
│ │ │ │ └── TopicStatsTable.java
│ │ │ ├── body/
│ │ │ │ ├── AclInfo.java
│ │ │ │ ├── BatchAck.java
│ │ │ │ ├── BatchAckMessageRequestBody.java
│ │ │ │ ├── BrokerMemberGroup.java
│ │ │ │ ├── BrokerReplicasInfo.java
│ │ │ │ ├── BrokerStatsData.java
│ │ │ │ ├── BrokerStatsItem.java
│ │ │ │ ├── CMResult.java
│ │ │ │ ├── CheckClientRequestBody.java
│ │ │ │ ├── ClusterInfo.java
│ │ │ │ ├── Connection.java
│ │ │ │ ├── ConsumeByWho.java
│ │ │ │ ├── ConsumeMessageDirectlyResult.java
│ │ │ │ ├── ConsumeQueueData.java
│ │ │ │ ├── ConsumeStatsList.java
│ │ │ │ ├── ConsumeStatus.java
│ │ │ │ ├── ConsumerConnection.java
│ │ │ │ ├── ConsumerOffsetSerializeWrapper.java
│ │ │ │ ├── ConsumerRunningInfo.java
│ │ │ │ ├── CreateTopicListRequestBody.java
│ │ │ │ ├── ElectMasterResponseBody.java
│ │ │ │ ├── EpochEntryCache.java
│ │ │ │ ├── GetBrokerLiteInfoResponseBody.java
│ │ │ │ ├── GetBrokerMemberGroupResponseBody.java
│ │ │ │ ├── GetConsumerStatusBody.java
│ │ │ │ ├── GetLiteClientInfoResponseBody.java
│ │ │ │ ├── GetLiteGroupInfoResponseBody.java
│ │ │ │ ├── GetLiteTopicInfoResponseBody.java
│ │ │ │ ├── GetParentTopicInfoResponseBody.java
│ │ │ │ ├── GroupList.java
│ │ │ │ ├── HARuntimeInfo.java
│ │ │ │ ├── KVTable.java
│ │ │ │ ├── LiteSubscriptionCtlRequestBody.java
│ │ │ │ ├── LockBatchRequestBody.java
│ │ │ │ ├── LockBatchResponseBody.java
│ │ │ │ ├── MessageRequestModeSerializeWrapper.java
│ │ │ │ ├── PopProcessQueueInfo.java
│ │ │ │ ├── ProcessQueueInfo.java
│ │ │ │ ├── ProducerConnection.java
│ │ │ │ ├── ProducerInfo.java
│ │ │ │ ├── ProducerTableInfo.java
│ │ │ │ ├── QueryAssignmentRequestBody.java
│ │ │ │ ├── QueryAssignmentResponseBody.java
│ │ │ │ ├── QueryConsumeQueueResponseBody.java
│ │ │ │ ├── QueryConsumeTimeSpanBody.java
│ │ │ │ ├── QueryCorrectionOffsetBody.java
│ │ │ │ ├── QuerySubscriptionResponseBody.java
│ │ │ │ ├── QueueTimeSpan.java
│ │ │ │ ├── RegisterBrokerBody.java
│ │ │ │ ├── ResetOffsetBody.java
│ │ │ │ ├── ResetOffsetBodyForC.java
│ │ │ │ ├── RoleChangeNotifyEntry.java
│ │ │ │ ├── SetMessageRequestModeRequestBody.java
│ │ │ │ ├── SubscriptionGroupList.java
│ │ │ │ ├── SubscriptionGroupWrapper.java
│ │ │ │ ├── SyncStateSet.java
│ │ │ │ ├── TopicConfigAndMappingSerializeWrapper.java
│ │ │ │ ├── TopicConfigSerializeWrapper.java
│ │ │ │ ├── TopicList.java
│ │ │ │ ├── TopicQueueMappingSerializeWrapper.java
│ │ │ │ ├── UnlockBatchRequestBody.java
│ │ │ │ └── UserInfo.java
│ │ │ ├── filter/
│ │ │ │ └── FilterAPI.java
│ │ │ ├── header/
│ │ │ │ ├── AckMessageRequestHeader.java
│ │ │ │ ├── AddBrokerRequestHeader.java
│ │ │ │ ├── ChangeInvisibleTimeRequestHeader.java
│ │ │ │ ├── ChangeInvisibleTimeResponseHeader.java
│ │ │ │ ├── CheckRocksdbCqWriteProgressRequestHeader.java
│ │ │ │ ├── CheckTransactionStateRequestHeader.java
│ │ │ │ ├── CheckTransactionStateResponseHeader.java
│ │ │ │ ├── CloneGroupOffsetRequestHeader.java
│ │ │ │ ├── ConsumeMessageDirectlyResultRequestHeader.java
│ │ │ │ ├── ConsumerSendMsgBackRequestHeader.java
│ │ │ │ ├── CreateAclRequestHeader.java
│ │ │ │ ├── CreateTopicListRequestHeader.java
│ │ │ │ ├── CreateTopicRequestHeader.java
│ │ │ │ ├── CreateUserRequestHeader.java
│ │ │ │ ├── DeleteAclRequestHeader.java
│ │ │ │ ├── DeleteSubscriptionGroupRequestHeader.java
│ │ │ │ ├── DeleteTopicRequestHeader.java
│ │ │ │ ├── DeleteUserRequestHeader.java
│ │ │ │ ├── EndTransactionRequestHeader.java
│ │ │ │ ├── EndTransactionResponseHeader.java
│ │ │ │ ├── ExchangeHAInfoRequestHeader.java
│ │ │ │ ├── ExchangeHAInfoResponseHeader.java
│ │ │ │ ├── ExportRocksDBConfigToJsonRequestHeader.java
│ │ │ │ ├── ExtraInfoUtil.java
│ │ │ │ ├── GetAclRequestHeader.java
│ │ │ │ ├── GetAllProducerInfoRequestHeader.java
│ │ │ │ ├── GetAllSubscriptionGroupRequestHeader.java
│ │ │ │ ├── GetAllSubscriptionGroupResponseHeader.java
│ │ │ │ ├── GetAllTopicConfigRequestHeader.java
│ │ │ │ ├── GetAllTopicConfigResponseHeader.java
│ │ │ │ ├── GetBrokerConfigResponseHeader.java
│ │ │ │ ├── GetBrokerMemberGroupRequestHeader.java
│ │ │ │ ├── GetConsumeStatsInBrokerHeader.java
│ │ │ │ ├── GetConsumeStatsRequestHeader.java
│ │ │ │ ├── GetConsumerConnectionListRequestHeader.java
│ │ │ │ ├── GetConsumerListByGroupRequestHeader.java
│ │ │ │ ├── GetConsumerListByGroupResponseBody.java
│ │ │ │ ├── GetConsumerListByGroupResponseHeader.java
│ │ │ │ ├── GetConsumerRunningInfoRequestHeader.java
│ │ │ │ ├── GetConsumerStatusRequestHeader.java
│ │ │ │ ├── GetEarliestMsgStoretimeRequestHeader.java
│ │ │ │ ├── GetEarliestMsgStoretimeResponseHeader.java
│ │ │ │ ├── GetLiteClientInfoRequestHeader.java
│ │ │ │ ├── GetLiteGroupInfoRequestHeader.java
│ │ │ │ ├── GetLiteTopicInfoRequestHeader.java
│ │ │ │ ├── GetMaxOffsetRequestHeader.java
│ │ │ │ ├── GetMaxOffsetResponseHeader.java
│ │ │ │ ├── GetMinOffsetRequestHeader.java
│ │ │ │ ├── GetMinOffsetResponseHeader.java
│ │ │ │ ├── GetParentTopicInfoRequestHeader.java
│ │ │ │ ├── GetProducerConnectionListRequestHeader.java
│ │ │ │ ├── GetSubscriptionGroupConfigRequestHeader.java
│ │ │ │ ├── GetTopicConfigRequestHeader.java
│ │ │ │ ├── GetTopicStatsInfoRequestHeader.java
│ │ │ │ ├── GetTopicsByClusterRequestHeader.java
│ │ │ │ ├── GetUserRequestHeader.java
│ │ │ │ ├── HeartbeatRequestHeader.java
│ │ │ │ ├── InitConsumerOffsetRequestHeader.java
│ │ │ │ ├── ListAclsRequestHeader.java
│ │ │ │ ├── ListUsersRequestHeader.java
│ │ │ │ ├── LiteSubscriptionCtlRequestHeader.java
│ │ │ │ ├── LockBatchMqRequestHeader.java
│ │ │ │ ├── NotificationRequestHeader.java
│ │ │ │ ├── NotificationResponseHeader.java
│ │ │ │ ├── NotifyBrokerRoleChangedRequestHeader.java
│ │ │ │ ├── NotifyConsumerIdsChangedRequestHeader.java
│ │ │ │ ├── NotifyMinBrokerIdChangeRequestHeader.java
│ │ │ │ ├── NotifyUnsubscribeLiteRequestHeader.java
│ │ │ │ ├── PeekMessageRequestHeader.java
│ │ │ │ ├── PollingInfoRequestHeader.java
│ │ │ │ ├── PollingInfoResponseHeader.java
│ │ │ │ ├── PopLiteMessageRequestHeader.java
│ │ │ │ ├── PopLiteMessageResponseHeader.java
│ │ │ │ ├── PopMessageRequestHeader.java
│ │ │ │ ├── PopMessageResponseHeader.java
│ │ │ │ ├── PullMessageRequestHeader.java
│ │ │ │ ├── PullMessageResponseHeader.java
│ │ │ │ ├── QueryConsumeQueueRequestHeader.java
│ │ │ │ ├── QueryConsumeTimeSpanRequestHeader.java
│ │ │ │ ├── QueryConsumerOffsetRequestHeader.java
│ │ │ │ ├── QueryConsumerOffsetResponseHeader.java
│ │ │ │ ├── QueryCorrectionOffsetHeader.java
│ │ │ │ ├── QueryMessageRequestHeader.java
│ │ │ │ ├── QueryMessageResponseHeader.java
│ │ │ │ ├── QuerySubscriptionByConsumerRequestHeader.java
│ │ │ │ ├── QueryTopicConsumeByWhoRequestHeader.java
│ │ │ │ ├── QueryTopicsByConsumerRequestHeader.java
│ │ │ │ ├── RecallMessageRequestHeader.java
│ │ │ │ ├── RecallMessageResponseHeader.java
│ │ │ │ ├── RemoveBrokerRequestHeader.java
│ │ │ │ ├── ReplyMessageRequestHeader.java
│ │ │ │ ├── ResetMasterFlushOffsetHeader.java
│ │ │ │ ├── ResetOffsetRequestHeader.java
│ │ │ │ ├── ResumeCheckHalfMessageRequestHeader.java
│ │ │ │ ├── SearchOffsetRequestHeader.java
│ │ │ │ ├── SearchOffsetResponseHeader.java
│ │ │ │ ├── SendMessageRequestHeader.java
│ │ │ │ ├── SendMessageRequestHeaderV2.java
│ │ │ │ ├── SendMessageResponseHeader.java
│ │ │ │ ├── StatisticsMessagesRequestHeader.java
│ │ │ │ ├── TriggerLiteDispatchRequestHeader.java
│ │ │ │ ├── UnlockBatchMqRequestHeader.java
│ │ │ │ ├── UnregisterClientRequestHeader.java
│ │ │ │ ├── UnregisterClientResponseHeader.java
│ │ │ │ ├── UpdateAclRequestHeader.java
│ │ │ │ ├── UpdateConsumerOffsetRequestHeader.java
│ │ │ │ ├── UpdateConsumerOffsetResponseHeader.java
│ │ │ │ ├── UpdateGroupForbiddenRequestHeader.java
│ │ │ │ ├── UpdateUserRequestHeader.java
│ │ │ │ ├── ViewBrokerStatsDataRequestHeader.java
│ │ │ │ ├── ViewMessageRequestHeader.java
│ │ │ │ ├── ViewMessageResponseHeader.java
│ │ │ │ ├── controller/
│ │ │ │ │ ├── AlterSyncStateSetRequestHeader.java
│ │ │ │ │ ├── AlterSyncStateSetResponseHeader.java
│ │ │ │ │ ├── ElectMasterRequestHeader.java
│ │ │ │ │ ├── ElectMasterResponseHeader.java
│ │ │ │ │ ├── GetMetaDataResponseHeader.java
│ │ │ │ │ ├── GetReplicaInfoRequestHeader.java
│ │ │ │ │ ├── GetReplicaInfoResponseHeader.java
│ │ │ │ │ ├── admin/
│ │ │ │ │ │ └── CleanControllerBrokerDataRequestHeader.java
│ │ │ │ │ └── register/
│ │ │ │ │ ├── ApplyBrokerIdRequestHeader.java
│ │ │ │ │ ├── ApplyBrokerIdResponseHeader.java
│ │ │ │ │ ├── GetNextBrokerIdRequestHeader.java
│ │ │ │ │ ├── GetNextBrokerIdResponseHeader.java
│ │ │ │ │ ├── RegisterBrokerToControllerRequestHeader.java
│ │ │ │ │ └── RegisterBrokerToControllerResponseHeader.java
│ │ │ │ └── namesrv/
│ │ │ │ ├── AddWritePermOfBrokerRequestHeader.java
│ │ │ │ ├── AddWritePermOfBrokerResponseHeader.java
│ │ │ │ ├── BrokerHeartbeatRequestHeader.java
│ │ │ │ ├── DeleteKVConfigRequestHeader.java
│ │ │ │ ├── DeleteTopicFromNamesrvRequestHeader.java
│ │ │ │ ├── GetKVConfigRequestHeader.java
│ │ │ │ ├── GetKVConfigResponseHeader.java
│ │ │ │ ├── GetKVListByNamespaceRequestHeader.java
│ │ │ │ ├── GetRouteInfoRequestHeader.java
│ │ │ │ ├── PutKVConfigRequestHeader.java
│ │ │ │ ├── QueryDataVersionRequestHeader.java
│ │ │ │ ├── QueryDataVersionResponseHeader.java
│ │ │ │ ├── RegisterBrokerRequestHeader.java
│ │ │ │ ├── RegisterBrokerResponseHeader.java
│ │ │ │ ├── RegisterOrderTopicRequestHeader.java
│ │ │ │ ├── RegisterTopicRequestHeader.java
│ │ │ │ ├── UnRegisterBrokerRequestHeader.java
│ │ │ │ ├── WipeWritePermOfBrokerRequestHeader.java
│ │ │ │ └── WipeWritePermOfBrokerResponseHeader.java
│ │ │ ├── heartbeat/
│ │ │ │ ├── ConsumeType.java
│ │ │ │ ├── ConsumerData.java
│ │ │ │ ├── HeartbeatData.java
│ │ │ │ ├── MessageModel.java
│ │ │ │ ├── ProducerData.java
│ │ │ │ └── SubscriptionData.java
│ │ │ ├── namesrv/
│ │ │ │ └── RegisterBrokerResult.java
│ │ │ ├── route/
│ │ │ │ ├── BrokerData.java
│ │ │ │ ├── MessageQueueRouteState.java
│ │ │ │ ├── QueueData.java
│ │ │ │ └── TopicRouteData.java
│ │ │ ├── statictopic/
│ │ │ │ ├── LogicQueueMappingItem.java
│ │ │ │ ├── TopicConfigAndQueueMapping.java
│ │ │ │ ├── TopicQueueMappingContext.java
│ │ │ │ ├── TopicQueueMappingDetail.java
│ │ │ │ ├── TopicQueueMappingInfo.java
│ │ │ │ ├── TopicQueueMappingOne.java
│ │ │ │ ├── TopicQueueMappingUtils.java
│ │ │ │ └── TopicRemappingDetailWrapper.java
│ │ │ ├── subscription/
│ │ │ │ ├── CustomizedRetryPolicy.java
│ │ │ │ ├── ExponentialRetryPolicy.java
│ │ │ │ ├── GroupForbidden.java
│ │ │ │ ├── GroupRetryPolicy.java
│ │ │ │ ├── GroupRetryPolicyType.java
│ │ │ │ ├── RetryPolicy.java
│ │ │ │ ├── SimpleSubscriptionData.java
│ │ │ │ └── SubscriptionGroupConfig.java
│ │ │ └── topic/
│ │ │ └── OffsetMovedEvent.java
│ │ ├── proxy/
│ │ │ └── SocksProxyConfig.java
│ │ ├── rpc/
│ │ │ ├── ClientMetadata.java
│ │ │ ├── RequestBuilder.java
│ │ │ ├── RpcClient.java
│ │ │ ├── RpcClientHook.java
│ │ │ ├── RpcClientImpl.java
│ │ │ ├── RpcClientUtils.java
│ │ │ ├── RpcException.java
│ │ │ ├── RpcRequest.java
│ │ │ ├── RpcRequestHeader.java
│ │ │ ├── RpcResponse.java
│ │ │ ├── TopicQueueRequestHeader.java
│ │ │ └── TopicRequestHeader.java
│ │ └── rpchook/
│ │ ├── DynamicalExtFieldRPCHook.java
│ │ └── StreamTypeRPCHook.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── remoting/
│ │ ├── ProxyProtocolTest.java
│ │ ├── RemotingServerTest.java
│ │ ├── SubRemotingServerTest.java
│ │ ├── TlsTest.java
│ │ ├── netty/
│ │ │ ├── FileRegionEncoderTest.java
│ │ │ ├── MockChannel.java
│ │ │ ├── MockChannelPromise.java
│ │ │ ├── NettyClientConfigTest.java
│ │ │ ├── NettyRemotingAbstractTest.java
│ │ │ ├── NettyRemotingClientTest.java
│ │ │ ├── NettyRemotingServerTest.java
│ │ │ ├── NettyServerConfigTest.java
│ │ │ └── RemotingCodeDistributionHandlerTest.java
│ │ ├── protocol/
│ │ │ ├── CheckpointFileTest.java
│ │ │ ├── ClusterInfoTest.java
│ │ │ ├── ConsumeStatusTest.java
│ │ │ ├── DataVersionTest.java
│ │ │ ├── GroupListTest.java
│ │ │ ├── LanguageCodeTest.java
│ │ │ ├── NamespaceUtilTest.java
│ │ │ ├── QueryConsumeTimeSpanBodyTest.java
│ │ │ ├── RegisterBrokerBodyTest.java
│ │ │ ├── RemotingCommandTest.java
│ │ │ ├── RemotingSerializableCompatTest.java
│ │ │ ├── RemotingSerializableTest.java
│ │ │ ├── RequestSourceTest.java
│ │ │ ├── RequestTypeTest.java
│ │ │ ├── RocketMQSerializableTest.java
│ │ │ ├── admin/
│ │ │ │ ├── ConsumeStatsTest.java
│ │ │ │ └── TopicStatsTableTest.java
│ │ │ ├── body/
│ │ │ │ ├── BatchAckTest.java
│ │ │ │ ├── BrokerStatsDataTest.java
│ │ │ │ ├── CheckClientRequestBodyTest.java
│ │ │ │ ├── ConsumeMessageDirectlyResultTest.java
│ │ │ │ ├── ConsumeStatsListTest.java
│ │ │ │ ├── ConsumerConnectionTest.java
│ │ │ │ ├── ConsumerRunningInfoTest.java
│ │ │ │ ├── KVTableTest.java
│ │ │ │ ├── MessageRequestModeSerializeWrapperTest.java
│ │ │ │ ├── QueryConsumeQueueResponseBodyTest.java
│ │ │ │ ├── QueryCorrectionOffsetBodyTest.java
│ │ │ │ ├── ResetOffsetBodyTest.java
│ │ │ │ └── SubscriptionGroupWrapperTest.java
│ │ │ ├── filter/
│ │ │ │ └── FilterAPITest.java
│ │ │ ├── header/
│ │ │ │ ├── ExportRocksDBConfigToJsonRequestHeaderTest.java
│ │ │ │ ├── ExtraInfoUtilTest.java
│ │ │ │ ├── FastCodesHeaderTest.java
│ │ │ │ ├── GetConsumeStatsRequestHeaderTest.java
│ │ │ │ └── SendMessageRequestHeaderV2Test.java
│ │ │ ├── heartbeat/
│ │ │ │ └── SubscriptionDataTest.java
│ │ │ ├── route/
│ │ │ │ └── TopicRouteDataTest.java
│ │ │ ├── statictopic/
│ │ │ │ ├── TopicQueueMappingTest.java
│ │ │ │ └── TopicQueueMappingUtilsTest.java
│ │ │ ├── subscription/
│ │ │ │ ├── CustomizedRetryPolicyTest.java
│ │ │ │ ├── ExponentialRetryPolicyTest.java
│ │ │ │ ├── GroupRetryPolicyTest.java
│ │ │ │ └── SimpleSubscriptionDataTest.java
│ │ │ └── topic/
│ │ │ └── OffsetMovedEventTest.java
│ │ └── rpc/
│ │ ├── ClientMetadataTest.java
│ │ ├── RpcClientImplTest.java
│ │ └── RpcRequestHeaderTest.java
│ └── resources/
│ ├── certs/
│ │ ├── badClient.key
│ │ ├── badClient.pem
│ │ ├── badServer.key
│ │ ├── badServer.pem
│ │ ├── ca.pem
│ │ ├── client.key
│ │ ├── client.pem
│ │ ├── privkey.pem
│ │ ├── server.key
│ │ └── server.pem
│ └── rmq.logback-test.xml
├── srvutil/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── srvutil/
│ │ ├── FileWatchService.java
│ │ ├── ServerUtil.java
│ │ └── ShutdownHookThread.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── srvutil/
│ │ └── FileWatchServiceTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── store/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── store/
│ │ ├── AllocateMappedFileService.java
│ │ ├── AppendMessageCallback.java
│ │ ├── AppendMessageResult.java
│ │ ├── AppendMessageStatus.java
│ │ ├── CommitLog.java
│ │ ├── CommitLogDispatchStore.java
│ │ ├── CommitLogDispatcher.java
│ │ ├── CompactionAppendMsgCallback.java
│ │ ├── ConsumeQueue.java
│ │ ├── ConsumeQueueExt.java
│ │ ├── DefaultMessageFilter.java
│ │ ├── DefaultMessageStore.java
│ │ ├── DispatchRequest.java
│ │ ├── FileQueueSnapshot.java
│ │ ├── FlushDiskWatcher.java
│ │ ├── FlushManager.java
│ │ ├── GetMessageResult.java
│ │ ├── GetMessageStatus.java
│ │ ├── LmqDispatch.java
│ │ ├── MappedFileQueue.java
│ │ ├── MessageArrivingListener.java
│ │ ├── MessageExtEncoder.java
│ │ ├── MessageFilter.java
│ │ ├── MessageStore.java
│ │ ├── MessageStoreStateMachine.java
│ │ ├── MultiPathMappedFileQueue.java
│ │ ├── PutMessageContext.java
│ │ ├── PutMessageLock.java
│ │ ├── PutMessageReentrantLock.java
│ │ ├── PutMessageResult.java
│ │ ├── PutMessageSpinLock.java
│ │ ├── PutMessageStatus.java
│ │ ├── QueryMessageResult.java
│ │ ├── ReferenceResource.java
│ │ ├── RocksDBMessageStore.java
│ │ ├── RunningFlags.java
│ │ ├── SelectMappedBufferResult.java
│ │ ├── SelectMappedFileResult.java
│ │ ├── StoreCheckpoint.java
│ │ ├── StoreStatsService.java
│ │ ├── StoreType.java
│ │ ├── StoreUtil.java
│ │ ├── Swappable.java
│ │ ├── TopicQueueLock.java
│ │ ├── TransientStorePool.java
│ │ ├── config/
│ │ │ ├── BrokerRole.java
│ │ │ ├── FlushDiskType.java
│ │ │ ├── MessageStoreConfig.java
│ │ │ └── StorePathConfigHelper.java
│ │ ├── dledger/
│ │ │ └── DLedgerCommitLog.java
│ │ ├── exception/
│ │ │ ├── ConsumeQueueException.java
│ │ │ └── StoreException.java
│ │ ├── ha/
│ │ │ ├── DefaultHAClient.java
│ │ │ ├── DefaultHAConnection.java
│ │ │ ├── DefaultHAService.java
│ │ │ ├── FlowMonitor.java
│ │ │ ├── GroupTransferService.java
│ │ │ ├── HAClient.java
│ │ │ ├── HAConnection.java
│ │ │ ├── HAConnectionState.java
│ │ │ ├── HAConnectionStateNotificationRequest.java
│ │ │ ├── HAConnectionStateNotificationService.java
│ │ │ ├── HAService.java
│ │ │ ├── WaitNotifyObject.java
│ │ │ ├── autoswitch/
│ │ │ │ ├── AutoSwitchHAClient.java
│ │ │ │ ├── AutoSwitchHAConnection.java
│ │ │ │ ├── AutoSwitchHAService.java
│ │ │ │ ├── BrokerMetadata.java
│ │ │ │ ├── EpochFileCache.java
│ │ │ │ ├── MetadataFile.java
│ │ │ │ └── TempBrokerMetadata.java
│ │ │ └── io/
│ │ │ ├── AbstractHAReader.java
│ │ │ ├── HAReadHook.java
│ │ │ ├── HAWriteHook.java
│ │ │ └── HAWriter.java
│ │ ├── hook/
│ │ │ ├── PutMessageHook.java
│ │ │ └── SendMessageBackHook.java
│ │ ├── index/
│ │ │ ├── IndexFile.java
│ │ │ ├── IndexHeader.java
│ │ │ ├── IndexService.java
│ │ │ ├── QueryOffsetResult.java
│ │ │ └── rocksdb/
│ │ │ ├── IndexRocksDBRecord.java
│ │ │ └── IndexRocksDBStore.java
│ │ ├── kv/
│ │ │ ├── CommitLogDispatcherCompaction.java
│ │ │ ├── CompactionLog.java
│ │ │ ├── CompactionPositionMgr.java
│ │ │ ├── CompactionService.java
│ │ │ ├── CompactionStore.java
│ │ │ └── MessageFetcher.java
│ │ ├── lock/
│ │ │ ├── AdaptiveBackOffSpinLock.java
│ │ │ ├── AdaptiveBackOffSpinLockImpl.java
│ │ │ ├── BackOffReentrantLock.java
│ │ │ └── BackOffSpinLock.java
│ │ ├── logfile/
│ │ │ ├── AbstractMappedFile.java
│ │ │ ├── DefaultMappedFile.java
│ │ │ ├── MappedFile.java
│ │ │ └── SharedByteBufferManager.java
│ │ ├── metrics/
│ │ │ ├── DefaultStoreMetricsConstant.java
│ │ │ ├── DefaultStoreMetricsManager.java
│ │ │ ├── RocksDBStoreMetricsManager.java
│ │ │ └── StoreMetricsManager.java
│ │ ├── plugin/
│ │ │ ├── AbstractPluginMessageStore.java
│ │ │ ├── MessageStoreFactory.java
│ │ │ └── MessageStorePluginContext.java
│ │ ├── pop/
│ │ │ ├── AckMsg.java
│ │ │ ├── BatchAckMsg.java
│ │ │ └── PopCheckPoint.java
│ │ ├── queue/
│ │ │ ├── AbstractConsumeQueueStore.java
│ │ │ ├── BatchConsumeQueue.java
│ │ │ ├── BatchOffsetIndex.java
│ │ │ ├── CombineConsumeQueueStore.java
│ │ │ ├── ConsumeQueueInterface.java
│ │ │ ├── ConsumeQueueStore.java
│ │ │ ├── ConsumeQueueStoreInterface.java
│ │ │ ├── CqUnit.java
│ │ │ ├── DispatchEntry.java
│ │ │ ├── FileQueueLifeCycle.java
│ │ │ ├── MultiDispatchUtils.java
│ │ │ ├── OffsetInitializer.java
│ │ │ ├── OffsetInitializerRocksDBImpl.java
│ │ │ ├── QueueOffsetOperator.java
│ │ │ ├── ReferredIterator.java
│ │ │ ├── RocksDBConsumeQueue.java
│ │ │ ├── RocksDBConsumeQueueOffsetTable.java
│ │ │ ├── RocksDBConsumeQueueStore.java
│ │ │ ├── RocksDBConsumeQueueTable.java
│ │ │ ├── RocksGroupCommitService.java
│ │ │ ├── SparseConsumeQueue.java
│ │ │ └── offset/
│ │ │ ├── OffsetEntry.java
│ │ │ └── OffsetEntryType.java
│ │ ├── rocksdb/
│ │ │ ├── ConsumeQueueCompactionFilterFactory.java
│ │ │ ├── ConsumeQueueRocksDBStorage.java
│ │ │ ├── MessageRocksDBStorage.java
│ │ │ └── RocksDBOptionsFactory.java
│ │ ├── stats/
│ │ │ ├── BrokerStats.java
│ │ │ ├── BrokerStatsManager.java
│ │ │ └── LmqBrokerStatsManager.java
│ │ ├── timer/
│ │ │ ├── Slot.java
│ │ │ ├── TimerCheckpoint.java
│ │ │ ├── TimerLog.java
│ │ │ ├── TimerMessageStore.java
│ │ │ ├── TimerMetrics.java
│ │ │ ├── TimerRequest.java
│ │ │ ├── TimerWheel.java
│ │ │ └── rocksdb/
│ │ │ ├── Timeline.java
│ │ │ ├── TimerMessageRocksDBStore.java
│ │ │ └── TimerRocksDBRecord.java
│ │ ├── transaction/
│ │ │ ├── TransMessageRocksDBStore.java
│ │ │ └── TransRocksDBRecord.java
│ │ └── util/
│ │ ├── LibC.java
│ │ └── PerfCounter.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── store/
│ │ ├── AppendCallbackTest.java
│ │ ├── AppendPropCRCTest.java
│ │ ├── BatchPutMessageTest.java
│ │ ├── ConsumeQueueExtTest.java
│ │ ├── ConsumeQueueTest.java
│ │ ├── DefaultMessageStoreCleanFilesTest.java
│ │ ├── DefaultMessageStoreShutDownTest.java
│ │ ├── DefaultMessageStoreTest.java
│ │ ├── FlushDiskWatcherTest.java
│ │ ├── GetMessageResultTest.java
│ │ ├── HATest.java
│ │ ├── MappedFileQueueTest.java
│ │ ├── MappedFileTest.java
│ │ ├── MessageExtBrokerInnerTest.java
│ │ ├── MessageStoreStateMachineTest.java
│ │ ├── MultiPathMappedFileQueueTest.java
│ │ ├── ReputMessageServiceTest.java
│ │ ├── RocksDBMessageStoreTest.java
│ │ ├── StoreCheckpointTest.java
│ │ ├── StoreStatsServiceTest.java
│ │ ├── StoreTestBase.java
│ │ ├── StoreTestUtil.java
│ │ ├── dledger/
│ │ │ ├── DLedgerCommitlogTest.java
│ │ │ ├── DLedgerMultiPathTest.java
│ │ │ ├── MessageStoreTestBase.java
│ │ │ └── MixCommitlogTest.java
│ │ ├── ha/
│ │ │ ├── FlowMonitorTest.java
│ │ │ ├── HAClientTest.java
│ │ │ ├── HAServerTest.java
│ │ │ ├── WaitNotifyObjectTest.java
│ │ │ └── autoswitch/
│ │ │ ├── AutoSwitchHATest.java
│ │ │ └── EpochFileCacheTest.java
│ │ ├── index/
│ │ │ ├── IndexFileTest.java
│ │ │ └── IndexServiceTest.java
│ │ ├── kv/
│ │ │ ├── CompactionLogTest.java
│ │ │ ├── CompactionPositionMgrTest.java
│ │ │ └── OffsetMapTest.java
│ │ ├── lock/
│ │ │ ├── AdaptiveBackOffSpinLockImplTest.java
│ │ │ └── AdaptiveLockTest.java
│ │ ├── logfile/
│ │ │ ├── DefaultMappedFileConcurrencyTest.java
│ │ │ ├── DefaultMappedFileErrorHandlingTest.java
│ │ │ ├── DefaultMappedFilePerformanceTest.java
│ │ │ ├── DefaultMappedFileTest.java
│ │ │ └── DefaultMappedFileWriteWithoutMmapTest.java
│ │ ├── pop/
│ │ │ ├── AckMsgTest.java
│ │ │ └── BatchAckMsgTest.java
│ │ ├── queue/
│ │ │ ├── BatchConsumeMessageTest.java
│ │ │ ├── BatchConsumeQueueTest.java
│ │ │ ├── CombineConsumeQueueStoreTest.java
│ │ │ ├── ConsumeQueueStoreTest.java
│ │ │ ├── ConsumeQueueTest.java
│ │ │ ├── QueueTestBase.java
│ │ │ ├── RocksDBConsumeQueueOffsetTableTest.java
│ │ │ ├── RocksDBConsumeQueueTableTest.java
│ │ │ ├── RocksDBConsumeQueueTest.java
│ │ │ └── SparseConsumeQueueTest.java
│ │ ├── rocksdb/
│ │ │ └── RocksDBOptionsFactoryTest.java
│ │ ├── stats/
│ │ │ └── BrokerStatsManagerTest.java
│ │ └── timer/
│ │ ├── StoreTestUtils.java
│ │ ├── TimerCheckPointTest.java
│ │ ├── TimerLogTest.java
│ │ ├── TimerMessageStoreTest.java
│ │ ├── TimerMetricsTest.java
│ │ └── TimerWheelTest.java
│ └── resources/
│ └── rmq.logback-test.xml
├── style/
│ ├── copyright/
│ │ ├── Apache.xml
│ │ └── profiles_settings.xml
│ ├── rmq_checkstyle.xml
│ ├── rmq_codeStyle.xml
│ └── spotbugs-suppressions.xml
├── test/
│ ├── BUILD.bazel
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── test/
│ │ ├── client/
│ │ │ ├── mq/
│ │ │ │ └── MQAsyncProducer.java
│ │ │ └── rmq/
│ │ │ ├── RMQAsyncSendProducer.java
│ │ │ ├── RMQBroadCastConsumer.java
│ │ │ ├── RMQNormalConsumer.java
│ │ │ ├── RMQNormalProducer.java
│ │ │ ├── RMQPopClient.java
│ │ │ ├── RMQPopConsumer.java
│ │ │ ├── RMQSqlConsumer.java
│ │ │ └── RMQTransactionalProducer.java
│ │ ├── clientinterface/
│ │ │ ├── AbstractMQConsumer.java
│ │ │ ├── AbstractMQProducer.java
│ │ │ ├── MQCollector.java
│ │ │ ├── MQConsumer.java
│ │ │ └── MQProducer.java
│ │ ├── factory/
│ │ │ ├── ConsumerFactory.java
│ │ │ ├── MQMessageFactory.java
│ │ │ ├── MessageFactory.java
│ │ │ ├── ProducerFactory.java
│ │ │ ├── SendCallBackFactory.java
│ │ │ └── TagMessage.java
│ │ ├── listener/
│ │ │ ├── AbstractListener.java
│ │ │ └── rmq/
│ │ │ ├── concurrent/
│ │ │ │ ├── RMQBlockListener.java
│ │ │ │ ├── RMQDelayListener.java
│ │ │ │ └── RMQNormalListener.java
│ │ │ └── order/
│ │ │ └── RMQOrderListener.java
│ │ ├── lmq/
│ │ │ └── benchmark/
│ │ │ └── BenchLmqStore.java
│ │ ├── message/
│ │ │ └── MessageQueueMsg.java
│ │ ├── schema/
│ │ │ ├── SchemaDefiner.java
│ │ │ └── SchemaTools.java
│ │ ├── sendresult/
│ │ │ └── ResultWrapper.java
│ │ └── util/
│ │ ├── Condition.java
│ │ ├── DuplicateMessageInfo.java
│ │ ├── FileUtil.java
│ │ ├── MQAdminTestUtils.java
│ │ ├── MQRandomUtils.java
│ │ ├── MQWait.java
│ │ ├── RandomUtil.java
│ │ ├── RandomUtils.java
│ │ ├── StatUtil.java
│ │ ├── TestUtil.java
│ │ ├── TestUtils.java
│ │ ├── VerifyUtils.java
│ │ ├── data/
│ │ │ └── collect/
│ │ │ ├── DataCollector.java
│ │ │ ├── DataCollectorManager.java
│ │ │ ├── DataFilter.java
│ │ │ └── impl/
│ │ │ ├── ListDataCollectorImpl.java
│ │ │ └── MapDataCollectorImpl.java
│ │ └── parallel/
│ │ ├── ParallelTask.java
│ │ ├── ParallelTaskExecutor.java
│ │ └── Task4Test.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── test/
│ │ ├── autoswitchrole/
│ │ │ ├── AutoSwitchRoleBase.java
│ │ │ └── AutoSwitchRoleIntegrationTest.java
│ │ ├── base/
│ │ │ ├── BaseConf.java
│ │ │ └── IntegrationTestBase.java
│ │ ├── client/
│ │ │ ├── consumer/
│ │ │ │ ├── balance/
│ │ │ │ │ ├── NormalMsgDynamicBalanceIT.java
│ │ │ │ │ └── NormalMsgStaticBalanceIT.java
│ │ │ │ ├── broadcast/
│ │ │ │ │ ├── BaseBroadcast.java
│ │ │ │ │ ├── normal/
│ │ │ │ │ │ ├── BroadcastNormalMsgNotReceiveIT.java
│ │ │ │ │ │ ├── BroadcastNormalMsgRecvCrashIT.java
│ │ │ │ │ │ ├── BroadcastNormalMsgRecvFailIT.java
│ │ │ │ │ │ ├── BroadcastNormalMsgRecvStartLaterIT.java
│ │ │ │ │ │ ├── BroadcastNormalMsgTwoDiffGroupRecvIT.java
│ │ │ │ │ │ └── NormalMsgTwoSameGroupConsumerIT.java
│ │ │ │ │ ├── order/
│ │ │ │ │ │ └── OrderMsgBroadcastIT.java
│ │ │ │ │ └── tag/
│ │ │ │ │ ├── BroadcastTwoConsumerFilterIT.java
│ │ │ │ │ ├── BroadcastTwoConsumerSubDiffTagIT.java
│ │ │ │ │ └── BroadcastTwoConsumerSubTagIT.java
│ │ │ │ ├── cluster/
│ │ │ │ │ ├── DynamicAddAndCrashIT.java
│ │ │ │ │ ├── DynamicAddConsumerIT.java
│ │ │ │ │ └── DynamicCrashConsumerIT.java
│ │ │ │ ├── filter/
│ │ │ │ │ └── SqlFilterIT.java
│ │ │ │ ├── pop/
│ │ │ │ │ ├── BasePop.java
│ │ │ │ │ ├── BasePopNormally.java
│ │ │ │ │ ├── BasePopOrderly.java
│ │ │ │ │ ├── BatchAckIT.java
│ │ │ │ │ ├── NotificationIT.java
│ │ │ │ │ ├── PopBigMessageIT.java
│ │ │ │ │ ├── PopMessageAndForwardingIT.java
│ │ │ │ │ ├── PopOrderlyIT.java
│ │ │ │ │ ├── PopPriorityIT.java
│ │ │ │ │ └── PopSubCheckIT.java
│ │ │ │ ├── tag/
│ │ │ │ │ ├── MulTagSubIT.java
│ │ │ │ │ ├── TagMessageWith1ConsumerIT.java
│ │ │ │ │ ├── TagMessageWithMulConsumerIT.java
│ │ │ │ │ └── TagMessageWithSameGroupConsumerIT.java
│ │ │ │ └── topic/
│ │ │ │ ├── MulConsumerMulTopicIT.java
│ │ │ │ └── OneConsumerMulTopicIT.java
│ │ │ └── producer/
│ │ │ ├── async/
│ │ │ │ ├── AsyncSendExceptionIT.java
│ │ │ │ ├── AsyncSendWithMessageQueueIT.java
│ │ │ │ ├── AsyncSendWithMessageQueueSelectorIT.java
│ │ │ │ └── AsyncSendWithOnlySendCallBackIT.java
│ │ │ ├── batch/
│ │ │ │ └── BatchSendIT.java
│ │ │ ├── exception/
│ │ │ │ ├── msg/
│ │ │ │ │ ├── ChinaPropIT.java
│ │ │ │ │ ├── MessageExceptionIT.java
│ │ │ │ │ └── MessageUserPropIT.java
│ │ │ │ └── producer/
│ │ │ │ └── ProducerGroupAndInstanceNameValidityIT.java
│ │ │ ├── oneway/
│ │ │ │ ├── OneWaySendExceptionIT.java
│ │ │ │ ├── OneWaySendIT.java
│ │ │ │ ├── OneWaySendWithMQIT.java
│ │ │ │ └── OneWaySendWithSelectorIT.java
│ │ │ ├── order/
│ │ │ │ ├── OrderMsgDynamicRebalanceIT.java
│ │ │ │ ├── OrderMsgIT.java
│ │ │ │ ├── OrderMsgRebalanceIT.java
│ │ │ │ └── OrderMsgWithTagIT.java
│ │ │ ├── querymsg/
│ │ │ │ ├── QueryMsgByIdExceptionIT.java
│ │ │ │ ├── QueryMsgByIdIT.java
│ │ │ │ └── QueryMsgByKeyIT.java
│ │ │ └── transaction/
│ │ │ └── TransactionalMsgIT.java
│ │ ├── container/
│ │ │ ├── AddAndRemoveBrokerIT.java
│ │ │ ├── BrokerFailoverIT.java
│ │ │ ├── BrokerMemberGroupIT.java
│ │ │ ├── ContainerIntegrationTestBase.java
│ │ │ ├── GetMaxOffsetFromSlaveIT.java
│ │ │ ├── GetMetadataReverseIT.java
│ │ │ ├── PopSlaveActingMasterIT.java
│ │ │ ├── PullMultipleReplicasIT.java
│ │ │ ├── PushMultipleReplicasIT.java
│ │ │ ├── RebalanceLockOnSlaveIT.java
│ │ │ ├── ScheduleSlaveActingMasterIT.java
│ │ │ ├── ScheduledMessageIT.java
│ │ │ ├── SendMultipleReplicasIT.java
│ │ │ ├── SlaveBrokerIT.java
│ │ │ ├── SyncConsumerOffsetIT.java
│ │ │ ├── TransactionListenerImpl.java
│ │ │ └── TransactionMessageIT.java
│ │ ├── delay/
│ │ │ ├── DelayConf.java
│ │ │ └── NormalMsgDelayIT.java
│ │ ├── dledger/
│ │ │ └── DLedgerProduceAndConsumeIT.java
│ │ ├── grpc/
│ │ │ └── v2/
│ │ │ ├── ClusterGrpcIT.java
│ │ │ ├── GrpcBaseIT.java
│ │ │ └── LocalGrpcIT.java
│ │ ├── lmq/
│ │ │ └── TestBenchLmqStore.java
│ │ ├── offset/
│ │ │ ├── LagCalculationIT.java
│ │ │ ├── OffsetNotFoundIT.java
│ │ │ ├── OffsetResetForPopIT.java
│ │ │ └── OffsetResetIT.java
│ │ ├── recall/
│ │ │ ├── RecallWithTraceIT.java
│ │ │ └── SendAndRecallDelayMessageIT.java
│ │ ├── retry/
│ │ │ └── PopConsumerRetryIT.java
│ │ ├── route/
│ │ │ └── CreateAndUpdateTopicIT.java
│ │ ├── schema/
│ │ │ └── SchemaTest.java
│ │ ├── smoke/
│ │ │ └── NormalMessageSendAndRecvIT.java
│ │ ├── statictopic/
│ │ │ └── StaticTopicIT.java
│ │ └── tls/
│ │ ├── TlsIT.java
│ │ ├── TlsMix2IT.java
│ │ └── TlsMixIT.java
│ └── resources/
│ ├── rmq-proxy-home/
│ │ └── conf/
│ │ ├── broker.conf
│ │ ├── logback_proxy.xml
│ │ └── rmq-proxy.json
│ ├── rmq.logback-test.xml
│ └── schema/
│ ├── api/
│ │ ├── client.consumer.AllocateMessageQueueStrategy.schema
│ │ ├── client.consumer.DefaultLitePullConsumer.schema
│ │ ├── client.consumer.DefaultMQPullConsumer.schema
│ │ ├── client.consumer.DefaultMQPushConsumer.schema
│ │ ├── client.consumer.PullCallback.schema
│ │ ├── client.consumer.PullResult.schema
│ │ ├── client.consumer.PullStatus.schema
│ │ ├── client.consumer.listener.ConsumeConcurrentlyContext.schema
│ │ ├── client.consumer.listener.ConsumeConcurrentlyStatus.schema
│ │ ├── client.consumer.listener.ConsumeOrderlyContext.schema
│ │ ├── client.consumer.listener.ConsumeOrderlyStatus.schema
│ │ ├── client.consumer.listener.MessageListener.schema
│ │ ├── client.consumer.listener.MessageListenerConcurrently.schema
│ │ ├── client.consumer.listener.MessageListenerOrderly.schema
│ │ ├── client.hook.CheckForbiddenHook.schema
│ │ ├── client.hook.ConsumeMessageContext.schema
│ │ ├── client.hook.ConsumeMessageHook.schema
│ │ ├── client.hook.EndTransactionContext.schema
│ │ ├── client.hook.EndTransactionHook.schema
│ │ ├── client.hook.FilterMessageContext.schema
│ │ ├── client.hook.FilterMessageHook.schema
│ │ ├── client.hook.SendMessageContext.schema
│ │ ├── client.hook.SendMessageHook.schema
│ │ ├── client.producer.DefaultMQProducer.schema
│ │ ├── client.producer.MessageQueueSelector.schema
│ │ ├── client.producer.SendCallback.schema
│ │ ├── client.producer.SendResult.schema
│ │ ├── client.producer.SendStatus.schema
│ │ ├── common.message.Message.schema
│ │ ├── common.message.MessageExt.schema
│ │ ├── common.message.MessageQueue.schema
│ │ ├── remoting.RPCHook.schema
│ │ └── tools.admin.DefaultMQAdminExt.schema
│ └── protocol/
│ ├── common.protocol.RequestCode.schema
│ ├── common.protocol.header.CheckTransactionStateRequestHeader.schema
│ ├── common.protocol.header.CheckTransactionStateResponseHeader.schema
│ ├── common.protocol.header.CloneGroupOffsetRequestHeader.schema
│ ├── common.protocol.header.ConsumeMessageDirectlyResultRequestHeader.schema
│ ├── common.protocol.header.ConsumerSendMsgBackRequestHeader.schema
│ ├── common.protocol.header.CreateAccessConfigRequestHeader.schema
│ ├── common.protocol.header.CreateTopicRequestHeader.schema
│ ├── common.protocol.header.DeleteAccessConfigRequestHeader.schema
│ ├── common.protocol.header.DeleteSubscriptionGroupRequestHeader.schema
│ ├── common.protocol.header.DeleteTopicRequestHeader.schema
│ ├── common.protocol.header.EndTransactionRequestHeader.schema
│ ├── common.protocol.header.EndTransactionResponseHeader.schema
│ ├── common.protocol.header.GetAllProducerInfoRequestHeader.schema
│ ├── common.protocol.header.GetAllTopicConfigResponseHeader.schema
│ ├── common.protocol.header.GetBrokerAclConfigResponseHeader.schema
│ ├── common.protocol.header.GetBrokerClusterAclConfigResponseHeader.schema
│ ├── common.protocol.header.GetBrokerConfigResponseHeader.schema
│ ├── common.protocol.header.GetConsumeStatsInBrokerHeader.schema
│ ├── common.protocol.header.GetConsumeStatsRequestHeader.schema
│ ├── common.protocol.header.GetConsumerConnectionListRequestHeader.schema
│ ├── common.protocol.header.GetConsumerListByGroupRequestHeader.schema
│ ├── common.protocol.header.GetConsumerListByGroupResponseHeader.schema
│ ├── common.protocol.header.GetConsumerRunningInfoRequestHeader.schema
│ ├── common.protocol.header.GetConsumerStatusRequestHeader.schema
│ ├── common.protocol.header.GetEarliestMsgStoretimeRequestHeader.schema
│ ├── common.protocol.header.GetEarliestMsgStoretimeResponseHeader.schema
│ ├── common.protocol.header.GetMaxOffsetRequestHeader.schema
│ ├── common.protocol.header.GetMaxOffsetResponseHeader.schema
│ ├── common.protocol.header.GetMinOffsetRequestHeader.schema
│ ├── common.protocol.header.GetMinOffsetResponseHeader.schema
│ ├── common.protocol.header.GetProducerConnectionListRequestHeader.schema
│ ├── common.protocol.header.GetTopicStatsInfoRequestHeader.schema
│ ├── common.protocol.header.GetTopicsByClusterRequestHeader.schema
│ ├── common.protocol.header.NotifyConsumerIdsChangedRequestHeader.schema
│ ├── common.protocol.header.PullMessageRequestHeader.schema
│ ├── common.protocol.header.PullMessageResponseHeader.schema
│ ├── common.protocol.header.QueryConsumeQueueRequestHeader.schema
│ ├── common.protocol.header.QueryConsumeTimeSpanRequestHeader.schema
│ ├── common.protocol.header.QueryConsumerOffsetRequestHeader.schema
│ ├── common.protocol.header.QueryConsumerOffsetResponseHeader.schema
│ ├── common.protocol.header.QueryCorrectionOffsetHeader.schema
│ ├── common.protocol.header.QueryMessageRequestHeader.schema
│ ├── common.protocol.header.QueryMessageResponseHeader.schema
│ ├── common.protocol.header.QueryTopicConsumeByWhoRequestHeader.schema
│ ├── common.protocol.header.ReplyMessageRequestHeader.schema
│ ├── common.protocol.header.ResetOffsetRequestHeader.schema
│ ├── common.protocol.header.ResumeCheckHalfMessageRequestHeader.schema
│ ├── common.protocol.header.SearchOffsetRequestHeader.schema
│ ├── common.protocol.header.SearchOffsetResponseHeader.schema
│ ├── common.protocol.header.SendMessageRequestHeader.schema
│ ├── common.protocol.header.SendMessageRequestHeaderV2.schema
│ ├── common.protocol.header.SendMessageResponseHeader.schema
│ ├── common.protocol.header.UnregisterClientRequestHeader.schema
│ ├── common.protocol.header.UnregisterClientResponseHeader.schema
│ ├── common.protocol.header.UpdateConsumerOffsetRequestHeader.schema
│ ├── common.protocol.header.UpdateConsumerOffsetResponseHeader.schema
│ ├── common.protocol.header.UpdateGlobalWhiteAddrsConfigRequestHeader.schema
│ ├── common.protocol.header.ViewBrokerStatsDataRequestHeader.schema
│ ├── common.protocol.header.ViewMessageRequestHeader.schema
│ ├── common.protocol.header.ViewMessageResponseHeader.schema
│ ├── common.protocol.header.filtersrv.RegisterFilterServerRequestHeader.schema
│ ├── common.protocol.header.filtersrv.RegisterFilterServerResponseHeader.schema
│ ├── common.protocol.header.filtersrv.RegisterMessageFilterClassRequestHeader.schema
│ ├── common.protocol.header.namesrv.AddWritePermOfBrokerRequestHeader.schema
│ ├── common.protocol.header.namesrv.AddWritePermOfBrokerResponseHeader.schema
│ ├── common.protocol.header.namesrv.DeleteKVConfigRequestHeader.schema
│ ├── common.protocol.header.namesrv.DeleteTopicFromNamesrvRequestHeader.schema
│ ├── common.protocol.header.namesrv.GetKVConfigRequestHeader.schema
│ ├── common.protocol.header.namesrv.GetKVConfigResponseHeader.schema
│ ├── common.protocol.header.namesrv.GetKVListByNamespaceRequestHeader.schema
│ ├── common.protocol.header.namesrv.GetRouteInfoRequestHeader.schema
│ ├── common.protocol.header.namesrv.PutKVConfigRequestHeader.schema
│ ├── common.protocol.header.namesrv.QueryDataVersionRequestHeader.schema
│ ├── common.protocol.header.namesrv.QueryDataVersionResponseHeader.schema
│ ├── common.protocol.header.namesrv.RegisterBrokerRequestHeader.schema
│ ├── common.protocol.header.namesrv.RegisterBrokerResponseHeader.schema
│ ├── common.protocol.header.namesrv.RegisterOrderTopicRequestHeader.schema
│ ├── common.protocol.header.namesrv.UnRegisterBrokerRequestHeader.schema
│ ├── common.protocol.header.namesrv.WipeWritePermOfBrokerRequestHeader.schema
│ └── common.protocol.header.namesrv.WipeWritePermOfBrokerResponseHeader.schema
├── tieredstore/
│ ├── BUILD.bazel
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── tieredstore/
│ │ ├── MessageStoreConfig.java
│ │ ├── MessageStoreExecutor.java
│ │ ├── TieredMessageStore.java
│ │ ├── common/
│ │ │ ├── AppendResult.java
│ │ │ ├── FileSegmentType.java
│ │ │ ├── GetMessageResultExt.java
│ │ │ ├── GroupCommitContext.java
│ │ │ └── SelectBufferResult.java
│ │ ├── core/
│ │ │ ├── MessageStoreDispatcher.java
│ │ │ ├── MessageStoreDispatcherImpl.java
│ │ │ ├── MessageStoreFetcher.java
│ │ │ ├── MessageStoreFetcherImpl.java
│ │ │ ├── MessageStoreFilter.java
│ │ │ └── MessageStoreTopicFilter.java
│ │ ├── exception/
│ │ │ ├── TieredStoreErrorCode.java
│ │ │ └── TieredStoreException.java
│ │ ├── file/
│ │ │ ├── FlatAppendFile.java
│ │ │ ├── FlatCommitLogFile.java
│ │ │ ├── FlatConsumeQueueFile.java
│ │ │ ├── FlatFileFactory.java
│ │ │ ├── FlatFileInterface.java
│ │ │ ├── FlatFileStore.java
│ │ │ └── FlatMessageFile.java
│ │ ├── index/
│ │ │ ├── IndexFile.java
│ │ │ ├── IndexItem.java
│ │ │ ├── IndexService.java
│ │ │ ├── IndexStoreFile.java
│ │ │ └── IndexStoreService.java
│ │ ├── metadata/
│ │ │ ├── DefaultMetadataStore.java
│ │ │ ├── MetadataStore.java
│ │ │ └── entity/
│ │ │ ├── FileSegmentMetadata.java
│ │ │ ├── QueueMetadata.java
│ │ │ └── TopicMetadata.java
│ │ ├── metrics/
│ │ │ ├── TieredStoreMetricsConstant.java
│ │ │ └── TieredStoreMetricsManager.java
│ │ ├── provider/
│ │ │ ├── FileSegment.java
│ │ │ ├── FileSegmentFactory.java
│ │ │ ├── FileSegmentProvider.java
│ │ │ ├── MemoryFileSegment.java
│ │ │ └── PosixFileSegment.java
│ │ ├── stream/
│ │ │ ├── CommitLogInputStream.java
│ │ │ ├── FileSegmentInputStream.java
│ │ │ └── FileSegmentInputStreamFactory.java
│ │ └── util/
│ │ ├── MessageFormatUtil.java
│ │ └── MessageStoreUtil.java
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── tieredstore/
│ │ ├── TieredMessageStoreTest.java
│ │ ├── common/
│ │ │ ├── FileSegmentTypeTest.java
│ │ │ ├── GetMessageResultExtTest.java
│ │ │ ├── GroupCommitContextTest.java
│ │ │ └── SelectBufferResultTest.java
│ │ ├── core/
│ │ │ ├── MessageStoreDispatcherImplTest.java
│ │ │ ├── MessageStoreFetcherImplTest.java
│ │ │ └── MessageStoreTopicFilterTest.java
│ │ ├── exception/
│ │ │ └── TieredStoreExceptionTest.java
│ │ ├── file/
│ │ │ ├── FlatAppendFileTest.java
│ │ │ ├── FlatCommitLogFileTest.java
│ │ │ ├── FlatFileFactoryTest.java
│ │ │ ├── FlatFileStoreTest.java
│ │ │ └── FlatMessageFileTest.java
│ │ ├── index/
│ │ │ ├── IndexItemTest.java
│ │ │ ├── IndexStoreFileTest.java
│ │ │ ├── IndexStoreServiceBenchTest.java
│ │ │ └── IndexStoreServiceTest.java
│ │ ├── metadata/
│ │ │ └── DefaultMetadataStoreTest.java
│ │ ├── metrics/
│ │ │ └── TieredStoreMetricsManagerTest.java
│ │ ├── provider/
│ │ │ ├── FileSegmentFactoryTest.java
│ │ │ ├── FileSegmentTest.java
│ │ │ └── MemoryFileSegmentTest.java
│ │ ├── stream/
│ │ │ └── FileSegmentInputStreamTest.java
│ │ └── util/
│ │ ├── MessageFormatUtilTest.java
│ │ └── MessageStoreUtilTest.java
│ └── resources/
│ └── rmq.logback-test.xml
└── tools/
├── BUILD.bazel
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── org/
│ │ └── apache/
│ │ └── rocketmq/
│ │ └── tools/
│ │ ├── admin/
│ │ │ ├── DefaultMQAdminExt.java
│ │ │ ├── DefaultMQAdminExtImpl.java
│ │ │ ├── MQAdminExt.java
│ │ │ ├── MQAdminUtils.java
│ │ │ ├── api/
│ │ │ │ ├── BrokerOperatorResult.java
│ │ │ │ ├── MessageTrack.java
│ │ │ │ └── TrackType.java
│ │ │ └── common/
│ │ │ ├── AdminToolHandler.java
│ │ │ ├── AdminToolResult.java
│ │ │ └── AdminToolsResultCodeEnum.java
│ │ ├── command/
│ │ │ ├── CommandUtil.java
│ │ │ ├── MQAdminStartup.java
│ │ │ ├── SubCommand.java
│ │ │ ├── SubCommandException.java
│ │ │ ├── auth/
│ │ │ │ ├── CopyAclsSubCommand.java
│ │ │ │ ├── CopyUsersSubCommand.java
│ │ │ │ ├── CreateAclSubCommand.java
│ │ │ │ ├── CreateUserSubCommand.java
│ │ │ │ ├── DeleteAclSubCommand.java
│ │ │ │ ├── DeleteUserSubCommand.java
│ │ │ │ ├── GetAclSubCommand.java
│ │ │ │ ├── GetUserSubCommand.java
│ │ │ │ ├── ListAclSubCommand.java
│ │ │ │ ├── ListUserSubCommand.java
│ │ │ │ ├── UpdateAclSubCommand.java
│ │ │ │ └── UpdateUserSubCommand.java
│ │ │ ├── broker/
│ │ │ │ ├── BrokerConsumeStatsSubCommand.java
│ │ │ │ ├── BrokerStatusSubCommand.java
│ │ │ │ ├── CleanExpiredCQSubCommand.java
│ │ │ │ ├── CleanUnusedTopicCommand.java
│ │ │ │ ├── CommitLogSetReadAheadSubCommand.java
│ │ │ │ ├── DeleteExpiredCommitLogSubCommand.java
│ │ │ │ ├── GetBrokerConfigCommand.java
│ │ │ │ ├── GetBrokerEpochSubCommand.java
│ │ │ │ ├── GetColdDataFlowCtrInfoSubCommand.java
│ │ │ │ ├── RemoveColdDataFlowCtrGroupConfigSubCommand.java
│ │ │ │ ├── ResetMasterFlushOffsetSubCommand.java
│ │ │ │ ├── SendMsgStatusCommand.java
│ │ │ │ ├── SwitchTimerEngineSubCommand.java
│ │ │ │ ├── UpdateBrokerConfigSubCommand.java
│ │ │ │ └── UpdateColdDataFlowCtrGroupConfigSubCommand.java
│ │ │ ├── cluster/
│ │ │ │ ├── CLusterSendMsgRTCommand.java
│ │ │ │ └── ClusterListSubCommand.java
│ │ │ ├── connection/
│ │ │ │ ├── ConsumerConnectionSubCommand.java
│ │ │ │ └── ProducerConnectionSubCommand.java
│ │ │ ├── consumer/
│ │ │ │ ├── ConsumerProgressSubCommand.java
│ │ │ │ ├── ConsumerStatusSubCommand.java
│ │ │ │ ├── ConsumerSubCommand.java
│ │ │ │ ├── DeleteSubscriptionGroupCommand.java
│ │ │ │ ├── GetConsumerConfigSubCommand.java
│ │ │ │ ├── SetConsumeModeSubCommand.java
│ │ │ │ ├── StartMonitoringSubCommand.java
│ │ │ │ ├── UpdateSubGroupListSubCommand.java
│ │ │ │ └── UpdateSubGroupSubCommand.java
│ │ │ ├── container/
│ │ │ │ ├── AddBrokerSubCommand.java
│ │ │ │ └── RemoveBrokerSubCommand.java
│ │ │ ├── controller/
│ │ │ │ ├── CleanControllerBrokerMetaSubCommand.java
│ │ │ │ ├── GetControllerConfigSubCommand.java
│ │ │ │ ├── GetControllerMetaDataSubCommand.java
│ │ │ │ ├── ReElectMasterSubCommand.java
│ │ │ │ └── UpdateControllerConfigSubCommand.java
│ │ │ ├── export/
│ │ │ │ ├── ExportConfigsCommand.java
│ │ │ │ ├── ExportMetadataCommand.java
│ │ │ │ ├── ExportMetadataInRocksDBCommand.java
│ │ │ │ ├── ExportMetricsCommand.java
│ │ │ │ └── ExportPopRecordCommand.java
│ │ │ ├── ha/
│ │ │ │ ├── GetSyncStateSetSubCommand.java
│ │ │ │ └── HAStatusSubCommand.java
│ │ │ ├── lite/
│ │ │ │ ├── GetBrokerLiteInfoSubCommand.java
│ │ │ │ ├── GetLiteClientInfoSubCommand.java
│ │ │ │ ├── GetLiteGroupInfoSubCommand.java
│ │ │ │ ├── GetLiteTopicInfoSubCommand.java
│ │ │ │ ├── GetParentTopicInfoSubCommand.java
│ │ │ │ └── TriggerLiteDispatchSubCommand.java
│ │ │ ├── message/
│ │ │ │ ├── CheckMsgSendRTCommand.java
│ │ │ │ ├── ConsumeMessageCommand.java
│ │ │ │ ├── DecodeMessageIdCommond.java
│ │ │ │ ├── DumpCompactionLogCommand.java
│ │ │ │ ├── PrintMessageByQueueCommand.java
│ │ │ │ ├── PrintMessageSubCommand.java
│ │ │ │ ├── QueryMsgByIdSubCommand.java
│ │ │ │ ├── QueryMsgByKeySubCommand.java
│ │ │ │ ├── QueryMsgByOffsetSubCommand.java
│ │ │ │ ├── QueryMsgByUniqueKeySubCommand.java
│ │ │ │ ├── QueryMsgTraceByIdSubCommand.java
│ │ │ │ └── SendMessageCommand.java
│ │ │ ├── metadata/
│ │ │ │ └── RocksDBConfigToJsonCommand.java
│ │ │ ├── namesrv/
│ │ │ │ ├── AddWritePermSubCommand.java
│ │ │ │ ├── DeleteKvConfigCommand.java
│ │ │ │ ├── GetNamesrvConfigCommand.java
│ │ │ │ ├── UpdateKvConfigCommand.java
│ │ │ │ ├── UpdateNamesrvConfigCommand.java
│ │ │ │ └── WipeWritePermSubCommand.java
│ │ │ ├── offset/
│ │ │ │ ├── CloneGroupOffsetCommand.java
│ │ │ │ ├── GetConsumerStatusCommand.java
│ │ │ │ ├── ResetOffsetByTimeCommand.java
│ │ │ │ ├── ResetOffsetByTimeOldCommand.java
│ │ │ │ └── SkipAccumulationSubCommand.java
│ │ │ ├── producer/
│ │ │ │ └── ProducerSubCommand.java
│ │ │ ├── queue/
│ │ │ │ ├── CheckRocksdbCqWriteProgressCommand.java
│ │ │ │ └── QueryConsumeQueueCommand.java
│ │ │ ├── stats/
│ │ │ │ └── StatsAllSubCommand.java
│ │ │ └── topic/
│ │ │ ├── AllocateMQSubCommand.java
│ │ │ ├── DeleteTopicSubCommand.java
│ │ │ ├── RebalanceResult.java
│ │ │ ├── RemappingStaticTopicSubCommand.java
│ │ │ ├── TopicClusterSubCommand.java
│ │ │ ├── TopicListSubCommand.java
│ │ │ ├── TopicRouteSubCommand.java
│ │ │ ├── TopicStatusSubCommand.java
│ │ │ ├── UpdateOrderConfCommand.java
│ │ │ ├── UpdateStaticTopicSubCommand.java
│ │ │ ├── UpdateTopicListSubCommand.java
│ │ │ ├── UpdateTopicPermSubCommand.java
│ │ │ └── UpdateTopicSubCommand.java
│ │ └── monitor/
│ │ ├── DefaultMonitorListener.java
│ │ ├── DeleteMsgsEvent.java
│ │ ├── FailedMsgs.java
│ │ ├── MonitorConfig.java
│ │ ├── MonitorListener.java
│ │ ├── MonitorService.java
│ │ └── UndoneMsgs.java
│ └── resources/
│ └── rmq.tools.logback.xml
└── test/
├── java/
│ └── org/
│ └── apache/
│ └── rocketmq/
│ └── tools/
│ ├── admin/
│ │ ├── DefaultMQAdminExtImplTest.java
│ │ └── DefaultMQAdminExtTest.java
│ ├── command/
│ │ ├── CommandUtilTest.java
│ │ ├── broker/
│ │ │ ├── BrokerConsumeStatsSubCommandTest.java
│ │ │ ├── BrokerStatusSubCommandTest.java
│ │ │ ├── CleanExpiredCQSubCommandTest.java
│ │ │ ├── CleanUnusedTopicCommandTest.java
│ │ │ ├── DeleteExpiredCommitLogSubCommandTest.java
│ │ │ ├── GetBrokerConfigCommandTest.java
│ │ │ ├── SendMsgStatusCommandTest.java
│ │ │ ├── SwitchTimerEngineSubCommandTest.java
│ │ │ └── UpdateBrokerConfigSubCommandTest.java
│ │ ├── connection/
│ │ │ ├── ConsumerConnectionSubCommandTest.java
│ │ │ └── ProducerConnectionSubCommandTest.java
│ │ ├── consumer/
│ │ │ ├── ConsumerProgressSubCommandTest.java
│ │ │ ├── ConsumerStatusSubCommandTest.java
│ │ │ ├── GetConsumerConfigSubCommandTest.java
│ │ │ └── UpdateSubGroupListSubCommandTest.java
│ │ ├── lite/
│ │ │ ├── GetBrokerLiteInfoSubCommandTest.java
│ │ │ └── GetLiteClientInfoSubCommandTest.java
│ │ ├── message/
│ │ │ ├── ConsumeMessageCommandTest.java
│ │ │ ├── QueryMsgByUniqueKeySubCommandTest.java
│ │ │ ├── QueryMsgTraceByIdSubCommandTest.java
│ │ │ └── SendMessageCommandTest.java
│ │ ├── metadata/
│ │ │ └── ExportMetadataInRocksDBCommandTest.java
│ │ ├── namesrv/
│ │ │ ├── AddWritePermSubCommandTest.java
│ │ │ ├── GetNamesrvConfigCommandTest.java
│ │ │ ├── UpdateKvConfigCommandTest.java
│ │ │ └── WipeWritePermSubCommandTest.java
│ │ ├── offset/
│ │ │ ├── GetConsumerStatusCommandTest.java
│ │ │ ├── ResetOffsetByTimeCommandTest.java
│ │ │ ├── ResetOffsetByTimeOldCommandTest.java
│ │ │ └── SkipAccumulationCommandTest.java
│ │ ├── producer/
│ │ │ └── ProducerSubCommandTest.java
│ │ ├── server/
│ │ │ ├── NameServerMocker.java
│ │ │ └── ServerResponseMocker.java
│ │ └── topic/
│ │ ├── AllocateMQSubCommandTest.java
│ │ ├── DeleteTopicSubCommandTest.java
│ │ ├── TopicClusterSubCommandTest.java
│ │ ├── TopicRouteSubCommandTest.java
│ │ ├── TopicStatusSubCommandTest.java
│ │ ├── UpdateOrderConfCommandTest.java
│ │ ├── UpdateTopicListSubCommandTest.java
│ │ ├── UpdateTopicPermSubCommandTest.java
│ │ └── UpdateTopicSubCommandTest.java
│ └── monitor/
│ ├── DefaultMonitorListenerTest.java
│ └── MonitorServiceTest.java
└── resources/
└── rmq.logback-test.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .asf.yaml
================================================
# 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.
github:
description: "Apache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications."
homepage: https://rocketmq.apache.org/
labels:
- messaging
- streaming
- eventing
- cloud-native
- rocketmq
- java
- hacktoberfest
enabled_merge_buttons:
# Enable squash button
squash: true
# Disable merge button
merge: false
# Disable rebase button
rebase: false
protected_branches:
master: {}
develop:
required_pull_request_reviews:
dismiss_stale_reviews: true
require_code_owner_reviews: false
required_approving_review_count: 1
required_status_checks:
contexts:
- misspell-check
- check-license
- maven-compile (ubuntu-latest, JDK-8)
- maven-compile (windows-latest, JDK-8)
- maven-compile (macos-latest, JDK-8)
notifications:
commits: commits@rocketmq.apache.org
issues: commits@rocketmq.apache.org
pullrequests: commits@rocketmq.apache.org
jobs: commits@rocketmq.apache.org
discussions: dev@rocketmq.apache.org
================================================
FILE: .bazelrc
================================================
#
# 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.
#
startup --host_jvm_args=-Xmx2g
run --color=yes
build --color=yes
build --enable_platform_specific_config
test --action_env=TEST_TMPDIR=/tmp
test --experimental_strict_java_deps=warn
test --experimental_ui_max_stdouterr_bytes=10485760
build --experimental_strict_java_deps=warn
test --test_output=errors
# This .bazelrc file contains all of the flags required for the provided
# toolchain with Remote Build Execution.
# Note your WORKSPACE must contain an rbe_autoconfig target with
# name="rbe_default" to use these flags as-is.
# Depending on how many machines are in the remote execution instance, setting
# this higher can make builds faster by allowing more jobs to run in parallel.
# Setting it too high can result in jobs that timeout, however, while waiting
# for a remote machine to execute them.
build:remote --jobs=150
build:remote --remote_executor=grpcs://remote.buildbuddy.io
build:remote --host_platform=@buildbuddy_toolchain//:platform
build:remote --platforms=@buildbuddy_toolchain//:platform
build:remote --extra_execution_platforms=@buildbuddy_toolchain//:platform
build:remote --crosstool_top=@buildbuddy_toolchain//:toolchain
build:remote --extra_toolchains=@buildbuddy_toolchain//:cc_toolchain
build:remote --java_language_version=8
build:remote --java_runtime_version=8
build:remote --define=EXECUTOR=remote
# Enable remote execution so actions are performed on the remote systems.
build:remote --remote_executor=grpcs://remote.buildbuddy.io
# Enforce stricter environment rules, which eliminates some non-hermetic
# behavior and therefore improves both the remote cache hit rate and the
# correctness and repeatability of the build.
build:remote --incompatible_strict_action_env=true
# Set a higher timeout value, just in case.
build:remote --remote_timeout=3600
# Use a pre-configured account, such that we may have pull-request replacing pull-request-target
build:remote --remote_header=x-buildbuddy-api-key=FD819nUEY7WjvqmoufsU
test:remote --remote_header=x-buildbuddy-api-key=FD819nUEY7WjvqmoufsU
================================================
FILE: .bazelversion
================================================
6.5.0
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
#
# 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.
#
name: Bug Report
title: "[Bug] Bug title "
description: Create a report to help us identify any unintended flaws, errors, or faults.
body:
- type: checkboxes
attributes:
label: Before Creating the Bug Report
options:
- label: >
I found a bug, not just asking a question, which should be created in [GitHub Discussions](https://github.com/apache/rocketmq/discussions).
required: true
- label: >
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.
required: true
- label: >
I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.
required: true
- type: textarea
attributes:
label: Runtime platform environment
description: Describe the runtime platform environment.
placeholder: >
OS: (e.g., "Ubuntu 20.04")
OS: (e.g., "Windows Server 2019")
validations:
required: true
- type: textarea
attributes:
label: RocketMQ version
description: Describe the RocketMQ version.
placeholder: >
branch: (e.g develop|4.9.x)
version: (e.g. 5.1.0|4.9.5)
Git commit id: (e.g. c88b5cfa72e204962929eea105687647146112c6)
validations:
required: true
- type: textarea
attributes:
label: JDK Version
description: Run or Compiler version.
placeholder: >
Compiler: (e.g., "Oracle JDK 11.0.17")
OS: (e.g., "Ubuntu 20.04")
Runtime (if different from JDK above): (e.g., "Oracle JRE 8u251")
OS (if different from OS compiled on): (e.g., "Windows Server 2019")
validations:
required: false
- type: textarea
attributes:
label: Describe the Bug
description: Describe what happened.
placeholder: >
A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Describe the steps to reproduce the bug here.
placeholder: >
If possible, provide a recipe for reproducing the error.
validations:
required: true
- type: textarea
attributes:
label: What Did You Expect to See?
description: You expect to see result.
placeholder: >
A clear and concise description of what you expected to see.
validations:
required: true
- type: textarea
attributes:
label: What Did You See Instead?
description: You instead to see result.
placeholder: >
A clear and concise description of what you saw instead.
validations:
required: true
- type: textarea
attributes:
label: Additional Context
description: Additional context.
placeholder: >
Add any other context about the problem here.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
#
# 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.
#
blank_issues_enabled: false
contact_links:
- name: Ask Question
url: https://github.com/apache/rocketmq/discussions
about: Please go to GitHub Disccusions to ask questions
================================================
FILE: .github/ISSUE_TEMPLATE/doc.yml
================================================
#
# 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.
#
name: Documentation Related
title: "[Doc] Documentation Related "
description: I find some issues related to the documentation.
labels: [ "module/doc" ]
body:
- type: checkboxes
attributes:
label: Search before creation
description: >
Please make sure to search in the [issues](https://github.com/apache/rocketmq/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/apache/rocketmq/issues) and found
no similar issues.
required: true
- type: textarea
attributes:
label: Documentation Related
description: Describe the suggestion about document.
placeholder: >
e.g There is a typo
validations:
required: true
- type: checkboxes
attributes:
label: Are you willing to submit PR?
description: >
This is absolutely not required, but we are happy to guide you in the contribution process
especially if you already have a good understanding of how to implement the fix.
options:
- label: Yes I am willing to submit a PR!
- type: markdown
attributes:
value: "Thanks for completing our form!"
================================================
FILE: .github/ISSUE_TEMPLATE/enhancement_request.yml
================================================
#
# 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.
#
name: Enhancement Request
title: "[Enhancement] Enhancement title"
description: Suggest an enhancement for this project
labels: [ "type/enhancement" ]
body:
- type: checkboxes
attributes:
label: Before Creating the Enhancement Request
description: >
Most of issues should be classified as bug or feature request. An issue should be considered as an enhancement when it proposes improvements to
existing functionality or user experience, without necessarily introducing new features or fixing existing bugs.
options:
- label: >
I have confirmed that this should be classified as an enhancement rather than a bug/feature.
required: true
- type: textarea
attributes:
label: Summary
placeholder: >
A clear and concise description of the enhancement you would like to see in the project.
validations:
required: true
- type: textarea
attributes:
label: Motivation
placeholder: >
Explain why you believe this enhancement is necessary, and how it benefits the project and community.
Include any specific use cases that you have in mind.
validations:
required: true
- type: textarea
attributes:
label: Describe the Solution You'd Like
placeholder: >
Describe the enhancement you propose, detailing the change and implementation steps involved.
If you have multiple solutions, please list them separately.
validations:
required: true
- type: textarea
attributes:
label: Describe Alternatives You've Considered
placeholder: >
List any alternative enhancements or implementations you have considered, and explain why they may not be as effective or appropriate.
validations:
required: true
- type: textarea
attributes:
label: Additional Context
placeholder: >
Add any relevant context, screenshots, prototypes, or other supplementary information to help illustrate the enhancement.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
#
# 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.
#
name: Feature Request
title: "[Feature] New feature title"
description: Suggest an idea for this project.
labels: [ "type/new feature" ]
body:
- type: textarea
attributes:
label: Is Your Feature Request Related to a Problem?
description: Please Describe It.
placeholder: >
A clear and concise description of what the problem is.
validations:
required: true
- type: textarea
attributes:
label: Describe the Solution You'd Like
description: Describe how you solved it.
placeholder: >
A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe Alternatives You've Considered
description: Describe your solution
placeholder: >
A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Additional Context
description: Additional context.
placeholder: >
Add any other context about the problem here.
validations:
required: false
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Which Issue(s) This PR Fixes
- Fixes #issue_id
### Brief Description
### How Did You Test This Change?
================================================
FILE: .github/asf-deploy-settings.xml
================================================
apache.snapshots.https
${env.NEXUS_DEPLOY_USERNAME}
${env.NEXUS_DEPLOY_PASSWORD}
60
================================================
FILE: .github/workflows/bazel.yml
================================================
name: Build and Run Tests by Bazel
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- master
- develop
- bazel
jobs:
build:
name: "bazel-compile (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Build
run: bazel build --config=remote //...
- name: Run Tests
run: bazel test --config=remote //...
================================================
FILE: .github/workflows/codeql_analysis.yml
================================================
name: CodeQL Analysis
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- master
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: java
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
================================================
FILE: .github/workflows/coverage.yml
================================================
name: Coverage
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: [master, develop]
jobs:
calculate-coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Set up JDK 8
uses: actions/setup-java@v4
with:
java-version: "8"
distribution: "corretto"
cache: "maven"
- name: Generate coverage report
run: mvn -B test -T 2C --file pom.xml
- name: Upload to Codecov
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
verbose: true
token: cf0cba0a-22f8-4580-89ab-4f1dec3bda6f
================================================
FILE: .github/workflows/integration-test.yml
================================================
name: Run Integration Tests
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: [master, develop]
jobs:
it-test:
name: "maven-compile (${{ matrix.os }}, JDK-${{ matrix.jdk }})"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
jdk: [8]
steps:
- name: Cache Maven Repos
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.jdk }}
distribution: "corretto"
cache: "maven"
- name: Run integration tests with Maven
run: mvn clean verify -Pit-test -Pskip-unit-tests
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always()
with:
report_paths: 'test/target/failsafe-reports/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
================================================
FILE: .github/workflows/license-checker.yaml
================================================
# 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.
name: License checker
on:
pull_request:
branches:
- develop
- master
jobs:
check-license:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check License Header
uses: apache/skywalking-eyes@v0.2.0
with:
log: info
config: .licenserc.yaml
================================================
FILE: .github/workflows/maven.yaml
================================================
name: Build and Run Tests by Maven
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: [master, develop, bazel]
jobs:
java_build:
name: "maven-compile (${{ matrix.os }}, JDK-${{ matrix.jdk }})"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
os: [ubuntu-latest, windows-latest, macos-latest]
jdk: [8]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.jdk }}
# See https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions
# AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore.
distribution: "corretto"
cache: "maven"
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Upload Auth JVM crash logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: jvm-crash-logs
path: /Users/runner/work/rocketmq/rocketmq/auth/hs_err_pid*.log
retention-days: 1
- name: Upload broker JVM crash logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: jvm-crash-logs
path: /Users/runner/work/rocketmq/rocketmq/broker/hs_err_pid*.log
retention-days: 1
================================================
FILE: .github/workflows/misspell_check.yml
================================================
name: Misspell Check
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: [master, develop]
jobs:
misspell-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install misspell
run: |
curl -L -o ./install-misspell.sh https://git.io/misspell
sh ./install-misspell.sh
- name: Run misspell
run: find . -type f -print0 | xargs -0 bin/misspell -error -i transfered,derivate
================================================
FILE: .github/workflows/pr-ci.yml
================================================
name: PR-CI
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
dist-tar:
name: Build distribution tar
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: "8"
cache: "maven"
- name: Build distribution tar
run: |
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
- uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: rocketmq
path: distribution/target/rocketmq*/rocketmq*
- name: Save PR number
run: |
mkdir -p ./pr
echo ${{ github.event.number }} > ./pr/NR
- uses: actions/upload-artifact@v4
with:
name: pr
path: pr/
================================================
FILE: .github/workflows/pr-e2e-test.yml
================================================
name: E2E test for pull request
# read-write repo token
# access to secrets
on:
workflow_run:
workflows: ["PR-CI"]
types:
- completed
env:
DOCKER_REPO: apache/rocketmq-ci
jobs:
docker:
if: >
github.repository == 'apache/rocketmq' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
base-image: ["ubuntu"]
java-version: ["8"]
steps:
- name: 'Download artifact'
uses: actions/github-script@v6
with:
script: |
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
let matchArtifactRmq = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "rocketmq"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifactRmq.id,
archive_format: 'zip',
});
var fs = require('fs');
fs.writeFileSync('${{github.workspace}}/rocketmq.zip', Buffer.from(download.data));
- run: |
unzip rocketmq.zip
mkdir rocketmq
cp -r rocketmq-* rocketmq/
ls
- uses: actions/checkout@v3
with:
repository: apache/rocketmq-docker.git
ref: master
path: rocketmq-docker
- name: docker-login
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and save docker images
id: build-images
run: |
cd rocketmq-docker/image-build-ci
version=${{ github.event.pull_request.number || github.ref_name }}-$(uuidgen)
mkdir versionlist
touch versionlist/"${version}-`echo ${{ matrix.base-image }} | sed -e "s/:/-/g"`"
sh ./build-image-local.sh ${version} ${{ matrix.base-image }} ${{ matrix.java-version }} ${DOCKER_REPO}
- uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: versionlist
path: rocketmq-docker/image-build-ci/versionlist/*
list-version:
if: >
github.repository == 'apache/rocketmq' &&
always()
name: List version
needs: [docker]
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
version-json: ${{ steps.show_versions.outputs.version-json }}
steps:
- uses: actions/download-artifact@v4
name: Download versionlist
with:
name: versionlist
path: versionlist
- name: Show versions
id: show_versions
run: |
a=(`ls versionlist`)
printf '%s\n' "${a[@]}" | jq -R . | jq -s .
echo version-json=`printf '%s\n' "${a[@]}" | jq -R . | jq -s .` >> $GITHUB_OUTPUT
deploy:
if: ${{ success() }}
name: Deploy RocketMQ
needs: [list-version,docker]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: Deploy rocketmq
with:
action: "deploy"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
chart-git: "https://ghproxy.com/https://github.com/apache/rocketmq-docker.git"
chart-branch: "master"
chart-path: "./rocketmq-k8s-helm"
job-id: ${{ strategy.job-index }}
helm-values: |
nameserver:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
broker:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
proxy:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
test-e2e-grpc-java:
if: ${{ success() }}
name: Test E2E grpc java
needs: [list-version, deploy]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://ghproxy.com/https://github.com/apache/rocketmq-e2e"
test-code-branch: "master"
test-code-path: java/e2e
test-cmd: "mvn -B test"
job-id: 0
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: test-e2e-grpc-java-log.txt
path: testlog.txt
test-e2e-golang:
if: ${{ success() }}
name: Test E2E golang
needs: [list-version, deploy]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://ghproxy.com/https://github.com/apache/rocketmq-e2e"
test-code-branch: "master"
test-code-path: golang
test-cmd: |
cd ../common && mvn -Prelease -DskipTests clean package -U
cd ../rocketmq-admintools && source bin/env.sh
LATEST_GO_VERSION=$(curl -s https://go.dev/VERSION?m=text | awk 'NR==1')
wget "https://go.dev/dl/${LATEST_GO_VERSION}.linux-amd64.tar.gz" && \
rm -rf /usr/local/go && tar -C /usr/local -xzf ${LATEST_GO_VERSION}.linux-amd64.tar.gz
cd ../golang && go get -u github.com/apache/rocketmq-clients/golang && gotestsum --junitfile ./target/surefire-reports/TEST-report.xml ./mqgotest/... -timeout 2m -v
job-id: 0
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: test-e2e-golang-log.txt
path: testlog.txt
test-e2e-remoting-java:
if: ${{ success() }}
name: Test E2E remoting java
needs: [ list-version, deploy ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://ghproxy.com/https://github.com/apache/rocketmq-e2e"
test-code-branch: "master"
test-code-path: java/e2e-v4
test-cmd: "mvn -B test"
job-id: 0
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: test-e2e-remoting-java-log.txt
path: testlog.txt
clean:
if: always()
name: Clean
needs: [list-version, test-e2e-grpc-java, test-e2e-golang, test-e2e-remoting-java]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: clean
with:
action: "clean"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
job-id: ${{ strategy.job-index }}
================================================
FILE: .github/workflows/push-ci.yml
================================================
name: PUSH-CI
on:
push:
branches: [master, develop]
#schedule:
# - cron: "0 18 * * *" # TimeZone: UTC 0
concurrency:
group: rocketmq-${{ github.ref }}
env:
MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
DOCKER_REPO: apache/rocketmq-ci
jobs:
dist-tar:
if: github.repository == 'apache/rocketmq'
name: Build dist tar
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: "8"
cache: "maven"
- name: Build distribution tar
run: |
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
- uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: rocketmq
path: distribution/target/rocketmq*/rocketmq*
docker:
if: ${{ success() }}
name: Docker images
needs: [dist-tar]
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
base-image: ["ubuntu"]
java-version: ["8"]
steps:
- uses: actions/checkout@v3
with:
repository: apache/rocketmq-docker.git
ref: master
path: rocketmq-docker
- uses: actions/download-artifact@v4
name: Download distribution tar
with:
name: rocketmq
path: rocketmq
- name: docker-login
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and save docker images
id: build-images
run: |
cd rocketmq-docker/image-build-ci
version=${{ github.event.pull_request.number || github.ref_name }}-$(uuidgen)
mkdir versionlist
touch versionlist/"${version}-`echo ${{ matrix.base-image }} | sed -e "s/:/-/g"`"
sh ./build-image-local.sh ${version} ${{ matrix.base-image }} ${{ matrix.java-version }} ${DOCKER_REPO}
- uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: versionlist
path: rocketmq-docker/image-build-ci/versionlist/*
list-version:
if: >
github.repository == 'apache/rocketmq' &&
always()
name: List version
needs: [docker]
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
version-json: ${{ steps.show_versions.outputs.version-json }}
steps:
- uses: actions/download-artifact@v4
name: Download versionlist
with:
name: versionlist
path: versionlist
- name: Show versions
id: show_versions
run: |
a=(`ls versionlist`)
printf '%s\n' "${a[@]}" | jq -R . | jq -s .
echo version-json=`printf '%s\n' "${a[@]}" | jq -R . | jq -s .` >> $GITHUB_OUTPUT
deploy-e2e:
if: ${{ success() }}
name: Deploy RocketMQ For E2E
needs: [list-version,docker]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: Deploy rocketmq
with:
action: "deploy"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
chart-git: "https://ghproxy.com/https://github.com/apache/rocketmq-docker.git"
chart-branch: "master"
chart-path: "./rocketmq-k8s-helm"
job-id: ${{ strategy.job-index }}
helm-values: |
nameserver:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
broker:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
proxy:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
deploy-benchmark:
if: ${{ success() }}
name: Deploy RocketMQ For Benchmarking
needs: [list-version,docker]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: Deploy rocketmq
with:
action: "deploy"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
chart-git: "https://ghproxy.com/https://github.com/apache/rocketmq-docker.git"
chart-branch: "master"
chart-path: "./rocketmq-k8s-helm"
job-id: "001-${{ strategy.job-index }}"
helm-values: |
nameserver:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
broker:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
proxy:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
test-e2e-grpc-java:
if: ${{ success() }}
name: Test E2E grpc java
needs: [list-version, deploy-e2e]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://ghproxy.com/https://github.com/apache/rocketmq-e2e"
test-code-branch: "master"
test-code-path: java/e2e
test-cmd: "mvn -B test"
job-id: 0
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: test-e2e-grpc-java-log.txt
path: testlog.txt
test-e2e-golang:
if: ${{ success() }}
name: Test E2E golang
needs: [list-version, deploy-e2e]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://ghproxy.com/https://github.com/apache/rocketmq-e2e"
test-code-branch: "master"
test-code-path: golang
test-cmd: |
cd ../common && mvn -Prelease -DskipTests clean package -U
cd ../rocketmq-admintools && source bin/env.sh
LATEST_GO_VERSION=$(curl -s https://go.dev/VERSION?m=text | awk 'NR==1')
wget "https://go.dev/dl/${LATEST_GO_VERSION}.linux-amd64.tar.gz" && \
rm -rf /usr/local/go && tar -C /usr/local -xzf ${LATEST_GO_VERSION}.linux-amd64.tar.gz
cd ../golang && go get -u github.com/apache/rocketmq-clients/golang && gotestsum --junitfile ./target/surefire-reports/TEST-report.xml ./mqgotest/... -timeout 2m -v
job-id: 0
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: test-e2e-golang-log.txt
path: testlog.txt
test-e2e-remoting-java:
if: ${{ success() }}
name: Test E2E remoting java
needs: [ list-version, deploy-e2e ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://ghproxy.com/https://github.com/apache/rocketmq-e2e"
test-code-branch: "master"
test-code-path: java/e2e-v4
test-cmd: "mvn -B test"
job-id: 0
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: test-e2e-remoting-java-log.txt
path: testlog.txt
benchmark-test:
if: ${{ success() }}
runs-on: ubuntu-latest
name: Performance benchmark test
needs: [ list-version, deploy-benchmark ]
timeout-minutes: 60
steps:
- uses: apache/rocketmq-test-tool/benchmark-runner@ce372e5f3906ca1891e4918b05be14608eae608e
name: Performance benchmark
with:
action: "performance-benchmark"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
job-id: "001-${{ strategy.job-index }}"
# The time to run the test, 15 minutes
test-time: "900"
# Some thresholds set in advance
min-send-tps-threshold: "12000"
max-rt-ms-threshold: "500"
avg-rt-ms-threshold: "10"
max-2c-rt-ms-threshold: "150"
avg-2c-rt-ms-threshold: "10"
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: benchmark-report
path: benchmark/
clean-e2e:
if: always()
name: Clean E2E
needs: [ list-version, test-e2e-grpc-java, test-e2e-golang, test-e2e-remoting-java ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: clean
with:
action: "clean"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
job-id: ${{ strategy.job-index }}
clean-benchmark:
if: always()
name: Clean Benchmarking
needs: [ list-version, benchmark-test ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@7d84d276ad7755b1dc5cf9657a7a9bff6ae6d288
name: clean
with:
action: "clean"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
job-id: "001-${{ strategy.job-index }}"
================================================
FILE: .github/workflows/rerun-workflow.yml
================================================
name: Rerun workflow
on:
workflow_run:
workflows: ["Build and Run Tests by Maven" , "Build and Run Tests by Bazel"]
types:
- completed
permissions:
actions: write
jobs:
rerun:
if: github.event.workflow_run.conclusion == 'failure' && fromJSON(github.event.workflow_run.run_attempt) < 3
runs-on: ubuntu-latest
steps:
- name: rerun ${{ github.event.workflow_run.id }}
env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh run watch ${{ github.event.workflow_run.id }} > /dev/null 2>&1
gh run rerun ${{ github.event.workflow_run.id }} --failed
================================================
FILE: .github/workflows/snapshot-automation.yml
================================================
# 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.
name: Snapshot Daily Release Automation
on:
schedule: # schedule the job to run at 12 a.m. daily
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
branch:
description: 'The branch to trigger the workflow, The default branch is "develop" when both branch and commit_id are empty'
required: false
commit_id:
description: 'The commit id to trigger the workflow. Do not set branch and commit_id together'
required: false
rocketmq_version:
description: 'Name of the SNAPSHOT version to be generated. The default version is "$VERSION-stable-SNAPSHOT"'
required: false
env:
MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
DOCKER_REPO: apache/rocketmq-ci
jobs:
dist-tar:
if: github.repository == 'apache/rocketmq'
name: Build dist tar
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout develop
if: github.event.inputs.branch == '' && github.event.inputs.commit_id == ''
uses: actions/checkout@v3
with:
ref: develop
- name: Checkout specific commit
if: github.event.inputs.branch == '' && github.event.inputs.commit_id != ''
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.commit_id }}
- name: Checkout specific branch
if: github.event.inputs.branch != '' && github.event.inputs.commit_id == ''
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.branch }}
- uses: actions/setup-java@v4
with:
distribution: "corretto"
java-version: "8"
cache: "maven"
- name: Build distribution tar
env:
MAVEN_SETTINGS: ${{ github.workspace }}/.github/asf-deploy-settings.xml
run: |
mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U
- uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: rocketmq
path: distribution/target/rocketmq*/rocketmq*
docker-build:
if: ${{ success() }}
name: Docker images
needs: [ dist-tar ]
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
base-image: [ "ubuntu" ]
java-version: [ "8" ]
steps:
- uses: actions/checkout@v3
with:
repository: apache/rocketmq-docker.git
ref: master
path: rocketmq-docker
- uses: actions/download-artifact@v4
name: Download distribution tar
with:
name: rocketmq
path: rocketmq
- name: docker-login
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and save docker images
id: build-images
run: |
cd rocketmq-docker/image-build-ci
version=${{ github.event.pull_request.number || github.ref_name }}-$(uuidgen)
mkdir versionlist
touch versionlist/"${version}-`echo ${{ matrix.base-image }} | sed -e "s/:/-/g"`"
sh ./build-image-local.sh ${version} ${{ matrix.base-image }} ${{ matrix.java-version }} ${DOCKER_REPO}
- uses: actions/upload-artifact@v4
name: Upload distribution tar
with:
name: versionlist
path: rocketmq-docker/image-build-ci/versionlist/*
list-version:
if: >
github.repository == 'apache/rocketmq' &&
always()
name: List version
needs: [ docker-build ]
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
version-json: ${{ steps.show_versions.outputs.version-json }}
steps:
- uses: actions/download-artifact@v4
name: Download versionlist
with:
name: versionlist
path: versionlist
- name: Show versions
id: show_versions
run: |
a=(`ls versionlist`)
printf '%s\n' "${a[@]}" | jq -R . | jq -s .
echo version-json=`printf '%s\n' "${a[@]}" | jq -R . | jq -s .` >> $GITHUB_OUTPUT
deploy-rocketmq:
if: ${{ success() }}
name: Deploy RocketMQ
needs: [ list-version,docker-build ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@1a646589accad17070423eabf0f54925e52b0666
name: Deploy rocketmq
with:
action: "deploy"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
chart-git: "https://github.com/apache/rocketmq-docker.git"
chart-branch: "master"
chart-path: "./rocketmq-k8s-helm"
job-id: ${{ strategy.job-index }}
helm-values: |
nameserver:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
broker:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
proxy:
image:
repository: ${{env.DOCKER_REPO}}
tag: ${{ matrix.version }}
java-grpc-e2e-test:
if: ${{ success() }}
name: E2E Test
needs: [ list-version, deploy-rocketmq ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@1a646589accad17070423eabf0f54925e52b0666
name: e2e test
with:
action: "test"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
test-code-git: "https://github.com/apache/rocketmq-e2e.git"
test-code-branch: "master"
test-code-path: java/e2e
test-cmd: "mvn -B test"
job-id: ${{ strategy.job-index }}
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: '**/test_report/TEST-*.xml'
annotate_only: true
include_passed: true
detailed_summary: true
- uses: actions/upload-artifact@v4
if: always()
name: Upload test log
with:
name: testlog.txt
path: testlog.txt
clean:
if: always()
name: Clean
needs: [ list-version, java-grpc-e2e-test ]
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
matrix:
version: ${{ fromJSON(needs.list-version.outputs.version-json) }}
steps:
- uses: apache/rocketmq-test-tool@1a646589accad17070423eabf0f54925e52b0666
name: clean
with:
action: "clean"
ask-config: "${{ secrets.ASK_CONFIG_VIRGINA }}"
test-version: "${{ matrix.version }}"
job-id: ${{ strategy.job-index }}
snapshot:
runs-on: ubuntu-latest
needs: [ java-grpc-e2e-test ]
env:
NEXUS_DEPLOY_USERNAME: ${{ secrets.NEXUS_USER }}
NEXUS_DEPLOY_PASSWORD: ${{ secrets.NEXUS_PW }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: develop
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 8
distribution: "corretto"
cache: "maven"
- name: Update default pom version
if: github.event.inputs.rocketmq_version == ''
run: |
VERSION=$(mvn -q -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive exec:exec)
VERSION=$(echo $VERSION | awk -F '-SNAPSHOT' '{print $1}')
VERSION=$VERSION-stable-SNAPSHOT
mvn versions:set -DnewVersion=$VERSION
- name: Update User-defined pom version
if: github.event.inputs.rocketmq_version != ''
run: |
mvn versions:set -DnewVersion=${{ github.event.inputs.rocketmq_version }}
- name: Deploy to ASF Snapshots Repository
timeout-minutes: 40
run: mvn clean deploy -DskipTests=true --settings .github/asf-deploy-settings.xml
================================================
FILE: .github/workflows/stale.yml
================================================
name: Close Stale Issues/PRs
permissions:
issues: write
pull-requests: write
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
operations-per-run: 128
days-before-issue-stale: 365
days-before-issue-close: 3
exempt-issue-labels: "no stale"
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 365 days with no activity. It will be closed in 3 days if no further activity occurs."
close-issue-message: "This issue was closed because it has been inactive for 3 days since being marked as stale."
remove-issue-stale-when-updated: true
days-before-pr-stale: 365
days-before-pr-close: 3
exempt-pr-labels: "no stale"
stale-pr-label: "stale"
stale-pr-message: "This PR is stale because it has been open for 365 days with no activity. It will be closed in 3 days if no further activity occurs. If you wish not to mark it as stale, please leave a comment in this PR."
close-pr-message: "This PR was closed because it has been inactive for 3 days since being marked as stale."
remove-pr-stale-when-updated: true
================================================
FILE: .gitignore
================================================
*dependency-reduced-pom.xml
.classpath
.project
.settings/
target/
devenv
*.log.*
*.iml
.idea/
*.versionsBackup
!NOTICE-BIN
!LICENSE-BIN
.DS_Store
localbin
nohup.out
bazel-out
bazel-bin
bazel-rocketmq
bazel-testlogs
.vscode
MODULE.bazel.lock
*.flattened-pom.xml
================================================
FILE: .licenserc.yaml
================================================
#
# 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.
#
header:
license:
spdx-id: Apache-2.0
copyright-owner: Apache Software Foundation
paths-ignore:
- '.gitignore'
- '.travis.yml'
- 'CONTRIBUTING.md'
- 'LICENSE'
- 'NOTICE'
- '**/*.md'
- 'BUILDING'
- '.github/**'
- '*/src/test/resources/certs/*'
- 'src/test/**/*.log'
- '*/src/test/resources/META-INF/service/*'
- '*/src/main/resources/META-INF/service/*'
- '*/src/test/resources/rmq-proxy-home/conf/rmq-proxy.json'
- '*/src/test/resources/mockito-extensions/*'
- '**/target/**'
- '**/*.iml'
- 'docs/**'
- 'localbin/**'
- 'distribution/LICENSE-BIN'
- 'distribution/NOTICE-BIN'
- 'distribution/conf/rmq-proxy.json'
- '.bazelversion'
- 'common/src/main/resources/META-INF/services/org.apache.rocketmq.logging.ch.qos.logback.classic.spi.Configurator'
comment: on-failure
================================================
FILE: BUILD.bazel
================================================
#
# 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.
#
java_library(
name = "test_deps",
visibility = ["//visibility:public"],
exports = [
"@maven//:junit_junit",
"@maven//:org_assertj_assertj_core",
"@maven//:org_hamcrest_hamcrest_library",
"@maven//:org_mockito_mockito_core",
"@maven//:org_powermock_powermock_module_junit4",
"@maven//:org_powermock_powermock_api_mockito2",
"@maven//:org_hamcrest_hamcrest_core",
"@maven//:ch_qos_logback_logback_classic",
"@maven//:org_awaitility_awaitility",
"@maven//:org_openjdk_jmh_jmh_core",
"@maven//:org_openjdk_jmh_jmh_generator_annprocess",
"@maven//:org_mockito_mockito_junit_jupiter",
],
)
================================================
FILE: BUILDING
================================================
Build Instructions for Apache RocketMQ
====================================================
(1) Prerequisites
JDK 1.8+ is required in order to compile and run RocketMQ.
RocketMQ utilizes Maven as a distribution management and packaging tool. Version 3.0.3 or later is required.
Maven installation and configuration instructions can be found here:
http://maven.apache.org/run-maven/index.html
(2) Run test cases
Execute the following command in order to compile and run test cases of each components:
$ mvn test
(3) Import projects to Eclipse IDE
First, generate eclipse project files:
$ mvn -U eclipse:eclipse
Then, import to eclipse by specifying the root directory of the project via:
[File] > [Import] > [Existing Projects into Workspace].
(4) Build distribution packages
Execute the following command in order to build the tar.gz packages and install JAR into local repository:
$ mvn -Prelease-all -DskipTests clean install -U
================================================
FILE: CONTRIBUTING.md
================================================
## How To Contribute
We are always very happy to have contributions, whether for trivial cleanups or big new features.
We want to have high quality, well documented codes for each programming language, as well as the surrounding [ecosystem](https://github.com/apache/rocketmq-externals) of integration tools that people use with RocketMQ.
Nor is code the only way to contribute to the project. We strongly value documentation, integration with other projects, and gladly accept improvements for these aspects.
Recommend reading:
* [Contributors Tech Guide](http://www.apache.org/dev/contributors)
* [Get involved!](http://www.apache.org/foundation/getinvolved.html)
## Contributing code
To submit a change for inclusion, please do the following:
#### If the change is non-trivial please include some unit tests that cover the new functionality.
#### If you are introducing a completely new feature or API it is a good idea to start a [RIP](https://github.com/apache/rocketmq/wiki/RocketMQ-Improvement-Proposal) and get consensus on the basic design first.
#### It is our job to follow up on patches in a timely fashion. Nag us if we aren't doing our job (sometimes we drop things).
### Squash commits
If you have a pull request on GitHub, and updated more than once, it's better to squash all commits.
1. Identify how many commits you made since you began: ``git log``.
2. Squash these commits by N: ``git rebase -i HEAD~N`` .
3. Leave "pick" tag in the first line.
4. Change all other commits from "pick" to "fixup".
5. Then do "force push" to overwrite remote history: ``git push -u origin ROCKETMQ-9999 --force``
6. All your changes are now in a single commit, that would be much better for review.
More details of squash can be found at [stackoverflow](https://stackoverflow.com/questions/5189560/squash-my-last-x-commits-together-using-git).
## Becoming a Committer
We are always interested in adding new contributors. What we look for are series of contributions, good taste and ongoing interest in the project. If you are interested in becoming a committer, please let one of the existing committers know and they can help you walk through the process.
Nowadays, we have several important contribution points:
#### Wiki & JavaDoc
#### RocketMQ SDK(C++\.Net\Php\Python\Go\Node.js)
#### RocketMQ Connectors
##### Prerequisites
If you want to contribute to the above listed points, you must abide by the following prerequisites:
###### Readability - API must have Javadoc, and some very important methods must also have Javadoc
###### Testability - Above 80% unit test coverage for the main process
###### Maintainability - Comply with our [checkstyle spec](style/rmq_checkstyle.xml), and at least a 3-month update frequency
###### Deployability - We encourage you to deploy into [maven repository](http://search.maven.org/)
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
================================================
FILE: MODULE.bazel
================================================
#
# 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.
#
###############################################################################
# Bazel now uses Bzlmod by default to manage external dependencies.
# Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel.
#
# For more details, please check https://github.com/bazelbuild/bazel/issues/18958
###############################################################################
================================================
FILE: NOTICE
================================================
Apache RocketMQ
Copyright 2016-2026 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
================================================
FILE: README.md
================================================
## Apache RocketMQ
[![Build Status][maven-build-image]][maven-build-url]
[![CodeCov][codecov-image]][codecov-url]
[![Maven Central][maven-central-image]][maven-central-url]
[![Release][release-image]][release-url]
[![License][license-image]][license-url]
[![Average Time to Resolve An Issue][average-time-to-resolve-an-issue-image]][average-time-to-resolve-an-issue-url]
[![Percentage of Issues Still Open][percentage-of-issues-still-open-image]][percentage-of-issues-still-open-url]
[![Twitter Follow][twitter-follow-image]][twitter-follow-url]
**[Apache RocketMQ](https://rocketmq.apache.org) is a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.**
It offers a variety of features:
* Messaging patterns including publish/subscribe, request/reply and streaming
* Financial grade transactional message
* Built-in fault tolerance and high availability configuration options based on [DLedger Controller](docs/en/controller/quick_start.md)
* Built-in message tracing capability, also supports opentracing
* Versatile big-data and streaming ecosystem integration
* Message retroactivity by time or offset
* Reliable FIFO and strict ordered messaging in the same queue
* Efficient pull and push consumption model
* Million-level message accumulation capacity in a single queue
* Multiple messaging protocols like gRPC, MQTT, JMS and OpenMessaging
* Flexible distributed scale-out deployment architecture
* Lightning-fast batch message exchange system
* Various message filter mechanics such as SQL and Tag
* Docker images for isolated testing and cloud isolated clusters
* Feature-rich administrative dashboard for configuration, metrics and monitoring
* Authentication and authorization
* Free open source connectors, for both sources and sinks
* Lightweight real-time computing
----------
## Quick Start
This paragraph guides you through steps of installing RocketMQ in different ways.
For local development and testing, only one instance will be created for each component.
### Run RocketMQ locally
RocketMQ runs on all major operating systems and requires only a Java JDK version 8 or higher to be installed.
To check, run `java -version`:
```shell
$ java -version
java version "1.8.0_121"
```
For Windows users, click [here](https://dist.apache.org/repos/dist/release/rocketmq/5.4.0/rocketmq-all-5.4.0-bin-release.zip) to download the 5.4.0 RocketMQ binary release,
unpack it to your local disk, such as `D:\rocketmq`.
For macOS and Linux users, execute following commands:
```shell
# Download release from the Apache mirror
$ wget https://dist.apache.org/repos/dist/release/rocketmq/5.4.0/rocketmq-all-5.4.0-bin-release.zip
# Unpack the release
$ unzip rocketmq-all-5.4.0-bin-release.zip
```
Prepare a terminal and change to the extracted `bin` directory:
```shell
$ cd rocketmq-all-5.4.0-bin-release/bin
```
**1) Start NameServer**
NameServer will be listening at `0.0.0.0:9876`, make sure that the port is not used by others on the local machine, and then do as follows.
For macOS and Linux users:
```shell
### start Name Server
$ nohup sh mqnamesrv &
### check whether Name Server is successfully started
$ tail -f ~/logs/rocketmqlogs/namesrv.log
The Name Server boot success...
```
For Windows users, you need to set environment variables first:
- From the desktop, right click the Computer icon.
- Choose Properties from the context menu.
- Click the Advanced system settings link.
- Click Environment Variables.
- Add Environment `ROCKETMQ_HOME="D:\rocketmq"`.
Then change directory to rocketmq, type and run:
```shell
$ mqnamesrv.cmd
The Name Server boot success...
```
**2) Start Broker**
For macOS and Linux users:
```shell
### start Broker
$ nohup sh mqbroker -n localhost:9876 &
### check whether Broker is successfully started, eg: Broker's IP is 192.168.1.2, Broker's name is broker-a
$ tail -f ~/logs/rocketmqlogs/broker.log
The broker[broker-a, 192.168.1.2:10911] boot success...
```
For Windows users:
```shell
$ mqbroker.cmd -n localhost:9876
The broker[broker-a, 192.168.1.2:10911] boot success...
```
### Run RocketMQ in Docker
You can run RocketMQ on your own machine within Docker containers,
`host` network will be used to expose listening port in the container.
**1) Start NameServer**
```shell
$ docker run -it --net=host apache/rocketmq ./mqnamesrv
```
**2) Start Broker**
```shell
$ docker run -it --net=host --mount type=bind,source=/tmp/store,target=/home/rocketmq/store apache/rocketmq ./mqbroker -n localhost:9876
```
### Run RocketMQ in Kubernetes
You can also run a RocketMQ cluster within a Kubernetes cluster using [RocketMQ Operator](https://github.com/apache/rocketmq-operator).
Before your operations, make sure that `kubectl` and related kubeconfig file installed on your machine.
**1) Install CRDs**
```shell
### install CRDs
$ git clone https://github.com/apache/rocketmq-operator
$ cd rocketmq-operator && make deploy
### check whether CRDs are successfully installed
$ kubectl get crd | grep rocketmq.apache.org
brokers.rocketmq.apache.org 2022-05-12T09:23:18Z
consoles.rocketmq.apache.org 2022-05-12T09:23:19Z
nameservices.rocketmq.apache.org 2022-05-12T09:23:18Z
topictransfers.rocketmq.apache.org 2022-05-12T09:23:19Z
### check whether operator is running
$ kubectl get pods | grep rocketmq-operator
rocketmq-operator-6f65c77c49-8hwmj 1/1 Running 0 93s
```
**2) Create Cluster Instance**
```shell
### create RocketMQ cluster resource
$ cd example && kubectl create -f rocketmq_v1alpha1_rocketmq_cluster.yaml
### check whether cluster resources are running
$ kubectl get sts
NAME READY AGE
broker-0-master 1/1 107m
broker-0-replica-1 1/1 107m
name-service 1/1 107m
```
---
## Apache RocketMQ Community
* [RocketMQ Streams](https://github.com/apache/rocketmq-streams): A lightweight stream computing engine based on Apache RocketMQ.
* [RocketMQ Flink](https://github.com/apache/rocketmq-flink): The Apache RocketMQ connector of Apache Flink that supports source and sink connector in data stream and Table.
* [RocketMQ APIs](https://github.com/apache/rocketmq-apis): RocketMQ protobuf protocol.
* [RocketMQ Clients](https://github.com/apache/rocketmq-clients): gRPC/protobuf-based RocketMQ clients.
* RocketMQ Remoting-based Clients
- [RocketMQ Client CPP](https://github.com/apache/rocketmq-client-cpp)
- [RocketMQ Client Go](https://github.com/apache/rocketmq-client-go)
- [RocketMQ Client Python](https://github.com/apache/rocketmq-client-python)
- [RocketMQ Client Nodejs](https://github.com/apache/rocketmq-client-nodejs)
* [RocketMQ Spring](https://github.com/apache/rocketmq-spring): A project which helps developers quickly integrate Apache RocketMQ with Spring Boot.
* [RocketMQ Exporter](https://github.com/apache/rocketmq-exporter): An Apache RocketMQ exporter for Prometheus.
* [RocketMQ Operator](https://github.com/apache/rocketmq-operator): Providing a way to run an Apache RocketMQ cluster on Kubernetes.
* [RocketMQ Docker](https://github.com/apache/rocketmq-docker): The Git repo of the Docker Image for Apache RocketMQ.
* [RocketMQ Dashboard](https://github.com/apache/rocketmq-dashboard): Operation and maintenance console of Apache RocketMQ.
* [RocketMQ Connect](https://github.com/apache/rocketmq-connect): A tool for scalably and reliably streaming data between Apache RocketMQ and other systems.
* [RocketMQ MQTT](https://github.com/apache/rocketmq-mqtt): A new MQTT protocol architecture model, based on which Apache RocketMQ can better support messages from terminals such as IoT devices and Mobile APP.
* [RocketMQ EventBridge](https://github.com/apache/rocketmq-eventbridge): EventBridge makes it easier to build an event-driven application.
* [RocketMQ Incubating Community Projects](https://github.com/apache/rocketmq-externals): Incubator community projects of Apache RocketMQ, including [logappender](https://github.com/apache/rocketmq-externals/tree/master/logappender), [rocketmq-ansible](https://github.com/apache/rocketmq-externals/tree/master/rocketmq-ansible), [rocketmq-beats-integration](https://github.com/apache/rocketmq-externals/tree/master/rocketmq-beats-integration), [rocketmq-cloudevents-binding](https://github.com/apache/rocketmq-externals/tree/master/rocketmq-cloudevents-binding), etc.
* [RocketMQ Site](https://github.com/apache/rocketmq-site): The repository for Apache RocketMQ website.
* [RocketMQ E2E](https://github.com/apache/rocketmq-e2e): A project for testing Apache RocketMQ, including end-to-end, performance, compatibility tests.
----------
## Learn it & Contact us
* Mailing Lists:
* Home:
* Docs:
* Issues:
* Rips:
* Ask:
* Slack:
----------
## Contributing
We always welcome new contributions, whether for trivial cleanups, [big new features](https://github.com/apache/rocketmq/wiki/RocketMQ-Improvement-Proposal) or other material rewards, more details see [here](http://rocketmq.apache.org/docs/how-to-contribute/).
----------
## License
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) Copyright (C) Apache Software Foundation
----------
## Export Control Notice
This distribution includes cryptographic software. The country in which you currently reside may have
restrictions on the import, possession, use, and/or re-export to another country, of encryption software.
BEFORE using any encryption software, please check your country's laws, regulations and policies concerning
the import, possession, or use, and re-export of encryption software, to see if this is permitted. See
for more information.
The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this
software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software
using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache
Software Foundation distribution makes it eligible for export under the License Exception ENC Technology
Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for
both object code and source code.
The following provides more details on the included cryptographic software:
This software uses Apache Commons Crypto (https://commons.apache.org/proper/commons-crypto/) to
support authentication, and encryption and decryption of data sent across the network between
services.
[maven-build-image]: https://github.com/apache/rocketmq/actions/workflows/maven.yaml/badge.svg
[maven-build-url]: https://github.com/apache/rocketmq/actions/workflows/maven.yaml
[codecov-image]: https://codecov.io/gh/apache/rocketmq/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/apache/rocketmq
[maven-central-image]: https://maven-badges.herokuapp.com/maven-central/org.apache.rocketmq/rocketmq-all/badge.svg
[maven-central-url]: http://search.maven.org/#search%7Cga%7C1%7Corg.apache.rocketmq
[release-image]: https://img.shields.io/badge/release-download-orange.svg
[release-url]: https://rocketmq.apache.org/download/
[license-image]: https://img.shields.io/badge/license-Apache%202-4EB1BA.svg
[license-url]: https://www.apache.org/licenses/LICENSE-2.0.html
[average-time-to-resolve-an-issue-image]: http://isitmaintained.com/badge/resolution/apache/rocketmq.svg
[average-time-to-resolve-an-issue-url]: http://isitmaintained.com/project/apache/rocketmq
[percentage-of-issues-still-open-image]: http://isitmaintained.com/badge/open/apache/rocketmq.svg
[percentage-of-issues-still-open-url]: http://isitmaintained.com/project/apache/rocketmq
[twitter-follow-image]: https://img.shields.io/twitter/follow/ApacheRocketMQ?style=social
[twitter-follow-url]: https://twitter.com/intent/follow?screen_name=ApacheRocketMQ
================================================
FILE: WORKSPACE
================================================
#
# 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.
#
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
RULES_JVM_EXTERNAL_TAG = "4.2"
RULES_JVM_EXTERNAL_SHA = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca"
http_archive(
name = "rules_jvm_external",
sha256 = RULES_JVM_EXTERNAL_SHA,
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)
load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
rules_jvm_external_setup()
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
artifacts = [
"junit:junit:4.13.2",
"com.alibaba:fastjson:1.2.76",
"com.alibaba.fastjson2:fastjson2:2.0.59",
"org.hamcrest:hamcrest-library:1.3",
"io.netty:netty-all:4.1.65.Final",
"org.assertj:assertj-core:3.22.0",
"org.mockito:mockito-core:3.10.0",
"org.powermock:powermock-module-junit4:2.0.9",
"org.powermock:powermock-api-mockito2:2.0.9",
"org.powermock:powermock-core:2.0.9",
"com.github.luben:zstd-jni:1.5.2-2",
"org.lz4:lz4-java:1.8.0",
"commons-validator:commons-validator:1.7",
"org.apache.commons:commons-lang3:3.12.0",
"org.hamcrest:hamcrest-core:1.3",
"io.openmessaging.storage:dledger:0.3.2",
"net.java.dev.jna:jna:4.2.2",
"ch.qos.logback:logback-classic:1.2.10",
"ch.qos.logback:logback-core:1.2.10",
"io.opentracing:opentracing-api:0.33.0",
"io.opentracing:opentracing-mock:0.33.0",
"commons-collections:commons-collections:3.2.2",
"org.awaitility:awaitility:4.1.0",
"commons-cli:commons-cli:1.5.0",
"com.google.guava:guava:31.0.1-jre",
"org.yaml:snakeyaml:2.0",
"commons-codec:commons-codec:1.13",
"commons-io:commons-io:2.7",
"com.google.truth:truth:0.30",
"org.bouncycastle:bcpkix-jdk15on:1.69",
"com.google.code.gson:gson:2.8.9",
"com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2",
"org.apache.rocketmq:rocketmq-proto:2.1.1",
"com.google.protobuf:protobuf-java:3.20.1",
"com.google.protobuf:protobuf-java-util:3.20.1",
"com.conversantmedia:disruptor:1.2.10",
"org.apache.tomcat:annotations-api:6.0.53",
"com.google.code.findbugs:jsr305:3.0.2",
"org.checkerframework:checker-qual:3.12.0",
"org.reflections:reflections:0.9.11",
"org.openjdk.jmh:jmh-core:1.19",
"org.openjdk.jmh:jmh-generator-annprocess:1.19",
"com.github.ben-manes.caffeine:caffeine:2.9.3",
"io.grpc:grpc-services:1.47.0",
"io.grpc:grpc-netty-shaded:1.47.0",
"io.grpc:grpc-context:1.47.0",
"io.grpc:grpc-stub:1.47.0",
"io.grpc:grpc-api:1.47.0",
"io.grpc:grpc-testing:1.47.0",
"org.springframework:spring-core:5.3.26",
"io.opentelemetry:opentelemetry-exporter-otlp:1.29.0",
"io.opentelemetry:opentelemetry-exporter-prometheus:1.29.0-alpha",
"io.opentelemetry:opentelemetry-exporter-logging:1.29.0",
"io.opentelemetry:opentelemetry-sdk:1.29.0",
"io.opentelemetry:opentelemetry-exporter-logging-otlp:1.29.0",
"com.squareup.okio:okio-jvm:3.0.0",
"io.opentelemetry:opentelemetry-api:1.29.0",
"io.opentelemetry:opentelemetry-sdk-metrics:1.29.0",
"io.opentelemetry:opentelemetry-sdk-common:1.29.0",
"io.github.aliyunmq:rocketmq-slf4j-api:1.0.0",
"io.github.aliyunmq:rocketmq-logback-classic:1.0.0",
"org.slf4j:jul-to-slf4j:2.0.6",
"org.jetbrains:annotations:23.1.0",
"io.github.aliyunmq:rocketmq-shaded-slf4j-api-bridge:1.0.0",
"software.amazon.awssdk:s3:2.20.29",
"com.fasterxml.jackson.core:jackson-databind:2.13.4.2",
"com.adobe.testing:s3mock-junit4:2.11.0",
"io.github.aliyunmq:rocketmq-grpc-netty-codec-haproxy:1.0.0",
"org.apache.rocketmq:rocketmq-rocksdb:1.0.6",
"com.alipay.sofa:jraft-core:1.3.14",
"com.alipay.sofa:hessian:3.3.6",
"io.netty:netty-tcnative-boringssl-static:2.0.48.Final",
"org.mockito:mockito-junit-jupiter:4.11.0",
"com.alibaba.fastjson2:fastjson2:2.0.59",
"org.junit.jupiter:junit-jupiter-api:5.9.1",
],
fetch_sources = True,
repositories = [
# Private repositories are supported through HTTP Basic auth
"https://repo1.maven.org/maven2",
],
)
http_archive(
name = "io_buildbuddy_buildbuddy_toolchain",
sha256 = "b12273608db627eb14051eb75f8a2134590172cd69392086d392e25f3954ea6e",
strip_prefix = "buildbuddy-toolchain-8d5d18373adfca9d8e33b4812915abc9b132f1ee",
urls = ["https://github.com/buildbuddy-io/buildbuddy-toolchain/archive/8d5d18373adfca9d8e33b4812915abc9b132f1ee.tar.gz"],
)
load("@io_buildbuddy_buildbuddy_toolchain//:deps.bzl", "buildbuddy_deps")
buildbuddy_deps()
load("@io_buildbuddy_buildbuddy_toolchain//:rules.bzl", "buildbuddy")
buildbuddy(name = "buildbuddy_toolchain")
http_archive(
name = "bazel_skylib",
sha256 = "51b5105a760b353773f904d2bbc5e664d0987fbaf22265164de65d43e910d8ac",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.8.1/bazel-skylib-1.8.1.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.8.1/bazel-skylib-1.8.1.tar.gz",
],
)
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()
http_archive(
name = "rules_java",
urls = [
"https://github.com/bazelbuild/rules_java/releases/download/7.12.5/rules_java-7.12.5.tar.gz",
],
sha256 = "17b18cb4f92ab7b94aa343ce78531b73960b1bed2ba166e5b02c9fdf0b0ac270",
)
load("@rules_java//java:repositories.bzl", "rules_java_dependencies", "rules_java_toolchains")
rules_java_dependencies()
rules_java_toolchains()
load("@rules_java//toolchains:local_java_repository.bzl", "local_java_repository")
local_java_repository(
name = "jdk8",
version = "8",
java_home = "/usr/lib/jvm/java-8-openjdk-amd64",
)
================================================
FILE: auth/BUILD.bazel
================================================
#
# 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.
#
load("//bazel:GenTestRules.bzl", "GenTestRules")
java_library(
name = "auth",
srcs = glob(["src/main/java/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
"//common",
"//remoting",
"//client",
"@maven//:commons_codec_commons_codec",
"@maven//:org_apache_commons_commons_lang3",
"@maven//:commons_collections_commons_collections",
"@maven//:com_alibaba_fastjson2_fastjson2",
"@maven//:org_apache_rocketmq_rocketmq_proto",
"@maven//:org_slf4j_slf4j_api",
"@maven//:com_github_ben_manes_caffeine_caffeine",
"@maven//:io_grpc_grpc_api",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_google_protobuf_protobuf_java_util",
"@maven//:io_netty_netty_all",
"@maven//:com_google_guava_guava",
"@maven//:org_apache_rocketmq_rocketmq_rocksdb",
],
)
java_library(
name = "tests",
srcs = glob(["src/test/java/**/*.java"]),
resources = glob(["src/test/resources/**/*.yml"]),
visibility = ["//visibility:public"],
deps = [
":auth",
"//:test_deps",
"//common",
"//remoting",
"//client",
"@maven//:commons_codec_commons_codec",
"@maven//:org_apache_commons_commons_lang3",
"@maven//:commons_collections_commons_collections",
"@maven//:com_alibaba_fastjson2_fastjson2",
"@maven//:org_apache_rocketmq_rocketmq_proto",
"@maven//:org_slf4j_slf4j_api",
"@maven//:com_github_ben_manes_caffeine_caffeine",
"@maven//:io_grpc_grpc_api",
"@maven//:com_google_protobuf_protobuf_java",
"@maven//:com_google_protobuf_protobuf_java_util",
"@maven//:io_netty_netty_all",
"@maven//:com_google_guava_guava",
"@maven//:org_apache_rocketmq_rocketmq_rocksdb",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["src/test/java/**/*Test.java"]),
deps = [
":tests",
],
)
================================================
FILE: auth/pom.xml
================================================
4.0.0
org.apache.rocketmq
rocketmq-all
${revision}
rocketmq-auth
rocketmq-auth ${project.version}
${basedir}/..
${project.groupId}
rocketmq-proto
${project.groupId}
rocketmq-client
commons-codec
commons-codec
org.apache.commons
commons-lang3
com.google.protobuf
protobuf-java-util
org.slf4j
slf4j-api
com.github.ben-manes.caffeine
caffeine
org.checkerframework
checker-qual
junit
junit
maven-surefire-plugin
${maven-surefire-plugin.version}
1
false
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/AuthenticationEvaluator.java
================================================
/*
* 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.rocketmq.auth.authentication;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.authentication.context.AuthenticationContext;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.strategy.AuthenticationStrategy;
import org.apache.rocketmq.auth.config.AuthConfig;
public class AuthenticationEvaluator {
private final AuthenticationStrategy authenticationStrategy;
public AuthenticationEvaluator(AuthConfig authConfig) {
this(authConfig, null);
}
public AuthenticationEvaluator(AuthConfig authConfig, Supplier> metadataService) {
this.authenticationStrategy = AuthenticationFactory.getStrategy(authConfig, metadataService);
}
public void evaluate(AuthenticationContext context) {
if (context == null) {
return;
}
this.authenticationStrategy.evaluate(context);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/builder/AuthenticationContextBuilder.java
================================================
/*
* 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.rocketmq.auth.authentication.builder;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public interface AuthenticationContextBuilder {
AuthenticationContext build(Metadata metadata, GeneratedMessageV3 request);
AuthenticationContext build(ChannelHandlerContext context, RemotingCommand request);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/builder/DefaultAuthenticationContextBuilder.java
================================================
/*
* 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.rocketmq.auth.authentication.builder;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.acl.common.AclUtils;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.auth.authentication.context.DefaultAuthenticationContext;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.constant.CommonConstants;
import org.apache.rocketmq.common.constant.GrpcConstants;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class DefaultAuthenticationContextBuilder implements AuthenticationContextBuilder {
private static final String CREDENTIAL = "Credential";
private static final String SIGNATURE = "Signature";
@Override
public DefaultAuthenticationContext build(Metadata metadata, GeneratedMessageV3 request) {
try {
DefaultAuthenticationContext context = new DefaultAuthenticationContext();
context.setChannelId(metadata.get(GrpcConstants.CHANNEL_ID));
context.setRpcCode(request.getDescriptorForType().getFullName());
String authorization = metadata.get(GrpcConstants.AUTHORIZATION);
if (StringUtils.isEmpty(authorization)) {
return context;
}
String datetime = metadata.get(GrpcConstants.DATE_TIME);
if (StringUtils.isEmpty(datetime)) {
throw new AuthenticationException("datetime is null.");
}
String[] result = authorization.split(CommonConstants.SPACE, 2);
if (result.length != 2) {
throw new AuthenticationException("authentication header is incorrect.");
}
String[] keyValues = result[1].split(CommonConstants.COMMA);
for (String keyValue : keyValues) {
String[] kv = keyValue.trim().split(CommonConstants.EQUAL, 2);
int kvLength = kv.length;
if (kv.length != 2) {
throw new AuthenticationException("authentication keyValues length is incorrect, actual length={}.", kvLength);
}
String authItem = kv[0];
if (CREDENTIAL.equals(authItem)) {
String[] credential = kv[1].split(CommonConstants.SLASH);
int credentialActualLength = credential.length;
if (credentialActualLength == 0) {
throw new AuthenticationException("authentication credential length is incorrect, actual length={}.", credentialActualLength);
}
context.setUsername(credential[0]);
continue;
}
if (SIGNATURE.equals(authItem)) {
context.setSignature(this.hexToBase64(kv[1]));
}
}
context.setContent(datetime.getBytes(StandardCharsets.UTF_8));
return context;
} catch (AuthenticationException e) {
throw e;
} catch (Throwable e) {
throw new AuthenticationException("create authentication context error.", e);
}
}
@Override
public DefaultAuthenticationContext build(ChannelHandlerContext context, RemotingCommand request) {
HashMap fields = request.getExtFields();
DefaultAuthenticationContext result = new DefaultAuthenticationContext();
result.setChannelId(context.channel().id().asLongText());
result.setRpcCode(String.valueOf(request.getCode()));
if (MapUtils.isEmpty(fields)) {
return result;
}
if (!fields.containsKey(SessionCredentials.ACCESS_KEY)) {
return result;
}
result.setUsername(fields.get(SessionCredentials.ACCESS_KEY));
result.setSignature(fields.get(SessionCredentials.SIGNATURE));
// Content
SortedMap map = new TreeMap<>();
for (Map.Entry entry : fields.entrySet()) {
if (request.getVersion() <= MQVersion.Version.V4_9_3.ordinal() &&
MixAll.UNIQUE_MSG_QUERY_FLAG.equals(entry.getKey())) {
continue;
}
if (!SessionCredentials.SIGNATURE.equals(entry.getKey())) {
map.put(entry.getKey(), entry.getValue());
}
}
result.setContent(AclUtils.combineRequestContent(request, map));
return result;
}
public String hexToBase64(String input) throws DecoderException {
byte[] bytes = Hex.decodeHex(input);
return Base64.encodeBase64String(bytes);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/chain/DefaultAuthenticationHandler.java
================================================
/*
* 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.rocketmq.auth.authentication.chain;
import java.security.MessageDigest;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.acl.common.AclSigner;
import org.apache.rocketmq.auth.authentication.context.DefaultAuthenticationContext;
import org.apache.rocketmq.auth.authentication.enums.UserStatus;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationMetadataProvider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.chain.Handler;
import org.apache.rocketmq.common.chain.HandlerChain;
public class DefaultAuthenticationHandler implements Handler> {
private final AuthenticationMetadataProvider authenticationMetadataProvider;
public DefaultAuthenticationHandler(AuthConfig config, Supplier> metadataService) {
this.authenticationMetadataProvider = AuthenticationFactory.getMetadataProvider(config, metadataService);
}
@Override
public CompletableFuture handle(DefaultAuthenticationContext context,
HandlerChain> chain) {
return getUser(context).thenAccept(user -> doAuthenticate(context, user));
}
protected CompletableFuture getUser(DefaultAuthenticationContext context) {
if (this.authenticationMetadataProvider == null) {
throw new AuthenticationException("The authenticationMetadataProvider is not configured");
}
if (StringUtils.isEmpty(context.getUsername())) {
throw new AuthenticationException("username cannot be null.");
}
return this.authenticationMetadataProvider.getUser(context.getUsername());
}
protected void doAuthenticate(DefaultAuthenticationContext context, User user) {
if (user == null) {
throw new AuthenticationException("User:{} is not found.", context.getUsername());
}
if (user.getUserStatus() == UserStatus.DISABLE) {
throw new AuthenticationException("User:{} is disabled.", context.getUsername());
}
String signature = AclSigner.calSignature(context.getContent(), user.getPassword());
if (context.getSignature() == null
|| !MessageDigest.isEqual(signature.getBytes(AclSigner.DEFAULT_CHARSET), context.getSignature().getBytes(AclSigner.DEFAULT_CHARSET))) {
throw new AuthenticationException("check signature failed.");
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/context/AuthenticationContext.java
================================================
/*
* 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.rocketmq.auth.authentication.context;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
public abstract class AuthenticationContext {
private String channelId;
private String rpcCode;
private Map extInfo;
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getRpcCode() {
return rpcCode;
}
public void setRpcCode(String rpcCode) {
this.rpcCode = rpcCode;
}
@SuppressWarnings("unchecked")
public T getExtInfo(String key) {
if (StringUtils.isBlank(key)) {
return null;
}
if (this.extInfo == null) {
return null;
}
Object value = this.extInfo.get(key);
if (value == null) {
return null;
}
return (T) value;
}
public void setExtInfo(String key, Object value) {
if (StringUtils.isBlank(key) || value == null) {
return;
}
if (this.extInfo == null) {
this.extInfo = new HashMap<>();
}
this.extInfo.put(key, value);
}
public boolean hasExtInfo(String key) {
Object value = getExtInfo(key);
return value != null;
}
public Map getExtInfo() {
return extInfo;
}
public void setExtInfo(Map extInfo) {
this.extInfo = extInfo;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/context/DefaultAuthenticationContext.java
================================================
/*
* 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.rocketmq.auth.authentication.context;
public class DefaultAuthenticationContext extends AuthenticationContext {
private String username;
private byte[] content;
private String signature;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/enums/SubjectType.java
================================================
/*
* 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.rocketmq.auth.authentication.enums;
import com.alibaba.fastjson2.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
public enum SubjectType {
USER((byte) 1, "User");
@JSONField(value = true)
private final byte code;
private final String name;
SubjectType(byte code, String name) {
this.code = code;
this.name = name;
}
public static SubjectType getByName(String name) {
for (SubjectType subjectType : SubjectType.values()) {
if (StringUtils.equalsIgnoreCase(subjectType.getName(), name)) {
return subjectType;
}
}
return null;
}
public byte getCode() {
return code;
}
public String getName() {
return name;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/enums/UserStatus.java
================================================
/*
* 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.rocketmq.auth.authentication.enums;
import com.alibaba.fastjson2.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
public enum UserStatus {
ENABLE((byte) 1, "enable"),
DISABLE((byte) 2, "disable");
@JSONField(value = true)
private final byte code;
private final String name;
UserStatus(byte code, String name) {
this.code = code;
this.name = name;
}
public static UserStatus getByName(String name) {
for (UserStatus subjectType : UserStatus.values()) {
if (StringUtils.equalsIgnoreCase(subjectType.getName(), name)) {
return subjectType;
}
}
return null;
}
public byte getCode() {
return code;
}
public String getName() {
return name;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/enums/UserType.java
================================================
/*
* 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.rocketmq.auth.authentication.enums;
import com.alibaba.fastjson2.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
public enum UserType {
SUPER((byte) 1, "Super"),
NORMAL((byte) 2, "Normal");
@JSONField(value = true)
private final byte code;
private final String name;
UserType(byte code, String name) {
this.code = code;
this.name = name;
}
public static UserType getByName(String name) {
for (UserType subjectType : UserType.values()) {
if (StringUtils.equalsIgnoreCase(subjectType.getName(), name)) {
return subjectType;
}
}
return null;
}
public byte getCode() {
return code;
}
public String getName() {
return name;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/exception/AuthenticationException.java
================================================
/*
* 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.rocketmq.auth.authentication.exception;
import org.slf4j.helpers.MessageFormatter;
public class AuthenticationException extends RuntimeException {
public AuthenticationException(String message) {
super(message);
}
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
public AuthenticationException(String messagePattern, Object... argArray) {
super(MessageFormatter.arrayFormat(messagePattern, argArray).getMessage());
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/factory/AuthenticationFactory.java
================================================
/*
* 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.rocketmq.auth.authentication.factory;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.AuthenticationEvaluator;
import org.apache.rocketmq.auth.authentication.context.AuthenticationContext;
import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManager;
import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManagerImpl;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationMetadataProvider;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationProvider;
import org.apache.rocketmq.auth.authentication.provider.DefaultAuthenticationProvider;
import org.apache.rocketmq.auth.authentication.strategy.AuthenticationStrategy;
import org.apache.rocketmq.auth.authentication.strategy.StatelessAuthenticationStrategy;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class AuthenticationFactory {
private static final Map INSTANCE_MAP = new HashMap<>();
private static final String PROVIDER_PREFIX = "PROVIDER_";
private static final String METADATA_PROVIDER_PREFIX = "METADATA_PROVIDER_";
private static final String EVALUATOR_PREFIX = "EVALUATOR_";
@SuppressWarnings("unchecked")
public static AuthenticationProvider getProvider(AuthConfig config) {
if (config == null) {
return null;
}
return computeIfAbsent(PROVIDER_PREFIX + config.getConfigName(), key -> {
try {
Class extends AuthenticationProvider extends AuthenticationContext>> clazz =
DefaultAuthenticationProvider.class;
if (StringUtils.isNotBlank(config.getAuthenticationProvider())) {
clazz = (Class extends AuthenticationProvider extends AuthenticationContext>>) Class.forName(config.getAuthenticationProvider());
}
return (AuthenticationProvider) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to load the authentication provider.", e);
}
});
}
public static AuthenticationMetadataProvider getMetadataProvider(AuthConfig config) {
return getMetadataProvider(config, null);
}
public static AuthenticationMetadataManager getMetadataManager(AuthConfig config) {
return new AuthenticationMetadataManagerImpl(config);
}
@SuppressWarnings("unchecked")
public static AuthenticationMetadataProvider getMetadataProvider(AuthConfig config, Supplier> metadataService) {
if (config == null) {
return null;
}
return computeIfAbsent(METADATA_PROVIDER_PREFIX + config.getConfigName(), key -> {
try {
if (StringUtils.isBlank(config.getAuthenticationMetadataProvider())) {
return null;
}
Class extends AuthenticationMetadataProvider> clazz = (Class extends AuthenticationMetadataProvider>)
Class.forName(config.getAuthenticationMetadataProvider());
AuthenticationMetadataProvider result = clazz.getDeclaredConstructor().newInstance();
result.initialize(config, metadataService);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to load the authentication metadata provider", e);
}
});
}
public static AuthenticationEvaluator getEvaluator(AuthConfig config) {
return computeIfAbsent(EVALUATOR_PREFIX + config.getConfigName(), key -> new AuthenticationEvaluator(config));
}
public static AuthenticationEvaluator getEvaluator(AuthConfig config, Supplier> metadataService) {
return computeIfAbsent(EVALUATOR_PREFIX + config.getConfigName(), key -> new AuthenticationEvaluator(config, metadataService));
}
@SuppressWarnings("unchecked")
public static AuthenticationStrategy getStrategy(AuthConfig config, Supplier> metadataService) {
try {
Class extends AuthenticationStrategy> clazz = StatelessAuthenticationStrategy.class;
if (StringUtils.isNotBlank(config.getAuthenticationStrategy())) {
clazz = (Class extends AuthenticationStrategy>) Class.forName(config.getAuthenticationStrategy());
}
return clazz.getDeclaredConstructor(AuthConfig.class, Supplier.class).newInstance(config, metadataService);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static AuthenticationContext newContext(AuthConfig config, Metadata metadata, GeneratedMessageV3 request) {
AuthenticationProvider authenticationProvider = getProvider(config);
if (authenticationProvider == null) {
return null;
}
return authenticationProvider.newContext(metadata, request);
}
public static AuthenticationContext newContext(AuthConfig config, ChannelHandlerContext context,
RemotingCommand command) {
AuthenticationProvider authenticationProvider = getProvider(config);
if (authenticationProvider == null) {
return null;
}
return authenticationProvider.newContext(context, command);
}
@SuppressWarnings("unchecked")
private static V computeIfAbsent(String key, Function function) {
Object result = null;
if (INSTANCE_MAP.containsKey(key)) {
result = INSTANCE_MAP.get(key);
}
if (result == null) {
synchronized (INSTANCE_MAP) {
if (INSTANCE_MAP.containsKey(key)) {
result = INSTANCE_MAP.get(key);
}
if (result == null) {
result = function.apply(key);
if (result != null) {
INSTANCE_MAP.put(key, result);
}
}
}
}
return result != null ? (V) result : null;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/manager/AuthenticationMetadataManager.java
================================================
/*
* 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.rocketmq.auth.authentication.manager;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.config.AuthConfig;
public interface AuthenticationMetadataManager {
void shutdown();
void initUser(AuthConfig authConfig);
CompletableFuture createUser(User user);
CompletableFuture updateUser(User user);
CompletableFuture deleteUser(String username);
CompletableFuture getUser(String username);
CompletableFuture> listUser(String filter);
CompletableFuture isSuperUser(String username);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/manager/AuthenticationMetadataManagerImpl.java
================================================
/*
* 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.rocketmq.auth.authentication.manager;
import com.alibaba.fastjson2.JSON;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.auth.authentication.enums.UserStatus;
import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationMetadataProvider;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.provider.AuthorizationMetadataProvider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.utils.ExceptionUtils;
public class AuthenticationMetadataManagerImpl implements AuthenticationMetadataManager {
private final AuthenticationMetadataProvider authenticationMetadataProvider;
private final AuthorizationMetadataProvider authorizationMetadataProvider;
public AuthenticationMetadataManagerImpl(AuthConfig authConfig) {
this.authenticationMetadataProvider = AuthenticationFactory.getMetadataProvider(authConfig);
this.authorizationMetadataProvider = AuthorizationFactory.getMetadataProvider(authConfig);
this.initUser(authConfig);
}
@Override
public void shutdown() {
if (this.authenticationMetadataProvider != null) {
this.authenticationMetadataProvider.shutdown();
}
if (this.authorizationMetadataProvider != null) {
this.authorizationMetadataProvider.shutdown();
}
}
@Override
public void initUser(AuthConfig authConfig) {
if (authConfig == null) {
return;
}
if (StringUtils.isNotBlank(authConfig.getInitAuthenticationUser())) {
try {
User initUser = JSON.parseObject(authConfig.getInitAuthenticationUser(), User.class);
initUser.setUserType(UserType.SUPER);
this.getUser(initUser.getUsername()).thenCompose(user -> {
if (user != null) {
return CompletableFuture.completedFuture(null);
}
return this.createUser(initUser);
}).join();
} catch (Exception e) {
throw new AuthenticationException("Init authentication user error.", e);
}
}
if (StringUtils.isNotBlank(authConfig.getInnerClientAuthenticationCredentials())) {
try {
SessionCredentials credentials = JSON.parseObject(authConfig.getInnerClientAuthenticationCredentials(), SessionCredentials.class);
User innerUser = User.of(credentials.getAccessKey(), credentials.getSecretKey(), UserType.SUPER);
this.getUser(innerUser.getUsername()).thenCompose(user -> {
if (user != null) {
return CompletableFuture.completedFuture(null);
}
return this.createUser(innerUser);
}).join();
} catch (Exception e) {
throw new AuthenticationException("Init inner client authentication credentials error", e);
}
}
}
@Override
public CompletableFuture createUser(User user) {
CompletableFuture result = new CompletableFuture<>();
try {
this.validate(user, true);
if (user.getUserType() == null) {
user.setUserType(UserType.NORMAL);
}
if (user.getUserStatus() == null) {
user.setUserStatus(UserStatus.ENABLE);
}
result = this.getAuthenticationMetadataProvider().getUser(user.getUsername()).thenCompose(old -> {
if (old != null) {
throw new AuthenticationException("The user is existed");
}
return this.getAuthenticationMetadataProvider().createUser(user);
});
} catch (Exception e) {
this.handleException(e, result);
}
return result;
}
@Override
public CompletableFuture updateUser(User user) {
CompletableFuture result = new CompletableFuture<>();
try {
this.validate(user, false);
result = this.getAuthenticationMetadataProvider().getUser(user.getUsername()).thenCompose(old -> {
if (old == null) {
throw new AuthenticationException("The user is not exist");
}
if (StringUtils.isNotBlank(user.getPassword())) {
old.setPassword(user.getPassword());
}
if (user.getUserType() != null) {
old.setUserType(user.getUserType());
}
if (user.getUserStatus() != null) {
old.setUserStatus(user.getUserStatus());
}
return this.getAuthenticationMetadataProvider().updateUser(old);
});
} catch (Exception e) {
this.handleException(e, result);
}
return result;
}
@Override
public CompletableFuture deleteUser(String username) {
CompletableFuture result = new CompletableFuture<>();
try {
if (StringUtils.isBlank(username)) {
throw new AuthenticationException("username can not be blank");
}
CompletableFuture deleteUser = this.getAuthenticationMetadataProvider().deleteUser(username);
CompletableFuture deleteAcl = this.getAuthorizationMetadataProvider().deleteAcl(User.of(username));
return CompletableFuture.allOf(deleteUser, deleteAcl);
} catch (Exception e) {
this.handleException(e, result);
}
return result;
}
@Override
public CompletableFuture getUser(String username) {
CompletableFuture result = new CompletableFuture<>();
try {
if (StringUtils.isBlank(username)) {
throw new AuthenticationException("username can not be blank");
}
result = this.getAuthenticationMetadataProvider().getUser(username);
} catch (Exception e) {
this.handleException(e, result);
}
return result;
}
@Override
public CompletableFuture> listUser(String filter) {
CompletableFuture> result = new CompletableFuture<>();
try {
result = this.getAuthenticationMetadataProvider().listUser(filter);
} catch (Exception e) {
this.handleException(e, result);
}
return result;
}
@Override
public CompletableFuture isSuperUser(String username) {
return this.getUser(username).thenApply(user -> {
if (user == null) {
throw new AuthenticationException("User:{} is not found", username);
}
return user.getUserType() == UserType.SUPER;
});
}
private void validate(User user, boolean isCreate) {
if (user == null) {
throw new AuthenticationException("user can not be null");
}
if (StringUtils.isBlank(user.getUsername())) {
throw new AuthenticationException("username can not be blank");
}
if (isCreate && StringUtils.isBlank(user.getPassword())) {
throw new AuthenticationException("password can not be blank");
}
}
private void handleException(Exception e, CompletableFuture> result) {
Throwable throwable = ExceptionUtils.getRealException(e);
result.completeExceptionally(throwable);
}
private AuthenticationMetadataProvider getAuthenticationMetadataProvider() {
if (authenticationMetadataProvider == null) {
throw new IllegalStateException("The authenticationMetadataProvider is not configured.");
}
return authenticationMetadataProvider;
}
private AuthorizationMetadataProvider getAuthorizationMetadataProvider() {
if (authorizationMetadataProvider == null) {
throw new IllegalStateException("The authorizationMetadataProvider is not configured.");
}
return authorizationMetadataProvider;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/model/Subject.java
================================================
/*
* 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.rocketmq.auth.authentication.model;
import com.alibaba.fastjson2.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.enums.SubjectType;
import org.apache.rocketmq.common.constant.CommonConstants;
public interface Subject {
@JSONField(serialize = false)
String getSubjectKey();
SubjectType getSubjectType();
default boolean isSubject(SubjectType subjectType) {
return subjectType == this.getSubjectType();
}
@SuppressWarnings("unchecked")
static T of(String subjectKey) {
String type = StringUtils.substringBefore(subjectKey, CommonConstants.COLON);
SubjectType subjectType = SubjectType.getByName(type);
if (subjectType == null) {
return null;
}
if (subjectType == SubjectType.USER) {
return (T) User.of(StringUtils.substringAfter(subjectKey, CommonConstants.COLON));
}
return null;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/model/User.java
================================================
/*
* 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.rocketmq.auth.authentication.model;
import org.apache.rocketmq.auth.authentication.enums.SubjectType;
import org.apache.rocketmq.auth.authentication.enums.UserStatus;
import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.common.constant.CommonConstants;
public class User implements Subject {
private String username;
private String password;
private UserType userType;
private UserStatus userStatus;
public static User of(String username) {
User user = new User();
user.setUsername(username);
return user;
}
public static User of(String username, String password) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
return user;
}
public static User of(String username, String password, UserType userType) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setUserType(userType);
return user;
}
@Override
public String getSubjectKey() {
return this.getSubjectType().getName() + CommonConstants.COLON + this.username;
}
@Override
public SubjectType getSubjectType() {
return SubjectType.USER;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public UserType getUserType() {
return userType;
}
public void setUserType(UserType userType) {
this.userType = userType;
}
public UserStatus getUserStatus() {
return userStatus;
}
public void setUserStatus(UserStatus userStatus) {
this.userStatus = userStatus;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/provider/AuthenticationMetadataProvider.java
================================================
/*
* 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.rocketmq.auth.authentication.provider;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.config.AuthConfig;
public interface AuthenticationMetadataProvider {
void initialize(AuthConfig authConfig, Supplier> metadataService);
void shutdown();
CompletableFuture createUser(User user);
CompletableFuture deleteUser(String username);
CompletableFuture updateUser(User user);
CompletableFuture getUser(String username);
CompletableFuture> listUser(String filter);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/provider/AuthenticationProvider.java
================================================
/*
* 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.rocketmq.auth.authentication.provider;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public interface AuthenticationProvider {
void initialize(AuthConfig config, Supplier> metadataService);
CompletableFuture authenticate(AuthenticationContext context);
AuthenticationContext newContext(Metadata metadata, GeneratedMessageV3 request);
AuthenticationContext newContext(ChannelHandlerContext context, RemotingCommand command);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/provider/DefaultAuthenticationProvider.java
================================================
/*
* 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.rocketmq.auth.authentication.provider;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.builder.AuthenticationContextBuilder;
import org.apache.rocketmq.auth.authentication.builder.DefaultAuthenticationContextBuilder;
import org.apache.rocketmq.auth.authentication.context.DefaultAuthenticationContext;
import org.apache.rocketmq.auth.authentication.chain.DefaultAuthenticationHandler;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.chain.HandlerChain;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DefaultAuthenticationProvider implements AuthenticationProvider {
protected final Logger log = LoggerFactory.getLogger(LoggerName.ROCKETMQ_AUTH_AUDIT_LOGGER_NAME);
protected AuthConfig authConfig;
protected Supplier> metadataService;
protected AuthenticationContextBuilder authenticationContextBuilder;
@Override
public void initialize(AuthConfig config, Supplier> metadataService) {
this.authConfig = config;
this.metadataService = metadataService;
this.authenticationContextBuilder = new DefaultAuthenticationContextBuilder();
}
@Override
public CompletableFuture authenticate(DefaultAuthenticationContext context) {
return this.newHandlerChain().handle(context)
.whenComplete((nil, ex) -> doAuditLog(context, ex));
}
@Override
public DefaultAuthenticationContext newContext(Metadata metadata, GeneratedMessageV3 request) {
return this.authenticationContextBuilder.build(metadata, request);
}
@Override
public DefaultAuthenticationContext newContext(ChannelHandlerContext context, RemotingCommand command) {
return this.authenticationContextBuilder.build(context, command);
}
protected HandlerChain> newHandlerChain() {
return HandlerChain.>create()
.addNext(new DefaultAuthenticationHandler(this.authConfig, metadataService));
}
protected void doAuditLog(DefaultAuthenticationContext context, Throwable ex) {
if (StringUtils.isBlank(context.getUsername())) {
return;
}
if (ex != null) {
log.info("[AUTHENTICATION] User:{} is authenticated failed with Signature = {}.", context.getUsername(), context.getSignature());
} else {
log.debug("[AUTHENTICATION] User:{} is authenticated success with Signature = {}.", context.getUsername(), context.getSignature());
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/provider/LocalAuthenticationMetadataProvider.java
================================================
/*
* 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.rocketmq.auth.authentication.provider;
import com.alibaba.fastjson2.JSON;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.config.ConfigRocksDBStorage;
import org.apache.rocketmq.common.thread.ThreadPoolMonitor;
import org.rocksdb.RocksDB;
public class LocalAuthenticationMetadataProvider implements AuthenticationMetadataProvider {
private final static String AUTH_METADATA_COLUMN_FAMILY = new String(RocksDB.DEFAULT_COLUMN_FAMILY,
StandardCharsets.UTF_8);
private ConfigRocksDBStorage storage;
private LoadingCache userCache;
protected ThreadPoolExecutor cacheRefreshExecutor;
@Override
public void initialize(AuthConfig authConfig, Supplier> metadataService) {
this.storage = ConfigRocksDBStorage.getStore(authConfig.getAuthConfigPath() + File.separator + "users", false);
if (!this.storage.start()) {
throw new RuntimeException("Failed to load rocksdb for auth_user, please check whether it is occupied");
}
this.cacheRefreshExecutor = ThreadPoolMonitor.createAndMonitor(
1,
1,
1000 * 60,
TimeUnit.MILLISECONDS,
"UserCacheRefresh",
100000
);
this.userCache = Caffeine.newBuilder()
.maximumSize(authConfig.getUserCacheMaxNum())
.expireAfterAccess(authConfig.getUserCacheExpiredSecond(), TimeUnit.SECONDS)
.refreshAfterWrite(authConfig.getUserCacheRefreshSecond(), TimeUnit.SECONDS)
.executor(cacheRefreshExecutor)
.build(new UserCacheLoader(this.storage));
}
@Override
public CompletableFuture createUser(User user) {
try {
byte[] keyBytes = user.getUsername().getBytes(StandardCharsets.UTF_8);
byte[] valueBytes = JSON.toJSONBytes(user);
this.storage.put(AUTH_METADATA_COLUMN_FAMILY, keyBytes, keyBytes.length, valueBytes);
this.storage.flushWAL();
this.userCache.invalidate(user.getUsername());
} catch (Exception e) {
throw new AuthenticationException("create user to RocksDB failed", e);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture deleteUser(String username) {
try {
this.storage.delete(AUTH_METADATA_COLUMN_FAMILY, username.getBytes(StandardCharsets.UTF_8));
this.storage.flushWAL();
this.userCache.invalidate(username);
} catch (Exception e) {
throw new AuthenticationException("delete user from RocksDB failed", e);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture updateUser(User user) {
try {
byte[] keyBytes = user.getUsername().getBytes(StandardCharsets.UTF_8);
byte[] valueBytes = JSON.toJSONBytes(user);
this.storage.put(AUTH_METADATA_COLUMN_FAMILY, keyBytes, keyBytes.length, valueBytes);
this.storage.flushWAL();
this.userCache.invalidate(user.getUsername());
} catch (Exception e) {
throw new AuthenticationException("update user to RocksDB failed", e);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture getUser(String username) {
User user = this.userCache.get(username);
if (user == UserCacheLoader.EMPTY_USER) {
return CompletableFuture.completedFuture(null);
}
return CompletableFuture.completedFuture(user);
}
@Override
public CompletableFuture> listUser(String filter) {
List result = new ArrayList<>();
CompletableFuture> future = new CompletableFuture<>();
try {
this.storage.iterate(AUTH_METADATA_COLUMN_FAMILY, (key, value) -> {
String username = new String(key, StandardCharsets.UTF_8);
if (StringUtils.isNotBlank(filter) && !username.contains(filter)) {
return;
}
User user = JSON.parseObject(new String(value, StandardCharsets.UTF_8), User.class);
result.add(user);
});
} catch (Exception e) {
future.completeExceptionally(e);
}
future.complete(result);
return future;
}
@Override
public void shutdown() {
if (this.storage != null) {
this.storage.shutdown();
}
if (this.cacheRefreshExecutor != null) {
this.cacheRefreshExecutor.shutdown();
}
}
private static class UserCacheLoader implements CacheLoader {
private final ConfigRocksDBStorage storage;
public static final User EMPTY_USER = new User();
public UserCacheLoader(ConfigRocksDBStorage storage) {
this.storage = storage;
}
@Override
public User load(String username) {
try {
byte[] keyBytes = username.getBytes(StandardCharsets.UTF_8);
byte[] valueBytes = storage.get(AUTH_METADATA_COLUMN_FAMILY, keyBytes);
if (ArrayUtils.isEmpty(valueBytes)) {
return EMPTY_USER;
}
return JSON.parseObject(new String(valueBytes, StandardCharsets.UTF_8), User.class);
} catch (Exception e) {
throw new AuthenticationException("Get user from RocksDB failed.", e);
}
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/strategy/AbstractAuthenticationStrategy.java
================================================
/*
* 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.rocketmq.auth.authentication.strategy;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.context.AuthenticationContext;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationProvider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.utils.ExceptionUtils;
public abstract class AbstractAuthenticationStrategy implements AuthenticationStrategy {
protected final AuthConfig authConfig;
protected final Set authenticationWhiteSet = new HashSet<>();
protected final AuthenticationProvider authenticationProvider;
public AbstractAuthenticationStrategy(AuthConfig authConfig, Supplier> metadataService) {
this.authConfig = authConfig;
this.authenticationProvider = AuthenticationFactory.getProvider(authConfig);
if (this.authenticationProvider != null) {
this.authenticationProvider.initialize(authConfig, metadataService);
}
if (StringUtils.isNotBlank(authConfig.getAuthenticationWhitelist())) {
String[] whitelist = StringUtils.split(authConfig.getAuthenticationWhitelist(), ",");
for (String rpcCode : whitelist) {
this.authenticationWhiteSet.add(StringUtils.trim(rpcCode));
}
}
}
protected void doEvaluate(AuthenticationContext context) {
if (context == null) {
return;
}
if (!authConfig.isAuthenticationEnabled()) {
return;
}
if (this.authenticationProvider == null) {
return;
}
if (this.authenticationWhiteSet.contains(context.getRpcCode())) {
return;
}
try {
this.authenticationProvider.authenticate(context).join();
} catch (AuthenticationException ex) {
throw ex;
} catch (Throwable ex) {
Throwable exception = ExceptionUtils.getRealException(ex);
if (exception instanceof AuthenticationException) {
throw (AuthenticationException) exception;
}
throw new AuthenticationException("Authentication failed. Please verify the credentials and try again.", exception);
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/strategy/AuthenticationStrategy.java
================================================
/*
* 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.rocketmq.auth.authentication.strategy;
import org.apache.rocketmq.auth.authentication.context.AuthenticationContext;
public interface AuthenticationStrategy {
void evaluate(AuthenticationContext context);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/strategy/StatefulAuthenticationStrategy.java
================================================
/*
* 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.rocketmq.auth.authentication.strategy;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.context.AuthenticationContext;
import org.apache.rocketmq.auth.authentication.context.DefaultAuthenticationContext;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.constant.CommonConstants;
public class StatefulAuthenticationStrategy extends AbstractAuthenticationStrategy {
protected Cache> authCache;
public StatefulAuthenticationStrategy(AuthConfig authConfig, Supplier> metadataService) {
super(authConfig, metadataService);
this.authCache = Caffeine.newBuilder()
.expireAfterWrite(authConfig.getStatefulAuthenticationCacheExpiredSecond(), TimeUnit.SECONDS)
.maximumSize(authConfig.getStatefulAuthenticationCacheMaxNum())
.build();
}
@Override
public void evaluate(AuthenticationContext context) {
if (StringUtils.isBlank(context.getChannelId())) {
this.doEvaluate(context);
return;
}
Pair result = this.authCache.get(buildKey(context), key -> {
try {
this.doEvaluate(context);
return Pair.of(true, null);
} catch (AuthenticationException ex) {
return Pair.of(false, ex);
}
});
if (result != null && result.getObject1() == Boolean.FALSE) {
throw result.getObject2();
}
}
private String buildKey(AuthenticationContext context) {
if (context instanceof DefaultAuthenticationContext) {
DefaultAuthenticationContext ctx = (DefaultAuthenticationContext) context;
if (StringUtils.isBlank(ctx.getUsername())) {
return ctx.getChannelId();
}
return ctx.getChannelId() + CommonConstants.POUND + ctx.getUsername();
}
throw new AuthenticationException("The request of {} is not support.", context.getClass().getSimpleName());
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authentication/strategy/StatelessAuthenticationStrategy.java
================================================
/*
* 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.rocketmq.auth.authentication.strategy;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.authentication.context.AuthenticationContext;
import org.apache.rocketmq.auth.config.AuthConfig;
public class StatelessAuthenticationStrategy extends AbstractAuthenticationStrategy {
public StatelessAuthenticationStrategy(AuthConfig authConfig, Supplier> metadataService) {
super(authConfig, metadataService);
}
@Override
public void evaluate(AuthenticationContext context) {
super.doEvaluate(context);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/AuthorizationEvaluator.java
================================================
/*
* 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.rocketmq.auth.authorization;
import java.util.List;
import java.util.function.Supplier;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.strategy.AuthorizationStrategy;
import org.apache.rocketmq.auth.config.AuthConfig;
public class AuthorizationEvaluator {
private final AuthorizationStrategy authorizationStrategy;
public AuthorizationEvaluator(AuthConfig authConfig) {
this(authConfig, null);
}
public AuthorizationEvaluator(AuthConfig authConfig, Supplier> metadataService) {
this.authorizationStrategy = AuthorizationFactory.getStrategy(authConfig, metadataService);
}
public void evaluate(List contexts) {
if (CollectionUtils.isEmpty(contexts)) {
return;
}
contexts.forEach(this.authorizationStrategy::evaluate);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/AuthorizationContextBuilder.java
================================================
/*
* 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.rocketmq.auth.authorization.builder;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.List;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public interface AuthorizationContextBuilder {
List build(Metadata metadata, GeneratedMessageV3 message);
List build(ChannelHandlerContext context, RemotingCommand command);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java
================================================
/*
* 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.rocketmq.auth.authorization.builder;
import apache.rocketmq.v2.AckMessageRequest;
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
import apache.rocketmq.v2.ClientType;
import apache.rocketmq.v2.EndTransactionRequest;
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
import apache.rocketmq.v2.HeartbeatRequest;
import apache.rocketmq.v2.NotifyClientTerminationRequest;
import apache.rocketmq.v2.QueryAssignmentRequest;
import apache.rocketmq.v2.QueryRouteRequest;
import apache.rocketmq.v2.RecallMessageRequest;
import apache.rocketmq.v2.ReceiveMessageRequest;
import apache.rocketmq.v2.SendMessageRequest;
import apache.rocketmq.v2.Subscription;
import apache.rocketmq.v2.SubscriptionEntry;
import apache.rocketmq.v2.TelemetryCommand;
import apache.rocketmq.v2.SyncLiteSubscriptionRequest;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.common.action.RocketMQAction;
import org.apache.rocketmq.common.constant.CommonConstants;
import org.apache.rocketmq.common.constant.GrpcConstants;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.resource.ResourcePattern;
import org.apache.rocketmq.common.resource.ResourceType;
import org.apache.rocketmq.common.resource.RocketMQResource;
import org.apache.rocketmq.remoting.CommandCustomHeader;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.protocol.NamespaceUtil;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.RequestHeaderRegistry;
import org.apache.rocketmq.remoting.protocol.body.LockBatchRequestBody;
import org.apache.rocketmq.remoting.protocol.body.UnlockBatchRequestBody;
import org.apache.rocketmq.remoting.protocol.header.GetConsumerListByGroupRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.QueryConsumerOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.UnregisterClientRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumerData;
import org.apache.rocketmq.remoting.protocol.heartbeat.HeartbeatData;
import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
public class DefaultAuthorizationContextBuilder implements AuthorizationContextBuilder {
private static final String TOPIC = "topic";
private static final String GROUP = "group";
private static final String A = "a";
private static final String B = "b";
private static final String CONSUMER_GROUP = "consumerGroup";
private final AuthConfig authConfig;
private static final EnumSet CONSUMER_CLIENT_TYPES =
EnumSet.of(ClientType.PUSH_CONSUMER, ClientType.SIMPLE_CONSUMER, ClientType.PULL_CONSUMER);
private final RequestHeaderRegistry requestHeaderRegistry;
public DefaultAuthorizationContextBuilder(AuthConfig authConfig) {
this.authConfig = authConfig;
this.requestHeaderRegistry = RequestHeaderRegistry.getInstance();
}
@Override
public List build(Metadata metadata, GeneratedMessageV3 message) {
List result = null;
if (message instanceof SendMessageRequest) {
SendMessageRequest request = (SendMessageRequest) message;
if (request.getMessagesCount() <= 0) {
throw new AuthorizationException("message is null.");
}
result = newPubContext(metadata, request.getMessages(0).getTopic());
}
if (message instanceof RecallMessageRequest) {
RecallMessageRequest request = (RecallMessageRequest) message;
result = newPubContext(metadata, request.getTopic());
}
if (message instanceof EndTransactionRequest) {
EndTransactionRequest request = (EndTransactionRequest) message;
result = newPubContext(metadata, request.getTopic());
}
if (message instanceof HeartbeatRequest) {
HeartbeatRequest request = (HeartbeatRequest) message;
if (!isConsumerClientType(request.getClientType())) {
return null;
}
result = newGroupSubContexts(metadata, request.getGroup());
}
if (message instanceof ReceiveMessageRequest) {
ReceiveMessageRequest request = (ReceiveMessageRequest) message;
if (!request.hasMessageQueue()) {
throw new AuthorizationException("messageQueue is null.");
}
result = newSubContexts(metadata, request.getGroup(), request.getMessageQueue().getTopic());
}
if (message instanceof SyncLiteSubscriptionRequest) {
SyncLiteSubscriptionRequest request = (SyncLiteSubscriptionRequest) message;
if (request.getLiteTopicSetCount() <= 0) {
return null;
}
result = newSubContexts(metadata, request.getGroup(), request.getTopic());
}
if (message instanceof AckMessageRequest) {
AckMessageRequest request = (AckMessageRequest) message;
result = newSubContexts(metadata, request.getGroup(), request.getTopic());
}
if (message instanceof ForwardMessageToDeadLetterQueueRequest) {
ForwardMessageToDeadLetterQueueRequest request = (ForwardMessageToDeadLetterQueueRequest) message;
result = newSubContexts(metadata, request.getGroup(), request.getTopic());
}
if (message instanceof NotifyClientTerminationRequest) {
NotifyClientTerminationRequest request = (NotifyClientTerminationRequest) message;
if (StringUtils.isNotBlank(request.getGroup().getName())) {
result = newGroupSubContexts(metadata, request.getGroup());
}
}
if (message instanceof ChangeInvisibleDurationRequest) {
ChangeInvisibleDurationRequest request = (ChangeInvisibleDurationRequest) message;
result = newGroupSubContexts(metadata, request.getGroup());
}
if (message instanceof QueryRouteRequest) {
QueryRouteRequest request = (QueryRouteRequest) message;
result = newContext(metadata, request);
}
if (message instanceof QueryAssignmentRequest) {
QueryAssignmentRequest request = (QueryAssignmentRequest) message;
result = newSubContexts(metadata, request.getGroup(), request.getTopic());
}
if (message instanceof TelemetryCommand) {
TelemetryCommand request = (TelemetryCommand) message;
result = newContext(metadata, request);
}
if (CollectionUtils.isNotEmpty(result)) {
result.forEach(context -> {
context.setChannelId(metadata.get(GrpcConstants.CHANNEL_ID));
context.setRpcCode(message.getDescriptorForType().getFullName());
});
}
return result;
}
@Override
public List build(ChannelHandlerContext context, RemotingCommand command) {
List result = new ArrayList<>();
try {
HashMap fields = command.getExtFields();
if (MapUtils.isEmpty(fields)) {
return result;
}
Subject subject = null;
if (fields.containsKey(SessionCredentials.ACCESS_KEY)) {
subject = User.of(fields.get(SessionCredentials.ACCESS_KEY));
}
String remoteAddr = RemotingHelper.parseChannelRemoteAddr(context.channel());
String sourceIp = StringUtils.substringBeforeLast(remoteAddr, CommonConstants.COLON);
Resource topic;
Resource group;
switch (command.getCode()) {
case RequestCode.GET_ROUTEINFO_BY_TOPIC:
if (NamespaceUtil.isRetryTopic(fields.get(TOPIC))) {
group = Resource.ofGroup(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, group, Arrays.asList(Action.SUB, Action.GET), sourceIp));
} else {
topic = Resource.ofTopic(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, topic, Arrays.asList(Action.PUB, Action.SUB, Action.GET), sourceIp));
}
break;
case RequestCode.SEND_MESSAGE:
if (NamespaceUtil.isRetryTopic(fields.get(TOPIC))) {
group = Resource.ofGroup(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
} else {
topic = Resource.ofTopic(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, topic, Action.PUB, sourceIp));
}
break;
case RequestCode.SEND_MESSAGE_V2:
case RequestCode.SEND_BATCH_MESSAGE:
if (NamespaceUtil.isRetryTopic(fields.get(B))) {
group = Resource.ofGroup(fields.get(B));
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
} else {
topic = Resource.ofTopic(fields.get(B));
result.add(DefaultAuthorizationContext.of(subject, topic, Action.PUB, sourceIp));
}
break;
case RequestCode.RECALL_MESSAGE:
topic = Resource.ofTopic(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, topic, Action.PUB, sourceIp));
break;
case RequestCode.END_TRANSACTION:
if (StringUtils.isNotBlank(fields.get(TOPIC))) {
topic = Resource.ofTopic(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, topic, Action.PUB, sourceIp));
}
break;
case RequestCode.CONSUMER_SEND_MSG_BACK:
group = Resource.ofGroup(fields.get(GROUP));
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
break;
case RequestCode.PULL_MESSAGE:
if (!NamespaceUtil.isRetryTopic(fields.get(TOPIC))) {
topic = Resource.ofTopic(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, topic, Action.SUB, sourceIp));
}
group = Resource.ofGroup(fields.get(CONSUMER_GROUP));
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
break;
case RequestCode.QUERY_MESSAGE:
topic = Resource.ofTopic(fields.get(TOPIC));
result.add(DefaultAuthorizationContext.of(subject, topic, Arrays.asList(Action.SUB, Action.GET), sourceIp));
break;
case RequestCode.HEART_BEAT:
HeartbeatData heartbeatData = HeartbeatData.decode(command.getBody(), HeartbeatData.class);
for (ConsumerData data : heartbeatData.getConsumerDataSet()) {
group = Resource.ofGroup(data.getGroupName());
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
for (SubscriptionData subscriptionData : data.getSubscriptionDataSet()) {
if (NamespaceUtil.isRetryTopic(subscriptionData.getTopic())) {
continue;
}
topic = Resource.ofTopic(subscriptionData.getTopic());
result.add(DefaultAuthorizationContext.of(subject, topic, Action.SUB, sourceIp));
}
}
break;
case RequestCode.UNREGISTER_CLIENT:
final UnregisterClientRequestHeader unregisterClientRequestHeader =
command.decodeCommandCustomHeader(UnregisterClientRequestHeader.class);
if (StringUtils.isNotBlank(unregisterClientRequestHeader.getConsumerGroup())) {
group = Resource.ofGroup(unregisterClientRequestHeader.getConsumerGroup());
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
}
break;
case RequestCode.GET_CONSUMER_LIST_BY_GROUP:
final GetConsumerListByGroupRequestHeader getConsumerListByGroupRequestHeader =
command.decodeCommandCustomHeader(GetConsumerListByGroupRequestHeader.class);
group = Resource.ofGroup(getConsumerListByGroupRequestHeader.getConsumerGroup());
result.add(DefaultAuthorizationContext.of(subject, group, Arrays.asList(Action.SUB, Action.GET), sourceIp));
break;
case RequestCode.QUERY_CONSUMER_OFFSET:
final QueryConsumerOffsetRequestHeader queryConsumerOffsetRequestHeader =
command.decodeCommandCustomHeader(QueryConsumerOffsetRequestHeader.class);
if (!NamespaceUtil.isRetryTopic(queryConsumerOffsetRequestHeader.getTopic())) {
topic = Resource.ofTopic(queryConsumerOffsetRequestHeader.getTopic());
result.add(DefaultAuthorizationContext.of(subject, topic, Arrays.asList(Action.SUB, Action.GET), sourceIp));
}
group = Resource.ofGroup(queryConsumerOffsetRequestHeader.getConsumerGroup());
result.add(DefaultAuthorizationContext.of(subject, group, Arrays.asList(Action.SUB, Action.GET), sourceIp));
break;
case RequestCode.UPDATE_CONSUMER_OFFSET:
final UpdateConsumerOffsetRequestHeader updateConsumerOffsetRequestHeader =
command.decodeCommandCustomHeader(UpdateConsumerOffsetRequestHeader.class);
if (!NamespaceUtil.isRetryTopic(updateConsumerOffsetRequestHeader.getTopic())) {
topic = Resource.ofTopic(updateConsumerOffsetRequestHeader.getTopic());
result.add(DefaultAuthorizationContext.of(subject, topic, Arrays.asList(Action.SUB, Action.UPDATE), sourceIp));
}
group = Resource.ofGroup(updateConsumerOffsetRequestHeader.getConsumerGroup());
result.add(DefaultAuthorizationContext.of(subject, group, Arrays.asList(Action.SUB, Action.UPDATE), sourceIp));
break;
case RequestCode.LOCK_BATCH_MQ:
LockBatchRequestBody lockBatchRequestBody = LockBatchRequestBody.decode(command.getBody(), LockBatchRequestBody.class);
group = Resource.ofGroup(lockBatchRequestBody.getConsumerGroup());
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
if (CollectionUtils.isNotEmpty(lockBatchRequestBody.getMqSet())) {
for (MessageQueue messageQueue : lockBatchRequestBody.getMqSet()) {
if (NamespaceUtil.isRetryTopic(messageQueue.getTopic())) {
continue;
}
topic = Resource.ofTopic(messageQueue.getTopic());
result.add(DefaultAuthorizationContext.of(subject, topic, Action.SUB, sourceIp));
}
}
break;
case RequestCode.UNLOCK_BATCH_MQ:
UnlockBatchRequestBody unlockBatchRequestBody = UnlockBatchRequestBody.decode(command.getBody(), UnlockBatchRequestBody.class);
group = Resource.ofGroup(unlockBatchRequestBody.getConsumerGroup());
result.add(DefaultAuthorizationContext.of(subject, group, Action.SUB, sourceIp));
if (CollectionUtils.isNotEmpty(unlockBatchRequestBody.getMqSet())) {
for (MessageQueue messageQueue : unlockBatchRequestBody.getMqSet()) {
if (NamespaceUtil.isRetryTopic(messageQueue.getTopic())) {
continue;
}
topic = Resource.ofTopic(messageQueue.getTopic());
result.add(DefaultAuthorizationContext.of(subject, topic, Action.SUB, sourceIp));
}
}
break;
default:
result = buildContextByAnnotation(subject, command, sourceIp);
break;
}
if (CollectionUtils.isNotEmpty(result)) {
result.forEach(r -> {
r.setChannelId(context.channel().id().asLongText());
r.setRpcCode(String.valueOf(command.getCode()));
});
}
} catch (AuthorizationException ex) {
throw ex;
} catch (Throwable t) {
throw new AuthorizationException("parse authorization context error.", t);
}
return result;
}
private List buildContextByAnnotation(Subject subject, RemotingCommand request,
String sourceIp) throws Exception {
List result = new ArrayList<>();
Class extends CommandCustomHeader> clazz = this.requestHeaderRegistry.getRequestHeader(request.getCode());
if (clazz == null) {
return result;
}
CommandCustomHeader header = request.decodeCommandCustomHeader(clazz);
RocketMQAction rocketMQAction = clazz.getAnnotation(RocketMQAction.class);
ResourceType resourceType = rocketMQAction.resource();
Action[] actions = rocketMQAction.action();
Resource resource = null;
if (resourceType == ResourceType.CLUSTER) {
resource = Resource.ofCluster(authConfig.getClusterName());
}
Field[] fields = clazz.getDeclaredFields();
if (ArrayUtils.isNotEmpty(fields)) {
for (Field field : fields) {
RocketMQResource rocketMQResource = field.getAnnotation(RocketMQResource.class);
if (rocketMQResource == null) {
continue;
}
field.setAccessible(true);
try {
resourceType = rocketMQResource.value();
String splitter = rocketMQResource.splitter();
Object value = field.get(header);
if (value == null) {
continue;
}
String[] resourceValues;
if (StringUtils.isNotBlank(splitter)) {
resourceValues = StringUtils.split(value.toString(), splitter);
} else {
resourceValues = new String[] {value.toString()};
}
for (String resourceValue : resourceValues) {
if (resourceType == ResourceType.TOPIC && NamespaceUtil.isRetryTopic(resourceValue)) {
resource = Resource.ofGroup(resourceValue);
result.add(DefaultAuthorizationContext.of(subject, resource, Arrays.asList(actions), sourceIp));
} else {
resource = Resource.of(resourceType, resourceValue, ResourcePattern.LITERAL);
result.add(DefaultAuthorizationContext.of(subject, resource, Arrays.asList(actions), sourceIp));
}
}
} finally {
field.setAccessible(false);
}
}
}
if (CollectionUtils.isEmpty(result) && resource != null) {
result.add(DefaultAuthorizationContext.of(subject, resource, Arrays.asList(actions), sourceIp));
}
return result;
}
private List newContext(Metadata metadata, QueryRouteRequest request) {
apache.rocketmq.v2.Resource topic = request.getTopic();
if (StringUtils.isBlank(topic.getName())) {
throw new AuthorizationException("topic is null.");
}
Subject subject = null;
if (metadata.containsKey(GrpcConstants.AUTHORIZATION_AK)) {
subject = User.of(metadata.get(GrpcConstants.AUTHORIZATION_AK));
}
Resource resource = Resource.ofTopic(topic.getName());
String sourceIp = StringUtils.substringBeforeLast(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, Arrays.asList(Action.PUB, Action.SUB), sourceIp);
return Collections.singletonList(context);
}
private static List newContext(Metadata metadata, TelemetryCommand request) {
if (request.getCommandCase() != TelemetryCommand.CommandCase.SETTINGS) {
return null;
}
if (!request.getSettings().hasPublishing() && !request.getSettings().hasSubscription()) {
throw new AclException("settings command doesn't have publishing or subscription.");
}
List result = new ArrayList<>();
if (request.getSettings().hasPublishing()) {
List topicList = request.getSettings().getPublishing().getTopicsList();
for (apache.rocketmq.v2.Resource topic : topicList) {
result.addAll(newPubContext(metadata, topic));
}
}
if (request.getSettings().hasSubscription()) {
Subscription subscription = request.getSettings().getSubscription();
result.addAll(newSubContexts(metadata, ResourceType.GROUP, subscription.getGroup()));
for (SubscriptionEntry entry : subscription.getSubscriptionsList()) {
result.addAll(newSubContexts(metadata, ResourceType.TOPIC, entry.getTopic()));
}
}
return result;
}
private boolean isConsumerClientType(ClientType clientType) {
return CONSUMER_CLIENT_TYPES.contains(clientType);
}
private static List newPubContext(Metadata metadata, apache.rocketmq.v2.Resource topic) {
if (topic == null || StringUtils.isBlank(topic.getName())) {
throw new AuthorizationException("topic is null.");
}
Subject subject = null;
if (metadata.containsKey(GrpcConstants.AUTHORIZATION_AK)) {
subject = User.of(metadata.get(GrpcConstants.AUTHORIZATION_AK));
}
Resource resource = Resource.ofTopic(topic.getName());
String sourceIp = StringUtils.substringBeforeLast(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, Action.PUB, sourceIp);
return Collections.singletonList(context);
}
private List newSubContexts(Metadata metadata, apache.rocketmq.v2.Resource group,
apache.rocketmq.v2.Resource topic) {
List result = new ArrayList<>();
result.addAll(newGroupSubContexts(metadata, group));
result.addAll(newTopicSubContexts(metadata, topic));
return result;
}
private static List newTopicSubContexts(Metadata metadata,
apache.rocketmq.v2.Resource resource) {
return newSubContexts(metadata, ResourceType.TOPIC, resource);
}
private static List newGroupSubContexts(Metadata metadata,
apache.rocketmq.v2.Resource resource) {
return newSubContexts(metadata, ResourceType.GROUP, resource);
}
private static List newSubContexts(Metadata metadata, ResourceType resourceType,
apache.rocketmq.v2.Resource resource) {
if (resourceType == ResourceType.GROUP) {
if (resource == null || StringUtils.isBlank(resource.getName())) {
throw new AuthorizationException("group is null.");
}
return newSubContexts(metadata, Resource.ofGroup(resource.getName()));
}
if (resourceType == ResourceType.TOPIC) {
if (resource == null || StringUtils.isBlank(resource.getName())) {
throw new AuthorizationException("topic is null.");
}
return newSubContexts(metadata, Resource.ofTopic(resource.getName()));
}
throw new AuthorizationException("unknown resource type.");
}
private static List newSubContexts(Metadata metadata, Resource resource) {
List result = new ArrayList<>();
Subject subject = null;
if (metadata.containsKey(GrpcConstants.AUTHORIZATION_AK)) {
subject = User.of(metadata.get(GrpcConstants.AUTHORIZATION_AK));
}
String sourceIp = StringUtils.substringBeforeLast(metadata.get(GrpcConstants.REMOTE_ADDRESS), CommonConstants.COLON);
result.add(DefaultAuthorizationContext.of(subject, resource, Action.SUB, sourceIp));
return result;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/chain/AclAuthorizationHandler.java
================================================
/*
* 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.rocketmq.auth.authorization.chain;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.authorization.model.Environment;
import org.apache.rocketmq.auth.authorization.model.Policy;
import org.apache.rocketmq.auth.authorization.model.PolicyEntry;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.auth.authorization.provider.AuthorizationMetadataProvider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.chain.Handler;
import org.apache.rocketmq.common.chain.HandlerChain;
import org.apache.rocketmq.common.resource.ResourcePattern;
import org.apache.rocketmq.common.resource.ResourceType;
public class AclAuthorizationHandler implements Handler> {
private final AuthorizationMetadataProvider authorizationMetadataProvider;
public AclAuthorizationHandler(AuthConfig config) {
this.authorizationMetadataProvider = AuthorizationFactory.getMetadataProvider(config);
}
public AclAuthorizationHandler(AuthConfig config, Supplier> metadataService) {
this.authorizationMetadataProvider = AuthorizationFactory.getMetadataProvider(config, metadataService);
}
@Override
public CompletableFuture handle(DefaultAuthorizationContext context,
HandlerChain> chain) {
if (this.authorizationMetadataProvider == null) {
throw new AuthorizationException("The authorizationMetadataProvider is not configured");
}
return this.authorizationMetadataProvider.getAcl(context.getSubject()).thenAccept(acl -> {
if (acl == null) {
throwException(context, "no matched policies.");
}
// 1. get the defined acl entries which match the request.
PolicyEntry matchedEntry = matchPolicyEntries(context, acl);
// 2. if no matched acl entries, return deny
if (matchedEntry == null) {
throwException(context, "no matched policies.");
}
// 3. judge is the entries has denied decision.
if (matchedEntry.getDecision() == Decision.DENY) {
throwException(context, "the decision is deny.");
}
});
}
private PolicyEntry matchPolicyEntries(DefaultAuthorizationContext context, Acl acl) {
List policyEntries = new ArrayList<>();
Policy policy = acl.getPolicy(PolicyType.CUSTOM);
if (policy != null) {
List entries = matchPolicyEntries(context, policy.getEntries());
if (CollectionUtils.isNotEmpty(entries)) {
policyEntries.addAll(entries);
}
}
if (CollectionUtils.isEmpty(policyEntries)) {
policy = acl.getPolicy(PolicyType.DEFAULT);
if (policy != null) {
List entries = matchPolicyEntries(context, policy.getEntries());
if (CollectionUtils.isNotEmpty(entries)) {
policyEntries.addAll(entries);
}
}
}
if (CollectionUtils.isEmpty(policyEntries)) {
return null;
}
policyEntries.sort(this::comparePolicyEntries);
return policyEntries.get(0);
}
private List matchPolicyEntries(DefaultAuthorizationContext context, List entries) {
if (CollectionUtils.isEmpty(entries)) {
return null;
}
return entries.stream()
.filter(entry -> entry.isMatchResource(context.getResource()))
.filter(entry -> entry.isMatchAction(context.getActions()))
.filter(entry -> entry.isMatchEnvironment(Environment.of(context.getSourceIp())))
.collect(Collectors.toList());
}
private int comparePolicyEntries(PolicyEntry o1, PolicyEntry o2) {
int compare = 0;
Resource r1 = o1.getResource();
Resource r2 = o2.getResource();
if (r1.getResourceType() != r2.getResourceType()) {
if (r1.getResourceType() == ResourceType.ANY) {
compare = 1;
}
if (r2.getResourceType() == ResourceType.ANY) {
compare = -1;
}
} else if (r1.getResourcePattern() == r2.getResourcePattern()) {
if (r1.getResourcePattern() == ResourcePattern.PREFIXED) {
String n1 = r1.getResourceName();
String n2 = r2.getResourceName();
compare = -1 * Integer.compare(n1.length(), n2.length());
}
} else {
if (r1.getResourcePattern() == ResourcePattern.LITERAL) {
compare = -1;
} else if (r2.getResourcePattern() == ResourcePattern.LITERAL) {
compare = 1;
} else if (r1.getResourcePattern() == ResourcePattern.PREFIXED) {
compare = -1;
} else if (r2.getResourcePattern() == ResourcePattern.PREFIXED) {
compare = 1;
}
}
if (compare != 0) {
return compare;
}
// the decision deny has higher priority
Decision d1 = o1.getDecision();
Decision d2 = o2.getDecision();
if (d1 != d2) {
return d1 == Decision.DENY ? -1 : 1;
}
return 0;
}
private static void throwException(DefaultAuthorizationContext context, String detail) {
throw new AuthorizationException("{} has no permission to access {} from {}, " + detail,
context.getSubject().getSubjectKey(), context.getResource().getResourceKey(), context.getSourceIp());
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/chain/UserAuthorizationHandler.java
================================================
/*
* 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.rocketmq.auth.authorization.chain;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.authentication.enums.SubjectType;
import org.apache.rocketmq.auth.authentication.enums.UserStatus;
import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationMetadataProvider;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.chain.Handler;
import org.apache.rocketmq.common.chain.HandlerChain;
public class UserAuthorizationHandler implements Handler> {
private final AuthenticationMetadataProvider authenticationMetadataProvider;
public UserAuthorizationHandler(AuthConfig config, Supplier> metadataService) {
this.authenticationMetadataProvider = AuthenticationFactory.getMetadataProvider(config, metadataService);
}
@Override
public CompletableFuture handle(DefaultAuthorizationContext context, HandlerChain> chain) {
if (!context.getSubject().isSubject(SubjectType.USER)) {
return chain.handle(context);
}
return this.getUser(context.getSubject()).thenCompose(user -> {
if (user.getUserType() == UserType.SUPER) {
return CompletableFuture.completedFuture(null);
}
return chain.handle(context);
});
}
private CompletableFuture getUser(Subject subject) {
if (this.authenticationMetadataProvider == null) {
throw new AuthorizationException("The authenticationMetadataProvider is not configured");
}
User user = (User) subject;
return authenticationMetadataProvider.getUser(user.getUsername()).thenApply(result -> {
if (result == null) {
throw new AuthorizationException("User:{} not found.", user.getUsername());
}
if (result.getUserStatus() == UserStatus.DISABLE) {
throw new AuthorizationException("User:{} is disabled.", result.getUsername());
}
return result;
});
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/context/AuthorizationContext.java
================================================
/*
* 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.rocketmq.auth.authorization.context;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
public abstract class AuthorizationContext {
private String channelId;
private String rpcCode;
private Map extInfo;
@SuppressWarnings("unchecked")
public T getExtInfo(String key) {
if (StringUtils.isBlank(key)) {
return null;
}
if (this.extInfo == null) {
return null;
}
Object value = this.extInfo.get(key);
if (value == null) {
return null;
}
return (T) value;
}
public void setExtInfo(String key, Object value) {
if (StringUtils.isBlank(key) || value == null) {
return;
}
if (this.extInfo == null) {
this.extInfo = new HashMap<>();
}
this.extInfo.put(key, value);
}
public boolean hasExtInfo(String key) {
Object value = getExtInfo(key);
return value != null;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getRpcCode() {
return rpcCode;
}
public void setRpcCode(String rpcCode) {
this.rpcCode = rpcCode;
}
public Map getExtInfo() {
return extInfo;
}
public void setExtInfo(Map extInfo) {
this.extInfo = extInfo;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/context/DefaultAuthorizationContext.java
================================================
/*
* 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.rocketmq.auth.authorization.context;
import java.util.Collections;
import java.util.List;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.common.action.Action;
public class DefaultAuthorizationContext extends AuthorizationContext {
private Subject subject;
private Resource resource;
private List actions;
private String sourceIp;
public static DefaultAuthorizationContext of(Subject subject, Resource resource, Action action, String sourceIp) {
DefaultAuthorizationContext context = new DefaultAuthorizationContext();
context.setSubject(subject);
context.setResource(resource);
context.setActions(Collections.singletonList(action));
context.setSourceIp(sourceIp);
return context;
}
public static DefaultAuthorizationContext of(Subject subject, Resource resource, List actions, String sourceIp) {
DefaultAuthorizationContext context = new DefaultAuthorizationContext();
context.setSubject(subject);
context.setResource(resource);
context.setActions(actions);
context.setSourceIp(sourceIp);
return context;
}
public String getSubjectKey() {
return this.subject != null ? this.subject.getSubjectKey() : null;
}
public String getResourceKey() {
return this.resource != null ? this.resource.getResourceKey() : null;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public List getActions() {
return actions;
}
public void setActions(List actions) {
this.actions = actions;
}
public String getSourceIp() {
return sourceIp;
}
public void setSourceIp(String sourceIp) {
this.sourceIp = sourceIp;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/enums/Decision.java
================================================
/*
* 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.rocketmq.auth.authorization.enums;
import com.alibaba.fastjson2.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
public enum Decision {
ALLOW((byte) 1, "Allow"),
DENY((byte) 2, "Deny");
@JSONField(value = true)
private final byte code;
private final String name;
Decision(byte code, String name) {
this.code = code;
this.name = name;
}
public static Decision getByName(String name) {
for (Decision decision : Decision.values()) {
if (StringUtils.equalsIgnoreCase(decision.getName(), name)) {
return decision;
}
}
return null;
}
public byte getCode() {
return code;
}
public String getName() {
return name;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/enums/PolicyType.java
================================================
/*
* 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.rocketmq.auth.authorization.enums;
import com.alibaba.fastjson2.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
public enum PolicyType {
CUSTOM((byte) 1, "Custom"),
DEFAULT((byte) 2, "Default");
@JSONField(value = true)
private final byte code;
private final String name;
PolicyType(byte code, String name) {
this.code = code;
this.name = name;
}
public static PolicyType getByName(String name) {
for (PolicyType policyType : PolicyType.values()) {
if (StringUtils.equalsIgnoreCase(policyType.getName(), name)) {
return policyType;
}
}
return null;
}
public byte getCode() {
return code;
}
public String getName() {
return name;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/exception/AuthorizationException.java
================================================
/*
* 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.rocketmq.auth.authorization.exception;
import org.slf4j.helpers.MessageFormatter;
public class AuthorizationException extends RuntimeException {
public AuthorizationException(String message) {
super(message);
}
public AuthorizationException(String message, Throwable cause) {
super(message, cause);
}
public AuthorizationException(String messagePattern, Object... argArray) {
super(MessageFormatter.arrayFormat(messagePattern, argArray).getMessage());
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/factory/AuthorizationFactory.java
================================================
/*
* 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.rocketmq.auth.authorization.factory;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authorization.AuthorizationEvaluator;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
import org.apache.rocketmq.auth.authorization.manager.AuthorizationMetadataManager;
import org.apache.rocketmq.auth.authorization.manager.AuthorizationMetadataManagerImpl;
import org.apache.rocketmq.auth.authorization.provider.AuthorizationMetadataProvider;
import org.apache.rocketmq.auth.authorization.provider.AuthorizationProvider;
import org.apache.rocketmq.auth.authorization.provider.DefaultAuthorizationProvider;
import org.apache.rocketmq.auth.authorization.strategy.AuthorizationStrategy;
import org.apache.rocketmq.auth.authorization.strategy.StatelessAuthorizationStrategy;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public class AuthorizationFactory {
private static final Map INSTANCE_MAP = new HashMap<>();
private static final String PROVIDER_PREFIX = "PROVIDER_";
private static final String METADATA_PROVIDER_PREFIX = "METADATA_PROVIDER_";
private static final String EVALUATOR_PREFIX = "EVALUATOR_";
@SuppressWarnings("unchecked")
public static AuthorizationProvider getProvider(AuthConfig config) {
if (config == null) {
return null;
}
return computeIfAbsent(PROVIDER_PREFIX + config.getConfigName(), key -> {
try {
Class extends AuthorizationProvider extends AuthorizationContext>> clazz =
DefaultAuthorizationProvider.class;
if (StringUtils.isNotBlank(config.getAuthorizationProvider())) {
clazz = (Class extends AuthorizationProvider extends AuthorizationContext>>) Class.forName(config.getAuthorizationProvider());
}
return (AuthorizationProvider) clazz
.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to load the authorization provider.", e);
}
});
}
public static AuthorizationMetadataProvider getMetadataProvider(AuthConfig config) {
return getMetadataProvider(config, null);
}
public static AuthorizationMetadataManager getMetadataManager(AuthConfig config) {
return new AuthorizationMetadataManagerImpl(config);
}
@SuppressWarnings("unchecked")
public static AuthorizationMetadataProvider getMetadataProvider(AuthConfig config, Supplier> metadataService) {
if (config == null) {
return null;
}
return computeIfAbsent(METADATA_PROVIDER_PREFIX + config.getConfigName(), key -> {
try {
if (StringUtils.isBlank(config.getAuthorizationMetadataProvider())) {
return null;
}
Class extends AuthorizationMetadataProvider> clazz = (Class extends AuthorizationMetadataProvider>)
Class.forName(config.getAuthorizationMetadataProvider());
AuthorizationMetadataProvider result = clazz.getDeclaredConstructor().newInstance();
result.initialize(config, metadataService);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to load the authorization metadata provider.", e);
}
});
}
public static AuthorizationEvaluator getEvaluator(AuthConfig config) {
return computeIfAbsent(EVALUATOR_PREFIX + config.getConfigName(), key -> new AuthorizationEvaluator(config));
}
public static AuthorizationEvaluator getEvaluator(AuthConfig config, Supplier> metadataService) {
return computeIfAbsent(EVALUATOR_PREFIX + config.getConfigName(), key -> new AuthorizationEvaluator(config, metadataService));
}
@SuppressWarnings("unchecked")
public static AuthorizationStrategy getStrategy(AuthConfig config, Supplier> metadataService) {
try {
Class extends AuthorizationStrategy> clazz = StatelessAuthorizationStrategy.class;
if (StringUtils.isNotBlank(config.getAuthorizationStrategy())) {
clazz = (Class extends AuthorizationStrategy>) Class.forName(config.getAuthorizationStrategy());
}
return clazz.getDeclaredConstructor(AuthConfig.class, Supplier.class).newInstance(config, metadataService);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static List newContexts(AuthConfig config, Metadata metadata,
GeneratedMessageV3 message) {
AuthorizationProvider authorizationProvider = getProvider(config);
if (authorizationProvider == null) {
return null;
}
return authorizationProvider.newContexts(metadata, message);
}
public static List newContexts(AuthConfig config, ChannelHandlerContext context,
RemotingCommand command) {
AuthorizationProvider authorizationProvider = getProvider(config);
if (authorizationProvider == null) {
return null;
}
return authorizationProvider.newContexts(context, command);
}
@SuppressWarnings("unchecked")
private static V computeIfAbsent(String key, Function function) {
Object result = null;
if (INSTANCE_MAP.containsKey(key)) {
result = INSTANCE_MAP.get(key);
}
if (result == null) {
synchronized (INSTANCE_MAP) {
if (INSTANCE_MAP.containsKey(key)) {
result = INSTANCE_MAP.get(key);
}
if (result == null) {
result = function.apply(key);
if (result != null) {
INSTANCE_MAP.put(key, result);
}
}
}
}
return result != null ? (V) result : null;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/manager/AuthorizationMetadataManager.java
================================================
/*
* 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.rocketmq.auth.authorization.manager;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.authorization.model.Resource;
public interface AuthorizationMetadataManager {
void shutdown();
CompletableFuture createAcl(Acl acl);
CompletableFuture updateAcl(Acl acl);
CompletableFuture deleteAcl(Subject subject);
CompletableFuture deleteAcl(Subject subject, PolicyType policyType, Resource resource);
CompletableFuture getAcl(Subject subject);
CompletableFuture> listAcl(String subjectFilter, String resourceFilter);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/manager/AuthorizationMetadataManagerImpl.java
================================================
/*
* 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.rocketmq.auth.authorization.manager;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.enums.SubjectType;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authentication.provider.AuthenticationMetadataProvider;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.authorization.model.Environment;
import org.apache.rocketmq.auth.authorization.model.Policy;
import org.apache.rocketmq.auth.authorization.model.PolicyEntry;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.auth.authorization.provider.AuthorizationMetadataProvider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.common.utils.ExceptionUtils;
import org.apache.rocketmq.common.utils.IPAddressUtils;
public class AuthorizationMetadataManagerImpl implements AuthorizationMetadataManager {
private final AuthorizationMetadataProvider authorizationMetadataProvider;
private final AuthenticationMetadataProvider authenticationMetadataProvider;
public AuthorizationMetadataManagerImpl(AuthConfig authConfig) {
this.authorizationMetadataProvider = AuthorizationFactory.getMetadataProvider(authConfig);
this.authenticationMetadataProvider = AuthenticationFactory.getMetadataProvider(authConfig);
}
@Override
public void shutdown() {
if (this.authenticationMetadataProvider != null) {
this.authenticationMetadataProvider.shutdown();
}
if (this.authorizationMetadataProvider != null) {
this.authorizationMetadataProvider.shutdown();
}
}
@Override
public CompletableFuture createAcl(Acl acl) {
try {
validate(acl);
initAcl(acl);
CompletableFuture extends Subject> subjectFuture;
if (acl.getSubject().isSubject(SubjectType.USER)) {
User user = (User) acl.getSubject();
subjectFuture = this.getAuthenticationMetadataProvider().getUser(user.getUsername());
} else {
subjectFuture = CompletableFuture.completedFuture(acl.getSubject());
}
return subjectFuture.thenCompose(subject -> {
if (subject == null) {
throw new AuthorizationException("The subject of {} is not exist.", acl.getSubject().getSubjectKey());
}
return this.getAuthorizationMetadataProvider().getAcl(acl.getSubject());
}).thenCompose(oldAcl -> {
if (oldAcl == null) {
return this.getAuthorizationMetadataProvider().createAcl(acl);
}
oldAcl.updatePolicy(acl.getPolicies());
return this.getAuthorizationMetadataProvider().updateAcl(oldAcl);
});
} catch (Exception e) {
return this.handleException(e);
}
}
@Override
public CompletableFuture updateAcl(Acl acl) {
try {
validate(acl);
initAcl(acl);
CompletableFuture extends Subject> subjectFuture;
if (acl.getSubject().isSubject(SubjectType.USER)) {
User user = (User) acl.getSubject();
subjectFuture = this.getAuthenticationMetadataProvider().getUser(user.getUsername());
} else {
subjectFuture = CompletableFuture.completedFuture(acl.getSubject());
}
return subjectFuture.thenCompose(subject -> {
if (subject == null) {
throw new AuthorizationException("The subject of {} is not exist.", acl.getSubject().getSubjectKey());
}
return this.getAuthorizationMetadataProvider().getAcl(acl.getSubject());
}).thenCompose(oldAcl -> {
if (oldAcl == null) {
return this.getAuthorizationMetadataProvider().createAcl(acl);
}
oldAcl.updatePolicy(acl.getPolicies());
return this.getAuthorizationMetadataProvider().updateAcl(oldAcl);
});
} catch (Exception e) {
return this.handleException(e);
}
}
@Override
public CompletableFuture deleteAcl(Subject subject) {
return this.deleteAcl(subject, null, null);
}
@Override
public CompletableFuture deleteAcl(Subject subject, PolicyType policyType, Resource resource) {
try {
if (subject == null) {
throw new AuthorizationException("The subject is null.");
}
if (policyType == null) {
policyType = PolicyType.CUSTOM;
}
CompletableFuture extends Subject> subjectFuture;
if (subject.isSubject(SubjectType.USER)) {
User user = (User) subject;
subjectFuture = this.getAuthenticationMetadataProvider().getUser(user.getUsername());
} else {
subjectFuture = CompletableFuture.completedFuture(subject);
}
CompletableFuture aclFuture = this.getAuthorizationMetadataProvider().getAcl(subject);
PolicyType finalPolicyType = policyType;
return subjectFuture.thenCombine(aclFuture, (sub, oldAcl) -> {
if (sub == null) {
throw new AuthorizationException("The subject is not exist.");
}
if (oldAcl == null) {
throw new AuthorizationException("The acl is not exist.");
}
return oldAcl;
}).thenCompose(oldAcl -> {
if (resource != null) {
oldAcl.deletePolicy(finalPolicyType, resource);
}
if (resource == null || CollectionUtils.isEmpty(oldAcl.getPolicies())) {
return this.getAuthorizationMetadataProvider().deleteAcl(subject);
}
return this.getAuthorizationMetadataProvider().updateAcl(oldAcl);
});
} catch (Exception e) {
return this.handleException(e);
}
}
@Override
public CompletableFuture getAcl(Subject subject) {
try {
if (subject == null) {
throw new AuthorizationException("The subject is null.");
}
CompletableFuture extends Subject> subjectFuture;
if (subject.isSubject(SubjectType.USER)) {
User user = (User) subject;
subjectFuture = this.getAuthenticationMetadataProvider().getUser(user.getUsername());
} else {
subjectFuture = CompletableFuture.completedFuture(subject);
}
return subjectFuture.thenCompose(sub -> {
if (sub == null) {
throw new AuthorizationException("The subject is not exist.");
}
return this.getAuthorizationMetadataProvider().getAcl(sub);
});
} catch (Exception e) {
return this.handleException(e);
}
}
@Override
public CompletableFuture> listAcl(String subjectFilter, String resourceFilter) {
return this.getAuthorizationMetadataProvider().listAcl(subjectFilter, resourceFilter);
}
private static void initAcl(Acl acl) {
acl.getPolicies().forEach(policy -> {
if (policy.getPolicyType() == null) {
policy.setPolicyType(PolicyType.CUSTOM);
}
});
}
private void validate(Acl acl) {
Subject subject = acl.getSubject();
if (subject.getSubjectType() == null) {
throw new AuthorizationException("The subject type is null.");
}
List policies = acl.getPolicies();
if (CollectionUtils.isEmpty(policies)) {
throw new AuthorizationException("The policies is empty.");
}
for (Policy policy : policies) {
this.validate(policy);
}
}
private void validate(Policy policy) {
List policyEntries = policy.getEntries();
if (CollectionUtils.isEmpty(policyEntries)) {
throw new AuthorizationException("The policy entries is empty.");
}
for (PolicyEntry policyEntry : policyEntries) {
this.validate(policyEntry);
}
}
private void validate(PolicyEntry entry) {
Resource resource = entry.getResource();
if (resource == null) {
throw new AuthorizationException("The resource is null.");
}
if (resource.getResourceType() == null) {
throw new AuthorizationException("The resource type is null.");
}
if (resource.getResourcePattern() == null) {
throw new AuthorizationException("The resource pattern is null.");
}
if (CollectionUtils.isEmpty(entry.getActions())) {
throw new AuthorizationException("The actions is empty.");
}
if (entry.getActions().contains(Action.ANY)) {
throw new AuthorizationException("The actions can not be Any.");
}
Environment environment = entry.getEnvironment();
if (environment != null && CollectionUtils.isNotEmpty(environment.getSourceIps())) {
for (String sourceIp : environment.getSourceIps()) {
if (StringUtils.isBlank(sourceIp)) {
throw new AuthorizationException("The source ip is empty.");
}
if (!IPAddressUtils.isValidIPOrCidr(sourceIp)) {
throw new AuthorizationException("The source ip is invalid.");
}
}
}
if (entry.getDecision() == null) {
throw new AuthorizationException("The decision is null or illegal.");
}
}
private CompletableFuture handleException(Exception e) {
CompletableFuture result = new CompletableFuture<>();
Throwable throwable = ExceptionUtils.getRealException(e);
result.completeExceptionally(throwable);
return result;
}
private AuthenticationMetadataProvider getAuthenticationMetadataProvider() {
if (authenticationMetadataProvider == null) {
throw new IllegalStateException("The authenticationMetadataProvider is not configured.");
}
return authenticationMetadataProvider;
}
private AuthorizationMetadataProvider getAuthorizationMetadataProvider() {
if (authorizationMetadataProvider == null) {
throw new IllegalStateException("The authorizationMetadataProvider is not configured.");
}
return authorizationMetadataProvider;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/model/Acl.java
================================================
/*
* 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.rocketmq.auth.authorization.model;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
import org.apache.rocketmq.common.action.Action;
public class Acl {
private Subject subject;
private List policies;
public static Acl of(Subject subject, Policy policy) {
return of(subject, Lists.newArrayList(policy));
}
public static Acl of(Subject subject, List policies) {
Acl acl = new Acl();
acl.setSubject(subject);
acl.setPolicies(policies);
return acl;
}
public static Acl of(Subject subject, List resources, List actions, Environment environment,
Decision decision) {
Acl acl = new Acl();
acl.setSubject(subject);
Policy policy = Policy.of(resources, actions, environment, decision);
acl.setPolicies(Lists.newArrayList(policy));
return acl;
}
public void updatePolicy(Policy policy) {
this.updatePolicy(Lists.newArrayList(policy));
}
public void updatePolicy(List policies) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
policies.forEach(newPolicy -> {
Policy oldPolicy = this.getPolicy(newPolicy.getPolicyType());
if (oldPolicy == null) {
this.policies.add(newPolicy);
} else {
oldPolicy.updateEntry(newPolicy.getEntries());
}
});
}
public void deletePolicy(PolicyType policyType, Resource resource) {
Policy policy = getPolicy(policyType);
if (policy == null) {
return;
}
policy.deleteEntry(resource);
if (CollectionUtils.isEmpty(policy.getEntries())) {
this.policies.remove(policy);
}
}
public Policy getPolicy(PolicyType policyType) {
if (CollectionUtils.isEmpty(this.policies)) {
return null;
}
for (Policy policy : this.policies) {
if (policy.getPolicyType() == policyType) {
return policy;
}
}
return null;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
public List getPolicies() {
return policies;
}
public void setPolicies(List policies) {
this.policies = policies;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/model/Environment.java
================================================
/*
* 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.rocketmq.auth.authorization.model;
import java.util.Collections;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.common.utils.IPAddressUtils;
public class Environment {
private List sourceIps;
public static Environment of(String sourceIp) {
if (StringUtils.isEmpty(sourceIp)) {
return null;
}
return of(Collections.singletonList(sourceIp));
}
public static Environment of(List sourceIps) {
if (CollectionUtils.isEmpty(sourceIps)) {
return null;
}
Environment environment = new Environment();
environment.setSourceIps(sourceIps);
return environment;
}
public boolean isMatch(Environment environment) {
if (CollectionUtils.isEmpty(this.sourceIps)) {
return true;
}
if (CollectionUtils.isEmpty(environment.getSourceIps())) {
return false;
}
String targetIp = environment.getSourceIps().get(0);
for (String sourceIp : this.sourceIps) {
if (IPAddressUtils.isIPInRange(targetIp, sourceIp)) {
return true;
}
}
return false;
}
public List getSourceIps() {
return sourceIps;
}
public void setSourceIps(List sourceIps) {
this.sourceIps = sourceIps;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/model/Policy.java
================================================
/*
* 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.rocketmq.auth.authorization.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
public class Policy {
private PolicyType policyType;
private List entries;
public static Policy of(List resources, List actions, Environment environment,
Decision decision) {
return of(PolicyType.CUSTOM, resources, actions, environment, decision);
}
public static Policy of(PolicyType policyType, List resources, List actions,
Environment environment,
Decision decision) {
Policy policy = new Policy();
policy.setPolicyType(policyType);
List entries = resources.stream()
.map(resource -> PolicyEntry.of(resource, actions, environment, decision))
.collect(Collectors.toList());
policy.setEntries(entries);
return policy;
}
public static Policy of(PolicyType type, List entries) {
Policy policy = new Policy();
policy.setPolicyType(type);
policy.setEntries(entries);
return policy;
}
public void updateEntry(List newEntries) {
if (this.entries == null) {
this.entries = new ArrayList<>();
}
newEntries.forEach(newEntry -> {
PolicyEntry entry = getEntry(newEntry.getResource());
if (entry == null) {
this.entries.add(newEntry);
} else {
entry.updateEntry(newEntry.getActions(), newEntry.getEnvironment(), newEntry.getDecision());
}
});
}
public void deleteEntry(Resource resources) {
PolicyEntry entry = getEntry(resources);
if (entry != null) {
this.entries.remove(entry);
}
}
private PolicyEntry getEntry(Resource resource) {
if (CollectionUtils.isEmpty(this.entries)) {
return null;
}
for (PolicyEntry entry : this.entries) {
if (Objects.equals(entry.getResource(), resource)) {
return entry;
}
}
return null;
}
public PolicyType getPolicyType() {
return policyType;
}
public void setPolicyType(PolicyType policyType) {
this.policyType = policyType;
}
public List getEntries() {
return entries;
}
public void setEntries(List entries) {
this.entries = entries;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/model/PolicyEntry.java
================================================
/*
* 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.rocketmq.auth.authorization.model;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.auth.authorization.enums.Decision;
public class PolicyEntry {
private Resource resource;
private List actions;
private Environment environment;
private Decision decision;
public static PolicyEntry of(Resource resource, List actions, Environment environment, Decision decision) {
PolicyEntry policyEntry = new PolicyEntry();
policyEntry.setResource(resource);
policyEntry.setActions(actions);
policyEntry.setEnvironment(environment);
policyEntry.setDecision(decision);
return policyEntry;
}
public void updateEntry(List actions, Environment environment,
Decision decision) {
this.setActions(actions);
this.setEnvironment(environment);
this.setDecision(decision);
}
public boolean isMatchResource(Resource resource) {
return this.resource.isMatch(resource);
}
public boolean isMatchAction(List actions) {
if (CollectionUtils.isEmpty(this.actions)) {
return false;
}
if (actions.contains(Action.ANY)) {
return true;
}
return actions.stream()
.anyMatch(action -> this.actions.contains(action)
|| this.actions.contains(Action.ALL));
}
public boolean isMatchEnvironment(Environment environment) {
if (this.environment == null) {
return true;
}
return this.environment.isMatch(environment);
}
public String toResourceStr() {
if (resource == null) {
return null;
}
return resource.getResourceKey();
}
public List toActionsStr() {
if (CollectionUtils.isEmpty(actions)) {
return null;
}
return actions.stream().map(Action::getName)
.collect(Collectors.toList());
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public List getActions() {
return actions;
}
public void setActions(List actions) {
this.actions = actions;
}
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Decision getDecision() {
return decision;
}
public void setDecision(Decision decision) {
this.decision = decision;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/model/RequestContext.java
================================================
/*
* 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.rocketmq.auth.authorization.model;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.common.action.Action;
public class RequestContext {
private Subject subject;
private Resource resource;
private Action action;
private String sourceIp;
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public Action getAction() {
return action;
}
public void setAction(Action action) {
this.action = action;
}
public String getSourceIp() {
return sourceIp;
}
public void setSourceIp(String sourceIp) {
this.sourceIp = sourceIp;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/model/Resource.java
================================================
/*
* 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.rocketmq.auth.authorization.model;
import com.alibaba.fastjson2.annotation.JSONField;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.common.resource.ResourceType;
import org.apache.rocketmq.common.resource.ResourcePattern;
import org.apache.rocketmq.common.constant.CommonConstants;
import org.apache.rocketmq.remoting.protocol.NamespaceUtil;
public class Resource {
private ResourceType resourceType;
private String resourceName;
private ResourcePattern resourcePattern;
public static Resource ofCluster(String clusterName) {
return of(ResourceType.CLUSTER, clusterName, ResourcePattern.LITERAL);
}
public static Resource ofTopic(String topicName) {
return of(ResourceType.TOPIC, topicName, ResourcePattern.LITERAL);
}
public static Resource ofGroup(String groupName) {
if (NamespaceUtil.isRetryTopic(groupName)) {
groupName = NamespaceUtil.withOutRetryAndDLQ(groupName);
}
return of(ResourceType.GROUP, groupName, ResourcePattern.LITERAL);
}
public static Resource of(ResourceType resourceType, String resourceName, ResourcePattern resourcePattern) {
Resource resource = new Resource();
resource.resourceType = resourceType;
resource.resourceName = resourceName;
resource.resourcePattern = resourcePattern;
return resource;
}
public static List of(List resourceKeys) {
if (CollectionUtils.isEmpty(resourceKeys)) {
return null;
}
return resourceKeys.stream().map(Resource::of).collect(Collectors.toList());
}
public static Resource of(String resourceKey) {
if (StringUtils.isBlank(resourceKey)) {
return null;
}
if (StringUtils.equals(resourceKey, CommonConstants.ASTERISK)) {
return of(ResourceType.ANY, null, ResourcePattern.ANY);
}
String type = StringUtils.substringBefore(resourceKey, CommonConstants.COLON);
ResourceType resourceType = ResourceType.getByName(type);
if (resourceType == null) {
return null;
}
String resourceName = StringUtils.substringAfter(resourceKey, CommonConstants.COLON);
ResourcePattern resourcePattern = ResourcePattern.LITERAL;
if (StringUtils.equals(resourceName, CommonConstants.ASTERISK)) {
resourceName = null;
resourcePattern = ResourcePattern.ANY;
} else if (StringUtils.endsWith(resourceName, CommonConstants.ASTERISK)) {
resourceName = StringUtils.substringBefore(resourceName, CommonConstants.ASTERISK);
resourcePattern = ResourcePattern.PREFIXED;
}
return of(resourceType, resourceName, resourcePattern);
}
@JSONField(serialize = false)
public String getResourceKey() {
if (resourceType == ResourceType.ANY) {
return CommonConstants.ASTERISK;
}
switch (resourcePattern) {
case ANY:
return resourceType.getName() + CommonConstants.COLON + CommonConstants.ASTERISK;
case LITERAL:
return resourceType.getName() + CommonConstants.COLON + resourceName;
case PREFIXED:
return resourceType.getName() + CommonConstants.COLON + resourceName + CommonConstants.ASTERISK;
default:
return null;
}
}
public boolean isMatch(Resource resource) {
if (this.resourceType == ResourceType.ANY) {
return true;
}
if (this.resourceType != resource.resourceType) {
return false;
}
switch (resourcePattern) {
case ANY:
return true;
case LITERAL:
return StringUtils.equals(resource.resourceName, this.resourceName);
case PREFIXED:
return StringUtils.startsWith(resource.resourceName, this.resourceName);
default:
return false;
}
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Resource resource = (Resource) o;
return resourceType == resource.resourceType
&& Objects.equals(resourceName, resource.resourceName)
&& resourcePattern == resource.resourcePattern;
}
@Override
public int hashCode() {
return Objects.hash(resourceType, resourceName, resourcePattern);
}
public ResourceType getResourceType() {
return resourceType;
}
public void setResourceType(ResourceType resourceType) {
this.resourceType = resourceType;
}
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
public ResourcePattern getResourcePattern() {
return resourcePattern;
}
public void setResourcePattern(ResourcePattern resourcePattern) {
this.resourcePattern = resourcePattern;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/provider/AuthorizationMetadataProvider.java
================================================
/*
* 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.rocketmq.auth.authorization.provider;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.config.AuthConfig;
public interface AuthorizationMetadataProvider {
void initialize(AuthConfig authConfig, Supplier> metadataService);
void shutdown();
CompletableFuture createAcl(Acl acl);
CompletableFuture deleteAcl(Subject subject);
CompletableFuture updateAcl(Acl acl);
CompletableFuture getAcl(Subject subject);
CompletableFuture> listAcl(String subjectFilter, String resourceFilter);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/provider/AuthorizationProvider.java
================================================
/*
* 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.rocketmq.auth.authorization.provider;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
public interface AuthorizationProvider {
void initialize(AuthConfig config);
void initialize(AuthConfig config, Supplier> metadataService);
CompletableFuture authorize(AuthorizationContext context);
List newContexts(Metadata metadata, GeneratedMessageV3 message);
List newContexts(ChannelHandlerContext context, RemotingCommand command);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/provider/DefaultAuthorizationProvider.java
================================================
/*
* 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.rocketmq.auth.authorization.provider;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.ChannelHandlerContext;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.rocketmq.auth.authorization.builder.AuthorizationContextBuilder;
import org.apache.rocketmq.auth.authorization.builder.DefaultAuthorizationContextBuilder;
import org.apache.rocketmq.auth.authorization.chain.AclAuthorizationHandler;
import org.apache.rocketmq.auth.authorization.chain.UserAuthorizationHandler;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.common.chain.HandlerChain;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DefaultAuthorizationProvider implements AuthorizationProvider {
protected final Logger log = LoggerFactory.getLogger(LoggerName.ROCKETMQ_AUTH_AUDIT_LOGGER_NAME);
protected AuthConfig authConfig;
protected Supplier> metadataService;
protected AuthorizationContextBuilder authorizationContextBuilder;
@Override
public void initialize(AuthConfig config) {
this.initialize(config, null);
}
@Override
public void initialize(AuthConfig config, Supplier> metadataService) {
this.authConfig = config;
this.metadataService = metadataService;
this.authorizationContextBuilder = new DefaultAuthorizationContextBuilder(config);
}
@Override
public CompletableFuture authorize(DefaultAuthorizationContext context) {
return this.newHandlerChain().handle(context)
.whenComplete((nil, ex) -> doAuditLog(context, ex));
}
@Override
public List newContexts(Metadata metadata, GeneratedMessageV3 message) {
return this.authorizationContextBuilder.build(metadata, message);
}
@Override
public List newContexts(ChannelHandlerContext context, RemotingCommand command) {
return this.authorizationContextBuilder.build(context, command);
}
protected HandlerChain> newHandlerChain() {
return HandlerChain.>create()
.addNext(new UserAuthorizationHandler(authConfig, metadataService))
.addNext(new AclAuthorizationHandler(authConfig, metadataService));
}
protected void doAuditLog(DefaultAuthorizationContext context, Throwable ex) {
if (context.getSubject() == null) {
return;
}
Decision decision = Decision.ALLOW;
if (ex != null) {
decision = Decision.DENY;
}
String subject = context.getSubject().getSubjectKey();
String actions = context.getActions().stream().map(Action::getName)
.collect(Collectors.joining(","));
String sourceIp = context.getSourceIp();
String resource = context.getResource().getResourceKey();
String request = context.getRpcCode();
String format = "[AUTHORIZATION] Subject = {} is {} Action = {} from sourceIp = {} on resource = {} for request = {}.";
if (decision == Decision.ALLOW) {
log.debug(format, subject, decision.getName(), actions, sourceIp, resource, request);
} else {
log.info(format, subject, decision.getName(), actions, sourceIp, resource, request);
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/provider/LocalAuthorizationMetadataProvider.java
================================================
/*
* 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.rocketmq.auth.authorization.provider;
import com.alibaba.fastjson2.JSON;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.authorization.model.Policy;
import org.apache.rocketmq.auth.authorization.model.PolicyEntry;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.config.ConfigRocksDBStorage;
import org.apache.rocketmq.common.thread.ThreadPoolMonitor;
import org.rocksdb.RocksDB;
public class LocalAuthorizationMetadataProvider implements AuthorizationMetadataProvider {
private final static String AUTH_METADATA_COLUMN_FAMILY = new String(RocksDB.DEFAULT_COLUMN_FAMILY,
StandardCharsets.UTF_8);
private ConfigRocksDBStorage storage;
private LoadingCache aclCache;
protected ThreadPoolExecutor cacheRefreshExecutor;
@Override
public void initialize(AuthConfig authConfig, Supplier> metadataService) {
this.storage = ConfigRocksDBStorage.getStore(authConfig.getAuthConfigPath() + File.separator + "acls", false);
if (!this.storage.start()) {
throw new RuntimeException("Failed to load rocksdb for auth_acl, please check whether it is occupied.");
}
this.cacheRefreshExecutor = ThreadPoolMonitor.createAndMonitor(
1,
1,
1000 * 60,
TimeUnit.MILLISECONDS,
"AclCacheRefresh",
100000
);
this.aclCache = Caffeine.newBuilder()
.maximumSize(authConfig.getAclCacheMaxNum())
.expireAfterAccess(authConfig.getAclCacheExpiredSecond(), TimeUnit.SECONDS)
.refreshAfterWrite(authConfig.getAclCacheRefreshSecond(), TimeUnit.SECONDS)
.executor(cacheRefreshExecutor)
.build(new AclCacheLoader(this.storage));
}
@Override
public CompletableFuture createAcl(Acl acl) {
try {
Subject subject = acl.getSubject();
byte[] keyBytes = subject.getSubjectKey().getBytes(StandardCharsets.UTF_8);
byte[] valueBytes = JSON.toJSONBytes(acl);
this.storage.put(AUTH_METADATA_COLUMN_FAMILY, keyBytes, keyBytes.length, valueBytes);
this.storage.flushWAL();
this.aclCache.invalidate(subject.getSubjectKey());
} catch (Exception e) {
throw new AuthorizationException("create Acl to RocksDB failed.", e);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture deleteAcl(Subject subject) {
try {
byte[] keyBytes = subject.getSubjectKey().getBytes(StandardCharsets.UTF_8);
this.storage.delete(AUTH_METADATA_COLUMN_FAMILY, keyBytes);
this.storage.flushWAL();
this.aclCache.invalidate(subject.getSubjectKey());
} catch (Exception e) {
throw new AuthorizationException("delete Acl from RocksDB failed.", e);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture updateAcl(Acl acl) {
try {
Subject subject = acl.getSubject();
byte[] keyBytes = subject.getSubjectKey().getBytes(StandardCharsets.UTF_8);
byte[] valueBytes = JSON.toJSONBytes(acl);
this.storage.put(AUTH_METADATA_COLUMN_FAMILY, keyBytes, keyBytes.length, valueBytes);
this.storage.flushWAL();
this.aclCache.invalidate(subject.getSubjectKey());
} catch (Exception e) {
throw new AuthorizationException("update Acl to RocksDB failed.", e);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture getAcl(Subject subject) {
Acl acl = aclCache.get(subject.getSubjectKey());
if (acl == AclCacheLoader.EMPTY_ACL) {
return CompletableFuture.completedFuture(null);
}
return CompletableFuture.completedFuture(acl);
}
@Override
public CompletableFuture> listAcl(String subjectFilter, String resourceFilter) {
List result = new ArrayList<>();
CompletableFuture> future = new CompletableFuture<>();
try {
this.storage.iterate(AUTH_METADATA_COLUMN_FAMILY, (key, value) -> {
String subjectKey = new String(key, StandardCharsets.UTF_8);
if (StringUtils.isNotBlank(subjectFilter) && !subjectKey.contains(subjectFilter)) {
return;
}
Subject subject = Subject.of(subjectKey);
Acl acl = JSON.parseObject(new String(value, StandardCharsets.UTF_8), Acl.class);
List policies = acl.getPolicies();
if (!CollectionUtils.isNotEmpty(policies)) {
return;
}
Iterator policyIterator = policies.iterator();
while (policyIterator.hasNext()) {
Policy policy = policyIterator.next();
List entries = policy.getEntries();
if (CollectionUtils.isEmpty(entries)) {
continue;
}
if (StringUtils.isNotBlank(resourceFilter)) {
entries.removeIf(entry -> !entry.toResourceStr().contains(resourceFilter));
}
if (CollectionUtils.isEmpty(entries)) {
policyIterator.remove();
}
}
if (CollectionUtils.isNotEmpty(policies)) {
result.add(Acl.of(subject, policies));
}
});
} catch (Exception e) {
future.completeExceptionally(e);
}
future.complete(result);
return future;
}
@Override
public void shutdown() {
if (this.storage != null) {
this.storage.shutdown();
}
if (this.cacheRefreshExecutor != null) {
this.cacheRefreshExecutor.shutdown();
}
}
private static class AclCacheLoader implements CacheLoader {
private final ConfigRocksDBStorage storage;
public static final Acl EMPTY_ACL = new Acl();
public AclCacheLoader(ConfigRocksDBStorage storage) {
this.storage = storage;
}
@Override
public Acl load(String subjectKey) {
try {
byte[] keyBytes = subjectKey.getBytes(StandardCharsets.UTF_8);
Subject subject = Subject.of(subjectKey);
byte[] valueBytes = this.storage.get(AUTH_METADATA_COLUMN_FAMILY, keyBytes);
if (ArrayUtils.isEmpty(valueBytes)) {
return EMPTY_ACL;
}
Acl acl = JSON.parseObject(valueBytes, Acl.class);
return Acl.of(subject, acl.getPolicies());
} catch (Exception e) {
throw new AuthorizationException("get Acl from RocksDB failed.", e);
}
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/strategy/AbstractAuthorizationStrategy.java
================================================
/*
* 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.rocketmq.auth.authorization.strategy;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.provider.AuthorizationProvider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.utils.ExceptionUtils;
public abstract class AbstractAuthorizationStrategy implements AuthorizationStrategy {
protected final AuthConfig authConfig;
protected final Set authorizationWhiteSet = new HashSet<>();
protected final AuthorizationProvider authorizationProvider;
public AbstractAuthorizationStrategy(AuthConfig authConfig, Supplier> metadataService) {
this.authConfig = authConfig;
this.authorizationProvider = AuthorizationFactory.getProvider(authConfig);
if (this.authorizationProvider != null) {
this.authorizationProvider.initialize(authConfig, metadataService);
}
if (StringUtils.isNotBlank(authConfig.getAuthorizationWhitelist())) {
String[] whitelist = StringUtils.split(authConfig.getAuthorizationWhitelist(), ",");
for (String rpcCode : whitelist) {
this.authorizationWhiteSet.add(StringUtils.trim(rpcCode));
}
}
}
public void doEvaluate(AuthorizationContext context) {
if (context == null) {
return;
}
if (!this.authConfig.isAuthorizationEnabled()) {
return;
}
if (this.authorizationProvider == null) {
return;
}
if (this.authorizationWhiteSet.contains(context.getRpcCode())) {
return;
}
try {
this.authorizationProvider.authorize(context).join();
} catch (AuthorizationException ex) {
throw ex;
} catch (Throwable ex) {
Throwable exception = ExceptionUtils.getRealException(ex);
if (exception instanceof AuthorizationException) {
throw (AuthorizationException) exception;
}
throw new AuthorizationException("Authorization failed. Please verify your access rights and try again.", exception);
}
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/strategy/AuthorizationStrategy.java
================================================
/*
* 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.rocketmq.auth.authorization.strategy;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
public interface AuthorizationStrategy {
void evaluate(AuthorizationContext context);
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/strategy/StatefulAuthorizationStrategy.java
================================================
/*
* 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.rocketmq.auth.authorization.strategy;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.common.constant.CommonConstants;
public class StatefulAuthorizationStrategy extends AbstractAuthorizationStrategy {
protected Cache> authCache;
public StatefulAuthorizationStrategy(AuthConfig authConfig, Supplier> metadataService) {
super(authConfig, metadataService);
this.authCache = Caffeine.newBuilder()
.expireAfterWrite(authConfig.getStatefulAuthorizationCacheExpiredSecond(), TimeUnit.SECONDS)
.maximumSize(authConfig.getStatefulAuthorizationCacheMaxNum())
.build();
}
@Override
public void evaluate(AuthorizationContext context) {
if (StringUtils.isBlank(context.getChannelId())) {
this.doEvaluate(context);
return;
}
Pair result = this.authCache.get(buildKey(context), key -> {
try {
this.doEvaluate(context);
return Pair.of(true, null);
} catch (AuthorizationException ex) {
return Pair.of(false, ex);
}
});
if (result != null && result.getObject1() == Boolean.FALSE) {
throw result.getObject2();
}
}
private String buildKey(AuthorizationContext context) {
if (context instanceof DefaultAuthorizationContext) {
DefaultAuthorizationContext ctx = (DefaultAuthorizationContext) context;
return ctx.getChannelId()
+ (ctx.getSubject() != null ? CommonConstants.POUND + ctx.getSubjectKey() : "")
+ CommonConstants.POUND + ctx.getResourceKey()
+ CommonConstants.POUND + StringUtils.join(ctx.getActions(), CommonConstants.COMMA)
+ CommonConstants.POUND + ctx.getSourceIp();
}
throw new AuthorizationException("The request of {} is not support.", context.getClass().getSimpleName());
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/authorization/strategy/StatelessAuthorizationStrategy.java
================================================
/*
* 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.rocketmq.auth.authorization.strategy;
import java.util.function.Supplier;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
import org.apache.rocketmq.auth.config.AuthConfig;
public class StatelessAuthorizationStrategy extends AbstractAuthorizationStrategy {
public StatelessAuthorizationStrategy(AuthConfig authConfig, Supplier> metadataService) {
super(authConfig, metadataService);
}
@Override
public void evaluate(AuthorizationContext context) {
super.doEvaluate(context);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/config/AuthConfig.java
================================================
/*
* 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.rocketmq.auth.config;
public class AuthConfig implements Cloneable {
private String configName;
private String clusterName;
private String authConfigPath;
private boolean authenticationEnabled = false;
private String authenticationProvider;
private String authenticationMetadataProvider;
private String authenticationStrategy;
private String authenticationWhitelist;
private String initAuthenticationUser;
private String innerClientAuthenticationCredentials;
private boolean authorizationEnabled = false;
private String authorizationProvider;
private String authorizationMetadataProvider;
private String authorizationStrategy;
private String authorizationWhitelist;
private boolean migrateAuthFromV1Enabled = false;
private int userCacheMaxNum = 1000;
private int userCacheExpiredSecond = 600;
private int userCacheRefreshSecond = 60;
private int aclCacheMaxNum = 1000;
private int aclCacheExpiredSecond = 600;
private int aclCacheRefreshSecond = 60;
private int statefulAuthenticationCacheMaxNum = 10000;
private int statefulAuthenticationCacheExpiredSecond = 60;
private int statefulAuthorizationCacheMaxNum = 10000;
private int statefulAuthorizationCacheExpiredSecond = 60;
@Override
public AuthConfig clone() {
try {
return (AuthConfig) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public String getConfigName() {
return configName;
}
public void setConfigName(String configName) {
this.configName = configName;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getAuthConfigPath() {
return authConfigPath;
}
public void setAuthConfigPath(String authConfigPath) {
this.authConfigPath = authConfigPath;
}
public boolean isAuthenticationEnabled() {
return authenticationEnabled;
}
public void setAuthenticationEnabled(boolean authenticationEnabled) {
this.authenticationEnabled = authenticationEnabled;
}
public String getAuthenticationProvider() {
return authenticationProvider;
}
public void setAuthenticationProvider(String authenticationProvider) {
this.authenticationProvider = authenticationProvider;
}
public String getAuthenticationMetadataProvider() {
return authenticationMetadataProvider;
}
public void setAuthenticationMetadataProvider(String authenticationMetadataProvider) {
this.authenticationMetadataProvider = authenticationMetadataProvider;
}
public String getAuthenticationStrategy() {
return authenticationStrategy;
}
public void setAuthenticationStrategy(String authenticationStrategy) {
this.authenticationStrategy = authenticationStrategy;
}
public String getAuthenticationWhitelist() {
return authenticationWhitelist;
}
public void setAuthenticationWhitelist(String authenticationWhitelist) {
this.authenticationWhitelist = authenticationWhitelist;
}
public String getInitAuthenticationUser() {
return initAuthenticationUser;
}
public void setInitAuthenticationUser(String initAuthenticationUser) {
this.initAuthenticationUser = initAuthenticationUser;
}
public String getInnerClientAuthenticationCredentials() {
return innerClientAuthenticationCredentials;
}
public void setInnerClientAuthenticationCredentials(String innerClientAuthenticationCredentials) {
this.innerClientAuthenticationCredentials = innerClientAuthenticationCredentials;
}
public boolean isAuthorizationEnabled() {
return authorizationEnabled;
}
public void setAuthorizationEnabled(boolean authorizationEnabled) {
this.authorizationEnabled = authorizationEnabled;
}
public String getAuthorizationProvider() {
return authorizationProvider;
}
public void setAuthorizationProvider(String authorizationProvider) {
this.authorizationProvider = authorizationProvider;
}
public String getAuthorizationMetadataProvider() {
return authorizationMetadataProvider;
}
public void setAuthorizationMetadataProvider(String authorizationMetadataProvider) {
this.authorizationMetadataProvider = authorizationMetadataProvider;
}
public String getAuthorizationStrategy() {
return authorizationStrategy;
}
public void setAuthorizationStrategy(String authorizationStrategy) {
this.authorizationStrategy = authorizationStrategy;
}
public String getAuthorizationWhitelist() {
return authorizationWhitelist;
}
public void setAuthorizationWhitelist(String authorizationWhitelist) {
this.authorizationWhitelist = authorizationWhitelist;
}
public boolean isMigrateAuthFromV1Enabled() {
return migrateAuthFromV1Enabled;
}
public void setMigrateAuthFromV1Enabled(boolean migrateAuthFromV1Enabled) {
this.migrateAuthFromV1Enabled = migrateAuthFromV1Enabled;
}
public int getUserCacheMaxNum() {
return userCacheMaxNum;
}
public void setUserCacheMaxNum(int userCacheMaxNum) {
this.userCacheMaxNum = userCacheMaxNum;
}
public int getUserCacheExpiredSecond() {
return userCacheExpiredSecond;
}
public void setUserCacheExpiredSecond(int userCacheExpiredSecond) {
this.userCacheExpiredSecond = userCacheExpiredSecond;
}
public int getUserCacheRefreshSecond() {
return userCacheRefreshSecond;
}
public void setUserCacheRefreshSecond(int userCacheRefreshSecond) {
this.userCacheRefreshSecond = userCacheRefreshSecond;
}
public int getAclCacheMaxNum() {
return aclCacheMaxNum;
}
public void setAclCacheMaxNum(int aclCacheMaxNum) {
this.aclCacheMaxNum = aclCacheMaxNum;
}
public int getAclCacheExpiredSecond() {
return aclCacheExpiredSecond;
}
public void setAclCacheExpiredSecond(int aclCacheExpiredSecond) {
this.aclCacheExpiredSecond = aclCacheExpiredSecond;
}
public int getAclCacheRefreshSecond() {
return aclCacheRefreshSecond;
}
public void setAclCacheRefreshSecond(int aclCacheRefreshSecond) {
this.aclCacheRefreshSecond = aclCacheRefreshSecond;
}
public int getStatefulAuthenticationCacheMaxNum() {
return statefulAuthenticationCacheMaxNum;
}
public void setStatefulAuthenticationCacheMaxNum(int statefulAuthenticationCacheMaxNum) {
this.statefulAuthenticationCacheMaxNum = statefulAuthenticationCacheMaxNum;
}
public int getStatefulAuthenticationCacheExpiredSecond() {
return statefulAuthenticationCacheExpiredSecond;
}
public void setStatefulAuthenticationCacheExpiredSecond(int statefulAuthenticationCacheExpiredSecond) {
this.statefulAuthenticationCacheExpiredSecond = statefulAuthenticationCacheExpiredSecond;
}
public int getStatefulAuthorizationCacheMaxNum() {
return statefulAuthorizationCacheMaxNum;
}
public void setStatefulAuthorizationCacheMaxNum(int statefulAuthorizationCacheMaxNum) {
this.statefulAuthorizationCacheMaxNum = statefulAuthorizationCacheMaxNum;
}
public int getStatefulAuthorizationCacheExpiredSecond() {
return statefulAuthorizationCacheExpiredSecond;
}
public void setStatefulAuthorizationCacheExpiredSecond(int statefulAuthorizationCacheExpiredSecond) {
this.statefulAuthorizationCacheExpiredSecond = statefulAuthorizationCacheExpiredSecond;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/AuthMigrator.java
================================================
/*
* 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.rocketmq.auth.migration;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.acl.common.AclConstants;
import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManager;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.manager.AuthorizationMetadataManager;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.authorization.model.Policy;
import org.apache.rocketmq.auth.authorization.model.PolicyEntry;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.auth.migration.v1.PlainPermissionManager;
import org.apache.rocketmq.auth.migration.v1.AclConfig;
import org.apache.rocketmq.auth.migration.v1.PlainAccessConfig;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.common.constant.CommonConstants;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.resource.ResourcePattern;
import org.apache.rocketmq.common.resource.ResourceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class AuthMigrator {
protected static final Logger LOG = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
private final AuthConfig authConfig;
private final PlainPermissionManager plainPermissionManager;
private final AuthenticationMetadataManager authenticationMetadataManager;
private final AuthorizationMetadataManager authorizationMetadataManager;
public AuthMigrator(AuthConfig authConfig) {
this.authConfig = authConfig;
this.plainPermissionManager = new PlainPermissionManager();
this.authenticationMetadataManager = AuthenticationFactory.getMetadataManager(authConfig);
this.authorizationMetadataManager = AuthorizationFactory.getMetadataManager(authConfig);
}
public void migrate() {
if (!authConfig.isMigrateAuthFromV1Enabled()) {
return;
}
AclConfig aclConfig = this.plainPermissionManager.getAllAclConfig();
List accessConfigs = aclConfig.getPlainAccessConfigs();
if (CollectionUtils.isEmpty(accessConfigs)) {
return;
}
for (PlainAccessConfig accessConfig : accessConfigs) {
doMigrate(accessConfig);
}
}
private void doMigrate(PlainAccessConfig accessConfig) {
this.isUserExisted(accessConfig.getAccessKey()).thenCompose(existed -> {
if (existed) {
return CompletableFuture.completedFuture(null);
}
return createUserAndAcl(accessConfig);
}).exceptionally(ex -> {
LOG.error("[ACL MIGRATE] An error occurred while migrating ACL configurations for AccessKey:{}.", accessConfig.getAccessKey(), ex);
return null;
}).join();
}
private CompletableFuture createUserAndAcl(PlainAccessConfig accessConfig) {
return createUser(accessConfig).thenCompose(nil -> createAcl(accessConfig));
}
private CompletableFuture createUser(PlainAccessConfig accessConfig) {
User user = new User();
user.setUsername(accessConfig.getAccessKey());
user.setPassword(accessConfig.getSecretKey());
if (accessConfig.isAdmin()) {
user.setUserType(UserType.SUPER);
} else {
user.setUserType(UserType.NORMAL);
}
return this.authenticationMetadataManager.createUser(user);
}
private CompletableFuture createAcl(PlainAccessConfig config) {
Subject subject = User.of(config.getAccessKey());
List policies = new ArrayList<>();
Policy customPolicy = null;
if (CollectionUtils.isNotEmpty(config.getTopicPerms())) {
for (String topicPerm : config.getTopicPerms()) {
String[] temp = StringUtils.split(topicPerm, CommonConstants.EQUAL);
if (temp.length != 2) {
continue;
}
String topicName = StringUtils.trim(temp[0]);
String perm = StringUtils.trim(temp[1]);
Resource resource = Resource.ofTopic(topicName);
List actions = parseActions(perm);
Decision decision = parseDecision(perm);
PolicyEntry policyEntry = PolicyEntry.of(resource, actions, null, decision);
if (customPolicy == null) {
customPolicy = Policy.of(PolicyType.CUSTOM, new ArrayList<>());
}
customPolicy.getEntries().add(policyEntry);
}
}
if (CollectionUtils.isNotEmpty(config.getGroupPerms())) {
for (String groupPerm : config.getGroupPerms()) {
String[] temp = StringUtils.split(groupPerm, CommonConstants.EQUAL);
if (temp.length != 2) {
continue;
}
String groupName = StringUtils.trim(temp[0]);
String perm = StringUtils.trim(temp[1]);
Resource resource = Resource.ofGroup(groupName);
List actions = parseActions(perm);
Decision decision = parseDecision(perm);
PolicyEntry policyEntry = PolicyEntry.of(resource, actions, null, decision);
if (customPolicy == null) {
customPolicy = Policy.of(PolicyType.CUSTOM, new ArrayList<>());
}
customPolicy.getEntries().add(policyEntry);
}
}
if (customPolicy != null) {
policies.add(customPolicy);
}
Policy defaultPolicy = null;
if (StringUtils.isNotBlank(config.getDefaultTopicPerm())) {
String topicPerm = StringUtils.trim(config.getDefaultTopicPerm());
Resource resource = Resource.of(ResourceType.TOPIC, null, ResourcePattern.ANY);
List actions = parseActions(topicPerm);
Decision decision = parseDecision(topicPerm);
PolicyEntry policyEntry = PolicyEntry.of(resource, actions, null, decision);
defaultPolicy = Policy.of(PolicyType.DEFAULT, new ArrayList<>());
defaultPolicy.getEntries().add(policyEntry);
}
if (StringUtils.isNotBlank(config.getDefaultGroupPerm())) {
String groupPerm = StringUtils.trim(config.getDefaultGroupPerm());
Resource resource = Resource.of(ResourceType.GROUP, null, ResourcePattern.ANY);
List actions = parseActions(groupPerm);
Decision decision = parseDecision(groupPerm);
PolicyEntry policyEntry = PolicyEntry.of(resource, actions, null, decision);
if (defaultPolicy == null) {
defaultPolicy = Policy.of(PolicyType.DEFAULT, new ArrayList<>());
}
defaultPolicy.getEntries().add(policyEntry);
}
if (defaultPolicy != null) {
policies.add(defaultPolicy);
}
if (CollectionUtils.isEmpty(policies)) {
return CompletableFuture.completedFuture(null);
}
Acl acl = Acl.of(subject, policies);
return this.authorizationMetadataManager.createAcl(acl);
}
private Decision parseDecision(String str) {
if (StringUtils.isBlank(str)) {
return Decision.DENY;
}
return StringUtils.equals(str, AclConstants.DENY) ? Decision.DENY : Decision.ALLOW;
}
private List parseActions(String str) {
List result = new ArrayList<>();
if (StringUtils.isBlank(str)) {
result.add(Action.ALL);
}
switch (StringUtils.trim(str)) {
case AclConstants.PUB:
result.add(Action.PUB);
break;
case AclConstants.SUB:
result.add(Action.SUB);
break;
case AclConstants.PUB_SUB:
case AclConstants.SUB_PUB:
result.add(Action.PUB);
result.add(Action.SUB);
break;
case AclConstants.DENY:
result.add(Action.ALL);
break;
default:
result.add(Action.ALL);
break;
}
return result;
}
private CompletableFuture isUserExisted(String username) {
return this.authenticationMetadataManager.getUser(username).thenApply(Objects::nonNull);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/v1/AccessResource.java
================================================
/*
* 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.rocketmq.auth.migration.v1;
public interface AccessResource {
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/v1/AclConfig.java
================================================
/*
* 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.rocketmq.auth.migration.v1;
import java.util.List;
public class AclConfig {
private List globalWhiteAddrs;
private List plainAccessConfigs;
public List getGlobalWhiteAddrs() {
return globalWhiteAddrs;
}
public void setGlobalWhiteAddrs(List globalWhiteAddrs) {
this.globalWhiteAddrs = globalWhiteAddrs;
}
public List getPlainAccessConfigs() {
return plainAccessConfigs;
}
public void setPlainAccessConfigs(List plainAccessConfigs) {
this.plainAccessConfigs = plainAccessConfigs;
}
@Override
public String toString() {
return "AclConfig{" +
"globalWhiteAddrs=" + globalWhiteAddrs +
", plainAccessConfigs=" + plainAccessConfigs +
'}';
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/v1/PlainAccessConfig.java
================================================
/*
* 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.rocketmq.auth.migration.v1;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
public class PlainAccessConfig implements Serializable {
private static final long serialVersionUID = -4517357000307227637L;
private String accessKey;
private String secretKey;
private String whiteRemoteAddress;
private boolean admin;
private String defaultTopicPerm;
private String defaultGroupPerm;
private List topicPerms;
private List groupPerms;
public String getAccessKey() {
return accessKey;
}
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getWhiteRemoteAddress() {
return whiteRemoteAddress;
}
public void setWhiteRemoteAddress(String whiteRemoteAddress) {
this.whiteRemoteAddress = whiteRemoteAddress;
}
public boolean isAdmin() {
return admin;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
public String getDefaultTopicPerm() {
return defaultTopicPerm;
}
public void setDefaultTopicPerm(String defaultTopicPerm) {
this.defaultTopicPerm = defaultTopicPerm;
}
public String getDefaultGroupPerm() {
return defaultGroupPerm;
}
public void setDefaultGroupPerm(String defaultGroupPerm) {
this.defaultGroupPerm = defaultGroupPerm;
}
public List getTopicPerms() {
return topicPerms;
}
public void setTopicPerms(List topicPerms) {
this.topicPerms = topicPerms;
}
public List getGroupPerms() {
return groupPerms;
}
public void setGroupPerms(List groupPerms) {
this.groupPerms = groupPerms;
}
@Override
public String toString() {
return "PlainAccessConfig{" +
"accessKey='" + accessKey + '\'' +
", whiteRemoteAddress='" + whiteRemoteAddress + '\'' +
", admin=" + admin +
", defaultTopicPerm='" + defaultTopicPerm + '\'' +
", defaultGroupPerm='" + defaultGroupPerm + '\'' +
", topicPerms=" + topicPerms +
", groupPerms=" + groupPerms +
'}';
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PlainAccessConfig config = (PlainAccessConfig) o;
return admin == config.admin && Objects.equals(accessKey, config.accessKey) && Objects.equals(secretKey, config.secretKey) && Objects.equals(whiteRemoteAddress, config.whiteRemoteAddress) && Objects.equals(defaultTopicPerm, config.defaultTopicPerm) && Objects.equals(defaultGroupPerm, config.defaultGroupPerm) && Objects.equals(topicPerms, config.topicPerms) && Objects.equals(groupPerms, config.groupPerms);
}
@Override public int hashCode() {
return Objects.hash(accessKey, secretKey, whiteRemoteAddress, admin, defaultTopicPerm, defaultGroupPerm, topicPerms, groupPerms);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/v1/PlainAccessData.java
================================================
/*
* 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.rocketmq.auth.migration.v1;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class PlainAccessData implements Serializable {
private static final long serialVersionUID = -7971775135605117152L;
private List globalWhiteRemoteAddresses = new ArrayList<>();
private List accounts = new ArrayList<>();
private List dataVersion = new ArrayList<>();
public List getGlobalWhiteRemoteAddresses() {
return globalWhiteRemoteAddresses;
}
public void setGlobalWhiteRemoteAddresses(List globalWhiteRemoteAddresses) {
this.globalWhiteRemoteAddresses = globalWhiteRemoteAddresses;
}
public List getAccounts() {
return accounts;
}
public void setAccounts(List accounts) {
this.accounts = accounts;
}
public List getDataVersion() {
return dataVersion;
}
public void setDataVersion(List dataVersion) {
this.dataVersion = dataVersion;
}
public static class DataVersion implements Serializable {
private static final long serialVersionUID = 6437361970079056954L;
private long timestamp;
private long counter;
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public long getCounter() {
return counter;
}
public void setCounter(long counter) {
this.counter = counter;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DataVersion that = (DataVersion) o;
return timestamp == that.timestamp && counter == that.counter;
}
@Override
public int hashCode() {
return Objects.hash(timestamp, counter);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PlainAccessData that = (PlainAccessData) o;
return Objects.equals(globalWhiteRemoteAddresses, that.globalWhiteRemoteAddresses) && Objects.equals(accounts, that.accounts) && Objects.equals(dataVersion, that.dataVersion);
}
@Override
public int hashCode() {
return Objects.hash(globalWhiteRemoteAddresses, accounts, dataVersion);
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/v1/PlainAccessResource.java
================================================
/*
* 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.rocketmq.auth.migration.v1;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.rocketmq.common.KeyBuilder;
import org.apache.rocketmq.common.MixAll;
import java.util.HashMap;
import java.util.Map;
public class PlainAccessResource implements AccessResource {
// Identify the user
private String accessKey;
private String secretKey;
private String whiteRemoteAddress;
private boolean admin;
private byte defaultTopicPerm = 1;
private byte defaultGroupPerm = 1;
private Map resourcePermMap;
private int requestCode;
// The content to calculate the content
private byte[] content;
private String signature;
private String secretToken;
private String recognition;
public PlainAccessResource() {
}
public static String getGroupFromRetryTopic(String retryTopic) {
if (retryTopic == null) {
return null;
}
return KeyBuilder.parseGroup(retryTopic);
}
public static String getRetryTopic(String group) {
if (group == null) {
return null;
}
return MixAll.getRetryTopic(group);
}
public void addResourceAndPerm(String resource, byte perm) {
if (resource == null) {
return;
}
if (resourcePermMap == null) {
resourcePermMap = new HashMap<>();
}
resourcePermMap.put(resource, perm);
}
public String getAccessKey() {
return accessKey;
}
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getWhiteRemoteAddress() {
return whiteRemoteAddress;
}
public void setWhiteRemoteAddress(String whiteRemoteAddress) {
this.whiteRemoteAddress = whiteRemoteAddress;
}
public boolean isAdmin() {
return admin;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
public byte getDefaultTopicPerm() {
return defaultTopicPerm;
}
public void setDefaultTopicPerm(byte defaultTopicPerm) {
this.defaultTopicPerm = defaultTopicPerm;
}
public byte getDefaultGroupPerm() {
return defaultGroupPerm;
}
public void setDefaultGroupPerm(byte defaultGroupPerm) {
this.defaultGroupPerm = defaultGroupPerm;
}
public Map getResourcePermMap() {
return resourcePermMap;
}
public String getRecognition() {
return recognition;
}
public void setRecognition(String recognition) {
this.recognition = recognition;
}
public int getRequestCode() {
return requestCode;
}
public void setRequestCode(int requestCode) {
this.requestCode = requestCode;
}
public String getSecretToken() {
return secretToken;
}
public void setSecretToken(String secretToken) {
this.secretToken = secretToken;
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
}
================================================
FILE: auth/src/main/java/org/apache/rocketmq/auth/migration/v1/PlainPermissionManager.java
================================================
/*
* 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.rocketmq.auth.migration.v1;
import org.apache.rocketmq.acl.common.AclException;
import org.apache.rocketmq.acl.common.AclUtils;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.constant.LoggerName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class PlainPermissionManager {
private static final Logger log = LoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME);
private String fileHome = MixAll.ROCKETMQ_HOME_DIR;
private String defaultAclDir;
private String defaultAclFile;
private List fileList = new ArrayList<>();
public PlainPermissionManager() {
this.defaultAclDir = MixAll.dealFilePath(fileHome + File.separator + "conf" + File.separator + "acl");
this.defaultAclFile = MixAll.dealFilePath(fileHome + File.separator + System.getProperty("rocketmq.acl.plain.file", "conf" + File.separator + "plain_acl.yml"));
load();
}
public List getAllAclFiles(String path) {
if (!new File(path).exists()) {
log.info("The default acl dir {} is not exist", path);
return new ArrayList<>();
}
List allAclFileFullPath = new ArrayList<>();
File file = new File(path);
File[] files = file.listFiles();
for (int i = 0; files != null && i < files.length; i++) {
String fileName = files[i].getAbsolutePath();
File f = new File(fileName);
if (fileName.equals(fileHome + MixAll.ACL_CONF_TOOLS_FILE)) {
continue;
} else if (fileName.endsWith(".yml") || fileName.endsWith(".yaml")) {
allAclFileFullPath.add(fileName);
} else if (f.isDirectory()) {
allAclFileFullPath.addAll(getAllAclFiles(fileName));
}
}
return allAclFileFullPath;
}
public void load() {
if (fileHome == null || fileHome.isEmpty()) {
return;
}
assureAclConfigFilesExist();
fileList = getAllAclFiles(defaultAclDir);
if (new File(defaultAclFile).exists() && !fileList.contains(defaultAclFile)) {
fileList.add(defaultAclFile);
}
}
/**
* Currently GlobalWhiteAddress is defined in {@link #defaultAclFile}, so make sure it exists.
*/
private void assureAclConfigFilesExist() {
final Path defaultAclFilePath = Paths.get(this.defaultAclFile);
if (!Files.exists(defaultAclFilePath)) {
try {
Files.createFile(defaultAclFilePath);
} catch (FileAlreadyExistsException e) {
// Maybe created by other threads
} catch (IOException e) {
log.error("Error in creating " + this.defaultAclFile, e);
throw new AclException(e.getMessage());
}
}
}
public AclConfig getAllAclConfig() {
AclConfig aclConfig = new AclConfig();
List configs = new ArrayList<>();
List whiteAddrs = new ArrayList<>();
Set accessKeySets = new HashSet<>();
for (String path : fileList) {
PlainAccessData plainAclConfData = AclUtils.getYamlDataObject(path, PlainAccessData.class);
if (plainAclConfData == null) {
continue;
}
List globalWhiteAddrs = plainAclConfData.getGlobalWhiteRemoteAddresses();
if (globalWhiteAddrs != null && !globalWhiteAddrs.isEmpty()) {
whiteAddrs.addAll(globalWhiteAddrs);
}
List plainAccessConfigs = plainAclConfData.getAccounts();
if (plainAccessConfigs != null && !plainAccessConfigs.isEmpty()) {
for (PlainAccessConfig accessConfig : plainAccessConfigs) {
if (!accessKeySets.contains(accessConfig.getAccessKey())) {
accessKeySets.add(accessConfig.getAccessKey());
PlainAccessConfig plainAccessConfig = new PlainAccessConfig();
plainAccessConfig.setGroupPerms(accessConfig.getGroupPerms());
plainAccessConfig.setDefaultTopicPerm(accessConfig.getDefaultTopicPerm());
plainAccessConfig.setDefaultGroupPerm(accessConfig.getDefaultGroupPerm());
plainAccessConfig.setAccessKey(accessConfig.getAccessKey());
plainAccessConfig.setSecretKey(accessConfig.getSecretKey());
plainAccessConfig.setAdmin(accessConfig.isAdmin());
plainAccessConfig.setTopicPerms(accessConfig.getTopicPerms());
plainAccessConfig.setWhiteRemoteAddress(accessConfig.getWhiteRemoteAddress());
configs.add(plainAccessConfig);
}
}
}
}
aclConfig.setPlainAccessConfigs(configs);
aclConfig.setGlobalWhiteAddrs(whiteAddrs);
return aclConfig;
}
}
================================================
FILE: auth/src/test/java/org/apache/rocketmq/auth/authentication/AuthenticationEvaluatorTest.java
================================================
/*
* 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.rocketmq.auth.authentication;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.auth.authentication.context.DefaultAuthenticationContext;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManager;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.auth.helper.AuthTestHelper;
import org.apache.rocketmq.common.MixAll;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class AuthenticationEvaluatorTest {
private AuthConfig authConfig;
private AuthenticationEvaluator evaluator;
private AuthenticationMetadataManager authenticationMetadataManager;
@Before
public void setUp() throws Exception {
if (MixAll.isMac()) {
return;
}
this.authConfig = AuthTestHelper.createDefaultConfig();
this.evaluator = new AuthenticationEvaluator(authConfig);
this.authenticationMetadataManager = AuthenticationFactory.getMetadataManager(authConfig);
this.clearAllUsers();
}
@After
public void tearDown() throws Exception {
if (MixAll.isMac()) {
return;
}
this.clearAllUsers();
this.authenticationMetadataManager.shutdown();
}
@Test
public void evaluate1() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user);
DefaultAuthenticationContext context = new DefaultAuthenticationContext();
context.setRpcCode("11");
context.setUsername("test");
context.setContent("test".getBytes(StandardCharsets.UTF_8));
context.setSignature("DJRRXBXlCVuKh6ULoN87847QX+Y=");
this.evaluator.evaluate(context);
}
@Test
public void evaluate2() {
if (MixAll.isMac()) {
return;
}
DefaultAuthenticationContext context = new DefaultAuthenticationContext();
context.setRpcCode("11");
context.setUsername("test");
context.setContent("test".getBytes(StandardCharsets.UTF_8));
context.setSignature("DJRRXBXlCVuKh6ULoN87847QX+Y=");
Assert.assertThrows(AuthenticationException.class, () -> this.evaluator.evaluate(context));
}
@Test
public void evaluate3() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user);
DefaultAuthenticationContext context = new DefaultAuthenticationContext();
context.setRpcCode("11");
context.setUsername("test");
context.setContent("test".getBytes(StandardCharsets.UTF_8));
context.setSignature("test");
Assert.assertThrows(AuthenticationException.class, () -> this.evaluator.evaluate(context));
}
@Test
public void evaluate4() {
if (MixAll.isMac()) {
return;
}
this.authConfig.setAuthenticationWhitelist("11");
this.evaluator = new AuthenticationEvaluator(authConfig);
DefaultAuthenticationContext context = new DefaultAuthenticationContext();
context.setRpcCode("11");
context.setUsername("test");
context.setContent("test".getBytes(StandardCharsets.UTF_8));
context.setSignature("test");
this.evaluator.evaluate(context);
}
@Test
public void evaluate5() {
if (MixAll.isMac()) {
return;
}
this.authConfig.setAuthenticationEnabled(false);
this.evaluator = new AuthenticationEvaluator(authConfig);
DefaultAuthenticationContext context = new DefaultAuthenticationContext();
context.setRpcCode("11");
context.setUsername("test");
context.setContent("test".getBytes(StandardCharsets.UTF_8));
context.setSignature("test");
this.evaluator.evaluate(context);
}
private void clearAllUsers() {
if (MixAll.isMac()) {
return;
}
List users = this.authenticationMetadataManager.listUser(null).join();
if (CollectionUtils.isEmpty(users)) {
return;
}
users.forEach(user -> this.authenticationMetadataManager.deleteUser(user.getUsername()).join());
}
}
================================================
FILE: auth/src/test/java/org/apache/rocketmq/auth/authentication/builder/DefaultAuthenticationContextBuilderTest.java
================================================
/*
* 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.rocketmq.auth.authentication.builder;
import apache.rocketmq.v2.Message;
import apache.rocketmq.v2.Resource;
import apache.rocketmq.v2.SendMessageRequest;
import com.google.protobuf.ByteString;
import io.grpc.Metadata;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import java.nio.charset.StandardCharsets;
import org.apache.rocketmq.auth.authentication.context.DefaultAuthenticationContext;
import org.apache.rocketmq.common.constant.GrpcConstants;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.header.SendMessageRequestHeader;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class)
public class DefaultAuthenticationContextBuilderTest {
private DefaultAuthenticationContextBuilder builder;
@Mock
private ChannelHandlerContext channelHandlerContext;
@Mock
private Channel channel;
@Before
public void setUp() throws Exception {
builder = new DefaultAuthenticationContextBuilder();
}
@Test
public void build1() {
Resource topic = Resource.newBuilder().setName("topic-test").build();
{
SendMessageRequest request = SendMessageRequest.newBuilder()
.addMessages(Message.newBuilder().setTopic(topic)
.setBody(ByteString.copyFromUtf8("message-body"))
.build())
.build();
Metadata metadata = new Metadata();
metadata.put(GrpcConstants.AUTHORIZATION, "MQv2-HMAC-SHA1 Credential=abc, SignedHeaders=x-mq-date-time, Signature=D18A9CBCDDBA9041D6693268FEF15A989E64430B");
metadata.put(GrpcConstants.DATE_TIME, "20231227T194619Z");
DefaultAuthenticationContext context = builder.build(metadata, request);
Assert.assertNotNull(context);
Assert.assertEquals("abc", context.getUsername());
Assert.assertEquals("0YqcvN26kEHWaTJo/vFamJ5kQws=", context.getSignature());
Assert.assertEquals("20231227T194619Z", new String(context.getContent(), StandardCharsets.UTF_8));
}
}
@Test
public void build2() {
when(channel.id()).thenReturn(mockChannelId("channel-id"));
when(channelHandlerContext.channel()).thenReturn(channel);
SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
requestHeader.setTopic("topic-test");
requestHeader.setQueueId(0);
requestHeader.setBornTimestamp(117036786441330L);
requestHeader.setBname("brokerName-1");
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, requestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "abc");
request.addExtField("Signature", "ZG26exJ5u9q1fwZlO4DCmz2Rs88=");
request.makeCustomHeaderToNet();
DefaultAuthenticationContext context = builder.build(channelHandlerContext, request);
Assert.assertNotNull(context);
Assert.assertEquals("abc", context.getUsername());
Assert.assertEquals("ZG26exJ5u9q1fwZlO4DCmz2Rs88=", context.getSignature());
Assert.assertEquals("abcbrokerName-11170367864413300topic-test", new String(context.getContent(), StandardCharsets.UTF_8));
}
private ChannelId mockChannelId(String channelId) {
return new ChannelId() {
@Override
public String asShortText() {
return channelId;
}
@Override
public String asLongText() {
return channelId;
}
@Override
public int compareTo(ChannelId o) {
return 0;
}
};
}
}
================================================
FILE: auth/src/test/java/org/apache/rocketmq/auth/authentication/manager/AuthenticationMetadataManagerTest.java
================================================
/*
* 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.rocketmq.auth.authentication.manager;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.auth.authentication.enums.UserType;
import org.apache.rocketmq.auth.authentication.exception.AuthenticationException;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.auth.helper.AuthTestHelper;
import org.apache.rocketmq.common.MixAll;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class AuthenticationMetadataManagerTest {
private AuthConfig authConfig;
private AuthenticationMetadataManager authenticationMetadataManager;
@Before
public void setUp() throws Exception {
if (MixAll.isMac()) {
return;
}
this.authConfig = AuthTestHelper.createDefaultConfig();
this.authenticationMetadataManager = AuthenticationFactory.getMetadataManager(this.authConfig);
this.clearAllUsers();
}
@After
public void tearDown() throws Exception {
if (MixAll.isMac()) {
return;
}
this.clearAllUsers();
this.authenticationMetadataManager.shutdown();
}
@Test
public void createUser() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNotNull(user);
Assert.assertEquals(user.getUsername(), "test");
Assert.assertEquals(user.getPassword(), "test");
Assert.assertEquals(user.getUserType(), UserType.NORMAL);
user = User.of("super", "super", UserType.SUPER);
this.authenticationMetadataManager.createUser(user).join();
user = this.authenticationMetadataManager.getUser("super").join();
Assert.assertNotNull(user);
Assert.assertEquals(user.getUsername(), "super");
Assert.assertEquals(user.getPassword(), "super");
Assert.assertEquals(user.getUserType(), UserType.SUPER);
Assert.assertThrows(AuthenticationException.class, () -> {
try {
User user2 = User.of("test", "test");
this.authenticationMetadataManager.createUser(user2).join();
} catch (Exception e) {
AuthTestHelper.handleException(e);
}
});
}
@Test
public void updateUser() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNotNull(user);
Assert.assertEquals(user.getUsername(), "test");
Assert.assertEquals(user.getPassword(), "test");
Assert.assertEquals(user.getUserType(), UserType.NORMAL);
user.setPassword("123");
this.authenticationMetadataManager.updateUser(user).join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNotNull(user);
Assert.assertEquals(user.getUsername(), "test");
Assert.assertEquals(user.getPassword(), "123");
Assert.assertEquals(user.getUserType(), UserType.NORMAL);
user.setUserType(UserType.SUPER);
this.authenticationMetadataManager.updateUser(user).join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNotNull(user);
Assert.assertEquals(user.getUsername(), "test");
Assert.assertEquals(user.getPassword(), "123");
Assert.assertEquals(user.getUserType(), UserType.SUPER);
Assert.assertThrows(AuthenticationException.class, () -> {
try {
User user2 = User.of("no_user", "no_user");
this.authenticationMetadataManager.updateUser(user2).join();
} catch (Exception e) {
AuthTestHelper.handleException(e);
}
});
}
@Test
public void deleteUser() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNotNull(user);
this.authenticationMetadataManager.deleteUser("test").join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNull(user);
this.authenticationMetadataManager.deleteUser("no_user").join();
}
@Test
public void getUser() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
user = this.authenticationMetadataManager.getUser("test").join();
Assert.assertNotNull(user);
Assert.assertEquals(user.getUsername(), "test");
Assert.assertEquals(user.getPassword(), "test");
Assert.assertEquals(user.getUserType(), UserType.NORMAL);
user = this.authenticationMetadataManager.getUser("no_user").join();
Assert.assertNull(user);
}
@Test
public void listUser() {
if (MixAll.isMac()) {
return;
}
List users = this.authenticationMetadataManager.listUser(null).join();
Assert.assertTrue(CollectionUtils.isEmpty(users));
User user = User.of("test-1", "test-1");
this.authenticationMetadataManager.createUser(user).join();
users = this.authenticationMetadataManager.listUser(null).join();
Assert.assertEquals(users.size(), 1);
user = User.of("test-2", "test-2");
this.authenticationMetadataManager.createUser(user).join();
users = this.authenticationMetadataManager.listUser("test").join();
Assert.assertEquals(users.size(), 2);
}
private void clearAllUsers() {
List users = this.authenticationMetadataManager.listUser(null).join();
if (CollectionUtils.isEmpty(users)) {
return;
}
users.forEach(user -> this.authenticationMetadataManager.deleteUser(user.getUsername()).join());
}
}
================================================
FILE: auth/src/test/java/org/apache/rocketmq/auth/authentication/provider/LocalAuthenticationMetadataProviderTest.java
================================================
/*
* 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.rocketmq.auth.authentication.provider;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.auth.helper.AuthTestHelper;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class LocalAuthenticationMetadataProviderTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void testShutdownReleasesCacheExecutor() throws Exception {
AuthConfig authConfig = AuthTestHelper.createDefaultConfig();
authConfig.setAuthConfigPath(tempFolder.newFolder("auth-test").getAbsolutePath());
LocalAuthenticationMetadataProvider provider = new LocalAuthenticationMetadataProvider();
// Initialize provider to create the internal cache refresh executor
provider.initialize(authConfig, () -> null);
// After initialization, the executor should exist and not be shutdown
Assert.assertNotNull(provider.cacheRefreshExecutor);
Assert.assertFalse(provider.cacheRefreshExecutor.isShutdown());
// Shutdown provider should also shutdown its executor to release resources
provider.shutdown();
// Verify that the cache refresh executor has been shutdown
Assert.assertTrue(provider.cacheRefreshExecutor.isShutdown());
}
}
================================================
FILE: auth/src/test/java/org/apache/rocketmq/auth/authorization/AuthorizationEvaluatorTest.java
================================================
/*
* 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.rocketmq.auth.authorization;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.auth.authentication.factory.AuthenticationFactory;
import org.apache.rocketmq.auth.authentication.manager.AuthenticationMetadataManager;
import org.apache.rocketmq.auth.authentication.model.Subject;
import org.apache.rocketmq.auth.authentication.model.User;
import org.apache.rocketmq.auth.authorization.context.AuthorizationContext;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.authorization.enums.Decision;
import org.apache.rocketmq.auth.authorization.enums.PolicyType;
import org.apache.rocketmq.auth.authorization.exception.AuthorizationException;
import org.apache.rocketmq.auth.authorization.factory.AuthorizationFactory;
import org.apache.rocketmq.auth.authorization.manager.AuthorizationMetadataManager;
import org.apache.rocketmq.auth.authorization.model.Acl;
import org.apache.rocketmq.auth.authorization.model.Resource;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.auth.helper.AuthTestHelper;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.action.Action;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class AuthorizationEvaluatorTest {
private AuthConfig authConfig;
private AuthorizationEvaluator evaluator;
private AuthenticationMetadataManager authenticationMetadataManager;
private AuthorizationMetadataManager authorizationMetadataManager;
@Before
public void setUp() throws Exception {
if (MixAll.isMac()) {
return;
}
this.authConfig = AuthTestHelper.createDefaultConfig();
this.evaluator = new AuthorizationEvaluator(authConfig);
this.authenticationMetadataManager = AuthenticationFactory.getMetadataManager(authConfig);
this.authorizationMetadataManager = AuthorizationFactory.getMetadataManager(authConfig);
this.clearAllAcls();
this.clearAllUsers();
}
@After
public void tearDown() throws Exception {
if (MixAll.isMac()) {
return;
}
this.clearAllAcls();
this.clearAllUsers();
this.authenticationMetadataManager.shutdown();
}
@Test
public void evaluate1() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
Acl acl = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl).join();
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
// acl sourceIp is null
acl = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub", null, Decision.ALLOW);
this.authorizationMetadataManager.updateAcl(acl).join();
subject = Subject.of("User:test");
resource = Resource.ofTopic("test");
action = Action.PUB;
sourceIp = "192.168.0.1";
context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
}
@Test
public void evaluate2() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
Acl acl = AuthTestHelper.buildAcl("User:test", "Topic:test*,Group:test*", "Sub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl).join();
List contexts = new ArrayList<>();
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.SUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context1 = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context1.setRpcCode("11");
contexts.add(context1);
subject = Subject.of("User:test");
resource = Resource.ofGroup("test");
action = Action.SUB;
sourceIp = "192.168.0.1";
DefaultAuthorizationContext context2 = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context2.setRpcCode("11");
contexts.add(context2);
this.evaluator.evaluate(contexts);
}
@Test
public void evaluate4() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
Acl acl = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl).join();
// user not exist
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:abc");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
// resource not match
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("abc");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
// action not match
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.SUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
// sourceIp not match
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "10.10.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
// decision is deny
acl = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub", "192.168.0.0/24", Decision.DENY);
this.authorizationMetadataManager.updateAcl(acl).join();
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
}
@Test
public void evaluate5() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
Acl acl = AuthTestHelper.buildAcl("User:test", "*", "Pub,Sub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl).join();
acl = AuthTestHelper.buildAcl("User:test", "Topic:*", "Pub,Sub", "192.168.0.0/24", Decision.DENY);
this.authorizationMetadataManager.updateAcl(acl).join();
acl = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub,Sub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.updateAcl(acl).join();
acl = AuthTestHelper.buildAcl("User:test", "Topic:test-1", "Pub,Sub", "192.168.0.0/24", Decision.DENY);
this.authorizationMetadataManager.updateAcl(acl).join();
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test-1");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
{
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test-2");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
}
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("abc");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
{
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofGroup("test-2");
Action action = Action.SUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
}
}
@Test
public void evaluate6() {
if (MixAll.isMac()) {
return;
}
this.authConfig.setAuthorizationWhitelist("10");
this.evaluator = new AuthorizationEvaluator(this.authConfig);
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
}
@Test
public void evaluate7() {
if (MixAll.isMac()) {
return;
}
this.authConfig.setAuthorizationEnabled(false);
this.evaluator = new AuthorizationEvaluator(this.authConfig);
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
}
@Test
public void evaluate8() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
Acl acl = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub", "192.168.0.0/24", Decision.DENY);
this.authorizationMetadataManager.createAcl(acl).join();
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("abc");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
acl = AuthTestHelper.buildAcl("User:test", PolicyType.DEFAULT, "Topic:*", "Pub", null, Decision.ALLOW);
this.authorizationMetadataManager.updateAcl(acl).join();
{
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("abc");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
}
}
@Test
public void evaluate9() {
if (MixAll.isMac()) {
return;
}
User user = User.of("test", "test");
this.authenticationMetadataManager.createUser(user).join();
Acl acl0 = AuthTestHelper.buildAcl("User:test", "*", "Pub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl0).join();
Acl acl1 = AuthTestHelper.buildAcl("User:test", "Topic:*", "Pub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl1).join();
Acl acl2 = AuthTestHelper.buildAcl("User:test", "Topic:test*", "Pub", "192.168.0.0/24", Decision.ALLOW);
this.authorizationMetadataManager.createAcl(acl2).join();
Acl acl3 = AuthTestHelper.buildAcl("User:test", "Topic:test_*", "Pub", "192.168.0.0/24", Decision.DENY);
this.authorizationMetadataManager.createAcl(acl3).join();
Acl acl4 = AuthTestHelper.buildAcl("User:test", "Topic:test_001", "Pub", "192.168.0.0/24", Decision.DENY);
this.authorizationMetadataManager.createAcl(acl4).join();
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test_001");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
Assert.assertThrows(AuthorizationException.class, () -> {
Subject subject = Subject.of("User:test");
Resource resource = Resource.ofTopic("test_002");
Action action = Action.PUB;
String sourceIp = "192.168.0.1";
DefaultAuthorizationContext context = DefaultAuthorizationContext.of(subject, resource, action, sourceIp);
context.setRpcCode("10");
this.evaluator.evaluate(Collections.singletonList(context));
});
}
private void clearAllUsers() {
List users = this.authenticationMetadataManager.listUser(null).join();
if (CollectionUtils.isEmpty(users)) {
return;
}
users.forEach(user -> this.authenticationMetadataManager.deleteUser(user.getUsername()).join());
}
private void clearAllAcls() {
List acls = this.authorizationMetadataManager.listAcl(null, null).join();
if (CollectionUtils.isEmpty(acls)) {
return;
}
acls.forEach(acl -> this.authorizationMetadataManager.deleteAcl(acl.getSubject(), null, null).join());
}
}
================================================
FILE: auth/src/test/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilderTest.java
================================================
/*
* 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.rocketmq.auth.authorization.builder;
import apache.rocketmq.v2.AckMessageRequest;
import apache.rocketmq.v2.ChangeInvisibleDurationRequest;
import apache.rocketmq.v2.ClientType;
import apache.rocketmq.v2.EndTransactionRequest;
import apache.rocketmq.v2.ForwardMessageToDeadLetterQueueRequest;
import apache.rocketmq.v2.HeartbeatRequest;
import apache.rocketmq.v2.Message;
import apache.rocketmq.v2.MessageQueue;
import apache.rocketmq.v2.NotifyClientTerminationRequest;
import apache.rocketmq.v2.Publishing;
import apache.rocketmq.v2.QueryAssignmentRequest;
import apache.rocketmq.v2.QueryRouteRequest;
import apache.rocketmq.v2.RecallMessageRequest;
import apache.rocketmq.v2.ReceiveMessageRequest;
import apache.rocketmq.v2.Resource;
import apache.rocketmq.v2.SendMessageRequest;
import apache.rocketmq.v2.Settings;
import apache.rocketmq.v2.Subscription;
import apache.rocketmq.v2.SubscriptionEntry;
import apache.rocketmq.v2.TelemetryCommand;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Sets;
import com.google.protobuf.GeneratedMessageV3;
import io.grpc.Metadata;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import java.util.Arrays;
import java.util.List;
import org.apache.rocketmq.auth.authorization.context.DefaultAuthorizationContext;
import org.apache.rocketmq.auth.config.AuthConfig;
import org.apache.rocketmq.common.TopicFilterType;
import org.apache.rocketmq.common.action.Action;
import org.apache.rocketmq.common.constant.GrpcConstants;
import org.apache.rocketmq.common.resource.ResourceType;
import org.apache.rocketmq.remoting.netty.AttributeKeys;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RequestCode;
import org.apache.rocketmq.remoting.protocol.RequestHeaderRegistry;
import org.apache.rocketmq.remoting.protocol.body.LockBatchRequestBody;
import org.apache.rocketmq.remoting.protocol.body.UnlockBatchRequestBody;
import org.apache.rocketmq.remoting.protocol.header.ConsumerSendMsgBackRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.CreateTopicRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.CreateUserRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.EndTransactionRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.GetConsumerListByGroupRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.HeartbeatRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.PullMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.QueryConsumerOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.QueryMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.RecallMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.SendMessageRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.SendMessageRequestHeaderV2;
import org.apache.rocketmq.remoting.protocol.header.UnregisterClientRequestHeader;
import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetRequestHeader;
import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumerData;
import org.apache.rocketmq.remoting.protocol.heartbeat.HeartbeatData;
import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class)
public class DefaultAuthorizationContextBuilderTest {
private AuthorizationContextBuilder builder;
@Mock
private ChannelHandlerContext channelHandlerContext;
@Mock
private Channel channel;
@Before
public void setUp() throws Exception {
AuthConfig authConfig = new AuthConfig();
authConfig.setClusterName("DefaultCluster");
builder = new DefaultAuthorizationContextBuilder(authConfig);
RequestHeaderRegistry.getInstance().initialize();
}
@Test
public void buildGrpc() {
Metadata metadata = new Metadata();
metadata.put(GrpcConstants.AUTHORIZATION_AK, "rocketmq");
metadata.put(GrpcConstants.REMOTE_ADDRESS, "192.168.0.1");
metadata.put(GrpcConstants.CHANNEL_ID, "channel-id");
GeneratedMessageV3 request = SendMessageRequest.newBuilder()
.addMessages(Message.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.build())
.build();
List result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
Assert.assertEquals(result.get(0).getSourceIp(), "192.168.0.1");
Assert.assertEquals(result.get(0).getChannelId(), "channel-id");
Assert.assertEquals(result.get(0).getRpcCode(), SendMessageRequest.getDescriptor().getFullName());
request = RecallMessageRequest.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.setRecallHandle("handle")
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
Assert.assertEquals(result.get(0).getSourceIp(), "192.168.0.1");
Assert.assertEquals(result.get(0).getChannelId(), "channel-id");
Assert.assertEquals(result.get(0).getRpcCode(), RecallMessageRequest.getDescriptor().getFullName());
request = EndTransactionRequest.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
request = HeartbeatRequest.newBuilder()
.setClientType(ClientType.PUSH_CONSUMER)
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
request = ReceiveMessageRequest.newBuilder()
.setMessageQueue(MessageQueue.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.build())
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals(getContext(result, ResourceType.GROUP).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.GROUP).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
request = AckMessageRequest.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals(getContext(result, ResourceType.GROUP).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.GROUP).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
request = ForwardMessageToDeadLetterQueueRequest.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals(getContext(result, ResourceType.GROUP).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.GROUP).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
request = NotifyClientTerminationRequest.newBuilder()
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
request = ChangeInvisibleDurationRequest.newBuilder()
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
request = QueryRouteRequest.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(result.get(0).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(result.get(0).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB, Action.SUB)));
request = QueryAssignmentRequest.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.setGroup(Resource.newBuilder().setName("group").build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals(getContext(result, ResourceType.GROUP).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.GROUP).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
request = TelemetryCommand.newBuilder()
.setSettings(Settings.newBuilder()
.setPublishing(Publishing.newBuilder()
.addTopics(Resource.newBuilder().setName("topic").build())
.build())
.build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.PUB)));
request = TelemetryCommand.newBuilder()
.setSettings(Settings.newBuilder()
.setSubscription(Subscription.newBuilder()
.setGroup(Resource.newBuilder().setName("group").build())
.addSubscriptions(SubscriptionEntry.newBuilder()
.setTopic(Resource.newBuilder().setName("topic").build())
.build())
.build())
.build())
.build();
result = builder.build(metadata, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals(getContext(result, ResourceType.GROUP).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.GROUP).getResource().getResourceKey(), "Group:group");
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey(), "User:rocketmq");
Assert.assertEquals(getContext(result, ResourceType.TOPIC).getResource().getResourceKey(), "Topic:topic");
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
}
@Test
public void buildRemoting() {
when(channel.id()).thenReturn(mockChannelId("channel-id"));
when(channel.hasAttr(eq(AttributeKeys.PROXY_PROTOCOL_ADDR))).thenReturn(true);
when(channel.attr(eq(AttributeKeys.PROXY_PROTOCOL_ADDR))).thenReturn(mockAttribute("192.168.0.1"));
when(channel.hasAttr(eq(AttributeKeys.PROXY_PROTOCOL_PORT))).thenReturn(true);
when(channel.attr(eq(AttributeKeys.PROXY_PROTOCOL_PORT))).thenReturn(mockAttribute("1234"));
when(channelHandlerContext.channel()).thenReturn(channel);
SendMessageRequestHeader sendMessageRequestHeader = new SendMessageRequestHeader();
sendMessageRequestHeader.setTopic("topic");
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, sendMessageRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
List result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
Assert.assertEquals("192.168.0.1", result.get(0).getSourceIp());
Assert.assertEquals("channel-id", result.get(0).getChannelId());
Assert.assertEquals(RequestCode.SEND_MESSAGE + "", result.get(0).getRpcCode());
sendMessageRequestHeader = new SendMessageRequestHeader();
sendMessageRequestHeader.setTopic("%RETRY%group");
request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, sendMessageRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
SendMessageRequestHeaderV2 sendMessageRequestHeaderV2 = new SendMessageRequestHeaderV2();
sendMessageRequestHeaderV2.setTopic("topic");
request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE_V2, sendMessageRequestHeaderV2);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
sendMessageRequestHeaderV2 = new SendMessageRequestHeaderV2();
sendMessageRequestHeaderV2.setTopic("%RETRY%group");
request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE_V2, sendMessageRequestHeaderV2);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
RecallMessageRequestHeader recallMessageRequestHeader = new RecallMessageRequestHeader();
recallMessageRequestHeader.setTopic("topic");
recallMessageRequestHeader.setRecallHandle("handle");
request = RemotingCommand.createRequestCommand(RequestCode.RECALL_MESSAGE, recallMessageRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
Assert.assertEquals("192.168.0.1", result.get(0).getSourceIp());
Assert.assertEquals("channel-id", result.get(0).getChannelId());
Assert.assertEquals(RequestCode.RECALL_MESSAGE + "", result.get(0).getRpcCode());
EndTransactionRequestHeader endTransactionRequestHeader = new EndTransactionRequestHeader();
endTransactionRequestHeader.setTopic("topic");
request = RemotingCommand.createRequestCommand(RequestCode.END_TRANSACTION, endTransactionRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.PUB)));
endTransactionRequestHeader = new EndTransactionRequestHeader();
request = RemotingCommand.createRequestCommand(RequestCode.END_TRANSACTION, endTransactionRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(0, result.size());
ConsumerSendMsgBackRequestHeader consumerSendMsgBackRequestHeader = new ConsumerSendMsgBackRequestHeader();
consumerSendMsgBackRequestHeader.setGroup("group");
request = RemotingCommand.createRequestCommand(RequestCode.CONSUMER_SEND_MSG_BACK, consumerSendMsgBackRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
PullMessageRequestHeader pullMessageRequestHeader = new PullMessageRequestHeader();
pullMessageRequestHeader.setTopic("topic");
pullMessageRequestHeader.setConsumerGroup("group");
request = RemotingCommand.createRequestCommand(RequestCode.PULL_MESSAGE, pullMessageRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.GROUP).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", getContext(result, ResourceType.GROUP).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", getContext(result, ResourceType.TOPIC).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
QueryMessageRequestHeader queryMessageRequestHeader = new QueryMessageRequestHeader();
queryMessageRequestHeader.setTopic("topic");
request = RemotingCommand.createRequestCommand(RequestCode.QUERY_MESSAGE, queryMessageRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB, Action.GET)));
HeartbeatRequestHeader heartbeatRequestHeader = new HeartbeatRequestHeader();
request = RemotingCommand.createRequestCommand(RequestCode.HEART_BEAT, heartbeatRequestHeader);
HeartbeatData heartbeatData = new HeartbeatData();
ConsumerData consumerData = new ConsumerData();
consumerData.setGroupName("group");
SubscriptionData subscriptionData = new SubscriptionData();
subscriptionData.setTopic("topic");
consumerData.setSubscriptionDataSet(Sets.newHashSet(subscriptionData));
heartbeatData.setConsumerDataSet(Sets.newHashSet(consumerData));
request.setBody(JSON.toJSONBytes(heartbeatData));
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.GROUP).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", getContext(result, ResourceType.GROUP).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", getContext(result, ResourceType.TOPIC).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
UnregisterClientRequestHeader unregisterClientRequestHeader = new UnregisterClientRequestHeader();
unregisterClientRequestHeader.setConsumerGroup("group");
request = RemotingCommand.createRequestCommand(RequestCode.UNREGISTER_CLIENT, unregisterClientRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB)));
GetConsumerListByGroupRequestHeader getConsumerListByGroupRequestHeader = new GetConsumerListByGroupRequestHeader();
getConsumerListByGroupRequestHeader.setConsumerGroup("group");
request = RemotingCommand.createRequestCommand(RequestCode.GET_CONSUMER_LIST_BY_GROUP, getConsumerListByGroupRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.SUB, Action.GET)));
QueryConsumerOffsetRequestHeader queryConsumerOffsetRequestHeader = new QueryConsumerOffsetRequestHeader();
queryConsumerOffsetRequestHeader.setTopic("topic");
queryConsumerOffsetRequestHeader.setConsumerGroup("group");
request = RemotingCommand.createRequestCommand(RequestCode.QUERY_CONSUMER_OFFSET, queryConsumerOffsetRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.GROUP).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", getContext(result, ResourceType.GROUP).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", getContext(result, ResourceType.TOPIC).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
UpdateConsumerOffsetRequestHeader updateConsumerOffsetRequestHeader = new UpdateConsumerOffsetRequestHeader();
updateConsumerOffsetRequestHeader.setTopic("topic");
updateConsumerOffsetRequestHeader.setConsumerGroup("group");
request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_CONSUMER_OFFSET, updateConsumerOffsetRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.GROUP).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", getContext(result, ResourceType.GROUP).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB, Action.UPDATE)));
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", getContext(result, ResourceType.TOPIC).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB, Action.UPDATE)));
CreateTopicRequestHeader createTopicRequestHeader = new CreateTopicRequestHeader();
createTopicRequestHeader.setTopic("topic");
createTopicRequestHeader.setTopicFilterType(TopicFilterType.SINGLE_TAG.name());
request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_AND_CREATE_TOPIC, createTopicRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.CREATE)));
CreateUserRequestHeader createUserRequestHeader = new CreateUserRequestHeader();
createUserRequestHeader.setUsername("abc");
request = RemotingCommand.createRequestCommand(RequestCode.AUTH_CREATE_USER, createUserRequestHeader);
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(1, result.size());
Assert.assertEquals("User:rocketmq", result.get(0).getSubject().getSubjectKey());
Assert.assertEquals("Cluster:DefaultCluster", result.get(0).getResource().getResourceKey());
Assert.assertTrue(result.get(0).getActions().containsAll(Arrays.asList(Action.UPDATE)));
LockBatchRequestBody lockBatchRequestBody = new LockBatchRequestBody();
lockBatchRequestBody.setConsumerGroup("group");
java.util.Set lockMqSet = new java.util.HashSet<>();
lockMqSet.add(new org.apache.rocketmq.common.message.MessageQueue("topic", "broker-a", 0));
// retry topic, should be skipped
lockMqSet.add(new org.apache.rocketmq.common.message.MessageQueue("%RETRY%group", "broker-a", 1));
lockBatchRequestBody.setMqSet(lockMqSet);
request = RemotingCommand.createRequestCommand(RequestCode.LOCK_BATCH_MQ, null);
request.setBody(JSON.toJSONBytes(lockBatchRequestBody));
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.GROUP).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", getContext(result, ResourceType.GROUP).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", getContext(result, ResourceType.TOPIC).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("192.168.0.1", getContext(result, ResourceType.TOPIC).getSourceIp());
Assert.assertEquals("channel-id", getContext(result, ResourceType.TOPIC).getChannelId());
Assert.assertEquals(String.valueOf(RequestCode.LOCK_BATCH_MQ), getContext(result, ResourceType.TOPIC).getRpcCode());
UnlockBatchRequestBody unlockBatchRequestBody = new UnlockBatchRequestBody();
unlockBatchRequestBody.setConsumerGroup("group");
java.util.Set unlockMqSet = new java.util.HashSet<>();
unlockMqSet.add(new org.apache.rocketmq.common.message.MessageQueue("topic", "broker-a", 0));
// retry topic, should be skipped
unlockMqSet.add(new org.apache.rocketmq.common.message.MessageQueue("%RETRY%group", "broker-a", 1));
unlockBatchRequestBody.setMqSet(unlockMqSet);
request = RemotingCommand.createRequestCommand(RequestCode.UNLOCK_BATCH_MQ, null);
request.setBody(JSON.toJSONBytes(unlockBatchRequestBody));
request.setVersion(441);
request.addExtField("AccessKey", "rocketmq");
request.makeCustomHeaderToNet();
result = builder.build(channelHandlerContext, request);
Assert.assertEquals(2, result.size());
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.GROUP).getSubject().getSubjectKey());
Assert.assertEquals("Group:group", getContext(result, ResourceType.GROUP).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.GROUP).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("User:rocketmq", getContext(result, ResourceType.TOPIC).getSubject().getSubjectKey());
Assert.assertEquals("Topic:topic", getContext(result, ResourceType.TOPIC).getResource().getResourceKey());
Assert.assertTrue(getContext(result, ResourceType.TOPIC).getActions().containsAll(Arrays.asList(Action.SUB)));
Assert.assertEquals("192.168.0.1", getContext(result, ResourceType.TOPIC).getSourceIp());
Assert.assertEquals("channel-id", getContext(result, ResourceType.TOPIC).getChannelId());
Assert.assertEquals(String.valueOf(RequestCode.UNLOCK_BATCH_MQ), getContext(result, ResourceType.TOPIC).getRpcCode());
}
private DefaultAuthorizationContext getContext(List contexts,
ResourceType resourceType) {
return contexts.stream().filter(context -> context.getResource().getResourceType() == resourceType)
.findFirst().orElse(null);
}
private ChannelId mockChannelId(String channelId) {
return new ChannelId() {
@Override
public String asShortText() {
return channelId;
}
@Override
public String asLongText() {
return channelId;
}
@Override
public int compareTo(ChannelId o) {
return 0;
}
};
}
private Attribute