Skip to content

Commit

Permalink
Add ability to hide specific ACL entries from listing
Browse files Browse the repository at this point in the history
  • Loading branch information
tvainika committed Nov 11, 2024
1 parent 48cd186 commit e2ad8ef
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 39 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ for each project.
Permission type allows to define the verification result in case of an ACL match.
By default, the permission type is `ALLOW`.

A specific ACL entry can be hidden from public listing by setting hidden flag.

### Example

[
Expand All @@ -31,6 +33,7 @@ By default, the permission type is `ALLOW`.
"principal_type": "Prune",
"host": "*",
"resource": "^(.*)$",
"hidden": true
},
{
"operation": "^(Describe|DescribeConfigs|Read|Write)$",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ public final List<? extends CompletionStage<AclDeleteResult>> deleteAcls(
public final Iterable<AclBinding> acls(final AclBindingFilter filter) {
if (this.config.listAclsEnabled()) {
return this.cacheReference.get().aclEntries()
.filter(acl -> !acl.isHidden())
.flatMap(acl -> AclAivenToNativeConverter.convert(acl).stream())
.filter(filter::matches)
.collect(Collectors.toList());
Expand Down
30 changes: 24 additions & 6 deletions src/main/java/io/aiven/kafka/auth/json/AivenAcl.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,24 @@ public class AivenAcl {
@SerializedName("host")
private final String hostMatcher;

private final boolean hidden;

public AivenAcl(final String principalType,
final String principal,
final String host,
final String operation,
final String resource,
final String resourcePattern,
final AclPermissionType permissionType) {
final AclPermissionType permissionType,
final boolean hidden) {
this.principalType = principalType;
this.principalRe = Pattern.compile(principal);
this.hostMatcher = host;
this.operationRe = Pattern.compile(operation);
this.resourceRe = Objects.nonNull(resource) ? Pattern.compile(resource) : null;
this.resourceRePattern = resourcePattern;
this.permissionType = Objects.requireNonNullElse(permissionType, AclPermissionType.ALLOW);
this.hidden = hidden;
}

public AclPermissionType getPermissionType() {
Expand Down Expand Up @@ -119,13 +123,22 @@ public boolean equals(final Object o) {
return false;
}
final AivenAcl aivenAcl = (AivenAcl) o;
return Objects.equals(principalType, aivenAcl.principalType)
&& comparePattern(principalRe, aivenAcl.principalRe)
return equalsPrincipal(aivenAcl)
&& getHostMatcher().equals(aivenAcl.getHostMatcher())
&& comparePattern(operationRe, aivenAcl.operationRe)
&& comparePattern(resourceRe, aivenAcl.resourceRe)
&& Objects.equals(resourceRePattern, aivenAcl.resourceRePattern)
&& getPermissionType() == aivenAcl.getPermissionType(); // always compare permission type using getter
&& equalsResource(aivenAcl)
&& getPermissionType() == aivenAcl.getPermissionType() // always compare permission type using getter
&& hidden == aivenAcl.hidden;
}

private boolean equalsPrincipal(final AivenAcl aivenAcl) {
return Objects.equals(principalType, aivenAcl.principalType)
&& comparePattern(principalRe, aivenAcl.principalRe);
}

private boolean equalsResource(final AivenAcl aivenAcl) {
return comparePattern(resourceRe, aivenAcl.resourceRe)
&& Objects.equals(resourceRePattern, aivenAcl.resourceRePattern);
}

private boolean comparePattern(final Pattern p1, final Pattern p2) {
Expand Down Expand Up @@ -158,6 +171,11 @@ public String toString() {
+ ", resourceRePattern='" + resourceRePattern
+ "', permissionType=" + getPermissionType()
+ ", hostMatcher='" + getHostMatcher()
+ ", hidden=" + hidden
+ "'}";
}

public boolean isHidden() {
return hidden;
}
}
6 changes: 3 additions & 3 deletions src/test/java/io/aiven/kafka/auth/json/AivenAclTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void testAivenAclEntry() {
"^(Describe|Read)$", // operation
"^Topic:p_(.*)_s", // resource,
null, // resource pattern
null
null, false
);

assertTrue(entry.match("User", "CN=p_pass_s", "*", "Read", "Topic:p_pass_s"));
Expand All @@ -49,7 +49,7 @@ public void testAivenAclEntry() {
"^(Describe|Read)$", // operation
"^Topic:p_(.*)_s", // resource
null, // resource pattern
null
null, false
);

assertTrue(entry.match("User", "CN=p_pass_s", "*", "Read", "Topic:p_pass_s"));
Expand All @@ -65,7 +65,7 @@ public void testAivenAclEntry() {
"^(Describe|Read)$", // operation
null, // resource
"^Topic:p_${username}_s\\$", // resource pattern
null
null, false
);

assertTrue(entry.match("User", "CN=p_user1_s", "*", "Read", "Topic:p_user1_s"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,28 @@ public final void parseAcls() {
final var jsonReader = new AclJsonReader(path);
final var acls = jsonReader.read();
assertThat(acls).containsExactly(
new AivenAcl("User", "^pass-3$", "*", "^Read$", "^Topic:denied$", null, AclPermissionType.DENY),
new AivenAcl("User", "^pass-0$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-1$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-2$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-3$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-4$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-5$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-6$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-7$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-8$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-9$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-10$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-11$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-12$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl(null, "^pass-notype$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-3$", "*", "^Read$", "^Topic:denied$", null, AclPermissionType.DENY, false),
new AivenAcl("User", "^pass-0$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-1$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-2$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-3$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-4$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-5$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-6$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-7$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-8$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-9$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-10$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-11$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-12$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl(null, "^pass-notype$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl(
"User", "^pass-resource-pattern$", "*", "^Read$",
null, "^Topic:${projectid}-(.*)", AclPermissionType.ALLOW
null, "^Topic:${projectid}-(.*)", AclPermissionType.ALLOW, false
),
new AivenAcl("User", "^pass-13$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW),
new AivenAcl("User", "^pass-14$", "example.com", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW)
new AivenAcl("User", "^pass-13$", "*", "^Read$", "^Topic:(.*)$", null, AclPermissionType.ALLOW, false),
new AivenAcl("User", "^pass-14$", "example.com", "^Read$", "^Topic:(.*)$",
null, AclPermissionType.ALLOW, true)
);
}

Expand All @@ -72,7 +73,7 @@ public final void parseDenyAcl() {
"^Read$",
"^(.*)$",
null,
AclPermissionType.ALLOW
AclPermissionType.ALLOW, false
);
final var denyAcl = new AivenAcl(
"User",
Expand All @@ -81,7 +82,7 @@ public final void parseDenyAcl() {
"^Read$",
"^(.*)$",
null,
AclPermissionType.DENY
AclPermissionType.DENY, false
);
assertThat(acls).containsExactly(allowAcl, allowAcl, allowAcl, denyAcl, denyAcl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public final void testConvertSimple() {
"^(Alter|AlterConfigs|Delete|Read|Write)$",
"^Topic:(xxx)$",
null,
io.aiven.kafka.auth.json.AclPermissionType.ALLOW
io.aiven.kafka.auth.json.AclPermissionType.ALLOW, false
)
);
final ResourcePattern resourcePattern = new ResourcePattern(ResourceType.TOPIC, "xxx", PatternType.LITERAL);
Expand Down Expand Up @@ -77,7 +77,7 @@ public final void testNullPermissionTypeIsAllow() {
"^Read$",
"^Topic:(xxx)$",
null,
null
null, false
)
);
final ResourcePattern resourcePattern = new ResourcePattern(ResourceType.TOPIC, "xxx", PatternType.LITERAL);
Expand All @@ -98,7 +98,7 @@ public final void testConvertPrefix() {
"^Read$",
"^Topic:(topic\\.(.*))$",
null,
null
null, false
)
);
assertThat(result).containsExactly(
Expand All @@ -119,7 +119,7 @@ public final void testDeny() {
"^Read$",
"^Topic:(topic\\.(.*))$",
null,
io.aiven.kafka.auth.json.AclPermissionType.DENY
io.aiven.kafka.auth.json.AclPermissionType.DENY, false
)
);
assertThat(result).containsExactly(
Expand All @@ -140,7 +140,7 @@ public final void testConvertMultiplePrefixes() {
"^(Delete|Read|Write)$",
"^Topic:(topic\\.(.*)|prefix\\-(.*))$",
null,
null
null, false
)
);
assertThat(result).containsExactly(
Expand Down Expand Up @@ -181,7 +181,7 @@ public final void testSuperadmin() {
"^(.*)$",
"^(.*)$",
null,
null
null, false
)
);

Expand Down Expand Up @@ -215,7 +215,7 @@ public final void testAllUsers() {
"^Read$",
"^Topic:(xxx)$",
null,
null
null, false
)
);

Expand All @@ -237,7 +237,7 @@ public final void testNoUserPrincipalType() {
"^Read$",
"^Topic:(xxx)$",
null,
null
null, false
)
);

Expand All @@ -254,7 +254,7 @@ public final void testConvertHostMatcher() {
"^Read$",
"^Topic:(xxx)$",
null,
null
null, false
)
);

Expand Down
3 changes: 2 additions & 1 deletion src/test/resources/acls_full.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"principal": "^pass-14$",
"host": "example.com",
"operation": "^Read$",
"resource": "^Topic:(.*)$"
"resource": "^Topic:(.*)$",
"hidden": true
}
]
7 changes: 7 additions & 0 deletions src/test/resources/test_acls_for_acls_method.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,12 @@
"principal": "^(.*)$",
"principal_type": "Service",
"resource": "^(.*)$"
},
{
"operation": "^(Delete)$",
"principal": "^(test\\-user)$",
"principal_type": "User",
"resource": "^Topic:(hidden\\.(.*))$",
"hidden": true
}
]

0 comments on commit e2ad8ef

Please sign in to comment.