Browse Source

[FEATURE-736] integrate ldap authentication (#3743)

* [FEATURE-736] integrate ldap authentication

* add ldap authentication type
* refactor authentication with ldap and password
* add  createUser for ldap in user service
* remove duplicate password authenticator
geosmart 4 years ago
parent
commit
50237afe08
16 changed files with 761 additions and 176 deletions
  1. 2 0
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/AuthenticationType.java
  2. 4 1
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/Authenticator.java
  3. 8 1
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/SecurityConfig.java
  4. 25 10
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/PasswordAuthenticator.java
  5. 45 0
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticator.java
  6. 133 0
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapService.java
  7. 34 0
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/pwd/PasswordAuthenticator.java
  8. 127 69
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java
  9. 13 1
      dolphinscheduler-api/src/main/resources/application-api.properties
  10. 45 0
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/SecurityConfigLDAPTest.java
  11. 3 1
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/SecurityConfigTest.java
  12. 142 0
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticatorTest.java
  13. 81 0
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapServiceTest.java
  14. 18 6
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/PasswordAuthenticatorTest.java
  15. 77 85
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java
  16. 4 2
      pom.xml

+ 2 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/AuthenticationType.java

@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.api.security;
 
 import com.baomidou.mybatisplus.annotation.EnumValue;
@@ -24,6 +25,7 @@ import com.baomidou.mybatisplus.annotation.EnumValue;
 public enum AuthenticationType {
 
     PASSWORD(0, "verify via user name and password"),
+    LDAP(1, "verify via LDAP server"),
     ;
 
     AuthenticationType(int code, String desc) {

+ 4 - 1
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/Authenticator.java

@@ -14,13 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.api.security;
 
 import org.apache.dolphinscheduler.api.utils.Result;
 import org.apache.dolphinscheduler.dao.entity.User;
-import javax.servlet.http.HttpServletRequest;
+
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
+
 public interface Authenticator {
     /**
      * Verifying legality via username and password

+ 8 - 1
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/SecurityConfig.java

@@ -14,9 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.api.security;
 
-import org.apache.commons.lang.StringUtils;
+import org.apache.dolphinscheduler.api.security.impl.ldap.LdapAuthenticator;
+import org.apache.dolphinscheduler.api.security.impl.pwd.PasswordAuthenticator;
+import org.apache.dolphinscheduler.common.utils.StringUtils;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -58,6 +62,9 @@ public class SecurityConfig {
             case PASSWORD:
                 authenticator = new PasswordAuthenticator();
                 break;
+            case LDAP:
+                authenticator = new LdapAuthenticator();
+                break;
             default:
                 throw new IllegalStateException("Unexpected value: " + authenticationType);
         }

+ 25 - 10
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/PasswordAuthenticator.java

@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.api.security;
+
+package org.apache.dolphinscheduler.api.security.impl;
 
 import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.api.security.Authenticator;
 import org.apache.dolphinscheduler.api.service.SessionService;
 import org.apache.dolphinscheduler.api.service.UsersService;
 import org.apache.dolphinscheduler.api.utils.Result;
@@ -24,26 +26,38 @@ import org.apache.dolphinscheduler.common.Constants;
 import org.apache.dolphinscheduler.common.enums.Flag;
 import org.apache.dolphinscheduler.dao.entity.Session;
 import org.apache.dolphinscheduler.dao.entity.User;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import javax.servlet.http.HttpServletRequest;
-import java.util.Collections;
-import java.util.Map;
 
-public class PasswordAuthenticator implements Authenticator {
-    private static final Logger logger = LoggerFactory.getLogger(PasswordAuthenticator.class);
+public abstract class AbstractAuthenticator implements Authenticator {
+    private static final Logger logger = LoggerFactory.getLogger(AbstractAuthenticator.class);
 
     @Autowired
     private UsersService userService;
     @Autowired
     private SessionService sessionService;
 
+    /**
+     * user login and return user in db
+     *
+     * @param userId user identity field
+     * @param password user login password
+     * @param extra extra user login field
+     * @return user object in databse
+     */
+    public abstract User login(String userId, String password, String extra);
+
     @Override
-    public Result<Map<String, String>> authenticate(String username, String password, String extra) {
+    public Result<Map<String, String>> authenticate(String userId, String password, String extra) {
         Result<Map<String, String>> result = new Result<>();
-        // verify username and password
-        User user = userService.queryUser(username, password);
+        User user = login(userId, password, extra);
         if (user == null) {
             result.setCode(Status.USER_NAME_PASSWD_ERROR.getCode());
             result.setMsg(Status.USER_NAME_PASSWD_ERROR.getMsg());
@@ -64,7 +78,7 @@ public class PasswordAuthenticator implements Authenticator {
             result.setMsg(Status.LOGIN_SESSION_FAILED.getMsg());
             return result;
         }
-        logger.info("sessionId : {}" , sessionId);
+        logger.info("sessionId : {}", sessionId);
         result.setData(Collections.singletonMap(Constants.SESSION_ID, sessionId));
         result.setCode(Status.SUCCESS.getCode());
         result.setMsg(Status.LOGIN_SUCCESS.getMsg());
@@ -81,4 +95,5 @@ public class PasswordAuthenticator implements Authenticator {
         //get user object from session
         return userService.queryUser(session.getUserId());
     }
+
 }

+ 45 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticator.java

@@ -0,0 +1,45 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.ldap;
+
+import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
+import org.apache.dolphinscheduler.api.service.UsersService;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class LdapAuthenticator extends AbstractAuthenticator {
+    @Autowired
+    private UsersService usersService;
+    @Autowired
+    LdapService ldapService;
+
+    @Override
+    public User login(String userId, String password, String extra) {
+        User user = null;
+        String ldapEmail = ldapService.ldapLogin(userId, password);
+        if (ldapEmail != null) {
+            //check if user exist
+            user = usersService.getUserByUserName(userId);
+            if (user == null) {
+                user = usersService.createUser(ldapService.getUserType(userId), userId, ldapEmail);
+            }
+        }
+        return user;
+    }
+}

+ 133 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapService.java

@@ -0,0 +1,133 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.ldap;
+
+import org.apache.dolphinscheduler.common.enums.UserType;
+
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+@Component
+@Configuration
+public class LdapService {
+    private static final Logger logger = LoggerFactory.getLogger(LdapService.class);
+
+    @Value("${security.authentication.ldap.user.admin:null}")
+    private String adminUserId;
+
+    @Value("${ldap.urls:null}")
+    private String ldapUrls;
+
+    @Value("${ldap.base.dn:null}")
+    private String ldapBaseDn;
+
+    @Value("${ldap.username:null}")
+    private String ldapSecurityPrincipal;
+
+    @Value("${ldap.password:null}")
+    private String ldapPrincipalPassword;
+
+    @Value("${ldap.user.identity.attribute:null}")
+    private String ldapUserIdentifyingAttribute;
+
+    @Value("${ldap.user.email.attribute:null}")
+    private String ldapEmailAttribute;
+
+    /***
+     * get user type by configured admin userId
+     * @param userId login userId
+     * @return user type
+     */
+    public UserType getUserType(String userId) {
+        return adminUserId.equalsIgnoreCase(userId) ? UserType.ADMIN_USER : UserType.GENERAL_USER;
+    }
+
+    /**
+     * login by userId and return user email
+     *
+     * @param userId user identity id
+     * @param userPwd user login password
+     * @return user email
+     */
+    public String ldapLogin(String userId, String userPwd) {
+        Properties searchEnv = getManagerLdapEnv();
+        try {
+            //Connect to the LDAP server and Authenticate with a service user of whom we know the DN and credentials
+            LdapContext ctx = new InitialLdapContext(searchEnv, null);
+            SearchControls sc = new SearchControls();
+            sc.setReturningAttributes(new String[]{ldapEmailAttribute});
+            sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
+            String searchFilter = String.format("(%s=%s)", ldapUserIdentifyingAttribute, userId);
+            //Search for the user you want to authenticate, search him with some attribute
+            NamingEnumeration<SearchResult> results = ctx.search(ldapBaseDn, searchFilter, sc);
+            if (results.hasMore()) {
+                // get the users DN (distinguishedName) from the result
+                SearchResult result = results.next();
+                NamingEnumeration attrs = result.getAttributes().getAll();
+                while (attrs.hasMore()) {
+                    //Open another connection to the LDAP server with the found DN and the password
+                    searchEnv.put(Context.SECURITY_PRINCIPAL, result.getNameInNamespace());
+                    searchEnv.put(Context.SECURITY_CREDENTIALS, userPwd);
+                    try {
+                        new InitialDirContext(searchEnv);
+                    } catch (Exception e) {
+                        logger.warn("invalid ldap credentials or ldap search error", e);
+                        return null;
+                    }
+                    Attribute attr = (Attribute) attrs.next();
+                    if (attr.getID().equals(ldapEmailAttribute)) {
+                        return (String) attr.get();
+                    }
+                }
+            }
+        } catch (NamingException e) {
+            logger.error("ldap search error", e);
+            return null;
+        }
+        return null;
+    }
+
+    /***
+     * get ldap env fot ldap server search
+     * @return Properties
+     */
+    Properties getManagerLdapEnv() {
+        Properties env = new Properties();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        env.put(Context.SECURITY_PRINCIPAL, ldapSecurityPrincipal);
+        env.put(Context.SECURITY_CREDENTIALS, ldapPrincipalPassword);
+        env.put(Context.PROVIDER_URL, ldapUrls);
+        return env;
+    }
+}

+ 34 - 0
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/pwd/PasswordAuthenticator.java

@@ -0,0 +1,34 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.pwd;
+
+import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
+import org.apache.dolphinscheduler.api.service.UsersService;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class PasswordAuthenticator extends AbstractAuthenticator {
+    @Autowired
+    private UsersService userService;
+
+    @Override
+    public User login(String userId, String password, String extra) {
+        return userService.queryUser(userId, password);
+    }
+}

+ 127 - 69
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java

@@ -14,10 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.api.service;
 
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent;
 import org.apache.dolphinscheduler.api.dto.resources.visitor.ResourceTreeVisitor;
 import org.apache.dolphinscheduler.api.enums.Status;
@@ -29,20 +28,49 @@ import org.apache.dolphinscheduler.common.Constants;
 import org.apache.dolphinscheduler.common.enums.Flag;
 import org.apache.dolphinscheduler.common.enums.ResourceType;
 import org.apache.dolphinscheduler.common.enums.UserType;
-import org.apache.dolphinscheduler.common.utils.*;
-import org.apache.dolphinscheduler.dao.entity.*;
-import org.apache.dolphinscheduler.dao.mapper.*;
+import org.apache.dolphinscheduler.common.utils.CollectionUtils;
+import org.apache.dolphinscheduler.common.utils.EncryptionUtils;
+import org.apache.dolphinscheduler.common.utils.HadoopUtils;
+import org.apache.dolphinscheduler.common.utils.PropertyUtils;
+import org.apache.dolphinscheduler.common.utils.StringUtils;
+import org.apache.dolphinscheduler.dao.entity.AlertGroup;
+import org.apache.dolphinscheduler.dao.entity.DatasourceUser;
+import org.apache.dolphinscheduler.dao.entity.ProjectUser;
+import org.apache.dolphinscheduler.dao.entity.Resource;
+import org.apache.dolphinscheduler.dao.entity.ResourcesUser;
+import org.apache.dolphinscheduler.dao.entity.Tenant;
+import org.apache.dolphinscheduler.dao.entity.UDFUser;
+import org.apache.dolphinscheduler.dao.entity.User;
+import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper;
+import org.apache.dolphinscheduler.dao.mapper.DataSourceUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper;
+import org.apache.dolphinscheduler.dao.mapper.ProjectUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.ResourceMapper;
+import org.apache.dolphinscheduler.dao.mapper.ResourceUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.TenantMapper;
+import org.apache.dolphinscheduler.dao.mapper.UDFUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.UserMapper;
 import org.apache.dolphinscheduler.dao.utils.ResourceProcessDefinitionUtils;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.stream.Collectors;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 /**
  * user service
@@ -109,7 +137,7 @@ public class UsersService extends BaseService {
         String msg = this.checkUserParams(userName, userPassword, email, phone);
 
         if (!StringUtils.isEmpty(msg)) {
-            putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR,msg);
+            putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, msg);
             return result;
         }
         if (!isAdmin(loginUser)) {
@@ -126,12 +154,12 @@ public class UsersService extends BaseService {
 
         Tenant tenant = tenantMapper.queryById(tenantId);
         // resource upload startup
-        if (PropertyUtils.getResUploadStartupState()){
+        if (PropertyUtils.getResUploadStartupState()) {
             // if tenant not exists
-            if (!HadoopUtils.getInstance().exists(HadoopUtils.getHdfsTenantDir(tenant.getTenantCode()))){
+            if (!HadoopUtils.getInstance().exists(HadoopUtils.getHdfsTenantDir(tenant.getTenantCode()))) {
                 createTenantDirIfNotExists(tenant.getTenantCode());
             }
-            String userPath = HadoopUtils.getHdfsUserDir(tenant.getTenantCode(),user.getId());
+            String userPath = HadoopUtils.getHdfsUserDir(tenant.getTenantCode(), user.getId());
             HadoopUtils.getInstance().mkdir(userPath);
         }
 
@@ -142,12 +170,12 @@ public class UsersService extends BaseService {
 
     @Transactional(rollbackFor = RuntimeException.class)
     public User createUser(String userName,
-                                          String userPassword,
-                                          String email,
-                                          int tenantId,
-                                          String phone,
-                                          String queue,
-                                          int state) {
+                           String userPassword,
+                           String email,
+                           int tenantId,
+                           String phone,
+                           String queue,
+                           int state) {
         User user = new User();
         Date now = new Date();
 
@@ -161,7 +189,7 @@ public class UsersService extends BaseService {
         user.setUserType(UserType.GENERAL_USER);
         user.setCreateTime(now);
         user.setUpdateTime(now);
-        if (StringUtils.isEmpty(queue)){
+        if (StringUtils.isEmpty(queue)) {
             queue = "";
         }
         user.setQueue(queue);
@@ -171,8 +199,40 @@ public class UsersService extends BaseService {
         return user;
     }
 
+    /***
+     * create User for ldap login
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public User createUser(UserType userType, String userId, String email) {
+        User user = new User();
+        Date now = new Date();
+
+        user.setUserName(userId);
+        user.setEmail(email);
+        // create general users, administrator users are currently built-in
+        user.setUserType(userType);
+        user.setCreateTime(now);
+        user.setUpdateTime(now);
+        user.setQueue("");
+
+        // save user
+        userMapper.insert(user);
+        return user;
+    }
+
+    /**
+     * get user by user name
+     *
+     * @param userName user name
+     * @return exist user or null
+     */
+    public User getUserByUserName(String userName) {
+        return userMapper.queryByUserNameAccurately(userName);
+    }
+
     /**
      * query user by id
+     *
      * @param id id
      * @return user info
      */
@@ -182,6 +242,7 @@ public class UsersService extends BaseService {
 
     /**
      * query user
+     *
      * @param name name
      * @return user info
      */
@@ -203,6 +264,7 @@ public class UsersService extends BaseService {
 
     /**
      * get user id by user name
+     *
      * @param name user name
      * @return if name empty 0, user not exists -1, user exist user id
      */
@@ -242,7 +304,7 @@ public class UsersService extends BaseService {
         IPage<User> scheduleList = userMapper.queryUserPaging(page, searchVal);
 
         PageInfo<User> pageInfo = new PageInfo<>(pageNo, pageSize);
-        pageInfo.setTotalCount((int)scheduleList.getTotal());
+        pageInfo.setTotalCount((int) scheduleList.getTotal());
         pageInfo.setLists(scheduleList.getRecords());
         result.put(Constants.DATA_LIST, pageInfo);
         putMsg(result, Status.SUCCESS);
@@ -261,7 +323,7 @@ public class UsersService extends BaseService {
      * @param email email
      * @param tenantId tennat id
      * @param phone phone
-     * @param queue  queue
+     * @param queue queue
      * @return update result code
      * @throws Exception exception
      */
@@ -286,8 +348,8 @@ public class UsersService extends BaseService {
         }
         if (StringUtils.isNotEmpty(userName)) {
 
-            if (!CheckUtils.checkUserName(userName)){
-                putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR,userName);
+            if (!CheckUtils.checkUserName(userName)) {
+                putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, userName);
                 return result;
             }
 
@@ -300,23 +362,23 @@ public class UsersService extends BaseService {
         }
 
         if (StringUtils.isNotEmpty(userPassword)) {
-            if (!CheckUtils.checkPassword(userPassword)){
-                putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR,userPassword);
+            if (!CheckUtils.checkPassword(userPassword)) {
+                putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, userPassword);
                 return result;
             }
             user.setUserPassword(EncryptionUtils.getMd5(userPassword));
         }
 
         if (StringUtils.isNotEmpty(email)) {
-            if (!CheckUtils.checkEmail(email)){
-                putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR,email);
+            if (!CheckUtils.checkEmail(email)) {
+                putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, email);
                 return result;
             }
             user.setEmail(email);
         }
 
         if (StringUtils.isNotEmpty(phone) && !CheckUtils.checkPhone(phone)) {
-            putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR,phone);
+            putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, phone);
             return result;
         }
         user.setPhone(phone);
@@ -332,13 +394,13 @@ public class UsersService extends BaseService {
             Tenant newTenant = tenantMapper.queryById(tenantId);
             if (newTenant != null) {
                 // if hdfs startup
-                if (PropertyUtils.getResUploadStartupState() && oldTenant != null){
+                if (PropertyUtils.getResUploadStartupState() && oldTenant != null) {
                     String newTenantCode = newTenant.getTenantCode();
                     String oldResourcePath = HadoopUtils.getHdfsResDir(oldTenant.getTenantCode());
                     String oldUdfsPath = HadoopUtils.getHdfsUdfDir(oldTenant.getTenantCode());
 
                     // if old tenant dir exists
-                    if (HadoopUtils.getInstance().exists(oldResourcePath)){
+                    if (HadoopUtils.getInstance().exists(oldResourcePath)) {
                         String newResourcePath = HadoopUtils.getHdfsResDir(newTenantCode);
                         String newUdfsPath = HadoopUtils.getHdfsUdfDir(newTenantCode);
 
@@ -361,18 +423,18 @@ public class UsersService extends BaseService {
                         }
 
                         //Delete the user from the old tenant directory
-                        String oldUserPath = HadoopUtils.getHdfsUserDir(oldTenant.getTenantCode(),userId);
+                        String oldUserPath = HadoopUtils.getHdfsUserDir(oldTenant.getTenantCode(), userId);
                         HadoopUtils.getInstance().delete(oldUserPath, true);
-                    }else {
+                    } else {
                         // if old tenant dir not exists , create
                         createTenantDirIfNotExists(oldTenant.getTenantCode());
                     }
 
-                    if (HadoopUtils.getInstance().exists(HadoopUtils.getHdfsTenantDir(newTenant.getTenantCode()))){
+                    if (HadoopUtils.getInstance().exists(HadoopUtils.getHdfsTenantDir(newTenant.getTenantCode()))) {
                         //create user in the new tenant directory
-                        String newUserPath = HadoopUtils.getHdfsUserDir(newTenant.getTenantCode(),user.getId());
+                        String newUserPath = HadoopUtils.getHdfsUserDir(newTenant.getTenantCode(), user.getId());
                         HadoopUtils.getInstance().mkdir(newUserPath);
-                    }else {
+                    } else {
                         // if new tenant dir not exists , create
                         createTenantDirIfNotExists(newTenant.getTenantCode());
                     }
@@ -414,7 +476,7 @@ public class UsersService extends BaseService {
 
         if (user != null) {
             if (PropertyUtils.getResUploadStartupState()) {
-                String userPath = HadoopUtils.getHdfsUserDir(user.getTenantCode(),id);
+                String userPath = HadoopUtils.getHdfsUserDir(user.getTenantCode(), id);
                 if (HadoopUtils.getInstance().exists(userPath)) {
                     HadoopUtils.getInstance().delete(userPath, true);
                 }
@@ -493,7 +555,7 @@ public class UsersService extends BaseService {
             return result;
         }
         User user = userMapper.selectById(userId);
-        if(user == null){
+        if (user == null) {
             putMsg(result, Status.USER_NOT_EXIST, userId);
             return result;
         }
@@ -504,7 +566,7 @@ public class UsersService extends BaseService {
             // need authorize resource id set
             for (String resourceFullId : resourceFullIdArr) {
                 String[] resourceIdArr = resourceFullId.split("-");
-                for (int i=0;i<=resourceIdArr.length-1;i++) {
+                for (int i = 0; i <= resourceIdArr.length - 1; i++) {
                     int resourceIdValue = Integer.parseInt(resourceIdArr[i]);
                     needAuthorizeResIds.add(resourceIdValue);
                 }
@@ -531,7 +593,7 @@ public class UsersService extends BaseService {
             if (CollectionUtils.isNotEmpty(resourceIdSet)) {
                 logger.error("can't be deleted,because it is used of process definition");
                 for (Integer resId : resourceIdSet) {
-                    logger.error("resource id:{} is used of process definition {}",resId,resourceProcessMap.get(resId));
+                    logger.error("resource id:{} is used of process definition {}", resId, resourceProcessMap.get(resId));
                 }
                 putMsg(result, Status.RESOURCE_IS_USED);
                 return result;
@@ -558,7 +620,7 @@ public class UsersService extends BaseService {
             resourcesUser.setResourcesId(resourceIdValue);
             if (resource.isDirectory()) {
                 resourcesUser.setPerm(Constants.AUTHORIZE_READABLE_PERM);
-            }else{
+            } else {
                 resourcesUser.setPerm(Constants.AUTHORIZE_WRITABLE_PERM);
             }
 
@@ -591,7 +653,7 @@ public class UsersService extends BaseService {
             return result;
         }
         User user = userMapper.selectById(userId);
-        if(user == null){
+        if (user == null) {
             putMsg(result, Status.USER_NOT_EXIST, userId);
             return result;
         }
@@ -626,7 +688,7 @@ public class UsersService extends BaseService {
      *
      * @param loginUser login user
      * @param userId user id
-     * @param datasourceIds  data source id array
+     * @param datasourceIds data source id array
      * @return grant result code
      */
     @Transactional(rollbackFor = RuntimeException.class)
@@ -639,7 +701,7 @@ public class UsersService extends BaseService {
             return result;
         }
         User user = userMapper.selectById(userId);
-        if(user == null){
+        if (user == null) {
             putMsg(result, Status.USER_NOT_EXIST, userId);
             return result;
         }
@@ -738,7 +800,7 @@ public class UsersService extends BaseService {
             return result;
         }
 
-        List<User> userList = userMapper.selectList(null );
+        List<User> userList = userMapper.selectList(null);
         result.put(Constants.DATA_LIST, userList);
         putMsg(result, Status.SUCCESS);
 
@@ -782,7 +844,7 @@ public class UsersService extends BaseService {
             return result;
         }
 
-        List<User> userList = userMapper.selectList(null );
+        List<User> userList = userMapper.selectList(null);
         List<User> resultUsers = new ArrayList<>();
         Set<User> userSet = null;
         if (userList != null && userList.size() > 0) {
@@ -833,11 +895,6 @@ public class UsersService extends BaseService {
     }
 
     /**
-     *
-     * @param userName
-     * @param password
-     * @param email
-     * @param phone
      * @return if check failed return the field, otherwise return null
      */
     private String checkUserParams(String userName, String password, String email, String phone) {
@@ -862,35 +919,36 @@ public class UsersService extends BaseService {
 
     /**
      * copy resource files
+     *
      * @param resourceComponent resource component
-     * @param srcBasePath       src base path
-     * @param dstBasePath       dst base path
-     * @throws IOException      io exception
+     * @param srcBasePath src base path
+     * @param dstBasePath dst base path
+     * @throws IOException io exception
      */
     private void copyResourceFiles(ResourceComponent resourceComponent, String srcBasePath, String dstBasePath) throws IOException {
         List<ResourceComponent> components = resourceComponent.getChildren();
 
         if (CollectionUtils.isNotEmpty(components)) {
-            for (ResourceComponent component:components) {
+            for (ResourceComponent component : components) {
                 // verify whether exist
-                if (!HadoopUtils.getInstance().exists(String.format("%s/%s",srcBasePath,component.getFullName()))){
-                    logger.error("resource file: {} not exist,copy error",component.getFullName());
+                if (!HadoopUtils.getInstance().exists(String.format("%s/%s", srcBasePath, component.getFullName()))) {
+                    logger.error("resource file: {} not exist,copy error", component.getFullName());
                     throw new ServiceException(Status.RESOURCE_NOT_EXIST);
                 }
 
                 if (!component.isDirctory()) {
                     // copy it to dst
-                    HadoopUtils.getInstance().copy(String.format("%s/%s",srcBasePath,component.getFullName()),String.format("%s/%s",dstBasePath,component.getFullName()),false,true);
+                    HadoopUtils.getInstance().copy(String.format("%s/%s", srcBasePath, component.getFullName()), String.format("%s/%s", dstBasePath, component.getFullName()), false, true);
                     continue;
                 }
 
-                if(CollectionUtils.isEmpty(component.getChildren())) {
+                if (CollectionUtils.isEmpty(component.getChildren())) {
                     // if not exist,need create it
-                    if (!HadoopUtils.getInstance().exists(String.format("%s/%s",dstBasePath,component.getFullName()))) {
-                        HadoopUtils.getInstance().mkdir(String.format("%s/%s",dstBasePath,component.getFullName()));
+                    if (!HadoopUtils.getInstance().exists(String.format("%s/%s", dstBasePath, component.getFullName()))) {
+                        HadoopUtils.getInstance().mkdir(String.format("%s/%s", dstBasePath, component.getFullName()));
                     }
-                }else{
-                    copyResourceFiles(component,srcBasePath,dstBasePath);
+                } else {
+                    copyResourceFiles(component, srcBasePath, dstBasePath);
                 }
             }
         }
@@ -899,10 +957,10 @@ public class UsersService extends BaseService {
     /**
      * register user, default state is 0, default tenant_id is 1, no phone, no queue
      *
-     * @param userName       user name
-     * @param userPassword   user password
+     * @param userName user name
+     * @param userPassword user password
      * @param repeatPassword repeat password
-     * @param email          email
+     * @param email email
      * @return register result code
      * @throws Exception exception
      */
@@ -914,7 +972,7 @@ public class UsersService extends BaseService {
         String msg = this.checkUserParams(userName, userPassword, email, "");
 
         if (!StringUtils.isEmpty(msg)) {
-            putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR,msg);
+            putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, msg);
             return result;
         }
 
@@ -932,7 +990,7 @@ public class UsersService extends BaseService {
      * activate user, only system admin have permission, change user state code 0 to 1
      *
      * @param loginUser login user
-     * @param userName  user name
+     * @param userName user name
      * @return create result code
      */
     public Map<String, Object> activateUser(User loginUser, String userName) {
@@ -944,7 +1002,7 @@ public class UsersService extends BaseService {
             return result;
         }
 
-        if (!CheckUtils.checkUserName(userName)){
+        if (!CheckUtils.checkUserName(userName)) {
             putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, userName);
             return result;
         }

+ 13 - 1
dolphinscheduler-api/src/main/resources/application-api.properties

@@ -45,6 +45,18 @@ spring.messages.basename=i18n/messages
 # Authentication types (supported types: PASSWORD)
 security.authentication.type=PASSWORD
 
-
+#============================================================================
+# LDAP Config
+# mock ldap server from https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
+#============================================================================
+# admin userId
+#security.authentication.ldap.user.admin=read-only-admin
+# ldap server config
+#ldap.urls=ldap://ldap.forumsys.com:389/
+#ldap.base.dn=dc=example,dc=com
+#ldap.username=cn=read-only-admin,dc=example,dc=com
+#ldap.password=password
+#ldap.user.identity.attribute=uid
+#ldap.user.email.attribute=mail
 
 

+ 45 - 0
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/SecurityConfigLDAPTest.java

@@ -0,0 +1,45 @@
+/*
+ * 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.dolphinscheduler.api.security;
+
+import org.apache.dolphinscheduler.api.ApiApplicationServer;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = ApiApplicationServer.class)
+@TestPropertySource(properties = {
+        "security.authentication.type=LDAP",
+})
+public class SecurityConfigLDAPTest {
+
+    @Autowired
+    private SecurityConfig securityConfig;
+
+    @Test
+    public void testAuthenticator() {
+        Authenticator authenticator = securityConfig.authenticator();
+        Assert.assertNotNull(authenticator);
+    }
+}

+ 3 - 1
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/SecurityConfigTest.java

@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.api.security;
 
 import org.apache.dolphinscheduler.api.ApiApplicationServer;
+
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -30,7 +32,7 @@ import org.springframework.test.context.junit4.SpringRunner;
 @TestPropertySource(properties = {
         "security.authentication.type=PASSWORD",
 })
-public class SecurityConfigTest {
+public class SecurityConfigPasswordTest {
 
     @Autowired
     private SecurityConfig securityConfig;

+ 142 - 0
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticatorTest.java

@@ -0,0 +1,142 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.ldap;
+
+import static org.mockito.Mockito.when;
+
+import org.apache.dolphinscheduler.api.ApiApplicationServer;
+import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.api.service.SessionService;
+import org.apache.dolphinscheduler.api.service.UsersService;
+import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.common.enums.Flag;
+import org.apache.dolphinscheduler.common.enums.UserType;
+import org.apache.dolphinscheduler.dao.entity.Session;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import java.util.Date;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = ApiApplicationServer.class)
+@TestPropertySource(
+        properties = {
+                "security.authentication.type=LDAP",
+                "security.authentication.ldap.user.admin=read-only-admin",
+                "ldap.urls=ldap://ldap.forumsys.com:389/",
+                "ldap.base.dn=dc=example,dc=com",
+                "ldap.username=cn=read-only-admin,dc=example,dc=com",
+                "ldap.password=password",
+                "ldap.user.identity.attribute=uid",
+                "ldap.user.email.attribute=mail",
+        })
+public class LdapAuthenticatorTest {
+    private static Logger logger = LoggerFactory.getLogger(LdapAuthenticatorTest.class);
+    @Autowired
+    protected AutowireCapableBeanFactory beanFactory;
+    @MockBean
+    private LdapService ldapService;
+    @MockBean
+    private SessionService sessionService;
+    @MockBean
+    private UsersService usersService;
+
+    private LdapAuthenticator ldapAuthenticator;
+
+    //test param
+    private User mockUser;
+    private Session mockSession;
+
+    private String ldapUid = "test";
+    private String ldapUserPwd = "password";
+    private String ldapEmail = "test@example.com";
+    private String ip = "127.0.0.1";
+    private UserType userType = UserType.GENERAL_USER;
+
+    @Before
+    public void setUp() {
+        ldapAuthenticator = new LdapAuthenticator();
+        beanFactory.autowireBean(ldapAuthenticator);
+
+        mockUser = new User();
+        mockUser.setId(1);
+        mockUser.setUserName(ldapUid);
+        mockUser.setEmail(ldapEmail);
+        mockUser.setUserType(userType);
+        mockUser.setState(Flag.YES.getCode());
+
+        mockSession = new Session();
+        mockSession.setId(UUID.randomUUID().toString());
+        mockSession.setIp(ip);
+        mockSession.setUserId(1);
+        mockSession.setLastLoginTime(new Date());
+
+    }
+
+    @Test
+    public void testAuthenticate() {
+        when(usersService.createUser(userType, ldapUid, ldapEmail)).thenReturn(mockUser);
+        when(usersService.getUserByUserName(ldapUid)).thenReturn(mockUser);
+        when(sessionService.createSession(mockUser, ip)).thenReturn(mockSession.getId());
+
+        when(ldapService.ldapLogin(ldapUid, ldapUserPwd)).thenReturn(ldapEmail);
+
+        Result result = ldapAuthenticator.authenticate(ldapUid, ldapUserPwd, ip);
+        Assert.assertEquals(Status.SUCCESS.getCode(), (int) result.getCode());
+        logger.info(result.toString());
+
+        when(sessionService.createSession(mockUser, ip)).thenReturn(null);
+        result = ldapAuthenticator.authenticate(ldapUid, ldapUserPwd, ip);
+        Assert.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode());
+
+        when(sessionService.createSession(mockUser, ip)).thenReturn(mockSession.getId());
+        when(usersService.getUserByUserName(ldapUid)).thenReturn(null);
+        result = ldapAuthenticator.authenticate(ldapUid, ldapUserPwd, ip);
+        Assert.assertEquals(Status.USER_NAME_PASSWD_ERROR.getCode(), (int) result.getCode());
+    }
+
+    @Test
+    public void testGetAuthUser() {
+        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+        when(usersService.queryUser(mockUser.getId())).thenReturn(mockUser);
+        when(sessionService.getSession(request)).thenReturn(mockSession);
+
+        User user = ldapAuthenticator.getAuthUser(request);
+        Assert.assertNotNull(user);
+
+        when(sessionService.getSession(request)).thenReturn(null);
+        user = ldapAuthenticator.getAuthUser(request);
+        Assert.assertNull(user);
+    }
+}

+ 81 - 0
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapServiceTest.java

@@ -0,0 +1,81 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.ldap;
+
+import org.apache.dolphinscheduler.api.ApiApplicationServer;
+import org.apache.dolphinscheduler.common.enums.UserType;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = ApiApplicationServer.class)
+@TestPropertySource(
+        properties = {
+                "security.authentication.type=LDAP",
+                "security.authentication.ldap.user.admin=read-only-admin",
+                "ldap.urls=ldap://ldap.forumsys.com:389/",
+                "ldap.base.dn=dc=example,dc=com",
+                "ldap.username=cn=read-only-admin,dc=example,dc=com",
+                "ldap.password=password",
+                "ldap.user.identity.attribute=uid",
+                "ldap.user.email.attribute=mail",
+        })
+public class LdapServiceTest {
+    @Autowired
+    protected AutowireCapableBeanFactory beanFactory;
+
+    private LdapService ldapService;
+
+    @Before
+    public void setUp() {
+        ldapService = new LdapService();
+        beanFactory.autowireBean(ldapService);
+    }
+
+    @Test
+    public void getUserType() {
+        UserType userType = ldapService.getUserType("read-only-admin");
+        Assert.assertEquals(UserType.ADMIN_USER, userType);
+    }
+
+    @Test
+    public void ldapLogin() {
+        String email = ldapService.ldapLogin("tesla", "password");
+        Assert.assertEquals("tesla@ldap.forumsys.com", email);
+
+        String email2 = ldapService.ldapLogin("tesla", "error password");
+        Assert.assertNull(email2);
+    }
+
+    @Test
+    public void ldapLoginError() {
+        String email = ldapService.ldapLogin("tesla", "password");
+        Assert.assertEquals("tesla@ldap.forumsys.com", email);
+
+        String email2 = ldapService.ldapLogin("tesla", "error password");
+        Assert.assertNull(email2);
+    }
+}

+ 18 - 6
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/PasswordAuthenticatorTest.java

@@ -14,7 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.api.security;
+
+package org.apache.dolphinscheduler.api.security.impl.pwd;
+
+import static org.mockito.Mockito.when;
 
 import org.apache.dolphinscheduler.api.ApiApplicationServer;
 import org.apache.dolphinscheduler.api.enums.Status;
@@ -23,12 +26,17 @@ import org.apache.dolphinscheduler.api.service.UsersService;
 import org.apache.dolphinscheduler.api.utils.Result;
 import org.apache.dolphinscheduler.dao.entity.Session;
 import org.apache.dolphinscheduler.dao.entity.User;
+
+import java.util.Date;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
-import static org.mockito.Mockito.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -36,9 +44,6 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.test.context.junit4.SpringRunner;
-import javax.servlet.http.HttpServletRequest;
-import java.util.Date;
-import java.util.UUID;
 
 @RunWith(SpringRunner.class)
 @SpringBootTest(classes = ApiApplicationServer.class)
@@ -58,7 +63,7 @@ public class PasswordAuthenticatorTest {
     private Session mockSession;
 
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         authenticator = new PasswordAuthenticator();
         beanFactory.autowireBean(authenticator);
 
@@ -76,6 +81,13 @@ public class PasswordAuthenticatorTest {
         mockSession.setLastLoginTime(new Date());
     }
 
+    @Test
+    public void testLogin() {
+        when(usersService.queryUser("test", "test")).thenReturn(mockUser);
+        User login = authenticator.login("test", "test", "127.0.0.1");
+        Assert.assertNotNull(login);
+    }
+
     @Test
     public void testAuthenticate() {
         when(usersService.queryUser("test", "test")).thenReturn(mockUser);

+ 77 - 85
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java

@@ -14,10 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.api.service;
 
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
 import org.apache.dolphinscheduler.api.enums.Status;
 import org.apache.dolphinscheduler.api.utils.PageInfo;
 import org.apache.dolphinscheduler.api.utils.Result;
@@ -29,7 +32,19 @@ import org.apache.dolphinscheduler.common.utils.EncryptionUtils;
 import org.apache.dolphinscheduler.dao.entity.Resource;
 import org.apache.dolphinscheduler.dao.entity.Tenant;
 import org.apache.dolphinscheduler.dao.entity.User;
-import org.apache.dolphinscheduler.dao.mapper.*;
+import org.apache.dolphinscheduler.dao.mapper.AlertGroupMapper;
+import org.apache.dolphinscheduler.dao.mapper.DataSourceUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.ProjectUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.ResourceMapper;
+import org.apache.dolphinscheduler.dao.mapper.ResourceUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.TenantMapper;
+import org.apache.dolphinscheduler.dao.mapper.UDFUserMapper;
+import org.apache.dolphinscheduler.dao.mapper.UserMapper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -42,13 +57,8 @@ import org.mockito.junit.MockitoJUnitRunner;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 @RunWith(MockitoJUnitRunner.class)
 public class UsersServiceTest {
@@ -73,30 +83,34 @@ public class UsersServiceTest {
     @Mock
     private ResourceMapper resourceMapper;
 
-    private String queueName ="UsersServiceTestQueue";
-
+    private String queueName = "UsersServiceTestQueue";
 
     @Before
-    public void before(){
-
-
+    public void before() {
     }
+
     @After
-    public  void after(){
+    public void after() {
 
     }
 
-
     @Test
-    public void testCreateUser(){
+    public void testCreateUserForLdap() {
+        String userName = "user1";
+        String email = "user1@ldap.com";
+        User user = usersService.createUser(UserType.ADMIN_USER, userName, email);
+        Assert.assertNotNull(user);
+    }
 
+    @Test
+    public void testCreateUser() {
         User user = new User();
         user.setUserType(UserType.ADMIN_USER);
         String userName = "userTest0001~";
         String userPassword = "userTest";
         String email = "123@qq.com";
         int tenantId = Integer.MAX_VALUE;
-        String phone= "13456432345";
+        String phone = "13456432345";
         int state = 1;
         try {
             //userName error
@@ -119,7 +133,7 @@ public class UsersServiceTest {
             Assert.assertEquals(Status.REQUEST_PARAMS_NOT_VALID_ERROR, result.get(Constants.STATUS));
 
             email = "122222@qq.com";
-            phone ="2233";
+            phone = "2233";
             //phone error
             result = usersService.createUser(user, userName, userPassword, email, tenantId, phone, queueName, state);
             logger.info(result.toString());
@@ -137,20 +151,19 @@ public class UsersServiceTest {
             Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
 
         } catch (Exception e) {
-            logger.error(Status.CREATE_USER_ERROR.getMsg(),e);
+            logger.error(Status.CREATE_USER_ERROR.getMsg(), e);
             Assert.assertTrue(false);
         }
     }
 
     @Test
-    public void testQueryUser(){
-
+    public void testQueryUser() {
         String userName = "userTest0001";
         String userPassword = "userTest0001";
-        when(userMapper.queryUserByNamePassword(userName,EncryptionUtils.getMd5(userPassword))).thenReturn(getGeneralUser());
-        User queryUser = usersService.queryUser(userName,  userPassword);
+        when(userMapper.queryUserByNamePassword(userName, EncryptionUtils.getMd5(userPassword))).thenReturn(getGeneralUser());
+        User queryUser = usersService.queryUser(userName, userPassword);
         logger.info(queryUser.toString());
-        Assert.assertTrue(queryUser!=null);
+        Assert.assertTrue(queryUser != null);
     }
 
     @Test
@@ -176,11 +189,8 @@ public class UsersServiceTest {
     }
 
 
-
     @Test
-    public void testQueryUserList(){
-
-
+    public void testQueryUserList() {
         User user = new User();
 
         //no operate
@@ -190,37 +200,34 @@ public class UsersServiceTest {
 
         //success
         user.setUserType(UserType.ADMIN_USER);
-        when(userMapper.selectList(null )).thenReturn(getUserList());
+        when(userMapper.selectList(null)).thenReturn(getUserList());
         result = usersService.queryUserList(user);
         List<User> userList = (List<User>) result.get(Constants.DATA_LIST);
-        Assert.assertTrue(userList.size()>0);
+        Assert.assertTrue(userList.size() > 0);
     }
 
     @Test
-    public void testQueryUserListPage(){
-
-
+    public void testQueryUserListPage() {
         User user = new User();
-        IPage<User> page = new Page<>(1,10);
+        IPage<User> page = new Page<>(1, 10);
         page.setRecords(getUserList());
         when(userMapper.queryUserPaging(any(Page.class), eq("userTest"))).thenReturn(page);
 
         //no operate
-        Map<String, Object> result = usersService.queryUserList(user,"userTest",1,10);
+        Map<String, Object> result = usersService.queryUserList(user, "userTest", 1, 10);
         logger.info(result.toString());
         Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
 
         //success
         user.setUserType(UserType.ADMIN_USER);
-        result = usersService.queryUserList(user,"userTest",1,10);
+        result = usersService.queryUserList(user, "userTest", 1, 10);
         Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
-        PageInfo<User> pageInfo  = (PageInfo<User>) result.get(Constants.DATA_LIST);
-        Assert.assertTrue(pageInfo.getLists().size()>0);
+        PageInfo<User> pageInfo = (PageInfo<User>) result.get(Constants.DATA_LIST);
+        Assert.assertTrue(pageInfo.getLists().size() > 0);
     }
 
     @Test
-    public void testUpdateUser(){
-
+    public void testUpdateUser() {
         String userName = "userTest0001";
         String userPassword = "userTest0001";
         try {
@@ -235,48 +242,46 @@ public class UsersServiceTest {
             logger.info(result.toString());
             Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
         } catch (Exception e) {
-            logger.error("update user error",e);
+            logger.error("update user error", e);
             Assert.assertTrue(false);
         }
     }
 
     @Test
-    public void testDeleteUserById(){
-
+    public void testDeleteUserById() {
         User loginUser = new User();
         try {
             when(userMapper.queryTenantCodeByUserId(1)).thenReturn(getUser());
             when(userMapper.selectById(1)).thenReturn(getUser());
 
             //no operate
-            Map<String, Object> result = usersService.deleteUserById(loginUser,3);
+            Map<String, Object> result = usersService.deleteUserById(loginUser, 3);
             logger.info(result.toString());
             Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
 
             // user not exist
             loginUser.setUserType(UserType.ADMIN_USER);
-            result = usersService.deleteUserById(loginUser,3);
+            result = usersService.deleteUserById(loginUser, 3);
             logger.info(result.toString());
             Assert.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS));
 
             //success
-            result = usersService.deleteUserById(loginUser,1);
+            result = usersService.deleteUserById(loginUser, 1);
             logger.info(result.toString());
             Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
         } catch (Exception e) {
-           logger.error("delete user error",e);
-           Assert.assertTrue(false);
+            logger.error("delete user error", e);
+            Assert.assertTrue(false);
         }
 
 
     }
 
     @Test
-    public void testGrantProject(){
-
+    public void testGrantProject() {
         when(userMapper.selectById(1)).thenReturn(getUser());
         User loginUser = new User();
-        String  projectIds= "100000,120000";
+        String projectIds = "100000,120000";
         Map<String, Object> result = usersService.grantProject(loginUser, 1, projectIds);
         logger.info(result.toString());
         Assert.assertEquals(Status.USER_NO_OPERATION_PERM, result.get(Constants.STATUS));
@@ -292,8 +297,7 @@ public class UsersServiceTest {
     }
 
     @Test
-    public void testGrantResources(){
-
+    public void testGrantResources() {
         String resourceIds = "100000,120000";
         when(userMapper.selectById(1)).thenReturn(getUser());
         User loginUser = new User();
@@ -317,8 +321,7 @@ public class UsersServiceTest {
 
 
     @Test
-    public void testGrantUDFFunction(){
-
+    public void testGrantUDFFunction() {
         String udfIds = "100000,120000";
         when(userMapper.selectById(1)).thenReturn(getUser());
         User loginUser = new User();
@@ -337,8 +340,7 @@ public class UsersServiceTest {
     }
 
     @Test
-    public void testGrantDataSource(){
-
+    public void testGrantDataSource() {
         String datasourceIds = "100000,120000";
         when(userMapper.selectById(1)).thenReturn(getUser());
         User loginUser = new User();
@@ -365,8 +367,7 @@ public class UsersServiceTest {
     }
 
     @Test
-    public void getUserInfo(){
-
+    public void getUserInfo() {
         User loginUser = new User();
         loginUser.setUserName("admin");
         loginUser.setUserType(UserType.ADMIN_USER);
@@ -376,7 +377,7 @@ public class UsersServiceTest {
         Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
         User tempUser = (User) result.get(Constants.DATA_LIST);
         //check userName
-        Assert.assertEquals("admin",tempUser.getUserName());
+        Assert.assertEquals("admin", tempUser.getUserName());
 
         //get general user
         loginUser.setUserType(null);
@@ -387,13 +388,12 @@ public class UsersServiceTest {
         Assert.assertEquals(Status.SUCCESS, result.get(Constants.STATUS));
         tempUser = (User) result.get(Constants.DATA_LIST);
         //check userName
-        Assert.assertEquals("userTest0001",tempUser.getUserName());
+        Assert.assertEquals("userTest0001", tempUser.getUserName());
     }
 
 
     @Test
-    public void testQueryAllGeneralUsers(){
-
+    public void testQueryAllGeneralUsers() {
         User loginUser = new User();
         //no operate
         Map<String, Object> result = usersService.queryAllGeneralUsers(loginUser);
@@ -410,8 +410,7 @@ public class UsersServiceTest {
     }
 
     @Test
-    public void testVerifyUserName(){
-
+    public void testVerifyUserName() {
         //not exist user
         Result result = usersService.verifyUserName("admin89899");
         logger.info(result.toString());
@@ -424,11 +423,10 @@ public class UsersServiceTest {
     }
 
     @Test
-    public void testUnauthorizedUser(){
-
+    public void testUnauthorizedUser() {
         User loginUser = new User();
-        when(userMapper.selectList(null )).thenReturn(getUserList());
-        when( userMapper.queryUserListByAlertGroupId(2)).thenReturn(getUserList());
+        when(userMapper.selectList(null)).thenReturn(getUserList());
+        when(userMapper.queryUserListByAlertGroupId(2)).thenReturn(getUserList());
         //no operate
         Map<String, Object> result = usersService.unauthorizedUser(loginUser, 2);
         logger.info(result.toString());
@@ -442,8 +440,7 @@ public class UsersServiceTest {
 
 
     @Test
-    public void testAuthorizedUser(){
-
+    public void testAuthorizedUser() {
         User loginUser = new User();
         when(userMapper.queryUserListByAlertGroupId(2)).thenReturn(getUserList());
         //no operate
@@ -571,10 +568,8 @@ public class UsersServiceTest {
 
     /**
      * get disabled user
-     * @return
      */
     private User getDisabledUser() {
-
         User user = new User();
         user.setUserType(UserType.GENERAL_USER);
         user.setUserName("userTest0001");
@@ -586,10 +581,8 @@ public class UsersServiceTest {
 
     /**
      * get user
-     * @return
      */
-    private User getGeneralUser(){
-
+    private User getGeneralUser() {
         User user = new User();
         user.setUserType(UserType.GENERAL_USER);
         user.setUserName("userTest0001");
@@ -597,8 +590,7 @@ public class UsersServiceTest {
         return user;
     }
 
-
-    private List<User> getUserList(){
+    private List<User> getUserList() {
         List<User> userList = new ArrayList<>();
         userList.add(getGeneralUser());
         return userList;
@@ -607,8 +599,7 @@ public class UsersServiceTest {
     /**
      * get user
      */
-    private User getUser(){
-
+    private User getUser() {
         User user = new User();
         user.setUserType(UserType.ADMIN_USER);
         user.setUserName("userTest0001");
@@ -619,9 +610,10 @@ public class UsersServiceTest {
 
     /**
      * get tenant
+     *
      * @return tenant
      */
-    private Tenant getTenant(){
+    private Tenant getTenant() {
         Tenant tenant = new Tenant();
         tenant.setId(1);
         return tenant;
@@ -629,10 +621,10 @@ public class UsersServiceTest {
 
     /**
      * get resource
+     *
      * @return resource
      */
-    private Resource getResource(){
-
+    private Resource getResource() {
         Resource resource = new Resource();
         resource.setPid(-1);
         resource.setUserId(1);

+ 4 - 2
pom.xml

@@ -725,8 +725,10 @@
                         <include>**/api/exceptions/ApiExceptionHandlerTest.java</include>
                         <include>**/api/exceptions/ServiceExceptionTest.java</include>
                         <include>**/api/interceptor/LoginHandlerInterceptorTest.java</include>
-                        <include>**/api/security/PasswordAuthenticatorTest.java</include>
-                        <include>**/api/security/SecurityConfigTest.java</include>
+                        <include>**/api/security/impl/pwd/PasswordAuthenticatorTest.java</include>
+                        <include>**/api/security/impl/ldap/LdapAuthenticatorTest.java</include>
+                        <include>**/api/security/SecurityConfigLDAPTest.java</include>
+                        <include>**/api/security/SecurityConfigPasswordTest.java</include>
                         <include>**/api/service/AccessTokenServiceTest.java</include>
                         <include>**/api/service/AlertGroupServiceTest.java</include>
                         <include>**/api/service/BaseDAGServiceTest.java</include>