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:37 UTC
[75/79] incubator-geode git commit: Integrated Security Related
Changes for Pulse ReviewBoard Request : https://reviews.apache.org/r/39646/
Integrated Security Related Changes for Pulse
ReviewBoard Request : 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/813d24c2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/813d24c2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/813d24c2
Branch: refs/heads/feature/GEODE-17
Commit: 813d24c2b317f56c58c43f7c13e07b638e4e7f25
Parents: 3f931ba
Author: tushark <tu...@apache.org>
Authored: Tue Oct 27 12:44:02 2015 +0530
Committer: tushark <tu...@apache.org>
Committed: Tue Nov 3 16:54:35 2015 +0530
----------------------------------------------------------------------
.../tools/pulse/internal/PulseAppListener.java | 54 +++++-
.../internal/controllers/PulseController.java | 14 +-
.../tools/pulse/internal/data/Cluster.java | 15 ++
.../pulse/internal/data/JMXDataUpdater.java | 186 +++++++++++--------
.../pulse/internal/data/PulseConstants.java | 17 ++
.../tools/pulse/internal/data/Repository.java | 11 ++
.../security/GemFireAuthentication.java | 142 ++++++++++++++
.../security/GemFireAuthenticationProvider.java | 68 +++++++
.../pulse/internal/security/LogoutHandler.java | 39 ++++
pulse/src/main/resources/pulse.properties | 16 +-
.../src/main/webapp/WEB-INF/spring-security.xml | 59 ++++--
11 files changed, 509 insertions(+), 112 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/PulseAppListener.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/PulseAppListener.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/PulseAppListener.java
index a063a32..16db34d 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/PulseAppListener.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/PulseAppListener.java
@@ -20,15 +20,20 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map.Entry;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
-import java.util.Map.Entry;
import java.util.logging.Level;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
import com.vmware.gemfire.tools.pulse.internal.controllers.PulseController;
import com.vmware.gemfire.tools.pulse.internal.data.PulseConfig;
import com.vmware.gemfire.tools.pulse.internal.data.PulseConstants;
@@ -64,6 +69,11 @@ public class PulseAppListener implements ServletContextListener {
private boolean sysPulseUseSSLLocator;
private boolean sysPulseUseSSLManager;
+
+ //This property determines if pulse webApp login is authenticated against
+ //GemFire integrated security or custom spring-security config provided
+ //in pulse-authentication-custom.xml
+ private boolean useGemFireCredentials;
@Override
public void contextDestroyed(ServletContextEvent event) {
@@ -80,7 +90,7 @@ public class PulseAppListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
-
+
messagesToBeLogged = messagesToBeLogged
.concat(formatLogString(resourceBundle
.getString("LOG_MSG_CONTEXT_INITIALIZED")));
@@ -179,10 +189,11 @@ public class PulseAppListener implements ServletContextListener {
loadJMXUserDetails();
// Load locator and/or manager details
loadLocatorManagerDetails();
-
+
+ useGemFireCredentials = areWeUsingGemFireSecurityProfile(event);
}
-
- // Set user details in repository
+
+ // Set user details in repository
repository.setJmxUserName(jmxUserName);
repository.setJmxUserPassword(jmxUserPassword);
@@ -195,7 +206,40 @@ public class PulseAppListener implements ServletContextListener {
initializeSSL();
repository.setUseSSLLocator(sysPulseUseSSLLocator);
repository.setUseSSLManager(sysPulseUseSSLManager);
+
+ repository.setUseGemFireCredentials(useGemFireCredentials);
+
+ }
+ /**
+ * Return true if pulse is configure to authenticate using gemfire
+ * integrated security
+ *
+ * @param event
+ * @return
+ */
+ private boolean areWeUsingGemFireSecurityProfile(ServletContextEvent event) {
+ String profile = null;
+ WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
+ if (ctx.getEnvironment() != null) {
+ String[] profiles = ctx.getEnvironment().getActiveProfiles();
+ if (profiles != null && profiles.length > 0) {
+ StringBuilder sb = new StringBuilder();
+ for (String p : profiles)
+ sb.append(p).append(",");
+ LOGGER.info("#SpringProfilesConfigured : " + sb.toString());
+ profile = ctx.getEnvironment().getActiveProfiles()[0];
+ LOGGER.info("#First Profile : " + profile);
+ } else {
+ LOGGER.info("No SpringProfileConfigured using default spring profile");
+ return false;
+ }
+ }
+ if (PulseConstants.APPLICATION_PROPERTY_PULSE_SEC_PROFILE_GEMFIRE.equals(profile)) {
+ LOGGER.info("Using gemfire integrated security profile");
+ return true;
+ }
+ return false;
}
// Function to load pulse version details from properties file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/controllers/PulseController.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/controllers/PulseController.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/controllers/PulseController.java
index 22a9805..8f51099 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/controllers/PulseController.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/controllers/PulseController.java
@@ -22,6 +22,7 @@ import javax.servlet.http.HttpSession;
import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -34,6 +35,7 @@ import com.vmware.gemfire.tools.pulse.internal.json.JSONArray;
import com.vmware.gemfire.tools.pulse.internal.json.JSONException;
import com.vmware.gemfire.tools.pulse.internal.json.JSONObject;
import com.vmware.gemfire.tools.pulse.internal.log.PulseLogWriter;
+import com.vmware.gemfire.tools.pulse.internal.security.GemFireAuthentication;
import com.vmware.gemfire.tools.pulse.internal.service.PulseService;
import com.vmware.gemfire.tools.pulse.internal.service.PulseServiceFactory;
import com.vmware.gemfire.tools.pulse.internal.service.SystemAlertsService;
@@ -150,17 +152,25 @@ public class PulseController {
}
}
+ /* Not used replaced by SpringSecurity Logout tag with LogoutHandler
@RequestMapping(value = "/clusterLogout", method = RequestMethod.GET)
public void clusterLogout(HttpServletRequest request,
HttpServletResponse response) throws IOException {
+ PulseLogWriter LOGGER = PulseLogWriter.getLogger();
+ LOGGER.info("Inside #clusterLogout...");
+ if(Repository.get().isUseGemFireCredentials()) {
+ GemFireAuthentication authentication = (GemFireAuthentication) SecurityContextHolder.getContext()
+ .getAuthentication();
+ authentication.getJmxc().close();
+ LOGGER.info("Closing GemFireAuthentication JMX Connection...");
+ }
HttpSession session = request.getSession(false);
if (session != null) {
-
// End session and redirect
session.invalidate();
}
response.sendRedirect("../Login.html");
- }
+ }*/
@RequestMapping(value = "/pulseVersion", method = RequestMethod.GET)
public void pulseVersion(HttpServletRequest request,
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Cluster.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Cluster.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Cluster.java
index d79438b..edf34d4 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Cluster.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Cluster.java
@@ -37,6 +37,8 @@ import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.management.remote.JMXConnector;
+
import org.apache.commons.collections.buffer.CircularFifoBuffer;
import com.vmware.gemfire.tools.pulse.internal.json.JSONArray;
@@ -2351,6 +2353,11 @@ public class Cluster extends Thread {
if (LOGGER.infoEnabled()) {
LOGGER.info("Exception Occured while updating cluster data : " + e.getMessage());
}
+
+ if (LOGGER.fineEnabled()) {
+ LOGGER.fine("Exception Occured while updating cluster data : ",e);
+ }
+
}
try {
@@ -2864,6 +2871,14 @@ public class Cluster extends Thread {
public boolean deleteQueryById(String userId, String queryId) {
return this.getDataBrowser().deleteQueryById(userId, queryId);
}
+
+ public JMXConnector connectToGemFire(String user, String password) {
+ if(this.updater instanceof JMXDataUpdater) {
+ return ((JMXDataUpdater) this.updater).getJMXConnection(user, password, false);
+ } else {
+ return null;
+ }
+ }
/**
* inner class for creating Mock Data
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/JMXDataUpdater.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/JMXDataUpdater.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/JMXDataUpdater.java
index 27aed67..6d7c8d5 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/JMXDataUpdater.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/JMXDataUpdater.java
@@ -49,11 +49,14 @@ import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.rmi.ssl.SslRMIClientSocketFactory;
+import org.springframework.security.core.context.SecurityContextHolder;
+
import com.vmware.gemfire.tools.pulse.internal.controllers.PulseController;
import com.vmware.gemfire.tools.pulse.internal.data.JmxManagerFinder.JmxManagerInfo;
import com.vmware.gemfire.tools.pulse.internal.json.JSONException;
import com.vmware.gemfire.tools.pulse.internal.json.JSONObject;
import com.vmware.gemfire.tools.pulse.internal.log.PulseLogWriter;
+import com.vmware.gemfire.tools.pulse.internal.security.GemFireAuthentication;
import com.vmware.gemfire.tools.pulse.internal.util.StringUtils;
/**
@@ -133,57 +136,92 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
}
}
+
+ private JmxManagerInfo getManagerInfoFromLocator(Repository repository) {
- private JMXConnector getJMXConnection() {
- JMXConnector connection = null;
- // Reference to repository
- Repository repository = Repository.get();
try {
-
- String jmxSerURL = "";
+ String locatorHost = repository.getJmxHost();
+ int locatorPort = Integer.parseInt(repository.getJmxPort());
if (LOGGER.infoEnabled()) {
- LOGGER.info(resourceBundle.getString("LOG_MSG_USE_LOCATOR_VALUE") + ":"
- + repository.getJmxUseLocator());
+ LOGGER.info(resourceBundle.getString("LOG_MSG_HOST") + " : " + locatorHost + " & "
+ + resourceBundle.getString("LOG_MSG_PORT") + " : " + locatorPort);
}
- if (repository.getJmxUseLocator()) {
+ InetAddress inetAddr = InetAddress.getByName(locatorHost);
+
+ if ((inetAddr instanceof Inet4Address) || (inetAddr instanceof Inet6Address)) {
- String locatorHost = repository.getJmxHost();
- int locatorPort = Integer.parseInt(repository.getJmxPort());
+ if (inetAddr instanceof Inet4Address) {
+ if (LOGGER.infoEnabled()) {
+ LOGGER.info(resourceBundle.getString("LOG_MSG_LOCATOR_IPV4_ADDRESS") + " - " + inetAddr.toString());
+ }
+ } else {
+ if (LOGGER.infoEnabled()) {
+ LOGGER.info(resourceBundle.getString("LOG_MSG_LOCATOR_IPV6_ADDRESS") + " - " + inetAddr.toString());
+ }
+ }
+
+ JmxManagerInfo jmxManagerInfo = JmxManagerFinder.askLocatorForJmxManager(inetAddr, locatorPort, 15000,
+ repository.isUseSSLLocator());
+ if (jmxManagerInfo.port == 0) {
+ if (LOGGER.infoEnabled()) {
+ LOGGER.info(resourceBundle.getString("LOG_MSG_LOCATOR_COULD_NOT_FIND_MANAGER"));
+ }
+ }
+ return jmxManagerInfo;
+ } else {
+ // Locator has Invalid locator Address
if (LOGGER.infoEnabled()) {
- LOGGER.info(resourceBundle.getString("LOG_MSG_HOST") + " : "
- + locatorHost + " & " + resourceBundle.getString("LOG_MSG_PORT")
- + " : " + locatorPort);
+ LOGGER.info(resourceBundle.getString("LOG_MSG_LOCATOR_BAD_ADDRESS"));
}
+ cluster.setConnectionErrorMsg(resourceBundle.getString("LOG_MSG_JMX_CONNECTION_BAD_ADDRESS"));
+ // update message to display on UI
+ cluster.setConnectionErrorMsg(resourceBundle
+ .getString("LOG_MSG_JMX_CONNECTION_BAD_ADDRESS"));
+ return null;
+ }
+ } catch (IOException e) {
+ StringWriter swBuffer = new StringWriter();
+ PrintWriter prtWriter = new PrintWriter(swBuffer);
+ e.printStackTrace(prtWriter);
+ LOGGER.severe("Exception Details : " + swBuffer.toString() + "\n");
+ }
+ return null;
+ }
+
+ /**
+ * Default connection is Pulse which uses configured userName and password
+ * @return
+ */
+ public JMXConnector getJMXConnection() {
+ return getJMXConnection(this.userName, this.userPassword, true);
+ }
- InetAddress inetAddr = InetAddress.getByName(locatorHost);
+ /**
+ * Get connection for given userName and password. This is used for DataBrowser
+ * queries which has to be fired using credentials provided at pulse login page
+ *
+ * @param user jmxUser name
+ * @param password password
+ * @return
+ */
+ public JMXConnector getJMXConnection(String user, String password, final boolean registerURL) {
+ JMXConnector connection = null;
+ // Reference to repository
+ Repository repository = Repository.get();
+ try {
- if ((inetAddr instanceof Inet4Address)
- || (inetAddr instanceof Inet6Address)) {
+ String jmxSerURL = "";
- if (inetAddr instanceof Inet4Address) {
- // Locator has IPv4 Address
- if (LOGGER.infoEnabled()) {
- LOGGER.info(resourceBundle
- .getString("LOG_MSG_LOCATOR_IPV4_ADDRESS")
- + " - "
- + inetAddr.toString());
- }
- } else {
- // Locator has IPv6 Address
- if (LOGGER.infoEnabled()) {
- LOGGER.info(resourceBundle
- .getString("LOG_MSG_LOCATOR_IPV6_ADDRESS")
- + " - "
- + inetAddr.toString());
- }
- }
+ if (LOGGER.infoEnabled()) {
+ LOGGER.info(resourceBundle.getString("LOG_MSG_USE_LOCATOR_VALUE") + ":"
+ + repository.getJmxUseLocator());
+ }
- JmxManagerInfo jmxManagerInfo = JmxManagerFinder
- .askLocatorForJmxManager(inetAddr, locatorPort, 15000,
- repository.isUseSSLLocator());
+ if (repository.getJmxUseLocator()) {
+ JmxManagerInfo jmxManagerInfo = getManagerInfoFromLocator(repository);
if (jmxManagerInfo.port == 0) {
if (LOGGER.infoEnabled()) {
@@ -210,28 +248,6 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
jmxSerURL = formJMXServiceURLString(jmxManagerInfo.host,
String.valueOf(jmxManagerInfo.port));
}
-
- } /*
- * else if (inetAddr instanceof Inet6Address) { // Locator has IPv6
- * Address if(LOGGER.infoEnabled()){
- * LOGGER.info(resourceBundle.getString
- * ("LOG_MSG_LOCATOR_IPV6_ADDRESS")); } // update message to display
- * on UI cluster.setConnectionErrorMsg(resourceBundle.getString(
- * "LOG_MSG_JMX_CONNECTION_IPv6_ADDRESS"));
- *
- * }
- */else {
- // Locator has Invalid locator Address
- if (LOGGER.infoEnabled()) {
- LOGGER
- .info(resourceBundle.getString("LOG_MSG_LOCATOR_BAD_ADDRESS"));
- }
- // update message to display on UI
- cluster.setConnectionErrorMsg(resourceBundle
- .getString("LOG_MSG_JMX_CONNECTION_BAD_ADDRESS"));
-
- }
-
} else {
if (LOGGER.infoEnabled()) {
LOGGER.info(resourceBundle.getString("LOG_MSG_HOST") + " : "
@@ -243,11 +259,8 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
if (StringUtils.isNotNullNotEmptyNotWhiteSpace(jmxSerURL)) {
JMXServiceURL url = new JMXServiceURL(jmxSerURL);
-
- // String[] creds = {"controlRole", "R&D"};
- String[] creds = { this.userName, this.userPassword };
+ String[] creds = { user, password };
Map<String, Object> env = new HashMap<String, Object>();
-
env.put(JMXConnector.CREDENTIALS, creds);
if (repository.isUseSSLManager()) {
@@ -255,20 +268,19 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
env.put("com.sun.jndi.rmi.factory.socket",
new SslRMIClientSocketFactory());
}
-
+ LOGGER.info("Connecting to jmxURL : " + jmxSerURL);
connection = JMXConnectorFactory.connect(url, env);
// Register Pulse URL if not already present in the JMX Manager
- registerPulseUrlToManager(connection);
+ if(registerURL)
+ registerPulseUrlToManager(connection);
}
} catch (Exception e) {
if (e instanceof UnknownHostException) {
- // update message to display on UI
cluster.setConnectionErrorMsg(resourceBundle
.getString("LOG_MSG_JMX_CONNECTION_UNKNOWN_HOST"));
}
- // write errors
StringWriter swBuffer = new StringWriter();
PrintWriter prtWriter = new PrintWriter(swBuffer);
e.printStackTrace(prtWriter);
@@ -283,7 +295,6 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
this.conn = null;
}
}
-
return connection;
}
@@ -2257,7 +2268,12 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_MEMBER);
Cluster.Member member = cluster.getMembersHMap().get(memberName);
-
+
+ //Following attributes are not present in 9.0
+ //"Members"
+ //"EmptyNodes"
+ //"SystemRegionEntryCount"
+ //"MemberCount"
AttributeList attributeList = this.mbs.getAttributes(mbeanName,
PulseConstants.REGION_MBEAN_ATTRIBUTES);
@@ -2453,16 +2469,33 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
@Override
public JSONObject executeQuery(String queryText, String members, int limit)
throws JSONException {
+ JSONObject queryResult = new JSONObject();
+ MBeanServerConnection mbs = this.mbs;
+ Set<ObjectName> systemMBeans = this.systemMBeans;
+
+ boolean useUserConnection = Repository.get().isUseGemFireCredentials();
+ if(useUserConnection) {
+ GemFireAuthentication authentication = (GemFireAuthentication) SecurityContextHolder.getContext()
+ .getAuthentication();
+ JMXConnector conn = authentication.getJmxc();
+ try {
+ mbs = conn.getMBeanServerConnection();
+ systemMBeans = mbs.queryNames(this.MBEAN_OBJECT_NAME_SYSTEM_DISTRIBUTED, null);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ LOGGER.fine("#executeQuery : Using GemFireAuthentication connection for query");
+ } else {
+ LOGGER.fine("#executeQuery : Using Global connection for query");
+ }
- JSONObject queryResult = new JSONObject();
-
- if (this.mbs != null && this.systemMBeans != null) {
+ if (mbs != null && systemMBeans != null) {
Object opParams[] = { queryText, members, limit };
- for (ObjectName sysMBean : this.systemMBeans) {
+ for (ObjectName sysMBean : systemMBeans) {
try {
- String resultString = (String) (this.mbs.invoke(sysMBean,
+ String resultString = (String) (mbs.invoke(sysMBean,
PulseConstants.MBEAN_OPERATION_QUERYDATABROWSER, opParams,
this.opSignature));
@@ -2478,7 +2511,10 @@ public class JMXDataUpdater implements IClusterUpdater, NotificationListener {
}
}
}
-
+
+ if(useUserConnection) {
+ //TODO : close connection??
+ }
return queryResult;
}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/PulseConstants.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/PulseConstants.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/PulseConstants.java
index cac9b04..6da5883 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/PulseConstants.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/PulseConstants.java
@@ -91,6 +91,9 @@ public class PulseConstants {
public static final String APPLICATION_PROPERTY_PULSE_LOGLEVEL = "pulse.Log-Level";
public static final String APPLICATION_PROPERTY_PULSE_LOGAPPEND = "pulse.Log-Append";
public static final String APPLICATION_PROPERTY_PULSE_PRODUCTSUPPORT = "pulse.product";
+ public static final String APPLICATION_PROPERTY_PULSE_SEC_PROFILE_GEMFIRE = "pulse.authentication.gemfire";
+ public static final String APPLICATION_PROPERTY_PULSE_SEC_PROFILE_DEFAULT = "pulse.authentication.default";
+ public static final String APPLICATION_PROPERTY_PULSE_SPRING_PROFILE_KEY = "spring.profiles.default";
// STRING FLAGS
public static final String STRING_FLAG_TRUE = "true";
@@ -114,6 +117,7 @@ public class PulseConstants {
public static final String OBJECT_NAME_TABLE_AGGREGATE_PATTERN = OBJECT_DOMAIN_NAME_SQLFIRE + ":service=Table,type=Aggregate,table=";
public static final String OBJECT_NAME_REGION_ON_MEMBER_REGION = OBJECT_DOMAIN_NAME_GEMFIRE + ":service=Region,name=";
public static final String OBJECT_NAME_REGION_ON_MEMBER_MEMBER = ",type=Member,member=";
+ public static final String OBJECT_NAME_ACCESSCONTROL_MBEAN = "GemFire:service=AccessControl,type=Distributed";
public static final String MBEAN_KEY_PROPERTY_SERVICE = "service";
public static final String MBEAN_KEY_PROPERTY_SERVICE_VALUE_REGION = "Region";
@@ -300,6 +304,11 @@ public class PulseConstants {
public static final String PRODUCT_NAME_GEMFIRE = "gemfire"; // For GemFire
public static final String PRODUCT_NAME_SQLFIRE = "gemfirexd"; // For SQLFire
+ //Following attributes are not present in 9.0
+ //"Members"
+ //"EmptyNodes"
+ //"SystemRegionEntryCount"
+ //"MemberCount"
public static final String[] REGION_MBEAN_ATTRIBUTES = {
MBEAN_ATTRIBUTE_MEMBERS, MBEAN_ATTRIBUTE_FULLPATH,
MBEAN_ATTRIBUTE_DISKREADSRATE, MBEAN_ATTRIBUTE_DISKWRITESRATE,
@@ -388,6 +397,14 @@ public class PulseConstants {
public static final String[] SF_TABLE_MBEAN_ATTRIBUTES = {
MBEAN_ATTRIBUTE_ENTRYSIZE, MBEAN_ATTRIBUTE_NUMBEROFROWS };
+
+ public static final String PULSE_ROLES[] = {
+ "PULSE_DASHBOARD",
+ "PULSE_DATABROWSER",
+ "PULSE_WEBGFSH",
+ "PULSE_ADMIN_V1",
+ "PULSE_ADMIN_V2"
+ };
// SSL Related attributes
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Repository.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Repository.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Repository.java
index 5d9f650..87bf778 100644
--- a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Repository.java
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/data/Repository.java
@@ -37,6 +37,7 @@ public class Repository {
private Boolean isEmbeddedMode;
private boolean useSSLLocator = false;
private boolean useSSLManager = false;
+ private boolean useGemFireCredentials = false;
private String pulseWebAppUrl;
@@ -202,4 +203,14 @@ public class Repository {
return this.resourceBundle;
}
+ public boolean isUseGemFireCredentials() {
+ return useGemFireCredentials;
+ }
+
+ public void setUseGemFireCredentials(boolean useGemFireCredentials) {
+ this.useGemFireCredentials = useGemFireCredentials;
+ }
+
+
+
}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthentication.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthentication.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthentication.java
new file mode 100644
index 0000000..23f43e0
--- /dev/null
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthentication.java
@@ -0,0 +1,142 @@
+package com.vmware.gemfire.tools.pulse.internal.security;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.remote.JMXConnector;
+
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.SpringSecurityCoreVersion;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.vmware.gemfire.tools.pulse.internal.data.PulseConstants;
+import com.vmware.gemfire.tools.pulse.internal.log.PulseLogWriter;
+
+/**
+ * Spring security authentication object for GemFire
+ *
+ * To use GemFire Integrated Security Model set Spring Application Profile to pulse.authentication.gemfire
+ *
+ * 1. Authentication :
+ * 1.a GemFire profile creates JMX connection with given credentials at the login time.
+ * 1.b Successful connect is considered as Successful Authentication for Pulse WebApp
+ *
+ *
+ * 2. Authorization :
+ * 2.a Using newly created authenticated connection AccessControlMXBean is called to get authentication
+ * levels. See @See {@link #populateAuthorities(JMXConnector)}. This sets Spring Security Authorities
+ * 2.b DataBrowser end-points are required to be authorized against Spring Granted Authority
+ * @See spring-security.xml
+ * 2.c When executing Data-Browser query, user-level jmx connection is used so at to put access-control
+ * over the resources query is accessing.
+ * @See #com.vmware.gemfire.tools.pulse.internal.data.JMXDataUpdater#executeQuery
+ *
+ * 3. Connection Management - Spring Security LogoutHandler closes session level connection
+ *
+ * TODO : Better model would be to maintain background connection map for Databrowser instead
+ * of each web session creating rmi connection and map user to correct entry in the connection map
+ *
+ * @author Tushar Khairnar
+ * @since version 9.0
+ */
+public class GemFireAuthentication extends UsernamePasswordAuthenticationToken {
+
+ private final static PulseLogWriter LOGGER = PulseLogWriter.getLogger();
+
+ private JMXConnector jmxc=null;
+
+ public GemFireAuthentication(Object principal, Object credentials, Collection<GrantedAuthority> list, JMXConnector jmxc) {
+ super(principal, credentials, list);
+ this.jmxc = jmxc;
+ }
+
+ private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
+
+
+ public void closeJMXConnection(){
+ try {
+ jmxc.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MBeanServerConnection getRemoteMBeanServer() {
+ try {
+ return jmxc.getMBeanServerConnection();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static boolean authorize(String role){
+ try {
+ GemFireAuthentication authentication = (GemFireAuthentication) SecurityContextHolder
+ .getContext().getAuthentication();
+ MBeanServerConnection mbeanServer = authentication
+ .getRemoteMBeanServer();
+ LOGGER.fine("#GemFireAuthentication : Checking for role="+role);
+ ObjectName name = new ObjectName(PulseConstants.OBJECT_NAME_ACCESSCONTROL_MBEAN);
+ Object[] params = new Object[] {role};
+ String[] signature = new String[] {String.class.getCanonicalName()};
+ Boolean result = (Boolean)mbeanServer.invoke(name, "authorize", params, signature);
+ return result;
+ } catch (MalformedObjectNameException e) {
+ throw new RuntimeException(e);
+ } catch (InstanceNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (MBeanException e) {
+ throw new RuntimeException(e);
+ } catch (ReflectionException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static ArrayList<GrantedAuthority> populateAuthorities(JMXConnector jmxc) {
+ ObjectName name;
+ try {
+ name = new ObjectName(PulseConstants.OBJECT_NAME_ACCESSCONTROL_MBEAN);
+ MBeanServerConnection mbeanServer = jmxc.getMBeanServerConnection();
+ ArrayList<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+ authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
+ for(String role : PulseConstants.PULSE_ROLES){
+ Object[] params = new Object[] {role};
+ String[] signature = new String[] {String.class.getCanonicalName()};
+ boolean result = (Boolean)mbeanServer.invoke(name, "authorize", params, signature);
+ if(result){
+ //spring sec require ROLE_ prefix
+ authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
+ }
+ }
+ return authorities;
+ } catch (MalformedObjectNameException e) {
+ throw new RuntimeException(e);
+ } catch (InstanceNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (MBeanException e) {
+ throw new RuntimeException(e);
+ } catch (ReflectionException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public JMXConnector getJmxc() {
+ return jmxc;
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthenticationProvider.java b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthenticationProvider.java
new file mode 100644
index 0000000..704c177
--- /dev/null
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/GemFireAuthenticationProvider.java
@@ -0,0 +1,68 @@
+package com.vmware.gemfire.tools.pulse.internal.security;
+
+import java.util.Collection;
+
+import javax.management.remote.JMXConnector;
+
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+
+import com.vmware.gemfire.tools.pulse.internal.data.Repository;
+import com.vmware.gemfire.tools.pulse.internal.log.PulseLogWriter;
+
+/**
+ * Spring security AuthenticationProvider for GemFire. It connects to
+ * gemfire manager using given credentials. Successful connect is treated
+ * as successful authentication and web user is authenticated
+ *
+ * @author Tushar Khairnar
+ * @since version 9.0
+ */
+public class GemFireAuthenticationProvider implements AuthenticationProvider {
+
+ private final static PulseLogWriter LOGGER = PulseLogWriter.getLogger();
+
+
+ @Override
+ public Authentication authenticate(Authentication authentication)
+ throws AuthenticationException {
+
+ if (authentication instanceof GemFireAuthentication) {
+ GemFireAuthentication gemAuth = (GemFireAuthentication) authentication;
+ LOGGER.fine("GemAuthentication is connected? = "
+ + gemAuth.getJmxc());
+ if(gemAuth.getJmxc()!=null && gemAuth.isAuthenticated())
+ return gemAuth;
+ }
+
+ String name = authentication.getName();
+ String password = authentication.getCredentials().toString();
+
+ try {
+ LOGGER.fine("Connecting to GemFire with user=" + name);
+ JMXConnector jmxc = Repository.get().getCluster().connectToGemFire(name, password);
+ if(jmxc!=null) {
+ Collection<GrantedAuthority> list = GemFireAuthentication.populateAuthorities(jmxc);
+ GemFireAuthentication auth = new GemFireAuthentication(
+ authentication.getPrincipal(),
+ authentication.getCredentials(), list, jmxc);
+ LOGGER.fine("For user " + name + " authList="+ list);
+ return auth;
+ } else
+ throw new AuthenticationServiceException("JMX Connection unavailable");
+ } catch (Exception e) {
+ throw new BadCredentialsException("Error connecting to GemFire JMX Server", e);
+ }
+ }
+
+ @Override
+ public boolean supports(Class<?> authentication) {
+ return authentication.equals(UsernamePasswordAuthenticationToken.class);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/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
new file mode 100644
index 0000000..78d4703
--- /dev/null
+++ b/pulse/src/main/java/com/vmware/gemfire/tools/pulse/internal/security/LogoutHandler.java
@@ -0,0 +1,39 @@
+package com.vmware.gemfire.tools.pulse.internal.security;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
+
+import com.vmware.gemfire.tools.pulse.internal.data.Repository;
+import com.vmware.gemfire.tools.pulse.internal.log.PulseLogWriter;
+
+/**
+ * Handler is used to close jmx connection maintained at user-level
+ * @author tushark
+ *
+ */
+public class LogoutHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
+
+ public LogoutHandler(String defaultTargetURL) {
+ this.setDefaultTargetUrl(defaultTargetURL);
+ }
+
+ public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+ throws IOException, ServletException {
+ PulseLogWriter LOGGER = PulseLogWriter.getLogger();
+ LOGGER.fine("Invoked #LogoutHandler ...");
+ if (Repository.get().isUseGemFireCredentials()) {
+ GemFireAuthentication gemauthentication = (GemFireAuthentication) authentication;
+ gemauthentication.getJmxc().close();
+ LOGGER.info("#LogoutHandler : Closing GemFireAuthentication JMX Connection...");
+ }
+ super.onLogoutSuccess(request, response, authentication);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/resources/pulse.properties
----------------------------------------------------------------------
diff --git a/pulse/src/main/resources/pulse.properties b/pulse/src/main/resources/pulse.properties
index 17299c2..4286615 100644
--- a/pulse/src/main/resources/pulse.properties
+++ b/pulse/src/main/resources/pulse.properties
@@ -2,22 +2,16 @@
#Tue, 09 Oct 2012 16:39:00
# JMX Locator/Manager Properties
-#pulse.useLocator=true
-#pulse.host=SachinK.clarice.com
-#pulse.useLocator=true
-#pulse.host=pnq-pratik.vmware.com
-#pulse.port=10334
-
-pulse.useLocator=true
-pulse.host=pnq-pratik.vmware.com
-pulse.port=10334
+pulse.useLocator=false
+pulse.host=localhost
+pulse.port=1099
#pulse.useSSL.locator=true
#pulse.useSSL.manager=true
# JMX User Properties
-pulse.jmxUserName=controlRole
-pulse.jmxUserPassword=R&D
+pulse.jmxUserName=admin
+pulse.jmxUserPassword=admin
# Logging Configurations Properties
pulse.Log-File-Name=PULSELOG
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/813d24c2/pulse/src/main/webapp/WEB-INF/spring-security.xml
----------------------------------------------------------------------
diff --git a/pulse/src/main/webapp/WEB-INF/spring-security.xml b/pulse/src/main/webapp/WEB-INF/spring-security.xml
index 4264566..cb24e1f 100644
--- a/pulse/src/main/webapp/WEB-INF/spring-security.xml
+++ b/pulse/src/main/webapp/WEB-INF/spring-security.xml
@@ -9,32 +9,41 @@
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
- <http auto-config="true">
+ <http auto-config="true" use-expressions="true">
<!-- Can be invoked w/o auth -->
- <intercept-url pattern="/Login.html" access="IS_AUTHENTICATED_ANONYMOUSLY" />
- <intercept-url pattern="/pulse/pulseVersion" access="IS_AUTHENTICATED_ANONYMOUSLY" />
- <intercept-url pattern="/pulse/authenticateUser" access="IS_AUTHENTICATED_ANONYMOUSLY" />
- <intercept-url pattern="/pulse/pulseProductSupport" access="IS_AUTHENTICATED_ANONYMOUSLY" />
+ <intercept-url pattern="/Login.html" access="permitAll" />
+ <intercept-url pattern="/pulse/pulseVersion" access="permitAll" />
+ <intercept-url pattern="/pulse/authenticateUser" access="permitAll" />
+ <intercept-url pattern="/pulse/pulseProductSupport" access="permitAll" />
<!-- Can be invoked w/o auth -->
+ <!-- Restricted urls -->
+ <intercept-url pattern="/DataBrowser.html" access="hasRole('ROLE_PULSE_DASHBOARD') and hasRole('ROLE_PULSE_DATABROWSER')" />
+ <intercept-url pattern="/clusterDetail.html" access="hasRole('ROLE_PULSE_DASHBOARD')" />
+ <intercept-url pattern="/MemberDetails.html" access="hasRole('ROLE_PULSE_DASHBOARD')" />
+ <intercept-url pattern="/regionDetail.html" access="hasRole('ROLE_PULSE_DASHBOARD')" />
+ <intercept-url pattern="/pulse/*" access="hasRole('ROLE_PULSE_DASHBOARD')" />
+ <intercept-url pattern="/clearAlerts" access="hasRole('ROLE_PULSE_DASHBOARD')" />
+ <intercept-url pattern="/acknowledgeAlert" access="hasRole('ROLE_PULSE_DASHBOARD')" />
+ <!-- /dataBrowserRegions, /dataBrowserQuery, /dataBrowserQueryHistory, /dataBrowserExport -->
+ <intercept-url pattern="/dataBrowser*" access="hasRole('ROLE_PULSE_DASHBOARD') and hasRole('ROLE_PULSE_DATABROWSER')" />
+ <intercept-url pattern="/getQueryStatisticsGridModel/*" access="hasRole('ROLE_PULSE_DASHBOARD') and hasRole('ROLE_PULSE_DATABROWSER')" />
+
<!-- Restricted urls -->
- <!-- Hide Data Browser tab for Pulse-Cheetah Release -->
- <!-- <intercept-url pattern="/DataBrowser.html" access="ROLE_RESTRICTED" /> -->
- <!-- Restricted urls -->
-
- <!-- Can be invoked only with auth -->
- <intercept-url pattern="/*.html" access="ROLE_USER,ROLE_GEMFIRETESTING,ROLE_PULSEUSER" />
- <intercept-url pattern="/pulse/*" access="ROLE_USER,ROLE_GEMFIRETESTING,ROLE_PULSEUSER" />
- <!-- Can be invoked only with auth -->
-
<form-login login-page="/Login.html"
authentication-failure-handler-ref="authenticationFailureHandler"
default-target-url="/clusterDetail.html" />
-
- <logout logout-url="/pulse/clusterLogout" logout-success-url="/Login.html" />
+
+ <logout logout-url="/pulse/clusterLogout" success-handler-ref="customLogoutSuccessHandler"/>
+
</http>
+ <beans:bean name="customLogoutSuccessHandler"
+ class="com.vmware.gemfire.tools.pulse.internal.security.LogoutHandler">
+ <beans:constructor-arg value="/Login.html"/>
+ </beans:bean>
+
<beans:bean id="authenticationFailureHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="exceptionMappings">
@@ -46,21 +55,33 @@
</beans:props>
</beans:property>
</beans:bean>
+
+ <beans:bean id="gemAuthenticationProvider"
+ class="com.vmware.gemfire.tools.pulse.internal.security.GemFireAuthenticationProvider">
+ </beans:bean>
- <!-- Default user authentication based on in-memory user service -->
+
+ <!-- Default user authentication -->
<beans:beans profile="pulse.authentication.default">
<authentication-manager>
<authentication-provider>
<user-service>
- <user name="admin" password="admin" authorities="ROLE_USER" />
+ <user name="admin" password="admin" authorities="ROLE_USER,ROLE_PULSE_DASHBOARD,ROLE_PULSE_DATABROWSER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
+
+ <!-- Default user authentication based on gemfire integrated security -->
+ <beans:beans profile="pulse.authentication.gemfire">
+ <authentication-manager alias="authenticationManager">
+ <authentication-provider ref="gemAuthenticationProvider"/>
+ </authentication-manager>
+ </beans:beans>
<!-- Custom user authentication specified externally -->
<beans:beans profile="pulse.authentication.custom">
<beans:import resource="classpath:pulse-authentication-custom.xml" />
</beans:beans>
-
+
</beans:beans>
\ No newline at end of file