Author: ldywicki Date: Wed Feb 1 12:58:51 2012 New Revision: 1239114 URL: http://svn.apache.org/viewvc?rev=1239114&view=rev Log: Add support for hierarchical roles.
Signed-off-by: Lukasz Dywicki <l...@code-house.org> Added: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoleCheckingStrategy.java karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoles.java karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/SecuredPageLink.java karaf/webconsole/trunk/core/src/test/java/org/apache/karaf/webconsole/core/security/ karaf/webconsole/trunk/core/src/test/java/org/apache/karaf/webconsole/core/security/HierarchicalRolesTest.java Modified: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/internal/WebConsoleApplication.java karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/page/SecuredPage.java Modified: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/internal/WebConsoleApplication.java URL: http://svn.apache.org/viewvc/karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/internal/WebConsoleApplication.java?rev=1239114&r1=1239113&r2=1239114&view=diff ============================================================================== --- karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/internal/WebConsoleApplication.java (original) +++ karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/internal/WebConsoleApplication.java Wed Feb 1 12:58:51 2012 @@ -18,9 +18,11 @@ package org.apache.karaf.webconsole.core import org.apache.karaf.webconsole.core.dashboard.DashboardPage; import org.apache.karaf.webconsole.core.page.LoginPage; +import org.apache.karaf.webconsole.core.security.HierarchicalRoleCheckingStrategy; import org.apache.karaf.webconsole.core.security.KarafJaasWebSession; import org.apache.wicket.authentication.AuthenticatedWebApplication; import org.apache.wicket.authentication.AuthenticatedWebSession; +import org.apache.wicket.authorization.strategies.role.RoleAuthorizationStrategy; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.pages.AccessDeniedPage; import org.apache.wicket.markup.html.pages.PageExpiredErrorPage; @@ -44,6 +46,8 @@ public class WebConsoleApplication exten getApplicationSettings().setAccessDeniedPage(AccessDeniedPage.class); getApplicationSettings().setPageExpiredErrorPage(PageExpiredErrorPage.class); + + getSecuritySettings().setAuthorizationStrategy(new RoleAuthorizationStrategy(new HierarchicalRoleCheckingStrategy())); } /** Modified: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/page/SecuredPage.java URL: http://svn.apache.org/viewvc/karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/page/SecuredPage.java?rev=1239114&r1=1239113&r2=1239114&view=diff ============================================================================== --- karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/page/SecuredPage.java (original) +++ karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/page/SecuredPage.java Wed Feb 1 12:58:51 2012 @@ -22,10 +22,10 @@ import org.apache.karaf.webconsole.core. import org.apache.karaf.webconsole.core.navigation.ConsoleTabProvider; import org.apache.karaf.webconsole.core.navigation.markup.NavigationPanel; import org.apache.karaf.webconsole.core.preferences.PreferencesPage; +import org.apache.karaf.webconsole.core.security.SecuredPageLink; import org.apache.karaf.webconsole.core.security.WebConsoleSession; import org.apache.wicket.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.StringResourceModel; @@ -56,7 +56,7 @@ public class SecuredPage extends BasePag add(new AvatarImage("avatar", preferences.getUserPreferences(username))); add(new Label("username", username)); - add(new BookmarkablePageLink<PreferencesPage>("preferencesLink", PreferencesPage.class)); + add(new SecuredPageLink<PreferencesPage>("preferencesLink", PreferencesPage.class)); Link<Void> aLink = new Link<Void>("logoutLink") { @Override Added: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoleCheckingStrategy.java URL: http://svn.apache.org/viewvc/karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoleCheckingStrategy.java?rev=1239114&view=auto ============================================================================== --- karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoleCheckingStrategy.java (added) +++ karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoleCheckingStrategy.java Wed Feb 1 12:58:51 2012 @@ -0,0 +1,36 @@ +/* + * 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.karaf.webconsole.core.security; + +import org.apache.wicket.authentication.AuthenticatedWebSession; +import org.apache.wicket.authorization.strategies.role.IRoleCheckingStrategy; +import org.apache.wicket.authorization.strategies.role.Roles; + +/** + * Role checking strategy which follow the hierarchy. + */ +public class HierarchicalRoleCheckingStrategy implements IRoleCheckingStrategy { + + public boolean hasAnyRole(Roles roles) { + HierarchicalRoles assigned = new HierarchicalRoles(AuthenticatedWebSession.get().getRoles()); + + HierarchicalRoles requested = new HierarchicalRoles(roles); + + return assigned.hasAnyRole(requested); + } + +} Added: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoles.java URL: http://svn.apache.org/viewvc/karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoles.java?rev=1239114&view=auto ============================================================================== --- karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoles.java (added) +++ karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/HierarchicalRoles.java Wed Feb 1 12:58:51 2012 @@ -0,0 +1,125 @@ +/* + * 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.karaf.webconsole.core.security; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.wicket.authorization.strategies.role.Roles; + +/** + * An extension of roles to support hierarchical roles. Hierarchy is created by + * splitting dahses in the role names. + * + * The role checking follow the structure with one exception. If assgidned roles + * do not have a "specific" extension then check will pass. For example, if user + * have assigned role "test-role" and component requires role "test-role-one" + * then user will be authorized. + */ +public class HierarchicalRoles { + + /** + * Helper class. + */ + class Node { + + private Map<String, Node> nodes = new TreeMap<String, Node>(); + + private String name; + + public Node(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public Map<String, Node> getNodes() { + return nodes; + } + } + + /** + * Assgined roles. + */ + private Map<String, Node> nodes = new TreeMap<String, Node>(); + + public HierarchicalRoles(Roles roles) { + for (String role : roles) { + String[] path = role.split("-"); + + createNode(nodes, new ArrayList<String>(Arrays.asList(path))); + } + } + + public HierarchicalRoles() { + this(new Roles()); + } + + public HierarchicalRoles(String roles) { + this(new Roles(roles)); + } + + private void createNode(Map<String, Node> nodes, List<String> path) { + String name = path.remove(0); + + if (!nodes.containsKey(name)) { + nodes.put(name, new Node(name)); + } + + if (path.size() >= 1) { + createNode(nodes.get(name).nodes, path); + } + } + + public boolean hasAnyRole(HierarchicalRoles requested) { + // + if (requested.getNodes().isEmpty()) { + return true; + } + + Map<String, Node> requestedNodes = requested.getNodes(); + + return matchesAny(nodes, requestedNodes); + } + + private static boolean matchesAny(Map<String, Node> assignedNodes, Map<String, Node> requestedNodes) { + boolean match = false; + for (Node node : requestedNodes.values()) { + Node assigned = assignedNodes.get(node.getName()); + if (assigned != null) { + if (assigned.getNodes().size() > 0) { + match = matchesAny(assigned.getNodes(), node.getNodes()); + } else { + match = true; + } + } + } + + return match; + } + + public Map<String, Node> getNodes() { + return Collections.unmodifiableMap(nodes); + } +} + Added: karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/SecuredPageLink.java URL: http://svn.apache.org/viewvc/karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/SecuredPageLink.java?rev=1239114&view=auto ============================================================================== --- karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/SecuredPageLink.java (added) +++ karaf/webconsole/trunk/core/src/main/java/org/apache/karaf/webconsole/core/security/SecuredPageLink.java Wed Feb 1 12:58:51 2012 @@ -0,0 +1,24 @@ +package org.apache.karaf.webconsole.core.security; + +import org.apache.karaf.webconsole.core.page.SecuredPage; +import org.apache.wicket.PageParameters; +import org.apache.wicket.authentication.AuthenticatedWebSession; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; + +public class SecuredPageLink<T extends SecuredPage> extends BookmarkablePageLink<T> { + + public SecuredPageLink(String id, Class<T> pageClass) { + this(id, pageClass, null); + } + + public SecuredPageLink(String id, Class<T> pageClass, PageParameters parameters) { + super(id, pageClass, parameters); + } + + @Override + public boolean isVisible() { + AuthenticatedWebSession session = AuthenticatedWebSession.get(); + return session.getAuthorizationStrategy().isInstantiationAuthorized(getPageClass()); + } + +} Added: karaf/webconsole/trunk/core/src/test/java/org/apache/karaf/webconsole/core/security/HierarchicalRolesTest.java URL: http://svn.apache.org/viewvc/karaf/webconsole/trunk/core/src/test/java/org/apache/karaf/webconsole/core/security/HierarchicalRolesTest.java?rev=1239114&view=auto ============================================================================== --- karaf/webconsole/trunk/core/src/test/java/org/apache/karaf/webconsole/core/security/HierarchicalRolesTest.java (added) +++ karaf/webconsole/trunk/core/src/test/java/org/apache/karaf/webconsole/core/security/HierarchicalRolesTest.java Wed Feb 1 12:58:51 2012 @@ -0,0 +1,113 @@ +/* + * 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.karaf.webconsole.core.security; + +import static junit.framework.Assert.*; + +import java.util.Map; + +import org.apache.karaf.webconsole.core.security.HierarchicalRoles.Node; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * Tests role inheritance and matching. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class HierarchicalRolesTest { + + @Test + public void testEmptyRoles() { + HierarchicalRoles roles = new HierarchicalRoles(); + HierarchicalRoles requested = new HierarchicalRoles("test-user"); + + assertEquals(0, roles.getNodes().size()); + assertFalse(roles.hasAnyRole(requested)); + assertTrue(requested.hasAnyRole(roles)); + } + + @Test + public void testHierarchy() { + HierarchicalRoles roles = new HierarchicalRoles("test-user, test-dev, test-operator"); + + Map<String, Node> nodes = roles.getNodes(); + assertEquals(1, nodes.size()); + + nodes = nodes.get("test").getNodes(); + assertEquals(3, nodes.size()); + + assertTrue(nodes.containsKey("user")); + assertTrue(nodes.containsKey("dev")); + assertTrue(nodes.containsKey("operator")); + } + + @Test + public void testMultipleHierarchy() { + HierarchicalRoles roles = new HierarchicalRoles("test-user, user-one, user-two, test-mock"); + + Map<String, Node> nodes = roles.getNodes(); + assertEquals(2, nodes.size()); + + Map<String, Node> testNodes = nodes.get("test").getNodes(); + assertEquals(2, testNodes.size()); + + assertTrue(testNodes.containsKey("user")); + assertTrue(testNodes.containsKey("mock")); + + Map<String, Node> userNodes = nodes.get("user").getNodes(); + assertEquals(2, userNodes.size()); + + assertTrue(userNodes.containsKey("one")); + assertTrue(userNodes.containsKey("two")); + } + + @Test + public void testHierarchyMatching() { + HierarchicalRoles assigned = new HierarchicalRoles("test"); + HierarchicalRoles requested = new HierarchicalRoles("test-user"); + + assertTrue(assigned.hasAnyRole(requested)); + assertFalse(requested.hasAnyRole(assigned)); + } + + @Test + public void testTwoLevelHierarchyMatching() { + HierarchicalRoles assigned = new HierarchicalRoles("test-user"); + HierarchicalRoles requested = new HierarchicalRoles("test-user-one,test-user-two,test-user-four"); + + assertTrue(assigned.hasAnyRole(requested)); + assertFalse(requested.hasAnyRole(assigned)); + + assigned = new HierarchicalRoles("test-user2"); + assertFalse(assigned.hasAnyRole(requested)); + assertFalse(requested.hasAnyRole(assigned)); + + assigned = new HierarchicalRoles("test-user-one"); + assertTrue(assigned.hasAnyRole(requested)); + assertTrue(requested.hasAnyRole(assigned)); + } + + @Test + public void testMatching() { + HierarchicalRoles assigned = new HierarchicalRoles("test"); + HierarchicalRoles requested = new HierarchicalRoles("test"); + + assertTrue(assigned.hasAnyRole(requested)); + assertTrue(requested.hasAnyRole(assigned)); + } +}