From ffd4ae427d3fe6b31c8898388c17d08966cd687b Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Wed, 22 Jul 2020 11:35:19 +0800 Subject: [PATCH 01/13] =?UTF-8?q?fix:=20LDAP=E6=94=AF=E6=8C=81=E9=82=AE?= =?UTF-8?q?=E7=AE=B1=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ldap/controller/LdapController.java | 26 ++- .../io/metersphere/ldap/dao/PersonRepo.java | 9 - .../metersphere/ldap/dao/PersonRepoImpl.java | 177 --------------- .../metersphere/ldap/service/LdapService.java | 202 +++++++++++++++++- .../io/metersphere/security/ShiroDBRealm.java | 16 +- .../io/metersphere/service/UserService.java | 18 +- .../resources/i18n/messages_en_US.properties | 5 +- .../resources/i18n/messages_zh_CN.properties | 3 + .../resources/i18n/messages_zh_TW.properties | 3 + .../settings/system/LdapSetting.vue | 14 +- frontend/src/i18n/en-US.js | 1 + frontend/src/i18n/zh-CN.js | 1 + frontend/src/i18n/zh-TW.js | 1 + 13 files changed, 256 insertions(+), 220 deletions(-) delete mode 100644 backend/src/main/java/io/metersphere/ldap/dao/PersonRepo.java delete mode 100644 backend/src/main/java/io/metersphere/ldap/dao/PersonRepoImpl.java diff --git a/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java b/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java index 4a8c3b8deb..74f054f80e 100644 --- a/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java +++ b/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java @@ -7,12 +7,12 @@ import io.metersphere.commons.exception.MSException; import io.metersphere.controller.ResultHolder; import io.metersphere.controller.request.LoginRequest; import io.metersphere.i18n.Translator; -import io.metersphere.ldap.domain.Person; import io.metersphere.ldap.service.LdapService; import io.metersphere.service.SystemParameterService; import io.metersphere.service.UserService; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; +import org.springframework.ldap.core.DirContextOperations; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -36,29 +36,37 @@ public class LdapController { MSException.throwException(Translator.get("ldap_authentication_not_enabled")); } - Person person = ldapService.authenticate(request); + DirContextOperations dirContext = ldapService.authenticate(request); + String email = ldapService.getMappingAttr("email", dirContext); + String userId = ldapService.getMappingAttr("username", dirContext); SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LDAP.name()); + SecurityUtils.getSubject().getSession().setAttribute("email", email); - String username = request.getUsername(); - - String email = person.getEmail(); if (StringUtils.isBlank(email)) { MSException.throwException(Translator.get("login_fail_email_null")); } - User u = userService.selectUser(request.getUsername()); + // userId 或 email 有一个相同即为存在本地用户 + User u = userService.selectUser(userId, email); if (u == null) { + + // 新建用户 获取LDAP映射属性 + String name = ldapService.getMappingAttr("name", dirContext); + User user = new User(); - user.setId(username); - user.setName(username); + user.setId(userId); + user.setName(name); user.setEmail(email); user.setSource(UserSource.LDAP.name()); userService.addLdapUser(user); } - return userService.login(request); + // 执行 ShiroDBRealm 中 LDAP 登录逻辑 + LoginRequest loginRequest = new LoginRequest(); + loginRequest.setUsername(userId); + return userService.login(loginRequest); } @PostMapping("/test/connect") diff --git a/backend/src/main/java/io/metersphere/ldap/dao/PersonRepo.java b/backend/src/main/java/io/metersphere/ldap/dao/PersonRepo.java deleted file mode 100644 index bf0306cefd..0000000000 --- a/backend/src/main/java/io/metersphere/ldap/dao/PersonRepo.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.metersphere.ldap.dao; - - -import io.metersphere.ldap.domain.Person; - -public interface PersonRepo { - - Person getDnForUser(String name); -} diff --git a/backend/src/main/java/io/metersphere/ldap/dao/PersonRepoImpl.java b/backend/src/main/java/io/metersphere/ldap/dao/PersonRepoImpl.java deleted file mode 100644 index 8f9ac90de6..0000000000 --- a/backend/src/main/java/io/metersphere/ldap/dao/PersonRepoImpl.java +++ /dev/null @@ -1,177 +0,0 @@ -package io.metersphere.ldap.dao; - - -import io.metersphere.commons.constants.ParamConstants; -import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.EncryptUtils; -import io.metersphere.i18n.Translator; -import io.metersphere.ldap.domain.Person; -import io.metersphere.service.SystemParameterService; -import org.apache.commons.lang3.StringUtils; -import org.apache.shiro.realm.ldap.LdapUtils; -import org.springframework.ldap.AuthenticationException; -import org.springframework.ldap.InvalidNameException; -import org.springframework.ldap.InvalidSearchFilterException; -import org.springframework.ldap.NameNotFoundException; -import org.springframework.ldap.core.*; -import org.springframework.ldap.core.support.AbstractContextMapper; -import org.springframework.ldap.core.support.DefaultDirObjectFactory; -import org.springframework.ldap.core.support.LdapContextSource; -import org.springframework.ldap.query.SearchScope; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import javax.naming.directory.DirContext; -import javax.naming.ldap.LdapContext; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - -import static org.springframework.ldap.query.LdapQueryBuilder.query; - -@Service -public class PersonRepoImpl implements PersonRepo { - - @Resource - private SystemParameterService service; - - public boolean authenticate(String dn, String credentials) { - LdapTemplate ldapTemplate = getConnection(); - return authenticate(dn, credentials, ldapTemplate); - } - - private boolean authenticate(String dn, String credentials, LdapTemplate ldapTemplate) throws AuthenticationException { - DirContext ctx = null; - try { - ctx = ldapTemplate.getContextSource().getContext(dn, credentials); - return true; - } finally { - // It is imperative that the created DirContext instance is always closed - LdapUtils.closeContext((LdapContext) ctx); - } - } - - - @Override - public Person getDnForUser(String username) { - LdapTemplate ldapTemplate = getConnection(); - - String filter = getUserFilter(); - String[] arr = getUserOu(); - - List result = null; - for (String ou : arr) { - try { - result = ldapTemplate.search(query().base(ou.trim()).filter(filter, username), getContextMapper()); - if (result.size() == 1) { - return result.get(0); - } - } catch (NameNotFoundException e) { - MSException.throwException(Translator.get("login_fail_ou_error")); - } catch (InvalidNameException e) { - MSException.throwException(Translator.get("login_fail_ou_error")); - } catch (InvalidSearchFilterException e) { - MSException.throwException(Translator.get("login_fail_filter_error")); - } - } - - if (result.size() != 1) { - MSException.throwException(Translator.get("user_not_found_or_not_unique")); - } - - return result.get(0); - } - - private String getUserFilter() { - String filter = service.getValue(ParamConstants.LDAP.FILTER.getValue()); - - if (StringUtils.isBlank(filter)) { - MSException.throwException(Translator.get("ldap_user_filter_is_null")); - } - - return filter; - } - - private String[] getUserOu() { - String ou = service.getValue(ParamConstants.LDAP.OU.getValue()); - - if (StringUtils.isBlank(ou)) { - MSException.throwException(Translator.get("ldap_ou_is_null")); - } - - String[] arr = ou.split("\\|"); - - return arr; - } - - protected ContextMapper getContextMapper() { - return new PersonContextMapper(); - } - - private static class PersonContextMapper extends AbstractContextMapper { - @Override - public Person doMapFromContext(DirContextOperations context) { - Person person = new Person(); - person.setDn(context.getNameInNamespace()); - person.setUid(context.getStringAttribute("uid")); - person.setCommonName(context.getStringAttribute("cn")); - person.setSurName(context.getStringAttribute("sn")); - person.setUsername(context.getStringAttribute("sAMAccountName")); - person.setEmail(context.getStringAttribute("mail")); - return person; - } - } - - public LdapTemplate getConnection() { - - String url = service.getValue(ParamConstants.LDAP.URL.getValue()); - String dn = service.getValue(ParamConstants.LDAP.DN.getValue()); - String password = service.getValue(ParamConstants.LDAP.PASSWORD.getValue()); - - preConnect(url, dn, password); - - String credentials = EncryptUtils.aesDecrypt(password).toString(); - - LdapContextSource sourceLdapCtx = new LdapContextSource(); - sourceLdapCtx.setUrl(url); - sourceLdapCtx.setUserDn(dn); - sourceLdapCtx.setPassword(credentials); - sourceLdapCtx.setDirObjectFactory(DefaultDirObjectFactory.class); - sourceLdapCtx.afterPropertiesSet(); - LdapTemplate ldapTemplate = new LdapTemplate(sourceLdapCtx); - ldapTemplate.setIgnorePartialResultException(true); - Map baseEnv = new Hashtable<>(); - baseEnv.put("com.sun.jndi.ldap.connect.timeout", "3000"); - baseEnv.put("com.sun.jndi.ldap.read.timeout", "3000"); - sourceLdapCtx.setBaseEnvironmentProperties(baseEnv); - ldapTemplate.setDefaultSearchScope(SearchScope.SUBTREE.getId()); - - // ldapTemplate 是否可用 - try { - authenticate(dn, credentials, ldapTemplate); - } catch (AuthenticationException e) { - MSException.throwException(Translator.get("ldap_connect_fail_user")); - } catch (Exception e) { - MSException.throwException(Translator.get("ldap_connect_fail")); - } - - return ldapTemplate; - } - - private void preConnect(String url, String dn, String password) { - - if (StringUtils.isBlank(url)) { - MSException.throwException(Translator.get("ldap_url_is_null")); - } - - if (StringUtils.isBlank(dn)) { - MSException.throwException(Translator.get("ldap_dn_is_null")); - } - - if (StringUtils.isBlank(password)) { - MSException.throwException(Translator.get("ldap_password_is_null")); - } - - } - -} diff --git a/backend/src/main/java/io/metersphere/ldap/service/LdapService.java b/backend/src/main/java/io/metersphere/ldap/service/LdapService.java index 849a1d1565..a4cdf932e9 100644 --- a/backend/src/main/java/io/metersphere/ldap/service/LdapService.java +++ b/backend/src/main/java/io/metersphere/ldap/service/LdapService.java @@ -1,39 +1,221 @@ package io.metersphere.ldap.service; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.commons.constants.ParamConstants; import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.EncryptUtils; import io.metersphere.controller.request.LoginRequest; import io.metersphere.i18n.Translator; -import io.metersphere.ldap.dao.PersonRepoImpl; -import io.metersphere.ldap.domain.Person; +import io.metersphere.service.SystemParameterService; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.realm.ldap.LdapUtils; import org.springframework.ldap.AuthenticationException; +import org.springframework.ldap.InvalidNameException; +import org.springframework.ldap.InvalidSearchFilterException; +import org.springframework.ldap.NameNotFoundException; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.AbstractContextMapper; +import org.springframework.ldap.core.support.DefaultDirObjectFactory; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.query.SearchScope; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import static org.springframework.ldap.query.LdapQueryBuilder.query; @Service public class LdapService { @Resource - private PersonRepoImpl personRepo; + private SystemParameterService service; - - public Person authenticate(LoginRequest request) { + public DirContextOperations authenticate(LoginRequest request) { String username = request.getUsername(); String credentials = request.getPassword(); - Person person = null; + DirContextOperations dirContextOperations = null; try { - person = personRepo.getDnForUser(username); - personRepo.authenticate(person.getDn(), credentials); + // 获取LDAP用户相关信息 + dirContextOperations = getContextMapper(username); + // 执行登录认证 + authenticate(String.valueOf(dirContextOperations.getDn()), credentials); } catch (AuthenticationException e) { MSException.throwException(Translator.get("authentication_failed")); } - return person; + // 检查属性是否存在 + getMappingAttr("name", dirContextOperations); + getMappingAttr("email", dirContextOperations); + + + + return dirContextOperations; } public void testConnect() { - personRepo.getConnection(); + getConnection(); + } + + public boolean authenticate(String dn, String credentials) { + LdapTemplate ldapTemplate = getConnection(); + return authenticate(dn, credentials, ldapTemplate); + } + + private boolean authenticate(String dn, String credentials, LdapTemplate ldapTemplate) throws AuthenticationException { + DirContext ctx = null; + try { + ctx = ldapTemplate.getContextSource().getContext(dn, credentials); + return true; + } finally { + // It is imperative that the created DirContext instance is always closed + LdapUtils.closeContext((LdapContext) ctx); + } + } + + public DirContextOperations getContextMapper(String username) { + LdapTemplate ldapTemplate = getConnection(); + + String filter = getUserFilter(); + String[] arr = getUserOu(); + + List result = null; + // 多OU + for (String ou : arr) { + try { + result = ldapTemplate.search(query().base(ou.trim()).filter(filter, username), new MsContextMapper()); + if (result.size() == 1) { + return result.get(0); + } + } catch (NameNotFoundException e) { + MSException.throwException(Translator.get("login_fail_ou_error")); + } catch (InvalidNameException e) { + MSException.throwException(Translator.get("login_fail_ou_error")); + } catch (InvalidSearchFilterException e) { + MSException.throwException(Translator.get("login_fail_filter_error")); + } + } + + if (result.size() != 1) { + MSException.throwException(Translator.get("user_not_found_or_not_unique")); + } + + return result.get(0); + } + + private String getUserFilter() { + String filter = service.getValue(ParamConstants.LDAP.FILTER.getValue()); + + if (StringUtils.isBlank(filter)) { + MSException.throwException(Translator.get("ldap_user_filter_is_null")); + } + + return filter; + } + + private String[] getUserOu() { + String ou = service.getValue(ParamConstants.LDAP.OU.getValue()); + + if (StringUtils.isBlank(ou)) { + MSException.throwException(Translator.get("ldap_ou_is_null")); + } + + String[] arr = ou.split("\\|"); + + return arr; + } + + private static class MsContextMapper extends AbstractContextMapper { + @Override + public DirContextOperations doMapFromContext(DirContextOperations context) { + return context; + } + } + + public LdapTemplate getConnection() { + + String url = service.getValue(ParamConstants.LDAP.URL.getValue()); + String dn = service.getValue(ParamConstants.LDAP.DN.getValue()); + String password = service.getValue(ParamConstants.LDAP.PASSWORD.getValue()); + + preConnect(url, dn, password); + + String credentials = EncryptUtils.aesDecrypt(password).toString(); + + LdapContextSource sourceLdapCtx = new LdapContextSource(); + sourceLdapCtx.setUrl(url); + sourceLdapCtx.setUserDn(dn); + sourceLdapCtx.setPassword(credentials); + sourceLdapCtx.setDirObjectFactory(DefaultDirObjectFactory.class); + sourceLdapCtx.afterPropertiesSet(); + LdapTemplate ldapTemplate = new LdapTemplate(sourceLdapCtx); + ldapTemplate.setIgnorePartialResultException(true); + Map baseEnv = new Hashtable<>(); + baseEnv.put("com.sun.jndi.ldap.connect.timeout", "3000"); + baseEnv.put("com.sun.jndi.ldap.read.timeout", "3000"); + sourceLdapCtx.setBaseEnvironmentProperties(baseEnv); + ldapTemplate.setDefaultSearchScope(SearchScope.SUBTREE.getId()); + + // ldapTemplate 是否可用 + try { + authenticate(dn, credentials, ldapTemplate); + } catch (AuthenticationException e) { + MSException.throwException(Translator.get("ldap_connect_fail_user")); + } catch (Exception e) { + MSException.throwException(Translator.get("ldap_connect_fail")); + } + + return ldapTemplate; + } + + private void preConnect(String url, String dn, String password) { + + if (StringUtils.isBlank(url)) { + MSException.throwException(Translator.get("ldap_url_is_null")); + } + + if (StringUtils.isBlank(dn)) { + MSException.throwException(Translator.get("ldap_dn_is_null")); + } + + if (StringUtils.isBlank(password)) { + MSException.throwException(Translator.get("ldap_password_is_null")); + } + + } + + private String getLdapMapping() { + String mapping = service.getValue(ParamConstants.LDAP.MAPPING.getValue()); + + if (StringUtils.isBlank(mapping)) { + MSException.throwException(Translator.get("ldap_user_mapping_is_null")); + } + + return mapping; + } + + public String getMappingAttr(String attr, DirContextOperations dirContext) { + // 检查LDAP映射属性 + String mapping = getLdapMapping(); + JSONObject jsonObject = JSONObject.parseObject(mapping); + + String mapAttr = jsonObject.getString(attr); + if (StringUtils.isBlank(mapAttr)) { + MSException.throwException(Translator.get("check_ldap_mapping") + " " + attr); + } + + String result = dirContext.getStringAttribute(mapAttr); + if (StringUtils.isBlank(result)) { + MSException.throwException(Translator.get("ldap_mapping_value_null") + " " + mapAttr); + } + + return result; } } diff --git a/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java b/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java index 0cbdd0f1d0..f21b8e3315 100644 --- a/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java +++ b/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java @@ -112,15 +112,19 @@ public class ShiroDBRealm extends AuthorizingRealm { private AuthenticationInfo loginLdapMode(String userId, String password) { - // + // userId 或 email 有一个相同就返回User + String email = (String) SecurityUtils.getSubject().getSession().getAttribute("email"); UserDTO user = userService.getLoginUser(userId, Arrays.asList(UserSource.LDAP.name(), UserSource.LOCAL.name())); String msg; if (user == null) { - msg = "The user does not exist: " + userId; - logger.warn(msg); - throw new UnknownAccountException(Translator.get("user_not_exist") + userId); + user = userService.getLoginUserByEmail(email, Arrays.asList(UserSource.LDAP.name(), UserSource.LOCAL.name())); + if (user == null) { + msg = "The user does not exist: " + userId; + logger.warn(msg); + throw new UnknownAccountException(Translator.get("user_not_exist") + userId); + } + userId = user.getId(); } - userId = user.getId(); SessionUser sessionUser = SessionUser.fromUser(user); SessionUtils.putUser(sessionUser); @@ -132,7 +136,7 @@ public class ShiroDBRealm extends AuthorizingRealm { UserDTO user = userService.getLoginUser(userId, Collections.singletonList(UserSource.LOCAL.name())); String msg; if (user == null) { - user = userService.getLoginUserByEmail(userId, UserSource.LOCAL.name()); + user = userService.getLoginUserByEmail(userId, Collections.singletonList(UserSource.LOCAL.name())); if (user == null) { msg = "The user does not exist: " + userId; logger.warn(msg); diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index 617753d018..f61843832b 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -78,8 +78,18 @@ public class UserService { return getUserDTO(user.getId()); } - public User selectUser(String id) { - return userMapper.selectByPrimaryKey(id); + public User selectUser(String userId, String email) { + User user = userMapper.selectByPrimaryKey(userId); + if (user == null) { + UserExample example = new UserExample(); + example.createCriteria().andEmailEqualTo(email); + List users = userMapper.selectByExample(example); + if (!CollectionUtils.isEmpty(users)) { + return users.get(0); + } + } + return user; + } private void insertUserRole(List> roles, String userId) { @@ -199,9 +209,9 @@ public class UserService { return getUserDTO(users.get(0).getId()); } - public UserDTO getLoginUserByEmail(String email, String source) { + public UserDTO getLoginUserByEmail(String email, List list) { UserExample example = new UserExample(); - example.createCriteria().andEmailEqualTo(email).andSourceEqualTo(source); + example.createCriteria().andEmailEqualTo(email).andSourceIn(list); List users = userMapper.selectByExample(example); if (users == null || users.size() <= 0) { return null; diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 45d60af93e..23b93a72a3 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -126,10 +126,13 @@ ldap_password_is_null=LDAP password is empty ldap_connect_fail=Connection LDAP failed ldap_connect_fail_user=Connection LDAP failed, wrong DN or password bound ldap_user_filter_is_null=LDAP user filter is empty +ldap_user_mapping_is_null=LDAP user mapping is empty authentication_failed=User authentication failed,wrong user name or password user_not_found_or_not_unique=User does not exist or is not unique find_more_user=Multiple users found ldap_authentication_not_enabled=LDAP authentication is not enabled login_fail_email_null=Login failed, user mailbox is empty login_fail_ou_error=Login failed, please check the user OU -login_fail_filter_error=Login failed, please check the user filter \ No newline at end of file +login_fail_filter_error=Login failed, please check the user filter +check_ldap_mapping=Check LDAP attribute mapping +ldap_mapping_value_null=LDAP user attribute mapping field is empty \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 16291b123c..67a2bfcf75 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -126,6 +126,7 @@ ldap_password_is_null=LDAP密码为空 ldap_connect_fail=连接LDAP失败 ldap_connect_fail_user=连接LDAP失败,绑定的DN或密码错误 ldap_user_filter_is_null=LDAP用户过滤器为空 +ldap_user_mapping_is_null=LDAP用户属性映射为空 authentication_failed=用户认证失败,用户名或密码错误 user_not_found_or_not_unique=用户不存在或者不唯一 find_more_user=查找到多个用户 @@ -133,5 +134,7 @@ ldap_authentication_not_enabled=LDAP认证未启用 login_fail_email_null=登录失败,用户邮箱为空 login_fail_ou_error=登录失败,请检查用户OU login_fail_filter_error=登录失败,请检查用户过滤器 +check_ldap_mapping=检查LDAP属性映射 +ldap_mapping_value_null=LDAP用户属性映射字段为空值 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 6cb58bea1f..edcbdfdeb2 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -126,6 +126,7 @@ ldap_password_is_null=LDAP密碼為空 ldap_connect_fail=連接LDAP失敗 ldap_connect_fail_user=連接LDAP失敗,綁定的DN或密碼錯誤 ldap_user_filter_is_null=LDAP用戶過濾器為空 +ldap_user_mapping_is_null=LDAP用戶屬性映射為空 authentication_failed=用戶認證失敗,用戶名或密碼錯誤 user_not_found_or_not_unique=用戶不存在或者不唯一 find_more_user=查找到多個用戶 @@ -133,3 +134,5 @@ ldap_authentication_not_enabled=LDAP認證未啟用 login_fail_email_null=登錄失敗,用戶郵箱為空 login_fail_ou_error=登錄失敗,請檢查用戶OU login_fail_filter_error=登錄失敗,請檢查用戶過濾器 +check_ldap_mapping=檢查LDAP屬性映射 +ldap_mapping_value_null=LDAP用戶屬性映射預設為空值 diff --git a/frontend/src/business/components/settings/system/LdapSetting.vue b/frontend/src/business/components/settings/system/LdapSetting.vue index 25053afc78..6859896ee6 100644 --- a/frontend/src/business/components/settings/system/LdapSetting.vue +++ b/frontend/src/business/components/settings/system/LdapSetting.vue @@ -18,9 +18,9 @@ - - - + + + @@ -82,7 +82,8 @@ dn: {required: true, message: this.$t('ldap.input_dn'), trigger: ['change', 'blur']}, password: {required: true, message: this.$t('ldap.input_password'), trigger: ['change', 'blur']}, ou: {required: true, message: this.$t('ldap.input_ou'), trigger: ['change', 'blur']}, - filter: {required: true, message: this.$t('ldap.input_filter'), trigger: ['change', 'blur']} + filter: {required: true, message: this.$t('ldap.input_filter'), trigger: ['change', 'blur']}, + mapping: {required: true, message: this.$t('ldap.input_mapping'), trigger: ['change', 'blur']} }, loginFormRules: { username: {required: true, message: this.$t('ldap.input_username'), trigger: 'blur'}, @@ -142,6 +143,11 @@ return false; } + if (!this.form.mapping) { + this.$warning(this.$t('ldap.mapping_cannot_be_empty')); + return false; + } + this.loginForm = {}; this.loginVisible = true; }, diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index fa6a34cd7f..a7db656be3 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -677,6 +677,7 @@ export default { ou_cannot_be_empty: 'LDAP OU cannot be empty', filter_cannot_be_empty: 'LDAP user filter cannot be empty', password_cannot_be_empty: 'LDAP password cannot be empty', + mapping_cannot_be_empty: 'LDAP mapping cannot be empty', }, schedule: { not_set: "Not Set", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index cfa62fe7ba..b2aaed9d87 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -675,6 +675,7 @@ export default { dn_cannot_be_empty: 'LDAP DN不能为空', ou_cannot_be_empty: 'LDAP OU不能为空', filter_cannot_be_empty: 'LDAP 用户过滤器不能为空', + mapping_cannot_be_empty: 'LDAP 用户属性映射不能为空', password_cannot_be_empty: 'LDAP 密码不能为空', }, schedule: { diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 1a2bf8efce..c69a9b53f6 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -675,6 +675,7 @@ export default { ou_cannot_be_empty: 'LDAP OU不能為空', filter_cannot_be_empty: 'LDAP 用戶過濾器不能為空', password_cannot_be_empty: 'LDAP 密碼不能為空', + mapping_cannot_be_empty: 'LDAP 用戶屬性映射不能為空', }, schedule: { not_set: "未設置", From 8312c4f090fc5607c61d215cb62dee3d96fdb7f9 Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Wed, 22 Jul 2020 12:47:03 +0800 Subject: [PATCH 02/13] fix: ldap i18n --- .../src/business/components/settings/system/LdapSetting.vue | 2 +- frontend/src/i18n/en-US.js | 1 + frontend/src/i18n/zh-CN.js | 1 + frontend/src/i18n/zh-TW.js | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/business/components/settings/system/LdapSetting.vue b/frontend/src/business/components/settings/system/LdapSetting.vue index 6859896ee6..785101f267 100644 --- a/frontend/src/business/components/settings/system/LdapSetting.vue +++ b/frontend/src/business/components/settings/system/LdapSetting.vue @@ -19,7 +19,7 @@ - + diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 2d1567fcdf..cc4c7c6880 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -678,6 +678,7 @@ export default { input_url_placeholder: 'Please enter the LDAP address (eg ldap://localhost:389)', input_ou_placeholder: 'Enter user OU (use | to separate each OU)', input_filter_placeholder: 'Input filter [Possible options are cn or uid or sAMAccountName={0}, eg: (uid={0})]', + input_mapping_placeholder: 'eg:{"username":"uid","name":"sn","email":"mail"}, The username mapping option may be cn or uid or sAMAccountName', test_connect: 'Test Connection', test_login: 'Test Login', edit: 'Edit', diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 66f7d28d6c..b413d578b5 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -678,6 +678,7 @@ export default { input_url_placeholder: '请输入LDAP地址 (如 ldap://localhost:389)', input_ou_placeholder: '输入用户OU (使用|分隔各OU)', input_filter_placeholder: '输入过滤器 [可能的选项是cn或uid或sAMAccountName={0}, 如:(uid={0})]', + input_mapping_placeholder: '如:{"username":"uid","name":"sn","email":"mail"}, username映射的选项可能是cn或uid或sAMAccountName', test_connect: '测试连接', test_login: '测试登录', edit: '编辑', diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 254d44d510..4d943726c4 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -676,6 +676,7 @@ export default { input_url_placeholder: '請輸入LDAP地址 (如 ldap://localhost:389)', input_ou_placeholder: '輸入用戶OU (使用|分隔各OU)', input_filter_placeholder: '輸入過濾器 [可能的選項是cn或uid或sAMAccountName={0}, 如:(uid={0})]', + input_mapping_placeholder: '如:{"username":"uid","name":"sn","email":"mail"}, username映射的選項可能是cn或uid或sAMAccountName', test_connect: '測試連接', test_login: '測試登錄', edit: '編輯', From d8383748fd64afec9520293d842432f076b4edca Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Wed, 22 Jul 2020 12:57:42 +0800 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20=E6=B5=8B=E8=AF=95=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E6=89=A7=E8=A1=8C=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BA=A7=E7=94=9F=E7=9A=84=E6=B5=8B=E8=AF=95=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E6=96=B9=E5=BC=8F=E4=B8=BA=E7=A9=BA=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/track/plan/view/comonents/test/ApiTestDetail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/business/components/track/plan/view/comonents/test/ApiTestDetail.vue b/frontend/src/business/components/track/plan/view/comonents/test/ApiTestDetail.vue index 0152f08a7a..a1f299a012 100644 --- a/frontend/src/business/components/track/plan/view/comonents/test/ApiTestDetail.vue +++ b/frontend/src/business/components/track/plan/view/comonents/test/ApiTestDetail.vue @@ -81,7 +81,7 @@ }); }, runTest() { - this.result = this.$post("/api/run", {id: this.test.id}, (response) => { + this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => { this.$success(this.$t('api_test.running')); this.$emit('runTest', response.data) }); From 23351a71e2bb16aaa909642ced54d83b99709579 Mon Sep 17 00:00:00 2001 From: "Captain.B" Date: Wed, 22 Jul 2020 13:22:54 +0800 Subject: [PATCH 04/13] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=E5=92=8C=E6=B5=8B=E8=AF=95=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E7=9A=84=E9=AB=98=E7=BA=A7=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/ext/ExtTestPlanTestCaseMapper.xml | 111 +++- .../QueryTestPlanCaseRequest.java | 2 + .../view/comonents/TestPlanTestCaseList.vue | 512 +++++++++--------- 3 files changed, 377 insertions(+), 248 deletions(-) diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml index 3e0f0b57fb..d502c3aa90 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml @@ -1,6 +1,100 @@ + + + + like CONCAT('%', #{${object}.value},'%') + + + not like CONCAT('%', #{${object}.value},'%') + + + in + + #{v} + + + + not in + + #{v} + + + + between #{${object}.value[0]} and #{${object}.value[1]} + + + > #{${object}.value} + + + < #{${object}.value} + + + >= #{${object}.value} + + + <= #{${object}.value} + + + = '${@io.metersphere.commons.utils.SessionUtils@getUserId()}' + + + = #{${object}.value} + + + + + + + and test_case.name + + + + + + and test_case.node_path + + + + + + and test_case.priority + + + + + + and test_case.create_time + + + + + + and test_case.type + + + + + + and test_case.update_time + + + + + + and test_case.method + + + + + + and test_case.maintainer + + + + +