http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipalTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipalTest.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipalTest.java deleted file mode 100644 index 7fbb454..0000000 --- a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipalTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 static org.assertj.core.api.Assertions.*; - -import java.io.Serializable; - -import org.apache.commons.lang.SerializationUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import com.gemstone.gemfire.test.junit.categories.SecurityTest; -import com.gemstone.gemfire.test.junit.categories.UnitTest; - -/** - * Unit tests for {@link UsernamePrincipal} - */ -@Category({ UnitTest.class, SecurityTest.class }) -public class UsernamePrincipalTest { - - @Test - public void isSerializable() throws Exception { - assertThat(UsernamePrincipal.class).isInstanceOf(Serializable.class); - } - - @Test - public void canBeSerialized() throws Exception { - String name = "jsmith"; - UsernamePrincipal instance = new UsernamePrincipal(name); - - UsernamePrincipal cloned = (UsernamePrincipal) SerializationUtils.clone(instance); - - assertThat(cloned.getName()).isEqualTo(name); - } -}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlAuthorization.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlAuthorization.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlAuthorization.java deleted file mode 100755 index 4349260..0000000 --- a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlAuthorization.java +++ /dev/null @@ -1,615 +0,0 @@ -/* - * 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.IOException; -import java.io.InputStream; -import java.security.Principal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; - -import com.gemstone.gemfire.LogWriter; -import com.gemstone.gemfire.cache.Cache; -import com.gemstone.gemfire.cache.operations.ExecuteFunctionOperationContext; -import com.gemstone.gemfire.cache.operations.OperationContext; -import com.gemstone.gemfire.cache.operations.OperationContext.OperationCode; -import com.gemstone.gemfire.cache.operations.QueryOperationContext; -import com.gemstone.gemfire.distributed.DistributedMember; -import com.gemstone.gemfire.security.AccessControl; -import com.gemstone.gemfire.security.NotAuthorizedException; - -/** - * An implementation of the {@link AccessControl} interface that allows - * authorization using the permissions as specified in the given XML - * file. - * - * The format of the XML file is specified in <a href="authz5_5.dtd"/>. It - * implements a role-based authorization at the operation level for each region. - * Each principal name may be associated with a set of roles. The name of the - * principal is obtained using the {@link Principal#getName()} method and no other - * information of the principal is utilized. Each role can be provided - * permissions to execute operations for each region. - * - * The top-level element in the XML is "acl" tag that contains the "role" and - * "permission" tags. The "role" tag contains the list of users that have been - * given that role. The name of the role is specified in the "role" attribute - * and the users are contained in the "user" tags insided the "role" tag. - * - * The "permissions" tag contains the list of operations allowed for a - * particular region. The role name is specified as the "role" attribute, the - * list of comma separated region names as the optional "regions" attribute and - * the operation names are contained in the "operation" tags inside the - * "permissions" tag. The allowed operation names are: GET, PUT, PUTALL, - * DESTROY, REGISTER_INTEREST, UNREGISTER_INTEREST, CONTAINS_KEY, KEY_SET, - * QUERY, EXECUTE_CQ, STOP_CQ, CLOSE_CQ, REGION_CLEAR, REGION_CREATE, - * REGION_DESTROY. These correspond to the operations in the - * {@link OperationCode} enumeration with the same name. - * - * When no region name is specified then the operation is allowed for all - * regions in the cache. Any permissions specified for regions using the - * "regions" attribute override these permissions. This allows users to provide - * generic permissions without any region name, and override for specific - * regions specified using the "regions" attribute. A cache-level operation - * (e.g. {@link OperationCode#REGION_DESTROY}) specified for a particular region - * is ignored i.e. the cache-level operations are only applicable when no region - * name is specified. A {@link OperationCode#QUERY} operation is permitted when - * either the {@code QUERY} permission is provided at the cache-level for - * the user or when {@code QUERY} permission is provided for all the - * regions that are part of the query string. - * - * Any roles specified in the "user" tag that do not have a specified permission - * set using the "permission" tags are ignored. When no {@link Principal} is - * associated with the current connection, then empty user name is used to - * search for the roles so an empty user name can be used to specify roles of - * unauthenticated clients (i.e. {@code Everyone}). - * - * This sample implementation is useful only for pre-operation checks and should - * not be used for post-operation authorization since it does nothing useful for - * post-operation case. - * - * @since GemFire 5.5 - */ -public class XmlAuthorization implements AccessControl { - - public static final String DOC_URI_PROP_NAME = "security-authz-xml-uri"; - - private static final Object sync = new Object(); - private static final String EMPTY_VALUE = ""; - - private static final String TAG_ROLE = "role"; - private static final String TAG_USER = "user"; - private static final String TAG_PERMS = "permission"; - private static final String TAG_OP = "operation"; - - private static final String ATTR_ROLENAME = "name"; - private static final String ATTR_ROLE = "role"; - private static final String ATTR_REGIONS = "regions"; - private static final String ATTR_FUNCTION_IDS = "functionIds"; - private static final String ATTR_FUNCTION_OPTIMIZE_FOR_WRITE = "optimizeForWrite"; - private static final String ATTR_FUNCTION_KEY_SET = "keySet"; - - private static String currentDocUri = null; - private static Map<String, HashSet<String>> userRoles = null; - private static Map<String, Map<String, Map<OperationCode, FunctionSecurityPrmsHolder>>> rolePermissions = null; - private static NotAuthorizedException xmlLoadFailure = null; - - private final Map<String, Map<OperationCode, FunctionSecurityPrmsHolder>> allowedOps; - - protected LogWriter systemLogWriter; - protected LogWriter securityLogWriter; - - /** - * Public static factory method to create an instance of - * {@code XmlAuthorization}. The fully qualified name of the class - * ({@code com.gemstone.gemfire.security.templates.XmlAuthorization.create}) - * should be mentioned as the {@code security-client-accessor} system - * property to enable pre-operation authorization checks as implemented in - * this class. - * - * @return an object of {@code XmlAuthorization} class - */ - public static AccessControl create() { - return new XmlAuthorization(); - } - - /** - * Clear all the statically cached information. - */ - public static void clear() { - XmlAuthorization.currentDocUri = null; - if (XmlAuthorization.userRoles != null) { - XmlAuthorization.userRoles.clear(); - XmlAuthorization.userRoles = null; - } - if (XmlAuthorization.rolePermissions != null) { - XmlAuthorization.rolePermissions.clear(); - XmlAuthorization.rolePermissions = null; - } - XmlAuthorization.xmlLoadFailure = null; - } - - /** - * Change the region name to a standard format having single '/' as separator - * and starting with a '/' as in standard POSIX paths - */ - public static String normalizeRegionName(final String regionName) { - if (regionName == null || regionName.length() == 0) { - return EMPTY_VALUE; - } - - char[] resultName = new char[regionName.length() + 1]; - boolean changed = false; - boolean isPrevCharSlash = false; - int startIndex; - - if (regionName.charAt(0) != '/') { - changed = true; - startIndex = 0; - } else { - isPrevCharSlash = true; - startIndex = 1; - } - - resultName[0] = '/'; - int resultLength = 1; - - // Replace all more than one '/'s with a single '/' - for (int index = startIndex; index < regionName.length(); ++index) { - char currChar = regionName.charAt(index); - if (currChar == '/') { - if (isPrevCharSlash) { - changed = true; - continue; - } - isPrevCharSlash = true; - } else { - isPrevCharSlash = false; - } - resultName[resultLength++] = currChar; - } - - // Remove any trailing slash - if (resultName[resultLength - 1] == '/') { - --resultLength; - changed = true; - } - - if (changed) { - return new String(resultName, 0, resultLength); - } else { - return regionName; - } - } - - private XmlAuthorization() { - this.allowedOps = new HashMap<String, Map<OperationCode, FunctionSecurityPrmsHolder>>(); - this.systemLogWriter = null; - this.securityLogWriter = null; - } - - /** - * Initialize the {@code XmlAuthorization} callback for a client having - * the given principal. - * - * This method caches the full XML authorization file the first time it is - * invoked and caches all the permissions for the provided - * {@code principal} to speed up lookup the - * {@code authorizeOperation} calls. The permissions for the principal - * are maintained as a {@link Map} of region name to the {@link HashSet} of - * operations allowed for that region. A global entry with region name as - * empty string is also made for permissions provided for all the regions. - * - * @param principal - * the principal associated with the authenticated client - * @param cache - * reference to the cache object - * @param remoteMember - * the {@link DistributedMember} object for the remote authenticated - * client - * - * @throws NotAuthorizedException - * if some exception condition happens during the initialization - * while reading the XML; in such a case all subsequent client - * operations will throw {@code NotAuthorizedException} - */ - @Override - public void init(final Principal principal, final DistributedMember remoteMember, final Cache cache) throws NotAuthorizedException { - synchronized (sync) { - XmlAuthorization.init(cache); - } - - this.systemLogWriter = cache.getLogger(); - this.securityLogWriter = cache.getSecurityLogger(); - - String name; - if (principal != null) { - name = principal.getName(); - } else { - name = EMPTY_VALUE; - } - - HashSet<String> roles = XmlAuthorization.userRoles.get(name); - if (roles != null) { - for (String roleName : roles) { - Map<String, Map<OperationCode, FunctionSecurityPrmsHolder>> regionOperationMap = XmlAuthorization.rolePermissions.get(roleName); - if (regionOperationMap != null) { - for (Map.Entry<String, Map<OperationCode, FunctionSecurityPrmsHolder>> regionEntry : regionOperationMap.entrySet()) { - String regionName = regionEntry.getKey(); - Map<OperationCode, FunctionSecurityPrmsHolder> regionOperations = this.allowedOps.get(regionName); - if (regionOperations == null) { - regionOperations = new HashMap<OperationCode, FunctionSecurityPrmsHolder>(); - this.allowedOps.put(regionName, regionOperations); - } - regionOperations.putAll(regionEntry.getValue()); - } - } - } - } - } - - /** - * Return true if the given operation is allowed for the cache/region. - * - * This looks up the cached permissions of the principal in the map for the - * provided region name. If none are found then the global permissions with - * empty region name are looked up. The operation is allowed if it is found - * this permission list. - * - * @param regionName - * When null then it indicates a cache-level operation, else the - * name of the region for the operation. - * @param context - * the data required by the operation - * - * @return true if the operation is authorized and false otherwise - */ - @Override - public boolean authorizeOperation(String regionName, final OperationContext context) { - Map<OperationCode, FunctionSecurityPrmsHolder> operationMap; - - // Check GET permissions for updates from server to client - if (context.isClientUpdate()) { - operationMap = this.allowedOps.get(regionName); - if (operationMap == null && regionName.length() > 0) { - operationMap = this.allowedOps.get(EMPTY_VALUE); - } - if (operationMap != null) { - return operationMap.containsKey(OperationCode.GET); - } - return false; - } - - OperationCode opCode = context.getOperationCode(); - if (opCode.isQuery() || opCode.isExecuteCQ() || opCode.isCloseCQ() || opCode.isStopCQ()) { - // First check if cache-level permission has been provided - operationMap = this.allowedOps.get(EMPTY_VALUE); - boolean globalPermission = (operationMap != null && operationMap .containsKey(opCode)); - Set<String> regionNames = ((QueryOperationContext)context) .getRegionNames(); - if (regionNames == null || regionNames.size() == 0) { - return globalPermission; - } - - for (String r : regionNames) { - regionName = normalizeRegionName(r); - operationMap = this.allowedOps.get(regionName); - if (operationMap == null) { - if (!globalPermission) { - return false; - } - } else if (!operationMap.containsKey(opCode)) { - return false; - } - } - return true; - } - - final String normalizedRegionName = normalizeRegionName(regionName); - operationMap = this.allowedOps.get(normalizedRegionName); - if (operationMap == null && normalizedRegionName.length() > 0) { - operationMap = this.allowedOps.get(EMPTY_VALUE); - } - if (operationMap != null) { - if (context.getOperationCode() != OperationCode.EXECUTE_FUNCTION) { - return operationMap.containsKey(context.getOperationCode()); - - } else { - if (!operationMap.containsKey(context.getOperationCode())) { - return false; - - } else { - if (!context.isPostOperation()) { - FunctionSecurityPrmsHolder functionParameter = operationMap.get(context.getOperationCode()); - ExecuteFunctionOperationContext functionContext = (ExecuteFunctionOperationContext) context; - // OnRegion execution - if (functionContext.getRegionName() != null) { - if (functionParameter.isOptimizeForWrite() != null && functionParameter.isOptimizeForWrite().booleanValue() != functionContext.isOptimizeForWrite()) { - return false; - } - if (functionParameter.getFunctionIds() != null && !functionParameter.getFunctionIds().contains( functionContext.getFunctionId())) { - return false; - } - if (functionParameter.getKeySet() != null && functionContext.getKeySet() != null) { - if (functionContext.getKeySet().containsAll( functionParameter.getKeySet())) { - return false; - } - } - return true; - - } else {// On Server execution - if (functionParameter.getFunctionIds() != null && !functionParameter.getFunctionIds().contains(functionContext.getFunctionId())) { - return false; - } - return true; - } - - } else { - ExecuteFunctionOperationContext functionContext = (ExecuteFunctionOperationContext)context; - FunctionSecurityPrmsHolder functionParameter = operationMap.get(context.getOperationCode()); - if (functionContext.getRegionName() != null) { - if (functionContext.getResult() instanceof ArrayList && functionParameter.getKeySet() != null) { - ArrayList<String> resultList = (ArrayList)functionContext.getResult(); - Set<String> nonAllowedKeys = functionParameter.getKeySet(); - if (resultList.containsAll(nonAllowedKeys)) { - return false; - } - } - return true; - - } else { - ArrayList<String> resultList = (ArrayList)functionContext.getResult(); - final String inSecureItem = "Insecure item"; - if (resultList.contains(inSecureItem)) { - return false; - } - return true; - } - } - } - } - } - return false; - } - - /** - * Clears the cached information for this principal. - */ - @Override - public void close() { - this.allowedOps.clear(); - } - - /** Get the attribute value for a given attribute name of a node. */ - private static String getAttributeValue(final Node node, final String attrName) { - NamedNodeMap attrMap = node.getAttributes(); - Node attrNode; - if (attrMap != null && (attrNode = attrMap.getNamedItem(attrName)) != null) { - return ((Attr)attrNode).getValue(); - } - return EMPTY_VALUE; - } - - /** Get the string contained in the first text child of the node. */ - private static String getNodeValue(final Node node) { - NodeList childNodes = node.getChildNodes(); - for (int index = 0; index < childNodes.getLength(); index++) { - Node childNode = childNodes.item(index); - if (childNode.getNodeType() == Node.TEXT_NODE) { - return childNode.getNodeValue(); - } - } - return EMPTY_VALUE; - } - - /** - * Cache authorization information for all users statically. This method is - * not thread-safe and is should either be invoked only once, or the caller - * should take the appropriate locks. - * - * @param cache reference to the cache object for the distributed system - */ - private static void init(final Cache cache) throws NotAuthorizedException { - final LogWriter systemLogWriter = cache.getLogger(); - final String xmlDocumentUri = (String)cache.getDistributedSystem().getSecurityProperties().get(DOC_URI_PROP_NAME); - - try { - if (xmlDocumentUri == null) { - throw new NotAuthorizedException("No ACL file defined using tag [" + DOC_URI_PROP_NAME + "] in system properties"); - } - if (xmlDocumentUri.equals(XmlAuthorization.currentDocUri)) { - if (XmlAuthorization.xmlLoadFailure != null) { - throw XmlAuthorization.xmlLoadFailure; - } - return; - } - - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setIgnoringComments(true); - factory.setIgnoringElementContentWhitespace(true); - factory.setValidating(true); - - final DocumentBuilder builder = factory.newDocumentBuilder(); - final XmlErrorHandler errorHandler = new XmlErrorHandler(systemLogWriter, xmlDocumentUri); - builder.setErrorHandler(errorHandler); - builder.setEntityResolver(new AuthzDtdResolver()); - - final Document xmlDocument = builder.parse(xmlDocumentUri); - - XmlAuthorization.userRoles = new HashMap<String, HashSet<String>>(); - XmlAuthorization.rolePermissions = new HashMap<String, Map<String, Map<OperationCode, FunctionSecurityPrmsHolder>>>(); - - final NodeList roleUserNodes = xmlDocument.getElementsByTagName(TAG_ROLE); - - for (int roleIndex = 0; roleIndex < roleUserNodes.getLength(); roleIndex++) { - final Node roleUserNode = roleUserNodes.item(roleIndex); - final String roleName = getAttributeValue(roleUserNode, ATTR_ROLENAME); - final NodeList userNodes = roleUserNode.getChildNodes(); - - for (int userIndex = 0; userIndex < userNodes.getLength(); userIndex++) { - final Node userNode = userNodes.item(userIndex); - - if (TAG_USER.equals(userNode.getNodeName())) { - final String userName = getNodeValue(userNode); - HashSet<String> userRoleSet = XmlAuthorization.userRoles.get(userName); - if (userRoleSet == null) { - userRoleSet = new HashSet<String>(); - XmlAuthorization.userRoles.put(userName, userRoleSet); - } - userRoleSet.add(roleName); - - } else { - throw new SAXParseException("Unknown tag [" + userNode.getNodeName() + "] as child of tag [" + TAG_ROLE + ']', null); - } - } - } - - final NodeList rolePermissionNodes = xmlDocument.getElementsByTagName(TAG_PERMS); - - for (int permIndex = 0; permIndex < rolePermissionNodes.getLength(); permIndex++) { - final Node rolePermissionNode = rolePermissionNodes.item(permIndex); - final String roleName = getAttributeValue(rolePermissionNode, ATTR_ROLE); - Map<String, Map<OperationCode, FunctionSecurityPrmsHolder>> regionOperationMap = XmlAuthorization.rolePermissions.get(roleName); - - if (regionOperationMap == null) { - regionOperationMap = new HashMap<String, Map<OperationCode, FunctionSecurityPrmsHolder>>(); - XmlAuthorization.rolePermissions.put(roleName, regionOperationMap); - } - - final NodeList operationNodes = rolePermissionNode.getChildNodes(); - final HashMap<OperationCode, FunctionSecurityPrmsHolder> operationMap = new HashMap<OperationCode, FunctionSecurityPrmsHolder>(); - - for (int opIndex = 0; opIndex < operationNodes.getLength(); opIndex++) { - final Node operationNode = operationNodes.item(opIndex); - - if (TAG_OP.equals(operationNode.getNodeName())) { - final String operationName = getNodeValue(operationNode); - final OperationCode code = OperationCode.valueOf(operationName); - - if (code == null) { - throw new SAXParseException("Unknown operation [" + operationName + ']', null); - } - - if (code != OperationCode.EXECUTE_FUNCTION) { - operationMap.put(code, null); - - } else { - final String optimizeForWrite = getAttributeValue(operationNode, ATTR_FUNCTION_OPTIMIZE_FOR_WRITE); - final String functionAttr = getAttributeValue(operationNode, ATTR_FUNCTION_IDS); - final String keysAttr = getAttributeValue(operationNode, ATTR_FUNCTION_KEY_SET); - - Boolean isOptimizeForWrite; - HashSet<String> functionIds; - HashSet<String> keySet; - - if (optimizeForWrite == null || optimizeForWrite.length() == 0) { - isOptimizeForWrite = null; - } else { - isOptimizeForWrite = Boolean.parseBoolean(optimizeForWrite); - } - - if (functionAttr == null || functionAttr.length() == 0) { - functionIds = null; - } else { - final String[] functionArray = functionAttr.split(","); - functionIds = new HashSet<String>(); - for (int strIndex = 0; strIndex < functionArray.length; ++strIndex) { - functionIds.add((functionArray[strIndex])); - } - } - - if (keysAttr == null || keysAttr.length() == 0) { - keySet = null; - } else { - final String[] keySetArray = keysAttr.split(","); - keySet = new HashSet<String>(); - for (int strIndex = 0; strIndex < keySetArray.length; ++strIndex) { - keySet.add((keySetArray[strIndex])); - } - } - - final FunctionSecurityPrmsHolder functionContext = new FunctionSecurityPrmsHolder(isOptimizeForWrite, functionIds, keySet); - operationMap.put(code, functionContext); - } - - } else { - throw new SAXParseException("Unknown tag [" + operationNode.getNodeName() + "] as child of tag [" + TAG_PERMS + ']', null); - } - } - - final String regionNames = getAttributeValue(rolePermissionNode, ATTR_REGIONS); - if (regionNames == null || regionNames.length() == 0) { - regionOperationMap.put(EMPTY_VALUE, operationMap); - } else { - final String[] regionNamesSplit = regionNames.split(","); - for (int strIndex = 0; strIndex < regionNamesSplit.length; ++strIndex) { - regionOperationMap.put(normalizeRegionName(regionNamesSplit[strIndex]), operationMap); - } - } - } - XmlAuthorization.currentDocUri = xmlDocumentUri; - - } catch (Exception ex) { - String message; - if (ex instanceof NotAuthorizedException) { - message = ex.getMessage(); - } - else { - message = ex.getClass().getName() + ": " + ex.getMessage(); - } - systemLogWriter.warning("XmlAuthorization.init: " + message); - XmlAuthorization.xmlLoadFailure = new NotAuthorizedException(message, ex); - throw XmlAuthorization.xmlLoadFailure; - } - } - - private static class AuthzDtdResolver implements EntityResolver { - final Pattern authzPattern = Pattern.compile("authz.*\\.dtd"); - - @Override - public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException { - try { - final Matcher matcher = authzPattern.matcher(systemId); - if (matcher.find()) { - final String dtdName = matcher.group(0); - final InputStream stream = XmlAuthorization.class.getResourceAsStream(dtdName); - return new InputSource(stream); - } - - } catch(Exception e) { - //do nothing, use the default resolver - } - - return null; - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlErrorHandler.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlErrorHandler.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlErrorHandler.java deleted file mode 100755 index c770eb7..0000000 --- a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlErrorHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 org.apache.logging.log4j.Logger; -import org.xml.sax.ErrorHandler; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; - -import com.gemstone.gemfire.LogWriter; -import com.gemstone.gemfire.internal.logging.LogService; - -/** - * Implementation of {@link ErrorHandler} interface to handle validation errors - * while XML parsing. - * - * This throws back exceptions raised for {@code error} and {@code fatalError} - * cases while a {@link LogWriter#warning(String)} level logging is done for - * the {@code warning} case. - * - * @since GemFire 5.5 - */ -public class XmlErrorHandler implements ErrorHandler { - - private static final Logger logger = LogService.getLogger(); - - private final LogWriter systemLogWriter; - private final String xmlFileName; - - public XmlErrorHandler(final LogWriter systemLogWriter, final String xmlFileName) { - this.systemLogWriter = systemLogWriter; - this.xmlFileName = xmlFileName; - } - - /** - * Throws back the exception with the name of the XML file and the position - * where the exception occurred. - */ - @Override - public void error(final SAXParseException exception) throws SAXException { - throw new SAXParseException("Error while parsing XML at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), null, exception); - } - - /** - * Throws back the exception with the name of the XML file and the position - * where the exception occurred. - */ - @Override - public void fatalError(final SAXParseException exception) throws SAXException { - throw new SAXParseException("Fatal error while parsing XML at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), null, exception); - } - - /** - * Log the exception at {@link LogWriter#warning(String)} level with XML - * filename and the position of exception in the file. - */ - @Override - public void warning(final SAXParseException exception) throws SAXException { - this.systemLogWriter.warning("Warning while parsing XML [" + this.xmlFileName + "] at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception); - } -} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/org/apache/geode/security/AbstractSecureServerDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/security/AbstractSecureServerDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/AbstractSecureServerDUnitTest.java new file mode 100644 index 0000000..fd38814 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/security/AbstractSecureServerDUnitTest.java @@ -0,0 +1,162 @@ +/* + * 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; + +import static com.gemstone.gemfire.distributed.ConfigurationProperties.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.geode.security.templates.SampleSecurityManager; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.Before; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionShortcut; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; +import com.gemstone.gemfire.cache.client.ClientRegionShortcut; +import com.gemstone.gemfire.cache.server.CacheServer; +import com.gemstone.gemfire.distributed.*; +import com.gemstone.gemfire.security.templates.UserPasswordAuthInit; +import com.gemstone.gemfire.test.dunit.Host; +import com.gemstone.gemfire.test.dunit.Invoke; +import com.gemstone.gemfire.test.dunit.VM; +import com.gemstone.gemfire.test.dunit.cache.internal.JUnit4CacheTestCase; + +public class AbstractSecureServerDUnitTest extends JUnit4CacheTestCase { + + protected static final String REGION_NAME = "AuthRegion"; + + protected VM client1 = null; + protected VM client2 = null; + protected VM client3 = null; + protected int serverPort; + + // child classes can customize these parameters + protected Class postProcessor = null; + protected boolean pdxPersistent = false; + protected int jmxPort = 0; + protected int restPort = 0; + protected Map<String, Object> values; + protected volatile Properties dsProperties; + + public AbstractSecureServerDUnitTest(){ + values = new HashMap(); + for(int i=0; i<5; i++){ + values.put("key"+i, "value"+i); + } + } + + @Before + public void before() throws Exception { + final Host host = Host.getHost(0); + this.client1 = host.getVM(1); + this.client2 = host.getVM(2); + this.client3 = host.getVM(3); + + Properties props = new Properties(); + props.setProperty(SampleSecurityManager.SECURITY_JSON, "com/gemstone/gemfire/management/internal/security/clientServer.json"); + props.setProperty(SECURITY_MANAGER, SampleSecurityManager.class.getName()); +// props.setProperty(SECURITY_SHIRO_INIT, "shiro.ini"); + props.setProperty(LOCATORS, ""); + props.setProperty(MCAST_PORT, "0"); + if (postProcessor!=null) { + props.setProperty(SECURITY_POST_PROCESSOR, postProcessor.getName()); + } + props.setProperty(SECURITY_LOG_LEVEL, "finest"); + + props.setProperty("security-pdx", pdxPersistent+""); + if(jmxPort>0){ + props.put(JMX_MANAGER, "true"); + props.put(JMX_MANAGER_START, "true"); + props.put(JMX_MANAGER_PORT, String.valueOf(jmxPort)); + } + + if(restPort>0){ + props.setProperty(START_DEV_REST_API, "true"); + props.setProperty(HTTP_SERVICE_BIND_ADDRESS, "localhost"); + props.setProperty(HTTP_SERVICE_PORT, restPort+""); + } + + props.put(ConfigurationProperties.ENABLE_NETWORK_PARTITION_DETECTION, "false"); + + this.dsProperties = props; + + getSystem(props); + + CacheFactory cf = new CacheFactory(); + cf.setPdxPersistent(pdxPersistent); + cf.setPdxReadSerialized(pdxPersistent); + Cache cache = getCache(cf); + + Region region = cache.createRegionFactory(RegionShortcut.REPLICATE).create(REGION_NAME); + + CacheServer server = cache.addCacheServer(); + server.setPort(0); + server.start(); + + this.serverPort = server.getPort(); + + for(Entry entry:values.entrySet()){ + region.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public Properties getDistributedSystemProperties() { + return dsProperties; + } + + @Override + public void preTearDownCacheTestCase() throws Exception { + Invoke.invokeInEveryVM(()->closeCache()); + closeCache(); + } + + public static void assertNotAuthorized(ThrowingCallable shouldRaiseThrowable, String permString) { + assertThatThrownBy(shouldRaiseThrowable).hasMessageContaining(permString); + } + + public static Properties createClientProperties(String userName, String password) { + Properties props = new Properties(); + props.setProperty(UserPasswordAuthInit.USER_NAME, userName); + props.setProperty(UserPasswordAuthInit.PASSWORD, password); + props.setProperty(LOG_LEVEL, "fine"); + props.setProperty(LOCATORS, ""); + props.setProperty(MCAST_PORT, "0"); + props.setProperty(SECURITY_CLIENT_AUTH_INIT, UserPasswordAuthInit.class.getName() + ".create"); + props.setProperty(SECURITY_LOG_LEVEL, "finest"); + return props; + } + + public static ClientCache createClientCache(String username, String password, int serverPort){ + ClientCache cache = new ClientCacheFactory(createClientProperties(username, password)) + .setPoolSubscriptionEnabled(true) + .addPoolServer("localhost", serverPort) + .create(); + + cache.createClientRegionFactory(ClientRegionShortcut.PROXY).create(REGION_NAME); + return cache; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationDUnitTest.java new file mode 100644 index 0000000..dbc782f --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationDUnitTest.java @@ -0,0 +1,90 @@ +/* + * 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; + +import com.gemstone.gemfire.test.junit.categories.DistributedTest; +import com.gemstone.gemfire.test.junit.categories.FlakyTest; +import com.gemstone.gemfire.test.junit.categories.SecurityTest; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Test for authentication from client to server. This tests for both valid and + * invalid credentials/modules. It also checks for authentication + * success/failure in case of failover and for the notification channel. + * + * @since GemFire 5.5 + */ +@Category({ DistributedTest.class, SecurityTest.class }) +public class ClientAuthenticationDUnitTest extends ClientAuthenticationTestCase { + + @Test + public void testValidCredentials() throws Exception { + doTestValidCredentials(false); + } + + @Test + public void testNoCredentials() throws Exception { + doTestNoCredentials(false); + } + + @Test + public void testInvalidCredentials() throws Exception { + doTestInvalidCredentials(false); + } + + @Test + public void testInvalidAuthInit() throws Exception { + doTestInvalidAuthInit(false); + } + + @Test + public void testNoAuthInitWithCredentials() throws Exception { + doTestNoAuthInitWithCredentials(false); + } + + @Test + public void testInvalidAuthenticator() throws Exception { + doTestInvalidAuthenticator(false); + } + + @Test + public void testNoAuthenticatorWithCredentials() throws Exception { + doTestNoAuthenticatorWithCredentials(false); + } + + @Test + public void testCredentialsWithFailover() throws Exception { + doTestCredentialsWithFailover(false); + } + + @Category(FlakyTest.class) // GEODE-838: random ports, thread sleeps, time sensitive + @Test + public void testCredentialsForNotifications() throws Exception { + doTestCredentialsForNotifications(false); + } + + @Ignore("Disabled for unknown reason") + @Test + public void testValidCredentialsForMultipleUsers() throws Exception { + doTestValidCredentials(true); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationPart2DUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationPart2DUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationPart2DUnitTest.java new file mode 100644 index 0000000..24fcc3f --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationPart2DUnitTest.java @@ -0,0 +1,76 @@ +/* + * 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; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import com.gemstone.gemfire.test.junit.categories.DistributedTest; +import com.gemstone.gemfire.test.junit.categories.SecurityTest; + +/** + * this class contains test methods that used to be in its superclass but + * that test started taking too long and caused dunit runs to hang + */ +@Category({ DistributedTest.class, SecurityTest.class }) +public class ClientAuthenticationPart2DUnitTest extends ClientAuthenticationTestCase { + + @Test + public void testNoCredentialsForMultipleUsers() throws Exception { + doTestNoCredentials(true); + } + + @Test + public void testInvalidCredentialsForMultipleUsers() throws Exception { + doTestInvalidCredentials(true); + } + + @Test + public void testInvalidAuthInitForMultipleUsers() throws Exception { + doTestInvalidAuthInit(true); + } + + @Test + public void testNoAuthInitWithCredentialsForMultipleUsers() throws Exception { + doTestNoAuthInitWithCredentials(true); + } + + @Test + public void testInvalidAuthenitcatorForMultipleUsers() throws Exception { + doTestInvalidAuthenticator(true); + } + + @Test + public void testNoAuthenticatorWithCredentialsForMultipleUsers() throws Exception { + doTestNoAuthenticatorWithCredentials(true); + } + + @Ignore("Disabled for unknown reason") + @Test + public void testCredentialsWithFailoverForMultipleUsers() throws Exception { + doTestCredentialsWithFailover(true); + } + + @Ignore("Disabled for unknown reason") + @Test + public void testCredentialsForNotificationsForMultipleUsers() throws Exception { + doTestCredentialsForNotifications(true); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestCase.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestCase.java b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestCase.java new file mode 100644 index 0000000..7e6d022 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestCase.java @@ -0,0 +1,562 @@ +/* + * 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; + +import static com.gemstone.gemfire.internal.AvailablePort.*; +import static com.gemstone.gemfire.security.ClientAuthenticationTestUtils.createCacheClient; +import static com.gemstone.gemfire.security.ClientAuthenticationTestUtils.createCacheServer; +import static com.gemstone.gemfire.security.ClientAuthenticationTestUtils.*; +import static com.gemstone.gemfire.security.SecurityTestUtils.*; +import static com.gemstone.gemfire.security.SecurityTestUtils.createCacheClient; +import static com.gemstone.gemfire.test.dunit.IgnoredException.*; +import static com.gemstone.gemfire.test.dunit.LogWriterUtils.*; +import static com.gemstone.gemfire.test.dunit.Wait.*; + +import java.io.IOException; +import java.util.Properties; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; + +import com.gemstone.gemfire.security.generator.CredentialGenerator; +import com.gemstone.gemfire.security.generator.DummyCredentialGenerator; +import com.gemstone.gemfire.test.dunit.Host; +import com.gemstone.gemfire.test.dunit.VM; +import com.gemstone.gemfire.test.dunit.internal.JUnit4DistributedTestCase; + +public abstract class ClientAuthenticationTestCase extends JUnit4DistributedTestCase { + + private VM server1 = null; + private VM server2 = null; + private VM client1 = null; + private VM client2 = null; + + private static final String[] serverIgnoredExceptions = { + AuthenticationRequiredException.class.getName(), + AuthenticationFailedException.class.getName(), + GemFireSecurityException.class.getName(), + ClassNotFoundException.class.getName(), + IOException.class.getName(), + SSLException.class.getName(), + SSLHandshakeException.class.getName() + }; + + private static final String[] clientIgnoredExceptions = { + AuthenticationRequiredException.class.getName(), + AuthenticationFailedException.class.getName(), + SSLHandshakeException.class.getName() + }; + + @Override + public final void postSetUp() throws Exception { + final Host host = Host.getHost(0); + server1 = host.getVM(0); + server2 = host.getVM(1); + client1 = host.getVM(2); + client2 = host.getVM(3); + + addIgnoredException("Connection refused: connect"); + + server1.invoke(() -> registerExpectedExceptions(serverIgnoredExceptions)); + server2.invoke(() -> registerExpectedExceptions(serverIgnoredExceptions)); + client1.invoke(() -> registerExpectedExceptions(clientIgnoredExceptions)); + client2.invoke(() -> registerExpectedExceptions(clientIgnoredExceptions)); + } + + protected void doTestValidCredentials(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testValidCredentials: Using scheme: " + gen.classCode()); + getLogWriter().info("testValidCredentials: Using authenticator: " + authenticator); + getLogWriter().info("testValidCredentials: Using authinit: " + authInit); + + // Start the servers + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = server1.invoke(() -> createCacheServer(locPort1, locString, authenticator, extraProps, javaProps)); + int port2 = server2.invoke(() -> createCacheServer(locPort2, locString, authenticator, extraProps, javaProps)); + + // Start the clients with valid credentials + Properties credentials1 = gen.getValidCredentials(1); + Properties javaProps1 = gen.getJavaProperties(); + + getLogWriter().info("testValidCredentials: For first client credentials: " + credentials1 + " : " + javaProps1); + + Properties credentials2 = gen.getValidCredentials(2); + Properties javaProps2 = gen.getJavaProperties(); + + getLogWriter().info("testValidCredentials: For second client credentials: " + credentials2 + " : " + javaProps2); + + createClientsNoException(multiUser, authInit, port1, port2, credentials1, javaProps1, credentials2, javaProps2); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(2)); + + // Verify that the puts succeeded + client2.invoke(() -> doGets(2)); + + if (multiUser) { + client1.invoke(() -> doProxyCacheClose()); + client2.invoke(() -> doProxyCacheClose()); + client1.invoke(() -> doSimplePut("CacheClosedException")); + client2.invoke(() -> doSimpleGet("CacheClosedException")); + } + } + + protected void doTestNoCredentials(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testNoCredentials: Using scheme: " + gen.classCode()); + getLogWriter().info("testNoCredentials: Using authenticator: " + authenticator); + getLogWriter().info("testNoCredentials: Using authinit: " + authInit); + + // Start the servers + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = createServer1(extraProps, javaProps, authenticator, locPort1, locString); + int port2 = server2.invoke(() -> createCacheServer(locPort2, locString, authenticator, extraProps, javaProps)); + + // Start first client with valid credentials + Properties credentials1 = gen.getValidCredentials(1); + Properties javaProps1 = gen.getJavaProperties(); + + getLogWriter().info("testNoCredentials: For first client credentials: " + credentials1 + " : " + javaProps1); + + createClient1NoException(multiUser, authInit, port1, port2, credentials1, javaProps1); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(2)); + + // Trying to create the region on client2 + if (gen.classCode().equals(CredentialGenerator.ClassCode.SSL)) { + // For SSL the exception may not come since the server can close socket + // before handshake message is sent from client. However exception + // should come in any region operations. + client2.invoke(() -> createCacheClient(null, null, null, port1, port2, 0, multiUser, NO_EXCEPTION)); + client2.invoke(() -> doPuts(2, OTHER_EXCEPTION)); + + } else { + client2.invoke(() -> createCacheClient(null, null, null, port1, port2, 0, multiUser, AUTHREQ_EXCEPTION)); + } + } + + protected void doTestInvalidCredentials(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testInvalidCredentials: Using scheme: " + gen.classCode()); + getLogWriter().info("testInvalidCredentials: Using authenticator: " + authenticator); + getLogWriter().info("testInvalidCredentials: Using authinit: " + authInit); + + // Start the servers + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = createServer1(extraProps, javaProps, authenticator, locPort1, locString); + int port2 = server2.invoke(() -> createCacheServer(locPort2, locString, authenticator, extraProps, javaProps)); + + // Start first client with valid credentials + Properties credentials1 = gen.getValidCredentials(1); + Properties javaProps1 = gen.getJavaProperties(); + getLogWriter().info("testInvalidCredentials: For first client credentials: " + credentials1 + " : " + javaProps1); + + createClient1NoException(multiUser, authInit, port1, port2, credentials1, javaProps1); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(2)); + + // Start second client with invalid credentials + // Trying to create the region on client2 should throw a security + // exception + Properties credentials2 = gen.getInvalidCredentials(1); + Properties javaProps2 = gen.getJavaProperties(); + getLogWriter().info("testInvalidCredentials: For second client credentials: " + credentials2 + " : " + javaProps2); + + client2.invoke(() -> createCacheClient(authInit, credentials2, javaProps2, port1, port2, 0, multiUser, AUTHFAIL_EXCEPTION)); + } + + protected void doTestInvalidAuthInit(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + final Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + + getLogWriter().info("testInvalidAuthInit: Using scheme: " + gen.classCode()); + getLogWriter().info("testInvalidAuthInit: Using authenticator: " + authenticator); + + // Start the server + int locPort1 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = createServer1(extraProps, javaProps, authenticator, locPort1, locString); + Properties credentials = gen.getValidCredentials(1); + getLogWriter().info("testInvalidAuthInit: For first client credentials: " + credentials + " : " + javaProps); + + client1.invoke(() -> createCacheClient("com.gemstone.none", credentials, javaProps, new int[] { port1 }, 0, false, multiUser, true, SECURITY_EXCEPTION)); + } + + protected void doTestNoAuthInitWithCredentials(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + + getLogWriter().info("testNoAuthInitWithCredentials: Using scheme: " + gen.classCode()); + getLogWriter().info("testNoAuthInitWithCredentials: Using authenticator: " + authenticator); + + // Start the servers + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = createServer1(extraProps, javaProps, authenticator, locPort1, locString); + int port2 = server2.invoke(() -> createCacheServer(locPort2, locString, authenticator, extraProps, javaProps)); + + // Start the clients with valid credentials + Properties credentials1 = gen.getValidCredentials(1); + Properties javaProps1 = gen.getJavaProperties(); + getLogWriter().info("testNoAuthInitWithCredentials: For first client credentials: " + credentials1 + " : " + javaProps1); + + Properties credentials2 = gen.getValidCredentials(2); + Properties javaProps2 = gen.getJavaProperties(); + getLogWriter().info("testNoAuthInitWithCredentials: For second client credentials: " + credentials2 + " : " + javaProps2); + + client1.invoke(() -> createCacheClient(null, credentials1, javaProps1, port1, port2, 0, multiUser, AUTHREQ_EXCEPTION)); + client2.invoke(() -> createCacheClient(null, credentials2, javaProps2, port1, port2, 0, multiUser, AUTHREQ_EXCEPTION)); + client2.invoke(() -> closeCache()); + + // Now also try with invalid credentials + Properties credentials3 = gen.getInvalidCredentials(5); + Properties javaProps3 = gen.getJavaProperties(); + + client2.invoke(() -> createCacheClient(null, credentials3, javaProps3, port1, port2, 0, multiUser, AUTHREQ_EXCEPTION)); + } + + /** + * NOTE: "final boolean multiUser" is unused + */ + protected void doTestInvalidAuthenticator(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testInvalidAuthenticator: Using scheme: " + gen.classCode()); + getLogWriter().info("testInvalidAuthenticator: Using authinit: " + authInit); + + // Start the server with invalid authenticator + int locPort1 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + server1.invoke(() -> createCacheServer(locPort1, locString, "com.gemstone.gemfire.none", extraProps, javaProps, AUTHREQ_EXCEPTION)); + } + + protected void doTestNoAuthenticatorWithCredentials(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testNoAuthenticatorWithCredentials: Using scheme: " + gen.classCode()); + getLogWriter().info("testNoAuthenticatorWithCredentials: Using authinit: " + authInit); + + // Start the servers with no authenticator + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = server1.invoke(() -> createCacheServer(locPort1, locString, null, extraProps, javaProps)); + int port2 = server2.invoke(() -> createCacheServer(locPort2, locString, null, extraProps, javaProps)); + + // Clients should connect successfully and work properly with + // valid/invalid credentials when none are required on the server side + Properties credentials1 = gen.getValidCredentials(3); + Properties javaProps1 = gen.getJavaProperties(); + getLogWriter().info("testNoAuthenticatorWithCredentials: For first client credentials: " + credentials1 + " : " + javaProps1); + + Properties credentials2 = gen.getInvalidCredentials(5); + Properties javaProps2 = gen.getJavaProperties(); + getLogWriter().info("testNoAuthenticatorWithCredentials: For second client credentials: " + credentials2 + " : " + javaProps2); + + createClientsNoException(multiUser, authInit, port1, port2, credentials1, javaProps1, credentials2, javaProps2); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(2)); + + // Verify that the puts succeeded + client2.invoke(() -> doGets(2)); + } + + protected void doTestCredentialsWithFailover(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testCredentialsWithFailover: Using scheme: " + gen.classCode()); + getLogWriter().info("testCredentialsWithFailover: Using authenticator: " + authenticator); + getLogWriter().info("testCredentialsWithFailover: Using authinit: " + authInit); + + // Start the first server + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = server1.invoke(() -> createCacheServer(locPort1, locString, authenticator, extraProps, javaProps)); + + // Get a port for second server but do not start it + // This forces the clients to connect to the first server + int port2 = getRandomAvailablePort(SOCKET); + + // Start the clients with valid credentials + Properties credentials1 = gen.getValidCredentials(5); + Properties javaProps1 = gen.getJavaProperties(); + getLogWriter().info("testCredentialsWithFailover: For first client credentials: " + credentials1 + " : " + javaProps1); + + Properties credentials2 = gen.getValidCredentials(6); + Properties javaProps2 = gen.getJavaProperties(); + getLogWriter().info("testCredentialsWithFailover: For second client credentials: " + credentials2 + " : " + javaProps2); + + createClientsNoException(multiUser, authInit, port1, port2, credentials1, javaProps1, credentials2, javaProps2); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(2)); + // Verify that the puts succeeded + client2.invoke(() -> doGets(2)); + + // start the second one and stop the first server to force a failover + server2.invoke(() -> createCacheServer(locPort2, locString, port2, authenticator, extraProps, javaProps)); + server1.invoke(() -> closeCache()); + + // Perform some create/update operations from client1 + client1.invoke(() -> doNPuts(4)); + // Verify that the creates/updates succeeded + client2.invoke(() -> doNGets(4)); + + // Try to connect client2 with no credentials + // Verify that the creation of region throws security exception + if (gen.classCode().equals(CredentialGenerator.ClassCode.SSL)) { + // For SSL the exception may not come since the server can close socket + // before handshake message is sent from client. However exception + // should come in any region operations. + client2.invoke(() -> createCacheClient(null, null, null, port1, port2, 0, multiUser, NOFORCE_AUTHREQ_EXCEPTION)); + client2.invoke(() -> doPuts(2, OTHER_EXCEPTION)); + + } else { + client2.invoke(() -> createCacheClient(null, null, null, port1, port2, 0, multiUser, AUTHREQ_EXCEPTION)); + } + + // Now try to connect client1 with invalid credentials + // Verify that the creation of region throws security exception + Properties credentials3 = gen.getInvalidCredentials(7); + Properties javaProps3 = gen.getJavaProperties(); + getLogWriter().info("testCredentialsWithFailover: For first client invalid credentials: " + credentials3 + " : " + javaProps3); + + client1.invoke(() -> createCacheClient(authInit, credentials3, javaProps3, port1, port2, 0, multiUser, AUTHFAIL_EXCEPTION)); + + if (multiUser) { + client1.invoke(() -> doProxyCacheClose()); + client2.invoke(() -> doProxyCacheClose()); + client1.invoke(() -> doSimplePut("CacheClosedException")); + client2.invoke(() -> doSimpleGet("CacheClosedException")); + } + } + + protected void doTestCredentialsForNotifications(final boolean multiUser) throws Exception { + CredentialGenerator gen = new DummyCredentialGenerator(); + Properties extraProps = gen.getSystemProperties(); + Properties javaProps = gen.getJavaProperties(); + String authenticator = gen.getAuthenticator(); + String authInit = gen.getAuthInit(); + + getLogWriter().info("testCredentialsForNotifications: Using scheme: " + gen.classCode()); + getLogWriter().info("testCredentialsForNotifications: Using authenticator: " + authenticator); + getLogWriter().info("testCredentialsForNotifications: Using authinit: " + authInit); + + // Start the first server + int locPort1 = getLocatorPort(); + int locPort2 = getLocatorPort(); + String locString = getAndClearLocatorString(); + + int port1 = server1.invoke(() -> createCacheServer(locPort1, locString, authenticator, extraProps, javaProps)); + + // Get a port for second server but do not start it + // This forces the clients to connect to the first server + int port2 = getRandomAvailablePort(SOCKET); + + // Start the clients with valid credentials + Properties credentials1 = gen.getValidCredentials(3); + Properties javaProps1 = gen.getJavaProperties(); + getLogWriter().info("testCredentialsForNotifications: For first client credentials: " + credentials1 + " : " + javaProps1); + + Properties credentials2 = gen.getValidCredentials(4); + Properties javaProps2 = gen.getJavaProperties(); + getLogWriter().info("testCredentialsForNotifications: For second client credentials: " + credentials2 + " : " + javaProps2); + + createClient1NoException(multiUser, authInit, port1, port2, credentials1, javaProps1); + + // Set up zero forward connections to check notification handshake only + int zeroConns = 0; + createClient2NoException(multiUser, authInit, port1, port2, credentials2, javaProps2, zeroConns); + + // Register interest on all keys on second client + client2.invoke(() -> registerAllInterest()); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(2)); + + // Verify that the puts succeeded + client2.invoke(() -> doLocalGets(2)); + + // start the second one and stop the first server to force a failover + server2.invoke(() -> createCacheServer(locPort2, locString, port2, authenticator, extraProps, javaProps)); + server1.invoke(() -> closeCache()); + + // Wait for failover to complete + pause(500); + + // Perform some create/update operations from client1 + client1.invoke(() -> doNPuts(4)); + // Verify that the creates/updates succeeded + client2.invoke(() -> doNLocalGets(4)); + + // Try to connect client1 with no credentials + // Verify that the creation of region throws security exception + final int p = server1.invoke(() -> createCacheServer(locPort1, locString, 0, authenticator, extraProps, javaProps)); + if (gen.classCode().equals(CredentialGenerator.ClassCode.SSL)) { + // For SSL the exception may not come since the server can close socket + // before handshake message is sent from client. However exception + // should come in any region operations. + client1.invoke(() -> createCacheClient(null, null, null, p, port2, zeroConns, multiUser, NOFORCE_AUTHREQ_EXCEPTION)); + client1.invoke(() -> doPuts(2, OTHER_EXCEPTION)); + + } else { + client1.invoke(() -> createCacheClient(null, null, null, p, port2, zeroConns, multiUser, AUTHREQ_EXCEPTION)); + } + + // Now try to connect client2 with invalid credentials + // Verify that the creation of region throws security exception + credentials2 = gen.getInvalidCredentials(3); + javaProps2 = gen.getJavaProperties(); + getLogWriter().info("testCredentialsForNotifications: For second client invalid credentials: " + credentials2 + " : " + javaProps2); + + createClient2WithException(multiUser, authInit, p, port2, credentials2, javaProps2, zeroConns); + + // Now try to connect client2 with invalid auth-init method + // Trying to create the region on client with valid credentials should + // throw a security exception + client2.invoke(() -> createCacheClient("com.gemstone.none", credentials1, javaProps1, p, port2, zeroConns, multiUser, SECURITY_EXCEPTION)); + + // Try connection with null auth-init on clients. + // Skip this test for a scheme which does not have an authInit in the + // first place (e.g. SSL). + if (authInit != null && authInit.length() > 0) { + final int p1 = server1.invoke(() -> createCacheServer(locPort1, locString, 0, authenticator, extraProps, javaProps)); + final int p2 = server2.invoke(() -> createCacheServer(locPort2, locString, 0, authenticator, extraProps, javaProps)); + client1.invoke(() -> createCacheClient(null, credentials1, javaProps1, p1, p2, 0, multiUser, AUTHREQ_EXCEPTION)); + + createClient2AuthReqException(multiUser, p1, p2, credentials2, javaProps2, zeroConns); + createClient2AuthReqException(multiUser, p1, p2, credentials2, javaProps2, zeroConns); + + } else { + getLogWriter().info("testCredentialsForNotifications: Skipping null authInit for scheme [" + gen.classCode() + "] which has no authInit"); + } + + // Try connection with null authenticator on server and sending + // valid/invalid credentials. + // If the scheme does not have an authenticator in the first place (e.g. + // SSL) then skip it since this test is useless. + if (authenticator != null && authenticator.length() > 0) { + final int p1 = server1.invoke(() -> createCacheServer(locPort1, locString, 0, null, extraProps, javaProps)); + final int p2 = server2.invoke(() -> createCacheServer(locPort2, locString, 0, null, extraProps, javaProps)); + + createClient1NoException(multiUser, authInit, p1, p2, credentials1, javaProps1); + createClient2NoException(multiUser, authInit, p1, p2, credentials2, javaProps2, zeroConns); + + // Register interest on all keys on second client + client2.invoke(() -> registerAllInterest()); + + // Perform some put operations from client1 + client1.invoke(() -> doPuts(4)); + + // Verify that the puts succeeded + client2.invoke(() -> doLocalGets(4)); + + // Now also try with valid credentials on client2 + createClient1NoException(multiUser, authInit, p1, p2, credentials2, javaProps2); + createClient2NoException(multiUser, authInit, p1, p2, credentials1, javaProps1, zeroConns); + + // Register interest on all keys on second client + client2.invoke(() -> registerAllInterest()); + + // Perform some put operations from client1 + client1.invoke(() -> doNPuts(4)); + + // Verify that the puts succeeded + client2.invoke(() -> doNLocalGets(4)); + + } else { + getLogWriter().info("testCredentialsForNotifications: Skipping scheme [" + gen.classCode() + "] which has no authenticator"); + } + } + + private int createServer1(final Properties extraProps, final Properties javaProps, final String authenticator, final int locPort1, final String locString) { + return server1.invoke(() -> createCacheServer(locPort1, locString, authenticator, extraProps, javaProps)); + } + + private void createClient1NoException(final boolean multiUser, final String authInit, final int port1, final int port2, final Properties credentials2, final Properties javaProps2) { + client1.invoke(() -> createCacheClient(authInit, credentials2, javaProps2, port1, port2, 0, multiUser, NO_EXCEPTION)); + } + + private void createClient2AuthReqException(final boolean multiUser, final int port1, final int port2, final Properties credentials2, final Properties javaProps2, final int zeroConns) { + client2.invoke(() -> createCacheClient(null, credentials2, javaProps2, port1, port2, zeroConns, multiUser, AUTHREQ_EXCEPTION)); + } + + private void createClient1WithException(final boolean multiUser, final String authInit, final int port1, final int port2, final Properties credentials2, final Properties javaProps2, final int zeroConns) { + client1.invoke(() -> createCacheClient(authInit, credentials2, javaProps2, port1, port2, zeroConns, multiUser, AUTHFAIL_EXCEPTION)); + } + + private void createClient2WithException(final boolean multiUser, final String authInit, final int port1, final int port2, final Properties credentials2, final Properties javaProps2, final int zeroConns) { + client2.invoke(() -> createCacheClient(authInit, credentials2, javaProps2, port1, port2, zeroConns, multiUser, AUTHFAIL_EXCEPTION)); + } + + private void createClient2NoException(final boolean multiUser, final String authInit, final int port1, final int port2, final Properties credentials2, final Properties javaProps2, final int zeroConns) { + client2.invoke(() -> createCacheClient(authInit, credentials2, javaProps2, port1, port2, zeroConns, multiUser, NO_EXCEPTION)); + } + + private void createClientsNoException(final boolean multiUser, final String authInit, final int port1, final int port2, final Properties credentials1, final Properties javaProps1, final Properties credentials2, final Properties javaProps2) { + createClient1NoException(multiUser, authInit, port1, port2, credentials1, javaProps1); + client2.invoke(() -> createCacheClient(authInit, credentials2, javaProps2, port1, port2, 0, multiUser, NO_EXCEPTION)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9d7a6960/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestUtils.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestUtils.java b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestUtils.java new file mode 100644 index 0000000..3073705 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/security/ClientAuthenticationTestUtils.java @@ -0,0 +1,90 @@ +/* + * 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; + +import static com.gemstone.gemfire.distributed.ConfigurationProperties.*; +import static com.gemstone.gemfire.security.SecurityTestUtils.*; +import static org.junit.Assert.*; + +import java.util.Properties; + +import com.gemstone.gemfire.cache.Region; + +/** + * Extracted from ClientAuthenticationDUnitTest + */ +public abstract class ClientAuthenticationTestUtils { + + protected ClientAuthenticationTestUtils() { + } + + protected static Integer createCacheServer(final int locatorPort, final String locatorString, final String authenticator, final Properties extraProps, final Properties javaProps) { + return createCacheServer(locatorPort, locatorString, 0, authenticator, extraProps, javaProps, NO_EXCEPTION); + } + + protected static Integer createCacheServer(final int locatorPort, final String locatorString, final int serverPort, final String authenticator, final Properties extraProps, final Properties javaProps) { + return createCacheServer(locatorPort, locatorString, serverPort, authenticator, extraProps, javaProps, NO_EXCEPTION); + } + protected static Integer createCacheServer(final int locatorPort, final String locatorString, final String authenticator, final Properties extraProps, final Properties javaProps, final int expectedResult) { + + return createCacheServer(locatorPort, locatorString, 0, authenticator, extraProps, javaProps, expectedResult); + } + + protected static Integer createCacheServer(final int locatorPort, final String locatorString, final int serverPort, final String authenticator, final Properties extraProps, final Properties javaProps, int expectedResult) { + Properties authProps; + if (extraProps == null) { + authProps = new Properties(); + } else { + authProps = extraProps; + } + + if (authenticator != null) { + authProps.setProperty(SECURITY_CLIENT_AUTHENTICATOR, authenticator); + } + return SecurityTestUtils.createCacheServer(authProps, javaProps, locatorPort, locatorString, serverPort, expectedResult); + } + + protected static void createCacheClient(final String authInit, final Properties authProps, final Properties javaProps, final int[] ports, final int numConnections, final boolean multiUserMode, final boolean subscriptionEnabled, final int expectedResult) { + SecurityTestUtils.createCacheClient(authInit, authProps, javaProps, ports, numConnections, false, multiUserMode, subscriptionEnabled, expectedResult); + } + + protected static void createCacheClient(final String authInit, final Properties authProps, final Properties javaProps, final int[] ports, final int numConnections, final boolean multiUserMode, final int expectedResult) { + createCacheClient(authInit, authProps, javaProps, ports, numConnections, multiUserMode, true, expectedResult); + } + + protected static void createCacheClient(final String authInit, final Properties authProps, final Properties javaProps, final int port1, final int numConnections, final int expectedResult) { + createCacheClient(authInit, authProps, javaProps, new int[] { port1 }, numConnections, false, true, expectedResult); + } + + protected static void createCacheClient(final String authInit, final Properties authProps, final Properties javaProps, final int port1, final int port2, final int numConnections, final int expectedResult) { + createCacheClient(authInit, authProps, javaProps, port1, port2, numConnections, false, expectedResult); + } + + protected static void createCacheClient(final String authInit, final Properties authProps, final Properties javaProps, final int port1, final int port2, final int numConnections, final boolean multiUserMode, final int expectedResult) { + createCacheClient(authInit, authProps, javaProps, port1, port2, numConnections, multiUserMode, true, expectedResult); + } + + protected static void createCacheClient(final String authInit, final Properties authProps, final Properties javaProps, final int port1, final int port2, final int numConnections, final boolean multiUserMode, final boolean subscriptionEnabled, final int expectedResult) { + createCacheClient(authInit, authProps, javaProps, new int[] { port1, port2 }, numConnections, multiUserMode, subscriptionEnabled, expectedResult); + } + + protected static void registerAllInterest() { + Region region = getCache().getRegion(REGION_NAME); + assertNotNull(region); + region.registerInterestRegex(".*"); + } +}
