You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/03/17 23:58:29 UTC
[29/33] incubator-geode git commit: GEODE-949: refactor and repackage
security test code
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/generator/PKCSCredentialGenerator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/generator/PKCSCredentialGenerator.java b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/PKCSCredentialGenerator.java
new file mode 100755
index 0000000..6d33493
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/PKCSCredentialGenerator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.generator;
+
+import com.gemstone.gemfire.util.test.TestUtil;
+import com.gemstone.gemfire.security.templates.PKCSAuthInit;
+import com.gemstone.gemfire.security.templates.PKCSAuthenticator;
+
+import java.security.Principal;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Properties;
+
+public class PKCSCredentialGenerator extends CredentialGenerator {
+
+ public static String keyStoreDir = getKeyStoreDir();
+ public static boolean usesIBMJSSE;
+
+ // Checks if the current JVM uses only IBM JSSE providers.
+ private static boolean usesIBMProviders() {
+ final Provider[] providers = Security.getProviders();
+ for (int index = 0; index < providers.length; ++index) {
+ if (!providers[index].getName().toLowerCase().startsWith("ibm")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static String getKeyStoreDir() {
+ usesIBMJSSE = usesIBMProviders();
+ if (usesIBMJSSE) {
+ return "/com/gemstone/gemfire/security/generator/keys/ibm";
+ } else {
+ return "/com/gemstone/gemfire/security/generator/keys";
+ }
+ }
+
+ @Override
+ protected Properties initialize() throws IllegalArgumentException {
+ final String keyStoreFile = TestUtil.getResourcePath(PKCSCredentialGenerator.class, keyStoreDir + "/publickeyfile");
+
+ final Properties props = new Properties();
+ props.setProperty(PKCSAuthenticator.PUBLIC_KEY_FILE, keyStoreFile);
+ props.setProperty(PKCSAuthenticator.PUBLIC_KEYSTORE_PASSWORD, "gemfire");
+
+ return props;
+ }
+
+ @Override
+ public ClassCode classCode() {
+ return ClassCode.PKCS;
+ }
+
+ @Override
+ public String getAuthInit() {
+ return PKCSAuthInit.class.getName() + ".create";
+ }
+
+ @Override
+ public String getAuthenticator() {
+ return PKCSAuthenticator.class.getName() + ".create";
+ }
+
+ @Override
+ public Properties getInvalidCredentials(int index) {
+ final String keyStoreFile = TestUtil.getResourcePath(PKCSCredentialGenerator.class, keyStoreDir + "/gemfire11.keystore");
+
+ final Properties props = new Properties();
+ props.setProperty(PKCSAuthInit.KEYSTORE_FILE_PATH, keyStoreFile);
+ props.setProperty(PKCSAuthInit.KEYSTORE_ALIAS, "gemfire11");
+ props.setProperty(PKCSAuthInit.KEYSTORE_PASSWORD, "gemfire");
+
+ return props;
+ }
+
+ @Override
+ public Properties getValidCredentials(int index) {
+ final int aliasnum = (index % 10) + 1;
+ final String keyStoreFile = TestUtil.getResourcePath(PKCSCredentialGenerator.class, keyStoreDir + "/gemfire" + aliasnum + ".keystore");
+
+ final Properties props = new Properties();
+ props.setProperty(PKCSAuthInit.KEYSTORE_FILE_PATH, keyStoreFile);
+ props.setProperty(PKCSAuthInit.KEYSTORE_ALIAS, "gemfire" + aliasnum);
+ props.setProperty(PKCSAuthInit.KEYSTORE_PASSWORD, "gemfire");
+
+ return props;
+ }
+
+ @Override
+ public Properties getValidCredentials(Principal principal) {
+ final String keyStoreFile = TestUtil.getResourcePath(PKCSCredentialGenerator.class, keyStoreDir + principal.getName() + ".keystore");
+
+ final Properties props = new Properties();
+ props.setProperty(PKCSAuthInit.KEYSTORE_FILE_PATH, keyStoreFile);
+ props.setProperty(PKCSAuthInit.KEYSTORE_ALIAS, principal.getName());
+ props.setProperty(PKCSAuthInit.KEYSTORE_PASSWORD, "gemfire");
+
+ return props;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/generator/SSLCredentialGenerator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/generator/SSLCredentialGenerator.java b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/SSLCredentialGenerator.java
new file mode 100755
index 0000000..ff23f78
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/SSLCredentialGenerator.java
@@ -0,0 +1,121 @@
+/*
+ * 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.generator;
+
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Properties;
+
+public class SSLCredentialGenerator extends CredentialGenerator {
+
+ private static final Logger logger = LogService.getLogger();
+
+ @Override
+ protected Properties initialize() throws IllegalArgumentException {
+ this.javaProperties = getValidJavaSSLProperties();
+ return getSSLProperties();
+ }
+
+ @Override
+ public ClassCode classCode() {
+ return ClassCode.SSL;
+ }
+
+ @Override
+ public String getAuthInit() {
+ return null;
+ }
+
+ @Override
+ public String getAuthenticator() {
+ return null;
+ }
+
+ @Override
+ public Properties getValidCredentials(int index) {
+ this.javaProperties = getValidJavaSSLProperties();
+ return getSSLProperties();
+ }
+
+ @Override
+ public Properties getValidCredentials(final Principal principal) {
+ this.javaProperties = getValidJavaSSLProperties();
+ return getSSLProperties();
+ }
+
+ @Override
+ public Properties getInvalidCredentials(final int index) {
+ this.javaProperties = getInvalidJavaSSLProperties();
+ return getSSLProperties();
+ }
+
+ private File findTrustedJKS() {
+ final File ssldir = new File(System.getProperty("JTESTS") + "/ssl");
+ return new File(ssldir, "trusted.keystore");
+ }
+
+ private File findUntrustedJKS() {
+ final File ssldir = new File(System.getProperty("JTESTS") + "/ssl");
+ return new File(ssldir, "untrusted.keystore");
+ }
+
+ private Properties getValidJavaSSLProperties() {
+ final File jks = findTrustedJKS();
+
+ try {
+ final Properties props = new Properties();
+ props.setProperty("javax.net.ssl.trustStore", jks.getCanonicalPath());
+ props.setProperty("javax.net.ssl.trustStorePassword", "password");
+ props.setProperty("javax.net.ssl.keyStore", jks.getCanonicalPath());
+ props.setProperty("javax.net.ssl.keyStorePassword", "password");
+ return props;
+
+ } catch (IOException ex) {
+ throw new AuthenticationFailedException("SSL: Exception while opening the key store: " + ex.getMessage(), ex);
+ }
+ }
+
+ private Properties getInvalidJavaSSLProperties() {
+ final File jks = findUntrustedJKS();
+
+ try {
+ final Properties props = new Properties();
+ props.setProperty("javax.net.ssl.trustStore", jks.getCanonicalPath());
+ props.setProperty("javax.net.ssl.trustStorePassword", "password");
+ props.setProperty("javax.net.ssl.keyStore", jks.getCanonicalPath());
+ props.setProperty("javax.net.ssl.keyStorePassword", "password");
+ return props;
+
+ } catch (IOException ex) {
+ throw new AuthenticationFailedException("SSL: Exception while opening the key store: " + ex.getMessage(), ex);
+ }
+ }
+
+ private Properties getSSLProperties() {
+ Properties props = new Properties();
+ props.setProperty("ssl-enabled", "true");
+ props.setProperty("ssl-require-authentication", "true");
+ props.setProperty("ssl-ciphers", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
+ props.setProperty("ssl-protocols", "TLSv1");
+ return props;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/generator/UserPasswordWithExtraPropsAuthInit.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/generator/UserPasswordWithExtraPropsAuthInit.java b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/UserPasswordWithExtraPropsAuthInit.java
new file mode 100755
index 0000000..b29f16b
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/UserPasswordWithExtraPropsAuthInit.java
@@ -0,0 +1,69 @@
+/*
+ * 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.generator;
+
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.security.AuthInitialize;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+import com.gemstone.gemfire.security.templates.UserPasswordAuthInit;
+
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * An {@link AuthInitialize} implementation that obtains the user name and
+ * password as the credentials from the given set of properties. If
+ * keep-extra-props property exits, it will copy rest of the
+ * properties provided in getCredential props argument will also be
+ * copied as new credentials.
+ *
+ * @since 5.5
+ */
+public class UserPasswordWithExtraPropsAuthInit extends UserPasswordAuthInit {
+
+ public static final String SECURITY_PREFIX = "security-";
+ public static final String EXTRA_PROPS = "security-keep-extra-props";
+
+ public static AuthInitialize create() {
+ return new UserPasswordWithExtraPropsAuthInit();
+ }
+
+ public UserPasswordWithExtraPropsAuthInit() {
+ super();
+ }
+
+ @Override
+ public Properties getCredentials(final Properties securityProperties, final DistributedMember server, final boolean isPeer) throws AuthenticationFailedException {
+ final Properties securityPropertiesCopy = super.getCredentials(securityProperties, server, isPeer);
+ final String extraProps = securityProperties.getProperty(EXTRA_PROPS);
+
+ if (extraProps != null) {
+ for (Iterator it = securityProperties.keySet().iterator(); it.hasNext();) {
+ final String key = (String) it.next();
+ if (key.startsWith(SECURITY_PREFIX) &&
+ key.equalsIgnoreCase(USER_NAME) == false &&
+ key.equalsIgnoreCase(PASSWORD) == false &&
+ key.equalsIgnoreCase(EXTRA_PROPS) == false) {
+ securityPropertiesCopy.setProperty(key, securityProperties.getProperty(key));
+ }
+ }
+ this.securityLogWriter.fine("got everything and now have: " + securityPropertiesCopy.keySet().toString());
+ }
+
+ return securityPropertiesCopy;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/generator/XmlAuthzCredentialGenerator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/generator/XmlAuthzCredentialGenerator.java b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/XmlAuthzCredentialGenerator.java
new file mode 100755
index 0000000..5d07004
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/generator/XmlAuthzCredentialGenerator.java
@@ -0,0 +1,257 @@
+/*
+ * 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.generator;
+
+import com.gemstone.gemfire.cache.operations.OperationContext.OperationCode;
+import com.gemstone.gemfire.util.test.TestUtil;
+import com.gemstone.gemfire.security.templates.UsernamePrincipal;
+import com.gemstone.gemfire.security.templates.XmlAuthorization;
+
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+public class XmlAuthzCredentialGenerator extends AuthzCredentialGenerator {
+
+ private static final String dummyXml = "authz-dummy.xml";
+ private static final String ldapXml = "authz-ldap.xml";
+ private static final String pkcsXml = "authz-pkcs.xml";
+ private static final String sslXml = "authz-ssl.xml";
+
+ private static final String[] QUERY_REGIONS = { "/Portfolios", "/Positions", "/AuthRegion" };
+
+ public static OperationCode[] READER_OPS = {
+ OperationCode.GET,
+ OperationCode.REGISTER_INTEREST,
+ OperationCode.UNREGISTER_INTEREST,
+ OperationCode.KEY_SET,
+ OperationCode.CONTAINS_KEY,
+ OperationCode.EXECUTE_FUNCTION };
+
+ public static OperationCode[] WRITER_OPS = {
+ OperationCode.PUT,
+ OperationCode.DESTROY,
+ OperationCode.INVALIDATE,
+ OperationCode.REGION_CLEAR };
+
+ public static OperationCode[] QUERY_OPS = {
+ OperationCode.QUERY,
+ OperationCode.EXECUTE_CQ,
+ OperationCode.STOP_CQ,
+ OperationCode.CLOSE_CQ };
+
+ private static final byte READER_ROLE = 1;
+ private static final byte WRITER_ROLE = 2;
+ private static final byte QUERY_ROLE = 3;
+ private static final byte ADMIN_ROLE = 4;
+
+ private static Set readerOpsSet;
+ private static Set writerOpsSet;
+ private static Set queryOpsSet;
+ private static Set queryRegionSet;
+
+ static {
+ readerOpsSet = new HashSet();
+ for (int index = 0; index < READER_OPS.length; index++) {
+ readerOpsSet.add(READER_OPS[index]);
+ }
+
+ writerOpsSet = new HashSet();
+ for (int index = 0; index < WRITER_OPS.length; index++) {
+ writerOpsSet.add(WRITER_OPS[index]);
+ }
+
+ queryOpsSet = new HashSet();
+ for (int index = 0; index < QUERY_OPS.length; index++) {
+ queryOpsSet.add(QUERY_OPS[index]);
+ }
+
+ queryRegionSet = new HashSet();
+ for (int index = 0; index < QUERY_REGIONS.length; index++) {
+ queryRegionSet.add(QUERY_REGIONS[index]);
+ }
+ }
+
+ @Override
+ protected Properties init() throws IllegalArgumentException {
+ final Properties sysProps = new Properties();
+ final String dirName = "/com/gemstone/gemfire/security/generator/";
+
+ if (this.generator.classCode().isDummy()) {
+ final String xmlFilename = TestUtil.getResourcePath(XmlAuthzCredentialGenerator.class, dirName + dummyXml);
+ sysProps.setProperty(XmlAuthorization.DOC_URI_PROP_NAME, xmlFilename);
+
+ } else if (this.generator.classCode().isLDAP()) {
+ final String xmlFilename = TestUtil.getResourcePath(XmlAuthzCredentialGenerator.class, dirName + ldapXml);
+ sysProps.setProperty(XmlAuthorization.DOC_URI_PROP_NAME, xmlFilename);
+
+ // } else if (this.generator.classCode().isPKCS()) {
+ // sysProps.setProperty(XmlAuthorization.DOC_URI_PROP_NAME, dirName + pkcsXml);
+ // }
+ // } else if (this.generator.classCode().isSSL()) {
+ // sysProps.setProperty(XmlAuthorization.DOC_URI_PROP_NAME, dirName + sslXml);
+ // }
+
+ } else {
+ throw new IllegalArgumentException("No XML defined for XmlAuthorization module to work with " + this.generator.getAuthenticator());
+ }
+ return sysProps;
+ }
+
+ @Override
+ public ClassCode classCode() {
+ return ClassCode.XML;
+ }
+
+ @Override
+ public String getAuthorizationCallback() {
+ return XmlAuthorization.class.getName() + ".create";
+ }
+
+ private Principal getDummyPrincipal(final byte roleType, final int index) {
+ final String[] admins = new String[] { "root", "admin", "administrator" };
+ final int numReaders = 3;
+ final int numWriters = 3;
+
+ switch (roleType) {
+ case READER_ROLE:
+ return new UsernamePrincipal("reader" + (index % numReaders));
+ case WRITER_ROLE:
+ return new UsernamePrincipal("writer" + (index % numWriters));
+ case QUERY_ROLE:
+ return new UsernamePrincipal("reader" + ((index % 2) + 3));
+ default:
+ return new UsernamePrincipal(admins[index % admins.length]);
+ }
+ }
+
+ @Override
+ protected Principal getAllowedPrincipal(final OperationCode[] opCodes, final String[] regionNames, final int index) {
+ if (this.generator.classCode().isDummy()) {
+ final byte roleType = getRequiredRole(opCodes, regionNames);
+ return getDummyPrincipal(roleType, index);
+
+ } else if (this.generator.classCode().isLDAP()) {
+ final byte roleType = getRequiredRole(opCodes, regionNames);
+ return getLdapPrincipal(roleType, index);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Principal getDisallowedPrincipal(final OperationCode[] opCodes, final String[] regionNames, final int index) {
+ final byte roleType = getRequiredRole(opCodes, regionNames);
+
+ byte disallowedRoleType = READER_ROLE;
+ switch (roleType) {
+ case READER_ROLE:
+ disallowedRoleType = WRITER_ROLE;
+ break;
+ case WRITER_ROLE:
+ disallowedRoleType = READER_ROLE;
+ break;
+ case QUERY_ROLE:
+ disallowedRoleType = READER_ROLE;
+ break;
+ case ADMIN_ROLE:
+ disallowedRoleType = READER_ROLE;
+ break;
+ }
+
+ if (this.generator.classCode().isDummy()) {
+ return getDummyPrincipal(disallowedRoleType, index);
+
+ } else if (this.generator.classCode().isLDAP()) {
+ return getLdapPrincipal(disallowedRoleType, index);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected int getNumPrincipalTries(final OperationCode[] opCodes, final String[] regionNames) {
+ return 5;
+ }
+
+ private Principal getLdapPrincipal(final byte roleType, final int index) {
+ final String userPrefix = "gemfire";
+ final int[] readerIndices = { 3, 4, 5 };
+ final int[] writerIndices = { 6, 7, 8 };
+ final int[] queryIndices = { 9, 10 };
+ final int[] adminIndices = { 1, 2 };
+
+ switch (roleType) {
+ case READER_ROLE:
+ int readerIndex = readerIndices[index % readerIndices.length];
+ return new UsernamePrincipal(userPrefix + readerIndex);
+ case WRITER_ROLE:
+ int writerIndex = writerIndices[index % writerIndices.length];
+ return new UsernamePrincipal(userPrefix + writerIndex);
+ case QUERY_ROLE:
+ int queryIndex = queryIndices[index % queryIndices.length];
+ return new UsernamePrincipal(userPrefix + queryIndex);
+ default:
+ int adminIndex = adminIndices[index % adminIndices.length];
+ return new UsernamePrincipal(userPrefix + adminIndex);
+ }
+ }
+
+ private byte getRequiredRole(final OperationCode[] opCodes, final String[] regionNames) {
+ byte roleType = ADMIN_ROLE;
+ boolean requiresReader = true;
+ boolean requiresWriter = true;
+ boolean requiresQuery = true;
+
+ for (int opNum = 0; opNum < opCodes.length; opNum++) {
+ final OperationCode opCode = opCodes[opNum];
+ if (requiresReader && !readerOpsSet.contains(opCode)) {
+ requiresReader = false;
+ }
+ if (requiresWriter && !writerOpsSet.contains(opCode)) {
+ requiresWriter = false;
+ }
+ if (requiresQuery && !queryOpsSet.contains(opCode)) {
+ requiresQuery = false;
+ }
+ }
+
+ if (requiresReader) {
+ roleType = READER_ROLE;
+
+ } else if (requiresWriter) {
+ roleType = WRITER_ROLE;
+
+ } else if (requiresQuery) {
+ if (regionNames != null && regionNames.length > 0) {
+ for (int index = 0; index < regionNames.length; index++) {
+ final String regionName = XmlAuthorization.normalizeRegionName(regionNames[index]);
+ if (requiresQuery && !queryRegionSet.contains(regionName)) {
+ requiresQuery = false;
+ break;
+ }
+ }
+ if (requiresQuery) {
+ roleType = QUERY_ROLE;
+ }
+ }
+ }
+
+ return roleType;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthenticator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthenticator.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthenticator.java
new file mode 100755
index 0000000..f9293b8
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthenticator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.security.Principal;
+import java.util.Properties;
+
+import com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+import com.gemstone.gemfire.security.Authenticator;
+
+/**
+ * A dummy implementation of the {@link Authenticator} interface that expects a
+ * user name and password allowing authentication depending on the format of the
+ * user name.
+ *
+ * @since 5.5
+ */
+public class DummyAuthenticator implements Authenticator {
+
+ public static Authenticator create() {
+ return new DummyAuthenticator();
+ }
+
+ public static boolean checkValidName(final String userName) {
+ return userName.startsWith("user") ||
+ userName.startsWith("reader") ||
+ userName.startsWith("writer") ||
+ userName.equals("admin") ||
+ userName.equals("root") ||
+ userName.equals("administrator");
+ }
+
+ @Override
+ public void init(final Properties securityProperties, final LogWriter systemLogWriter, final LogWriter securityLogWriter) throws AuthenticationFailedException {
+ }
+
+ @Override
+ public Principal authenticate(final Properties credentials, final DistributedMember member) throws AuthenticationFailedException {
+ final String userName = credentials.getProperty(UserPasswordAuthInit.USER_NAME);
+ if (userName == null) {
+ throw new AuthenticationFailedException("DummyAuthenticator: user name property [" + UserPasswordAuthInit.USER_NAME + "] not provided");
+ }
+
+ final String password = credentials.getProperty(UserPasswordAuthInit.PASSWORD);
+ if (password == null) {
+ throw new AuthenticationFailedException( "DummyAuthenticator: password property [" + UserPasswordAuthInit.PASSWORD + "] not provided");
+ }
+
+ if (userName.equals(password) && checkValidName(userName)) {
+ return new UsernamePrincipal(userName);
+ } else {
+ throw new AuthenticationFailedException("DummyAuthenticator: Invalid user name [" + userName + "], password supplied.");
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthorization.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthorization.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthorization.java
new file mode 100755
index 0000000..a76a46f
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/DummyAuthorization.java
@@ -0,0 +1,122 @@
+/*
+ * 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 com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.operations.OperationContext;
+import com.gemstone.gemfire.cache.operations.OperationContext.OperationCode;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.security.AccessControl;
+import com.gemstone.gemfire.security.NotAuthorizedException;
+
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A dummy implementation of the {@code AccessControl} interface that
+ * allows authorization depending on the format of the {@code Principal}
+ * string.
+ *
+ * @since 5.5
+ */
+public class DummyAuthorization implements AccessControl {
+
+ private Set allowedOps;
+ private DistributedMember remoteMember;
+ private LogWriter securityLogWriter;
+
+ public static final OperationCode[] READER_OPS = {
+ OperationCode.GET,
+ OperationCode.QUERY,
+ OperationCode.EXECUTE_CQ,
+ OperationCode.CLOSE_CQ,
+ OperationCode.STOP_CQ,
+ OperationCode.REGISTER_INTEREST,
+ OperationCode.UNREGISTER_INTEREST,
+ OperationCode.KEY_SET,
+ OperationCode.CONTAINS_KEY,
+ OperationCode.EXECUTE_FUNCTION };
+
+ public static final OperationCode[] WRITER_OPS = {
+ OperationCode.PUT,
+ OperationCode.PUTALL,
+ OperationCode.DESTROY,
+ OperationCode.INVALIDATE,
+ OperationCode.REGION_CLEAR };
+
+ public static AccessControl create() {
+ return new DummyAuthorization();
+ }
+
+ public DummyAuthorization() {
+ this.allowedOps = new HashSet(20);
+ }
+
+ @Override
+ public void init(final Principal principal, final DistributedMember remoteMember, final Cache cache) throws NotAuthorizedException {
+ if (principal != null) {
+
+ final String name = principal.getName().toLowerCase();
+
+ if (name != null) {
+
+ if (name.equals("root") || name.equals("admin") || name.equals("administrator")) {
+ addReaderOps();
+ addWriterOps();
+ this.allowedOps.add(OperationCode.REGION_CREATE);
+ this.allowedOps.add(OperationCode.REGION_DESTROY);
+
+ } else if (name.startsWith("writer")) {
+ addWriterOps();
+
+ } else if (name.startsWith("reader")) {
+ addReaderOps();
+ }
+
+ }
+ }
+
+ this.remoteMember = remoteMember;
+ this.securityLogWriter = cache.getSecurityLogger();
+ }
+
+ @Override
+ public boolean authorizeOperation(String regionName, OperationContext context) {
+ final OperationCode opCode = context.getOperationCode();
+ this.securityLogWriter.fine("Invoked authorize operation for [" + opCode + "] in region [" + regionName + "] for client: " + remoteMember);
+ return this.allowedOps.contains(opCode);
+ }
+
+ @Override
+ public void close() {
+ this.allowedOps.clear();
+ }
+
+ private void addReaderOps() {
+ for (int index = 0; index < READER_OPS.length; index++) {
+ this.allowedOps.add(READER_OPS[index]);
+ }
+ }
+
+ private void addWriterOps() {
+ for (int index = 0; index < WRITER_OPS.length; index++) {
+ this.allowedOps.add(WRITER_OPS[index]);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/FunctionSecurityPrmsHolder.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/FunctionSecurityPrmsHolder.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/FunctionSecurityPrmsHolder.java
new file mode 100755
index 0000000..9ce82f3
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/FunctionSecurityPrmsHolder.java
@@ -0,0 +1,50 @@
+/*
+ * 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.util.Set;
+
+/**
+ * This is a sample class for objects which hold information of the authorized
+ * function names and authorized value for the {@code optimizeForWrite}.
+ *
+ * @since 6.0
+ */
+public class FunctionSecurityPrmsHolder {
+
+ private final Boolean optimizeForWrite;
+ private final Set<String> functionIds;
+ private final Set<String> keySet;
+
+ public FunctionSecurityPrmsHolder(final Boolean optimizeForWrite, final Set<String> functionIds, final Set<String> keySet) {
+ this.optimizeForWrite = optimizeForWrite;
+ this.functionIds = functionIds;
+ this.keySet = keySet;
+ }
+
+ public Boolean isOptimizeForWrite() {
+ return this.optimizeForWrite;
+ }
+
+ public Set<String> getFunctionIds() {
+ return this.functionIds;
+ }
+
+ public Set<String> getKeySet() {
+ return this.keySet;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/LdapUserAuthenticator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/LdapUserAuthenticator.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/LdapUserAuthenticator.java
new file mode 100755
index 0000000..fd2286e
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/LdapUserAuthenticator.java
@@ -0,0 +1,106 @@
+/*
+ * 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.security.Principal;
+import java.util.Properties;
+import javax.naming.Context;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+import com.gemstone.gemfire.security.Authenticator;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * An implementation of {@link Authenticator} that uses LDAP.
+ *
+ * @since 5.5
+ */
+public class LdapUserAuthenticator implements Authenticator {
+
+ private static final Logger logger = LogService.getLogger();
+
+ public static final String LDAP_SERVER_NAME = "security-ldap-server";
+ public static final String LDAP_BASEDN_NAME = "security-ldap-basedn";
+ public static final String LDAP_SSL_NAME = "security-ldap-usessl";
+
+ private String ldapServer = null;
+ private String baseDomainName = null;
+ private String ldapUrlScheme = null;
+
+ public static Authenticator create() {
+ return new LdapUserAuthenticator();
+ }
+
+ @Override
+ public void init(final Properties securityProps, final LogWriter systemLogWriter, final LogWriter securityLogWriter) throws AuthenticationFailedException {
+ logger.info("Initializing LdapUserAuthenticator with {}", securityProps);
+
+ this.ldapServer = securityProps.getProperty(LDAP_SERVER_NAME);
+ if (this.ldapServer == null || this.ldapServer.length() == 0) {
+ throw new AuthenticationFailedException("LdapUserAuthenticator: LDAP server property [" + LDAP_SERVER_NAME + "] not specified");
+ }
+
+ this.baseDomainName = securityProps.getProperty(LDAP_BASEDN_NAME);
+ if (this.baseDomainName == null || this.baseDomainName.length() == 0) {
+ throw new AuthenticationFailedException("LdapUserAuthenticator: LDAP base DN property [" + LDAP_BASEDN_NAME + "] not specified");
+ }
+
+ final String sslName = securityProps.getProperty(LDAP_SSL_NAME);
+ if (sslName != null && sslName.toLowerCase().equals("true")) {
+ this.ldapUrlScheme = "ldaps://";
+ } else {
+ this.ldapUrlScheme = "ldap://";
+ }
+ }
+
+ @Override
+ public Principal authenticate(final Properties credentials, final DistributedMember member) {
+ final String userName = credentials.getProperty(UserPasswordAuthInit.USER_NAME);
+ if (userName == null) {
+ throw new AuthenticationFailedException("LdapUserAuthenticator: user name property [" + UserPasswordAuthInit.USER_NAME + "] not provided");
+ }
+
+ String password = credentials.getProperty(UserPasswordAuthInit.PASSWORD);
+ if (password == null) {
+ password = "";
+ }
+
+ final Properties env = new Properties();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, com.sun.jndi.ldap.LdapCtxFactory.class.getName());
+ env.put(Context.PROVIDER_URL, this.ldapUrlScheme + this.ldapServer + '/' + this.baseDomainName);
+ env.put(Context.SECURITY_PRINCIPAL, "uid=" + userName + "," + this.baseDomainName);
+ env.put(Context.SECURITY_CREDENTIALS, password);
+
+ try {
+ final DirContext ctx = new InitialDirContext(env);
+ ctx.close();
+ } catch (Exception e) {
+ throw new AuthenticationFailedException("LdapUserAuthenticator: Failure with provided username, password combination for user name: " + userName, e);
+ }
+
+ return new UsernamePrincipal(userName);
+ }
+
+ @Override
+ public void close() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthInit.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthInit.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthInit.java
new file mode 100755
index 0000000..a60c8fb
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthInit.java
@@ -0,0 +1,119 @@
+/*
+ * 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.FileInputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.Properties;
+
+import com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.security.AuthInitialize;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * An {@link AuthInitialize} implementation that obtains the digital signature
+ * for use with PKCS scheme on server from the given set of properties.
+ *
+ * To use this class the {@code security-client-auth-init} property should be
+ * set to the fully qualified name the static {@code create} function
+ * viz. <{@code templates.security.PKCSAuthInit.create}
+ *
+ * @since 5.5
+ */
+public class PKCSAuthInit implements AuthInitialize {
+
+ private static final Logger logger = LogService.getLogger();
+
+ public static final String KEYSTORE_FILE_PATH = "security-keystorepath";
+ public static final String KEYSTORE_ALIAS = "security-alias";
+ public static final String KEYSTORE_PASSWORD = "security-keystorepass";
+ public static final String SIGNATURE_DATA = "security-signature";
+
+ protected LogWriter systemLogWriter;
+ protected LogWriter securityLogWriter;
+
+ public static AuthInitialize create() {
+ return new PKCSAuthInit();
+ }
+
+ @Override
+ public void init(final LogWriter systemLogWriter, final LogWriter securityLogWriter) throws AuthenticationFailedException {
+ this.systemLogWriter = systemLogWriter;
+ this.securityLogWriter = securityLogWriter;
+ }
+
+ @Override
+ public Properties getCredentials(final Properties securityProperties, final DistributedMember server, final boolean isPeer) throws AuthenticationFailedException {
+ final String keyStorePath = securityProperties.getProperty(KEYSTORE_FILE_PATH);
+ if (keyStorePath == null) {
+ throw new AuthenticationFailedException("PKCSAuthInit: key-store file path property [" + KEYSTORE_FILE_PATH + "] not set.");
+ }
+
+ final String alias = securityProperties.getProperty(KEYSTORE_ALIAS);
+ if (alias == null) {
+ throw new AuthenticationFailedException("PKCSAuthInit: key alias name property [" + KEYSTORE_ALIAS + "] not set.");
+ }
+
+ final String keyStorePass = securityProperties.getProperty(KEYSTORE_PASSWORD);
+
+ try {
+ final KeyStore ks = KeyStore.getInstance("PKCS12");
+ final char[] passPhrase = (keyStorePass != null ? keyStorePass.toCharArray() : null);
+ final FileInputStream certificatefile = new FileInputStream(keyStorePath);
+
+ try {
+ ks.load(certificatefile, passPhrase);
+ } finally {
+ certificatefile.close();
+ }
+
+ final Key key = ks.getKey(alias, passPhrase);
+
+ if (key instanceof PrivateKey) {
+ final PrivateKey privKey = (PrivateKey)key;
+ final X509Certificate cert = (X509Certificate)ks.getCertificate(alias);
+ final Signature sig = Signature.getInstance(cert.getSigAlgName());
+
+ sig.initSign(privKey);
+ sig.update(alias.getBytes("UTF-8"));
+ final byte[] signatureBytes = sig.sign();
+
+ final Properties newprops = new Properties();
+ newprops.put(KEYSTORE_ALIAS, alias);
+ newprops.put(SIGNATURE_DATA, signatureBytes);
+ return newprops;
+
+ } else {
+ throw new AuthenticationFailedException("PKCSAuthInit: " + "Failed to load private key from the given file: " + keyStorePath);
+ }
+
+ } catch (Exception ex) {
+ throw new AuthenticationFailedException("PKCSAuthInit: Exception while getting credentials: " + ex, ex);
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthenticator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthenticator.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthenticator.java
new file mode 100755
index 0000000..971cf60
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSAuthenticator.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.security.templates;
+
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+import com.gemstone.gemfire.security.Authenticator;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * An implementation of {@link Authenticator} that uses PKCS.
+ */
+public class PKCSAuthenticator implements Authenticator {
+
+ private static final Logger logger = LogService.getLogger();
+
+ public static final String PUBLIC_KEY_FILE = "security-publickey-filepath";
+ public static final String PUBLIC_KEYSTORE_PASSWORD = "security-publickey-pass";
+
+ private String pubKeyFilePath;
+ private String pubKeyPass;
+ private Map aliasCertificateMap;
+
+ private LogWriter systemLogWriter;
+ private LogWriter securityLogWriter;
+
+ public static Authenticator create() {
+ return new PKCSAuthenticator();
+ }
+
+ @Override
+ public void init(final Properties securityProperties, final LogWriter systemLogWriter, final LogWriter securityLogWriter) throws AuthenticationFailedException {
+ this.systemLogWriter = systemLogWriter;
+ this.securityLogWriter = securityLogWriter;
+
+ this.pubKeyFilePath = securityProperties.getProperty(PUBLIC_KEY_FILE);
+ if (this.pubKeyFilePath == null) {
+ throw new AuthenticationFailedException("PKCSAuthenticator: property " + PUBLIC_KEY_FILE + " not specified as the public key file.");
+ }
+
+ this.pubKeyPass = securityProperties.getProperty(PUBLIC_KEYSTORE_PASSWORD);
+ this.aliasCertificateMap = new HashMap();
+
+ populateMap();
+ }
+
+ @Override
+ public Principal authenticate(final Properties credentials, final DistributedMember member) throws AuthenticationFailedException {
+ final String alias = (String)credentials.get(PKCSAuthInit.KEYSTORE_ALIAS);
+ if (alias == null || alias.length() <= 0) {
+ throw new AuthenticationFailedException("No alias received");
+ }
+
+ try {
+ final X509Certificate cert = getCertificate(alias);
+ if (cert == null) {
+ throw newException("No certificate found for alias:" + alias);
+ }
+
+ final byte[] signatureBytes = (byte[])credentials.get(PKCSAuthInit.SIGNATURE_DATA);
+ if (signatureBytes == null) {
+ throw newException("signature data property [" + PKCSAuthInit.SIGNATURE_DATA + "] not provided");
+ }
+
+ final Signature sig = Signature.getInstance(cert.getSigAlgName());
+ sig.initVerify(cert);
+ sig.update(alias.getBytes("UTF-8"));
+
+ if (!sig.verify(signatureBytes)) {
+ throw newException("verification of client signature failed");
+ }
+
+ return new PKCSPrincipal(alias);
+
+ } catch (Exception ex) {
+ throw newException(ex.toString(), ex);
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private void populateMap() {
+ try {
+ final KeyStore keyStore = KeyStore.getInstance("JKS");
+ final char[] passPhrase = this.pubKeyPass != null ? this.pubKeyPass.toCharArray() : null;
+ final FileInputStream keyStoreFile = new FileInputStream(this.pubKeyFilePath);
+
+ try {
+ keyStore.load(keyStoreFile, passPhrase);
+ } finally {
+ keyStoreFile.close();
+ }
+
+ for (Enumeration e = keyStore.aliases(); e.hasMoreElements();) {
+ final Object alias = e.nextElement();
+ final Certificate cert = keyStore.getCertificate((String)alias);
+ if (cert instanceof X509Certificate) {
+ this.aliasCertificateMap.put(alias, cert);
+ }
+ }
+
+ } catch (Exception e) {
+ throw new AuthenticationFailedException("Exception while getting public keys: " + e.getMessage(), e);
+ }
+ }
+
+ private AuthenticationFailedException newException(final String message, final Exception cause) {
+ final String fullMessage = "PKCSAuthenticator: Authentication of client failed due to: " + message;
+ if (cause != null) {
+ return new AuthenticationFailedException(fullMessage, cause);
+ } else {
+ return new AuthenticationFailedException(fullMessage);
+ }
+ }
+
+ private AuthenticationFailedException newException(final String message) {
+ return newException(message, null);
+ }
+
+ private X509Certificate getCertificate(final String alias) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ if (this.aliasCertificateMap.containsKey(alias)) {
+ return (X509Certificate) this.aliasCertificateMap.get(alias);
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipal.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipal.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipal.java
new file mode 100755
index 0000000..4a6c45e
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipal.java
@@ -0,0 +1,40 @@
+/*
+ * 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.Serializable;
+import java.security.Principal;
+
+public class PKCSPrincipal implements Principal, Serializable {
+
+ private final String alias;
+
+ public PKCSPrincipal(final String alias) {
+ this.alias = alias;
+ }
+
+ @Override
+ public String getName() {
+ return this.alias;
+ }
+
+ @Override
+ public String toString() {
+ return this.alias;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipalTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipalTest.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipalTest.java
new file mode 100644
index 0000000..e0bc1e4
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/PKCSPrincipalTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.categories.UnitTest;
+import org.apache.commons.lang.SerializationUtils;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.Serializable;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for {@link PKCSPrincipal}
+ */
+@Category(UnitTest.class)
+public class PKCSPrincipalTest {
+
+ @Test
+ public void isSerializable() throws Exception {
+ assertThat(PKCSPrincipal.class).isInstanceOf(Serializable.class);
+ }
+
+ @Test
+ public void canBeSerialized() throws Exception {
+ String name = "jsmith";
+ PKCSPrincipal instance = new PKCSPrincipal(name);
+
+ PKCSPrincipal cloned = (PKCSPrincipal) SerializationUtils.clone(instance);
+
+ assertThat(cloned.getName()).isEqualTo(name);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UserPasswordAuthInit.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UserPasswordAuthInit.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UserPasswordAuthInit.java
new file mode 100755
index 0000000..312f18b
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UserPasswordAuthInit.java
@@ -0,0 +1,75 @@
+/*
+ * 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 com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.distributed.DistributedMember;
+import com.gemstone.gemfire.security.AuthInitialize;
+import com.gemstone.gemfire.security.AuthenticationFailedException;
+
+import java.util.Properties;
+
+/**
+ * An {@link AuthInitialize} implementation that obtains the user name and
+ * password as the credentials from the given set of properties.
+ *
+ * To use this class the {@code security-client-auth-init} property should be
+ * set to the fully qualified name the static {@code create} function
+ * viz. {@code templates.security.UserPasswordAuthInit.create}
+ *
+ * @since 5.5
+ */
+public class UserPasswordAuthInit implements AuthInitialize {
+
+ public static final String USER_NAME = "security-username";
+ public static final String PASSWORD = "security-password";
+
+ protected LogWriter systemLogWriter;
+ protected LogWriter securityLogWriter;
+
+ public static AuthInitialize create() {
+ return new UserPasswordAuthInit();
+ }
+
+ @Override
+ public void init(final LogWriter systemLogWriter, final LogWriter securityLogWriter) throws AuthenticationFailedException {
+ this.systemLogWriter = systemLogWriter;
+ this.securityLogWriter = securityLogWriter;
+ }
+
+ @Override
+ public Properties getCredentials(final Properties securityProperties, final DistributedMember server, final boolean isPeer) throws AuthenticationFailedException {
+ String userName = securityProperties.getProperty(USER_NAME);
+ if (userName == null) {
+ throw new AuthenticationFailedException("UserPasswordAuthInit: user name property [" + USER_NAME + "] not set.");
+ }
+
+ String password = securityProperties.getProperty(PASSWORD);
+ if (password == null) {
+ password = "";
+ }
+
+ Properties securityPropertiesCopy = new Properties();
+ securityPropertiesCopy.setProperty(USER_NAME, userName);
+ securityPropertiesCopy.setProperty(PASSWORD, password);
+ return securityPropertiesCopy;
+ }
+
+ @Override
+ public void close() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipal.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipal.java b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipal.java
new file mode 100755
index 0000000..a5062f2
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipal.java
@@ -0,0 +1,44 @@
+/*
+ * 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.Serializable;
+import java.security.Principal;
+
+/**
+ * An implementation of {@link Principal} class for a simple user name.
+ *
+ * @since 5.5
+ */
+public class UsernamePrincipal implements Principal, Serializable {
+
+ private final String userName;
+
+ public UsernamePrincipal(final String userName) {
+ this.userName = userName;
+ }
+
+ @Override
+ public String getName() {
+ return this.userName;
+ }
+
+ @Override
+ public String toString() {
+ return this.userName;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8de59df1/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
new file mode 100644
index 0000000..e762d06
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/UsernamePrincipalTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.categories.UnitTest;
+import org.apache.commons.lang.SerializationUtils;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.Serializable;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for {@link UsernamePrincipal}
+ */
+@Category(UnitTest.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/8de59df1/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
new file mode 100755
index 0000000..b8f2e50
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/security/templates/XmlAuthorization.java
@@ -0,0 +1,614 @@
+/*
+ * 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 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;
+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;
+
+/**
+ * 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 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 (userNode.getNodeName() == TAG_USER) {
+ 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 (operationNode.getNodeName() == TAG_OP) {
+ final String operationName = getNodeValue(operationNode);
+ final OperationCode code = OperationCode.parse(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;
+ }
+ }
+}