This commit is contained in:
break60 2020-12-24 17:55:46 +08:00
commit 0f2684c00b
25 changed files with 889 additions and 261 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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);
}

View File

@ -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 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);
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -19,7 +19,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.apache.dolphinscheduler.dao.mapper.AccessTokenMapper">
<select id="selectAccessTokenPage" resultType="org.apache.dolphinscheduler.dao.entity.AccessToken">
select t.id, t.user_id, t.token, t.expire_time, t.create_time, t.update_time
select t.id, t.user_id, t.token, t.expire_time, t.create_time, t.update_time, u.user_name
from t_ds_access_token t
left join t_ds_user u on t.user_id = u.id
where 1 = 1

View File

@ -18,82 +18,97 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.apache.dolphinscheduler.dao.mapper.UdfFuncMapper">
<sql id="baseSql">
${alias}.id, ${alias}.user_id, ${alias}.func_name, ${alias}.class_name, ${alias}.type, ${alias}.arg_types,
${alias}.database, ${alias}.description, ${alias}.resource_id, ${alias}.resource_name, ${alias}.create_time, ${alias}.update_time
</sql>
<select id="selectUdfById" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
id, user_id, func_name, class_name, type, arg_types,
database, description, resource_id, resource_name, create_time, update_time
from t_ds_udfs
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where id = #{id}
</select>
<select id="queryUdfByIdStr" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
id, user_id, func_name, class_name, type, arg_types,
database, description, resource_id, resource_name, create_time, update_time
from t_ds_udfs
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where 1 = 1
<if test="ids != null and ids != ''">
and id in
and udf.id in
<foreach collection="ids" item="i" open="(" close=")" separator=",">
#{i}
</foreach>
</if>
<if test="funcNames != null and funcNames != ''">
and func_name = #{funcNames}
and udf.func_name = #{funcNames}
</if>
order by id asc
order by udf.id asc
</select>
<select id="queryUdfFuncPaging" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select id, user_id, func_name, class_name, type, arg_types, database, description, resource_id, resource_name, create_time, update_time
from t_ds_udfs
select
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where 1=1
<if test="searchVal!= null and searchVal != ''">
and func_name like concat('%', #{searchVal}, '%')
and udf.func_name like concat('%', #{searchVal}, '%')
</if>
<if test="userId != 0">
and id in (
and udf.id in (
select udf_id from t_ds_relation_udfs_user where user_id=#{userId}
union select id as udf_id from t_ds_udfs where user_id=#{userId})
</if>
order by create_time desc
order by udf.create_time desc
</select>
<select id="getUdfFuncByType" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
id, user_id, func_name, class_name, type, arg_types,
database, description, resource_id, resource_name, create_time, update_time
from t_ds_udfs
where type=#{type}
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where udf.type=#{type}
<if test="userId != 0">
and id in (
and udf.id in (
select udf_id from t_ds_relation_udfs_user where user_id=#{userId}
union select id as udf_id from t_ds_udfs where user_id=#{userId})
</if>
</select>
<select id="queryUdfFuncExceptUserId" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
id, user_id, func_name, class_name, type, arg_types,
database, description, resource_id, resource_name, create_time, update_time
from t_ds_udfs
where user_id <![CDATA[ <> ]]> #{userId}
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where udf.user_id <![CDATA[ <> ]]> #{userId}
</select>
<select id="queryAuthedUdfFunc" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
SELECT
u.id, u.user_id, u.func_name, u.class_name, u.type, u.arg_types,
u.database, u.description, u.resource_id, u.resource_name, u.create_time, u.update_time
from t_ds_udfs u,t_ds_relation_udfs_user rel
WHERE u.id = rel.udf_id
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf,t_ds_relation_udfs_user rel
WHERE udf.id = rel.udf_id
AND rel.user_id = #{userId}
</select>
<select id="listAuthorizedUdfFunc" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
u.id, u.user_id, u.func_name, u.class_name, u.type, u.arg_types,
u.database, u.description, u.resource_id, u.resource_name, u.create_time, u.update_time
from t_ds_udfs u
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where
id in (select udf_id from t_ds_relation_udfs_user where user_id=#{userId}
udf.id in (select udf_id from t_ds_relation_udfs_user where user_id=#{userId}
union select id as udf_id from t_ds_udfs where user_id=#{userId})
<if test="udfIds != null and udfIds != ''">
and id in
and udf.id in
<foreach collection="udfIds" item="i" open="(" close=")" separator=",">
#{i}
</foreach>
@ -101,12 +116,13 @@
</select>
<select id="listUdfByResourceId" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
u.id, u.user_id, u.func_name, u.class_name, u.type, u.arg_types,
u.database, u.description, u.resource_id, u.resource_name, u.create_time, u.update_time
from t_ds_udfs u
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where 1=1
<if test="resourceIds != null and resourceIds != ''">
and resource_id in
and udf.resource_id in
<foreach collection="resourceIds" item="i" open="(" close=")" separator=",">
#{i}
</foreach>
@ -114,14 +130,15 @@
</select>
<select id="listAuthorizedUdfByResourceId" resultType="org.apache.dolphinscheduler.dao.entity.UdfFunc">
select
u.id, u.user_id, u.func_name, u.class_name, u.type, u.arg_types,
u.database, u.description, u.resource_id, u.resource_name, u.create_time, u.update_time
from t_ds_udfs u
<include refid="baseSql">
<property name="alias" value="udf"/>
</include>
from t_ds_udfs udf
where
id in (select udf_id from t_ds_relation_udfs_user where user_id=#{userId}
udf.id in (select udf_id from t_ds_relation_udfs_user where user_id=#{userId}
union select id as udf_id from t_ds_udfs where user_id=#{userId})
<if test="resourceIds != null and resourceIds != ''">
and resource_id in
and udf.resource_id in
<foreach collection="resourceIds" item="i" open="(" close=")" separator=",">
#{i}
</foreach>

View File

@ -0,0 +1,30 @@
/*
* 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.server.utils;
public class ArgsUtils {
private ArgsUtils() throws IllegalStateException {
throw new IllegalStateException("Utility class");
}
public static String escape(String arg) {
return arg.replace(" ", "\\ ").replace("\"", "\\\"").replace("'", "\\'");
}
}

View File

@ -14,8 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.server.utils;
package org.apache.dolphinscheduler.server.utils;
import org.apache.commons.lang.StringUtils;
import org.apache.dolphinscheduler.common.Constants;
@ -26,7 +26,6 @@ import org.apache.dolphinscheduler.common.task.flink.FlinkParameters;
import java.util.ArrayList;
import java.util.List;
/**
* flink args utils
*/
@ -62,7 +61,7 @@ public class FlinkArgsUtils {
String appName = param.getAppName();
if (StringUtils.isNotEmpty(appName)) { //-ynm
args.add(Constants.FLINK_APP_NAME);
args.add(appName);
args.add(ArgsUtils.escape(appName));
}
// judge flink version,from flink1.10,the parameter -yn removed

View File

@ -25,8 +25,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import javax.transaction.NotSupportedException;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.sift.SiftingAppender;
@ -35,8 +33,8 @@ import ch.qos.logback.core.spi.AppenderAttachable;
public class LogUtils {
private LogUtils() throws NotSupportedException {
throw new NotSupportedException();
private LogUtils() throws IllegalStateException {
throw new IllegalStateException("Utility class");
}
/**

View File

@ -30,9 +30,6 @@ import org.apache.dolphinscheduler.remote.utils.Host;
import org.apache.dolphinscheduler.server.entity.TaskExecutionContext;
import org.apache.dolphinscheduler.service.log.LogClientService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@ -40,21 +37,28 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* mainly used to get the start command line of a process.
*/
public class ProcessUtils {
/**
* logger.
* logger
*/
private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);
private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);
/**
* Initialization regularization, solve the problem of pre-compilation performance,
* avoid the thread safety problem of multi-thread operation.
* avoid the thread safety problem of multi-thread operation
*/
private static final Pattern MACPATTERN = Pattern.compile("-[+|-]-\\s(\\d+)");
/**
* Expression of PID recognition in Windows scene
*/
private static final Pattern WINDOWSATTERN = Pattern.compile("(\\d+)");
private static final String LOCAL_PROCESS_EXEC = "jdk.lang.Process.allowAmbiguousCommands";
@ -79,7 +83,7 @@ public class ProcessUtils {
}
cmdstr = createCommandLine(
VERIFICATION_LEGACY, executablePath, cmd);
VERIFICATION_LEGACY, executablePath, cmd);
} else {
String executablePath;
try {
@ -102,7 +106,7 @@ public class ProcessUtils {
cmdstr = createCommandLine(
isShellFile(executablePath) ? VERIFICATION_CMD_BAT : VERIFICATION_WIN32, quoteString(executablePath), cmd);
isShellFile(executablePath) ? VERIFICATION_CMD_BAT : VERIFICATION_WIN32, quoteString(executablePath), cmd);
}
return cmdstr;
}
@ -211,8 +215,8 @@ public class ProcessUtils {
* create command line.
*
* @param verificationType verification type
* @param executablePath executable path
* @param cmd cmd
* @param executablePath executable path
* @param cmd cmd
* @return command line
*/
private static String createCommandLine(int verificationType, final String executablePath, final String[] cmd) {
@ -241,8 +245,8 @@ public class ProcessUtils {
* whether is quoted.
*
* @param noQuotesInside no quotes inside
* @param arg arg
* @param errorMessage error message
* @param arg arg
* @param errorMessage error message
* @return boolean
*/
private static boolean isQuoted(boolean noQuotesInside, String arg, String errorMessage) {
@ -266,7 +270,7 @@ public class ProcessUtils {
* whether needs escaping.
*
* @param verificationType verification type
* @param arg arg
* @param arg arg
* @return boolean
*/
private static boolean needsEscaping(int verificationType, String arg) {
@ -287,9 +291,9 @@ public class ProcessUtils {
/**
* kill yarn application.
*
* @param appIds app id list
* @param logger logger
* @param tenantCode tenant code
* @param appIds app id list
* @param logger logger
* @param tenantCode tenant code
* @param executePath execute path
*/
public static void cancelApplication(List<String> appIds, Logger logger, String tenantCode, String executePath) {
@ -301,7 +305,7 @@ public class ProcessUtils {
if (!applicationStatus.typeIsFinished()) {
String commandFile = String
.format("%s/%s.kill", executePath, appId);
.format("%s/%s.kill", executePath, appId);
String cmd = "yarn application -kill " + appId;
execYarnKillCommand(logger, tenantCode, appId, commandFile, cmd);
}
@ -315,11 +319,11 @@ public class ProcessUtils {
/**
* build kill command for yarn application
*
* @param logger logger
* @param tenantCode tenant code
* @param appId app id
* @param logger logger
* @param tenantCode tenant code
* @param appId app id
* @param commandFile command file
* @param cmd cmd
* @param cmd cmd
*/
private static void execYarnKillCommand(Logger logger, String tenantCode, String appId, String commandFile, String cmd) {
try {
@ -361,7 +365,7 @@ public class ProcessUtils {
int processId = taskExecutionContext.getProcessId();
if (processId == 0) {
logger.error("process kill failed, process id :{}, task id:{}",
processId, taskExecutionContext.getTaskInstanceId());
processId, taskExecutionContext.getTaskInstanceId());
return;
}
@ -422,8 +426,8 @@ public class ProcessUtils {
try {
logClient = new LogClientService();
log = logClient.viewLog(Host.of(taskExecutionContext.getHost()).getIp(),
Constants.RPC_PORT,
taskExecutionContext.getLogPath());
Constants.RPC_PORT,
taskExecutionContext.getLogPath());
} finally {
if (logClient != null) {
logClient.close();

View File

@ -89,7 +89,7 @@
type="input"
size="small"
v-model="appName"
:placeholder="$t('Please enter the job name of Flink')"
:placeholder="$t('Please enter the job name of Flink(optional)')"
style="width: 200px;">
</el-input>
</span>
@ -303,11 +303,6 @@
return false
}
if (!this.appName) {
this.$message.warning(`${i18n.$t('Please enter the job name of Flink')}`)
return false
}
if (!this.jobManagerMemory) {
this.$message.warning(`${i18n.$t('Please enter jobManager memory')}`)
return false
@ -353,9 +348,9 @@
flinkVersion: this.flinkVersion,
slot: this.slot,
taskManager: this.taskManager,
appName: this.appName,
jobManagerMemory: this.jobManagerMemory,
taskManagerMemory: this.taskManagerMemory,
appName: this.appName,
mainArgs: this.mainArgs,
others: this.others,
programType: this.programType
@ -489,9 +484,9 @@
localParams: this.localParams,
slot: this.slot,
taskManager: this.taskManager,
appName: this.appName,
jobManagerMemory: this.jobManagerMemory,
taskManagerMemory: this.taskManagerMemory,
appName: this.appName,
mainArgs: this.mainArgs,
others: this.others,
programType: this.programType
@ -522,7 +517,7 @@
this.taskManager = o.params.taskManager || '2'
this.jobManagerMemory = o.params.jobManagerMemory || '1G'
this.taskManagerMemory = o.params.taskManagerMemory || '2G'
this.appName = o.params.appName || ''
this.mainArgs = o.params.mainArgs || ''
this.others = o.params.others
this.programType = o.params.programType || 'SCALA'

View File

@ -108,7 +108,7 @@ export default {
'Driver memory use': 'Driver memory use',
'Please enter driver memory use': 'Please enter driver memory use',
'Number of Executors': 'Number of Executors',
'Please enter the job name of Flink': 'Please enter the job name of Flink',
'Please enter the job name of Flink(optional)': 'Please enter the job name of Flink(optional)',
'Please enter the number of Executor': 'Please enter the number of Executor',
'Executor memory': 'Executor memory',
'Please enter the Executor memory': 'Please enter the Executor memory',

View File

@ -106,7 +106,7 @@ export default {
'Driver memory use': 'Driver内存数',
'Please enter driver memory use': '请输入Driver内存数',
'Number of Executors': 'Executor数量',
'Please enter the job name of Flink': '请输入Flink任务名称',
'Please enter the job name of Flink(optional)': '请输入Flink任务名称(选填)',
'Please enter the number of Executor': '请输入Executor数量',
'Executor memory': 'Executor内存数',
'Please enter the Executor memory': '请输入Executor内存数',

View File

@ -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>