From 665d78eaf0efd817e5e31738e3edd4c38ffcad6d Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Wed, 17 Feb 2021 04:54:09 -0800 Subject: [PATCH 1/3] suppress unknown ResourceType and Action for basic-security authorizer stuff --- .../entity/BasicAuthorizerPermission.java | 9 ++ .../entity/BasicAuthorizerRole.java | 45 ++++++- .../druid/security/BasicAuthUtilsTest.java | 123 ++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerPermission.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerPermission.java index 8efddac96f42..63169761cb7e 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerPermission.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerPermission.java @@ -119,4 +119,13 @@ public int hashCode() : 0); return result; } + + @Override + public String toString() + { + return "BasicAuthorizerPermission{" + + "resourceAction=" + resourceAction + + ", resourceNamePattern=" + resourceNamePattern + + '}'; + } } diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java index 83b2d5590f94..0d4ac9ed40ce 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java @@ -21,19 +21,31 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.apache.druid.java.util.common.RE; +import org.apache.druid.java.util.common.logger.Logger; +import java.io.IOException; import java.util.ArrayList; import java.util.List; public class BasicAuthorizerRole { + private static final Logger log = new Logger(BasicAuthorizerRole.class); + private final String name; private final List permissions; @JsonCreator public BasicAuthorizerRole( @JsonProperty("name") String name, - @JsonProperty("permissions") List permissions + @JsonProperty("permissions") @JsonDeserialize(using = PermissionsDeserializer.class) List permissions ) { this.name = name; @@ -78,4 +90,35 @@ public int hashCode() result = 31 * result + (getPermissions() != null ? getPermissions().hashCode() : 0); return result; } + + + static class PermissionsDeserializer extends JsonDeserializer> + { + @Override + public List deserialize( + JsonParser jsonParser, + DeserializationContext deserializationContext + ) throws IOException + { + List permissions = new ArrayList<>(); + // sanity check + ObjectCodec codec = jsonParser.getCodec(); + JsonNode hopefullyAnArray = codec.readTree(jsonParser); + if (!hopefullyAnArray.isArray()) { + throw new RE("Failed to deserialize authorizer role list"); + } + + for (JsonNode node : hopefullyAnArray) { + try { + permissions.add(codec.treeToValue(node, BasicAuthorizerPermission.class)); + } + catch (JsonProcessingException e) { + // ignore unparseable, it might be resource types we don't know about + log.warn(e, "Failed to deserialize authorizer role, ignoring"); + } + } + + return permissions; + } + } } diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java index 142e44afb4bb..6a6ccb51f010 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java @@ -19,10 +19,25 @@ package org.apache.druid.security; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.security.basic.BasicAuthUtils; +import org.apache.druid.security.basic.BasicSecurityDruidModule; +import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerPermission; +import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRole; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import org.junit.Assert; import org.junit.Test; +import java.util.HashMap; +import java.util.Map; + public class BasicAuthUtilsTest { @Test @@ -36,4 +51,112 @@ public void testHashPassword() Assert.assertEquals(BasicAuthUtils.SALT_LENGTH, salt.length); Assert.assertEquals(BasicAuthUtils.KEY_LENGTH / 8, hash.length); } + + @Test + public void testPermissionSerdeIsChillAboutUnknownEnumStuffs() throws JsonProcessingException + { + final String someRoleName = "some-role"; + final String otherRoleName = "other-role"; + final String thirdRoleName = "third-role"; + final ResourceAction fooRead = new ResourceAction(new Resource("foo", ResourceType.DATASOURCE), Action.READ); + final ResourceAction barRead = new ResourceAction(new Resource("bar", ResourceType.DATASOURCE), Action.READ); + + final ObjectMapper mapper = new DefaultObjectMapper(); + mapper.registerModules(new BasicSecurityDruidModule().getJacksonModules()); + Map rawMap = new HashMap<>(); + rawMap.put( + someRoleName, + new BasicAuthorizerRole( + someRoleName, + BasicAuthorizerPermission.makePermissionList( + ImmutableList.of( + fooRead, + barRead + ) + ) + ) + ); + // bad ResourceType + rawMap.put( + otherRoleName, + ImmutableMap.of( + "name", + otherRoleName, + "permissions", + ImmutableList.of( + ImmutableMap.of( + "resourceAction", fooRead, + "resourceNamePattern", "foo" + ), + ImmutableMap.of( + "resourceAction", + ImmutableMap.of( + "resource", + ImmutableMap.of("name", "bar", "type", "UNKNOWN"), + "action", "READ" + ), + "resourceNamePattern", "bar" + ) + ) + ) + ); + // bad Action + rawMap.put( + thirdRoleName, + ImmutableMap.of( + "name", + thirdRoleName, + "permissions", + ImmutableList.of( + ImmutableMap.of( + "resourceAction", + ImmutableMap.of( + "resource", + ImmutableMap.of("name", "some-view", "type", "VIEW"), + "action", "READ" + ), + "resourceNamePattern", "some-view" + ), + ImmutableMap.of( + "resourceAction", + ImmutableMap.of( + "resource", + ImmutableMap.of("name", "foo", "type", "DATASOURCE"), + "action", "UNKNOWN" + ), + "resourceNamePattern", "some-view" + ) + ) + ) + ); + byte[] mapBytes = mapper.writeValueAsBytes(rawMap); + Map roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(mapper, mapBytes); + Assert.assertNotNull(roleMap); + Assert.assertEquals(3, roleMap.size()); + + Assert.assertTrue(roleMap.containsKey(someRoleName)); + Assert.assertEquals(2, roleMap.get(someRoleName).getPermissions().size()); + Assert.assertEquals( + BasicAuthorizerPermission.makePermissionList(ImmutableList.of(fooRead, barRead)), + roleMap.get(someRoleName).getPermissions() + ); + + // this one has an unknown ResourceType, expect only 1 permission to deserialize correctly and failure ignored + Assert.assertTrue(roleMap.containsKey(otherRoleName)); + Assert.assertEquals(1, roleMap.get(otherRoleName).getPermissions().size()); + Assert.assertEquals( + BasicAuthorizerPermission.makePermissionList(ImmutableList.of(fooRead)), + roleMap.get(otherRoleName).getPermissions() + ); + + // this one has an unknown Action, expect only 1 permission to deserialize correctly and failure ignored + Assert.assertTrue(roleMap.containsKey(thirdRoleName)); + Assert.assertEquals(1, roleMap.get(thirdRoleName).getPermissions().size()); + Assert.assertEquals( + BasicAuthorizerPermission.makePermissionList( + ImmutableList.of(new ResourceAction(new Resource("some-view", ResourceType.VIEW), Action.READ)) + ), + roleMap.get(thirdRoleName).getPermissions() + ); + } } From 414c40852ffee217b77f741c7729000b4dafb32c Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Wed, 17 Feb 2021 14:57:32 -0800 Subject: [PATCH 2/3] fix pom --- extensions-core/druid-basic-security/pom.xml | 7 +++++++ .../java/org/apache/druid/security/BasicAuthUtilsTest.java | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/extensions-core/druid-basic-security/pom.xml b/extensions-core/druid-basic-security/pom.xml index e0630ea75475..3b4bebdc8532 100644 --- a/extensions-core/druid-basic-security/pom.xml +++ b/extensions-core/druid-basic-security/pom.xml @@ -147,5 +147,12 @@ test-jar test + + org.apache.druid + druid-processing + ${project.parent.version} + test + test-jar + diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java index 6a6ccb51f010..7bcb1e24c778 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/BasicAuthUtilsTest.java @@ -23,11 +23,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.security.basic.BasicAuthUtils; import org.apache.druid.security.basic.BasicSecurityDruidModule; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerPermission; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRole; +import org.apache.druid.segment.TestHelper; import org.apache.druid.server.security.Action; import org.apache.druid.server.security.Resource; import org.apache.druid.server.security.ResourceAction; @@ -61,7 +61,7 @@ public void testPermissionSerdeIsChillAboutUnknownEnumStuffs() throws JsonProces final ResourceAction fooRead = new ResourceAction(new Resource("foo", ResourceType.DATASOURCE), Action.READ); final ResourceAction barRead = new ResourceAction(new Resource("bar", ResourceType.DATASOURCE), Action.READ); - final ObjectMapper mapper = new DefaultObjectMapper(); + final ObjectMapper mapper = TestHelper.makeJsonMapper(); mapper.registerModules(new BasicSecurityDruidModule().getJacksonModules()); Map rawMap = new HashMap<>(); rawMap.put( From bab3f0211bbe5f6fd07d35b4a353a8ad362c0d79 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Tue, 23 Feb 2021 02:11:53 -0800 Subject: [PATCH 3/3] print failed role, test logs --- .../entity/BasicAuthorizerRole.java | 2 +- .../src/test/resources/log4j2.xml | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 extensions-core/druid-basic-security/src/test/resources/log4j2.xml diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java index 0d4ac9ed40ce..f04f0883b9d3 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/entity/BasicAuthorizerRole.java @@ -114,7 +114,7 @@ public List deserialize( } catch (JsonProcessingException e) { // ignore unparseable, it might be resource types we don't know about - log.warn(e, "Failed to deserialize authorizer role, ignoring"); + log.warn(e, "Failed to deserialize authorizer role, ignoring: %s", node.toPrettyString()); } } diff --git a/extensions-core/druid-basic-security/src/test/resources/log4j2.xml b/extensions-core/druid-basic-security/src/test/resources/log4j2.xml new file mode 100644 index 000000000000..bca6c69fdb1b --- /dev/null +++ b/extensions-core/druid-basic-security/src/test/resources/log4j2.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + +