You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2016/02/10 15:09:01 UTC
[3/4] brooklyn-server git commit: Add entitlements checks for
sensor/config lookup
Add entitlements checks for sensor/config lookup
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/a0557178
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/a0557178
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/a0557178
Branch: refs/heads/master
Commit: a0557178d92efbad7b8f176b84b3f4d56da804d3
Parents: bb8980a
Author: Aled Sage <al...@gmail.com>
Authored: Tue Feb 9 17:27:40 2016 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Feb 10 12:42:52 2016 +0000
----------------------------------------------------------------------
.../core/mgmt/entitlement/Entitlements.java | 18 ++-
.../mgmt/entitlement/EntityEntitlementTest.java | 4 +
.../rest/resources/EntityConfigResource.java | 73 ++++++++----
.../brooklyn/rest/resources/SensorResource.java | 55 +++++++--
.../AbstractRestApiEntitlementsTest.java | 111 +++++++++++++++++++
.../AuthenticateAnyoneSecurityProvider.java | 41 +++++++
.../EntityConfigApiEntitlementsTest.java | 103 +++++++++++++++++
.../entitlement/SensorApiEntitlementsTest.java | 108 ++++++++++++++++++
.../entitlement/ServerApiEntitlementsTest.java | 34 ++++++
.../StaticDelegatingEntitlementManager.java | 37 +++++++
10 files changed, 554 insertions(+), 30 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
index 6c281fc..d3f9687 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
@@ -66,6 +66,7 @@ public class Entitlements {
public static EntitlementClass<Entity> SEE_ENTITY = new BasicEntitlementClassDefinition<Entity>("entity.see", Entity.class);
public static EntitlementClass<EntityAndItem<String>> SEE_SENSOR = new BasicEntitlementClassDefinition<EntityAndItem<String>>("sensor.see", EntityAndItem. typeToken(String.class));
+ public static EntitlementClass<EntityAndItem<String>> SEE_CONFIG = new BasicEntitlementClassDefinition<EntityAndItem<String>>("config.see", EntityAndItem. typeToken(String.class));
// string is effector name; argument may be a map or a list, depending how the args were supplied
public static EntitlementClass<EntityAndItem<StringAndArgument>> INVOKE_EFFECTOR = new BasicEntitlementClassDefinition<EntityAndItem<StringAndArgument>>("effector.invoke", EntityAndItem.typeToken(StringAndArgument.class));
public static EntitlementClass<Entity> MODIFY_ENTITY = new BasicEntitlementClassDefinition<Entity>("entity.modify", Entity.class);
@@ -274,6 +275,7 @@ public class Entitlements {
return "Entitlements.allowing(" + permission + " -> " + test + ")";
}
}
+
public static EntitlementManager seeNonSecretSensors() {
return allowing(SEE_SENSOR, new Predicate<EntityAndItem<String>>() {
@Override
@@ -288,13 +290,27 @@ public class Entitlements {
});
}
+ public static EntitlementManager seeNonSecretConfig() {
+ return allowing(SEE_CONFIG, new Predicate<EntityAndItem<String>>() {
+ @Override
+ public boolean apply(EntityAndItem<String> input) {
+ if (input == null) return false;
+ return !Sanitizer.IS_SECRET_PREDICATE.apply(input.getItem());
+ }
+ @Override
+ public String toString() {
+ return "Predicates.nonSecret";
+ }
+ });
+ }
}
/** allow read-only */
public static EntitlementManager readOnly() {
return FineGrainedEntitlements.anyOf(
FineGrainedEntitlements.allowing(SEE_ENTITY),
- FineGrainedEntitlements.seeNonSecretSensors()
+ FineGrainedEntitlements.seeNonSecretSensors(),
+ FineGrainedEntitlements.seeNonSecretConfig()
);
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/core/src/test/java/org/apache/brooklyn/core/mgmt/entitlement/EntityEntitlementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/entitlement/EntityEntitlementTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/entitlement/EntityEntitlementTest.java
index 933d117..6a0a8d0 100644
--- a/core/src/test/java/org/apache/brooklyn/core/mgmt/entitlement/EntityEntitlementTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/entitlement/EntityEntitlementTest.java
@@ -70,6 +70,7 @@ public class EntityEntitlementTest {
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+ Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_CONFIG, EntityAndItem.of(app, "any-config")));
// and can invoke methods, without any user/login registered
confirmEffectorEntitlement(true);
confirmSensorEntitlement(true);
@@ -83,6 +84,7 @@ public class EntityEntitlementTest {
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+ Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_CONFIG, EntityAndItem.of(app, "any-config")));
confirmEffectorEntitlement(true);
confirmSensorEntitlement(true);
@@ -121,6 +123,7 @@ public class EntityEntitlementTest {
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+ Assert.assertTrue(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_CONFIG, EntityAndItem.of(app, "any-config")));
confirmEffectorEntitlement(false);
confirmSensorEntitlement(true);
@@ -134,6 +137,7 @@ public class EntityEntitlementTest {
Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_ENTITY, app));
Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, StringAndArgument.of("any-eff", null))));
Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor")));
+ Assert.assertFalse(mgmt.getEntitlementManager().isEntitled(null, Entitlements.SEE_CONFIG, EntityAndItem.of(app, "any-config")));
confirmEffectorEntitlement(false);
confirmSensorEntitlement(false);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
index a6d3b8e..e6dd315 100644
--- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
+++ b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
@@ -18,18 +18,18 @@
*/
package org.apache.brooklyn.rest.resources;
-import static com.google.common.collect.Iterables.transform;
-
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
import org.apache.brooklyn.rest.api.EntityConfigApi;
import org.apache.brooklyn.rest.domain.EntityConfigSummary;
import org.apache.brooklyn.rest.filter.HaHotStateRequired;
@@ -43,7 +43,6 @@ import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -56,15 +55,26 @@ public class EntityConfigResource extends AbstractBrooklynRestResource implement
@Override
public List<EntityConfigSummary> list(final String application, final String entityToken) {
final Entity entity = brooklyn().getEntity(application, entityToken);
- // TODO merge with keys which have values
- return Lists.newArrayList(transform(
- entity.getEntityType().getConfigKeys(),
- new Function<ConfigKey<?>, EntityConfigSummary>() {
- @Override
- public EntityConfigSummary apply(ConfigKey<?> config) {
- return EntityTransformer.entityConfigSummary(entity, config);
- }
- }));
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
+ // TODO merge with keys which have values:
+ // ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
+ List<EntityConfigSummary> result = Lists.newArrayList();
+
+ for (ConfigKey<?> key : entity.getEntityType().getConfigKeys()) {
+ // Exclude config that user is not allowed to see
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, key.getName()))) {
+ LOG.trace("User {} not authorized to see config {} of entity {}; excluding from ConfigKey list results",
+ new Object[] {Entitlements.getEntitlementContext().user(), key.getName(), entity});
+ continue;
+ }
+ result.add(EntityTransformer.entityConfigSummary(entity, key));
+ }
+
+ return result;
}
// TODO support parameters ?show=value,summary&name=xxx &format={string,json,xml}
@@ -73,16 +83,23 @@ public class EntityConfigResource extends AbstractBrooklynRestResource implement
public Map<String, Object> batchConfigRead(String application, String entityToken, Boolean raw) {
// TODO: add test
Entity entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
// wrap in a task for better runtime view
- return Entities.submit(entity, Tasks.<Map<String,Object>>builder().displayName("REST API batch config read").body(new BatchConfigRead(this, entity, raw)).build()).getUnchecked();
+ return Entities.submit(entity, Tasks.<Map<String,Object>>builder().displayName("REST API batch config read").body(new BatchConfigRead(mgmt(), this, entity, raw)).build()).getUnchecked();
}
private static class BatchConfigRead implements Callable<Map<String,Object>> {
- private EntityConfigResource resource;
- private Entity entity;
- private Boolean raw;
+ private final ManagementContext mgmt;
+ private final EntityConfigResource resource;
+ private final Entity entity;
+ private final Boolean raw;
- public BatchConfigRead(EntityConfigResource resource, Entity entity, Boolean raw) {
+ public BatchConfigRead(ManagementContext mgmt, EntityConfigResource resource, Entity entity, Boolean raw) {
+ this.mgmt = mgmt;
this.resource = resource;
this.entity = entity;
this.raw = raw;
@@ -93,9 +110,17 @@ public class EntityConfigResource extends AbstractBrooklynRestResource implement
Map<ConfigKey<?>, ?> source = ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
Map<String, Object> result = Maps.newLinkedHashMap();
for (Map.Entry<ConfigKey<?>, ?> ek : source.entrySet()) {
+ ConfigKey<?> key = ek.getKey();
Object value = ek.getValue();
- result.put(ek.getKey().getName(),
- resource.resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(ek.getKey()).resolve());
+
+ // Exclude config that user is not allowed to see
+ if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, ek.getKey().getName()))) {
+ LOG.trace("User {} not authorized to see sensor {} of entity {}; excluding from current-state results",
+ new Object[] {Entitlements.getEntitlementContext().user(), ek.getKey().getName(), entity});
+ continue;
+ }
+ result.put(key.getName(),
+ resource.resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(key).resolve());
}
return result;
}
@@ -114,6 +139,16 @@ public class EntityConfigResource extends AbstractBrooklynRestResource implement
public Object get(boolean preferJson, String application, String entityToken, String configKeyName, Boolean raw) {
Entity entity = brooklyn().getEntity(application, entityToken);
ConfigKey<?> ck = findConfig(entity, configKeyName);
+
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, ck.getName()))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s' config '%s'",
+ Entitlements.getEntitlementContext().user(), entity, ck.getName());
+ }
+
Object value = ((EntityInternal)entity).config().getRaw(ck).orNull();
return resolving(value).preferJson(preferJson).asJerseyOutermostReturnValue(true).raw(raw).context(entity).timeout(ValueResolver.PRETTY_QUICK_WAIT).renderAs(ck).resolve();
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
index 303c0df..edb5c7f 100644
--- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
+++ b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/SensorResource.java
@@ -19,7 +19,6 @@
package org.apache.brooklyn.rest.resources;
import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.transform;
import java.util.List;
import java.util.Map;
@@ -29,6 +28,7 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
import org.apache.brooklyn.core.sensor.BasicAttributeSensor;
import org.apache.brooklyn.rest.api.SensorApi;
import org.apache.brooklyn.rest.domain.SensorSummary;
@@ -41,7 +41,6 @@ import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -50,28 +49,49 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
private static final Logger log = LoggerFactory.getLogger(SensorResource.class);
- @SuppressWarnings("rawtypes")
@Override
public List<SensorSummary> list(final String application, final String entityToken) {
final Entity entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
- return Lists.newArrayList(transform(filter(entity.getEntityType().getSensors(), AttributeSensor.class),
- new Function<AttributeSensor, SensorSummary>() {
- @Override
- public SensorSummary apply(AttributeSensor sensor) {
- return SensorTransformer.sensorSummary(entity, sensor);
- }
- }));
+ List<SensorSummary> result = Lists.newArrayList();
+
+ for (AttributeSensor<?> sensor : filter(entity.getEntityType().getSensors(), AttributeSensor.class)) {
+ // Exclude config that user is not allowed to see
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_SENSOR, new EntityAndItem<String>(entity, sensor.getName()))) {
+ log.trace("User {} not authorized to see sensor {} of entity {}; excluding from AttributeSensor list results",
+ new Object[] {Entitlements.getEntitlementContext().user(), sensor.getName(), entity});
+ continue;
+ }
+ result.add(SensorTransformer.sensorSummary(entity, sensor));
+ }
+
+ return result;
}
@Override
public Map<String, Object> batchSensorRead(final String application, final String entityToken, final Boolean raw) {
final Entity entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
Map<String, Object> sensorMap = Maps.newHashMap();
@SuppressWarnings("rawtypes")
Iterable<AttributeSensor> sensors = filter(entity.getEntityType().getSensors(), AttributeSensor.class);
for (AttributeSensor<?> sensor : sensors) {
+ // Exclude sensors that user is not allowed to see
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_SENSOR, new EntityAndItem<String>(entity, sensor.getName()))) {
+ log.trace("User {} not authorized to see sensor {} of entity {}; excluding from current-state results",
+ new Object[] {Entitlements.getEntitlementContext().user(), sensor.getName(), entity});
+ continue;
+ }
+
Object value = entity.getAttribute(findSensor(entity, sensor.getName()));
sensorMap.put(sensor.getName(),
resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(sensor).resolve());
@@ -82,6 +102,16 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
protected Object get(boolean preferJson, String application, String entityToken, String sensorName, Boolean raw) {
final Entity entity = brooklyn().getEntity(application, entityToken);
AttributeSensor<?> sensor = findSensor(entity, sensorName);
+
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_SENSOR, new EntityAndItem<String>(entity, sensor.getName()))) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to see entity '%s' sensor '%s'",
+ Entitlements.getEntitlementContext().user(), entity, sensor.getName());
+ }
+
Object value = entity.getAttribute(sensor);
return resolving(value).preferJson(preferJson).asJerseyOutermostReturnValue(true).raw(raw).context(entity).timeout(ValueResolver.PRETTY_QUICK_WAIT).renderAs(sensor).resolve();
}
@@ -140,6 +170,11 @@ public class SensorResource extends AbstractBrooklynRestResource implements Sens
@Override
public void delete(String application, String entityToken, String sensorName) {
final Entity entity = brooklyn().getEntity(application, entityToken);
+ if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
+ throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify entity '%s'",
+ Entitlements.getEntitlementContext().user(), entity);
+ }
+
AttributeSensor<?> sensor = findSensor(entity, sensorName);
if (log.isDebugEnabled())
log.debug("REST user "+Entitlements.getEntitlementContext()+" deleting sensor "+sensorName);
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java
new file mode 100644
index 0000000..c851b48
--- /dev/null
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AbstractRestApiEntitlementsTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.entitlement;
+
+import static org.apache.brooklyn.util.http.HttpTool.httpClientBuilder;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.PerUserEntitlementManager;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.BrooklynRestApiLauncher;
+import org.apache.brooklyn.rest.BrooklynRestApiLauncherTestFixture;
+import org.apache.brooklyn.util.http.HttpAsserts;
+import org.apache.brooklyn.util.http.HttpTool;
+import org.apache.brooklyn.util.http.HttpToolResponse;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/**
+ * Sets up the REST api with some standard users, ready for testing entitlements.
+ */
+public abstract class AbstractRestApiEntitlementsTest extends BrooklynRestApiLauncherTestFixture {
+
+ protected ManagementContext mgmt;
+ protected TestApplication app;
+ protected TestEntity entity;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+ props.put(Entitlements.GLOBAL_ENTITLEMENT_MANAGER.getName(), PerUserEntitlementManager.class.getName());
+ props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myRoot", "root");
+ props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myReadonly", "readonly");
+ props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myMinimal", "minimal");
+ props.put(PerUserEntitlementManager.PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".myCustom", StaticDelegatingEntitlementManager.class.getName());
+
+ mgmt = LocalManagementContextForTests.builder(true).useProperties(props).build();
+ app = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
+ .child(EntitySpec.create(TestEntity.class))
+ .configure(TestEntity.CONF_NAME, "myname"));
+ entity = (TestEntity) Iterables.getOnlyElement(app.getChildren());
+
+ useServerForTest(BrooklynRestApiLauncher.launcher()
+ .managementContext(mgmt)
+ .forceUseOfDefaultCatalogWithJavaClassPath(true)
+ .securityProvider(AuthenticateAnyoneSecurityProvider.class)
+ .start());
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (mgmt != null) Entities.destroyAll(mgmt);
+ }
+
+ protected HttpClient newClient(String user) throws Exception {
+ return httpClientBuilder()
+ .uri(getBaseUri())
+ .credentials(new UsernamePasswordCredentials(user, "ignoredPassword"))
+ .build();
+ }
+
+ protected String httpGet(String user, String path) throws Exception {
+ HttpToolResponse response = HttpTool.httpGet(newClient(user), URI.create(getBaseUri()).resolve(path), ImmutableMap.<String, String>of());
+ assertTrue(HttpAsserts.isHealthyStatusCode(response.getResponseCode()), "code="+response.getResponseCode()+"; reason="+response.getReasonPhrase());
+ return response.getContentAsString();
+ }
+
+ protected String assertPermitted(String user, String path) throws Exception {
+ return httpGet(user, path);
+ }
+
+ protected void assertForbidden(String user, String path) throws Exception {
+ HttpToolResponse response = HttpTool.httpGet(newClient(user), URI.create(getBaseUri()).resolve(path), ImmutableMap.<String, String>of());
+ assertEquals(response.getResponseCode(), 401, "code="+response.getResponseCode()+"; reason="+response.getReasonPhrase()+"; content="+response.getContentAsString());
+ }
+
+ protected void assert404(String user, String path) throws Exception {
+ HttpToolResponse response = HttpTool.httpGet(newClient(user), URI.create(getBaseUri()).resolve(path), ImmutableMap.<String, String>of());
+ assertEquals(response.getResponseCode(), 404, "code="+response.getResponseCode()+"; reason="+response.getReasonPhrase()+"; content="+response.getContentAsString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java
new file mode 100644
index 0000000..b7264b2
--- /dev/null
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/AuthenticateAnyoneSecurityProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.entitlement;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.brooklyn.rest.security.provider.SecurityProvider;
+
+public class AuthenticateAnyoneSecurityProvider implements SecurityProvider {
+
+ @Override
+ public boolean isAuthenticated(HttpSession session) {
+ return false;
+ }
+
+ @Override
+ public boolean authenticate(HttpSession session, String user, String password) {
+ return user != null;
+ }
+
+ @Override
+ public boolean logout(HttpSession session) {
+ return false;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java
new file mode 100644
index 0000000..cbda515
--- /dev/null
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/EntityConfigApiEntitlementsTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.entitlement;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.api.SensorApi;
+import org.apache.brooklyn.rest.resources.SensorResource;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.Test;
+
+/**
+ * Test the {@link SensorApi} implementation.
+ * <p>
+ * Check that {@link SensorResource} correctly renders {@link AttributeSensor}
+ * values, including {@link RendererHints.DisplayValue} hints.
+ */
+@Test(singleThreaded = true)
+public class EntityConfigApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+ @Test(groups = "Integration")
+ public void testGet() throws Exception {
+ String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/config/"+TestEntity.CONF_NAME.getName();
+ String val = "\"myname\"";
+
+ assertEquals(httpGet("myRoot", path), val);
+ assertEquals(httpGet("myReadonly", path), val);
+ assert404("myMinimal", path); // can't see app, to retrieve entity
+ assert404("unrecognisedUser", path);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, TestEntity.CONF_NAME.getName()));
+ assertEquals(httpGet("myCustom", path), val);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, "differentConfName"));
+ assertForbidden("myCustom", path);
+ }
+
+ @Test(groups = "Integration")
+ public void testCurrentState() throws Exception {
+ String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/config/current-state";
+ String confName = TestEntity.CONF_NAME.getName();
+ String regex = ".*"+confName+".*myname.*";
+
+ Asserts.assertStringMatchesRegex(httpGet("myRoot", path), regex);
+ Asserts.assertStringMatchesRegex(httpGet("myReadonly", path), regex);
+ assert404("myMinimal", path); // can't see app, to retrieve entity
+ assert404("unrecognisedUser", path);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, confName));
+ Asserts.assertStringMatchesRegex(httpGet("myCustom", path), regex);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveConfig(entity, "differentConfName"));
+ String resp = httpGet("myCustom", path);
+ assertFalse(resp.matches(regex), "resp="+resp);
+ }
+
+ public static class SeeSelectiveConfig implements EntitlementManager {
+ private final Entity entity;
+ private final String regex;
+
+ public SeeSelectiveConfig(Entity entity, String regex) {
+ this.entity = entity;
+ this.regex = regex;
+ }
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+ String type = entitlementClass.entitlementClassIdentifier();
+ if (Entitlements.SEE_CONFIG.entitlementClassIdentifier().equals(type)) {
+ EntityAndItem<String> entityAndItem = (EntityAndItem<String>) entitlementClassArgument;
+ return entity.equals(entityAndItem.getEntity()) && entityAndItem.getItem().matches(regex);
+ } else {
+ return true;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java
new file mode 100644
index 0000000..dab72ec
--- /dev/null
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/SensorApiEntitlementsTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.entitlement;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.rest.api.SensorApi;
+import org.apache.brooklyn.rest.resources.SensorResource;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.Test;
+
+/**
+ * Test the {@link SensorApi} implementation.
+ * <p>
+ * Check that {@link SensorResource} correctly renders {@link AttributeSensor}
+ * values, including {@link RendererHints.DisplayValue} hints.
+ */
+@Test(singleThreaded = true)
+public class SensorApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+ @Test(groups = "Integration")
+ public void testGet() throws Exception {
+ entity.sensors().set(TestEntity.NAME, "myval");
+
+ String sensorName = TestEntity.NAME.getName();
+ String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/sensors/"+sensorName;
+ String val = "\"myval\"";
+
+ assertEquals(httpGet("myRoot", path), val);
+ assertEquals(httpGet("myReadonly", path), val);
+ assert404("myMinimal", path); // can't see app, to retrieve entity
+ assert404("unrecognisedUser", path);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, sensorName));
+ assertEquals(httpGet("myCustom", path), val);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, "differentConfName"));
+ assertForbidden("myCustom", path);
+ }
+
+ @Test(groups = "Integration")
+ public void testCurrentState() throws Exception {
+ entity.sensors().set(TestEntity.NAME, "myval");
+
+ String path = "/v1/applications/"+app.getId()+"/entities/"+entity.getId()+"/sensors/current-state";
+ String sensorName = TestEntity.NAME.getName();
+ String regex = ".*"+sensorName+".*myval.*";
+
+ Asserts.assertStringMatchesRegex(httpGet("myRoot", path), regex);
+ Asserts.assertStringMatchesRegex(httpGet("myReadonly", path), regex);
+ assert404("myMinimal", path); // can't see app, to retrieve entity
+ assert404("unrecognisedUser", path);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, sensorName));
+ Asserts.assertStringMatchesRegex(httpGet("myCustom", path), regex);
+
+ StaticDelegatingEntitlementManager.setDelegate(new SeeSelectiveSensor(entity, "differentSensorName"));
+ String resp = httpGet("myCustom", path);
+ assertFalse(resp.matches(regex), "resp="+resp);
+ }
+
+ public static class SeeSelectiveSensor implements EntitlementManager {
+ private final Entity entity;
+ private final String regex;
+
+ public SeeSelectiveSensor(Entity entity, String regex) {
+ this.entity = entity;
+ this.regex = regex;
+ }
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+ String type = entitlementClass.entitlementClassIdentifier();
+ if (Entitlements.SEE_SENSOR.entitlementClassIdentifier().equals(type)) {
+ EntityAndItem<String> entityAndItem = (EntityAndItem<String>) entitlementClassArgument;
+ return entity.equals(entityAndItem.getEntity()) && entityAndItem.getItem().matches(regex);
+ } else {
+ return true;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java
new file mode 100644
index 0000000..afa42cb
--- /dev/null
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/ServerApiEntitlementsTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.entitlement;
+
+import org.testng.annotations.Test;
+
+@Test(singleThreaded = true)
+public class ServerApiEntitlementsTest extends AbstractRestApiEntitlementsTest {
+
+ @Test(groups = "Integration")
+ public void testGetHealthy() throws Exception {
+ String path = "/v1/server/up";
+ assertPermitted("myRoot", path);
+ assertForbidden("myReadonly", path);
+ assertForbidden("myMinimal", path);
+ assertForbidden("unrecognisedUser", path);
+ }
+}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a0557178/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java
----------------------------------------------------------------------
diff --git a/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java
new file mode 100644
index 0000000..4370ad6
--- /dev/null
+++ b/rest/rest-server/src/test/java/org/apache/brooklyn/rest/entitlement/StaticDelegatingEntitlementManager.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.entitlement;
+
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
+
+public class StaticDelegatingEntitlementManager implements EntitlementManager {
+
+ private static volatile EntitlementManager delegate;
+
+ public static void setDelegate(EntitlementManager val) {
+ delegate = val;
+ }
+
+ @Override
+ public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) {
+ return (delegate != null) ? delegate.isEntitled(context, entitlementClass, entitlementClassArgument) : false;
+ }
+}