This is an automated email from the ASF dual-hosted git repository.
jinmeiliao pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new 2e030f6 GEODE-7156: add token based authentication support in
management rest… (#4005)
2e030f6 is described below
commit 2e030f61137bab475f89cabdb1c2d0fc9a9c293d
Author: Jinmei Liao <[email protected]>
AuthorDate: Thu Sep 5 17:43:04 2019 -0700
GEODE-7156: add token based authentication support in management rest…
(#4005)
* GEODE-7156: add token based authentication support in management rest api
Co-authored-by: Joris Melchior <[email protected]>
* added security-auth-token-enabled-components property
* pass this property to the management web application context
* enabled auth token filter when that property is set
* improve SimpleSecurityManager to authenticate mock token
---
.../ManagementRestAuthTokenIntegrationTest.java | 70 ++++++++++++++++++++
.../integrationTest/resources/assembly_content.txt | 1 +
.../InternalDistributedSystemJUnitTest.java | 25 +++++++
.../geode/distributed/ConfigurationProperties.java | 21 ++++++
.../internal/AbstractDistributionConfig.java | 11 +++-
.../distributed/internal/DistributionConfig.java | 25 +++++++
.../internal/DistributionConfigImpl.java | 27 +++++++-
.../distributed/internal/InternalLocator.java | 8 +++
.../geode/examples/SimpleSecurityManager.java | 15 ++++-
.../org/apache/geode/internal/AbstractConfig.java | 9 ++-
.../geode/internal/cache/InternalHttpService.java | 1 +
.../internal/security/ResourceConstants.java | 3 +-
.../geode/security/AuthTokenEnabledComponents.java | 33 +++++-----
.../org/apache/geode/security/SecurityManager.java | 20 +++++-
.../sanctioned-geode-core-serializables.txt | 1 +
.../internal/DistributionConfigJUnitTest.java | 22 ++++++-
.../apache/geode/internal/AbstractConfigTest.java | 9 ++-
.../internal/rest/BaseLocatorContextLoader.java | 2 +
.../management/internal/rest/GeodeComponent.java | 4 ++
.../internal/rest/LocatorWebContext.java | 9 ---
.../internal/rest/SecuredLocatorContextLoader.java | 1 -
...dLocatorWithAuthTokenEnabledContextLoader.java} | 5 +-
.../internal/rest/RequestWithAuthTokenTest.java | 59 +++++++++++++++++
.../rest/security/GeodeAuthenticationProvider.java | 24 +++++--
.../rest/security/JwtAuthenticationFilter.java | 77 ++++++++++++++++++++++
.../rest/security/RestSecurityConfiguration.java | 48 ++++++++++----
.../rest/security/JwtAuthenticationFilterTest.java | 69 +++++++++++++++++++
.../security/LogNoPasswordDistributedTest.java | 2 +-
28 files changed, 538 insertions(+), 63 deletions(-)
diff --git
a/geode-assembly/src/integrationTest/java/org/apache/geode/rest/internal/web/ManagementRestAuthTokenIntegrationTest.java
b/geode-assembly/src/integrationTest/java/org/apache/geode/rest/internal/web/ManagementRestAuthTokenIntegrationTest.java
new file mode 100644
index 0000000..4f134d1
--- /dev/null
+++
b/geode-assembly/src/integrationTest/java/org/apache/geode/rest/internal/web/ManagementRestAuthTokenIntegrationTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.geode.rest.internal.web;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.examples.SimpleSecurityManager;
+import org.apache.geode.test.junit.rules.LocatorStarterRule;
+
+public class ManagementRestAuthTokenIntegrationTest {
+
+ @ClassRule
+ public static LocatorStarterRule locator = new LocatorStarterRule()
+
.withProperty(ConfigurationProperties.SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS,
+ "all,management")
+ .withSecurityManager(SimpleSecurityManager.class)
+ .withHttpService()
+ .withAutoStart();
+
+ @Test
+ public void name() throws Exception {
+ String response =
+ requestUseBearerToken(
+ "http://localhost:" + locator.getHttpPort() +
"/management/experimental/ping", "bar");
+
+ assertThat(response).isEqualTo("pong");
+ }
+
+ private static String requestUseBearerToken(String stringUrl, String
bearerToken)
+ throws Exception {
+ BufferedReader reader = null;
+ URL url = new URL(stringUrl);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestProperty("Authorization", "Bearer " + bearerToken);
+ connection.setDoOutput(true);
+ connection.setRequestMethod("GET");
+ reader = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
+ String line = null;
+ StringWriter out =
+ new StringWriter(connection.getContentLength() > 0 ?
connection.getContentLength() : 2048);
+ while ((line = reader.readLine()) != null) {
+ out.append(line);
+ }
+ String response = out.toString();
+ return response;
+ }
+}
diff --git a/geode-assembly/src/integrationTest/resources/assembly_content.txt
b/geode-assembly/src/integrationTest/resources/assembly_content.txt
index 06731a4..1aac949 100644
--- a/geode-assembly/src/integrationTest/resources/assembly_content.txt
+++ b/geode-assembly/src/integrationTest/resources/assembly_content.txt
@@ -899,6 +899,7 @@ javadoc/org/apache/geode/redis/package-summary.html
javadoc/org/apache/geode/redis/package-tree.html
javadoc/org/apache/geode/security/AccessControl.html
javadoc/org/apache/geode/security/AuthInitialize.html
+javadoc/org/apache/geode/security/AuthTokenEnabledComponents.html
javadoc/org/apache/geode/security/AuthenticationFailedException.html
javadoc/org/apache/geode/security/AuthenticationRequiredException.html
javadoc/org/apache/geode/security/Authenticator.html
diff --git
a/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/InternalDistributedSystemJUnitTest.java
b/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/InternalDistributedSystemJUnitTest.java
index d310a47..99329b0 100644
---
a/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/InternalDistributedSystemJUnitTest.java
+++
b/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/InternalDistributedSystemJUnitTest.java
@@ -30,12 +30,14 @@ import static
org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static
org.apache.geode.distributed.ConfigurationProperties.MEMBERSHIP_PORT_RANGE;
import static
org.apache.geode.distributed.ConfigurationProperties.MEMBER_TIMEOUT;
import static org.apache.geode.distributed.ConfigurationProperties.NAME;
+import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS;
import static
org.apache.geode.distributed.ConfigurationProperties.SERVER_SSL_ENABLED;
import static
org.apache.geode.distributed.ConfigurationProperties.SSL_ENABLED_COMPONENTS;
import static
org.apache.geode.distributed.ConfigurationProperties.START_LOCATOR;
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_ARCHIVE_FILE;
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLE_RATE;
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLING_ENABLED;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -678,6 +680,29 @@ public class InternalDistributedSystemJUnitTest {
}
@Test
+ public void testEmptySecurityAuthTokenProp() throws Exception {
+ Properties props = getCommonProperties();
+ props.setProperty(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS, "");
+ DistributionConfig config1 = new DistributionConfigImpl(props, false);
+ assertThat(config1.getSecurityAuthTokenEnabledComponents()).hasSize(0);
+ Properties securityProps = config1.getSecurityProps();
+
assertThat(securityProps.getProperty(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS)).isEqualTo("");
+ assertThat(config1.getSecurityAuthTokenEnabledComponents()).hasSize(0);
+ }
+
+ @Test
+ public void testSecurityAuthTokenProp() throws Exception {
+ Properties props = getCommonProperties();
+ props.setProperty(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS, "management");
+ DistributionConfig config1 = new DistributionConfigImpl(props, false);
+
assertThat(config1.getSecurityAuthTokenEnabledComponents()).containsExactly("MANAGEMENT");
+ Properties securityProps = config1.getSecurityProps();
+
assertThat(securityProps.getProperty(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS))
+ .isEqualTo("management");
+
assertThat(config1.getSecurityAuthTokenEnabledComponents()).containsExactly("MANAGEMENT");
+ }
+
+ @Test
public void testSSLEnabledComponents() {
Properties props = getCommonProperties();
props.setProperty(SSL_ENABLED_COMPONENTS, "cluster,server");
diff --git
a/geode-core/src/main/java/org/apache/geode/distributed/ConfigurationProperties.java
b/geode-core/src/main/java/org/apache/geode/distributed/ConfigurationProperties.java
index e88eb09..dd864e2 100644
---
a/geode-core/src/main/java/org/apache/geode/distributed/ConfigurationProperties.java
+++
b/geode-core/src/main/java/org/apache/geode/distributed/ConfigurationProperties.java
@@ -2003,6 +2003,27 @@ public interface ConfigurationProperties {
* {@link org.apache.geode.security.SecurableCommunicationChannels}
<U>Since</U>: Geode 1.0
*/
String SSL_ENABLED_COMPONENTS = "ssl-enabled-components";
+
+ /**
+ * The static String definition of the
<i>"security-auth-token-enabled-components"</i> property <a
+ * name="security-auth-token-enabled-components"/a>
+ * </p>
+ * <U>Description</U>: This setting is a comma delimited list of component
names which works in
+ * conjunction with
+ * the {@link #SECURITY_MANAGER} properties. if security manager is enabled,
this property will
+ * determine what rest end point will use token based authentication instead
of basic
+ * (username/password)
+ * authentication.
+ * </p>
+ * <U>Componant names</U>: "all","management" <U>Since</U>: Geode 1.11
+ * "all": shorthand for all the security components that support token
authentication.
+ * "management": the {@link #ENABLE_MANAGEMENT_REST_SERVICE Management REST
Service}
+ *
+ * Note: listing components that are not enabled does nothing.
+ *
+ * Default: empty. All security components use basic (username/password)
authentication
+ */
+ String SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS = SECURITY_PREFIX +
"auth-token-enabled-components";
/**
* The static String definition of the <i>"ssl-ciphers"</i> property <a
name="ssl-ciphers"/a>
* </p>
diff --git
a/geode-core/src/main/java/org/apache/geode/distributed/internal/AbstractDistributionConfig.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/AbstractDistributionConfig.java
index 71bf398..e7c043c 100644
---
a/geode-core/src/main/java/org/apache/geode/distributed/internal/AbstractDistributionConfig.java
+++
b/geode-core/src/main/java/org/apache/geode/distributed/internal/AbstractDistributionConfig.java
@@ -118,6 +118,7 @@ import static
org.apache.geode.distributed.ConfigurationProperties.REDUNDANCY_ZO
import static
org.apache.geode.distributed.ConfigurationProperties.REMOTE_LOCATORS;
import static
org.apache.geode.distributed.ConfigurationProperties.REMOVE_UNRESPONSIVE_CLIENT;
import static org.apache.geode.distributed.ConfigurationProperties.ROLES;
+import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_ACCESSOR;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_ACCESSOR_PP;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_AUTHENTICATOR;
@@ -756,7 +757,12 @@ public abstract class AbstractDistributionConfig extends
AbstractConfig
}
if (attName.startsWith(SECURITY_PREFIX)) {
- setSecurity(attName, attValue.toString());
+ // some security properties will be an array, such as
security-auth-token-enabled-components
+ if (attValue instanceof Object[]) {
+ setSecurity(attName, StringUtils.join((Object[]) attValue, ','));
+ } else {
+ setSecurity(attName, attValue.toString());
+ }
}
if (attName.startsWith(SSL_SYSTEM_PROPS_NAME) ||
attName.startsWith(SYS_PROP_NAME)) {
@@ -1218,6 +1224,9 @@ public abstract class AbstractDistributionConfig extends
AbstractConfig
m.put(SECURITY_PREFIX,
"Prefix for security related properties which are packed together and
invoked as authentication parameter. Neither key nor value can be NULL. Legal
tags can be [security-username, security-digitalid] and Legal values can be any
string data.");
+ m.put(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS,
+ "list of rest service to authenticate request with a Bearer token
passed in the 'Authentication' header of the REST request. Otherwise BASIC
authentication scheme is used. Possible value is a comma separated list of:
'all', 'management'. This property is ignored if 'security-manager' is not set.
Default value is empty.");
+
m.put(USERDEFINED_PREFIX_NAME,
"Prefix for user defined properties which are used for replacements in
Cache.xml. Neither key nor value can be NULL. Legal tags can be
[custom-any-string] and Legal values can be any string data.");
diff --git
a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfig.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfig.java
index e30d138..2d21361 100644
---
a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfig.java
+++
b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfig.java
@@ -118,6 +118,7 @@ import static
org.apache.geode.distributed.ConfigurationProperties.REDUNDANCY_ZO
import static
org.apache.geode.distributed.ConfigurationProperties.REMOTE_LOCATORS;
import static
org.apache.geode.distributed.ConfigurationProperties.REMOVE_UNRESPONSIVE_CLIENT;
import static org.apache.geode.distributed.ConfigurationProperties.ROLES;
+import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_ACCESSOR;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_ACCESSOR_PP;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_CLIENT_AUTHENTICATOR;
@@ -2623,6 +2624,27 @@ public interface DistributionConfig extends Config,
LogConfig, StatisticsConfig
String SECURITY_PREFIX_NAME = SECURITY_PREFIX;
+ /**
+ * Sets the value for
+ * {@link ConfigurationProperties#SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS}
+ */
+ @ConfigAttributeSetter(name = SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS)
+ void setSecurityAuthTokenEnabledComponents(String[] newValue);
+
+ /**
+ * Returns the value of
+ * {@link ConfigurationProperties#SSECURITY_AUTH_TOKEN_ENABLED_COMPONENTS}
property
+ */
+ @ConfigAttributeGetter(name = SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS)
+ String[] getSecurityAuthTokenEnabledComponents();
+
+ /**
+ * the name of the {@link
ConfigurationProperties#SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS}
+ * property
+ */
+ @ConfigAttribute(type = String[].class)
+ String SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS_NAME =
+ SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS;
/**
* The static String definition of the cluster ssl prefix
<i>"cluster-ssl"</i> used in conjunction
@@ -4996,6 +5018,9 @@ public interface DistributionConfig extends Config,
LogConfig, StatisticsConfig
SecurableCommunicationChannel[] DEFAULT_SSL_ENABLED_COMPONENTS =
new SecurableCommunicationChannel[] {};
+ @Immutable
+ String[] DEFAULT_SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS = new String[0];
+
boolean DEFAULT_SSL_USE_DEFAULT_CONTEXT = false;
@ConfigAttribute(type = Boolean.class)
diff --git
a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfigImpl.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfigImpl.java
index 2c9ac6e..c1a772b 100644
---
a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfigImpl.java
+++
b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionConfigImpl.java
@@ -91,6 +91,7 @@ import org.apache.geode.internal.ConfigSource;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.internal.process.ProcessLauncherContext;
import org.apache.geode.internal.security.SecurableCommunicationChannel;
+import org.apache.geode.security.AuthTokenEnabledComponents;
/**
* Provides an implementation of <code>DistributionConfig</code> that knows
how to read the
@@ -605,6 +606,8 @@ public class DistributionConfigImpl extends
AbstractDistributionConfig implement
private SecurableCommunicationChannel[] securableCommunicationChannels =
DEFAULT_SSL_ENABLED_COMPONENTS;
+ private String[] securityAuthTokenEnabledComponents =
+ DEFAULT_SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS;
private boolean sslUseDefaultSSLContext = DEFAULT_SSL_USE_DEFAULT_CONTEXT;
private String sslProtocols = DEFAULT_SSL_PROTOCOLS;
@@ -866,8 +869,8 @@ public class DistributionConfigImpl extends
AbstractDistributionConfig implement
validateSerializableObjects = other.getValidateSerializableObjects();
serializableObjectFilter = other.getSerializableObjectFilter();
- // following added for 9.9
enableManagementRestService = other.getEnableManagementRestService();
+ securityAuthTokenEnabledComponents =
other.getSecurityAuthTokenEnabledComponents();
}
/**
@@ -2541,6 +2544,28 @@ public class DistributionConfigImpl extends
AbstractDistributionConfig implement
}
@Override
+ public void setSecurityAuthTokenEnabledComponents(String[] newValue) {
+ // validate the value first
+ for (int i = 0; i < newValue.length; i++) {
+ String value = newValue[i];
+ try {
+ AuthTokenEnabledComponents.valueOf(value.toUpperCase());
+ // normalize the values to all uppercase
+ newValue[i] = value.toUpperCase();
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Invalid security-auth-token-enabled-components value: " + value);
+ }
+ }
+ securityAuthTokenEnabledComponents = newValue;
+ }
+
+ @Override
+ public String[] getSecurityAuthTokenEnabledComponents() {
+ return securityAuthTokenEnabledComponents;
+ }
+
+ @Override
public boolean getRemoveUnresponsiveClient() {
return removeUnresponsiveClient;
}
diff --git
a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
index 5685c85..ff5884d 100644
---
a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
+++
b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java
@@ -29,6 +29,7 @@ import java.net.URI;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -98,6 +99,7 @@ import
org.apache.geode.management.internal.configuration.handlers.SharedConfigu
import
org.apache.geode.management.internal.configuration.messages.ClusterManagementServiceInfoRequest;
import
org.apache.geode.management.internal.configuration.messages.SharedConfigurationStatusRequest;
import
org.apache.geode.management.internal.configuration.messages.SharedConfigurationStatusResponse;
+import org.apache.geode.security.AuthTokenEnabledComponents;
/**
* Provides the implementation of a distribution {@code Locator} as well as
internal-only
@@ -760,6 +762,12 @@ public class InternalLocator extends Locator implements
ConnectListener, LogConf
serviceAttributes.put(InternalHttpService.CLUSTER_MANAGEMENT_SERVICE_CONTEXT_PARAM,
clusterManagementService);
+ String[] authEnabledComponents =
distributionConfig.getSecurityAuthTokenEnabledComponents();
+
+ boolean managementAuthTokenEnabled = Arrays.stream(authEnabledComponents)
+ .anyMatch(AuthTokenEnabledComponents::hasManagement);
+ serviceAttributes.put(InternalHttpService.AUTH_TOKEN_ENABLED_PARAM,
managementAuthTokenEnabled);
+
if (distributionConfig.getEnableManagementRestService()) {
internalCache.getHttpService().ifPresent(x -> {
try {
diff --git
a/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java
b/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java
index 7743ddb..d97a2f3 100644
---
a/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java
+++
b/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java
@@ -14,6 +14,10 @@
*/
package org.apache.geode.examples;
+import static org.apache.geode.security.SecurityManager.PASSWORD;
+import static org.apache.geode.security.SecurityManager.TOKEN;
+import static org.apache.geode.security.SecurityManager.USER_NAME;
+
import java.util.Properties;
import org.apache.geode.security.AuthenticationFailedException;
@@ -33,8 +37,12 @@ public class SimpleSecurityManager implements
SecurityManager {
@Override
public Object authenticate(final Properties credentials) throws
AuthenticationFailedException {
- String username = credentials.getProperty("security-username");
- String password = credentials.getProperty("security-password");
+ String token = credentials.getProperty(TOKEN);
+ if (token != null) {
+ return "Bearer " + token;
+ }
+ String username = credentials.getProperty(USER_NAME);
+ String password = credentials.getProperty(PASSWORD);
if (username != null && username.equals(password)) {
return username;
}
@@ -43,6 +51,9 @@ public class SimpleSecurityManager implements SecurityManager
{
@Override
public boolean authorize(final Object principal, final ResourcePermission
permission) {
+ if (principal.toString().startsWith("Bearer ")) {
+ return true;
+ }
String[] principals = principal.toString().toLowerCase().split(",");
for (String role : principals) {
String permissionString = permission.toString().replace(":",
"").toLowerCase();
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/AbstractConfig.java
b/geode-core/src/main/java/org/apache/geode/internal/AbstractConfig.java
index 8dc718b..75b0b16 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/AbstractConfig.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/AbstractConfig.java
@@ -36,6 +36,8 @@ import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TreeSet;
+import org.apache.commons.lang3.StringUtils;
+
import org.apache.geode.InternalGemFireException;
import org.apache.geode.distributed.internal.FlowControlParams;
import org.apache.geode.internal.net.SocketCreator;
@@ -222,7 +224,12 @@ public abstract class AbstractConfig implements Config {
if (valueType.equals(String.class)) {
attObjectValue = value;
} else if (valueType.equals(String[].class)) {
- attObjectValue = value.split(",");
+ // this would avoid converting empty string value to an array of size
1.
+ if (StringUtils.isBlank(value)) {
+ attObjectValue = new String[0];
+ } else {
+ attObjectValue = value.split(",");
+ }
} else if (valueType.equals(Integer.class)) {
attObjectValue = Integer.valueOf(value);
} else if (valueType.equals(Long.class)) {
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/cache/InternalHttpService.java
b/geode-core/src/main/java/org/apache/geode/internal/cache/InternalHttpService.java
index b001569..f381246 100644
---
a/geode-core/src/main/java/org/apache/geode/internal/cache/InternalHttpService.java
+++
b/geode-core/src/main/java/org/apache/geode/internal/cache/InternalHttpService.java
@@ -43,6 +43,7 @@ import org.apache.geode.management.internal.SSLUtil;
public class InternalHttpService implements HttpService {
+ public static final String AUTH_TOKEN_ENABLED_PARAM =
"org.apache.geode.auth.token.enabled";
private static final Logger logger = LogService.getLogger();
private Server httpServer;
private String bindAddress = "0.0.0.0";
diff --git
a/geode-core/src/main/java/org/apache/geode/management/internal/security/ResourceConstants.java
b/geode-core/src/main/java/org/apache/geode/management/internal/security/ResourceConstants.java
index 1bac885..22f962c 100644
---
a/geode-core/src/main/java/org/apache/geode/management/internal/security/ResourceConstants.java
+++
b/geode-core/src/main/java/org/apache/geode/management/internal/security/ResourceConstants.java
@@ -118,6 +118,7 @@ public class ResourceConstants {
"GemFire:service=AccessControl,type=Distributed";
public static final String USER_NAME = "security-username";
public static final String PASSWORD = "security-password";
+ public static final String TOKEN = "security-token";
public static final String MBEAN_TYPE_DISTRIBUTED = "Distributed";
public static final String MBEAN_TYPE_MEMBER = "Member";
@@ -147,6 +148,4 @@ public class ResourceConstants {
public static final String GETTER_STATUS = "status";
public static final String MANAGEMENT_PACKAGE =
"org.apache.geode.management";
-
-
}
diff --git
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
b/geode-core/src/main/java/org/apache/geode/security/AuthTokenEnabledComponents.java
similarity index 61%
copy from
geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
copy to
geode-core/src/main/java/org/apache/geode/security/AuthTokenEnabledComponents.java
index 192dc0b..76fde33 100644
---
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
+++
b/geode-core/src/main/java/org/apache/geode/security/AuthTokenEnabledComponents.java
@@ -13,20 +13,21 @@
* the License.
*/
-package org.apache.geode.management.internal.rest;
-
-import org.apache.geode.internal.security.SecurityService;
-import org.apache.geode.management.api.ClusterManagementService;
-
-public interface GeodeComponent {
-
- void start();
-
- void stop();
-
- int getPort();
-
- SecurityService getSecurityService();
-
- ClusterManagementService getClusterManagementService();
+package org.apache.geode.security;
+
+public enum AuthTokenEnabledComponents {
+ /**
+ * This determines that all rest components will use token based
authentication. <U>Since</U>:
+ * Geode 1.11
+ */
+ ALL,
+ /**
+ * This determines that management rest service will use token based
authentication. <U>Since</U>:
+ * Geode 1.11
+ */
+ MANAGEMENT;
+
+ public static boolean hasManagement(String value) {
+ return ALL.name().equalsIgnoreCase(value) ||
MANAGEMENT.name().equalsIgnoreCase(value);
+ }
}
diff --git
a/geode-core/src/main/java/org/apache/geode/security/SecurityManager.java
b/geode-core/src/main/java/org/apache/geode/security/SecurityManager.java
index b1065b3..301b3d5 100644
--- a/geode-core/src/main/java/org/apache/geode/security/SecurityManager.java
+++ b/geode-core/src/main/java/org/apache/geode/security/SecurityManager.java
@@ -25,6 +25,18 @@ import org.apache.geode.distributed.DistributedSystem;
* @since Geode 1.0
*/
public interface SecurityManager {
+ /**
+ * property name of the username passed in the Properties in authenticate
method
+ */
+ String USER_NAME = "security-username";
+ /**
+ * property name of the password passed in the Properties in authenticate
method
+ */
+ String PASSWORD = "security-password";
+ /**
+ * property name of the token passed in the Properties in authenticate method
+ */
+ String TOKEN = "security-token";
/**
* Initialize the SecurityManager. This is invoked when a cache is created
@@ -46,8 +58,12 @@ public interface SecurityManager {
* property, so your securityManager implementation needs to validate these
kind of properties
* as well.
*
- * @param credentials it contains the security-username and
security-password as keys of the
- * properties, also the properties generated by your AuthInitialize
interface
+ * if a channel supports token-based-authentication, the token will be
passed to the
+ * security manager in the property with the key "security-token".
+ *
+ * @param credentials it contains the security-username, security-password
or security-token,
+ * as keys of the properties, also the properties generated by your
AuthInitialize
+ * interface
* @return a serializable principal object
*/
Object authenticate(Properties credentials) throws
AuthenticationFailedException;
diff --git
a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
index 70d25d1..b3507ca 100644
---
a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
+++
b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
@@ -585,6 +585,7 @@
org/apache/geode/pdx/PdxSerializationException,true,-3843814927034345635
org/apache/geode/pdx/internal/EnumInfo$PdxInstanceEnumInfo,true,7907582104525106416,ei:org/apache/geode/pdx/internal/EnumInfo,enumId:int
org/apache/geode/pdx/internal/PdxInputStream,false
org/apache/geode/pdx/internal/PdxReaderImpl,true,-6094553093860427759,blobType:org/apache/geode/pdx/internal/PdxType,dis:org/apache/geode/pdx/internal/PdxInputStream
+org/apache/geode/security/AuthTokenEnabledComponents,false
org/apache/geode/security/AuthenticationFailedException,true,-8202866472279088879
org/apache/geode/security/AuthenticationRequiredException,true,4675976651103154919
org/apache/geode/security/GemFireSecurityException,true,3814254578203076926,cause:java/lang/Throwable
diff --git
a/geode-core/src/test/java/org/apache/geode/distributed/internal/DistributionConfigJUnitTest.java
b/geode-core/src/test/java/org/apache/geode/distributed/internal/DistributionConfigJUnitTest.java
index a482892..fd53247 100644
---
a/geode-core/src/test/java/org/apache/geode/distributed/internal/DistributionConfigJUnitTest.java
+++
b/geode-core/src/test/java/org/apache/geode/distributed/internal/DistributionConfigJUnitTest.java
@@ -27,6 +27,7 @@ import static
org.apache.geode.distributed.ConfigurationProperties.LOG_FILE_SIZE
import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static
org.apache.geode.distributed.ConfigurationProperties.REDUNDANCY_ZONE;
+import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_LOG_LEVEL;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
import static
org.apache.geode.distributed.ConfigurationProperties.SECURITY_POST_PROCESSOR;
@@ -38,6 +39,7 @@ import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_ARC
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLE_RATE;
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLING_ENABLED;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -101,7 +103,7 @@ public class DistributionConfigJUnitTest {
@Test
public void testGetAttributeNames() {
String[] attNames = AbstractDistributionConfig._getAttNames();
- assertThat(attNames.length).isEqualTo(166);
+ assertThat(attNames.length).isEqualTo(167);
List boolList = new ArrayList();
List intList = new ArrayList();
@@ -139,7 +141,7 @@ public class DistributionConfigJUnitTest {
assertEquals(35, intList.size());
assertEquals(87, stringList.size());
assertEquals(5, fileList.size());
- assertEquals(4, otherList.size());
+ assertEquals(5, otherList.size());
}
@Test
@@ -450,4 +452,20 @@ public class DistributionConfigJUnitTest {
DistributionConfig config = new DistributionConfigImpl(props);
assertThat(config.getSSLEndPointIdentificationEnabled()).isEqualTo(true);
}
+
+ @Test
+ public void invalidAuthToken() throws Exception {
+ Properties props = new Properties();
+ props.put(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS, "manager");
+ assertThatThrownBy(() -> new DistributionConfigImpl(props))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ public void authTokenIsCaseInsensitive() throws Exception {
+ Properties props = new Properties();
+ props.put(SECURITY_AUTH_TOKEN_ENABLED_COMPONENTS, "MANAGEment");
+ DistributionConfig config = new DistributionConfigImpl(props);
+
assertThat(config.getSecurityAuthTokenEnabledComponents()).containsExactly("MANAGEMENT");
+ }
}
diff --git
a/geode-core/src/test/java/org/apache/geode/internal/AbstractConfigTest.java
b/geode-core/src/test/java/org/apache/geode/internal/AbstractConfigTest.java
index aebb928..12abe3e 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/AbstractConfigTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/AbstractConfigTest.java
@@ -15,7 +15,6 @@
package org.apache.geode.internal;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -52,14 +51,14 @@ public class AbstractConfigTest {
public void setAttributeForStringArrayTypeWithEmpty() {
abstractConfig.setAttribute(stringArrayAttributeName, "", source);
- verify(abstractConfig).setAttributeObject(stringArrayAttributeName, new
String[] {""}, source);
+ verify(abstractConfig).setAttributeObject(stringArrayAttributeName, new
String[0], source);
}
@Test
public void setAttributeForStringArrayTypeWithNull() {
- Throwable thrown =
- catchThrowable(() ->
abstractConfig.setAttribute(stringArrayAttributeName, null, source));
- assertThat(thrown).isInstanceOf(NullPointerException.class);
+ abstractConfig.setAttribute(stringArrayAttributeName, null, source);
+
+ verify(abstractConfig).setAttributeObject(stringArrayAttributeName, new
String[0], source);
}
@Test
diff --git
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java
index f0068fa..f5dae84 100644
---
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java
+++
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java
@@ -41,6 +41,8 @@ public abstract class BaseLocatorContextLoader extends
GenericXmlWebContextLoade
context.getServletContext().setAttribute(
InternalHttpService.CLUSTER_MANAGEMENT_SERVICE_CONTEXT_PARAM,
getClusterManagementService());
+
context.getServletContext().setAttribute(InternalHttpService.AUTH_TOKEN_ENABLED_PARAM,
+ isAuthTokenEnabled());
context.getServletContext().setAttribute("locator", this);
}
}
diff --git
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
index 192dc0b..2375999 100644
---
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
+++
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/GeodeComponent.java
@@ -29,4 +29,8 @@ public interface GeodeComponent {
SecurityService getSecurityService();
ClusterManagementService getClusterManagementService();
+
+ default boolean isAuthTokenEnabled() {
+ return false;
+ }
}
diff --git
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWebContext.java
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWebContext.java
index b3d1520..084d356 100644
---
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWebContext.java
+++
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWebContext.java
@@ -17,8 +17,6 @@ package org.apache.geode.management.internal.rest;
import static
org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
-import java.util.Properties;
-
import org.springframework.test.web.client.MockMvcClientHttpRequestFactory;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
@@ -58,13 +56,6 @@ public class LocatorWebContext {
return (GeodeComponent)
webApplicationContext.getServletContext().getAttribute("locator");
}
- public void login(String username, String password) {
- Properties properties = new Properties();
- properties.setProperty("security-username", username);
- properties.setProperty("security-password", password);
- getLocator().getSecurityService().login(properties);
- }
-
public MockMvcClientHttpRequestFactory getRequestFactory() {
return requestFactory;
}
diff --git
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
index f3e3e2f..af82d9b 100644
---
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
+++
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
@@ -44,5 +44,4 @@ public class SecuredLocatorContextLoader extends
BaseLocatorContextLoader {
public ClusterManagementService getClusterManagementService() {
return locator.getLocator().getClusterManagementService();
}
-
}
diff --git
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorWithAuthTokenEnabledContextLoader.java
similarity index 91%
copy from
geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
copy to
geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorWithAuthTokenEnabledContextLoader.java
index f3e3e2f..8f8c62d 100644
---
a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorContextLoader.java
+++
b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/SecuredLocatorWithAuthTokenEnabledContextLoader.java
@@ -20,7 +20,7 @@ import org.apache.geode.internal.security.SecurityService;
import org.apache.geode.management.api.ClusterManagementService;
import org.apache.geode.test.junit.rules.LocatorStarterRule;
-public class SecuredLocatorContextLoader extends BaseLocatorContextLoader {
+public class SecuredLocatorWithAuthTokenEnabledContextLoader extends
BaseLocatorContextLoader {
private final LocatorStarterRule locator =
new
LocatorStarterRule().withSecurityManager(SimpleSecurityManager.class).withAutoStart();
@@ -45,4 +45,7 @@ public class SecuredLocatorContextLoader extends
BaseLocatorContextLoader {
return locator.getLocator().getClusterManagementService();
}
+ public boolean isAuthTokenEnabled() {
+ return true;
+ }
}
diff --git
a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RequestWithAuthTokenTest.java
b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RequestWithAuthTokenTest.java
new file mode 100644
index 0000000..9f5d17b
--- /dev/null
+++
b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RequestWithAuthTokenTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.apache.geode.management.internal.rest;
+
+import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.context.WebApplicationContext;
+
+
+@RunWith(SpringRunner.class)
+@ContextConfiguration(locations =
{"classpath*:WEB-INF/management-servlet.xml"},
+ loader = SecuredLocatorWithAuthTokenEnabledContextLoader.class)
+@WebAppConfiguration
+public class RequestWithAuthTokenTest {
+
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ // needs to be used together with any LocatorContextLoader
+ private LocatorWebContext context;
+
+ @Before
+ public void before() {
+ context = new LocatorWebContext(webApplicationContext);
+
+ }
+
+ @Test
+ public void ping() throws Exception {
+ String accessToken = "foobar";
+ context.perform(get("/experimental/ping")
+ .header("Authorization", "Bearer " + accessToken))
+ .andExpect(status().isOk())
+ .andExpect(content().string("pong"));
+ }
+
+}
diff --git
a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/GeodeAuthenticationProvider.java
b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/GeodeAuthenticationProvider.java
index e2a6cb5..4445529 100644
---
a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/GeodeAuthenticationProvider.java
+++
b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/GeodeAuthenticationProvider.java
@@ -38,6 +38,7 @@ import org.apache.geode.security.GemFireSecurityException;
public class GeodeAuthenticationProvider implements AuthenticationProvider,
ServletContextAware {
private SecurityService securityService;
+ private boolean authTokenEnabled;
public SecurityService getSecurityService() {
@@ -46,13 +47,20 @@ public class GeodeAuthenticationProvider implements
AuthenticationProvider, Serv
@Override
public Authentication authenticate(Authentication authentication) throws
AuthenticationException {
+ Properties credentials = new Properties();
String username = authentication.getName();
String password = authentication.getCredentials().toString();
- Properties credentials = new Properties();
- if (username != null)
- credentials.put(ResourceConstants.USER_NAME, username);
- if (password != null)
- credentials.put(ResourceConstants.PASSWORD, password);
+
+ if (authTokenEnabled) {
+ if (password != null) {
+ credentials.setProperty(ResourceConstants.TOKEN, password);
+ }
+ } else {
+ if (username != null)
+ credentials.put(ResourceConstants.USER_NAME, username);
+ if (password != null)
+ credentials.put(ResourceConstants.PASSWORD, password);
+ }
try {
securityService.login(credentials);
@@ -68,9 +76,15 @@ public class GeodeAuthenticationProvider implements
AuthenticationProvider, Serv
return
authentication.isAssignableFrom(UsernamePasswordAuthenticationToken.class);
}
+ public boolean isAuthTokenEnabled() {
+ return authTokenEnabled;
+ }
+
@Override
public void setServletContext(ServletContext servletContext) {
securityService = (SecurityService) servletContext
.getAttribute(InternalHttpService.SECURITY_SERVICE_SERVLET_CONTEXT_PARAM);
+ authTokenEnabled =
+ (Boolean)
servletContext.getAttribute(InternalHttpService.AUTH_TOKEN_ENABLED_PARAM);
}
}
diff --git
a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/JwtAuthenticationFilter.java
b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..79faa29
--- /dev/null
+++
b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/JwtAuthenticationFilter.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.geode.management.internal.rest.security;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+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.web.authentication.AbstractAuthenticationProcessingFilter;
+
+/**
+ * Json Web Token authentication filter. This would filter the requests with
"Bearer" token in the
+ * authentication header, and put the token in the form of
UsernamePasswordAuthenticationToken
+ * format for the downstream to consume.
+ */
+public class JwtAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
+
+ public JwtAuthenticationFilter() {
+ super("/**");
+ }
+
+ @Override
+ protected boolean requiresAuthentication(HttpServletRequest request,
+ HttpServletResponse response) {
+ return true;
+ }
+
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest request,
+ HttpServletResponse response) throws AuthenticationException {
+
+ String header = request.getHeader("Authorization");
+
+ if (header == null || !header.startsWith("Bearer ")) {
+ throw new BadCredentialsException("No JWT token found in request
headers, header: " + header);
+ }
+
+ String[] tokens = header.split(" ");
+
+ if (tokens.length != 2) {
+ throw new BadCredentialsException("Wrong authentication header format: "
+ header);
+ }
+
+ return new UsernamePasswordAuthenticationToken(tokens[0], tokens[1]);
+ }
+
+ @Override
+ protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
+ FilterChain chain, Authentication authResult)
+ throws IOException, ServletException {
+ super.successfulAuthentication(request, response, chain, authResult);
+
+ // As this authentication is in HTTP header, after success we need to
continue the request
+ // normally and return the response as if the resource was not secured at
all
+ chain.doFilter(request, response);
+ }
+}
diff --git
a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java
b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java
index 3786adc..d06148a 100644
---
a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java
+++
b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java
@@ -36,6 +36,8 @@ import
org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
+import
org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import
org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.apache.geode.management.api.ClusterManagementResult;
@@ -73,22 +75,40 @@ public class RestSecurityConfiguration extends
WebSecurityConfigurerAdapter {
.anyRequest().authenticated().and().csrf().disable();
if (this.authProvider.getSecurityService().isIntegratedSecurity()) {
- http.httpBasic().authenticationEntryPoint(new AuthenticationEntryPoint()
{
- @Override
- public void commence(HttpServletRequest request, HttpServletResponse
response,
- AuthenticationException authException)
- throws IOException, ServletException {
- response.addHeader("WWW-Authenticate", "Basic realm=\"GEODE\"");
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
- ClusterManagementResult result =
- new
ClusterManagementResult(ClusterManagementResult.StatusCode.UNAUTHENTICATED,
- authException.getMessage());
- objectMapper.writeValue(response.getWriter(), result);
- }
- });
+ if (authProvider.isAuthTokenEnabled()) {
+ JwtAuthenticationFilter tokenEndpointFilter = new
JwtAuthenticationFilter();
+ tokenEndpointFilter.setAuthenticationSuccessHandler((request,
response, authentication) -> {
+ });
+ tokenEndpointFilter.setAuthenticationFailureHandler(new
AuthenticationFailedHandler());
+ http.addFilterBefore(tokenEndpointFilter,
BasicAuthenticationFilter.class);
+ } else {
+ http.httpBasic().authenticationEntryPoint(new
AuthenticationFailedHandler());
+ }
} else {
http.authorizeRequests().anyRequest().permitAll();
}
}
+
+ private class AuthenticationFailedHandler
+ implements AuthenticationFailureHandler, AuthenticationEntryPoint {
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse
response,
+ AuthenticationException authException)
+ throws IOException, ServletException {
+ response.addHeader("WWW-Authenticate", "Basic realm=\"GEODE\"");
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
+ ClusterManagementResult result =
+ new
ClusterManagementResult(ClusterManagementResult.StatusCode.UNAUTHENTICATED,
+ authException.getMessage());
+ objectMapper.writeValue(response.getWriter(), result);
+ }
+
+ @Override
+ public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
+ AuthenticationException exception)
+ throws IOException, ServletException {
+ commence(request, response, exception);
+ }
+ }
}
diff --git
a/geode-web-management/src/test/java/org/apache/geode/management/internal/rest/security/JwtAuthenticationFilterTest.java
b/geode-web-management/src/test/java/org/apache/geode/management/internal/rest/security/JwtAuthenticationFilterTest.java
new file mode 100644
index 0000000..524e36d
--- /dev/null
+++
b/geode-web-management/src/test/java/org/apache/geode/management/internal/rest/security/JwtAuthenticationFilterTest.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 org.apache.geode.management.internal.rest.security;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+
+public class JwtAuthenticationFilterTest {
+
+ private JwtAuthenticationFilter filter;
+ private HttpServletRequest request;
+
+ @Before
+ public void before() throws Exception {
+ filter = new JwtAuthenticationFilter();
+ request = mock(HttpServletRequest.class);
+ }
+
+ @Test
+ public void nullHeader() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn(null);
+ assertThatThrownBy(() -> filter.attemptAuthentication(request, null))
+ .isInstanceOf(BadCredentialsException.class);
+ }
+
+ @Test
+ public void notBearer() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn("foo bar");
+ assertThatThrownBy(() -> filter.attemptAuthentication(request, null))
+ .isInstanceOf(BadCredentialsException.class);
+ }
+
+ @Test
+ public void wrongFormat() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn("foo bar foo");
+ assertThatThrownBy(() -> filter.attemptAuthentication(request, null))
+ .isInstanceOf(BadCredentialsException.class);
+ }
+
+ @Test
+ public void correctHeader() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn("Bearer bar");
+ Authentication authentication = filter.attemptAuthentication(request,
null);
+ assertThat(authentication.getPrincipal().toString()).isEqualTo("Bearer");
+ assertThat(authentication.getCredentials().toString()).isEqualTo("bar");
+ }
+}
diff --git
a/geode-web/src/distributedTest/java/org/apache/geode/management/internal/security/LogNoPasswordDistributedTest.java
b/geode-web/src/distributedTest/java/org/apache/geode/management/internal/security/LogNoPasswordDistributedTest.java
index 6d5e6a5..bad4101 100644
---
a/geode-web/src/distributedTest/java/org/apache/geode/management/internal/security/LogNoPasswordDistributedTest.java
+++
b/geode-web/src/distributedTest/java/org/apache/geode/management/internal/security/LogNoPasswordDistributedTest.java
@@ -81,7 +81,7 @@ public class LogNoPasswordDistributedTest {
public Object authenticate(Properties properties) throws
AuthenticationFailedException {
String user = properties.getProperty("security-username");
String password = properties.getProperty("security-password");
- if (PASSWORD.equals(password)) {
+ if (LogNoPasswordDistributedTest.PASSWORD.equals(password)) {
return user;
}