Repository: wso2/micro-integrator
Issue: Prometheus API - basic authentication does not work with an external user store

**Steps to reproduce:**

1. Start the Prometheus server with the below configs in the prometheus.yml file. 
```
scrape_configs:
     - job_name: "esb_stats"
       static_configs:
         - targets: ['localhost:9201']
       metrics_path: "metric-service/metrics"
```

2. Have the following configurations in the deployment.yml with the external user store defined. 
```
[internal_apis.file_user_store]
enable = false

[user_store]
class = "org.wso2.micro.integrator.security.user.core.jdbc.JDBCUserStoreManager"
type = "database"
read_only = false

[[datasource]]
id = "WSO2_USER_DB"
url= "jdbc:mysql://localhost:3306/userdb"
username="root"
password="root"
driver="com.mysql.jdbc.Driver"
pool_options.maxActive=50
pool_options.maxWait = 60000
pool_options.testOnBorrow = true

[realm_manager]
data_source = "WSO2_USER_DB"

[prometheus_api]
protocols = "http"

[prometheus_api.basic_security_handler]
enable = true

[prometheus_api.authorization_handler]
enable = true

[[prometheus_api.authorization_handler.resources]]
path = "/metric"

[[management_api.authorization_handler.resources]]
path = "/user"

[management_api.cors]
enabled = true
allowed_origins = "https://localhost:9743,https://wso2.com:9743"
allowed_headers = "Authorization"
```

3. Start the micro integrator. 
4. Call the Prometheus API as below. 
```
curl http://localhost:9201/metric-service/metrics
```

Getting the following error in the micro integrator logs. 
```
[2020-06-30 11:26:49,171] ERROR {InboundHttpServerWorker} - Exception occurred when running org.wso2.carbon.inbound.endpoint.protocol.http.InboundHttpServerWorker java.lang.NullPointerException
	at org.wso2.micro.integrator.management.apis.security.handler.SecurityHandlerAdapter.needsHandling(SecurityHandlerAdapter.java:85)
	at org.wso2.micro.integrator.management.apis.security.handler.SecurityHandlerAdapter.invoke(SecurityHandlerAdapter.java:119)
	at org.wso2.carbon.inbound.endpoint.internal.http.api.InternalAPIDispatcher.dispatch(InternalAPIDispatcher.java:75)
	at org.wso2.carbon.inbound.endpoint.protocol.http.InboundHttpServerWorker.run(InboundHttpServerWorker.java:102)
	at org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
```

Changed files:
components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/BasicSecurityHandler.java
components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/JWTTokenSecurityHandler.java
components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java

diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java
index a99fe7c68..63dfb2bdd 100644
--- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java
+++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java
@@ -19,21 +19,30 @@
 package org.wso2.micro.integrator.management.apis.security.handler;
 
 import org.apache.axis2.transport.http.HTTPConstants;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.synapse.MessageContext;
 import org.apache.synapse.core.axis2.Axis2MessageContext;
 import org.wso2.micro.core.util.CarbonException;
 import org.wso2.micro.integrator.management.apis.ManagementApiUndefinedException;
+import org.wso2.micro.integrator.security.MicroIntegratorSecurityUtils;
+import org.wso2.micro.integrator.security.user.api.UserStoreException;
 
 import javax.xml.stream.XMLStreamException;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Objects;
 
