diff --git a/ChangeLog b/ChangeLog index 3c748c11..759bb8ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ +Changes in HQApi 3.3 + Changes in HQApi 3.2 + *) Add support to get and sync platform resources by FQDN. + No corresponding CLI command was explicitly implemented. + *) [HHQ-4058] Also update the agent when syncing platforms *) [HHQ-4060] Add --groupId option to metricData list CLI command. @@ -84,6 +89,24 @@ Changes in HQApi 3.0 *) [HHQ-3144] Add ServerConfigApi to allow manipulation of HQ server config settings. + +Changes in HQApi 2.6 + + *) [HHQ-4151] Add new API to ResourceApi to find a platform Resource + based on the passed resource id. Extended support for this into the + CLI using the --parentPlatform flag to resource list. + + *) [HHQ-4150] Make --count optional when listing alerts via the CLI. + + *) [HHQ-4145] Add --addRole, --removeRole, and --clearRoles options to + command line group syncing. These can only be used with --name. + +Changes in HQApi 2.5 + + *) [HHQ-4133] Perform permission checking on ResourceApi. + + *) Back port AlertApi to 2.x branch. As a part of this change portions of + the new MetricDataApi were also backported to aid in testing. Changes in HQApi 2.4 diff --git a/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceFind_test.java b/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceFind_test.java index 9f1bb120..2e5a72a8 100644 --- a/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceFind_test.java +++ b/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceFind_test.java @@ -32,7 +32,11 @@ import org.hyperic.hq.hqapi1.types.Resource; import org.hyperic.hq.hqapi1.types.ResourcePrototype; import org.hyperic.hq.hqapi1.types.ResourcePrototypeResponse; +import org.hyperic.hq.hqapi1.types.ResourceResponse; import org.hyperic.hq.hqapi1.types.ResourcesResponse; +import org.hyperic.hq.hqapi1.types.User; + +import java.util.List; public class ResourceFind_test extends ResourceTestBase { @@ -132,4 +136,56 @@ public void testFindByDescriptionNoMatches() throws Exception { assertTrue("Found matches for '" + DESC + "'", response.getResource().size() == 0); } + + public void testFindResourceByAgentUnauthorized() throws Exception { + List users = createTestUsers(1); + User user = users.get(0); + ResourceApi api = getApi(user.getName(), TESTUSER_PASSWORD).getResourceApi(); + + // Use admin user to get local agent.. + Agent agent = getRunningAgent(); + + // Test find by agent + ResourcesResponse response = api.getResources(agent, false, false); + hqAssertSuccess(response); + + assertTrue("Found resources with unauthorized user", response.getResource().size() == 0); + + deleteTestUsers(users); + } + + public void testFindResourceByPrototypeUnauthorized() throws Exception { + List users = createTestUsers(1); + User user = users.get(0); + ResourceApi api = getApi(user.getName(), TESTUSER_PASSWORD).getResourceApi(); + + // Use admin user to get local platform.. + Resource localPlatform = getLocalPlatformResource(false, false); + + // Test find by prototype + ResourcesResponse response = api.getResources(localPlatform.getResourcePrototype(), false, false); + hqAssertSuccess(response); + + assertTrue("Found resources with unauthorized user", response.getResource().size() == 0); + + deleteTestUsers(users); + } + + public void testFindResourceByDescriptionUnauthorized() throws Exception { + final String DESC = "Hyperic HQ monitor Agent"; + List users = createTestUsers(1); + User user = users.get(0); + ResourceApi api = getApi(user.getName(), TESTUSER_PASSWORD).getResourceApi(); + + // Use admin user to get local platform.. + Resource localPlatform = getLocalPlatformResource(false, false); + + // Test find by prototype + ResourcesResponse response = api.getResources(DESC, false, false); + hqAssertSuccess(response); + + assertTrue("Found resources with unauthorized user", response.getResource().size() == 0); + + deleteTestUsers(users); + } } diff --git a/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceGet_test.java b/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceGet_test.java index c5ec2827..0d0c5b90 100644 --- a/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceGet_test.java +++ b/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ResourceGet_test.java @@ -209,4 +209,97 @@ private String getFqdn(Resource r) { } return fqdn; } + + public void testGetResourceByIdUnauthorized() throws Exception { + List users = createTestUsers(1); + User user = users.get(0); + ResourceApi api = getApi(user.getName(), TESTUSER_PASSWORD).getResourceApi(); + + // Use admin user to get local platform.. + Resource localPlatform = getLocalPlatformResource(false, false); + + // Test find by ID + ResourceResponse response = api.getResource(localPlatform.getId(), false, false); + hqAssertFailurePermissionDenied(response); + + deleteTestUsers(users); + } + + public void testGetResourceByNameUnauthorized() throws Exception { + List users = createTestUsers(1); + User user = users.get(0); + ResourceApi api = getApi(user.getName(), TESTUSER_PASSWORD).getResourceApi(); + + // Use admin user to get local platform.. + Resource localPlatform = getLocalPlatformResource(false, false); + + // Test find by name + ResourceResponse response = api.getPlatformResource(localPlatform.getName(), false, false); + hqAssertFailurePermissionDenied(response); + + deleteTestUsers(users); + } + + public void testGetPlatformResourceByPlatform() throws Exception { + + ResourceApi api = getApi().getResourceApi(); + Resource localPlatform = getLocalPlatformResource(false, false); + + ResourceResponse response = api.getPlatformResource(localPlatform.getId(), false, false); + hqAssertSuccess(response); + + assertEquals("Platform ids not equal", localPlatform.getId(), + response.getResource().getId()); + } + + public void testGetPlatformResourceByServer() throws Exception { + + ResourceApi api = getApi().getResourceApi(); + Resource localPlatform = getLocalPlatformResource(false, true); + + List servers = localPlatform.getResource(); + assertTrue("No servers found for " + localPlatform.getName(), + servers.size() > 0); + + Resource server = servers.get(0); + + ResourceResponse response = api.getPlatformResource(server.getId(), false, false); + hqAssertSuccess(response); + + assertEquals("Platform ids not equal", localPlatform.getId(), + response.getResource().getId()); + } + + public void testGetPlatformResourceByService() throws Exception { + + ResourceApi api = getApi().getResourceApi(); + Resource localPlatform = getLocalPlatformResource(false, true); + + List servers = localPlatform.getResource(); + assertTrue("No servers found for " + localPlatform.getName(), + servers.size() > 0); + Resource service = null; + for (Resource server : servers) { + if (server.getResource().size() > 0) { + service = server.getResource().get(0); + break; + } + } + + assertNotNull("Unable to find a service for platform " + localPlatform.getName(), + service); + + ResourceResponse response = api.getPlatformResource(service.getId(), false, false); + hqAssertSuccess(response); + + assertEquals("Platform ids not equal", localPlatform.getId(), + response.getResource().getId()); + } + + public void testGetPlatformResourceInvalidId() throws Exception { + ResourceApi api = getApi().getResourceApi(); + + ResourceResponse response = api.getPlatformResource(Integer.MAX_VALUE, false, false); + hqAssertFailureObjectNotFound(response); + } } \ No newline at end of file diff --git a/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ServerConfigSet_test.java b/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ServerConfigSet_test.java index ebeba896..5bbe7dfd 100644 --- a/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ServerConfigSet_test.java +++ b/hqapi1-integration-tests/src/test/java/org/hyperic/hq/hqapi1/test/ServerConfigSet_test.java @@ -36,8 +36,9 @@ public void testSetSingleConfig() throws Exception { ServerConfigResponse configResponse = sApi.getConfig(); hqAssertSuccess(configResponse); + String configName = "HQ_ALERTS_ENABLED"; ServerConfig c = new ServerConfig(); - c.setKey("HQ_ALERTS_ENABLED"); + c.setKey(configName); c.setValue("false"); StatusResponse response = sApi.setConfig(c); @@ -49,8 +50,8 @@ public void testSetSingleConfig() throws Exception { List configs = configResponse.getServerConfig(); for (ServerConfig config : configs) { - if (config.getKey().equals("HQ_ALERTS_ENABLED")) { - assertTrue("HQ_ALERTS_ENABLED was not false", + if (config.getKey().equals(configName)) { + assertTrue(configName + " was not false", config.getValue().equals("false")); config.setValue("true"); // Re-enable } @@ -58,15 +59,14 @@ public void testSetSingleConfig() throws Exception { response = sApi.setConfig(configs); hqAssertSuccess(response); + assertTrue(configName + " was not true", + getConfigValue(configName).equals("true")); } public void testSetUnknownConfig() throws Exception { ServerConfigApi sApi = getApi().getServerConfigApi(); - ServerConfigResponse configResponse = sApi.getConfig(); - hqAssertSuccess(configResponse); - ServerConfig c = new ServerConfig(); c.setKey("HQ_UNKNOWN_CONFIG"); c.setValue("false"); @@ -79,9 +79,6 @@ public void testSetRestrictedConfig() throws Exception { ServerConfigApi sApi = getApi().getServerConfigApi(); - ServerConfigResponse configResponse = sApi.getConfig(); - hqAssertSuccess(configResponse); - ServerConfig c = new ServerConfig(); c.setKey("CAM_GUIDE_ENABLED"); c.setValue("false"); @@ -89,6 +86,156 @@ public void testSetRestrictedConfig() throws Exception { StatusResponse response = sApi.setConfig(c); hqAssertFailureOperationDenied(response); } + + public void testSetConfigAtMin() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + String configName = "CAM_DATA_PURGE_RAW"; + String serverValue = getConfigValue(configName); + + long minValue = 24 * 60 * 60 * 1000L; + ServerConfig c = new ServerConfig(); + c.setKey(configName); + c.setValue(Long.toString(minValue)); + + StatusResponse response = sApi.setConfig(c); + hqAssertSuccess(response); + assertTrue(configName + " was not " + minValue, + getConfigValue(configName).equals(Long.toString(minValue))); + + // reset to previous value + c.setValue(serverValue); + response = sApi.setConfig(c); + hqAssertSuccess(response); + assertTrue(configName + " was not " + serverValue, + getConfigValue(configName).equals(serverValue)); + + } + + public void testSetConfigBelowMin() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + ServerConfig c = new ServerConfig(); + c.setKey("CAM_DATA_PURGE_RAW"); + c.setValue("1"); + + StatusResponse response = sApi.setConfig(c); + hqAssertFailureInvalidParameters(response); + } + + public void testSetConfigAtMax() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + String configName = "ALERT_PURGE"; + String serverValue = getConfigValue(configName); + + long maxValue = 9999 * 24 * 60 * 60 * 1000L; + ServerConfig c = new ServerConfig(); + c.setKey(configName); + c.setValue(Long.toString(maxValue)); + + StatusResponse response = sApi.setConfig(c); + hqAssertSuccess(response); + assertTrue(configName + " was not " + maxValue, + getConfigValue(configName).equals(Long.toString(maxValue))); + + // reset to previous value + c.setValue(serverValue); + response = sApi.setConfig(c); + hqAssertSuccess(response); + assertTrue(configName + " was not " + serverValue, + getConfigValue(configName).equals(serverValue)); + + } + + public void testSetConfigAboveMax() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + long maxValue = 60 * 60 * 1000L * 99999; + ServerConfig c = new ServerConfig(); + c.setKey("ALERT_PURGE"); + c.setValue(Long.toString(maxValue)); + + StatusResponse response = sApi.setConfig(c); + hqAssertFailureInvalidParameters(response); + } + + public void testSetConfigValidMultiple() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + String configName = "EVENT_LOG_PURGE"; + String serverValue = getConfigValue(configName); + + long multipleValue = 50 * 24 * 60 * 60 * 1000L; + ServerConfig c = new ServerConfig(); + c.setKey(configName); + c.setValue(Long.toString(multipleValue)); + + StatusResponse response = sApi.setConfig(c); + hqAssertSuccess(response); + assertTrue(configName + " was not " + multipleValue, + getConfigValue(configName).equals(Long.toString(multipleValue))); + + // reset to previous value + c.setValue(serverValue); + response = sApi.setConfig(c); + hqAssertSuccess(response); + assertTrue(configName + " was not " + serverValue, + getConfigValue(configName).equals(serverValue)); + + } + + public void testSetConfigInvalidMultiple() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + ServerConfigResponse configResponse = sApi.getConfig(); + hqAssertSuccess(configResponse); + + long value = (2 * 60 * 60 * 1000L) + 1; + ServerConfig c = new ServerConfig(); + c.setKey("CAM_DATA_MAINTENANCE"); + c.setValue(Long.toString(value)); + + StatusResponse response = sApi.setConfig(c); + hqAssertFailureInvalidParameters(response); + } + + public void testSetConfigInvalidNumber() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + ServerConfigResponse configResponse = sApi.getConfig(); + hqAssertSuccess(configResponse); + + String value = "invalid"; + ServerConfig c = new ServerConfig(); + c.setKey("EVENT_LOG_PURGE"); + c.setValue(value); + + StatusResponse response = sApi.setConfig(c); + hqAssertFailureInvalidParameters(response); + } + + public void testSetConfigEmptyString() throws Exception { + + ServerConfigApi sApi = getApi().getServerConfigApi(); + + ServerConfigResponse configResponse = sApi.getConfig(); + hqAssertSuccess(configResponse); + + ServerConfig c = new ServerConfig(); + c.setKey("CAM_DATA_MAINTENANCE"); + c.setValue(""); + + StatusResponse response = sApi.setConfig(c); + hqAssertFailureInvalidParameters(response); + } public void testSetConfigEmpty() throws Exception { @@ -131,4 +278,25 @@ public void testSetConfigInvalidUser() throws Exception { api.getUserApi().deleteUser(userCreateResponse.getUser().getId()); hqAssertSuccess(deleteResponse); } + + /** + * TODO: The api should directly support getting config by name + */ + private String getConfigValue(String configName) throws Exception { + ServerConfigApi sApi = getApi().getServerConfigApi(); + + ServerConfigResponse configResponse = sApi.getConfig(); + hqAssertSuccess(configResponse); + + String configValue = null; + List configs = configResponse.getServerConfig(); + for (ServerConfig config : configs) { + if (config.getKey().equals(configName)) { + configValue = config.getValue(); + break; + } + } + + return configValue; + } } diff --git a/hqapi1-plugin/src/main/groovy/app/AlertController.groovy b/hqapi1-plugin/src/main/groovy/app/AlertController.groovy index c2c5ff0a..cb34f94f 100644 --- a/hqapi1-plugin/src/main/groovy/app/AlertController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/AlertController.groovy @@ -9,6 +9,7 @@ import org.hyperic.hq.escalation.shared.EscalationManager; import org.hyperic.hq.context.Bootstrap; import org.hyperic.hq.events.server.session.ClassicEscalationAlertType import org.hyperic.hq.authz.shared.PermissionException +import org.hyperic.hq.authz.shared.PermissionException public class AlertController extends ApiController { @@ -190,6 +191,8 @@ public class AlertController extends ApiController { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, "Invalid severity " + sev) + } catch (PermissionException e) { + // User cannot see this resource, continue with empty list } catch (Throwable t) { failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, t.getMessage()) diff --git a/hqapi1-plugin/src/main/groovy/app/AlertdefinitionController.groovy b/hqapi1-plugin/src/main/groovy/app/AlertdefinitionController.groovy index b1545162..37618249 100644 --- a/hqapi1-plugin/src/main/groovy/app/AlertdefinitionController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/AlertdefinitionController.groovy @@ -270,11 +270,17 @@ public class AlertdefinitionController extends ApiController { def postRequest = new XmlParser().parseText(getPostData()) def resources = [] for (xmlDef in postRequest['Resource']) { - def resource = getResource(xmlDef.'@id'?.toInteger()) - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Unable to find resource with id " + xmlDef.'@id') - break + try { + def resource = getResource(xmlDef.'@id'?.toInteger()) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find resource with id " + xmlDef.'@id') + break + } + + resources.add(resource) + } catch (PermissionException e) { + // Ignore resources the user cannot see } } @@ -352,7 +358,13 @@ public class AlertdefinitionController extends ApiController { } } } else if (resourceId != null) { - def resource = getResource(resourceId) + def resource = null + try { + resource = getResource(resourceId) + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) + } + if (!resource) { failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, "Resource with id = " + resourceId + @@ -506,11 +518,15 @@ public class AlertdefinitionController extends ApiController { } else if (xmlDef['Resource'].size() == 1) { typeBased = false def rid = xmlDef['Resource'][0].'@id'?.toInteger() - resource = getResource(rid) - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Cannot find resource with " + - "id " + id) + try { + resource = getResource(rid) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Cannot find resource with " + + "id " + id) + } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } } else if (xmlDef['ResourcePrototype'].size() == 1) { typeBased = true @@ -680,7 +696,12 @@ public class AlertdefinitionController extends ApiController { it.'@key' == 'action' }?.'@value' - def cResource = getResource(rId) + def cResource = null + try { + cResource = getResource(rId) + } catch (PermissionException e) { + // Ignore + } if (cResource != null && action != null) { def actions = cResource.getControlActions(user) if (!actions.find { it == action }) { diff --git a/hqapi1-plugin/src/main/groovy/app/ApiController.groovy b/hqapi1-plugin/src/main/groovy/app/ApiController.groovy index c91ae56b..1b7b8a0f 100644 --- a/hqapi1-plugin/src/main/groovy/app/ApiController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/ApiController.groovy @@ -50,6 +50,24 @@ class ApiController extends BaseController { return null } + /** + * Checks view permission for the passed in resource. + * @throws PermissionException if permission is not granted, otherwise + * the passed in Resource is returned. + */ + protected checkViewPermission(resource) { + if (resource.isPlatform()) { + return resource.toPlatform().checkPerms(operation: 'view', user:user) + } else if (resource.isServer()) { + return resource.toServer().checkPerms(operation: 'view', user:user) + } else if (resource.isService()) { + return resource.toService().checkPerms(operation: 'view', user:user) + } else { + log.warn("Unhandled resource type " + resource.prototype) + return resource // Group permission checking.. + } + } + /** * Get the resource based on the given id. If the resource is not found, * null is returned. @@ -69,10 +87,12 @@ class ApiController extends BaseController { try { resource.name // Check the object really exists resource.entityId // Check the object is an appdef object - return resource } catch (Throwable t) { return null } + + // ResourceHelper does not check permissions + return checkViewPermission(resource) } } diff --git a/hqapi1-plugin/src/main/groovy/app/ControlController.groovy b/hqapi1-plugin/src/main/groovy/app/ControlController.groovy index 3bdd4727..9deaa4c8 100644 --- a/hqapi1-plugin/src/main/groovy/app/ControlController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/ControlController.groovy @@ -29,18 +29,18 @@ class ControlController extends ApiController { def failureXml = null def resourceId = params.getOne('resourceId')?.toInteger() - def resource = getResource(resourceId) - def actions = [] - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Resource id " + resourceId + " not found") - } else { - try { + try { + def resource = getResource(resourceId) + + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Resource id " + resourceId + " not found") + } else { actions = resource.getControlActions(user) - } catch (PermissionException e) { - failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } renderXml() { @@ -62,17 +62,17 @@ class ControlController extends ApiController { def failureXml = null def resourceId = params.getOne('resourceId')?.toInteger() - def resource = getResource(resourceId) def history = [] - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Resource id " + resourceId + " not found") - } else { - try { - history = resource.getControlHistory(user) - } catch (PermissionException e) { - failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) + try { + def resource = getResource(resourceId) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Resource id " + resourceId + " not found") + } else { + history = resource.getControlHistory(user) } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } renderXml() { @@ -97,23 +97,27 @@ class ControlController extends ApiController { if (arguments == null) { arguments = "" } - def resource = getResource(resourceId) - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Resource id " + resourceId + " not found") - } else if (!action) { + + if (!action) { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, "No action given") } else { try { - def actions = resource.getControlActions(user) - if (!actions.contains(action)) { - failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, - "Resource type " + resource.prototype.name + - " does not support action " + action + - ". Possible values: " + actions) - } else { - resource.runAction(user, action, arguments) + def resource = getResource(resourceId) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Resource id " + resourceId + " not found") + } else { + + def actions = resource.getControlActions(user) + if (!actions.contains(action)) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Resource type " + resource.prototype.name + + " does not support action " + action + + ". Possible values: " + actions) + } else { + resource.runAction(user, action, arguments) + } } } catch (org.hyperic.hq.product.PluginException e) { failureXml = getFailureXML(ErrorCode.NOT_SUPPORTED, diff --git a/hqapi1-plugin/src/main/groovy/app/GroupController.groovy b/hqapi1-plugin/src/main/groovy/app/GroupController.groovy index 35773e19..91487ef2 100644 --- a/hqapi1-plugin/src/main/groovy/app/GroupController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/GroupController.groovy @@ -255,7 +255,12 @@ class GroupController extends ApiController { for (xmlResource in xmlGroup['Resource']) { log.debug("Found resource " + xmlResource.'@name') - def resource = getResource(xmlResource.'@id'?.toInteger()); + def resource = null + try { + resource = getResource(xmlResource.'@id'?.toInteger()); + } catch (PermissionException e) { + // Ignore resources the user cannot see. + } if (!resource) { failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, diff --git a/hqapi1-plugin/src/main/groovy/app/MetricController.groovy b/hqapi1-plugin/src/main/groovy/app/MetricController.groovy index 8ed3c8e9..772262c0 100644 --- a/hqapi1-plugin/src/main/groovy/app/MetricController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/MetricController.groovy @@ -3,6 +3,7 @@ import org.hyperic.hq.hqu.rendit.BaseController import org.hyperic.hq.hqapi1.ErrorCode; import org.hyperic.hq.zevents.ZeventManager; import org.hyperic.hq.appdef.server.session.ResourceRefreshZevent; +import org.hyperic.hq.authz.shared.PermissionException class MetricController extends ApiController { @@ -137,21 +138,25 @@ class MetricController extends ApiController { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, "Resource id not given") } else { - def res = getResource(resourceId) - if (!res) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Unable to find resource id=" + resourceId) - } else { - try { - if (enabled != null && enabled) { - metrics = res.enabledMetrics - } else { - metrics = res.metrics - } - } catch (Exception e) { - log.error("UnexpectedError: " + e.getMessage(), e) - failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR) + try { + def res = getResource(resourceId) + if (!res) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find resource id=" + resourceId) + } else { + try { + if (enabled != null && enabled) { + metrics = res.enabledMetrics + } else { + metrics = res.metrics + } + } catch (Exception e) { + log.error("UnexpectedError: " + e.getMessage(), e) + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR) + } } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } } @@ -506,7 +511,18 @@ class MetricController extends ApiController { // Validate the resources exist. def results = [] for (String id : ids) { - def resource = getResource(id.toInteger()) + def resource = null + try { + resource = getResource(id.toInteger()) + } catch (PermissionException e) { + renderXml() { + MetricsDataResponse() { + out << getFailureXML(ErrorCode.PERMISSION_DENIED) + } + } + return + } + if (!resource) { renderXml() { MetricsDataResponse() { @@ -560,7 +576,18 @@ class MetricController extends ApiController { def zevents = [] for (res in xmlResources) { - def resource = getResource(res.'@id'.toInteger()) + def resource = null + try { + resource = getResource(res.'@id'.toInteger()) + } catch (PermissionException e) { + renderXml() { + StatusResponse() { + out << getFailureXML(ErrorCode.PERMISSION_DENIED) + } + } + return + } + if (!resource) { renderXml() { StatusResponse() { diff --git a/hqapi1-plugin/src/main/groovy/app/ResourceController.groovy b/hqapi1-plugin/src/main/groovy/app/ResourceController.groovy index 50444371..3a1ed14f 100644 --- a/hqapi1-plugin/src/main/groovy/app/ResourceController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/ResourceController.groovy @@ -360,39 +360,49 @@ class ResourceController extends ApiController { def id = params.getOne("id")?.toInteger() def platformName = params.getOne("platformName") def fqdn = params.getOne("fqdn") + def platformId = params.getOne("platformId")?.toInteger() boolean children = params.getOne("children", "false").toBoolean() boolean verbose = params.getOne("verbose", "false").toBoolean() def resource = null def failureXml - if (!id && !platformName && !fqdn) { + if (!id && !platformName && !fqdn && !platformId) { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS) } else { - if (id) { - resource = getResource(id) - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Resource id=" + id + - " not found") - } - } else if (platformName) { - resource = resourceHelper.find('platform':platformName) - if (!resource) { - failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, - "Platform '" + platformName + - "' not found") - } - } else if (fqdn) { - try { + try { + if (id) { + resource = getResource(id) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Resource id=" + id + + " not found") + } + } else if (platformId) { + def wantedResource = getResource(platformId) + if (!wantedResource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Resource id=" + platformId + + " not found") + } else { + resource = wantedResource.platform + } + } else if (platformName) { + resource = resourceHelper.find('platform': platformName) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Platform '" + platformName + + "' not found") + } + } else if (fqdn) { resource = resourceHelper.find('byFqdn':fqdn) if (!resource) { failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, "Platform fqdn='" + fqdn + "' not found") } - } catch (PermissionException pe) { - failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) } } @@ -429,19 +439,42 @@ class ResourceController extends ApiController { " not found") } else { def platforms = agent.platforms - resources = platforms*.resource + for (platform in platforms) { + try { + resources.add(platform.checkPerms(operation: 'view', user:user)) + } catch (PermissionException e) { + log.debug("Ignoring platform " + platform.name + " due to permissions.") + } + } } } else if (prototype) { - resources = resourceHelper.find('byPrototype': prototype) + def matching = resourceHelper.find('byPrototype': prototype) + + for (resource in matching) { + try { + resources.add(checkViewPermission(resource)) + } catch (PermissionException e) { + log.debug("Ignoring resource " + resource.name + " due to permissions") + } + } } else if (description) { // TODO: Move into HQ. - def session = org.hyperic.hq.hibernate.SessionManager.currentSession() - resources.addAll(session.createQuery( + def matching = [] + def session = org.hyperic.hq.hibernate.SessionManager.currentSession() + matching.addAll(session.createQuery( "select p.resource from Platform p where p.description like '%${description}%'").list()) - resources.addAll(session.createQuery( + matching.addAll(session.createQuery( "select s.resource from Server s where s.description like '%${description}%'").list()) - resources.addAll(session.createQuery( + matching.addAll(session.createQuery( "select s.resource from Service s where s.description like '%${description}%'").list()) + + for (resource in matching) { + try { + resources.add(checkViewPermission(resource)) + } catch (PermissionException e) { + log.debug("Ignoring resource " + resource.name + " due to permissions") + } + } } else { // Shouldn't happen failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS) diff --git a/hqapi1-plugin/src/main/groovy/app/ServerconfigController.groovy b/hqapi1-plugin/src/main/groovy/app/ServerconfigController.groovy index b7de3633..6e2abb44 100644 --- a/hqapi1-plugin/src/main/groovy/app/ServerconfigController.groovy +++ b/hqapi1-plugin/src/main/groovy/app/ServerconfigController.groovy @@ -2,6 +2,7 @@ import org.hyperic.hq.appdef.shared.ServiceManager; import org.hyperic.hq.common.shared.ServerConfigManager; import org.hyperic.hq.context.Bootstrap; +import org.hyperic.hq.common.shared.HQConstants import org.hyperic.hq.hqapi1.ErrorCode class ServerconfigController extends ApiController { @@ -80,6 +81,7 @@ class ServerconfigController extends ApiController { try { if (!failureXml) { if (update) { + validateConfig(props) _serverMan.setConfig(user, props) } else { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, @@ -102,4 +104,56 @@ class ServerconfigController extends ApiController { } } } -} \ No newline at end of file + + private validateConfig(props) { + // TODO: Need to consolidate data validation logic. + // Currently, the HQ server validates it at the Struts layer. + + def oneHourInMillis = 60 * 60 * 1000L + + for (key in props.keySet()) { + def minVal = null + def maxVal = null + def multiple = null + + if (key.equals(HQConstants.DataPurgeRaw)) { + minVal = 24*oneHourInMillis + maxVal = 7*24*oneHourInMillis + multiple = 24*oneHourInMillis + } else if (key.equals(HQConstants.DataMaintenance)) { + minVal = oneHourInMillis + maxVal = 9999*oneHourInMillis + multiple = oneHourInMillis + } else if (key.equals(HQConstants.AlertPurge) + || key.equals(HQConstants.EventLogPurge)) { + minVal = 24*oneHourInMillis + maxVal = 9999*24*oneHourInMillis + multiple = 24*oneHourInMillis + } + + if (minVal && maxVal && multiple) { + def newVal = props.getProperty(key) + def longVal + + try { + longVal = Long.parseLong(newVal) + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + key + " is an invalid number") + } + + if (longVal < minVal || longVal > maxVal) { + throw new IllegalArgumentException( + key + " must be between " + minVal + + " and " + maxVal + " milliseconds") + } + + if (longVal % multiple != 0) { + throw new IllegalArgumentException( + key + " must be a multiple of " + + multiple + " milliseconds") + } + } + } + } +} diff --git a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/ResourceApi.java b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/ResourceApi.java index cc62f0bd..02174264 100644 --- a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/ResourceApi.java +++ b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/ResourceApi.java @@ -325,7 +325,34 @@ public ResourceResponse getPlatformResourceByFqdn(String fqdn, boolean verbose, return doGet("resource/get.hqu", params, new XmlResponseHandler(ResourceResponse.class)); } - + + /** + * Get the platform {@link Resource} for the given resource id. + * + * @param id The resource id to query. + * @param verbose Flag to indicate whether {@link org.hyperic.hq.hqapi1.types.ResourceConfig} + * and {@link org.hyperic.hq.hqapi1.types.ResourceProperty} information will + * be included. + * @param children Flag to control whether child resources of this resource + * will be included. + * @return On {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * the Resource is returned via + * {@link org.hyperic.hq.hqapi1.types.ResourceResponse#getResource()}. + * + * @throws java.io.IOException If a network error occurs while making the request. + */ + public ResourceResponse getPlatformResource(int id, boolean verbose, + boolean children) + throws IOException + { + Map params = new HashMap(); + params.put("platformId", new String[] { Integer.toString(id) }); + params.put("verbose", new String[] { Boolean.toString(verbose) }); + params.put("children", new String[] { Boolean.toString(children)}); + return doGet("resource/get.hqu", params, + new XmlResponseHandler(ResourceResponse.class)); + } + /** * Find the platform {@link Resource}s serviced by the given * {@link org.hyperic.hq.hqapi1.types.Agent}. diff --git a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/AlertCommand.java b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/AlertCommand.java index 8b820680..4d1fea91 100644 --- a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/AlertCommand.java +++ b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/AlertCommand.java @@ -106,7 +106,7 @@ private void list(String[] args) throws Exception { withRequiredArg().ofType(Long.class); p.accepts(OPT_END, "If specified, the end time in epoch-millis."). withRequiredArg().ofType(Long.class); - p.accepts(OPT_COUNT, "The maximum number of alerts to return.") + p.accepts(OPT_COUNT, "If specified, the maximum number of alerts to return.") .withRequiredArg().ofType(Integer.class); p.acceptsAll(Arrays.asList(OPT_INESC), "If specified, only return alerts which are in " + @@ -134,8 +134,12 @@ private void list(String[] args) throws Exception { end = System.currentTimeMillis(); } - // --count is required - Integer count = (Integer)getRequired(options, OPT_COUNT); + Integer count; + if (options.has(OPT_COUNT)) { + count = (Integer)getRequired(options, OPT_COUNT); + } else { + count = Integer.MAX_VALUE; + } Boolean inEsc = options.has(OPT_INESC[0]); Boolean notFixed = options.has(OPT_NOTFIXED[0]); diff --git a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/GroupCommand.java b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/GroupCommand.java index b6c8cf22..c9d046b1 100644 --- a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/GroupCommand.java +++ b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/GroupCommand.java @@ -34,6 +34,8 @@ import org.hyperic.hq.hqapi1.XmlUtil; import org.hyperic.hq.hqapi1.types.Group; import org.hyperic.hq.hqapi1.types.GroupsResponse; +import org.hyperic.hq.hqapi1.types.Role; +import org.hyperic.hq.hqapi1.types.RoleResponse; import org.hyperic.hq.hqapi1.types.StatusResponse; import org.hyperic.hq.hqapi1.types.GroupResponse; import org.hyperic.hq.hqapi1.types.ResponseStatus; @@ -76,6 +78,9 @@ public class GroupCommand extends AbstractCommand { private static String OPT_DESC = "description"; private static String OPT_CHILDREN = "children"; private static String OPT_DELETE = "delete"; + private static String OPT_ADDROLE = "addRole"; + private static String OPT_REMOVEROLE = "removeRole"; + private static String OPT_CLEARROLES = "clearRoles"; private void printUsage() { System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); @@ -175,8 +180,16 @@ private void sync(String[] args) throws Exception { withRequiredArg().ofType(String.class); p.accepts(OPT_CHILDREN, "If specified, include child resources of the " + "specified prototype and regex"); - p.accepts(OPT_DELETE, "If specifed, remove the specified resources from " + + p.accepts(OPT_DELETE, "If specified, remove the specified resources from " + "the given group"); + p.accepts(OPT_ADDROLE, "If specified, add the given role to this group. " + + "This option can only be used with --" + OPT_NAME). + withRequiredArg().ofType(String.class); + p.accepts(OPT_REMOVEROLE, "If specified, remove the given role from this group. " + + "This option can only be used with --" + OPT_NAME). + withRequiredArg().ofType(String.class); + p.accepts(OPT_CLEARROLES, "If specified, remove all roles from this group. " + + "This option can only be used with --" + OPT_NAME); OptionSet options = getOptions(p, args); @@ -214,6 +227,48 @@ private Map getFlattenResources(Collection resources return result; } + private void syncRoles(HQApi api, OptionSet s) throws Exception + { + // Required arguments + String name = (String)getRequired(s, OPT_NAME); + + // Check for existing group + GroupResponse groupResponse = api.getGroupApi().getGroup(name); + checkSuccess(groupResponse); + Group group = groupResponse.getGroup(); + String msg; + + if (s.has(OPT_CLEARROLES)) { + System.out.println(name + ": Clearing " + group.getRole().size() + " roles"); + group.getRole().clear(); + } else if (s.has(OPT_ADDROLE)) { + String roleName = (String)s.valueOf(OPT_ADDROLE); + RoleResponse roleResponse = api.getRoleApi().getRole(roleName); + checkSuccess(roleResponse); + group.getRole().add(roleResponse.getRole()); + System.out.println(name + ": Adding role " + roleResponse.getRole().getName()); + } else if (s.has(OPT_REMOVEROLE)) { + String roleName = (String)s.valueOf(OPT_REMOVEROLE); + RoleResponse roleResponse = api.getRoleApi().getRole(roleName); + checkSuccess(roleResponse); + for (Iterator it = group.getRole().iterator(); it.hasNext();) { + Role r = (Role)it.next(); + if (r.getName().equals(roleResponse.getRole().getName())) { + it.remove(); + } + } + System.out.println(name + ": Removing role " + roleResponse.getRole().getName()); + } else { + throw new IllegalArgumentException("Invalid role options"); + } + + GroupResponse response = api.getGroupApi().updateGroup(group); + checkSuccess(response); + String roleText = response.getGroup().getRole().size() == 1 ? "role" : "roles"; + System.out.println(name + ": Success (now contains " + response.getGroup().getRole().size() + + " " + roleText + ")"); + } + private void syncViaCommandLineArgs(OptionSet s) throws Exception { // Required args @@ -231,8 +286,13 @@ private void syncViaCommandLineArgs(OptionSet s) throws Exception HQApi api = getApi(s); - Map resources = new HashMap(); + // Command line role syncing is handled separately + if (s.has(OPT_CLEARROLES) || s.has(OPT_ADDROLE) || s.has(OPT_REMOVEROLE)) { + syncRoles(api, s); + return; + } + Map resources = new HashMap(); if (prototype != null && platform != null) { System.err.println("Only one of " + OPT_PROTOTYPE + " or " + OPT_PLATFORM + " is allowed."); @@ -336,7 +396,7 @@ private void syncViaCommandLineArgs(OptionSet s) throws Exception } } } else { - // TODO: could be more efficent here, server side will prune dups + // TODO: could be more efficient here, server side will prune dups group.getResource().addAll(flattenedResources.values()); } diff --git a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/ResourceCommand.java b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/ResourceCommand.java index 25d4272b..46a03114 100644 --- a/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/ResourceCommand.java +++ b/hqapi1/src/main/java/org/hyperic/hq/hqapi1/tools/ResourceCommand.java @@ -80,6 +80,7 @@ public class ResourceCommand extends AbstractCommand { private static String OPT_AGENT_ID = "agentId"; private static String OPT_BATCH_SIZE = "batchSize"; private static String OPT_TO = "to"; + private static String OPT_PARENT_PLATFORM = "parentPlatform"; private void printUsage() { System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); @@ -133,6 +134,8 @@ private void list(String[] args) throws Exception { p.accepts(OPT_VERBOSE, "Include resource configuration and properties"); p.accepts(OPT_CHILDREN, "Include child resources"); + p.accepts(OPT_PARENT_PLATFORM, "Return the parent platform Resource. " + + "Can only be used with --" + OPT_ID); OptionSet options = getOptions(p, args); @@ -181,7 +184,13 @@ private void list(String[] args) throws Exception { resources.getResource().add(resource.getResource()); } else if (options.has(OPT_ID)) { Integer id = (Integer)options.valueOf(OPT_ID); - ResourceResponse resource = resourceApi.getResource(id, verbose, children); + ResourceResponse resource; + if (options.has(OPT_PARENT_PLATFORM)) { + resource = resourceApi.getPlatformResource(id, verbose, children); + } else { + resource = resourceApi.getResource(id, verbose, children); + } + checkSuccess(resource); resources = new ResourcesResponse();