Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ public CompletableFuture<Void> grantPermissionAsync(TopicName topicName, Set<Aut
}
throw new IllegalStateException("policies are in readonly mode");
}
String topicUri = topicName.toString();
// Enhancement: only grant permission on based topic
String topicUri = topicName.getPartitionedTopicName();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might have been a misunderstanding of my review. The optimization is simply that we do not recursively add topic names when granting permission in the PersistentTopicsBase#internalGrantPermissionsOnTopic method. I think it is valid to grant a role permission to produce to a single partition of a partitioned topic, so we shouldn't make this update here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! All partitions should have the same permissions, is that right?

return pulsarResources.getNamespaceResources()
.setPoliciesAsync(topicName.getNamespaceObject(), policies -> {
policies.auth_policies.getTopicAuthentication()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,24 @@ protected CompletableFuture<Map<String, Set<AuthAction>>> internalGetPermissions
}
}
}

// If topic is partitioned, add based topic permission

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we should grant permission based on the partition's base topic. If the call is about a specific permission, we should respond with the result for that partition.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michaeljmarshall Is your idea the same as I described in #16792 (comment) ?? That's what I thought at the beginning, marked as plan 1.

plan 2 is described in #16792 (comment), this one is designed based on the idea of @nodece .

Both of the two plan can enhance. I think plan 1 would have better compatibility with previous version, while plan 2 would have cleaner permission record.

if (topicName.isPartitioned() && auth.getTopicAuthentication().containsKey(
topicName.getPartitionedTopicName())) {
for (Map.Entry<String, Set<AuthAction>> entry :
auth.getTopicAuthentication().get(topicName.getPartitionedTopicName()).entrySet()) {
String role = entry.getKey();
Set<AuthAction> topicPermissions = entry.getValue();

if (!permissions.containsKey(role)) {
permissions.put(role, topicPermissions);
} else {
// Do the union between namespace and topic level
Set<AuthAction> union = Sets.union(permissions.get(role), topicPermissions);
permissions.put(role, union);
}
}
}
Comment on lines +277 to +293

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing this is meant to grant permission based on a topic's partition name. This code block will be insufficient. We'll need to look at the AuthorizationProvider to get a complete implementation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the AuthorizationProvider should provide the get permissions method to avoid this case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of grant is in AuthorizationProvider, while get/revoke implementation is in persistentTopicsBase.
Is it better to provide the get/revoke permission method in AuthorizationProvider?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of grant is in AuthorizationProvider, while get/revoke implementation is in persistentTopicsBase.
Yes.

Is it better to provide the get/revoke permission method in AuthorizationProvider?

Just is a suggestion, you don't need to do any updates.

return permissions;
}));
}
Expand Down Expand Up @@ -326,20 +344,9 @@ protected void internalGrantPermissionsOnTopic(final AsyncResponse asyncResponse
// This operation should be reading from zookeeper and it should be allowed without having admin privileges
validateAdminAccessForTenantAsync(namespaceName.getTenant())
.thenCompose(__ -> validatePoliciesReadOnlyAccessAsync().thenCompose(unused1 ->
getPartitionedTopicMetadataAsync(topicName, true, false)
.thenCompose(metadata -> {
int numPartitions = metadata.partitions;
CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
if (numPartitions > 0) {
for (int i = 0; i < numPartitions; i++) {
TopicName topicNamePartition = topicName.getPartition(i);
future = future.thenCompose(unused -> grantPermissionsAsync(topicNamePartition, role,
actions));
}
}
Comment on lines -333 to -339

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to make this change backwards compatible, I think we need to leave this block (at least in the PR that is focused on fixing the historical bug). Otherwise, custom implementations of the AuthorizationProvider interface would have the behavior broken on them, which shouldn't happen in a patch release.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all that is required for the optimization (regarding my other comment).

return future.thenCompose(unused -> grantPermissionsAsync(topicName, role, actions))
.thenAccept(unused -> asyncResponse.resume(Response.noContent().build()));
}))).exceptionally(ex -> {
grantPermissionsAsync(topicName, role, actions)
.thenAccept(unused -> asyncResponse.resume(Response.noContent().build()))))
.exceptionally(ex -> {
Throwable realCause = FutureUtil.unwrapCompletionException(ex);
log.error("[{}] Failed to get permissions for topic {}", clientAppId(), topicName, realCause);
resumeAsyncResponseExceptionally(asyncResponse, realCause);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,6 @@ public void testDeleteAuthenticationPoliciesOfTopic() throws Exception {
Awaitility.await().untilAsserted(() -> {
assertTrue(pulsar.getPulsarResources().getNamespaceResources().getPolicies(NamespaceName.get("p1/ns1"))
.get().auth_policies.getTopicAuthentication().containsKey(partitionedTopic));
for (int i = 0; i < numPartitions; i++) {
assertTrue(pulsar.getPulsarResources().getNamespaceResources().getPolicies(NamespaceName.get("p1/ns1"))
.get().auth_policies.getTopicAuthentication()
.containsKey(TopicName.get(partitionedTopic).getPartition(i).toString()));
}
});

admin.topics().deletePartitionedTopic("persistent://p1/ns1/partitioned-topic");
Expand Down