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);
+    }
 }

Reply via email to