From 93ae28fe3249e761130c56cdaf757ab03b2c9d71 Mon Sep 17 00:00:00 2001 From: Rob Rudin Date: Fri, 25 Aug 2023 13:57:07 -0400 Subject: [PATCH] DEVEXP-478 New addFromString method for permissions This logic is from ml-javaclient-util, pretty much copy/pasted it and added tests. Also moved a test to the new "client.test.io" package as I don't like how we have a zillion tests in the "com.marklogic.client.test" package. --- .../client/io/DocumentMetadataHandle.java | 75 +++++++++++++++---- ...AddPermissionsFromDelimitedStringTest.java | 64 ++++++++++++++++ .../{ => io}/DocumentMetadataHandleTest.java | 3 +- 3 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 marklogic-client-api/src/test/java/com/marklogic/client/test/io/AddPermissionsFromDelimitedStringTest.java rename marklogic-client-api/src/test/java/com/marklogic/client/test/{ => io}/DocumentMetadataHandleTest.java (99%) diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/io/DocumentMetadataHandle.java b/marklogic-client-api/src/main/java/com/marklogic/client/io/DocumentMetadataHandle.java index db7909677..c49be2035 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/io/DocumentMetadataHandle.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/io/DocumentMetadataHandle.java @@ -21,7 +21,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -95,18 +94,33 @@ public void addAll(String... collections) { } } - /** - * Represents the permissions for a database document. - */ - public interface DocumentPermissions extends Map> { - /** - * Adds a role with one or more capabilities to the metadata that can be written - * for the document. - * @param role the role for users permitted to access the document - * @param capabilities the permissions to be granted to users with the role - */ - public void add(String role, Capability... capabilities); - } + /** + * Represents the permissions for a database document. + */ + public interface DocumentPermissions extends Map> { + /** + * Adds a role with one or more capabilities to the metadata that can be written + * for the document. + * + * @param role the role for users permitted to access the document + * @param capabilities the permissions to be granted to users with the role + */ + void add(String role, Capability... capabilities); + + /** + * Adds one or more permissions based on the given comma-delimited string. Each capability value is + * case-insensitive; you do not need to worry about providing the correct case. Similar to {@code add}, this + * method adds permissions and can thus add capabilities to roles already present in this object. + * + * For example, the following string would add two permissions - a "read" permission for "rest-reader" and an + * "update" permission for "rest-writer": rest-reader,read,rest-writer,update. + * + * @param commaDelimitedRolesAndCapabilities comma-delimited string of the pattern: role1,capability1,role2,capability2,etc. + * @since 6.3.0 + */ + void addFromDelimitedString(String commaDelimitedRolesAndCapabilities); + } + @SuppressWarnings("serial") static private class PermissionsImpl extends HashMap> implements DocumentPermissions { @Override @@ -130,7 +144,42 @@ public void add(String role, Capability capability) { put(role, caps ); } } + + /** + * + * @param commaDelimitedRolesAndCapabilities comma-delimited string of the pattern: role1,capability1,role2,capability2,etc. + * @since 6.3.0 + */ + @Override + public void addFromDelimitedString(String commaDelimitedRolesAndCapabilities) { + if (commaDelimitedRolesAndCapabilities != null && commaDelimitedRolesAndCapabilities.trim().length() > 0) { + String[] tokens = commaDelimitedRolesAndCapabilities.trim().split(","); + for (int i = 0; i < tokens.length; i += 2) { + String role = tokens[i]; + if (i + 1 >= tokens.length) { + throw new IllegalArgumentException(String.format( + "Unable to parse permissions string, which must be a comma-delimited " + + "list of role names and capabilities - i.e. role1,read,role2,update,role3,execute; string: %s", + commaDelimitedRolesAndCapabilities)); + } + Capability c; + try { + c = Capability.getValueOf(tokens[i + 1]); + } catch (Exception e) { + throw new IllegalArgumentException(String.format( + "Unable to parse permissions string: %s; cause: %s", + commaDelimitedRolesAndCapabilities, e.getMessage())); + } + if (this.containsKey(role)) { + this.get(role).add(c); + } else { + this.add(role, c); + } + } + } + } } + /** * A document operation restricted to users with a role. */ diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/io/AddPermissionsFromDelimitedStringTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/io/AddPermissionsFromDelimitedStringTest.java new file mode 100644 index 000000000..861e38b23 --- /dev/null +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/io/AddPermissionsFromDelimitedStringTest.java @@ -0,0 +1,64 @@ +package com.marklogic.client.test.io; + +import com.marklogic.client.io.DocumentMetadataHandle; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AddPermissionsFromDelimitedStringTest { + + private DocumentMetadataHandle.DocumentPermissions permissions; + + @BeforeEach + void beforeEach() { + permissions = new DocumentMetadataHandle().getPermissions(); + } + + @Test + void simpleCase() { + permissions.addFromDelimitedString("rest-admin,read,rest-admin,update,rest-extension-user,execute,app-user,node-update"); + + assertEquals(3, permissions.size()); + assertEquals(2, permissions.get("rest-admin").size()); + assertEquals(1, permissions.get("rest-extension-user").size()); + assertEquals(DocumentMetadataHandle.Capability.EXECUTE, permissions.get("rest-extension-user").iterator().next()); + assertEquals(1, permissions.get("app-user").size()); + assertEquals(DocumentMetadataHandle.Capability.NODE_UPDATE, permissions.get("app-user").iterator().next()); + } + + @Test + void roleAlreadyExists() { + permissions.addFromDelimitedString("rest-reader,read"); + assertEquals(1, permissions.size()); + assertEquals(1, permissions.get("rest-reader").size()); + + permissions.addFromDelimitedString("rest-reader,update"); + assertEquals(1, permissions.size()); + assertEquals(2, permissions.get("rest-reader").size()); + + permissions.addFromDelimitedString("rest-reader,read"); + assertEquals(1, permissions.size()); + assertEquals(2, permissions.get("rest-reader").size(), "A duplicate permission should be ignored since the " + + "set of capabilities is a Set."); + } + + @Test + void badInput() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> permissions.addFromDelimitedString("rest-admin,read,rest-admin")); + assertTrue(ex.getMessage().startsWith("Unable to parse permissions string")); + } + + @Test + void invalidCapability() { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> permissions.addFromDelimitedString("app-user,not-valid")); + assertEquals("Unable to parse permissions string: app-user,not-valid; cause: No enum constant com.marklogic.client.io.DocumentMetadataHandle.Capability.NOT_VALID", + ex.getMessage()); + } + +} diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/DocumentMetadataHandleTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/io/DocumentMetadataHandleTest.java similarity index 99% rename from marklogic-client-api/src/test/java/com/marklogic/client/test/DocumentMetadataHandleTest.java rename to marklogic-client-api/src/test/java/com/marklogic/client/test/io/DocumentMetadataHandleTest.java index 5b4cf2a11..cb3167e89 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/DocumentMetadataHandleTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/io/DocumentMetadataHandleTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.marklogic.client.test; +package com.marklogic.client.test.io; import com.marklogic.client.Transaction; import com.marklogic.client.document.BinaryDocumentManager; @@ -24,6 +24,7 @@ import com.marklogic.client.io.FileHandle; import com.marklogic.client.io.InputStreamHandle; import com.marklogic.client.io.StringHandle; +import com.marklogic.client.test.Common; import org.custommonkey.xmlunit.exceptions.XpathException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll;