This is an automated email from the ASF dual-hosted git repository. arshad pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/seatunnel-web.git
The following commit(s) were added to refs/heads/main by this push: new 90d05b03 [LDAP] Add LDAP Authentication Provider Support. (#269) 90d05b03 is described below commit 90d05b035764ca439f2887d8227cceb1335e5892 Author: Shashwat Tiwari <shati...@visa.com> AuthorDate: Fri Jan 17 18:47:32 2025 +0530 [LDAP] Add LDAP Authentication Provider Support. (#269) Co-authored-by: BilwaST <stbi...@gmail.com> --- README.md | 26 ++++++++ README_CN.md | 25 +++++++- pom.xml | 11 ++++ seatunnel-server/seatunnel-app/pom.xml | 9 +++ .../org/apache/seatunnel/app/common/Constants.java | 3 + .../app/config/LdapAuthenticationBuilder.java | 74 ++++++++++++++++++++++ .../SeatunnelAuthenticationProvidersConfig.java} | 25 +++----- .../SeatunnelLdapAuthenticationProvider.java | 64 +++++++++++++++++++ .../seatunnel/app/controller/UserController.java | 7 +- .../org/apache/seatunnel/app/dal/dao/IUserDao.java | 2 +- .../seatunnel/app/dal/dao/impl/UserDaoImpl.java | 5 +- .../org/apache/seatunnel/app/dal/entity/User.java | 2 + .../seatunnel/app/dal/mapper/UserMapper.java | 4 +- .../app/domain/dto/user/UpdateUserDto.java | 1 + .../strategy/IAuthenticationStrategy.java} | 17 ++--- .../strategy/impl/DBAuthenticationStrategy.java | 54 ++++++++++++++++ .../strategy/impl/LDAPAuthenticationStrategy.java | 68 ++++++++++++++++++++ .../apache/seatunnel/app/service/IUserService.java | 2 +- .../app/service/impl/UserServiceImpl.java | 53 ++++++++++++---- .../src/main/resources/application.yml | 12 +++- .../apache/seatunnel/app/dal/mapper/UserMapper.xml | 10 +-- .../main/resources/script/seatunnel_server_h2.sql | 1 + .../resources/script/seatunnel_server_mysql.sql | 1 + .../server/common/SeatunnelErrorEnum.java | 4 ++ .../app/common/SeatunnelWebTestingBase.java | 26 +++++++- .../seatunnel/app/test/UserControllerTest.java | 60 ++++++++++++++++-- .../src/test/resources/application.yml | 10 +++ tools/dependencies/known-dependencies.txt | 4 ++ 28 files changed, 519 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 28e06a61..30a1aada 100644 --- a/README.md +++ b/README.md @@ -238,3 +238,29 @@ NOTE: This feature is currently useful when execution is done through the API. T Execute the following SQL to upgrade the database: ```ALTER TABLE `t_st_job_instance` ADD COLUMN `error_message` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL;``` + +#### 2. Upgrade from 1.0.2 or before to 1.0.3 or after. +- Execute the following SQL to upgrade the database: + ``` + ALTER TABLE `user` ADD COLUMN `auth_provider` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT 'DB'; + ``` +- Enabling LDAP Support, + - For LDAP support, you need to add the LDAP server configurations and include LDAP in the list of authentication providers in the application.yml file. + - If no authentication providers are defined, default DB strategy will be used, and no changes are required. + - Below is a sample configuration for both the authentication providers and LDAP server settings. + ``` + # sample application.yaml + spring: + ldap: + url: ldap://localhost:389 + search: + base: ou=people,dc=example,dc=com + filter: (uid={0}) + domain: example.com + seatunnel: + authentication: + providers: + - DB + - LDAP + ``` + diff --git a/README_CN.md b/README_CN.md index 8be3e6ab..e221ecc1 100644 --- a/README_CN.md +++ b/README_CN.md @@ -231,4 +231,27 @@ sh bin/seatunnel-backend-daemon.sh start ```ALTER TABLE `t_st_job_instance` ADD COLUMN `error_message` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL;``` - +#### 2. 从1.0.2或更早版本升级到1.0.3或更高版本。 +- 执行以下SQL语句以升级数据库: + ``` + ALTER TABLE `user` ADD COLUMN `auth_provider` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT 'DB'; + ``` +- 启用LDAP支持, + - 要启用LDAP支持,您需要在`application.yml`文件中添加LDAP服务器配置,并将LDAP包含在认证提供者列表中。 + - 如果未定义任何认证提供者,将使用默认的DB策略,不需要做任何更改。 + - 以下是认证提供者和LDAP服务器设置的示例配置。 + ``` + # sample application.yaml + spring: + ldap: + url: ldap://localhost:389 + search: + base: ou=people,dc=example,dc=com + filter: (uid={0}) + domain: example.com + seatunnel: + authentication: + providers: + - DB + - LDAP + ``` diff --git a/pom.xml b/pom.xml index 3b064bad..852f2f30 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,7 @@ <spring-boot.version>2.6.8</spring-boot.version> <spring.version>5.3.20</spring.version> + <spring.security.version>5.6.5</spring.security.version> <mybatis-plus-boot-starter.version>3.5.3.1</mybatis-plus-boot-starter.version> <druid-spring-boot-starter.version>1.2.9</druid-spring-boot-starter.version> <springfox-swagger.version>2.6.1</springfox-swagger.version> @@ -349,6 +350,16 @@ <artifactId>spring-boot-starter-jdbc</artifactId> <version>${spring-boot.version}</version> </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-core</artifactId> + <version>${spring.security.version}</version> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-ldap</artifactId> + <version>${spring.security.version}</version> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> diff --git a/seatunnel-server/seatunnel-app/pom.xml b/seatunnel-server/seatunnel-app/pom.xml index 24c2a44f..98d93670 100644 --- a/seatunnel-server/seatunnel-app/pom.xml +++ b/seatunnel-server/seatunnel-app/pom.xml @@ -310,6 +310,15 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-core</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-ldap</artifactId> + </dependency> <dependency> <groupId>com.baomidou</groupId> diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/Constants.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/Constants.java index b32c48ed..886c1f70 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/Constants.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/common/Constants.java @@ -655,4 +655,7 @@ public final class Constants { public static final int DEFAULT_ALERT_GROUP_ID = 1; public static final String TASK_ID = "taskId"; + + public static final String AUTHENTICATION_PROVIDER_LDAP = "LDAP"; + public static final String AUTHENTICATION_PROVIDER_DB = "DB"; } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/LdapAuthenticationBuilder.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/LdapAuthenticationBuilder.java new file mode 100644 index 00000000..a69e9c63 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/LdapAuthenticationBuilder.java @@ -0,0 +1,74 @@ +/* + * 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.seatunnel.app.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; +import org.springframework.security.core.Authentication; +import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.authentication.BindAuthenticator; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class LdapAuthenticationBuilder { + + @Value("${spring.ldap.url}") + private String ldapUrl; + + @Value("${spring.ldap.search.base}") + private String ldapSearchBase; + + @Value("${spring.ldap.search.domain}") + private String ldapSearchDomain; + + public LdapAuthenticationProvider buildLdapAuthenticationProvider( + Authentication authentication) { + + LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource(ldapUrl); + String ldapBindUser = authentication.getName(); + ldapContextSource.setUserDn( + new StringBuilder() + .append(ldapBindUser) + .append("@") + .append(ldapSearchDomain) + .toString()); + ldapContextSource.setPassword(authentication.getCredentials().toString()); + ldapContextSource.setCacheEnvironmentProperties(false); + ldapContextSource.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy()); + ldapContextSource.afterPropertiesSet(); + + String ldapAuthID = + ldapBindUser.toLowerCase().endsWith("@" + ldapSearchDomain) + ? ldapBindUser + : ldapBindUser + "@" + ldapSearchDomain; + String searchFilter = "(userPrincipalName=" + ldapAuthID + ")"; + FilterBasedLdapUserSearch userSearch = + new FilterBasedLdapUserSearch(ldapSearchBase, searchFilter, ldapContextSource); + userSearch.setSearchSubtree(true); + BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource); + authenticator.setUserSearch(userSearch); + LdapAuthenticationProvider ldapAuthenticationProvider = + new LdapAuthenticationProvider(authenticator); + return ldapAuthenticationProvider; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/SeatunnelAuthenticationProvidersConfig.java similarity index 66% copy from seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java copy to seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/SeatunnelAuthenticationProvidersConfig.java index 73777653..e1c52c43 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/SeatunnelAuthenticationProvidersConfig.java @@ -14,26 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.seatunnel.app.config; -package org.apache.seatunnel.app.dal.entity; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; import lombok.Data; -import java.util.Date; +import java.util.ArrayList; +import java.util.List; @Data -public class User { - private Integer id; - - private String username; - - private String password; - - private Byte status; - - private Byte type; - - private Date createTime; - - private Date updateTime; +@Configuration +@ConfigurationProperties(prefix = "spring.authentication") +public class SeatunnelAuthenticationProvidersConfig { + private List<String> providers = new ArrayList<>(); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/SeatunnelLdapAuthenticationProvider.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/SeatunnelLdapAuthenticationProvider.java new file mode 100644 index 00000000..84694361 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/config/SeatunnelLdapAuthenticationProvider.java @@ -0,0 +1,64 @@ +/* + * 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.seatunnel.app.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; + +import lombok.extern.slf4j.Slf4j; + +@Configuration +@Slf4j +public class SeatunnelLdapAuthenticationProvider implements AuthenticationProvider { + + @Autowired LdapAuthenticationBuilder ldapAuthenticationBuilder; + + @Override + public boolean supports(Class<?> authentication) { + return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); + } + + @Override + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + try { + LdapAuthenticationProvider ldapAuthenticationProvider = + ldapAuthenticationBuilder.buildLdapAuthenticationProvider(authentication); + return ldapAuthenticationProvider.authenticate(authentication); + } catch (BadCredentialsException ex) { + log.error("Invalid credentials for user : {}", authentication.getName()); + throw new BadCredentialsException("Invalid credentials"); + } catch (Exception ex) { + log.error( + "Error while authenticating user : {}, reason :{}", + authentication.getName(), + ex.getMessage()); + throw new AuthenticationException(ex.getMessage().toString()) { + @Override + public String getMessage() { + return super.getMessage(); + } + }; + } + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java index 106f23ff..59c68ab4 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/controller/UserController.java @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -110,8 +111,10 @@ public class UserController { } @PostMapping("/login") - public Result<UserSimpleInfoRes> login(@RequestBody UserLoginReq req) { - return Result.success(iUserService.login(req)); + public Result<UserSimpleInfoRes> login( + @RequestBody UserLoginReq req, + @RequestHeader(value = "X-Seatunnel-Auth-Type", required = false) String authType) { + return Result.success(iUserService.login(req, authType)); } @PatchMapping("/logout") diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/IUserDao.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/IUserDao.java index 8adb6442..be5dd16d 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/IUserDao.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/IUserDao.java @@ -45,7 +45,7 @@ public interface IUserDao { User getByName(String user); - User checkPassword(String username, String password); + User checkPassword(String username, String password, String authProvider); long insertLoginLog(UserLoginLogDto dto); diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/UserDaoImpl.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/UserDaoImpl.java index 0b26991d..3d1afef2 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/UserDaoImpl.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/dao/impl/UserDaoImpl.java @@ -52,6 +52,7 @@ public class UserDaoImpl implements IUserDao { user.setPassword(dto.getPassword()); user.setType((byte) dto.getType()); user.setStatus((byte) dto.getStatus()); + user.setAuthProvider(dto.getAuthProvider()); userMapper.insert(user); return user.getId(); @@ -114,8 +115,8 @@ public class UserDaoImpl implements IUserDao { } @Override - public User checkPassword(String username, String password) { - return userMapper.selectByNameAndPasswd(username, password); + public User checkPassword(String username, String password, String authProvider) { + return userMapper.selectByNameAndPasswd(username, password, authProvider); } @Override diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java index 73777653..978360a0 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/entity/User.java @@ -36,4 +36,6 @@ public class User { private Date createTime; private Date updateTime; + + private String authProvider; } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/mapper/UserMapper.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/mapper/UserMapper.java index 5d13b976..9648ecd9 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/mapper/UserMapper.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/dal/mapper/UserMapper.java @@ -44,7 +44,9 @@ public interface UserMapper { int countBySelective(@Param("user") User user); User selectByNameAndPasswd( - @Param("username") String username, @Param("password") String password); + @Param("username") String username, + @Param("password") String password, + @Param("authProvider") String authProvider); List<User> queryEnabledUsers(); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java index d65383ed..f4d430cd 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java @@ -28,4 +28,5 @@ public class UpdateUserDto { private String password; private int status; private int type; + private String authProvider; } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/IAuthenticationStrategy.java similarity index 75% copy from seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java copy to seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/IAuthenticationStrategy.java index d65383ed..4e77c8ca 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/domain/dto/user/UpdateUserDto.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/IAuthenticationStrategy.java @@ -14,18 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.seatunnel.app.security.authentication.strategy; -package org.apache.seatunnel.app.domain.dto.user; +import org.apache.seatunnel.app.dal.entity.User; +import org.apache.seatunnel.app.domain.request.user.UserLoginReq; -import lombok.Builder; -import lombok.Data; - -@Builder -@Data -public class UpdateUserDto { - private Integer id; - private String username; - private String password; - private int status; - private int type; +public interface IAuthenticationStrategy { + User authenticate(UserLoginReq req); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/impl/DBAuthenticationStrategy.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/impl/DBAuthenticationStrategy.java new file mode 100644 index 00000000..eff7fa36 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/impl/DBAuthenticationStrategy.java @@ -0,0 +1,54 @@ +/* + * 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.seatunnel.app.security.authentication.strategy.impl; + +import org.apache.seatunnel.app.common.Constants; +import org.apache.seatunnel.app.dal.dao.IUserDao; +import org.apache.seatunnel.app.dal.entity.User; +import org.apache.seatunnel.app.domain.request.user.UserLoginReq; +import org.apache.seatunnel.app.security.authentication.strategy.IAuthenticationStrategy; +import org.apache.seatunnel.app.utils.PasswordUtils; +import org.apache.seatunnel.server.common.SeatunnelException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +import static org.apache.seatunnel.server.common.SeatunnelErrorEnum.USERNAME_PASSWORD_NO_MATCHED; + +@Component +public class DBAuthenticationStrategy implements IAuthenticationStrategy { + + @Autowired private IUserDao userDaoImpl; + + @Value("${user.default.passwordSalt:seatunnel}") + private String defaultSalt; + + @Override + public User authenticate(UserLoginReq req) { + final String password = PasswordUtils.encryptWithSalt(defaultSalt, req.getPassword()); + final User user = + userDaoImpl.checkPassword( + req.getUsername(), password, Constants.AUTHENTICATION_PROVIDER_DB); + if (Objects.isNull(user)) { + throw new SeatunnelException(USERNAME_PASSWORD_NO_MATCHED); + } + return user; + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/impl/LDAPAuthenticationStrategy.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/impl/LDAPAuthenticationStrategy.java new file mode 100644 index 00000000..1fa9bad5 --- /dev/null +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/security/authentication/strategy/impl/LDAPAuthenticationStrategy.java @@ -0,0 +1,68 @@ +/* + * 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.seatunnel.app.security.authentication.strategy.impl; + +import org.apache.seatunnel.app.common.Constants; +import org.apache.seatunnel.app.config.SeatunnelLdapAuthenticationProvider; +import org.apache.seatunnel.app.dal.dao.IUserDao; +import org.apache.seatunnel.app.dal.entity.User; +import org.apache.seatunnel.app.domain.dto.user.UpdateUserDto; +import org.apache.seatunnel.app.domain.request.user.UserLoginReq; +import org.apache.seatunnel.app.security.authentication.strategy.IAuthenticationStrategy; +import org.apache.seatunnel.server.common.SeatunnelException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +import static org.apache.seatunnel.server.common.SeatunnelErrorEnum.USERNAME_PASSWORD_NO_MATCHED; + +@Component +public class LDAPAuthenticationStrategy implements IAuthenticationStrategy { + + @Autowired private IUserDao userDaoImpl; + + @Autowired private SeatunnelLdapAuthenticationProvider seatunnelLdapAuthenticationProvider; + + @Override + public User authenticate(UserLoginReq req) { + String username = req.getUsername(); + String password = req.getPassword(); + Authentication authenticationRequest = + new UsernamePasswordAuthenticationToken(username, password); + try { + seatunnelLdapAuthenticationProvider.authenticate(authenticationRequest); + } catch (AuthenticationException ex) { + throw new SeatunnelException(USERNAME_PASSWORD_NO_MATCHED); + } + + if (userDaoImpl.getByName(username) == null) { + // 2. add a new user. + final UpdateUserDto dto = + UpdateUserDto.builder() + .id(null) + .username(username) + .password("") + .authProvider(Constants.AUTHENTICATION_PROVIDER_LDAP) + .build(); + userDaoImpl.add(dto); + } + return userDaoImpl.getByName(username); + } +} diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/IUserService.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/IUserService.java index 60ba8d43..1b84559a 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/IUserService.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/IUserService.java @@ -39,5 +39,5 @@ public interface IUserService { void disable(int id); - UserSimpleInfoRes login(UserLoginReq req); + UserSimpleInfoRes login(UserLoginReq req, String authType); } diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/impl/UserServiceImpl.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/impl/UserServiceImpl.java index 95f2321f..853e6819 100644 --- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/impl/UserServiceImpl.java +++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/service/impl/UserServiceImpl.java @@ -17,7 +17,9 @@ package org.apache.seatunnel.app.service.impl; +import org.apache.seatunnel.app.common.Constants; import org.apache.seatunnel.app.common.UserTokenStatusEnum; +import org.apache.seatunnel.app.config.SeatunnelAuthenticationProvidersConfig; import org.apache.seatunnel.app.dal.dao.IUserDao; import org.apache.seatunnel.app.dal.entity.User; import org.apache.seatunnel.app.domain.dto.user.ListUserDto; @@ -31,24 +33,31 @@ import org.apache.seatunnel.app.domain.response.PageInfo; import org.apache.seatunnel.app.domain.response.user.AddUserRes; import org.apache.seatunnel.app.domain.response.user.UserSimpleInfoRes; import org.apache.seatunnel.app.security.JwtUtils; +import org.apache.seatunnel.app.security.authentication.strategy.IAuthenticationStrategy; +import org.apache.seatunnel.app.security.authentication.strategy.impl.DBAuthenticationStrategy; +import org.apache.seatunnel.app.security.authentication.strategy.impl.LDAPAuthenticationStrategy; import org.apache.seatunnel.app.service.IRoleService; import org.apache.seatunnel.app.service.IUserService; import org.apache.seatunnel.app.utils.PasswordUtils; import org.apache.seatunnel.server.common.PageData; +import org.apache.seatunnel.server.common.SeatunnelErrorEnum; import org.apache.seatunnel.server.common.SeatunnelException; +import org.apache.commons.lang3.StringUtils; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.util.HashMap; import java.util.List; -import java.util.Objects; +import java.util.Map; import java.util.stream.Collectors; -import static org.apache.seatunnel.server.common.SeatunnelErrorEnum.USERNAME_PASSWORD_NO_MATCHED; - @Component public class UserServiceImpl implements IUserService { @Resource private IUserDao userDaoImpl; @@ -60,6 +69,26 @@ public class UserServiceImpl implements IUserService { @Value("${user.default.passwordSalt:seatunnel}") private String defaultSalt; + private final Map<String, IAuthenticationStrategy> strategies = new HashMap<>(); + + @Autowired private DBAuthenticationStrategy dbAuthenticationStrategy; + + @Autowired private LDAPAuthenticationStrategy ldapAuthenticationStrategy; + + @Autowired + private SeatunnelAuthenticationProvidersConfig seatunnelAuthenticationProvidersConfig; + + @PostConstruct + public void init() { + List<String> providers = seatunnelAuthenticationProvidersConfig.getProviders(); + if (providers.isEmpty() || providers.contains(Constants.AUTHENTICATION_PROVIDER_DB)) { + strategies.put(Constants.AUTHENTICATION_PROVIDER_DB, dbAuthenticationStrategy); + } + if (providers.contains(Constants.AUTHENTICATION_PROVIDER_LDAP)) { + strategies.put(Constants.AUTHENTICATION_PROVIDER_LDAP, ldapAuthenticationStrategy); + } + } + @Override @Transactional(rollbackFor = Exception.class) public AddUserRes add(AddUserReq addReq) { @@ -75,6 +104,7 @@ public class UserServiceImpl implements IUserService { .password(PasswordUtils.encryptWithSalt(defaultSalt, addReq.getPassword())) .status(addReq.getStatus()) .type(addReq.getType()) + .authProvider(Constants.AUTHENTICATION_PROVIDER_DB) .build(); final int userId = userDaoImpl.add(dto); @@ -139,16 +169,14 @@ public class UserServiceImpl implements IUserService { } @Override - public UserSimpleInfoRes login(UserLoginReq req) { - - final String username = req.getUsername(); - final String password = PasswordUtils.encryptWithSalt(defaultSalt, req.getPassword()); - - final User user = userDaoImpl.checkPassword(username, password); - if (Objects.isNull(user)) { - throw new SeatunnelException(USERNAME_PASSWORD_NO_MATCHED); + public UserSimpleInfoRes login(UserLoginReq req, String authType) { + authType = StringUtils.isEmpty(authType) ? Constants.AUTHENTICATION_PROVIDER_DB : authType; + if (!strategies.containsKey(authType)) { + throw new SeatunnelException( + SeatunnelErrorEnum.INVALID_AUTHENTICATION_PROVIDER, authType); } - + IAuthenticationStrategy strategy = strategies.get(authType); + User user = strategy.authenticate(req); UserSimpleInfoRes translate = translate(user); final String token = jwtUtils.genToken(translate.toMap()); translate.setToken(token); @@ -160,7 +188,6 @@ public class UserServiceImpl implements IUserService { .userId(user.getId()) .build(); userDaoImpl.insertLoginLog(logDto); - return translate; } diff --git a/seatunnel-server/seatunnel-app/src/main/resources/application.yml b/seatunnel-server/seatunnel-app/src/main/resources/application.yml index a85295a6..6a06d519 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/application.yml +++ b/seatunnel-server/seatunnel-app/src/main/resources/application.yml @@ -31,13 +31,23 @@ spring: mvc: pathmatch: matching-strategy: ant_path_matcher - + ldap: + url: ldap://localhost:389 + search: + base: ou=people,dc=example,dc=com + filter: (uid={0}) + domain: example.com + authentication: + providers: + - DB + #- LDAP # LDAP authentication is disabled by default jwt: expireTime: 86400 # please add key when deploy secretKey: algorithm: HS256 + --- spring: config: diff --git a/seatunnel-server/seatunnel-app/src/main/resources/org/apache/seatunnel/app/dal/mapper/UserMapper.xml b/seatunnel-server/seatunnel-app/src/main/resources/org/apache/seatunnel/app/dal/mapper/UserMapper.xml index 0539f9cc..f4e8abcb 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/org/apache/seatunnel/app/dal/mapper/UserMapper.xml +++ b/seatunnel-server/seatunnel-app/src/main/resources/org/apache/seatunnel/app/dal/mapper/UserMapper.xml @@ -23,6 +23,7 @@ <result column="type" jdbcType="TINYINT" property="type"/> <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/> + <result column="auth_provider" jdbcType="VARCHAR" property="authProvider"/> </resultMap> <sql id="Base_Column_List"> id, @@ -31,13 +32,14 @@ `status`, `type`, create_time, - update_time + update_time, + auth_provider </sql> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="org.apache.seatunnel.app.dal.entity.User" useGeneratedKeys="true"> - insert into `user` (username, password, status, type) + insert into `user` (username, password, status, type, auth_provider) values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{status,jdbcType=TINYINT}, - #{type,jdbcType=TINYINT}) + #{type,jdbcType=TINYINT}, #{authProvider,jdbcType=VARCHAR}) </insert> <update id="updateByPrimaryKey"> update `user` @@ -94,7 +96,7 @@ select <include refid="Base_Column_List"/> from `user` - where username = #{username,jdbcType=VARCHAR} and password = #{password,jdbcType=VARCHAR} + where username = #{username,jdbcType=VARCHAR} and password = #{password,jdbcType=VARCHAR} and auth_provider = #{authProvider,jdbcType=VARCHAR} </select> <select id="queryEnabledUsers" resultType="org.apache.seatunnel.app.dal.entity.User"> select diff --git a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql index f9bfb51a..b4231ce6 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql +++ b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_h2.sql @@ -219,6 +219,7 @@ CREATE TABLE "user" ( type TINYINT NOT NULL, create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + auth_provider varchar(10) NOT NULL DEFAULT 'DB', PRIMARY KEY (id) ); diff --git a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql index 2fd44da2..2262caa6 100644 --- a/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql +++ b/seatunnel-server/seatunnel-app/src/main/resources/script/seatunnel_server_mysql.sql @@ -235,6 +235,7 @@ CREATE TABLE `user` ( `type` tinyint(4) NOT NULL, `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), `update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + `auth_provider` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'DB', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; diff --git a/seatunnel-server/seatunnel-server-common/src/main/java/org/apache/seatunnel/server/common/SeatunnelErrorEnum.java b/seatunnel-server/seatunnel-server-common/src/main/java/org/apache/seatunnel/server/common/SeatunnelErrorEnum.java index 83ba0a4e..29c772ac 100644 --- a/seatunnel-server/seatunnel-server-common/src/main/java/org/apache/seatunnel/server/common/SeatunnelErrorEnum.java +++ b/seatunnel-server/seatunnel-server-common/src/main/java/org/apache/seatunnel/server/common/SeatunnelErrorEnum.java @@ -36,6 +36,10 @@ public enum SeatunnelErrorEnum { "The user name and password do not match, please check your input"), TOKEN_ILLEGAL(10008, "token illegal", "The token is expired or invalid, please login again."), + INVALID_AUTHENTICATION_PROVIDER( + 10010, + "please provide the supported authentication providers, default DB", + "Invalid authentication provider [%s]"), NO_SUCH_JOB(10009, "no such job", "No such job. Maybe deleted by others."), /** request dolphinscheduler failed */ diff --git a/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/common/SeatunnelWebTestingBase.java b/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/common/SeatunnelWebTestingBase.java index fd17fbb5..a006cd31 100644 --- a/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/common/SeatunnelWebTestingBase.java +++ b/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/common/SeatunnelWebTestingBase.java @@ -31,13 +31,23 @@ import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Map; public class SeatunnelWebTestingBase { protected final String baseUrl = "http://localhost:8802/seatunnel/api/v1"; - protected Result<UserSimpleInfoRes> login(UserLoginReq userLoginReq) { + public Result<UserSimpleInfoRes> login(UserLoginReq userLoginReq) { + return login(userLoginReq, null); + } + + public Result<UserSimpleInfoRes> login(UserLoginReq userLoginReq, String authType) { String requestBody = JsonUtils.toJsonString(userLoginReq); - String response = sendRequest(url("user/login"), requestBody, "POST"); + Map<String, String> headers = + authType != null + ? Collections.singletonMap("X-Seatunnel-Auth-Type", authType) + : null; + String response = sendRequest(url("user/login"), requestBody, "POST", headers); return JSONTestUtils.parseObject( response, new TypeReference<Result<UserSimpleInfoRes>>() {}); } @@ -51,10 +61,15 @@ public class SeatunnelWebTestingBase { } protected String sendRequest(String url) { - return sendRequest(url, null, "GET"); + return sendRequest(url, null, "GET", null); } protected String sendRequest(String url, String requestBody, String httpMethod) { + return sendRequest(url, requestBody, httpMethod, null); + } + + protected String sendRequest( + String url, String requestBody, String httpMethod, Map<String, String> headers) { HttpURLConnection connection = null; try { URL urlObject = new URL(url); @@ -69,6 +84,11 @@ public class SeatunnelWebTestingBase { if (!url.endsWith("user/login?")) { connection.setRequestProperty("token", TokenProvider.getToken()); } + if (headers != null && !headers.isEmpty()) { + for (Map.Entry<String, String> header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + } connection.setDoOutput(true); if (requestBody != null) { try (OutputStream os = connection.getOutputStream()) { diff --git a/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/test/UserControllerTest.java b/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/test/UserControllerTest.java index f6e72857..76c28b37 100644 --- a/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/test/UserControllerTest.java +++ b/seatunnel-web-it/src/test/java/org/apache/seatunnel/app/test/UserControllerTest.java @@ -21,19 +21,24 @@ import org.apache.seatunnel.app.common.SeaTunnelWebCluster; import org.apache.seatunnel.app.controller.UserControllerWrapper; import org.apache.seatunnel.app.domain.request.user.AddUserReq; import org.apache.seatunnel.app.domain.request.user.UpdateUserReq; +import org.apache.seatunnel.app.domain.request.user.UserLoginReq; import org.apache.seatunnel.app.domain.response.user.AddUserRes; +import org.apache.seatunnel.app.domain.response.user.UserSimpleInfoRes; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.util.function.Supplier; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class UserControllerTest { private static final SeaTunnelWebCluster seaTunnelWebCluster = new SeaTunnelWebCluster(); private static UserControllerWrapper userControllerWrapper; - private static String uniqueId = "_" + System.currentTimeMillis(); + final Supplier<String> uniqueId = () -> "_" + System.nanoTime(); @BeforeAll public static void setUp() { @@ -43,7 +48,7 @@ public class UserControllerTest { @Test public void addUser_shouldReturnSuccess_whenValidRequest() { - String user = "addUser" + uniqueId; + String user = "addUser" + uniqueId.get(); AddUserReq addUserReq = getAddUserReq(user, "pass1"); Result<AddUserRes> result = userControllerWrapper.addUser(addUserReq); assertTrue(result.isSuccess()); @@ -61,7 +66,7 @@ public class UserControllerTest { @Test public void updateUser_shouldReturnSuccess_whenValidRequest() { - String user = "updateUser" + uniqueId; + String user = "updateUser" + uniqueId.get(); AddUserReq addUserReq = getAddUserReq(user, "pass2"); Result<AddUserRes> result = userControllerWrapper.addUser(addUserReq); assertTrue(result.isSuccess()); @@ -79,7 +84,7 @@ public class UserControllerTest { @Test public void deleteUser_shouldReturnSuccess_whenValidUserId() { - String user = "deleteUser" + uniqueId; + String user = "deleteUser" + uniqueId.get(); AddUserReq addUserReq = getAddUserReq(user, "pass3"); Result<AddUserRes> result = userControllerWrapper.addUser(addUserReq); assertTrue(result.isSuccess()); @@ -95,6 +100,53 @@ public class UserControllerTest { assertNotNull(result.getData()); } + @Test + public void login_shouldPass_whenValidAuthType() { + String user = "loginUser" + uniqueId.get(); + AddUserReq addUserReq = getAddUserReq(user, "pass4"); + Result<AddUserRes> addUserResult = userControllerWrapper.addUser(addUserReq); + assertTrue(addUserResult.isSuccess()); + + UserLoginReq loginReq = new UserLoginReq(); + loginReq.setUsername(user); + loginReq.setPassword("pass4"); + + Result<UserSimpleInfoRes> loginResult = userControllerWrapper.login(loginReq, "DB"); + assertTrue(loginResult.isSuccess()); + } + + @Test + public void login_shouldPass_whenNoAuthType() { + String user = "loginUser" + uniqueId.get(); + AddUserReq addUserReq = getAddUserReq(user, "pass5"); + Result<AddUserRes> addUserResult = userControllerWrapper.addUser(addUserReq); + assertTrue(addUserResult.isSuccess()); + + UserLoginReq loginReq = new UserLoginReq(); + loginReq.setUsername(user); + loginReq.setPassword("pass5"); + + Result<UserSimpleInfoRes> loginResult = userControllerWrapper.login(loginReq); + assertTrue(loginResult.isSuccess()); + } + + @Test + public void login_shouldFail_whenInvalidAuthType() { + String user = "loginUser" + uniqueId.get(); + AddUserReq addUserReq = getAddUserReq(user, "pass6"); + Result<AddUserRes> addUserResult = userControllerWrapper.addUser(addUserReq); + assertTrue(addUserResult.isSuccess()); + + UserLoginReq loginReq = new UserLoginReq(); + loginReq.setUsername(user); + loginReq.setPassword("pass6"); + + Result<UserSimpleInfoRes> loginResult = + userControllerWrapper.login(loginReq, "INVALID_AUTH_TYPE"); + assertTrue(loginResult.isFailed()); + assertEquals("Invalid authentication provider [INVALID_AUTH_TYPE]", loginResult.getMsg()); + } + @AfterAll public static void tearDown() { Result<Void> logout = userControllerWrapper.logout(); diff --git a/seatunnel-web-it/src/test/resources/application.yml b/seatunnel-web-it/src/test/resources/application.yml index 276983f3..ef285655 100644 --- a/seatunnel-web-it/src/test/resources/application.yml +++ b/seatunnel-web-it/src/test/resources/application.yml @@ -31,6 +31,16 @@ spring: mvc: pathmatch: matching-strategy: ant_path_matcher + ldap: + url: ldap://localhost:389 + search: + base: ou=people,dc=example,dc=com + filter: (uid={0}) + domain: example.com + authentication: + providers: + - DB + #- LDAP # LDAP authentication is disabled by default jwt: expireTime: 86400 diff --git a/tools/dependencies/known-dependencies.txt b/tools/dependencies/known-dependencies.txt index a9d1ec32..2e110bef 100644 --- a/tools/dependencies/known-dependencies.txt +++ b/tools/dependencies/known-dependencies.txt @@ -74,8 +74,12 @@ spring-core-5.3.20.jar spring-expression-5.3.20.jar spring-jcl-5.3.20.jar spring-jdbc-5.3.20.jar +spring-ldap-core-2.3.7.RELEASE.jar spring-plugin-core-1.2.0.RELEASE.jar spring-plugin-metadata-1.2.0.RELEASE.jar +spring-security-core-5.6.5.jar +spring-security-ldap-5.6.5.jar +spring-security-crypto-5.6.5.jar spring-tx-5.3.20.jar spring-web-5.3.20.jar spring-webmvc-5.3.20.jar