diff --git a/build.yaml b/build.yaml
index fd354a08..0a7a9d1f 100644
--- a/build.yaml
+++ b/build.yaml
@@ -5,4 +5,4 @@ additionalTestConfigs:
mavenAdditionalArgs: -Dtest=none -DfailIfNoTests=false -DminVersion=4.3.0
jdk17:
testJdkTool: OPEN-JDK17
- mavenAdditionalArgs: -Dtest=none -DfailIfNoTests=false -DminVersion=4.6.0 -DruntimeVersion=4.6.0-rc2 -DruntimeProduct=MULE_EE -Dmule.jvm.version.extension.enforcement=LOOSE
+ mavenAdditionalArgs: -Dtest=none -DfailIfNoTests=false -DminVersion=4.6.0 -Dmule.jvm.version.extension.enforcement=LOOSE
diff --git a/module/pom.xml b/module/pom.xml
index 0c14fc63..05445b54 100644
--- a/module/pom.xml
+++ b/module/pom.xml
@@ -16,17 +16,17 @@
src/test/munit
${basedir}/target/test-mule/munit
- 1.2.0-rc2
- 3.1.0-rc3
- 1.2.0-rc1
+ 1.4.0
+ 3.4.0
+ 1.2.0
3.0.2
- 0.8.10
+ 0.8.12
0.7.0
- 1.8.1
- 1.2.12
- 1.2.3
+ 1.9.3
+ 1.2.13
+ 1.2.5
diff --git a/module/src/main/java/org/mule/extension/spring/api/SpringConfig.java b/module/src/main/java/org/mule/extension/spring/api/SpringConfig.java
index e52c552b..821e79eb 100644
--- a/module/src/main/java/org/mule/extension/spring/api/SpringConfig.java
+++ b/module/src/main/java/org/mule/extension/spring/api/SpringConfig.java
@@ -163,7 +163,7 @@ public Map getObjectsByType(Class type) {
@Override
public void dispose() {
if (applicationContext != null) {
- applicationContext.destroy();
+ applicationContext.close();
applicationContext = null;
}
}
diff --git a/module/src/main/java/org/mule/extension/spring/internal/beanfactory/ArtifactObjectsAwareBeanFactory.java b/module/src/main/java/org/mule/extension/spring/internal/beanfactory/ArtifactObjectsAwareBeanFactory.java
index 4f6accb7..810ed5e7 100644
--- a/module/src/main/java/org/mule/extension/spring/internal/beanfactory/ArtifactObjectsAwareBeanFactory.java
+++ b/module/src/main/java/org/mule/extension/spring/internal/beanfactory/ArtifactObjectsAwareBeanFactory.java
@@ -6,6 +6,16 @@
*/
package org.mule.extension.spring.internal.beanfactory;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import org.apache.commons.logging.LogFactory;
+import org.mule.extension.spring.internal.util.CustomPostAuthenticationChecks;
+import org.mule.extension.spring.internal.util.CustomPreAuthenticationChecks;
+import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.api.ioc.ObjectProvider;
import org.mule.runtime.api.lifecycle.Disposable;
import org.mule.runtime.api.lifecycle.Initialisable;
@@ -19,13 +29,24 @@
import java.util.Set;
import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValue;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.cache.NullUserCache;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
/**
* {@link DefaultListableBeanFactory} implementation that takes into account the objects provided by the mule artifact for
@@ -84,6 +105,11 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
*/
@Override
protected T doGetBean(String name, Class requiredType, Object[] args, boolean typeCheckOnly) throws BeansException {
+ if ("fips140-2".equals(System.getProperty("mule.security.model"))) {
+ if (name.contains(DaoAuthenticationProvider.class.getName()) || DaoAuthenticationProvider.class.equals(requiredType)) {
+ return (T) authenticationProvider(getUserDetailService(name));
+ }
+ }
if (containsBean(name) || !artifactObjectProvider.containsObject(name) || destroying) {
return super.doGetBean(name, requiredType, args, typeCheckOnly);
} else {
@@ -134,4 +160,59 @@ private boolean implementsLifecycle(BeanDefinition beanDefinition) {
public void markForDestroy() {
this.destroying = true;
}
+
+ /**
+ * TODO improve this code error check, ugly hack to bypass non FIPS compliant security algorithms
+ *
+ * The use of sun.misc.Unsafe API is discouraged. For the time being, using sun.misc.Unsafe to instantiate
+ * DaoAuthenticationProvider is a necessary workaround to avoid FIPS compliance issues caused by MD5 and SHA1.
+ * However, once we upgrade to Spring 6.x, we can switch to using the new constructor, passing a compliant PasswordEncoder.
+ * This will allow us to address the compliance issue more cleanly and avoid reliance on unsafe practices.
+ */
+ static DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
+ try {
+ Class> c = Class.forName("sun.misc.Unsafe");
+ Field field = Arrays.stream(c.getDeclaredFields()).filter(f -> f.getName().equals("theUnsafe"))
+ .findFirst().orElseThrow(() -> new RuntimeException("Field not found"));
+ field.setAccessible(true);
+ Method allocateInstance = c.getDeclaredMethod("allocateInstance", Class.class);
+ DaoAuthenticationProvider authProvider =
+ (DaoAuthenticationProvider) allocateInstance.invoke(field.get(null), DaoAuthenticationProvider.class);
+ authProvider.setPasswordEncoder(createDelegatingPasswordEncoder());
+ authProvider.setUserDetailsService(userDetailsService);
+ authProvider.setUserCache(new NullUserCache());
+ authProvider.setAuthoritiesMapper(new NullAuthoritiesMapper());
+
+ // Setting custom pre and post authentication checks
+ authProvider.setPreAuthenticationChecks(new CustomPreAuthenticationChecks());
+ authProvider.setPostAuthenticationChecks(new CustomPostAuthenticationChecks());
+
+ Field loggerField = AbstractUserDetailsAuthenticationProvider.class.getDeclaredField("logger");
+ loggerField.setAccessible(true);
+ loggerField.set(authProvider, LogFactory.getLog(AbstractUserDetailsAuthenticationProvider.class));
+
+ return authProvider;
+ } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException
+ | NoSuchFieldException e) {
+ throw new MuleRuntimeException(e);
+ }
+ }
+
+ static PasswordEncoder createDelegatingPasswordEncoder() {
+ String encodingId = "bcrypt";
+ Map encoders = new HashMap<>();
+ encoders.put(encodingId, new BCryptPasswordEncoder());
+ encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
+ return new DelegatingPasswordEncoder(encodingId, encoders);
+ }
+
+ private UserDetailsService getUserDetailService(String name) {
+ List propertyValues = super.getBeanDefinition(name).getPropertyValues().getPropertyValueList();
+ String userServiceName = propertyValues.stream()
+ .filter(propertyValue -> "userDetailsService".equals(propertyValue.getName()))
+ .map(propertyValue -> ((RuntimeBeanReference) propertyValue.getValue()).getBeanName())
+ .findFirst()
+ .orElse(null);
+ return (UserDetailsService) super.getBean(userServiceName != null ? userServiceName : "userService");
+ }
}
diff --git a/module/src/main/java/org/mule/extension/spring/internal/util/CustomPostAuthenticationChecks.java b/module/src/main/java/org/mule/extension/spring/internal/util/CustomPostAuthenticationChecks.java
new file mode 100644
index 00000000..a6810d22
--- /dev/null
+++ b/module/src/main/java/org/mule/extension/spring/internal/util/CustomPostAuthenticationChecks.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.extension.spring.internal.util;
+
+import org.springframework.security.authentication.CredentialsExpiredException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsChecker;
+
+public class CustomPostAuthenticationChecks implements UserDetailsChecker {
+
+ @Override
+ public void check(UserDetails user) {
+ if (!user.isCredentialsNonExpired()) {
+ throw new CredentialsExpiredException("User credentials have expired");
+ }
+ }
+}
diff --git a/module/src/main/java/org/mule/extension/spring/internal/util/CustomPreAuthenticationChecks.java b/module/src/main/java/org/mule/extension/spring/internal/util/CustomPreAuthenticationChecks.java
new file mode 100644
index 00000000..c65e8575
--- /dev/null
+++ b/module/src/main/java/org/mule/extension/spring/internal/util/CustomPreAuthenticationChecks.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
+ * The software in this package is published under the terms of the CPAL v1.0
+ * license, a copy of which has been included with this distribution in the
+ * LICENSE.txt file.
+ */
+package org.mule.extension.spring.internal.util;
+
+import org.springframework.security.authentication.AccountExpiredException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.LockedException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsChecker;
+
+public class CustomPreAuthenticationChecks implements UserDetailsChecker {
+
+ @Override
+ public void check(UserDetails user) {
+ if (!user.isAccountNonLocked()) {
+ throw new LockedException("User account is locked");
+ }
+ if (!user.isEnabled()) {
+ throw new DisabledException("User is disabled");
+ }
+ if (!user.isAccountNonExpired()) {
+ throw new AccountExpiredException("User account has expired");
+ }
+ }
+}
diff --git a/module/src/test/resources/beans.xml b/module/src/test/resources/beans.xml
index 915702af..57448628 100644
--- a/module/src/test/resources/beans.xml
+++ b/module/src/test/resources/beans.xml
@@ -14,8 +14,8 @@
-
-
+
+
diff --git a/src/test/munit/.gitkeep b/src/test/munit/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/src/test/resources/.gitkeep b/src/test/resources/.gitkeep
new file mode 100644
index 00000000..e69de29b