You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/11/02 05:44:34 UTC

[logging-log4j2] 01/02: Fix issues with authentication with Spring Cloud Config

This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit c0be9d20ff6304f2851ae864f5f53aa1110f3e3c
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Fri Nov 1 21:49:12 2019 -0700

    Fix issues with authentication with Spring Cloud Config
---
 .../log4j/core/config/ConfigurationFactory.java    | 39 ++++++++++--------
 .../core/util/BasicAuthorizationProvider.java      | 15 +++++--
 .../client/Log4j2CloudConfigLoggingSystem.java     |  7 ++--
 .../src/site/markdown/index.md                     | 47 ++++++++++++++++++++--
 4 files changed, 80 insertions(+), 28 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
index c0f2b23..d958929 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
@@ -137,7 +137,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
     private static final String HTTPS = "https";
     private static final String HTTP = "http";
 
-    private static AuthorizationProvider authorizationProvider = null;
+    private static volatile AuthorizationProvider authorizationProvider = null;
 
     /**
      * Returns the ConfigurationFactory.
@@ -175,21 +175,7 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
                     //noinspection NonThreadSafeLazyInitialization
                     factories = Collections.unmodifiableList(list);
                     final String authClass = props.getStringProperty(AUTHORIZATION_PROVIDER);
-                    if (authClass != null) {
-                        try {
-                            Object obj = LoaderUtil.newInstanceOf(authClass);
-                            if (obj instanceof AuthorizationProvider) {
-                                authorizationProvider = (AuthorizationProvider) obj;
-                            } else {
-                                LOGGER.warn("{} is not an AuthorizationProvider, using default", obj.getClass().getName());
-                            }
-                        } catch (Exception ex) {
-                            LOGGER.warn("Unable to create {}, using default: {}", authClass, ex.getMessage());
-                        }
-                    }
-                    if (authorizationProvider == null) {
-                        authorizationProvider = new BasicAuthorizationProvider(props);
-                    }
+                    authorizationProvider = authorizationProvider(props);
                 }
             } finally {
                 LOCK.unlock();
@@ -200,6 +186,27 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
         return configFactory;
     }
 
+    public static AuthorizationProvider authorizationProvider(PropertiesUtil props) {
+        final String authClass = props.getStringProperty(AUTHORIZATION_PROVIDER);
+        AuthorizationProvider provider = null;
+        if (authClass != null) {
+            try {
+                Object obj = LoaderUtil.newInstanceOf(authClass);
+                if (obj instanceof AuthorizationProvider) {
+                    provider = (AuthorizationProvider) obj;
+                } else {
+                    LOGGER.warn("{} is not an AuthorizationProvider, using default", obj.getClass().getName());
+                }
+            } catch (Exception ex) {
+                LOGGER.warn("Unable to create {}, using default: {}", authClass, ex.getMessage());
+            }
+        }
+        if (provider == null) {
+            provider = new BasicAuthorizationProvider(props);
+        }
+        return provider;
+    }
+
     public static AuthorizationProvider getAuthorizationProvider() {
         return authorizationProvider;
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BasicAuthorizationProvider.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BasicAuthorizationProvider.java
index 8ad23df..3a8aaa8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BasicAuthorizationProvider.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BasicAuthorizationProvider.java
@@ -17,6 +17,7 @@
 package org.apache.logging.log4j.core.util;
 
 import java.net.URLConnection;
+import java.util.function.Supplier;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -28,7 +29,10 @@ import org.apache.logging.log4j.util.PropertiesUtil;
  * Provides the Basic Authorization header to a request.
  */
 public class BasicAuthorizationProvider implements AuthorizationProvider {
-
+    private static final String[] PREFIXES = {"log4j2.config.", "logging.auth."};
+    private static final String AUTH_USER_NAME = "username";
+    private static final String AUTH_PASSWORD = "password";
+    private static final String AUTH_PASSWORD_DECRYPTOR = "passwordDecryptor";
     public static final String CONFIG_USER_NAME = "log4j2.configurationUserName";
     public static final String CONFIG_PASSWORD = "log4j2.configurationPassword";
     public static final String PASSWORD_DECRYPTOR = "log4j2.passwordDecryptor";
@@ -38,9 +42,12 @@ public class BasicAuthorizationProvider implements AuthorizationProvider {
     private String authString = null;
 
     public BasicAuthorizationProvider(PropertiesUtil props) {
-        String userName = props.getStringProperty(CONFIG_USER_NAME);
-        String password = props.getStringProperty(CONFIG_PASSWORD);
-        String decryptor = props.getStringProperty(PASSWORD_DECRYPTOR);
+        String userName = props.getStringProperty(PREFIXES,AUTH_USER_NAME,
+                () -> props.getStringProperty(CONFIG_USER_NAME));
+        String password = props.getStringProperty(PREFIXES, AUTH_PASSWORD,
+                () -> props.getStringProperty(CONFIG_PASSWORD));
+        String decryptor = props.getStringProperty(PREFIXES, AUTH_PASSWORD_DECRYPTOR,
+                () -> props.getStringProperty(PASSWORD_DECRYPTOR));
         if (decryptor != null) {
             try {
                 Object obj = LoaderUtil.newInstanceOf(decryptor);
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java
index 47dbfb3..4f170e3 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2CloudConfigLoggingSystem.java
@@ -34,6 +34,7 @@ import org.apache.logging.log4j.core.net.ssl.LaxHostnameVerifier;
 import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
 import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
 import org.apache.logging.log4j.core.util.AuthorizationProvider;
+import org.apache.logging.log4j.core.util.BasicAuthorizationProvider;
 import org.apache.logging.log4j.core.util.FileUtils;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.springframework.boot.logging.LogFile;
@@ -121,10 +122,8 @@ public class Log4j2CloudConfigLoggingSystem extends Log4J2LoggingSystem {
 
     private ConfigurationSource getConfigurationSource(URL url) throws IOException, URISyntaxException {
         URLConnection urlConnection = url.openConnection();
-        AuthorizationProvider provider = ConfigurationFactory.getAuthorizationProvider();
-        if (provider != null) {
-            provider.addAuthorization(urlConnection);
-        }
+        AuthorizationProvider provider = ConfigurationFactory.authorizationProvider(PropertiesUtil.getProperties());
+        provider.addAuthorization(urlConnection);
         if (url.getProtocol().equals(HTTPS)) {
             SslConfiguration sslConfiguration = SslConfigurationFactory.getSslConfiguration();
             if (sslConfiguration != null) {
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md
index 62f9d52..f3bbebd 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/site/markdown/index.md
@@ -46,16 +46,55 @@ running on the same hose outside of the docker container. Note that in accordanc
 practices but the application, profile, and label should be specified in the url.
 
 The Spring Cloud Config support also allows connections using TLS and/or basic authentication. When using basic 
-authentication the userid and password may be specified as system properties or in log4j2.component.properties as
+authentication the userid and password may be specified as system properties, log4j2.component.properties or Spring
+Boot's bootstrap.yml. The table below shows the alternate names that may be used to specify the properties. Any of
+the alternatives may be used in any configuration location.
+
+| Property | Alias  | Spring-like alias | Purpose |
+|----------|---------|---------|---------|
+| log4j2.configurationUserName | log4j2.config.username | logging.auth.username | User name for basic authentication |
+| log4j2.configurationPassword | log4j2.config.password | logging.auth.password | Password for basic authentication |
+| log4j2.authorizationProvider | log4j2.config.authorizationProvider | logging.auth.authorizationProvider | Class used to create HTTP Authorization header |
 
 ```
 log4j2.configurationUserName=guest
 log4j2.configurationPassword=guest
 ```
-Note that Log4j currently does not support encrypting the password. 
+As noted above, Log4j supports accessing logging configuration from bootstrap.yml. As an example, to configure reading 
+from a Spring Cloud Configuration service using basic authoriztion you can do:
+```
+spring:
+  application:
+    name: myApp
+  cloud:
+    config:
+      uri: https://spring-configuration-server.mycorp.com
+      username: appuser
+      password: changeme
+
+logging:
+  config: classpath:log4j2.xml
+  label: ${spring.cloud.config.label}
+
+---
+spring:
+  profiles: dev
+
+logging:
+  config: https://spring-configuration-server.mycorp.com/myApp/default/${logging.label}/log4j2-dev.xml
+  auth:
+    username: appuser
+    password: changeme
+```
+
+Note that Log4j currently does not directly support encrypting the password. However, Log4j does use Spring's 
+standard APIs to access properties in the Spring configuration so any customizations made to Spring's property
+handling would apply to the properties Log4j uses as well.
 
-If more extensive authentication is required an ```AuthorizationProvider``` can be implemented and defined in
-the log4j2.authorizationProvider system property or in log4j2.component.properties.
+If more extensive authentication is required an ```AuthorizationProvider``` can be implemented and the fully
+qualified class name in
+the ```log4j2.authorizationProvider``` system property, in log4j2.component.properties or in Spring's bootstrap.yml
+using either the ```log4j2.authorizationProvider``` key or with the key ```logging.auth.authorizationProvider```.
 
 TLS can be enabled by adding the following system properties or defining them in log4j2.component.properties