+import static org.wso2.micro.integrator.management.apis.Constants.USERNAME_PROPERTY;
+
 /**
  * This class provides an abstraction for all security handlers using authentication for management api.
  */
 public abstract class AuthenticationHandlerAdapter extends SecurityHandlerAdapter {
 
+    private static final Log LOG = LogFactory.getLog(AuthenticationHandlerAdapter.class);
+
     public AuthenticationHandlerAdapter(String context) throws CarbonException, XMLStreamException, IOException,
             ManagementApiUndefinedException {
         super(context);
@@ -88,4 +97,72 @@ public abstract class AuthenticationHandlerAdapter extends SecurityHandlerAdapte
      */
     protected abstract Boolean authenticate(String authHeaderToken);
 
+    /**
+     * Processes authentication request with basic auth and carbon user store.
+     *
+     * @param token extracted basic auth token
+     * @return if successfully authenticated
+     */
+    boolean processAuthRequestWithCarbonUserStore(String token) throws UserStoreException {
+
+        String[] userDetails = extractDetails(token);
+        if (userDetails.length == 0) {
+            return false;
+        }
+        boolean isAuthenticated = MicroIntegratorSecurityUtils.getUserStoreManager().authenticate(userDetails[0],
+                                                                                                  userDetails[1]);
+        if (isAuthenticated) {
+            messageContext.setProperty(USERNAME_PROPERTY, userDetails[0]);
+            LOG.info("User " + userDetails[0] + " logged in successfully");
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Processes authentication request with basic auth and in memory user store
+     *
+     * @param token extracted basic auth token
+     * @return boolean if successfully authenticated
+     */
+    boolean processAuthRequestWithFileBasedUserStore(String token) {
+
+        String[] userDetails = extractDetails(token);
+        if (userDetails.length == 0) {
+            return false;
+        }
+        if (!usersList.isEmpty()) {
+            for (String userNameFromStore : usersList.keySet()) {
+                if (userNameFromStore.equals(userDetails[0])) {
+                    String passwordFromStore = String.valueOf(usersList.get(userNameFromStore));
+                    if (isValid(passwordFromStore) && passwordFromStore.equals(userDetails[1])) {
+                        messageContext.setProperty(USERNAME_PROPERTY, userDetails[0]);
+                        LOG.info("User " + userDetails[0] + " logged in successfully");
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private String[] extractDetails(String token) {
+
+        String decodedCredentials = new String(new Base64().decode(token.getBytes()));
+        String[] usernamePasswordArray = decodedCredentials.split(":");
+        if (usernamePasswordArray.length != 2) {
+            return new String[] {};
+        }
+        return new String[] { usernamePasswordArray[0], usernamePasswordArray[1] };
+    }
+
+    /**
+     * Checks if a given value is not null and not empty.
+     *
+     * @param value String value
+     */
+    private Boolean isValid(String value) {
+
+        return (Objects.nonNull(value) && !value.isEmpty());
+    }
 }
diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/BasicSecurityHandler.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/BasicSecurityHandler.java
index bbe830c79..b7da12d38 100644
--- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/BasicSecurityHandler.java
+++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/BasicSecurityHandler.java
@@ -18,17 +18,15 @@
 
 package org.wso2.micro.integrator.management.apis.security.handler;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.synapse.MessageContext;
 import org.wso2.micro.core.util.CarbonException;
 import org.wso2.micro.integrator.management.apis.ManagementApiUndefinedException;
+import org.wso2.micro.integrator.security.user.api.UserStoreException;
 
-import javax.xml.stream.XMLStreamException;
 import java.io.IOException;
-import java.util.Objects;
-
-import static org.wso2.micro.integrator.management.apis.Constants.USERNAME_PROPERTY;
+import javax.xml.stream.XMLStreamException;
 
 /**
  * This class extends the AuthenticationHandlerAdapter to create a basic security handler with a user store defined in
@@ -55,38 +53,26 @@ public class BasicSecurityHandler extends AuthenticationHandlerAdapter {
         this.name = name;
     }
 
+    @Override
+    public Boolean invoke(MessageContext messageContext) {
+        this.messageContext = messageContext;
+        return super.invoke(messageContext);
+    }
+
     @Override
     protected Boolean authenticate(String authHeaderToken) {
-        LOG.debug("Handling authentication");
-        String decodedCredentials = new String(new Base64().decode(authHeaderToken.getBytes()));
-        String[] usernamePasswordArray = decodedCredentials.split(":");
-        // Avoid possible array index out of bound errors
-        if (usernamePasswordArray.length != 2) {
-            return false;
-        }
-        String userNameFromHeader = usernamePasswordArray[0];
-        String passwordFromHeader = usernamePasswordArray[1];
-        if (!usersList.isEmpty()) {
-            for (String userNameFromStore : usersList.keySet()) {
-                if (userNameFromStore.equals(userNameFromHeader)) {
-                    String passwordFromStore = String.valueOf(usersList.get(userNameFromStore));
-                    if (isValid(passwordFromStore) && passwordFromStore.equals(passwordFromHeader)) {
-                        messageContext.setProperty(USERNAME_PROPERTY, userNameFromHeader);
-                        return true;
-                    }
-                }
+
+        LOG.debug("Handling authentication with BasicSecurityHandler");
+        if (useCarbonUserStore) {
+            try {
+                return processAuthRequestWithCarbonUserStore(authHeaderToken);
+            } catch (UserStoreException e) {
+                LOG.error("Error while authenticating with carbon user store", e);
+                return false;
             }
+        } else {
+            return processAuthRequestWithFileBasedUserStore(authHeaderToken);
         }
-
-        return false;
     }
 
-    /**
-     * Checks if a given value is not null and not empty.
-     *
-     * @param value String value
-     */
-    private Boolean isValid(String value) {
-        return (Objects.nonNull(value) && !value.isEmpty());
-    }
 }
diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/JWTTokenSecurityHandler.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/JWTTokenSecurityHandler.java
index 26fdfffd5..448fb2898 100644
--- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/JWTTokenSecurityHandler.java
+++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/JWTTokenSecurityHandler.java
@@ -17,19 +17,16 @@
  */
 package org.wso2.micro.integrator.management.apis.security.handler;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.synapse.MessageContext;
 import org.wso2.micro.core.util.CarbonException;
 import org.wso2.micro.integrator.management.apis.Constants;
 import org.wso2.micro.integrator.management.apis.ManagementApiUndefinedException;
-import org.wso2.micro.integrator.security.MicroIntegratorSecurityUtils;
 import org.wso2.micro.integrator.security.user.api.UserStoreException;
 
 import javax.xml.stream.XMLStreamException;
 import java.io.IOException;
-import java.util.Objects;
 
 import static org.wso2.micro.integrator.management.apis.Constants.USERNAME_PROPERTY;
 
@@ -70,13 +67,13 @@ public class JWTTokenSecurityHandler extends AuthenticationHandlerAdapter {
             if (useCarbonUserStore) {
                 //Uses carbon user store
                 try {
-                    return processLoginRequestWithCarbonUserStore(authHeaderToken);
+                    return processAuthRequestWithCarbonUserStore(authHeaderToken);
                 } catch (UserStoreException e) {
                     LOG.error("Error while authenticating with carbon user store", e);
                 }
             } else {
                 //Uses in memory user store
-                return processLoginRequestInMemoryUserStore(authHeaderToken);
+                return processAuthRequestWithFileBasedUserStore(authHeaderToken);
             }
         } else {
             //Other resources apart from /login should be authenticated from JWT based auth
@@ -91,70 +88,4 @@ public class JWTTokenSecurityHandler extends AuthenticationHandlerAdapter {
         return false;
     }
 
-    /**
-     * Checks if a given value is not null and not empty.
-     *
-     * @param value String value
-     */
-    private Boolean isValid(String value) {
-
-        return (Objects.nonNull(value) && !value.isEmpty());
-    }
-
-    /**
-     * Processes /login request if the JWTToken Security Handler is engaged. Since /login is
-     * basic auth
-     *
-     * @param token extracted basic auth token
-     * @return boolean if successfully authenticated
-     */
-    private boolean processLoginRequestInMemoryUserStore(String token) {
-
-        String decodedCredentials = new String(new Base64().decode(token.getBytes()));
-        String[] usernamePasswordArray = decodedCredentials.split(":");
-        if (usernamePasswordArray.length != 2) {
-            return false;
-        }
-        String username = usernamePasswordArray[0];
-        String password = usernamePasswordArray[1];
-        if (!usersList.isEmpty()) {
-            for (String userNameFromStore : usersList.keySet()) {
-                if (userNameFromStore.equals(username)) {
-                    String passwordFromStore = String.valueOf(usersList.get(userNameFromStore));
-                    if (isValid(passwordFromStore) && passwordFromStore.equals(password)) {
-                        messageContext.setProperty(USERNAME_PROPERTY, username);
-                        LOG.info("User " + username + " logged in successfully");
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Processes /login request if the JWTToken Security Handler is engaged. Since /login is
-     * basic auth
-     *
-     * @param token extracted basic auth token
-     * @return if successfully authenticated
-     */
-    private boolean processLoginRequestWithCarbonUserStore(String token) throws UserStoreException {
-
-        String decodedCredentials = new String(new Base64().decode(token.getBytes()));
-        String[] usernamePasswordArray = decodedCredentials.split(":");
-        if (usernamePasswordArray.length != 2) {
-            return false;
-        }
-        String username = usernamePasswordArray[0];
-        String password = usernamePasswordArray[1];
-        boolean isAuthenticated = MicroIntegratorSecurityUtils.getUserStoreManager().authenticate(username,
-                password);
-        if (isAuthenticated) {
-            messageContext.setProperty(USERNAME_PROPERTY, username);
-            LOG.info("User " + username + " logged in successfully");
-            return true;
-        }
-        return false;
-    }
 }