Repository: incubator-brooklyn Updated Branches: refs/heads/master 974e7139f -> c563e653a
add per-user entitlement configuration and entitlements documentation Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/898aa73c Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/898aa73c Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/898aa73c Branch: refs/heads/master Commit: 898aa73ceafa9df4935fd73333ea4cc75a532e47 Parents: c97eaae Author: Alex Heneveld <[email protected]> Authored: Mon Dec 22 13:59:36 2014 +0000 Committer: Alex Heneveld <[email protected]> Committed: Mon Dec 22 18:36:48 2014 +0000 ---------------------------------------------------------------------- .../management/entitlement/Entitlements.java | 23 +-- .../entitlement/PerUserEntitlementManager.java | 97 ++++++++++++ .../PerUserEntitlementManagerWithDefault.java | 27 +--- .../entitlement/AcmeEntitlementManager.java | 24 ++- .../entitlement/AcmeEntitlementManagerTest.java | 114 ++------------ .../AcmeEntitlementManagerTestFixture.java | 157 +++++++++++++++++++ ...PerUserEntitlementManagerPropertiesTest.java | 52 ++++++ docs/guide/use/guide/management/entitlements.md | 49 ++++++ docs/guide/use/guide/management/toc.json | 4 +- 9 files changed, 406 insertions(+), 141 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/main/java/brooklyn/management/entitlement/Entitlements.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/entitlement/Entitlements.java b/core/src/main/java/brooklyn/management/entitlement/Entitlements.java index a555cac..9772b51 100644 --- a/core/src/main/java/brooklyn/management/entitlement/Entitlements.java +++ b/core/src/main/java/brooklyn/management/entitlement/Entitlements.java @@ -18,6 +18,8 @@ */ package brooklyn.management.entitlement; +import javax.annotation.Nullable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -283,25 +285,28 @@ public class Entitlements { // ----------------- initialization ---------------- - public static ConfigKey<String> GLOBAL_ENTITLEMENT_MANAGER = ConfigKeys.newStringConfigKey("brooklyn.entitlements.global", + public final static String ENTITLEMENTS_CONFIG_PREFIX = "brooklyn.entitlements"; + + public static ConfigKey<String> GLOBAL_ENTITLEMENT_MANAGER = ConfigKeys.newStringConfigKey(ENTITLEMENTS_CONFIG_PREFIX+".global", "Class for entitlements in effect globally; " + "short names 'minimal', 'readonly', or 'root' are permitted here, with the default 'root' giving full access to all declared users; " + "or supply the name of an "+EntitlementManager.class+" class to instantiate, taking a 1-arg BrooklynProperties constructor or a 0-arg constructor", "root"); public static EntitlementManager newManager(ManagementContext mgmt, BrooklynProperties brooklynProperties) { - EntitlementManager result = newGlobalManager(mgmt, brooklynProperties); - // TODO read per user settings from brooklyn.properties, if set there ? - return result; + return newGlobalManager(mgmt, brooklynProperties); } private static EntitlementManager newGlobalManager(ManagementContext mgmt, BrooklynProperties brooklynProperties) { - String type = brooklynProperties.getConfig(GLOBAL_ENTITLEMENT_MANAGER); - if ("root".equalsIgnoreCase(type)) return new PerUserEntitlementManagerWithDefault(root()); - if ("readonly".equalsIgnoreCase(type)) return new PerUserEntitlementManagerWithDefault(readOnly()); - if ("minimal".equalsIgnoreCase(type)) return new PerUserEntitlementManagerWithDefault(minimal()); + return load(mgmt, brooklynProperties, brooklynProperties.getConfig(GLOBAL_ENTITLEMENT_MANAGER)); + } + + public static EntitlementManager load(@Nullable ManagementContext mgmt, BrooklynProperties brooklynProperties, String type) { + if ("root".equalsIgnoreCase(type)) return root(); + if ("readonly".equalsIgnoreCase(type) || "read_only".equalsIgnoreCase(type)) return readOnly(); + if ("minimal".equalsIgnoreCase(type)) return minimal(); if (Strings.isNonBlank(type)) { try { - ClassLoader cl = ((ManagementContextInternal)mgmt).getBaseClassLoader(); + ClassLoader cl = mgmt==null ? null : ((ManagementContextInternal)mgmt).getBaseClassLoader(); if (cl==null) cl = Entitlements.class.getClassLoader(); Class<?> clazz = cl.loadClass(type); Optional<?> result = Reflections.invokeConstructorWithArgs(clazz, brooklynProperties); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java b/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java new file mode 100644 index 0000000..5ca0e58 --- /dev/null +++ b/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManager.java @@ -0,0 +1,97 @@ +/* + * 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 brooklyn.management.entitlement; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.BrooklynProperties; +import brooklyn.config.ConfigKey; +import brooklyn.config.ConfigPredicates; +import brooklyn.entity.basic.ConfigKeys; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.text.Strings; + +import com.google.common.base.Preconditions; + +public class PerUserEntitlementManager implements EntitlementManager { + + private static final Logger log = LoggerFactory.getLogger(PerUserEntitlementManager.class); + + public final static String PER_USER_ENTITLEMENTS_CONFIG_PREFIX = Entitlements.ENTITLEMENTS_CONFIG_PREFIX+".perUser"; + + public final static ConfigKey<String> DEFAULT_MANAGER = ConfigKeys.newStringConfigKey(PER_USER_ENTITLEMENTS_CONFIG_PREFIX+ + ".default", "Default entitlements manager for users without further specification", "minimal"); + + protected final EntitlementManager defaultManager; + protected final Map<String,EntitlementManager> perUserManagers = MutableMap.of(); + + private final static ThreadLocal<Boolean> ACTIVE = new ThreadLocal<Boolean>(); + + private static EntitlementManager load(BrooklynProperties properties, String type) { + if (Boolean.TRUE.equals(ACTIVE.get())) { + // prevent infinite loop + throw new IllegalStateException("Cannot set "+PerUserEntitlementManager.class.getName()+" within config for itself"); + } + try { + ACTIVE.set(true); + return Entitlements.load(null, properties, type); + } finally { + ACTIVE.remove(); + } + } + + public PerUserEntitlementManager(BrooklynProperties properties) { + this(load(properties, properties.getConfig(DEFAULT_MANAGER))); + + BrooklynProperties users = properties.submap(ConfigPredicates.startingWith(PER_USER_ENTITLEMENTS_CONFIG_PREFIX+".")); + for (Map.Entry<ConfigKey<?>,?> key: users.getAllConfig().entrySet()) { + if (key.getKey().getName().equals(DEFAULT_MANAGER.getName())) continue; + String user = Strings.removeFromStart(key.getKey().getName(), PER_USER_ENTITLEMENTS_CONFIG_PREFIX+"."); + addUser(user, load(properties, Strings.toString(key.getValue()))); + } + + log.info(getClass().getSimpleName()+" created with "+perUserManagers.size()+" user"+Strings.s(perUserManagers)+" and " + + "default "+defaultManager+" (users: "+perUserManagers+")"); + } + + public PerUserEntitlementManager(EntitlementManager defaultManager) { + this.defaultManager = Preconditions.checkNotNull(defaultManager); + } + + public void addUser(String user, EntitlementManager managerForThisUser) { + perUserManagers.put(user, managerForThisUser); + } + + @Override + public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) { + EntitlementManager entitlementInEffect = null; + if (context==null || context.user()==null) { + // no user means it is running as an internal process, always has root + entitlementInEffect = Entitlements.root(); + } else { + if (context!=null) entitlementInEffect = perUserManagers.get(context.user()); + if (entitlementInEffect==null) entitlementInEffect = defaultManager; + } + return entitlementInEffect.isEntitled(context, entitlementClass, entitlementClassArgument); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java b/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java index 07ee701..c049b67 100644 --- a/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java +++ b/core/src/main/java/brooklyn/management/entitlement/PerUserEntitlementManagerWithDefault.java @@ -18,31 +18,12 @@ */ package brooklyn.management.entitlement; -import java.util.Map; - -import com.google.common.base.Preconditions; - -import brooklyn.util.collections.MutableMap; - -public class PerUserEntitlementManagerWithDefault implements EntitlementManager { - - protected final EntitlementManager defaultManager; - protected final Map<String,EntitlementManager> perUserManagers = MutableMap.of(); +@Deprecated +/** @deprecated since 0.7.0 use {@link PerUserEntitlementManager} */ +public class PerUserEntitlementManagerWithDefault extends PerUserEntitlementManager { public PerUserEntitlementManagerWithDefault(EntitlementManager defaultManager) { - this.defaultManager = Preconditions.checkNotNull(defaultManager); + super(defaultManager); } - public void addUser(String user, EntitlementManager managerForThisUser) { - perUserManagers.put(user, managerForThisUser); - } - - @Override - public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) { - EntitlementManager entitlementInEffect = null; - if (context!=null) entitlementInEffect = perUserManagers.get(context.user()); - if (entitlementInEffect==null) entitlementInEffect = defaultManager; - return entitlementInEffect.isEntitled(context, entitlementClass, entitlementClassArgument); - } - } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java index 706d5bf..9cc02dd 100644 --- a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java +++ b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManager.java @@ -18,13 +18,29 @@ */ package brooklyn.management.entitlement; -class AcmeEntitlementManager extends PerUserEntitlementManagerWithDefault { +class AcmeEntitlementManager extends PerUserEntitlementManager { public AcmeEntitlementManager() { + // default mode (if no user specified) is root super(Entitlements.root()); - super.addUser("hacker", Entitlements.minimal()); - super.addUser("bob", Entitlements.readOnly()); - super.addUser("alice", Entitlements.root()); + + super.addUser("admin", Entitlements.root()); + + super.addUser("support", Entitlements.readOnly()); + + // metrics can log in but can't really do much else + super.addUser("metrics", Entitlements.minimal()); + + // 'navigator' defines a user with a custom entitlement manager allowing + // access to see entities (in the tree) but not to do anything + // or even see any sensor information on those entities + super.addUser("navigator", new EntitlementManager() { + @Override + public <T> boolean isEntitled(EntitlementContext context, EntitlementClass<T> entitlementClass, T entitlementClassArgument) { + if (Entitlements.SEE_ENTITY.equals(entitlementClass)) return true; + return false; + } + }); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java index f5575b4..7a34351 100644 --- a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java +++ b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java @@ -21,132 +21,38 @@ package brooklyn.management.entitlement; import java.net.URI; import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; - -import brooklyn.config.BrooklynProperties; -import brooklyn.entity.Application; -import brooklyn.entity.basic.ApplicationBuilder; -import brooklyn.entity.basic.BasicApplication; -import brooklyn.entity.basic.Entities; -import brooklyn.entity.proxying.EntitySpec; -import brooklyn.location.Location; -import brooklyn.management.ManagementContext; import brooklyn.management.entitlement.Entitlements.EntityAndItem; -import brooklyn.test.entity.LocalManagementContextForTests; -import brooklyn.util.config.ConfigBag; -import brooklyn.util.exceptions.Exceptions; - -public class AcmeEntitlementManagerTest { - ManagementContext mgmt; - Application app; - ConfigBag configBag; - - public void setup(ConfigBag cfg) { - mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty().addFrom(cfg)); - app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt); - } +public class AcmeEntitlementManagerTest extends AcmeEntitlementManagerTestFixture { - @BeforeMethod(alwaysRun=true) - public void init() { - Entitlements.clearEntitlementContext(); - configBag = ConfigBag.newInstance(); + protected void addGlobalConfig() { configBag.put(Entitlements.GLOBAL_ENTITLEMENT_MANAGER, AcmeEntitlementManager.class.getName()); } - @AfterMethod(alwaysRun=true) - public void tearDown() { - Entitlements.clearEntitlementContext(); - if (app != null) Entities.destroyAll(app.getManagementContext()); - if (mgmt != null) Entities.destroyAll(mgmt); - app = null; - mgmt = null; + @Test + public void testAnonUserHasAllPermissions() { + checkUserHasAllPermissions("anon"); } @Test - public void testUserWithMinimal() { - setup(configBag); - WebEntitlementContext entitlementContext = new WebEntitlementContext("hacker", "127.0.0.1", URI.create("/applications").toString(), "H"); - Entitlements.setEntitlementContext(entitlementContext); - Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); - Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); - Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); - Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); - // and can invoke methods - confirmEffectorEntitlement(false); + public void testNullUserHasAllPermissions() { + checkUserHasAllPermissions(null); } @Test - public void testUserWithReadOnly() { + public void testNavigatorHasListPermissionsOnly() { setup(configBag); - WebEntitlementContext entitlementContext = new WebEntitlementContext("bob", "127.0.0.1", URI.create("/applications").toString(), "B"); + WebEntitlementContext entitlementContext = new WebEntitlementContext("navigator", "127.0.0.1", URI.create("/X").toString(), "X"); Entitlements.setEntitlementContext(entitlementContext); Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); // and cannot invoke methods confirmEffectorEntitlement(false); } - @Test - public void testUserWithAllPermissions() { - setup(configBag); - WebEntitlementContext entitlementContext = new WebEntitlementContext("alice", "127.0.0.1", URI.create("/applications").toString(), "A"); - Entitlements.setEntitlementContext(entitlementContext); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); - // and can invoke methods - confirmEffectorEntitlement(true); - } - - @Test - public void testNullHasAllPermissions() { - setup(configBag); - WebEntitlementContext entitlementContext = new WebEntitlementContext(null, "127.0.0.1", URI.create("/applications").toString(), "X"); - Entitlements.setEntitlementContext(entitlementContext); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); - Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); - // and can invoke methods - confirmEffectorEntitlement(true); - } - - protected void confirmEffectorEntitlement(boolean shouldSucceed) { - try { - ((BasicApplication)app).start(ImmutableList.<Location>of()); - checkNoException(shouldSucceed); - } catch (Exception e) { - checkNotEntitledException(shouldSucceed, e); - } - } - - private void checkNoException(boolean shouldBeEntitled) { - checkNotEntitledException(shouldBeEntitled, null); - } - - private void checkNotEntitledException(boolean shouldBeEntitled, Exception e) { - if (e==null) { - if (shouldBeEntitled) return; - Assert.fail("entitlement should have been denied"); - } - - Exception notEntitled = Exceptions.getFirstThrowableOfType(e, NotEntitledException.class); - if (notEntitled==null) - throw Exceptions.propagate(e); - if (!shouldBeEntitled) { - /* denied, as it should have been */ - return; - } - Assert.fail("entitlement should have been granted, but was denied: "+e); - } - } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java new file mode 100644 index 0000000..24f1e52 --- /dev/null +++ b/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTestFixture.java @@ -0,0 +1,157 @@ +/* + * 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 brooklyn.management.entitlement; + +import java.io.IOException; +import java.net.URI; + +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +import brooklyn.config.BrooklynProperties; +import brooklyn.entity.Application; +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.BasicApplication; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.location.Location; +import brooklyn.management.ManagementContext; +import brooklyn.management.entitlement.Entitlements; +import brooklyn.management.entitlement.NotEntitledException; +import brooklyn.management.entitlement.WebEntitlementContext; +import brooklyn.management.entitlement.Entitlements.EntityAndItem; +import brooklyn.test.entity.LocalManagementContextForTests; +import brooklyn.util.config.ConfigBag; +import brooklyn.util.exceptions.Exceptions; + +public abstract class AcmeEntitlementManagerTestFixture { + + ManagementContext mgmt; + Application app; + ConfigBag configBag; + + public void setup(ConfigBag cfg) { + mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty().addFrom(cfg)); + app = ApplicationBuilder.newManagedApp(EntitySpec.create(BasicApplication.class), mgmt); + } + + @BeforeMethod(alwaysRun=true) + public void init() throws IOException { + Entitlements.clearEntitlementContext(); + configBag = ConfigBag.newInstance(); + addGlobalConfig(); + } + + protected abstract void addGlobalConfig() throws IOException; + + @AfterMethod(alwaysRun=true) + public void tearDown() { + Entitlements.clearEntitlementContext(); + if (app != null) Entities.destroyAll(app.getManagementContext()); + if (mgmt != null) Entities.destroyAll(mgmt); + app = null; + mgmt = null; + } + + @Test + public void testMetricsHasMinimalPermissions() { + checkUserHasMinimalPermissions("metrics"); + } + + public void checkUserHasMinimalPermissions(String username) { + setup(configBag); + WebEntitlementContext entitlementContext = new WebEntitlementContext(username, "127.0.0.1", URI.create("/applications").toString(), "H"); + Entitlements.setEntitlementContext(entitlementContext); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); + // and can invoke methods + confirmEffectorEntitlement(false); + } + + @Test + public void testSupportHasReadOnlyPermissions() { + checkUserHasReadOnlyPermissions("support"); + } + + public void checkUserHasReadOnlyPermissions(String username) { + setup(configBag); + WebEntitlementContext entitlementContext = new WebEntitlementContext(username, "127.0.0.1", URI.create("/X").toString(), "B"); + Entitlements.setEntitlementContext(entitlementContext); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); + Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); + Assert.assertFalse(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); + Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); + // and cannot invoke methods + confirmEffectorEntitlement(false); + } + + @Test + public void testAdminHasAllPermissions() { + checkUserHasAllPermissions("admin"); + } + + public void checkUserHasAllPermissions(String user) { + setup(configBag); + WebEntitlementContext entitlementContext = new WebEntitlementContext(user, "127.0.0.1", URI.create("/X").toString(), "A"); + Entitlements.setEntitlementContext(entitlementContext); + Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.ROOT, null)); + Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, app)); + Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, "any-eff"))); + Assert.assertTrue(Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_SENSOR, EntityAndItem.of(app, "any-sensor"))); + // and can invoke methods + confirmEffectorEntitlement(true); + } + + protected void confirmEffectorEntitlement(boolean shouldSucceed) { + try { + ((BasicApplication)app).start(ImmutableList.<Location>of()); + checkNoException(shouldSucceed); + } catch (Exception e) { + checkNotEntitledException(shouldSucceed, e); + } + } + + private void checkNoException(boolean shouldBeEntitled) { + checkNotEntitledException(shouldBeEntitled, null); + } + + private void checkNotEntitledException(boolean shouldBeEntitled, Exception e) { + if (e==null) { + if (shouldBeEntitled) return; + Assert.fail("entitlement should have been denied"); + } + + Exception notEntitled = Exceptions.getFirstThrowableOfType(e, NotEntitledException.class); + if (notEntitled==null) + throw Exceptions.propagate(e); + if (!shouldBeEntitled) { + /* denied, as it should have been */ + return; + } + Assert.fail("entitlement should have been granted, but was denied: "+e); + } + +} + http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java b/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java new file mode 100644 index 0000000..63e2f62 --- /dev/null +++ b/core/src/test/java/brooklyn/management/entitlement/PerUserEntitlementManagerPropertiesTest.java @@ -0,0 +1,52 @@ +/* + * 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 brooklyn.management.entitlement; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Properties; + +import org.testng.annotations.Test; + + +public class PerUserEntitlementManagerPropertiesTest extends AcmeEntitlementManagerTestFixture { + + protected void addGlobalConfig() throws IOException { + Properties moreProps = new Properties(); + moreProps.load(new StringReader( + "brooklyn.entitlements.global=brooklyn.management.entitlement.PerUserEntitlementManager\n" + + "brooklyn.entitlements.perUser.admin=root\n" + + "brooklyn.entitlements.perUser.support=readonly\n" + + "brooklyn.entitlements.perUser.metrics=minimal")); + configBag.putAll(moreProps); + } + + @Test + public void testAnonUserHasMinimalPermissions() { + checkUserHasMinimalPermissions("anon"); + } + + @Test + public void testNullUserHasAllPermissions() { + checkUserHasAllPermissions(null); + } + + +} + http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/docs/guide/use/guide/management/entitlements.md ---------------------------------------------------------------------- diff --git a/docs/guide/use/guide/management/entitlements.md b/docs/guide/use/guide/management/entitlements.md new file mode 100644 index 0000000..c3f6083 --- /dev/null +++ b/docs/guide/use/guide/management/entitlements.md @@ -0,0 +1,49 @@ +--- +title: Entitlements +layout: guide-normal +toc: ../guide_toc.json +categories: [use, guide] +--- + +Brooklyn supports a plug-in system for defining ``entitlements'' -- +essentially permissions. + +Any entitlement scheme can be implemented by supplying a class which implements one method on one class: + + public interface EntitlementManager { + public <T> boolean isEntitled(@Nullable EntitlementContext context, @Nonnull EntitlementClass<T> entitlementClass, @Nullable T entitlementClassArgument); + } + +This answers the question who is allowed do what to whom, looking at the following fields: + +* `context`: the user who is logged in and is attempting an action + (extensions can contain additional metadata) +* `entitlementClass`: the type of action being queried, e.g. `DEPLOY_APPLICATION` or `SEE_SENSOR` + (declared in the class `Entitlements`) +* `entitlementClassArgument`: details of the action being queried, + such as the blueprint in the case of `DEPLOY_APPLICATION` or the entity and sensor name in the case + of `SEE_SENSOR` + +To set a custom entitlements manager to apply across the board, simply use: + + brooklyn.entitlements.global=brooklyn.management.entitlement.AcmeEntitlementManager + +The example above refers to a sample manager which is included in the test JARs of Brooklyn, +which you can see [here]({{ site.brooklyn.url.git }}/core/src/test/java/brooklyn/management/entitlement/AcmeEntitlementManagerTest.java), +and include in your project by adding the core tests JAR to your `dropins` folder. + + +## Please Make it Simpler + +There are some entitlements schemes which exist out of the box, +and you can use them simply by editing your `brooklyn.properties`: + + brooklyn.entitlements.global=brooklyn.management.entitlement.PerUserEntitlementManager + brooklyn.entitlements.perUser.admin=root + brooklyn.entitlements.perUser.support=readonly + brooklyn.entitlements.perUser.metrics=minimal + + +Some users have gone further and build LDAP extensions which re-use the LDAP authorization support +in Brooklyn, allowing permissions objects to be declared in LDAP and used to make entitlement decisions. +For more information on this, ask on IRC or the mailing list. http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/898aa73c/docs/guide/use/guide/management/toc.json ---------------------------------------------------------------------- diff --git a/docs/guide/use/guide/management/toc.json b/docs/guide/use/guide/management/toc.json index e30d160..b8d5ae4 100644 --- a/docs/guide/use/guide/management/toc.json +++ b/docs/guide/use/guide/management/toc.json @@ -15,4 +15,6 @@ { "title": "Key APIs", "file": "{{ site.path.guide }}/use/guide/management/index.html#key-apis" }, { "title": "Sensors and Effectors", - "file": "{{ site.path.guide }}/use/guide/management/index.html#sensors-and-effectors" }] + "file": "{{ site.path.guide }}/use/guide/management/index.html#sensors-and-effectors" }, +{ "title": "Entitlements", + "file": "{{ site.path.guide }}/use/guide/management/entitlements.html" }]
