Skip to content

Commit 61bd4dd

Browse files
rjrudinSameeraPriyathamTadikonda
authored andcommitted
DHFPROD-4558: data-hub-developer can now deploy query rolesets
1 parent d415471 commit 61bd4dd

File tree

7 files changed

+159
-6
lines changed

7 files changed

+159
-6
lines changed

marklogic-data-hub/src/main/java/com/marklogic/hub/dhs/DhsDeployer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.marklogic.appdeployer.command.databases.DeployOtherDatabasesCommand;
1010
import com.marklogic.appdeployer.command.schemas.LoadSchemasCommand;
1111
import com.marklogic.appdeployer.command.security.DeployPrivilegesCommand;
12+
import com.marklogic.appdeployer.command.security.DeployProtectedPathsCommand;
1213
import com.marklogic.appdeployer.command.security.DeployRolesCommand;
1314
import com.marklogic.appdeployer.command.tasks.DeployScheduledTasksCommand;
1415
import com.marklogic.appdeployer.command.temporal.DeployTemporalAxesCommand;
@@ -19,6 +20,7 @@
1920
import com.marklogic.hub.DatabaseKind;
2021
import com.marklogic.hub.HubConfig;
2122
import com.marklogic.hub.deploy.HubAppDeployer;
23+
import com.marklogic.hub.dhs.installer.deploy.DeployHubQueryRolesetsCommand;
2224
import com.marklogic.hub.deploy.commands.HubDeployDatabaseCommandFactory;
2325
import com.marklogic.hub.deploy.commands.LoadUserArtifactsCommand;
2426
import com.marklogic.hub.deploy.commands.LoadUserModulesCommand;
@@ -183,6 +185,9 @@ protected List<Command> buildCommandsForDeveloper(HubConfig hubConfig) {
183185
commands.add(new LoadSchemasCommand());
184186
commands.add(new DeployScheduledTasksCommand());
185187

188+
commands.add(new DeployProtectedPathsCommand());
189+
commands.add(new DeployHubQueryRolesetsCommand());
190+
186191
return commands;
187192
}
188193

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.marklogic.hub.dhs.installer.deploy;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.marklogic.appdeployer.command.CommandContext;
5+
import com.marklogic.appdeployer.command.security.DeployQueryRolesetsCommand;
6+
import com.marklogic.mgmt.SaveReceipt;
7+
import com.marklogic.mgmt.resource.ResourceManager;
8+
import com.marklogic.mgmt.util.ObjectMapperFactory;
9+
import org.springframework.web.client.HttpClientErrorException;
10+
11+
/**
12+
* This command is used instead of DeployQueryRolesetsCommand for when a data-hub-developer user is deploying
13+
* query rolesets. See the comments in the test class for this class to understand why it exists.
14+
*/
15+
public class DeployHubQueryRolesetsCommand extends DeployQueryRolesetsCommand {
16+
17+
@Override
18+
public boolean cmaShouldBeUsed(CommandContext context) {
19+
return false;
20+
}
21+
22+
@Override
23+
protected SaveReceipt saveResource(ResourceManager mgr, CommandContext context, String payload) {
24+
SaveReceipt receipt = null;
25+
try {
26+
receipt = super.saveResource(mgr, context, payload);
27+
} catch (HttpClientErrorException ex) {
28+
if (isPermissionedDeniedException(ex)) {
29+
logger.info("Received SEC-PERMDENIED error when deploying query roleset; this can be safely ignored if the " +
30+
"query roleset already exists in MarkLogic.");
31+
} else {
32+
throw ex;
33+
}
34+
}
35+
return receipt;
36+
}
37+
38+
protected boolean isPermissionedDeniedException(HttpClientErrorException ex) {
39+
try {
40+
JsonNode error = ObjectMapperFactory.getObjectMapper().readTree(ex.getResponseBodyAsString());
41+
if (error.has("errorResponse")) {
42+
JsonNode errorResponse = error.get("errorResponse");
43+
if (errorResponse.has("messageCode")) {
44+
return "SEC-PERMDENIED".equals(errorResponse.get("messageCode").asText());
45+
}
46+
}
47+
} catch (Exception e) {
48+
logger.warn("Unexpected error when trying to parse error for deploying query rolesets: " + e.getMessage());
49+
}
50+
return false;
51+
}
52+
}

