You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by tu...@apache.org on 2015/11/03 12:26:35 UTC
[73/79] incubator-geode git commit: Integrated Security related
automated test using selenium. Test pulse authentication and access to data
browser through selenium Review : https://reviews.apache.org/r/39646
Integrated Security related automated test using selenium. Test pulse authentication and access to data browser through selenium
Review : https://reviews.apache.org/r/39646
Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/87c83da2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/87c83da2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/87c83da2
Branch: refs/heads/feature/GEODE-17
Commit: 87c83da23a4fd281d9ca0fda2cd61e57d7cfef52
Parents: 813d24c
Author: tushark <tu...@apache.org>
Authored: Mon Nov 2 18:32:11 2015 +0530
Committer: tushark <tu...@apache.org>
Committed: Tue Nov 3 16:54:35 2015 +0530
----------------------------------------------------------------------
.../pulse/internal/security/LogoutHandler.java | 6 +-
.../tools/pulse/tests/AccessControl.java | 45 ++++
.../tools/pulse/tests/AccessControlMBean.java | 7 +
.../tools/pulse/tests/IntegrateSecUITest.java | 245 +++++++++++++++++++
.../pulse/tests/PropsBackedAuthenticator.java | 48 ++++
.../gemfire/tools/pulse/tests/PulseTests.java | 2 +-
.../gemfire/tools/pulse/tests/Server.java | 86 ++++---
pulse/src/test/resources/test.properties | 12 +
8 files changed, 419 insertions(+), 32 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/LogoutHandler.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/LogoutHandler.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/LogoutHandler.java
index 78d4703..b6187bc 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/LogoutHandler.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/LogoutHandler.java
@@ -30,8 +30,10 @@ public class LogoutHandler extends SimpleUrlLogoutSuccessHandler implements Logo
LOGGER.fine("Invoked #LogoutHandler ...");
if (Repository.get().isUseGemFireCredentials()) {
GemFireAuthentication gemauthentication = (GemFireAuthentication) authentication;
- gemauthentication.getJmxc().close();
- LOGGER.info("#LogoutHandler : Closing GemFireAuthentication JMX Connection...");
+ if(gemauthentication!=null) {
+ gemauthentication.getJmxc().close();
+ LOGGER.fine("#LogoutHandler : Closing GemFireAuthentication JMX Connection...");
+ }
}
super.onLogoutSuccess(request, response, authentication);
}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControl.java
----------------------------------------------------------------------
diff --git a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControl.java b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControl.java
new file mode 100644
index 0000000..ee0d43b
--- /dev/null
+++ b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControl.java
@@ -0,0 +1,45 @@
+package com.vmware.gemfire.tools.pulse.tests;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.Set;
+
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+
+/**
+ *
+ * @author tushark
+ *
+ */
+public class AccessControl extends JMXBaseBean implements AccessControlMBean {
+
+ public static final String OBJECT_NAME_ACCESSCONTROL = "GemFire:service=AccessControl,type=Distributed";
+
+ @Override
+ public boolean authorize(String role) {
+ AccessControlContext acc = AccessController.getContext();
+ Subject subject = Subject.getSubject(acc);
+ Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
+ if (principals == null || principals.isEmpty()) {
+ throw new SecurityException("Access denied");
+ }
+
+ Principal principal = principals.iterator().next();
+ String roleArray[] = getStringArray(principal.getName());
+ if(roleArray!=null) {
+ for(String roleStr:roleArray) {
+ if(roleStr.equals(role))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected String getKey(String propName) {
+ return "users." + propName + "." + "roles";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControlMBean.java
----------------------------------------------------------------------
diff --git a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControlMBean.java b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControlMBean.java
new file mode 100644
index 0000000..70423e9
--- /dev/null
+++ b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/AccessControlMBean.java
@@ -0,0 +1,7 @@
+package com.vmware.gemfire.tools.pulse.tests;
+
+public interface AccessControlMBean {
+
+ public boolean authorize(String role);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/IntegrateSecUITest.java
----------------------------------------------------------------------
diff --git a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/IntegrateSecUITest.java b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/IntegrateSecUITest.java
new file mode 100644
index 0000000..cc6e5b6
--- /dev/null
+++ b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/IntegrateSecUITest.java
@@ -0,0 +1,245 @@
+package com.vmware.gemfire.tools.pulse.tests;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.management.JMException;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+import static org.junit.Assert.*;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.startup.Tomcat;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+public class IntegrateSecUITest {
+
+ private final static String jmxPropertiesFile = System.getProperty("pulse.propfile");
+ private static String path = System.getProperty("pulse.war");
+ private static Tomcat tomcat = null;
+ private static Server server = null;
+ private static String pulseURL = null;
+ private static String logoutURL = null;
+ private static String loginURL = null;
+ public static WebDriver driver;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ try {
+ //Enable Integrated Security Profile
+ System.setProperty("spring.profiles.active", "pulse.authentication.gemfire");
+ //assumes jmx port is 1099 in pulse war file
+ server = Server.createServer(1099, jmxPropertiesFile, true);
+ String host = "localhost";// InetAddress.getLocalHost().getHostAddress();
+ int port = 8080;
+ String context = "/pulse";
+ tomcat = TomcatHelper.startTomcat(host, port, context, path);
+ pulseURL = "http://" + host + ":" + port + context;
+ logoutURL = "http://" + host + ":" + port + context + "/pulse/clusterLogout";
+ loginURL = "http://" + host + ":" + port + context + "/Login.html";
+ Thread.sleep(5000); // wait till tomcat settles down
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ fail("Error " + e.getMessage());
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("Error " + e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Error " + e.getMessage());
+ }
+
+ driver = new FirefoxDriver();
+ driver.manage().window().maximize();
+ driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
+ driver.get(pulseURL);
+ }
+
+ /**
+ * This test only tests test extensions (ie. properties file based testbed) written for integrate security
+ * 1. PropsBackedAuthenticator
+ * 2. AccessControlMbean
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testServerAuthentication() throws IOException {
+ JMXConnector jmxc = attemptConnect("dataRead", "dataRead" , true);
+ jmxc.close();
+ attemptConnect("dataRead", "dataRead12321" , false);
+ }
+
+ @Test
+ public void testServerAuthorization() throws IOException, JMException {
+ try {
+ JMXConnector cc = attemptConnect("dataRead", "dataRead" , true);
+ testLevel(cc,"PULSE_DASHBOARD", true);
+ testLevel(cc,"PULSE_DATABROWSER", false);
+ cc.close();
+
+ cc = attemptConnect("dataWrite", "dataWrite" , true);
+ testLevel(cc,"PULSE_DASHBOARD", true);
+ testLevel(cc,"PULSE_DATABROWSER", true);
+ cc.close();
+
+ cc = attemptConnect("admin", "admin" , true);
+ testLevel(cc,"PULSE_DASHBOARD", true);
+ testLevel(cc,"PULSE_DATABROWSER", true);
+ cc.close();
+
+ } catch (SecurityException e) {
+ fail("Authentication failed " + e.getMessage());
+ }
+ }
+
+ /**
+ * Test pulse authentication through pulse login page and clusterLogout
+ */
+ @Test
+ public void testPulseAuthentication() throws InterruptedException {
+ login("dataRead", "dataRead", true, true);
+ login("dataRead", "dataRead1234", true, false);
+ logout();
+ }
+
+ /**
+ * Test pulse authorization through pulse data browser page
+ */
+ @Test
+ public void testPulseAuthorization() throws InterruptedException {
+ login("dataWrite", "dataWrite", false,true);
+ navigateToDataBrowerPage(true);
+ login("dataRead", "dataRead", true,true);
+ navigateToDataBrowerPage(false);
+ }
+
+ private JMXConnector attemptConnect(String user, String password, boolean expectSuccess) throws IOException {
+ String[] creds = { user, password };
+ Map<String, Object> env = new HashMap<String, Object>();
+ env.put(JMXConnector.CREDENTIALS, creds);
+ try {
+ JMXConnector cc = JMXConnectorFactory.connect(getURL(), env);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+ if(!expectSuccess)
+ fail("Expected Authentication to fail");
+ return cc;
+ } catch (SecurityException e) {
+ if(expectSuccess)
+ fail("Authentication failed " + e.getMessage());
+ }
+ return null;
+ }
+
+ private JMXServiceURL getURL() throws IOException {
+ return new JMXServiceURL("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi");
+ }
+
+ private void testLevel(JMXConnector jmxc, String role, boolean expectTrue) throws IOException, JMException {
+ MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
+ ObjectName accObjName = new ObjectName(AccessControl.OBJECT_NAME_ACCESSCONTROL);
+ boolean hasAccess = (Boolean)mbsc.invoke(accObjName, "authorize",
+ new Object[]{role}, new String[] {String.class.getName()});
+ if(expectTrue) {
+ assertTrue(hasAccess);
+ }
+ else {
+ if(hasAccess)
+ fail("Expected role "+ role + " rejection but user return true");
+ }
+ }
+
+ private void navigateToDataBrowerPage(boolean expectSuccess) {
+ WebElement element = driver.findElement(By.linkText("Data Browser"));
+ element.click();
+ if(expectSuccess) {
+ WebElement dbHeader = (new WebDriverWait(driver, 10))
+ .until(new ExpectedCondition<WebElement>() {
+ @Override
+ public WebElement apply(WebDriver d) {
+ return d.findElement(By.xpath("//*[@id=\"canvasWidth\"]/div[4]/div[1]/div[2]/label"));
+ }
+ });
+ assertNotNull(dbHeader);
+ }
+ }
+
+ private void logout() {
+ driver.get(logoutURL);
+ validateLoginPage();
+ }
+
+ private void validateSuccessfulLogin() {
+ WebElement userNameOnPulsePage = (new WebDriverWait(driver, 10))
+ .until(new ExpectedCondition<WebElement>() {
+ @Override
+ public WebElement apply(WebDriver d) {
+ return d.findElement(By.id("userName"));
+ }
+ });
+ assertNotNull(userNameOnPulsePage);
+ }
+
+ private void validateLoginPage() {
+ WebElement userNameOnPulseLoginPage = (new WebDriverWait(driver, 10))
+ .until(new ExpectedCondition<WebElement>() {
+ @Override
+ public WebElement apply(WebDriver d) {
+ return d.findElement(By.id("user_name"));
+ }
+ });
+ assertNotNull(userNameOnPulseLoginPage);
+ }
+
+ private void login(String userName, String password, boolean logoutFirst, boolean expectSuccess) throws InterruptedException {
+ if(logoutFirst) {
+ logout();
+ }
+ WebElement userNameElement = driver.findElement(By.id("user_name"));
+ WebElement passwordElement = driver.findElement(By.id("user_password"));
+ userNameElement.sendKeys(userName);
+ passwordElement.sendKeys(password);
+ passwordElement.submit();
+ if(expectSuccess) {
+ validateSuccessfulLogin();
+ } else {
+ //We expect login to be unsucceesful so it should go back to Login.html
+ validateLoginPage();
+ }
+ }
+
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ if(driver!=null)
+ driver.close();
+ try {
+ if (tomcat != null) {
+ tomcat.stop();
+ tomcat.destroy();
+ }
+ System.out.println("Tomcat Stopped");
+ if (server != null) {
+ server.stop();
+ }
+ System.out.println("Server Stopped");
+ } catch (LifecycleException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PropsBackedAuthenticator.java
----------------------------------------------------------------------
diff --git a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PropsBackedAuthenticator.java b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PropsBackedAuthenticator.java
new file mode 100644
index 0000000..6076fbf
--- /dev/null
+++ b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PropsBackedAuthenticator.java
@@ -0,0 +1,48 @@
+package com.vmware.gemfire.tools.pulse.tests;
+
+import java.util.Collections;
+
+import javax.management.remote.JMXAuthenticator;
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+
+/**
+ *
+ * @author tushark
+ *
+ */
+public class PropsBackedAuthenticator extends JMXBaseBean implements JMXAuthenticator {
+
+ @Override
+ public Subject authenticate(Object credentials) {
+ String username = null, password = null;
+ if (credentials instanceof String[]) {
+ final String[] aCredentials = (String[]) credentials;
+ username = (String) aCredentials[0];
+ password = (String) aCredentials[1];
+ System.out.println("#intSec User="+ username + " password="+password);
+ String users[] = getStringArray("users");
+ for(String u : users) {
+ if(username.equals(u)) {
+ String storedpassword = getString("users."+u+".password");
+ System.out.println("#intSec PropUser="+ u + " PropPassword="+storedpassword);
+ if(storedpassword!=null && storedpassword.equals(password)) {
+ return new Subject(true, Collections.singleton(new JMXPrincipal(username)), Collections.EMPTY_SET,
+ Collections.EMPTY_SET);
+ } else {
+ throw new SecurityException("Authentication Failed 1");
+ }
+ }
+ }
+ } else {
+ throw new SecurityException("Credentials Missing");
+ }
+ throw new SecurityException("Authentication Failed 2");
+ }
+
+ @Override
+ protected String getKey(String propName) {
+ return propName;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PulseTests.java
----------------------------------------------------------------------
diff --git a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PulseTests.java b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PulseTests.java
index b6dd7c6..c660ba7 100644
--- a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PulseTests.java
+++ b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/PulseTests.java
@@ -131,7 +131,7 @@ public class PulseTests {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
try {
- server = Server.createServer(9999, jmxPropertiesFile);
+ server = Server.createServer(9999, jmxPropertiesFile, false);
String host = "localhost";// InetAddress.getLocalHost().getHostAddress();
int port = 8080;
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/Server.java
----------------------------------------------------------------------
diff --git a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/Server.java b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/Server.java
index 42373c9..d20e10f 100644
--- a/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/Server.java
+++ b/pulse/src/test/java/com/vmware/gemfire/tools/pulse/tests/Server.java
@@ -7,23 +7,26 @@
*/
package com.vmware.gemfire.tools.pulse.tests;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Set;
-
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.MBeanRegistrationException;
-import javax.management.MBeanServer;
-import javax.management.MBeanServerFactory;
-import javax.management.MalformedObjectNameException;
-import javax.management.NotCompliantMBeanException;
-import javax.management.ObjectName;
-import javax.management.remote.JMXConnectorServer;
-import javax.management.remote.JMXConnectorServerFactory;
-import javax.management.remote.JMXServiceURL;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.remote.JMXAuthenticator;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
public class Server {
private static final String DEFAULT_HOST = "127.0.0.1"; //"localhost"
@@ -33,7 +36,7 @@ public class Server {
private final JMXConnectorServer cs;
private String propFile = null;
- public Server(int port, String properties) throws IOException {
+ public Server(int port, String properties, boolean startIntSec) throws IOException {
try {
java.rmi.registry.LocateRegistry.createRegistry(port);
System.out.println("RMI registry ready.");
@@ -44,14 +47,39 @@ public class Server {
this.propFile = properties;
mbs = MBeanServerFactory.createMBeanServer();
- url = new JMXServiceURL(formJMXServiceURLString(DEFAULT_HOST, "" + port));
- cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
-
- cs.start();
-
- loadMBeans();
- }
-
+ url = new JMXServiceURL(formJMXServiceURLString(DEFAULT_HOST, "" + port));
+
+ if(!startIntSec) {
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ } else {
+ Map<String,Object> env = new HashMap<String,Object>();
+ env.put(JMXConnectorServer.AUTHENTICATOR, new PropsBackedAuthenticator());
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
+ System.out.println("#intSec Using PropsBackedAuthenticator");
+ }
+
+ cs.start();
+ loadMBeans();
+ if(startIntSec)
+ loadAccessControllerMBean();
+ }
+
+
+ private void loadAccessControllerMBean() {
+ AccessControl ac = new AccessControl();
+ try {
+ mbs.registerMBean(ac, new ObjectName(AccessControl.OBJECT_NAME_ACCESSCONTROL));
+ } catch (InstanceAlreadyExistsException e) {
+ e.printStackTrace();
+ } catch (MBeanRegistrationException e) {
+ e.printStackTrace();
+ } catch (NotCompliantMBeanException e) {
+ e.printStackTrace();
+ } catch (MalformedObjectNameException e) {
+ e.printStackTrace();
+ }
+ }
+
private String formJMXServiceURLString(String host, String port)
throws UnknownHostException {
/*
@@ -330,14 +358,14 @@ public class Server {
}
}
- createServer(port, props);
+ createServer(port, props, false);
}
- public static Server createServer(int port, String properties)
+ public static Server createServer(int port, String properties, boolean intSecEnabled)
throws MalformedObjectNameException {
Server s = null;
try {
- s = new Server(port, properties);
+ s = new Server(port, properties, intSecEnabled);
} catch (Exception e) {
e.printStackTrace();
return null;
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/87c83da2/pulse/src/test/resources/test.properties
----------------------------------------------------------------------
diff --git a/pulse/src/test/resources/test.properties b/pulse/src/test/resources/test.properties
index fabeea1..67f51bb 100644
--- a/pulse/src/test/resources/test.properties
+++ b/pulse/src/test/resources/test.properties
@@ -6,6 +6,18 @@ members=M1 M2 M3
gemfirexdmembers=M1 M2 M3
aggregatequeries=Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 Q10 Q11 Q12 Q13 Q14 Q15 Q16 Q17 Q18 Q19 Q20 Q21 Q22 Q23 Q24 Q25
+# Integrated security
+users=dataRead dataWrite admin
+
+users.dataRead.password=dataRead
+users.dataRead.roles=PULSE_DASHBOARD
+
+users.dataWrite.password=dataWrite
+users.dataWrite.roles=PULSE_DASHBOARD PULSE_DATABROWSER
+
+users.admin.password=admin
+users.admin.roles=PULSE_DASHBOARD PULSE_DATABROWSER
+
server.S1.listCacheServers=true
server.S1.memberCount=3
server.S1.numClients=2