Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 999F8200B38 for ; Fri, 8 Jul 2016 17:51:32 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 986D4160A78; Fri, 8 Jul 2016 15:51:32 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 99F82160A36 for ; Fri, 8 Jul 2016 17:51:31 +0200 (CEST) Received: (qmail 57415 invoked by uid 500); 8 Jul 2016 15:51:30 -0000 Mailing-List: contact commits-help@geode.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.incubator.apache.org Delivered-To: mailing list commits@geode.incubator.apache.org Received: (qmail 57406 invoked by uid 99); 8 Jul 2016 15:51:30 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 08 Jul 2016 15:51:30 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 642321A617E for ; Fri, 8 Jul 2016 15:51:30 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.646 X-Spam-Level: X-Spam-Status: No, score=-4.646 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-1.426] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id Rg__VvfXJ8sd for ; Fri, 8 Jul 2016 15:51:26 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 4B62360D5C for ; Fri, 8 Jul 2016 15:51:23 +0000 (UTC) Received: (qmail 53600 invoked by uid 99); 8 Jul 2016 15:51:22 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 08 Jul 2016 15:51:22 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 25041E08FE; Fri, 8 Jul 2016 15:51:22 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jinmeiliao@apache.org To: commits@geode.incubator.apache.org Date: Fri, 08 Jul 2016 15:52:05 -0000 Message-Id: <0b59b5f63edd4dd5820f4af874131047@git.apache.org> In-Reply-To: <0d0976df1fac43289eca21d332ddfa0a@git.apache.org> References: <0d0976df1fac43289eca21d332ddfa0a@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [45/50] [abbrv] incubator-geode git commit: Fixing close method archived-at: Fri, 08 Jul 2016 15:51:32 -0000 Fixing close method Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/25a80da3 Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/25a80da3 Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/25a80da3 Branch: refs/heads/develop Commit: 25a80da35e9da3d4fc93f8a180ba50f74f3988a7 Parents: 0fd2288 Author: gmeilen Authored: Thu Jul 7 13:53:44 2016 -0700 Committer: gmeilen Committed: Thu Jul 7 13:53:44 2016 -0700 ---------------------------------------------------------------------- .../internal/cache/GemFireCacheImpl.java | 2 +- .../internal/security/GeodeSecurityUtil.java | 21 +- .../security/shiro/CustomAuthRealm.java | 1 + .../templates/SampleSecurityManager.java | 261 +++++++++++++++++++ ...edSecurityCacheLifecycleIntegrationTest.java | 3 +- 5 files changed, 275 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/25a80da3/geode-core/src/main/java/com/gemstone/gemfire/internal/cache/GemFireCacheImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/com/gemstone/gemfire/internal/cache/GemFireCacheImpl.java b/geode-core/src/main/java/com/gemstone/gemfire/internal/cache/GemFireCacheImpl.java index 567f377..e39ffdd 100755 --- a/geode-core/src/main/java/com/gemstone/gemfire/internal/cache/GemFireCacheImpl.java +++ b/geode-core/src/main/java/com/gemstone/gemfire/internal/cache/GemFireCacheImpl.java @@ -1899,7 +1899,7 @@ public class GemFireCacheImpl implements InternalCache, ClientCache, HasCachePer return; } final boolean isDebugEnabled = logger.isDebugEnabled(); - GeodeSecurityUtil.close(system.getConfig().getSecurityProps()); + GeodeSecurityUtil.close(); synchronized (GemFireCacheImpl.class) { // bugfix for bug 36512 "GemFireCache.close is not thread safe" http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/25a80da3/geode-core/src/main/java/com/gemstone/gemfire/internal/security/GeodeSecurityUtil.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/com/gemstone/gemfire/internal/security/GeodeSecurityUtil.java b/geode-core/src/main/java/com/gemstone/gemfire/internal/security/GeodeSecurityUtil.java index ff32f92..d93c93f 100644 --- a/geode-core/src/main/java/com/gemstone/gemfire/internal/security/GeodeSecurityUtil.java +++ b/geode-core/src/main/java/com/gemstone/gemfire/internal/security/GeodeSecurityUtil.java @@ -60,6 +60,7 @@ public class GeodeSecurityUtil { private static Logger logger = LogService.getLogger(); + /** * It first looks the shiro subject in AccessControlContext since JMX will use multiple threads to process operations from the same client. * then it looks into Shiro's thead context. @@ -284,6 +285,7 @@ public class GeodeSecurityUtil { } private static PostProcessor postProcessor; + private static SecurityManager securityManager; /** * initialize Shiro's Security Manager and Security Utilities @@ -314,9 +316,9 @@ public class GeodeSecurityUtil { // only set up shiro realm if user has implemented SecurityManager else if (authenticatorObject != null && authenticatorObject instanceof SecurityManager) { - SecurityManager authenticator = (SecurityManager) authenticatorObject; - authenticator.init(securityProps); - Realm realm = new CustomAuthRealm(authenticator); + securityManager = (SecurityManager) authenticatorObject; + securityManager.init(securityProps); + Realm realm = new CustomAuthRealm(securityManager); org.apache.shiro.mgt.SecurityManager securityManager = new DefaultSecurityManager(realm); SecurityUtils.setSecurityManager(securityManager); } @@ -337,15 +339,12 @@ public class GeodeSecurityUtil { } - public static void close(Properties securityProps) { - if (securityProps != null) { - String customAuthenticator = securityProps.getProperty(SECURITY_MANAGER); - Object authenticatorObject = getObject(customAuthenticator); - if (authenticatorObject != null && authenticatorObject instanceof SecurityManager) { - ((SecurityManager) authenticatorObject).close(); + public static void close() { + if (securityManager != null) { + securityManager.close(); + securityManager = null; } - } - return; + } public static Object postProcess(String regionPath, Object key, Object result){ http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/25a80da3/geode-core/src/main/java/com/gemstone/gemfire/internal/security/shiro/CustomAuthRealm.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/com/gemstone/gemfire/internal/security/shiro/CustomAuthRealm.java b/geode-core/src/main/java/com/gemstone/gemfire/internal/security/shiro/CustomAuthRealm.java index d80b936..98ca1f0 100644 --- a/geode-core/src/main/java/com/gemstone/gemfire/internal/security/shiro/CustomAuthRealm.java +++ b/geode-core/src/main/java/com/gemstone/gemfire/internal/security/shiro/CustomAuthRealm.java @@ -86,4 +86,5 @@ public class CustomAuthRealm extends AuthorizingRealm{ return externalSecurity.authorize(principal, context); } + } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/25a80da3/geode-core/src/main/java/com/gemstone/gemfire/security/templates/SampleSecurityManager.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/com/gemstone/gemfire/security/templates/SampleSecurityManager.java b/geode-core/src/main/java/com/gemstone/gemfire/security/templates/SampleSecurityManager.java new file mode 100644 index 0000000..a80782d --- /dev/null +++ b/geode-core/src/main/java/com/gemstone/gemfire/security/templates/SampleSecurityManager.java @@ -0,0 +1,261 @@ +/* + * 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 com.gemstone.gemfire.security.templates; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.management.remote.JMXPrincipal; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.apache.shiro.authz.Permission; + +import com.gemstone.gemfire.management.internal.security.ResourceConstants; +import com.gemstone.gemfire.security.AccessControl; +import com.gemstone.gemfire.security.AuthenticationFailedException; +import com.gemstone.gemfire.security.Authenticator; +import com.gemstone.gemfire.security.SecurityManager; +import com.gemstone.gemfire.security.GeodePermission; +import com.gemstone.gemfire.security.NotAuthorizedException; + +/** + * This class provides a sample implementation for authentication and authorization via the {@link AccessControl} + * and {@link Authenticator} interfaces. + * + * In order to use it, a Geode member must be started with the following properties: + *

+ * + * security-client-authenticator = com.gemstone.gemfire.security.examples.SampleSecurityManager.create + * security-client-accessor = com.gemstone.gemfire.security.examples.SampleSecurityManager.create + * + *

+ * The class is initialized with a JSON file called {@code security.json}. This file must exist on the classpath, + * so members should be started with an appropriate {@code --classpath} option. + *

+ * The format of the file is as follows: + *

+ * {
+ *   "roles": [
+ *     {
+ *       "name": "admin",
+ *       "operationsAllowed": [
+ *         "CLUSTER:MANAGE",
+ *         "DATA:MANAGE"
+ *       ]
+ *     },
+ *     {
+ *       "name": "readRegionA",
+ *       "operationsAllowed": [
+ *         "DATA:READ"
+ *       ],
+ *       "regions": ["RegionA", "RegionB"]
+ *     }
+ *   ]
+ *   "users": [
+ *     {
+ *       "name": "admin",
+ *       "password": "secret".
+ *       "roles": ["admin"]
+ *     },
+ *     {
+ *       "name": "guest",
+ *       "password": "guest",
+ *       "roles": ["readRegionA"]
+ *     }
+ *   ]
+ * }
+ * 
+ */ +public class SampleSecurityManager + implements SecurityManager { + + public static class Role { + List permissions = new ArrayList<>(); + String name; + String serverGroup; + } + + public static class User { + String name; + Set roles = new HashSet<>(); + String pwd; + } + + private static Map acl = null; + + public static SampleSecurityManager create() throws IOException { + if (acl == null) { + setUpWithJsonFile("security.json"); + } + return new SampleSecurityManager(); + } + + public static void setUpWithJsonFile(String jsonFileName) throws IOException { + InputStream input = ClassLoader.getSystemResourceAsStream(jsonFileName); + if (input == null) { + throw new RuntimeException("Could not find the required JSON security file on the classpath: " + jsonFileName); + } + + StringWriter writer = new StringWriter(); + IOUtils.copy(input, writer, "UTF-8"); + String json = writer.toString(); + readSecurityDescriptor(json); + } + + protected static void readSecurityDescriptor(String json) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(json); + acl = new HashMap<>(); + Map roleMap = readRoles(jsonNode); + readUsers(acl, jsonNode, roleMap); + } + + private static void readUsers(Map acl, JsonNode node, Map roleMap) { + for (JsonNode u : node.get("users")) { + User user = new User(); + user.name = u.get("name").asText(); + + if (u.has("password")) { + user.pwd = u.get("password").asText(); + } else { + user.pwd = user.name; + } + + for (JsonNode r : u.get("roles")) { + user.roles.add(roleMap.get(r.asText())); + } + + acl.put(user.name, user); + } + } + + private static Map readRoles(JsonNode jsonNode) { + Map roleMap = new HashMap<>(); + for (JsonNode r : jsonNode.get("roles")) { + Role role = new Role(); + role.name = r.get("name").asText(); + String regionNames = null; + String keys = null; + + JsonNode regions = r.get("regions"); + if (regions != null) { + if (regions.isArray()) { + regionNames = StreamSupport.stream(regions.spliterator(), false) + .map(JsonNode::asText) + .collect(Collectors.joining(",")); + } else { + regionNames = regions.asText(); + } + } + + for (JsonNode op : r.get("operationsAllowed")) { + String[] parts = op.asText().split(":"); + String resourcePart = (parts.length > 0) ? parts[0] : null; + String operationPart = (parts.length > 1) ? parts[1] : null; + if(parts.length>2){ + regionNames = parts[2]; + } + if(parts.length>3){ + keys = parts[3]; + } + String regionPart = (regionNames != null) ? regionNames : "*"; + String keyPart = (keys !=null) ? keys : "*"; + + role.permissions.add(new GeodePermission(resourcePart, operationPart, regionPart, keyPart)); + } + + roleMap.put(role.name, role); + + if (r.has("serverGroup")) { + role.serverGroup = r.get("serverGroup").asText(); + } + } + + return roleMap; + } + public static Map getAcl() { + return acl; + } + + private Principal principal = null; + + + @Override + public boolean authorize(Principal principal, GeodePermission context) { + if (principal == null) return false; + + User user = acl.get(principal.getName()); + if (user == null) return false; // this user is not authorized to do anything + + // check if the user has this permission defined in the context + for (Role role : acl.get(user.name).roles) { + for (Permission permitted : role.permissions) { + if (permitted.implies(context)) { + return true; + } + } + } + + return false; + } + + @Override + public void init(Properties props) throws NotAuthorizedException { + } + + @Override + public Principal authenticate(Properties props) throws AuthenticationFailedException { + String user = props.getProperty(ResourceConstants.USER_NAME); + String pwd = props.getProperty(ResourceConstants.PASSWORD); + + User userObj = acl.get(user); + if (userObj == null) { + throw new AuthenticationFailedException("Wrong username/password"); + } + + if (user != null && !userObj.pwd.equals(pwd) && !"".equals(user)) { + throw new AuthenticationFailedException("Wrong username/password"); + } + + return new JMXPrincipal(user); + } + + protected static String readFile(String name) throws IOException { + File file = new File(name); + FileReader reader = new FileReader(file); + char[] buffer = new char[(int) file.length()]; + reader.read(buffer); + String json = new String(buffer); + reader.close(); + return json; + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/25a80da3/geode-core/src/test/java/com/gemstone/gemfire/security/IntegratedSecurityCacheLifecycleIntegrationTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/IntegratedSecurityCacheLifecycleIntegrationTest.java b/geode-core/src/test/java/com/gemstone/gemfire/security/IntegratedSecurityCacheLifecycleIntegrationTest.java index 96a4a51..0b97888 100644 --- a/geode-core/src/test/java/com/gemstone/gemfire/security/IntegratedSecurityCacheLifecycleIntegrationTest.java +++ b/geode-core/src/test/java/com/gemstone/gemfire/security/IntegratedSecurityCacheLifecycleIntegrationTest.java @@ -26,13 +26,14 @@ import java.util.Properties; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.CacheFactory; import com.gemstone.gemfire.test.junit.categories.IntegrationTest; +import com.gemstone.gemfire.test.junit.categories.SecurityTest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; -@Category(IntegrationTest.class) +@Category({IntegrationTest.class, SecurityTest.class}) public class IntegratedSecurityCacheLifecycleIntegrationTest { private static SpySecurityManager spySecurityManager;