marklogic-data-hub/src/main/resources/hub-internal-config/security/roles/data-hub-developer.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
"data-hub-entity-model-writer"
1414
],
1515
"privilege": [
16+
{
17+
"privilege-name": "add-query-rolesets",
18+
"action": "http://marklogic.com/xdmp/privileges/add-query-rolesets",
19+
"kind": "execute"
20+
},
1621
{
1722
"privilege-name": "any-uri",
1823
"action": "http://marklogic.com/xdmp/privileges/any-uri",
@@ -38,6 +43,11 @@
3843
"action": "http://marklogic.com/xdmp/privileges/remove-path",
3944
"kind": "execute"
4045
},
46+
{
47+
"privilege-name": "remove-query-rolesets",
48+
"action": "http://marklogic.com/xdmp/privileges/remove-query-rolesets",
49+
"kind": "execute"
50+
},
4151
{
4252
"privilege-name": "rest-admin",
4353
"action": "http://marklogic.com/xdmp/privileges/rest-admin",

marklogic-data-hub/src/test/java/com/marklogic/hub/dhs/DeployAsDeveloperTest.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.marklogic.appdeployer.command.alert.DeployAlertRulesCommand;
1010
import com.marklogic.appdeployer.command.databases.DeployOtherDatabasesCommand;
1111
import com.marklogic.appdeployer.command.schemas.LoadSchemasCommand;
12+
import com.marklogic.appdeployer.command.security.DeployProtectedPathsCommand;
1213
import com.marklogic.appdeployer.command.tasks.DeployScheduledTasksCommand;
1314
import com.marklogic.appdeployer.command.temporal.DeployTemporalAxesCommand;
1415
import com.marklogic.appdeployer.command.temporal.DeployTemporalCollectionsCommand;
@@ -17,6 +18,7 @@
1718
import com.marklogic.hub.DatabaseKind;
1819
import com.marklogic.hub.HubConfig;
1920
import com.marklogic.hub.HubTestBase;
21+
import com.marklogic.hub.dhs.installer.deploy.DeployHubQueryRolesetsCommand;
2022
import com.marklogic.hub.deploy.commands.LoadUserArtifactsCommand;
2123
import com.marklogic.hub.deploy.commands.LoadUserModulesCommand;
2224
import com.marklogic.hub.impl.HubConfigImpl;
@@ -169,8 +171,9 @@ public void copyStagingSslConfigToAppServices() {
169171
public void buildCommandList() {
170172
List<Command> commands = new DhsDeployer().buildCommandsForDeveloper(hubConfig);
171173
Collections.sort(commands, Comparator.comparing(Command::getExecuteSortOrder));
172-
System.out.println(commands);
173174
int index = 0;
175+
assertTrue(commands.get(index++) instanceof DeployProtectedPathsCommand);
176+
assertTrue(commands.get(index++) instanceof DeployHubQueryRolesetsCommand);
174177
assertTrue(commands.get(index++) instanceof DeployOtherDatabasesCommand);
175178
assertTrue(commands.get(index++) instanceof LoadSchemasCommand);
176179
assertTrue(commands.get(index++) instanceof LoadUserModulesCommand);
@@ -182,13 +185,13 @@ public void buildCommandList() {
182185
assertTrue(commands.get(index++) instanceof DeployAlertConfigsCommand);
183186
assertTrue(commands.get(index++) instanceof DeployAlertActionsCommand);
184187
assertTrue(commands.get(index++) instanceof DeployAlertRulesCommand);
185-
assertEquals(11, commands.size(),
188+
assertEquals(13, commands.size(),
186189
"As of ML 10.0-3, the granular privilege for indexes doesn't seem to work with XML payloads. " +
187190
"Bug https://bugtrack.marklogic.com/54231 has been created to track that. Thus, " +
188191
"DeployDatabaseFieldCommand cannot be included and ml-config/database-fields/final-database.xml " +
189192
"cannot be processed.");
190193

191-
DeployOtherDatabasesCommand dodc = (DeployOtherDatabasesCommand) commands.get(0);
194+
DeployOtherDatabasesCommand dodc = (DeployOtherDatabasesCommand) commands.get(2);
192195
ResourceFilenameFilter filter = (ResourceFilenameFilter) dodc.getResourceFilenameFilter();
193196
assertEquals("(staging|final|job)-database.json", filter.getIncludePattern().pattern(),
194197
"DHS users aren't allowed to create their own databases, so the command for deploying databases is restricted " +
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.marklogic.hub.dhs.installer.deploy;
2+
3+
import com.marklogic.appdeployer.AppConfig;
4+
import com.marklogic.appdeployer.command.CommandContext;
5+
import com.marklogic.hub.security.AbstractSecurityTest;
6+
import com.marklogic.mgmt.SaveReceipt;
7+
import com.marklogic.mgmt.resource.security.QueryRolesetManager;
8+
import org.junit.jupiter.api.Test;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
public class DeployHubQueryRolesetsCommandTest extends AbstractSecurityTest {
13+
14+
@Override
15+
protected String getRoleName() {
16+
return "data-hub-developer";
17+
}
18+
19+
@Test
20+
void test() {
21+
DeployHubQueryRolesetsCommand command = new DeployHubQueryRolesetsCommand();
22+
assertFalse(command.cmaShouldBeUsed(null), "We don't want to use CMA for query rolesets, because " +
23+
"at least in ML 10.0-3, when QRs are submitted via CMA by a user without the security role and " +
24+
"one or more QRs already exist, an exception will be thrown");
25+
26+
27+
QueryRolesetManager mgr = new QueryRolesetManager(userWithRoleBeingTestedClient);
28+
final String payload = "{\n" +
29+
"\t\"role-name\": [\n" +
30+
"\t\t\"view-admin\",\n" +
31+
"\t\t\"flexrep-user\"\n" +
32+
"\t]\n" +
33+
"}\n";
34+
35+
SaveReceipt receipt = command.saveResource(mgr, new CommandContext(new AppConfig(), userWithRoleBeingTestedClient, null), payload);
36+
final String rolesetPath = receipt.getResponse().getHeaders().getLocation().toString();
37+
try {
38+
assertEquals(201, receipt.getResponse().getStatusCodeValue(), "The query roleset should have been created " +
39+
"successfully because data-hub-developer has the add-query-rolesets privilege");
40+
41+
receipt = command.saveResource(mgr, new CommandContext(new AppConfig(), userWithRoleBeingTestedClient, null), payload);
42+
assertNull(receipt, "The receipt object will be null because the Manage API threw an exception, since a " +
43+
"user without the security role can't call POST again on a query roleset, and the data-hub-developer " +
44+
"user doesn't have the security role. And a PUT call can't be made because the GET endpoint for a " +
45+
"roleset doesn't support passing in the role names that constitute a roleset, so there's no way to " +
46+
"figure out the roleset ID which would be required by the PUT call." +
47+
"" +
48+
"So instead of an exception being thrown, check the logging to verify that a message was logged " +
49+
"indicating that the SEC-PERMDENIED exception can be safely ignored if the query roleset has already " +
50+
"been deployed. This is the best we can do in DHF based on ML 10.0-3.");
51+
} finally {
52+
// The lack of an exception from this verifies that the roleset was deleted successfully
53+
userWithRoleBeingTestedClient.delete(rolesetPath);
54+
}
55+
}
56+
}

marklogic-data-hub/src/test/java/com/marklogic/hub/impl/DeployProtectedPathsWhenUpdatingIndexesTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ public void teardown() {
3333

3434
@Test
3535
public void test() {
36-
// Initializing this to ensure that updateIndexes can be called
37-
getDataHubAdminConfig();
36+
runAsDataHubDeveloper();
3837

3938
givenAProtectedPathFile();
4039
dataHub.updateIndexes();

marklogic-data-hub/src/test/java/com/marklogic/hub/security/DataHubDeveloperTest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.node.ObjectNode;
55
import com.marklogic.hub.HubConfig;
6+
import com.marklogic.mgmt.SaveReceipt;
67
import com.marklogic.mgmt.api.database.Database;
78
import com.marklogic.mgmt.api.database.GeospatialElementIndex;
89
import com.marklogic.mgmt.api.security.protectedpath.Permission;
910
import com.marklogic.mgmt.api.security.protectedpath.ProtectedPath;
11+
import com.marklogic.mgmt.api.security.queryroleset.QueryRoleset;
1012
import com.marklogic.mgmt.api.task.Task;
1113
import com.marklogic.mgmt.api.trigger.*;
1214
import com.marklogic.mgmt.resource.alert.AlertActionManager;
1315
import com.marklogic.mgmt.resource.alert.AlertConfigManager;
1416
import com.marklogic.mgmt.resource.alert.AlertRuleManager;
1517
import com.marklogic.mgmt.resource.security.ProtectedPathManager;
18+
import com.marklogic.mgmt.resource.security.QueryRolesetManager;
1619
import com.marklogic.mgmt.resource.tasks.TaskManager;
1720
import com.marklogic.mgmt.resource.temporal.TemporalAxesManager;
1821
import com.marklogic.mgmt.resource.temporal.TemporalCollectionManager;
@@ -24,6 +27,7 @@
2427
import java.io.IOException;
2528
import java.net.URLEncoder;
2629
import java.util.Arrays;
30+
import java.util.List;
2731

2832
import static org.junit.jupiter.api.Assertions.*;
2933

@@ -106,7 +110,31 @@ public void task10CreateProtectedPaths() throws Exception {
106110
} finally {
107111
userWithRoleBeingTestedClient.delete("/manage/v2/protected-paths/" + URLEncoder.encode(pathExpression, "UTF-8") + "?force=true");
108112
assertFalse(adminProtectedPathManager.exists(pathExpression),
109-
"The remove-path privilege should allow the user to delete a protected path");
113+
"The remove-path privilege should allow the user to delete a protected path. Note that in ML 10.0-3, the unprotect-path " +
114+
"privilege is not needed to perform this operation. The Admin UI requires first unprotecting a protected path, but " +
115+
"the Manage API is able to delete a protected path without unprotecting it first.");
116+
}
117+
}
118+
119+
@Test
120+
void task10CreateQueryRolesets() {
121+
final QueryRolesetManager mgr = new QueryRolesetManager(userWithRoleBeingTestedClient);
122+
final List<String> originalRolesetIds = mgr.getAsXml().getListItemIdRefs();
123+
124+
QueryRoleset qr = new QueryRoleset(null);
125+
qr.setRoleName(Arrays.asList("harmonized-reader", "harmonized-updater"));
126+
qr.setApi(userWithRoleBeingTestedApi);
127+
SaveReceipt receipt = mgr.save(qr.getJson());
128+
final String rolesetPath = receipt.getResponse().getHeaders().getLocation().toString();
129+
130+
final List<String> newRolesetIds = mgr.getAsXml().getListItemIdRefs();
131+
try {
132+
assertEquals(originalRolesetIds.size() + 1, newRolesetIds.size(),
133+
"Expected one more roleset to exist");
134+
} finally {
135+
userWithRoleBeingTestedClient.delete(rolesetPath);
136+
final List<String> rolesetIdsAfterDeletion = mgr.getAsXml().getListItemIdRefs();
137+
assertEquals(originalRolesetIds.size(), rolesetIdsAfterDeletion.size());
110138
}
111139
}
112140

0 commit comments

Comments
 (0)