From 767238ef59ea06ba37808158682059c97cfab19a Mon Sep 17 00:00:00 2001 From: onewe Date: Mon, 25 Apr 2022 18:02:13 +0800 Subject: [PATCH] [ISSUE #8214] Add ldap auth plugin (#8216) * [ISSUE #8214] Add ldap auth plugin - move the config of ldap from NacosAuthConfig to LdapAuthConfig Close #8214 * [ISSUE #8214] Resolve CI error * [ISSUE #8214] The constants of Ldap move to the plugin-impl module --- .../plugin/auth/impl/LdapAuthConfig.java | 83 +++++++++++++++++++ .../auth/impl/LdapAuthPluginService.java | 31 +++++++ .../auth/impl/LdapAuthenticationProvider.java | 35 ++++---- .../plugin/auth/impl/NacosAuthConfig.java | 77 +++++------------ .../auth/impl/constant/AuthConstants.java | 12 +++ ...s.plugin.auth.spi.server.AuthPluginService | 1 + .../plugin/auth/impl/JwtTokenManagerTest.java | 10 ++- .../nacos/plugin/auth/constant/Constants.java | 1 + 8 files changed, 174 insertions(+), 76 deletions(-) create mode 100644 plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java create mode 100644 plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthPluginService.java diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java new file mode 100644 index 00000000000..50faea72724 --- /dev/null +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.java @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.plugin.auth.impl; + +import com.alibaba.nacos.plugin.auth.impl.configuration.ConditionOnLdapAuth; +import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; +import com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl; +import com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.LdapContextSource; + +import java.util.HashMap; +import java.util.Map; + +/** + * ldap auth config. + * @author onewe + */ +@Configuration +@EnableAutoConfiguration(exclude = LdapAutoConfiguration.class) +public class LdapAuthConfig { + + @Value(("${" + AuthConstants.NACOS_CORE_AUTH_LDAP_URL + ":ldap://localhost:389}")) + private String ldapUrl; + + @Value(("${" + AuthConstants.NACOS_CORE_AUTH_LDAP_BASEDC + ":dc=example,dc=org}")) + private String ldapBaseDc; + + @Value(("${" + AuthConstants.NACOS_CORE_AUTH_LDAP_TIMEOUT + ":3000}")) + private String ldapTimeOut; + + @Value(("${" + AuthConstants.NACOS_CORE_AUTH_LDAP_USERDN + ":cn=admin,dc=example,dc=org}")) + private String userDn; + + @Value(("${ " + AuthConstants.NACOS_CORE_AUTH_LDAP_PASSWORD + ":password}")) + private String password; + + @Bean + @Conditional(ConditionOnLdapAuth.class) + public LdapTemplate ldapTemplate() { + LdapContextSource contextSource = new LdapContextSource(); + final Map config = new HashMap<>(16); + contextSource.setUrl(ldapUrl); + contextSource.setBase(ldapBaseDc); + contextSource.setUserDn(userDn); + contextSource.setPassword(password); + config.put("java.naming.ldap.attributes.binary", "objectGUID"); + config.put("com.sun.jndi.ldap.connect.timeout", ldapTimeOut); + contextSource.setPooled(true); + contextSource.setBaseEnvironmentProperties(config); + contextSource.afterPropertiesSet(); + return new LdapTemplate(contextSource); + + } + + @Bean + @Conditional(ConditionOnLdapAuth.class) + public LdapAuthenticationProvider ldapAuthenticationProvider(LdapTemplate ldapTemplate, + NacosUserDetailsServiceImpl userDetailsService, NacosRoleServiceImpl nacosRoleService) { + return new LdapAuthenticationProvider(ldapTemplate, userDetailsService, nacosRoleService); + } + +} diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthPluginService.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthPluginService.java new file mode 100644 index 00000000000..fedbf2d9f7b --- /dev/null +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthPluginService.java @@ -0,0 +1,31 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.plugin.auth.impl; + +import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; + +/** + * ldap auth plugin service. + * @author onewe + */ +public class LdapAuthPluginService extends NacosAuthPluginService { + + @Override + public String getAuthServiceName() { + return AuthConstants.LDAP_AUTH_PLUGIN_TYPE; + } +} diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java index 1061f3f9b0d..a212e598d19 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/LdapAuthenticationProvider.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.plugin.auth.impl; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; import com.alibaba.nacos.plugin.auth.impl.persistence.RoleInfo; import com.alibaba.nacos.plugin.auth.impl.persistence.User; @@ -24,10 +25,6 @@ import com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetails; import com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl; import com.alibaba.nacos.plugin.auth.impl.utils.PasswordEncoderUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.ldap.core.LdapTemplate; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -35,7 +32,6 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Component; import java.util.List; @@ -44,24 +40,24 @@ * * @author zjw */ -@Component public class LdapAuthenticationProvider implements AuthenticationProvider { - private static final Logger LOG = LoggerFactory.getLogger(LdapAuthenticationProvider.class); - private static final String DEFAULT_PASSWORD = "nacos"; private static final String LDAP_PREFIX = "LDAP_"; - @Autowired - private NacosUserDetailsServiceImpl userDetailsService; - - @Autowired - private NacosRoleServiceImpl nacosRoleService; + private final NacosUserDetailsServiceImpl userDetailsService; + + private final NacosRoleServiceImpl nacosRoleService; + + private final LdapTemplate ldapTemplate; - @Lazy - @Autowired - private LdapTemplate ldapTemplate; + public LdapAuthenticationProvider(LdapTemplate ldapTemplate, NacosUserDetailsServiceImpl userDetailsService, + NacosRoleServiceImpl nacosRoleService) { + this.ldapTemplate = ldapTemplate; + this.nacosRoleService = nacosRoleService; + this.userDetailsService = userDetailsService; + } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { @@ -77,7 +73,12 @@ public Authentication authenticate(Authentication authentication) throws Authent } } - if (!ldapLogin(username, password)) { + try { + if (!ldapLogin(username, password)) { + return null; + } + } catch (Exception e) { + Loggers.AUTH.error("[LDAP-LOGIN] failed", e); return null; } diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosAuthConfig.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosAuthConfig.java index d3df58673a6..d314e7a943a 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosAuthConfig.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/NacosAuthConfig.java @@ -19,22 +19,15 @@ import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.code.ControllerMethodsCache; -import com.alibaba.nacos.plugin.auth.impl.configuration.ConditionOnLdapAuth; import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants; import com.alibaba.nacos.plugin.auth.impl.constant.AuthSystemTypes; import com.alibaba.nacos.plugin.auth.impl.filter.JwtAuthenticationTokenFilter; import com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.io.DecodingException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.core.env.Environment; -import org.springframework.ldap.core.LdapTemplate; -import org.springframework.ldap.core.support.LdapContextSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -50,8 +43,6 @@ import javax.annotation.PostConstruct; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; import java.util.Properties; /** @@ -60,7 +51,6 @@ * @author Nacos */ @EnableGlobalMethodSecurity(prePostEnabled = true) -@EnableAutoConfiguration(exclude = LdapAutoConfiguration.class) public class NacosAuthConfig extends WebSecurityConfigurerAdapter { private static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ","; @@ -73,38 +63,17 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter { private static final String PROPERTY_IGNORE_URLS = "nacos.security.ignore.urls"; - @Value(("${nacos.core.auth.ldap.url:ldap://localhost:389}")) - private String ldapUrl; + private final Environment env; - @Value(("${nacos.core.auth.ldap.basedc:dc=example,dc=org}")) - private String ldapBaseDc; + private final JwtTokenManager tokenProvider; - @Value(("${nacos.core.auth.ldap.timeout:3000}")) - private String ldapTimeOut; + private final AuthConfigs authConfigs; - @Value(("${nacos.core.auth.ldap.userDn:cn=admin,dc=example,dc=org}")) - private String userDn; + private final NacosUserDetailsServiceImpl userDetailsService; - @Value(("${nacos.core.auth.ldap.password:password}")) - private String password; + private final LdapAuthenticationProvider ldapAuthenticationProvider; - @Autowired - private Environment env; - - @Autowired - private JwtTokenManager tokenProvider; - - @Autowired - private AuthConfigs authConfigs; - - @Autowired - private NacosUserDetailsServiceImpl userDetailsService; - - @Autowired - private LdapAuthenticationProvider ldapAuthenticationProvider; - - @Autowired - private ControllerMethodsCache methodsCache; + private final ControllerMethodsCache methodsCache; /** * secret key. @@ -121,6 +90,20 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter { */ private long tokenValidityInSeconds; + public NacosAuthConfig(Environment env, JwtTokenManager tokenProvider, AuthConfigs authConfigs, + NacosUserDetailsServiceImpl userDetailsService, + ObjectProvider ldapAuthenticationProvider, + ControllerMethodsCache methodsCache) { + + this.env = env; + this.tokenProvider = tokenProvider; + this.authConfigs = authConfigs; + this.userDetailsService = userDetailsService; + this.ldapAuthenticationProvider = ldapAuthenticationProvider.getIfAvailable(); + this.methodsCache = methodsCache; + + } + /** * Init. */ @@ -195,24 +178,6 @@ public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } - @Bean - @Conditional(ConditionOnLdapAuth.class) - public LdapTemplate ldapTemplate() { - LdapContextSource contextSource = new LdapContextSource(); - final Map config = new HashMap(16); - contextSource.setUrl(ldapUrl); - contextSource.setBase(ldapBaseDc); - contextSource.setUserDn(userDn); - contextSource.setPassword(password); - config.put("java.naming.ldap.attributes.binary", "objectGUID"); - config.put("com.sun.jndi.ldap.connect.timeout", ldapTimeOut); - contextSource.setPooled(true); - contextSource.setBaseEnvironmentProperties(config); - contextSource.afterPropertiesSet(); - return new LdapTemplate(contextSource); - - } - public byte[] getSecretKeyBytes() { if (secretKeyBytes == null) { try { diff --git a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java index 4aa02d78c16..cb2239a6275 100644 --- a/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java +++ b/plugin-default-impl/src/main/java/com/alibaba/nacos/plugin/auth/impl/constant/AuthConstants.java @@ -25,6 +25,8 @@ public class AuthConstants { public static final String AUTH_PLUGIN_TYPE = "nacos"; + public static final String LDAP_AUTH_PLUGIN_TYPE = "ldap"; + public static final String GLOBAL_ADMIN_ROLE = "ROLE_ADMIN"; public static final String AUTHORIZATION_HEADER = "Authorization"; @@ -48,4 +50,14 @@ public class AuthConstants { public static final String TOKEN_EXPIRE_SECONDS = "token.expire.seconds"; public static final String DEFAULT_TOKEN_EXPIRE_SECONDS = "18000"; + + public static final String NACOS_CORE_AUTH_LDAP_URL = "nacos.core.auth.ldap.url"; + + public static final String NACOS_CORE_AUTH_LDAP_BASEDC = "nacos.core.auth.ldap.basedc"; + + public static final String NACOS_CORE_AUTH_LDAP_TIMEOUT = "nacos.core.auth.ldap.timeout"; + + public static final String NACOS_CORE_AUTH_LDAP_USERDN = "nacos.core.auth.ldap.userDn"; + + public static final String NACOS_CORE_AUTH_LDAP_PASSWORD = "nacos.core.auth.ldap.password"; } diff --git a/plugin-default-impl/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService b/plugin-default-impl/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService index 6fb370ad04a..682a264ee54 100644 --- a/plugin-default-impl/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService +++ b/plugin-default-impl/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService @@ -15,3 +15,4 @@ # com.alibaba.nacos.plugin.auth.impl.NacosAuthPluginService +com.alibaba.nacos.plugin.auth.impl.LdapAuthPluginService \ No newline at end of file diff --git a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/JwtTokenManagerTest.java b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/JwtTokenManagerTest.java index 437d8f4980e..872e81d1a7d 100644 --- a/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/JwtTokenManagerTest.java +++ b/plugin-default-impl/src/test/java/com/alibaba/nacos/plugin/auth/impl/JwtTokenManagerTest.java @@ -24,6 +24,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.beans.factory.ObjectProvider; import java.lang.reflect.Field; import java.util.Properties; @@ -39,6 +40,9 @@ public class JwtTokenManagerTest { @Mock private ControllerMethodsCache methodsCache; + @Mock + private ObjectProvider ldapAuthenticationProvider; + private NacosAuthConfig nacosAuthConfig; @Test @@ -57,9 +61,9 @@ private void createToken(String secretKey) throws NoSuchFieldException, IllegalA properties.setProperty(AuthConstants.TOKEN_SECRET_KEY, secretKey); properties.setProperty(AuthConstants.TOKEN_EXPIRE_SECONDS, "300"); when(authConfigs.getAuthPluginProperties(AuthConstants.AUTH_PLUGIN_TYPE)).thenReturn(properties); - nacosAuthConfig = new NacosAuthConfig(); - injectProperty(nacosAuthConfig, "methodsCache", methodsCache); - injectProperty(nacosAuthConfig, "authConfigs", authConfigs); + + nacosAuthConfig = new NacosAuthConfig(null, null, authConfigs, null, + ldapAuthenticationProvider, methodsCache); nacosAuthConfig.init(); JwtTokenManager jwtTokenManager = new JwtTokenManager(); injectProperty(jwtTokenManager, "nacosAuthConfig", nacosAuthConfig); diff --git a/plugin/auth/src/main/java/com/alibaba/nacos/plugin/auth/constant/Constants.java b/plugin/auth/src/main/java/com/alibaba/nacos/plugin/auth/constant/Constants.java index 76b5b87a369..05f24e9e25b 100644 --- a/plugin/auth/src/main/java/com/alibaba/nacos/plugin/auth/constant/Constants.java +++ b/plugin/auth/src/main/java/com/alibaba/nacos/plugin/auth/constant/Constants.java @@ -36,6 +36,7 @@ public static class Auth { public static final String NACOS_CORE_AUTH_SERVER_IDENTITY_VALUE = "nacos.core.auth.server.identity.value"; public static final String NACOS_CORE_AUTH_ENABLE_USER_AGENT_AUTH_WHITE = "nacos.core.auth.enable.userAgentAuthWhite"; + } public static class Resource {