NIFI-9374 Added Deprecation Logger

- Added nifi-deprecation-log module with interface and implementation using SLF4J
- Updated standard logback.xml with nifi-deprecation.log appender
- Updated NiFiLegacyCipherProvider with deprecation logging
- Set Size, Time Policy, and Total Size Limit for Deprecation Log

This closes #6300
Signed-off-by: Paul Grey <greyp@apache.org>
This commit is contained in:
exceptionfactory 2022-08-15 11:01:54 -05:00 committed by Paul Grey
parent ca991a6805
commit fa85a05a2b
No known key found for this signature in database
GPG Key ID: 8DDF32B9C7EE39D0
12 changed files with 386 additions and 0 deletions

View File

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons</artifactId>
<version>1.18.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-deprecation-log</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,36 @@
/*
* 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.nifi.deprecation.log;
/**
* Deprecation Exception provides stack traces referencing deprecated features or capabilities
*/
class DeprecationException extends RuntimeException {
/**
* Deprecation Exception package-private constructor for internal usage within Standard Deprecation Logger
*
* @param referenceClass Reference Class
*/
DeprecationException(final Class<?> referenceClass) {
super(getMessage(referenceClass));
}
private static String getMessage(final Class<?> referenceClass) {
final ClassLoader classLoader = referenceClass.getClassLoader();
return String.format("Reference Class [%s] ClassLoader [%s]", referenceClass.getName(), classLoader);
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.nifi.deprecation.log;
/**
* Logger interface to indicate use of deprecated components or features
*/
public interface DeprecationLogger {
/**
* Log deprecation warning with optional arguments for message placeholders
*
* @param message Message required
* @param arguments Variable array of arguments to populate message placeholders
*/
void warn(String message, Object... arguments);
}

View File

@ -0,0 +1,35 @@
/*
* 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.nifi.deprecation.log;
import java.util.Objects;
/**
* Logger Factory provides concrete instances of Deprecation Loggers
*/
public class DeprecationLoggerFactory {
/**
* Get Deprecation Logger for Reference Class
*
* @param referenceClass Reference Class for deriving Logger
* @return Deprecation Logger
*/
public static DeprecationLogger getLogger(final Class<?> referenceClass) {
Objects.requireNonNull(referenceClass, "Reference Class required");
return new StandardDeprecationLogger(referenceClass);
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.nifi.deprecation.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Objects;
/**
* Standard implementation of Deprecation Logger based on SLF4J
*/
class StandardDeprecationLogger implements DeprecationLogger {
private static final String LOGGER_NAME_FORMAT = "deprecation.%s";
private final Class<?> referenceClass;
private final Logger logger;
/**
* Standard Deprecation Logger constructor with reference class for deriving logger
*
* @param referenceClass Reference Class
*/
StandardDeprecationLogger(final Class<?> referenceClass) {
this.referenceClass = Objects.requireNonNull(referenceClass, "Reference Class required");
this.logger = LoggerFactory.getLogger(String.format(LOGGER_NAME_FORMAT, referenceClass.getName()));
}
/**
* Log deprecation warning with optional arguments for message placeholders and Deprecation Exception for tracking
*
* @param message Message required
* @param arguments Variable array of arguments to populate message placeholders
*/
@Override
public void warn(final String message, final Object... arguments) {
Objects.requireNonNull(message, "Message required");
final Object[] extendedArguments = getExtendedArguments(arguments);
logger.warn(message, extendedArguments);
}
private Object[] getExtendedArguments(final Object... arguments) {
final Object[] messageArguments = arguments == null ? new Object[0] : arguments;
final int messageArgumentsLength = messageArguments.length;
final Object[] extendedArguments = Arrays.copyOf(messageArguments, messageArgumentsLength + 1);
extendedArguments[messageArgumentsLength] = new DeprecationException(referenceClass);
return extendedArguments;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.nifi.deprecation.log;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(MockitoExtension.class)
class DeprecationExceptionTest {
private static final Class<?> REFERENCE_CLASS = DeprecationExceptionTest.class;
@Test
void testGetMessageReferenceClassFound() {
final DeprecationException deprecationException = new DeprecationException(REFERENCE_CLASS);
final String message = deprecationException.getMessage();
assertTrue(message.contains(REFERENCE_CLASS.getName()));
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.nifi.deprecation.log;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
class DeprecationLoggerFactoryTest {
@Test
void testGetLogger() {
final DeprecationLogger deprecationLogger = DeprecationLoggerFactory.getLogger(DeprecationLoggerFactoryTest.class);
assertNotNull(deprecationLogger);
}
@Test
void testGetLoggerClassRequired() {
assertThrows(NullPointerException.class, () -> DeprecationLoggerFactory.getLogger(null));
}
}

View File

@ -0,0 +1,73 @@
/*
* 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.nifi.deprecation.log;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class StandardDeprecationLoggerTest {
private static final Class<?> REFERENCE_CLASS = StandardDeprecationLoggerTest.class;
private static final String LOGGER_NAME = String.format("deprecation.%s", REFERENCE_CLASS.getName());
private static final String MESSAGE = "Feature not used";
private static final String MESSAGE_PLACEHOLDER = "Feature not used [{}]";
private static final String PLACEHOLDER = "PLACEHOLDER";
@Mock
Logger logger;
@Test
void testWarn() {
try (final MockedStatic<LoggerFactory> ignored = mockStatic(LoggerFactory.class)) {
when(LoggerFactory.getLogger(eq(LOGGER_NAME))).thenReturn(logger);
final DeprecationLogger deprecationLogger = new StandardDeprecationLogger(REFERENCE_CLASS);
deprecationLogger.warn(MESSAGE);
verify(logger).warn(eq(MESSAGE), ArgumentMatchers.<Object[]>any());
}
}
@Test
void testWarnArguments() {
try (final MockedStatic<LoggerFactory> ignored = mockStatic(LoggerFactory.class)) {
when(LoggerFactory.getLogger(eq(LOGGER_NAME))).thenReturn(logger);
final DeprecationLogger deprecationLogger = new StandardDeprecationLogger(REFERENCE_CLASS);
deprecationLogger.warn(MESSAGE_PLACEHOLDER, PLACEHOLDER);
verify(logger).warn(eq(MESSAGE_PLACEHOLDER), ArgumentMatchers.<Object[]>any());
}
}
}

View File

@ -37,6 +37,11 @@
<artifactId>nifi-utils</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-deprecation-log</artifactId>
<version>1.18.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils-api</artifactId>

View File

@ -21,6 +21,9 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import org.apache.nifi.deprecation.log.DeprecationLogger;
import org.apache.nifi.deprecation.log.DeprecationLoggerFactory;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.security.util.EncryptionMethod;
import org.apache.nifi.stream.io.StreamUtils;
@ -40,9 +43,12 @@ import org.slf4j.LoggerFactory;
public class NiFiLegacyCipherProvider extends OpenSSLPKCS5CipherProvider implements PBECipherProvider {
private static final Logger logger = LoggerFactory.getLogger(NiFiLegacyCipherProvider.class);
private static final DeprecationLogger deprecationLogger = DeprecationLoggerFactory.getLogger(NiFiLegacyCipherProvider.class);
// Legacy magic number value
private static final int ITERATION_COUNT = 1000;
/**
* Returns an initialized cipher for the specified algorithm. The key (and IV if necessary) are derived using the NiFi legacy code, based on @see org.apache.nifi.crypto
* .OpenSSLPKCS5CipherProvider#getCipher(java.lang.String, java.lang.String, java.lang.String, byte[], boolean) [essentially {@code MD5(password || salt) * 1000 }].
@ -57,6 +63,7 @@ public class NiFiLegacyCipherProvider extends OpenSSLPKCS5CipherProvider impleme
*/
@Override
public Cipher getCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, int keyLength, boolean encryptMode) throws Exception {
deprecationLogger.warn("Insecure Cipher Provider Algorithm [{}] cipher requested", encryptionMethod.getAlgorithm());
try {
// This method is defined in the OpenSSL implementation and just uses a locally-overridden iteration count
return getInitializedCipher(encryptionMethod, password, salt, encryptMode);
@ -68,6 +75,7 @@ public class NiFiLegacyCipherProvider extends OpenSSLPKCS5CipherProvider impleme
}
public byte[] generateSalt(EncryptionMethod encryptionMethod) {
deprecationLogger.warn("Insecure Cipher Provider Algorithm [{}] generate salt requested", encryptionMethod.getAlgorithm());
byte[] salt = new byte[calculateSaltLength(encryptionMethod)];
new SecureRandom().nextBytes(salt);
return salt;
@ -106,6 +114,7 @@ public class NiFiLegacyCipherProvider extends OpenSSLPKCS5CipherProvider impleme
* @return the salt
*/
public byte[] readSalt(EncryptionMethod encryptionMethod, InputStream in) throws IOException {
deprecationLogger.warn("Insecure Cipher Provider Algorithm [{}] read salt requested", encryptionMethod.getAlgorithm());
if (in == null) {
throw new IllegalArgumentException("Cannot read salt from null InputStream");
}
@ -122,6 +131,7 @@ public class NiFiLegacyCipherProvider extends OpenSSLPKCS5CipherProvider impleme
@Override
public void writeSalt(byte[] salt, OutputStream out) throws IOException {
deprecationLogger.warn("Insecure Cipher Provider write salt requested");
if (out == null) {
throw new IllegalArgumentException("Cannot write salt to null OutputStream");
}

View File

@ -26,6 +26,7 @@
<module>nifi-bootstrap-utils</module>
<module>nifi-build</module>
<module>nifi-data-provenance-utils</module>
<module>nifi-deprecation-log</module>
<module>nifi-expression-language</module>
<module>nifi-external-resource-utils</module>
<module>nifi-flowfile-packager</module>

View File

@ -88,6 +88,19 @@
</encoder>
</appender>
<appender name="DEPRECATION_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${org.apache.nifi.bootstrap.config.log.dir}/nifi-deprecation.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${org.apache.nifi.bootstrap.config.log.dir}/nifi-deprecation_%d.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>10</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date %level [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date %level [%thread] %logger{40} %msg%n</pattern>
@ -96,6 +109,11 @@
<!-- valid logging levels: TRACE, DEBUG, INFO, WARN, ERROR -->
<!-- Deprecation Log -->
<logger name="deprecation" level="WARN" additivity="false">
<appender-ref ref="DEPRECATION_FILE"/>
</logger>
<logger name="org.apache.nifi" level="INFO"/>
<logger name="org.apache.nifi.processors" level="WARN"/>
<logger name="org.apache.nifi.processors.standard.LogAttribute" level="INFO"/>