This is an automated email from the ASF dual-hosted git repository.
mmerli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git
The following commit(s) were added to refs/heads/master by this push:
new d7526c8 [Pulsar-Broker-Common] Refactor AuthenticationProviderToken
(#3239)
d7526c8 is described below
commit d7526c8a24d1b17dd793cf621561d659bbe0285c
Author: Eren Avsarogullari <[email protected]>
AuthorDate: Fri Dec 21 23:10:00 2018 +0000
[Pulsar-Broker-Common] Refactor AuthenticationProviderToken (#3239)
* [Pulsar-Broker-Common] Refactor AuthenticationProviderToken
* Update Unit Test name
---
.../AuthenticationProviderToken.java | 61 ++++----
.../AuthenticationProviderTokenTest.java | 164 ++++++++++++++++++---
2 files changed, 177 insertions(+), 48 deletions(-)
diff --git
a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
index 4ed8868..7e155c3 100644
---
a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
+++
b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
@@ -34,7 +34,7 @@ import
org.apache.pulsar.broker.authentication.utils.AuthTokenUtils;
public class AuthenticationProviderToken implements AuthenticationProvider {
- public final static String HTTP_HEADER_NAME = "Authorization";
+ final static String HTTP_HEADER_NAME = "Authorization";
final static String HTTP_HEADER_VALUE_PREFIX = "Bearer ";
// When simmetric key is configured
@@ -43,6 +43,8 @@ public class AuthenticationProviderToken implements
AuthenticationProvider {
// When public/private key pair is configured
final static String CONF_TOKEN_PUBLIC_KEY = "tokenPublicKey";
+ final static String TOKEN = "token";
+
private Key validationKey;
@Override
@@ -57,33 +59,47 @@ public class AuthenticationProviderToken implements
AuthenticationProvider {
@Override
public String getAuthMethodName() {
- return "token";
+ return TOKEN;
}
@Override
public String authenticate(AuthenticationDataSource authData) throws
AuthenticationException {
- String token = null;
+ // Get Token
+ String token = getToken(authData);
+
+ // Parse Token by validating
+ return parseToken(token);
+ }
+ private String getToken(AuthenticationDataSource authData) throws
AuthenticationException {
if (authData.hasDataFromCommand()) {
// Authenticate Pulsar binary connection
- token = authData.getCommandData();
+ return authData.getCommandData();
} else if (authData.hasDataFromHttp()) {
// Authentication HTTP request. The format here should be
compliant to RFC-6750
- // (https://tools.ietf.org/html/rfc6750#section-2.1). Eg:
- //
- // Authorization: Bearer xxxxxxxxxxxxx
+ // (https://tools.ietf.org/html/rfc6750#section-2.1). Eg:
Authorization: Bearer xxxxxxxxxxxxx
String httpHeaderValue = authData.getHttpHeader(HTTP_HEADER_NAME);
if (httpHeaderValue == null ||
!httpHeaderValue.startsWith(HTTP_HEADER_VALUE_PREFIX)) {
throw new AuthenticationException("Invalid HTTP Authorization
header");
}
// Remove prefix
- token =
httpHeaderValue.substring(HTTP_HEADER_VALUE_PREFIX.length());
+ String token =
httpHeaderValue.substring(HTTP_HEADER_VALUE_PREFIX.length());
+ return validateToken(token);
} else {
throw new AuthenticationException("No token credentials passed");
}
+ }
- // Validate the token
+ private String validateToken(final String token) throws
AuthenticationException {
+ if(StringUtils.isNotBlank(token)) {
+ return token;
+ } else {
+ throw new AuthenticationException("Blank token found");
+ }
+ }
+
+ private String parseToken(final String token) throws
AuthenticationException {
try {
@SuppressWarnings("unchecked")
Jwt<?, Claims> jwt = Jwts.parser()
@@ -99,28 +115,19 @@ public class AuthenticationProviderToken implements
AuthenticationProvider {
/**
* Try to get the validation key for tokens from several possible config
options.
*/
- private static Key getValidationKey(ServiceConfiguration conf) throws
IOException {
- final boolean isPublicKey;
- final String validationKeyConfig;
-
+ private Key getValidationKey(ServiceConfiguration conf) throws IOException
{
if (conf.getProperty(CONF_TOKEN_SECRET_KEY) != null
- && !StringUtils.isBlank((String)
conf.getProperty(CONF_TOKEN_SECRET_KEY))) {
- isPublicKey = false;
- validationKeyConfig = (String)
conf.getProperty(CONF_TOKEN_SECRET_KEY);
+ && StringUtils.isNotBlank((String)
conf.getProperty(CONF_TOKEN_SECRET_KEY))) {
+ final String validationKeyConfig = (String)
conf.getProperty(CONF_TOKEN_SECRET_KEY);
+ final byte[] validationKey =
AuthTokenUtils.readKeyFromUrl(validationKeyConfig);
+ return AuthTokenUtils.decodeSecretKey(validationKey);
} else if (conf.getProperty(CONF_TOKEN_PUBLIC_KEY) != null
- && !StringUtils.isBlank((String)
conf.getProperty(CONF_TOKEN_PUBLIC_KEY))) {
- isPublicKey = true;
- validationKeyConfig = (String)
conf.getProperty(CONF_TOKEN_PUBLIC_KEY);
- } else {
- throw new IOException("No secret key was provided for token
authentication");
- }
-
- byte[] validationKey =
AuthTokenUtils.readKeyFromUrl(validationKeyConfig);
-
- if (isPublicKey) {
+ && StringUtils.isNotBlank((String)
conf.getProperty(CONF_TOKEN_PUBLIC_KEY))) {
+ final String validationKeyConfig = (String)
conf.getProperty(CONF_TOKEN_PUBLIC_KEY);
+ final byte[] validationKey =
AuthTokenUtils.readKeyFromUrl(validationKeyConfig);
return AuthTokenUtils.decodePublicKey(validationKey);
} else {
- return AuthTokenUtils.decodeSecretKey(validationKey);
+ throw new IOException("No secret key was provided for token
authentication");
}
}
}
diff --git
a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
index 078a7b6..36b14c6 100644
---
a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
+++
b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
@@ -19,6 +19,7 @@
package org.apache.pulsar.broker.authentication;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import io.jsonwebtoken.Claims;
@@ -48,6 +49,8 @@ import org.testng.annotations.Test;
public class AuthenticationProviderTokenTest {
+ private static final String SUBJECT = "my-test-subject";
+
@Test
public void testInvalidInitialize() throws Exception {
AuthenticationProviderToken provider = new
AuthenticationProviderToken();
@@ -57,17 +60,18 @@ public class AuthenticationProviderTokenTest {
fail("should have failed");
} catch (IOException e) {
// Expected, secret key was not defined
+ } finally {
+ // currently, will not close any resource
+ provider.close();
}
-
- provider.close();
}
@Test
- public void testSerializeSecretKey() throws Exception {
+ public void testSerializeSecretKey() {
SecretKey secretKey =
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
String token = Jwts.builder()
- .setSubject("my-test-subject")
+ .setSubject(SUBJECT)
.signWith(secretKey)
.compact();
@@ -76,7 +80,9 @@ public class AuthenticationProviderTokenTest {
.setSigningKey(AuthTokenUtils.decodeSecretKey(secretKey.getEncoded()))
.parse(token);
- System.out.println("Subject: " + jwt.getBody().getSubject());
+ assertNotNull(jwt);
+ assertNotNull(jwt.getBody());
+ assertEquals(jwt.getBody().getSubject(), SUBJECT);
}
@Test
@@ -87,7 +93,7 @@ public class AuthenticationProviderTokenTest {
String publicKey = AuthTokenUtils.encodeKeyBase64(keyPair.getPublic());
String token =
AuthTokenUtils.createToken(AuthTokenUtils.decodePrivateKey(Decoders.BASE64.decode(privateKey)),
- "my-test-subject",
+ SUBJECT,
Optional.empty());
@SuppressWarnings("unchecked")
@@ -95,7 +101,9 @@ public class AuthenticationProviderTokenTest {
.setSigningKey(AuthTokenUtils.decodePublicKey(Decoders.BASE64.decode(publicKey)))
.parse(token);
- System.out.println("Subject: " + jwt.getBody().getSubject());
+ assertNotNull(jwt);
+ assertNotNull(jwt.getBody());
+ assertEquals(jwt.getBody().getSubject(), SUBJECT);
}
@Test
@@ -103,7 +111,7 @@ public class AuthenticationProviderTokenTest {
SecretKey secretKey =
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
AuthenticationProviderToken provider = new
AuthenticationProviderToken();
- assertEquals(provider.getAuthMethodName(), "token");
+ assertEquals(provider.getAuthMethodName(),
AuthenticationProviderToken.TOKEN);
Properties properties = new Properties();
properties.setProperty(AuthenticationProviderToken.CONF_TOKEN_SECRET_KEY,
@@ -121,7 +129,7 @@ public class AuthenticationProviderTokenTest {
// expected, no credential passed
}
- String token = AuthTokenUtils.createToken(secretKey,
"my-test-subject", Optional.empty());
+ String token = AuthTokenUtils.createToken(secretKey, SUBJECT,
Optional.empty());
// Pulsar protocol auth
String subject = provider.authenticate(new AuthenticationDataSource() {
@@ -135,7 +143,7 @@ public class AuthenticationProviderTokenTest {
return token;
}
});
- assertEquals(subject, "my-test-subject");
+ assertEquals(subject, SUBJECT);
// HTTP protocol auth
provider.authenticate(new AuthenticationDataSource() {
@@ -146,17 +154,17 @@ public class AuthenticationProviderTokenTest {
@Override
public String getHttpHeader(String name) {
- if (name.equals("Authorization")) {
- return "Bearer " + token;
+ if (name.equals(AuthenticationProviderToken.HTTP_HEADER_NAME))
{
+ return
AuthenticationProviderToken.HTTP_HEADER_VALUE_PREFIX + token;
} else {
throw new IllegalArgumentException("Wrong HTTP header");
}
}
});
- assertEquals(subject, "my-test-subject");
+ assertEquals(subject, SUBJECT);
// Expired token. This should be rejected by the authentication
provider
- String expiredToken = AuthTokenUtils.createToken(secretKey,
"my-test-subject",
+ String expiredToken = AuthTokenUtils.createToken(secretKey, SUBJECT,
Optional.of(new Date(System.currentTimeMillis() -
TimeUnit.HOURS.toMillis(1))));
// Pulsar protocol auth
@@ -184,7 +192,7 @@ public class AuthenticationProviderTokenTest {
public void testAuthSecretKeyFromFile() throws Exception {
SecretKey secretKey =
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
- File secretKeyFile = File.createTempFile("pular-test-secret-key-",
".key");
+ File secretKeyFile = File.createTempFile("pulsar-test-secret-key-",
".key");
secretKeyFile.deleteOnExit();
Files.write(Paths.get(secretKeyFile.toString()),
secretKey.getEncoded());
@@ -197,7 +205,7 @@ public class AuthenticationProviderTokenTest {
conf.setProperties(properties);
provider.initialize(conf);
- String token = AuthTokenUtils.createToken(secretKey,
"my-test-subject", Optional.empty());
+ String token = AuthTokenUtils.createToken(secretKey, SUBJECT,
Optional.empty());
// Pulsar protocol auth
String subject = provider.authenticate(new AuthenticationDataSource() {
@@ -211,7 +219,7 @@ public class AuthenticationProviderTokenTest {
return token;
}
});
- assertEquals(subject, "my-test-subject");
+ assertEquals(subject, SUBJECT);
provider.close();
}
@@ -229,7 +237,7 @@ public class AuthenticationProviderTokenTest {
conf.setProperties(properties);
provider.initialize(conf);
- String token = AuthTokenUtils.createToken(secretKey,
"my-test-subject", Optional.empty());
+ String token = AuthTokenUtils.createToken(secretKey, SUBJECT,
Optional.empty());
// Pulsar protocol auth
String subject = provider.authenticate(new AuthenticationDataSource() {
@@ -243,7 +251,7 @@ public class AuthenticationProviderTokenTest {
return token;
}
});
- assertEquals(subject, "my-test-subject");
+ assertEquals(subject, SUBJECT);
provider.close();
}
@@ -266,7 +274,7 @@ public class AuthenticationProviderTokenTest {
// Use private key to generate token
PrivateKey privateKey =
AuthTokenUtils.decodePrivateKey(Decoders.BASE64.decode(privateKeyStr));
- String token = AuthTokenUtils.createToken(privateKey,
"my-test-subject", Optional.empty());
+ String token = AuthTokenUtils.createToken(privateKey, SUBJECT,
Optional.empty());
// Pulsar protocol auth
String subject = provider.authenticate(new AuthenticationDataSource() {
@@ -280,8 +288,122 @@ public class AuthenticationProviderTokenTest {
return token;
}
});
- assertEquals(subject, "my-test-subject");
+ assertEquals(subject, SUBJECT);
provider.close();
}
+
+ @Test(expectedExceptions = AuthenticationException.class)
+ public void testAuthenticateWhenNoJwtPassed() throws
AuthenticationException {
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.authenticate(new AuthenticationDataSource() {
+ @Override
+ public boolean hasDataFromCommand() {
+ return false;
+ }
+
+ @Override
+ public boolean hasDataFromHttp() {
+ return false;
+ }
+ });
+ }
+
+ @Test(expectedExceptions = AuthenticationException.class)
+ public void testAuthenticateWhenAuthorizationHeaderNotExist() throws
AuthenticationException {
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.authenticate(new AuthenticationDataSource() {
+ @Override
+ public String getHttpHeader(String name) {
+ return null;
+ }
+
+ @Override
+ public boolean hasDataFromHttp() {
+ return true;
+ }
+ });
+ }
+
+ @Test(expectedExceptions = AuthenticationException.class)
+ public void testAuthenticateWhenAuthHeaderValuePrefixIsInvalid() throws
AuthenticationException {
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.authenticate(new AuthenticationDataSource() {
+ @Override
+ public String getHttpHeader(String name) {
+ return "MyBearer ";
+ }
+
+ @Override
+ public boolean hasDataFromHttp() {
+ return true;
+ }
+ });
+ }
+
+ @Test(expectedExceptions = AuthenticationException.class)
+ public void testAuthenticateWhenJwtIsBlank() throws
AuthenticationException {
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.authenticate(new AuthenticationDataSource() {
+ @Override
+ public String getHttpHeader(String name) {
+ return AuthenticationProviderToken.HTTP_HEADER_VALUE_PREFIX +
" ";
+ }
+
+ @Override
+ public boolean hasDataFromHttp() {
+ return true;
+ }
+ });
+ }
+
+ @Test(expectedExceptions = AuthenticationException.class)
+ public void testAuthenticateWhenInvalidTokenIsPassed() throws
AuthenticationException, IOException {
+ SecretKey secretKey =
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
+
+ Properties properties = new Properties();
+
properties.setProperty(AuthenticationProviderToken.CONF_TOKEN_SECRET_KEY,
+ AuthTokenUtils.encodeKeyBase64(secretKey));
+
+ ServiceConfiguration conf = new ServiceConfiguration();
+ conf.setProperties(properties);
+
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.initialize(conf);
+ provider.authenticate(new AuthenticationDataSource() {
+ @Override
+ public String getHttpHeader(String name) {
+ return AuthenticationProviderToken.HTTP_HEADER_VALUE_PREFIX +
"invalid_token";
+ }
+
+ @Override
+ public boolean hasDataFromHttp() {
+ return true;
+ }
+ });
+ }
+
+ @Test(expectedExceptions = IOException.class)
+ public void testValidationKeyWhenBlankSecretKeyIsPassed() throws
IOException {
+ Properties properties = new Properties();
+
properties.setProperty(AuthenticationProviderToken.CONF_TOKEN_SECRET_KEY, "
");
+
+ ServiceConfiguration conf = new ServiceConfiguration();
+ conf.setProperties(properties);
+
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.initialize(conf);
+ }
+
+ @Test(expectedExceptions = IOException.class)
+ public void testValidationKeyWhenBlankPublicKeyIsPassed() throws
IOException {
+ Properties properties = new Properties();
+
properties.setProperty(AuthenticationProviderToken.CONF_TOKEN_PUBLIC_KEY, "
");
+
+ ServiceConfiguration conf = new ServiceConfiguration();
+ conf.setProperties(properties);
+
+ AuthenticationProviderToken provider = new
AuthenticationProviderToken();
+ provider.initialize(conf);
+ }
}