Browse Source

Expire session when update user password (#15219)

3.2.1-prepare
Wenjun Ruan 1 year ago committed by GitHub
parent
commit
12f8138167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/LoginController.java
  2. 28
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UsersController.java
  3. 7
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/Authenticator.java
  4. 29
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/AbstractAuthenticator.java
  5. 4
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticator.java
  6. 2
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/pwd/PasswordAuthenticator.java
  7. 4
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/sso/CasdoorAuthenticator.java
  8. 33
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/SessionService.java
  9. 26
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java
  10. 116
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SessionServiceImpl.java
  11. 99
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java
  12. 13
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AbstractControllerTest.java
  13. 9
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticatorTest.java
  14. 9
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/pwd/PasswordAuthenticatorTest.java
  15. 6
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/sso/CasdoorAuthenticatorTest.java
  16. 45
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/SessionServiceTest.java
  17. 51
      dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java
  18. 99
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Session.java
  19. 22
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/SessionMapper.java
  20. 17
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/BaseDao.java
  21. 10
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/IDao.java
  22. 30
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/SessionDao.java
  23. 49
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/SessionDaoImpl.java
  24. 37
      dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/SessionMapper.xml
  25. 5
      dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/SessionMapperTest.java

17
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/LoginController.java

@ -34,6 +34,7 @@ import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.common.enums.UserType; import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.common.utils.OkHttpUtils; import org.apache.dolphinscheduler.common.utils.OkHttpUtils;
import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -160,20 +161,13 @@ public class LoginController extends BaseController {
return Result.success(); return Result.success();
} }
/**
* sign out
*
* @param loginUser login user
* @param request request
* @return sign out result
*/
@Operation(summary = "signOut", description = "SIGNOUT_NOTES") @Operation(summary = "signOut", description = "SIGNOUT_NOTES")
@PostMapping(value = "/signOut") @PostMapping(value = "/signOut")
@ApiException(SIGN_OUT_ERROR) @ApiException(SIGN_OUT_ERROR)
public Result signOut(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result signOut(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
HttpServletRequest request) { HttpServletRequest request) {
String ip = getClientIpAddress(request); String ip = getClientIpAddress(request);
sessionService.signOut(ip, loginUser); sessionService.expireSession(loginUser.getId());
// clear session // clear session
request.removeAttribute(Constants.SESSION_USER); request.removeAttribute(Constants.SESSION_USER);
return success(); return success();
@ -244,13 +238,10 @@ public class LoginController extends BaseController {
if (user == null) { if (user == null) {
user = usersService.createUser(UserType.GENERAL_USER, username, null); user = usersService.createUser(UserType.GENERAL_USER, username, null);
} }
String sessionId = sessionService.createSession(user, null); Session session = sessionService.createSessionIfAbsent(user);
if (sessionId == null) {
log.error("Failed to create session, userName:{}.", user.getUserName());
}
response.setStatus(HttpStatus.SC_MOVED_TEMPORARILY); response.setStatus(HttpStatus.SC_MOVED_TEMPORARILY);
response.sendRedirect(String.format("%s?sessionId=%s&authType=%s", oAuth2ClientProperties.getCallbackUrl(), response.sendRedirect(String.format("%s?sessionId=%s&authType=%s", oAuth2ClientProperties.getCallbackUrl(),
sessionId, "oauth2")); session.getId(), "oauth2"));
} catch (Exception ex) { } catch (Exception ex) {
log.error(ex.getMessage(), ex); log.error(ex.getMessage(), ex);
response.setStatus(HttpStatus.SC_MOVED_TEMPORARILY); response.setStatus(HttpStatus.SC_MOVED_TEMPORARILY);

28
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/UsersController.java

@ -76,18 +76,6 @@ public class UsersController extends BaseController {
@Autowired @Autowired
private UsersService usersService; private UsersService usersService;
/**
* create user
*
* @param loginUser login user
* @param userName user name
* @param userPassword user password
* @param email email
* @param tenantId tenant id
* @param phone phone
* @param queue queue
* @return create result code
*/
@Operation(summary = "createUser", description = "CREATE_USER_NOTES") @Operation(summary = "createUser", description = "CREATE_USER_NOTES")
@Parameters({ @Parameters({
@Parameter(name = "userName", description = "USER_NAME", required = true, schema = @Schema(implementation = String.class)), @Parameter(name = "userName", description = "USER_NAME", required = true, schema = @Schema(implementation = String.class)),
@ -172,7 +160,7 @@ public class UsersController extends BaseController {
@PostMapping(value = "/update") @PostMapping(value = "/update")
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
@ApiException(UPDATE_USER_ERROR) @ApiException(UPDATE_USER_ERROR)
public Result updateUser(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, public Result<User> updateUser(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@RequestParam(value = "id") int id, @RequestParam(value = "id") int id,
@RequestParam(value = "userName") String userName, @RequestParam(value = "userName") String userName,
@RequestParam(value = "userPassword") String userPassword, @RequestParam(value = "userPassword") String userPassword,
@ -182,9 +170,17 @@ public class UsersController extends BaseController {
@RequestParam(value = "phone", required = false) String phone, @RequestParam(value = "phone", required = false) String phone,
@RequestParam(value = "state", required = false) int state, @RequestParam(value = "state", required = false) int state,
@RequestParam(value = "timeZone", required = false) String timeZone) throws Exception { @RequestParam(value = "timeZone", required = false) String timeZone) throws Exception {
Map<String, Object> result = usersService.updateUser(loginUser, id, userName, userPassword, email, tenantId, User user = usersService.updateUser(loginUser,
phone, queue, state, timeZone); id,
return returnDataList(result); userName,
userPassword,
email,
tenantId,
phone,
queue,
state,
timeZone);
return Result.success(user);
} }
/** /**

7
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/Authenticator.java

@ -24,16 +24,19 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import lombok.NonNull;
public interface Authenticator { public interface Authenticator {
/** /**
* Verifying legality via username and password * Verifying legality via username and password
*
* @param username user name * @param username user name
* @param password user password * @param password user password
* @param extra extra info * @param ip client ip
* @return result object * @return result object
*/ */
Result<Map<String, String>> authenticate(String username, String password, String extra); Result<Map<String, String>> authenticate(@NonNull String username, String password, @NonNull String ip);
/** /**
* Get authenticated user * Get authenticated user

29
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/AbstractAuthenticator.java

@ -29,15 +29,20 @@ import org.apache.dolphinscheduler.common.enums.Flag;
import org.apache.dolphinscheduler.dao.entity.Session; import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.util.WebUtils;
@Slf4j @Slf4j
public abstract class AbstractAuthenticator implements Authenticator { public abstract class AbstractAuthenticator implements Authenticator {
@ -56,15 +61,14 @@ public abstract class AbstractAuthenticator implements Authenticator {
* *
* @param userId user identity field * @param userId user identity field
* @param password user login password * @param password user login password
* @param extra extra user login field
* @return user object in databse * @return user object in databse
*/ */
public abstract User login(String userId, String password, String extra); public abstract User login(@NonNull String userId, String password);
@Override @Override
public Result<Map<String, String>> authenticate(String userId, String password, String extra) { public Result<Map<String, String>> authenticate(@NonNull String userId, String password, @NonNull String ip) {
Result<Map<String, String>> result = new Result<>(); Result<Map<String, String>> result = new Result<>();
User user = login(userId, password, extra); User user = login(userId, password);
if (user == null) { if (user == null) {
if (Objects.equals(securityConfig.getType(), AuthenticationType.CASDOOR_SSO.name())) { if (Objects.equals(securityConfig.getType(), AuthenticationType.CASDOOR_SSO.name())) {
log.error("State or code entered incorrectly."); log.error("State or code entered incorrectly.");
@ -87,9 +91,8 @@ public abstract class AbstractAuthenticator implements Authenticator {
} }
// create session // create session
String sessionId = sessionService.createSession(user, extra); Session session = sessionService.createSessionIfAbsent(user);
if (sessionId == null) { if (session == null) {
log.error("Failed to create session, userName:{}.", user.getUserName());
result.setCode(Status.LOGIN_SESSION_FAILED.getCode()); result.setCode(Status.LOGIN_SESSION_FAILED.getCode());
result.setMsg(Status.LOGIN_SESSION_FAILED.getMsg()); result.setMsg(Status.LOGIN_SESSION_FAILED.getMsg());
return result; return result;
@ -98,7 +101,7 @@ public abstract class AbstractAuthenticator implements Authenticator {
log.info("Session is created, userName:{}.", user.getUserName()); log.info("Session is created, userName:{}.", user.getUserName());
Map<String, String> data = new HashMap<>(); Map<String, String> data = new HashMap<>();
data.put(Constants.SESSION_ID, sessionId); data.put(Constants.SESSION_ID, session.getId());
data.put(Constants.SECURITY_CONFIG_TYPE, securityConfig.getType()); data.put(Constants.SECURITY_CONFIG_TYPE, securityConfig.getType());
result.setData(data); result.setData(data);
@ -109,9 +112,15 @@ public abstract class AbstractAuthenticator implements Authenticator {
@Override @Override
public User getAuthUser(HttpServletRequest request) { public User getAuthUser(HttpServletRequest request) {
Session session = sessionService.getSession(request); String sessionId = request.getHeader(Constants.SESSION_ID);
if (StringUtils.isBlank(sessionId)) {
Cookie cookie = WebUtils.getCookie(request, Constants.SESSION_ID);
if (cookie != null) {
sessionId = cookie.getValue();
}
}
Session session = sessionService.getSession(sessionId);
if (session == null) { if (session == null) {
log.info("session info is null ");
return null; return null;
} }
// get user object from session // get user object from session

4
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticator.java

@ -20,6 +20,8 @@ package org.apache.dolphinscheduler.api.security.impl.ldap;
import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator; import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
public class LdapAuthenticator extends AbstractAuthenticator { public class LdapAuthenticator extends AbstractAuthenticator {
@ -28,7 +30,7 @@ public class LdapAuthenticator extends AbstractAuthenticator {
LdapService ldapService; LdapService ldapService;
@Override @Override
public User login(String userId, String password, String extra) { public User login(@NonNull String userId, String password) {
User user = null; User user = null;
String ldapEmail = ldapService.ldapLogin(userId, password); String ldapEmail = ldapService.ldapLogin(userId, password);
if (ldapEmail != null) { if (ldapEmail != null) {

2
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/pwd/PasswordAuthenticator.java

@ -23,7 +23,7 @@ import org.apache.dolphinscheduler.dao.entity.User;
public class PasswordAuthenticator extends AbstractAuthenticator { public class PasswordAuthenticator extends AbstractAuthenticator {
@Override @Override
public User login(String userId, String password, String extra) { public User login(String userId, String password) {
return userService.queryUser(userId, password); return userService.queryUser(userId, password);
} }
} }

4
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/sso/CasdoorAuthenticator.java

@ -27,6 +27,8 @@ import java.security.MessageDigest;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import lombok.NonNull;
import org.casbin.casdoor.entity.CasdoorUser; import org.casbin.casdoor.entity.CasdoorUser;
import org.casbin.casdoor.service.CasdoorAuthService; import org.casbin.casdoor.service.CasdoorAuthService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -46,7 +48,7 @@ public class CasdoorAuthenticator extends AbstractSsoAuthenticator {
private String adminUserName; private String adminUserName;
@Override @Override
public User login(String state, String code, String extra) { public User login(@NonNull String state, String code) {
ServletRequestAttributes servletRequestAttributes = ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (servletRequestAttributes == null) { if (servletRequestAttributes == null) {

33
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/SessionService.java

@ -20,36 +20,13 @@ package org.apache.dolphinscheduler.api.service;
import org.apache.dolphinscheduler.dao.entity.Session; import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import javax.servlet.http.HttpServletRequest;
/**
* session service
*/
public interface SessionService { public interface SessionService {
/** Session getSession(String sessionId);
* get user session from request
*
* @param request request
* @return session
*/
Session getSession(HttpServletRequest request);
/** Session createSessionIfAbsent(User user);
* create session
*
* @param user user
* @param ip ip
* @return session string
*/
String createSession(User user, String ip);
/** void expireSession(Integer userId);
* sign out
* remove ip restrictions boolean isSessionExpire(Session session);
*
* @param ip no use
* @param loginUser login user
*/
void signOut(String ip, User loginUser);
} }

26
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/UsersService.java

@ -114,23 +114,15 @@ public interface UsersService {
*/ */
Result queryUserList(User loginUser, String searchVal, Integer pageNo, Integer pageSize); Result queryUserList(User loginUser, String searchVal, Integer pageNo, Integer pageSize);
/** User updateUser(User loginUser,
* updateProcessInstance user Integer userId,
* String userName,
* String userPassword,
* @param loginUser String email,
* @param userId user id Integer tenantId,
* @param userName user name String phone,
* @param userPassword user password String queue,
* @param email email int state,
* @param tenantId tennat id
* @param phone phone
* @param queue queue
* @return update result code
* @throws Exception exception
*/
Map<String, Object> updateUser(User loginUser, int userId, String userName, String userPassword, String email,
int tenantId, String phone, String queue, int state,
String timeZone) throws IOException; String timeZone) throws IOException;
/** /**

116
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SessionServiceImpl.java

@ -17,12 +17,11 @@
package org.apache.dolphinscheduler.api.service.impl; package org.apache.dolphinscheduler.api.service.impl;
import org.apache.dolphinscheduler.api.controller.BaseController;
import org.apache.dolphinscheduler.api.service.SessionService; import org.apache.dolphinscheduler.api.service.SessionService;
import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.dao.entity.Session; import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.SessionMapper; import org.apache.dolphinscheduler.dao.repository.SessionDao;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -31,15 +30,11 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.WebUtils;
/** /**
* session service implement * session service implement
@ -49,114 +44,57 @@ import org.springframework.web.util.WebUtils;
public class SessionServiceImpl extends BaseServiceImpl implements SessionService { public class SessionServiceImpl extends BaseServiceImpl implements SessionService {
@Autowired @Autowired
private SessionMapper sessionMapper; private SessionDao sessionDao;
/**
* get user session from request
*
* @param request request
* @return session
*/
@Override @Override
public Session getSession(HttpServletRequest request) { public Session getSession(String sessionId) {
String sessionId = request.getHeader(Constants.SESSION_ID);
if (StringUtils.isBlank(sessionId)) {
Cookie cookie = WebUtils.getCookie(request, Constants.SESSION_ID);
if (cookie != null) {
sessionId = cookie.getValue();
}
}
if (StringUtils.isBlank(sessionId)) { if (StringUtils.isBlank(sessionId)) {
return null; return null;
} }
return sessionDao.queryById(sessionId);
String ip = BaseController.getClientIpAddress(request);
log.debug("Get session: {}, ip: {}.", sessionId, ip);
return sessionMapper.selectById(sessionId);
} }
/**
* create session
*
* @param user user
* @param ip ip
* @return session string
*/
@Override @Override
@Transactional @Transactional
public String createSession(User user, String ip) { public Session createSessionIfAbsent(User user) {
Session session = null; Session session;
List<Session> sessionList = sessionDao.queryByUserId(user.getId());
// logined // todo: this can be remove after the old session data is cleared
List<Session> sessionList = sessionMapper.queryByUserId(user.getId());
Date now = new Date();
/**
* if you have logged in and are still valid, return directly
*/
if (CollectionUtils.isNotEmpty(sessionList)) { if (CollectionUtils.isNotEmpty(sessionList)) {
// is session list greater 1 , delete other ,get one // is session list greater 1 , delete other ,get one
if (sessionList.size() > 1) { if (sessionList.size() > 1) {
for (int i = 1; i < sessionList.size(); i++) { for (int i = 1; i < sessionList.size(); i++) {
sessionMapper.deleteById(sessionList.get(i).getId()); sessionDao.deleteById(sessionList.get(i).getId());
} }
} }
session = sessionList.get(0); session = sessionList.get(0);
if (now.getTime() - session.getLastLoginTime().getTime() <= Constants.SESSION_TIME_OUT * 1000) { if (isSessionExpire(session)) {
/** session.setLastLoginTime(new Date());
* updateProcessInstance the latest login time sessionDao.updateById(session);
*/ return session;
session.setLastLoginTime(now);
sessionMapper.updateById(session);
return session.getId();
} else { } else {
/** sessionDao.deleteById(session.getId());
* session expired, then delete this session first
*/
sessionMapper.deleteById(session.getId());
} }
} }
// assign new session // assign new session
session = new Session(); Session newSession = Session.builder()
.id(UUID.randomUUID().toString())
session.setId(UUID.randomUUID().toString()); .userId(user.getId())
session.setIp(ip); .lastLoginTime(new Date())
session.setUserId(user.getId()); .build();
session.setLastLoginTime(now); sessionDao.insert(newSession);
return newSession;
sessionMapper.insert(session);
return session.getId();
} }
/**
* sign out
* remove ip restrictions
*
* @param ip no use
* @param loginUser login user
*/
@Override @Override
public void signOut(String ip, User loginUser) { public void expireSession(Integer userId) {
try { sessionDao.deleteByUserId(userId);
/**
* query session by user id and ip
*/
Session session = sessionMapper.queryByUserIdAndIp(loginUser.getId(), ip);
// delete session
sessionMapper.deleteById(session.getId());
} catch (Exception e) {
log.warn("userId : {} , ip : {} , find more one session", loginUser.getId(), ip, e);
} }
@Override
public boolean isSessionExpire(Session session) {
return System.currentTimeMillis() - session.getLastLoginTime().getTime() <= Constants.SESSION_TIME_OUT * 1000;
} }
} }

99
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java

@ -23,6 +23,7 @@ import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent;
import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.exceptions.ServiceException; import org.apache.dolphinscheduler.api.exceptions.ServiceException;
import org.apache.dolphinscheduler.api.service.MetricsCleanUpService; import org.apache.dolphinscheduler.api.service.MetricsCleanUpService;
import org.apache.dolphinscheduler.api.service.SessionService;
import org.apache.dolphinscheduler.api.service.UsersService; import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.CheckUtils; import org.apache.dolphinscheduler.api.utils.CheckUtils;
import org.apache.dolphinscheduler.api.utils.PageInfo; import org.apache.dolphinscheduler.api.utils.PageInfo;
@ -132,6 +133,9 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
@Autowired @Autowired
private MetricsCleanUpService metricsCleanUpService; private MetricsCleanUpService metricsCleanUpService;
@Autowired
private SessionService sessionService;
/** /**
* create user, only system admin have permission * create user, only system admin have permission
* *
@ -359,102 +363,68 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
return result; return result;
} }
/**
* updateProcessInstance user
*
* @param userId user id
* @param userName user name
* @param userPassword user password
* @param email email
* @param tenantId tenant id
* @param phone phone
* @param queue queue
* @param state state
* @param timeZone timeZone
* @return update result code
* @throws Exception exception
*/
@Override @Override
public Map<String, Object> updateUser(User loginUser, int userId, @Transactional
public User updateUser(User loginUser,
Integer userId,
String userName, String userName,
String userPassword, String userPassword,
String email, String email,
int tenantId, Integer tenantId,
String phone, String phone,
String queue, String queue,
int state, int state,
String timeZone) throws IOException { String timeZone) {
Map<String, Object> result = new HashMap<>();
result.put(Constants.STATUS, false);
if (resourcePermissionCheckService.functionDisabled()) { if (resourcePermissionCheckService.functionDisabled()) {
putMsg(result, Status.FUNCTION_DISABLED); throw new ServiceException(Status.FUNCTION_DISABLED);
return result;
} }
if (check(result, !canOperator(loginUser, userId), Status.USER_NO_OPERATION_PERM)) { if (!canOperator(loginUser, userId)) {
log.warn("User does not have permission for this feature, userId:{}, userName:{}.", loginUser.getId(), throw new ServiceException(Status.USER_NO_OPERATION_PERM);
loginUser.getUserName());
return result;
} }
User user = userMapper.selectById(userId); User user = userMapper.selectById(userId);
if (user == null) { if (user == null) {
log.error("User does not exist, userId:{}.", userId); throw new ServiceException(Status.USER_NOT_EXIST, userId);
putMsg(result, Status.USER_NOT_EXIST, userId);
return result;
} }
if (StringUtils.isNotEmpty(userName)) { if (StringUtils.isNotEmpty(userName)) {
if (!CheckUtils.checkUserName(userName)) { if (!CheckUtils.checkUserName(userName)) {
log.warn("Parameter userName check failed."); throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, userName);
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, userName);
return result;
} }
// todo: use the db unique index
User tempUser = userMapper.queryByUserNameAccurately(userName); User tempUser = userMapper.queryByUserNameAccurately(userName);
if (tempUser != null && tempUser.getId() != userId) { if (tempUser != null && !userId.equals(tempUser.getId())) {
log.warn("User name already exists, userName:{}.", tempUser.getUserName()); throw new ServiceException(Status.USER_NAME_EXIST);
putMsg(result, Status.USER_NAME_EXIST);
return result;
} }
user.setUserName(userName); user.setUserName(userName);
} }
if (StringUtils.isNotEmpty(userPassword)) { if (StringUtils.isNotEmpty(userPassword)) {
if (!CheckUtils.checkPasswordLength(userPassword)) { if (!CheckUtils.checkPasswordLength(userPassword)) {
log.warn("Parameter userPassword check failed."); throw new ServiceException(Status.USER_PASSWORD_LENGTH_ERROR);
putMsg(result, Status.USER_PASSWORD_LENGTH_ERROR);
return result;
} }
user.setUserPassword(EncryptionUtils.getMd5(userPassword)); user.setUserPassword(EncryptionUtils.getMd5(userPassword));
sessionService.expireSession(user.getId());
} }
if (StringUtils.isNotEmpty(email)) { if (StringUtils.isNotEmpty(email)) {
if (!CheckUtils.checkEmail(email)) { if (!CheckUtils.checkEmail(email)) {
log.warn("Parameter email check failed."); throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, email);
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, email);
return result;
} }
user.setEmail(email); user.setEmail(email);
} }
if (StringUtils.isNotEmpty(phone) && !CheckUtils.checkPhone(phone)) { if (StringUtils.isNotEmpty(phone) && !CheckUtils.checkPhone(phone)) {
log.warn("Parameter phone check failed."); throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, phone);
putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR, phone);
return result;
} }
if (state == 0 && user.getState() != state && Objects.equals(loginUser.getId(), user.getId())) { if (state == 0 && user.getState() != state && Objects.equals(loginUser.getId(), user.getId())) {
log.warn("Not allow to disable your own account, userId:{}, userName:{}.", user.getId(), throw new ServiceException(Status.NOT_ALLOW_TO_DISABLE_OWN_ACCOUNT);
user.getUserName());
putMsg(result, Status.NOT_ALLOW_TO_DISABLE_OWN_ACCOUNT);
return result;
} }
if (StringUtils.isNotEmpty(timeZone)) { if (StringUtils.isNotEmpty(timeZone)) {
if (!CheckUtils.checkTimeZone(timeZone)) { if (!CheckUtils.checkTimeZone(timeZone)) {
log.warn("Parameter time zone is illegal."); throw new ServiceException(Status.TIME_ZONE_ILLEGAL, timeZone);
putMsg(result, Status.TIME_ZONE_ILLEGAL, timeZone);
return result;
} }
user.setTimeZone(timeZone); user.setTimeZone(timeZone);
} }
@ -462,20 +432,13 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
user.setPhone(phone); user.setPhone(phone);
user.setQueue(queue); user.setQueue(queue);
user.setState(state); user.setState(state);
Date now = new Date(); user.setUpdateTime(new Date());
user.setUpdateTime(now);
user.setTenantId(tenantId); user.setTenantId(tenantId);
// updateProcessInstance user // updateProcessInstance user
int update = userMapper.updateById(user); if (userMapper.updateById(user) <= 0) {
if (update > 0) { throw new ServiceException(Status.UPDATE_USER_ERROR);
log.info("User is updated and id is :{}.", userId);
putMsg(result, Status.SUCCESS);
} else {
log.error("User update error, userId:{}.", userId);
putMsg(result, Status.UPDATE_USER_ERROR);
} }
return user;
return result;
} }
/** /**
@ -521,6 +484,7 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
userMapper.queryTenantCodeByUserId(id); userMapper.queryTenantCodeByUserId(id);
accessTokenMapper.deleteAccessTokenByUserId(id); accessTokenMapper.deleteAccessTokenByUserId(id);
sessionService.expireSession(id);
if (userMapper.deleteById(id) > 0) { if (userMapper.deleteById(id) > 0) {
metricsCleanUpService.cleanUpApiResponseTimeMetricsByUserId(id); metricsCleanUpService.cleanUpApiResponseTimeMetricsByUserId(id);
@ -1459,10 +1423,13 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService {
*/ */
@Override @Override
@Transactional @Transactional
public User createUserIfNotExists(String userName, String userPassword, String email, String phone, public User createUserIfNotExists(String userName,
String userPassword,
String email,
String phone,
String tenantCode, String tenantCode,
String queue, String queue,
int state) throws IOException { int state) {
User user = userMapper.queryByUserNameAccurately(userName); User user = userMapper.queryByUserNameAccurately(userName);
if (Objects.isNull(user)) { if (Objects.isNull(user)) {
Tenant tenant = tenantMapper.queryByTenantCode(tenantCode); Tenant tenant = tenantMapper.queryByTenantCode(tenantCode);

13
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/AbstractControllerTest.java

@ -25,9 +25,9 @@ import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.dao.DaoConfiguration; import org.apache.dolphinscheduler.dao.DaoConfiguration;
import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.test.TestingServer; import org.apache.curator.test.TestingServer;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -77,17 +77,20 @@ public abstract class AbstractControllerTest {
@AfterEach @AfterEach
public void after() throws Exception { public void after() throws Exception {
sessionService.signOut("127.0.0.1", user); if (user != null) {
sessionService.expireSession(user.getId());
}
} }
private void createSession(User loginUser) { private void createSession(User loginUser) {
user = loginUser; user = loginUser;
String session = sessionService.createSession(loginUser, "127.0.0.1"); Session session = sessionService.createSessionIfAbsent(loginUser);
sessionId = session; Assertions.assertNotNull(session);
Assertions.assertFalse(StringUtils.isEmpty(session)); sessionId = session.getId();
Assertions.assertNotNull(sessionId);
} }
public Map<String, Object> success() { public Map<String, Object> success() {

9
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapAuthenticatorTest.java

@ -17,6 +17,7 @@
package org.apache.dolphinscheduler.api.security.impl.ldap; package org.apache.dolphinscheduler.api.security.impl.ldap;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.apache.dolphinscheduler.api.controller.AbstractControllerTest; import org.apache.dolphinscheduler.api.controller.AbstractControllerTest;
@ -109,7 +110,7 @@ public class LdapAuthenticatorTest extends AbstractControllerTest {
@Test @Test
public void testAuthenticate() { public void testAuthenticate() {
when(ldapService.ldapLogin(ldapUid, ldapUserPwd)).thenReturn(ldapEmail); when(ldapService.ldapLogin(ldapUid, ldapUserPwd)).thenReturn(ldapEmail);
when(sessionService.createSession(Mockito.any(User.class), Mockito.eq(ip))).thenReturn(mockSession.getId()); when(sessionService.createSessionIfAbsent(Mockito.any(User.class))).thenReturn(mockSession);
// test username pwd correct and user not exist, config user not exist action deny, so login denied // test username pwd correct and user not exist, config user not exist action deny, so login denied
when(ldapService.getLdapUserNotExistAction()).thenReturn(LdapUserNotExistActionType.DENY); when(ldapService.getLdapUserNotExistAction()).thenReturn(LdapUserNotExistActionType.DENY);
@ -125,7 +126,7 @@ public class LdapAuthenticatorTest extends AbstractControllerTest {
logger.info(result.toString()); logger.info(result.toString());
// test username pwd correct and user not exist, config action create but can't create session, so login failed // test username pwd correct and user not exist, config action create but can't create session, so login failed
when(sessionService.createSession(Mockito.any(User.class), Mockito.eq(ip))).thenReturn(null); when(sessionService.createSessionIfAbsent(Mockito.any(User.class))).thenReturn(null);
result = ldapAuthenticator.authenticate(ldapUid, ldapUserPwd, ip); result = ldapAuthenticator.authenticate(ldapUid, ldapUserPwd, ip);
Assertions.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode()); Assertions.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode());
@ -139,12 +140,12 @@ public class LdapAuthenticatorTest extends AbstractControllerTest {
public void testGetAuthUser() { public void testGetAuthUser() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(usersService.queryUser(mockUser.getId())).thenReturn(mockUser); when(usersService.queryUser(mockUser.getId())).thenReturn(mockUser);
when(sessionService.getSession(request)).thenReturn(mockSession); when(sessionService.getSession(any())).thenReturn(mockSession);
User user = ldapAuthenticator.getAuthUser(request); User user = ldapAuthenticator.getAuthUser(request);
Assertions.assertNotNull(user); Assertions.assertNotNull(user);
when(sessionService.getSession(request)).thenReturn(null); when(sessionService.getSession(any())).thenReturn(null);
user = ldapAuthenticator.getAuthUser(request); user = ldapAuthenticator.getAuthUser(request);
Assertions.assertNull(user); Assertions.assertNull(user);
} }

9
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/pwd/PasswordAuthenticatorTest.java

@ -17,6 +17,7 @@
package org.apache.dolphinscheduler.api.security.impl.pwd; package org.apache.dolphinscheduler.api.security.impl.pwd;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.apache.dolphinscheduler.api.controller.AbstractControllerTest; import org.apache.dolphinscheduler.api.controller.AbstractControllerTest;
@ -81,21 +82,21 @@ public class PasswordAuthenticatorTest extends AbstractControllerTest {
@Test @Test
public void testLogin() { public void testLogin() {
when(usersService.queryUser("test", "test")).thenReturn(mockUser); when(usersService.queryUser("test", "test")).thenReturn(mockUser);
User login = authenticator.login("test", "test", "127.0.0.1"); User login = authenticator.login("test", "test");
Assertions.assertNotNull(login); Assertions.assertNotNull(login);
} }
@Test @Test
public void testAuthenticate() { public void testAuthenticate() {
when(usersService.queryUser("test", "test")).thenReturn(mockUser); when(usersService.queryUser("test", "test")).thenReturn(mockUser);
when(sessionService.createSession(mockUser, "127.0.0.1")).thenReturn(mockSession.getId()); when(sessionService.createSessionIfAbsent(mockUser)).thenReturn(mockSession);
Result result = authenticator.authenticate("test", "test", "127.0.0.1"); Result result = authenticator.authenticate("test", "test", "127.0.0.1");
Assertions.assertEquals(Status.SUCCESS.getCode(), (int) result.getCode()); Assertions.assertEquals(Status.SUCCESS.getCode(), (int) result.getCode());
logger.info(result.toString()); logger.info(result.toString());
mockUser.setState(0); mockUser.setState(0);
when(usersService.queryUser("test", "test")).thenReturn(mockUser); when(usersService.queryUser("test", "test")).thenReturn(mockUser);
when(sessionService.createSession(mockUser, "127.0.0.1")).thenReturn(mockSession.getId()); when(sessionService.createSessionIfAbsent(mockUser)).thenReturn(mockSession);
Result result1 = authenticator.authenticate("test", "test", "127.0.0.1"); Result result1 = authenticator.authenticate("test", "test", "127.0.0.1");
Assertions.assertEquals(Status.USER_DISABLED.getCode(), (int) result1.getCode()); Assertions.assertEquals(Status.USER_DISABLED.getCode(), (int) result1.getCode());
logger.info(result1.toString()); logger.info(result1.toString());
@ -105,7 +106,7 @@ public class PasswordAuthenticatorTest extends AbstractControllerTest {
public void testGetAuthUser() { public void testGetAuthUser() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(usersService.queryUser(mockUser.getId())).thenReturn(mockUser); when(usersService.queryUser(mockUser.getId())).thenReturn(mockUser);
when(sessionService.getSession(request)).thenReturn(mockSession); when(sessionService.getSession(any())).thenReturn(mockSession);
User user = authenticator.getAuthUser(request); User user = authenticator.getAuthUser(request);
Assertions.assertNotNull(user); Assertions.assertNotNull(user);

6
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/security/impl/sso/CasdoorAuthenticatorTest.java

@ -112,7 +112,7 @@ public class CasdoorAuthenticatorTest extends AbstractControllerTest {
@Test @Test
public void testAuthenticate() { public void testAuthenticate() {
when(usersService.getUserByUserName(casdoorUsername)).thenReturn(mockUser); when(usersService.getUserByUserName(casdoorUsername)).thenReturn(mockUser);
when(sessionService.createSession(mockUser, ip)).thenReturn(mockSession.getId()); when(sessionService.createSessionIfAbsent(mockUser)).thenReturn(mockSession);
when(casdoorAuthService.getOAuthToken(code, state)).thenReturn(token); when(casdoorAuthService.getOAuthToken(code, state)).thenReturn(token);
when(casdoorAuthService.parseJwtToken(token)).thenReturn(mockCasdoorUser); when(casdoorAuthService.parseJwtToken(token)).thenReturn(mockCasdoorUser);
@ -131,13 +131,13 @@ public class CasdoorAuthenticatorTest extends AbstractControllerTest {
Objects.requireNonNull(request.getSession()).setAttribute(Constants.SSO_LOGIN_USER_STATE, state); Objects.requireNonNull(request.getSession()).setAttribute(Constants.SSO_LOGIN_USER_STATE, state);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
when(sessionService.createSession(mockUser, ip)).thenReturn(null); when(sessionService.createSessionIfAbsent(mockUser)).thenReturn(null);
result = casdoorAuthenticator.authenticate(state, code, ip); result = casdoorAuthenticator.authenticate(state, code, ip);
Assertions.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode()); Assertions.assertEquals(Status.LOGIN_SESSION_FAILED.getCode(), (int) result.getCode());
Objects.requireNonNull(request.getSession()).setAttribute(Constants.SSO_LOGIN_USER_STATE, state); Objects.requireNonNull(request.getSession()).setAttribute(Constants.SSO_LOGIN_USER_STATE, state);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
when(sessionService.createSession(mockUser, ip)).thenReturn(mockSession.getId()); when(sessionService.createSessionIfAbsent(mockUser)).thenReturn(mockSession);
when(usersService.getUserByUserName(casdoorUsername)).thenReturn(null); when(usersService.getUserByUserName(casdoorUsername)).thenReturn(null);
when(usersService.createUser(userType, casdoorUsername, casdoorEmail)).thenReturn(null); when(usersService.createUser(userType, casdoorUsername, casdoorEmail)).thenReturn(null);
result = casdoorAuthenticator.authenticate(state, code, ip); result = casdoorAuthenticator.authenticate(state, code, ip);

45
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/SessionServiceTest.java

@ -18,14 +18,11 @@
package org.apache.dolphinscheduler.api.service; package org.apache.dolphinscheduler.api.service;
import org.apache.dolphinscheduler.api.service.impl.SessionServiceImpl; import org.apache.dolphinscheduler.api.service.impl.SessionServiceImpl;
import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.common.enums.UserType; import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.common.utils.DateUtils; import org.apache.dolphinscheduler.common.utils.DateUtils;
import org.apache.dolphinscheduler.dao.entity.Session; import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.SessionMapper; import org.apache.dolphinscheduler.dao.repository.SessionDao;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
@ -43,8 +40,6 @@ import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockCookie;
import org.springframework.mock.web.MockHttpServletRequest;
/** /**
* session service test * session service test
@ -58,7 +53,7 @@ public class SessionServiceTest {
private SessionServiceImpl sessionService; private SessionServiceImpl sessionService;
@Mock @Mock
private SessionMapper sessionMapper; private SessionDao sessionDao;
private String sessionId = "aaaaaaaaaaaaaaaaaa"; private String sessionId = "aaaaaaaaaaaaaaaaaa";
@ -70,32 +65,14 @@ public class SessionServiceTest {
public void after() { public void after() {
} }
/**
* create session
*/
@Test @Test
public void testGetSession() { public void testGetSession() {
Mockito.when(sessionMapper.selectById(sessionId)).thenReturn(getSession()); Mockito.when(sessionDao.queryById(sessionId)).thenReturn(getSession());
// get sessionId from header
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
mockHttpServletRequest.addHeader(Constants.SESSION_ID, sessionId);
mockHttpServletRequest.addHeader("HTTP_X_FORWARDED_FOR", "127.0.0.1");
// query // query
Session session = sessionService.getSession(mockHttpServletRequest); Session session = sessionService.getSession(sessionId);
Assertions.assertNotNull(session); Assertions.assertNotNull(session);
logger.info("session ip {}", session.getIp());
// get sessionId from cookie
mockHttpServletRequest = new MockHttpServletRequest();
mockHttpServletRequest.addHeader("HTTP_X_FORWARDED_FOR", "127.0.0.1");
MockCookie mockCookie = new MockCookie(Constants.SESSION_ID, sessionId);
mockHttpServletRequest.setCookies(mockCookie);
// query
session = sessionService.getSession(mockHttpServletRequest);
Assertions.assertNotNull(session);
logger.info("session ip {}", session.getIp());
Assertions.assertEquals(session.getIp(), "127.0.0.1");
} }
/** /**
@ -107,10 +84,10 @@ public class SessionServiceTest {
User user = new User(); User user = new User();
user.setUserType(UserType.GENERAL_USER); user.setUserType(UserType.GENERAL_USER);
user.setId(1); user.setId(1);
Mockito.when(sessionMapper.queryByUserId(1)).thenReturn(getSessions()); Mockito.when(sessionDao.queryByUserId(1)).thenReturn(getSessions());
String sessionId = sessionService.createSession(user, ip); Session session = sessionService.createSessionIfAbsent(user);
logger.info("createSessionId is " + sessionId); Assertions.assertNotNull(session);
Assertions.assertTrue(!StringUtils.isEmpty(sessionId)); Assertions.assertNotNull(session.getId());
} }
/** /**
@ -118,15 +95,15 @@ public class SessionServiceTest {
* remove ip restrictions * remove ip restrictions
*/ */
@Test @Test
public void testSignOut() { public void testExpireSession() {
int userId = 88888888; int userId = 88888888;
String ip = "127.0.0.1"; String ip = "127.0.0.1";
User user = new User(); User user = new User();
user.setId(userId); user.setId(userId);
Mockito.when(sessionMapper.queryByUserIdAndIp(userId, ip)).thenReturn(getSession()); Mockito.doNothing().when(sessionDao).deleteByUserId(userId);
sessionService.signOut(ip, user); sessionService.expireSession(userId);
} }

51
dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java

@ -17,6 +17,8 @@
package org.apache.dolphinscheduler.api.service; package org.apache.dolphinscheduler.api.service;
import static org.apache.dolphinscheduler.api.AssertionsHelper.assertDoesNotThrow;
import static org.apache.dolphinscheduler.api.AssertionsHelper.assertThrowsServiceException;
import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.USER_MANAGER; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.USER_MANAGER;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -54,7 +56,6 @@ import org.apache.dolphinscheduler.spi.enums.ResourceType;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -131,6 +132,9 @@ public class UsersServiceTest {
@Mock @Mock
private ResourcePermissionCheckService resourcePermissionCheckService; private ResourcePermissionCheckService resourcePermissionCheckService;
@Mock
private SessionService sessionService;
private String queueName = "UsersServiceTestQueue"; private String queueName = "UsersServiceTestQueue";
private static final Logger serviceLogger = LoggerFactory.getLogger(BaseServiceImpl.class); private static final Logger serviceLogger = LoggerFactory.getLogger(BaseServiceImpl.class);
@ -301,20 +305,36 @@ public class UsersServiceTest {
} }
@Test @Test
public void testUpdateUser() throws IOException { public void testUpdateUser() {
String userName = "userTest0001"; String userName = "userTest0001";
String userPassword = "userTest0001"; String userPassword = "userTest0001";
// user not exist // user not exist
Map<String, Object> result = usersService.updateUser(getLoginUser(), 0, userName, userPassword, assertThrowsServiceException(
"3443@qq.com", 1, "13457864543", "queue", 1, "Asia/Shanghai"); Status.USER_NOT_EXIST,
Assertions.assertEquals(Status.USER_NOT_EXIST, result.get(Constants.STATUS)); () -> usersService.updateUser(getLoginUser(),
0,
userName,
userPassword,
"3443@qq.com",
1,
"13457864543",
"queue",
1,
"Asia/Shanghai"));
// success // success
when(userMapper.selectById(1)).thenReturn(getUser()); when(userMapper.selectById(any())).thenReturn(getUser());
when(userMapper.updateById(any())).thenReturn(1); when(userMapper.updateById(any())).thenReturn(1);
result = usersService.updateUser(getLoginUser(), 1, userName, userPassword, "32222s@qq.com", 1, assertDoesNotThrow(() -> usersService.updateUser(getLoginUser(),
"13457864543", "queue", 1, "Asia/Shanghai"); 1,
Assertions.assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); userName,
userPassword,
"32222s@qq.com",
1,
"13457864543",
"queue",
1,
"Asia/Shanghai"));
} }
@Test @Test
@ -784,7 +804,7 @@ public class UsersServiceTest {
} }
@Test @Test
public void testCreateUserIfNotExists() throws IOException { public void testCreateUserIfNotExists() {
User user; User user;
String userName = "userTest0001"; String userName = "userTest0001";
String userPassword = "userTest"; String userPassword = "userTest";
@ -794,11 +814,12 @@ public class UsersServiceTest {
int stat = 1; int stat = 1;
// User exists // User exists
Mockito.when(userMapper.existUser(userName)).thenReturn(true); when(userMapper.existUser(userName)).thenReturn(true);
Mockito.when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); when(userMapper.selectById(getUser().getId())).thenReturn(getUser());
Mockito.when(userMapper.queryDetailsById(getUser().getId())).thenReturn(getUser()); when(userMapper.queryDetailsById(getUser().getId())).thenReturn(getUser());
Mockito.when(userMapper.queryByUserNameAccurately(userName)).thenReturn(getUser()); when(userMapper.queryByUserNameAccurately(userName)).thenReturn(getUser());
Mockito.when(tenantMapper.queryByTenantCode(tenantCode)).thenReturn(getTenant()); when(userMapper.updateById(any())).thenReturn(1);
when(tenantMapper.queryByTenantCode(tenantCode)).thenReturn(getTenant());
user = usersService.createUserIfNotExists(userName, userPassword, email, phone, tenantCode, queueName, stat); user = usersService.createUserIfNotExists(userName, userPassword, email, phone, tenantCode, queueName, stat);
Assertions.assertEquals(getUser(), user); Assertions.assertEquals(getUser(), user);

99
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Session.java

@ -18,108 +18,31 @@ package org.apache.dolphinscheduler.dao.entity;
import java.util.Date; import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
/** @Builder
* session @Data
*/ @NoArgsConstructor
@AllArgsConstructor
@TableName("t_ds_session") @TableName("t_ds_session")
public class Session { public class Session {
/**
* id
*/
@TableId(value = "id", type = IdType.INPUT) @TableId(value = "id", type = IdType.INPUT)
private String id; private String id;
/**
* user id
*/
private int userId; private int userId;
/**
* last login time
*/
private Date lastLoginTime; private Date lastLoginTime;
/** // We will not bind session with ip
* user login ip @Deprecated
*/
private String ip; private String ip;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Date getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}
@Override
public String toString() {
return "Session{" +
"id=" + id +
", userId=" + userId +
", ip='" + ip + '\'' +
", lastLoginTime=" + lastLoginTime +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Session session = (Session) o;
if (userId != session.userId) {
return false;
}
if (!id.equals(session.id)) {
return false;
}
if (!lastLoginTime.equals(session.lastLoginTime)) {
return false;
}
return ip.equals(session.ip);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + userId;
result = 31 * result + lastLoginTime.hashCode();
result = 31 * result + ip.hashCode();
return result;
}
} }

22
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/SessionMapper.java

@ -18,30 +18,8 @@ package org.apache.dolphinscheduler.dao.mapper;
import org.apache.dolphinscheduler.dao.entity.Session; import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* session mapper interface
*/
public interface SessionMapper extends BaseMapper<Session> { public interface SessionMapper extends BaseMapper<Session> {
/**
* query session list by userId
* @param userId userId
* @return session list
*/
List<Session> queryByUserId(@Param("userId") int userId);
/**
* query session by userId and Ip
* @param userId userId
* @param ip ip
* @return session
*/
Session queryByUserIdAndIp(@Param("userId") int userId, @Param("ip") String ip);
} }

17
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/BaseDao.java

@ -27,6 +27,7 @@ import java.util.Optional;
import lombok.NonNull; import lombok.NonNull;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public abstract class BaseDao<ENTITY, MYBATIS_MAPPER extends BaseMapper<ENTITY>> implements IDao<ENTITY> { public abstract class BaseDao<ENTITY, MYBATIS_MAPPER extends BaseMapper<ENTITY>> implements IDao<ENTITY> {
@ -55,6 +56,14 @@ public abstract class BaseDao<ENTITY, MYBATIS_MAPPER extends BaseMapper<ENTITY>>
return mybatisMapper.selectBatchIds(ids); return mybatisMapper.selectBatchIds(ids);
} }
@Override
public List<ENTITY> queryByCondition(ENTITY queryCondition) {
if (queryCondition == null) {
throw new IllegalArgumentException("queryCondition can not be null");
}
return mybatisMapper.selectList(new QueryWrapper<>(queryCondition));
}
@Override @Override
public int insert(@NonNull ENTITY model) { public int insert(@NonNull ENTITY model) {
return mybatisMapper.insert(model); return mybatisMapper.insert(model);
@ -88,4 +97,12 @@ public abstract class BaseDao<ENTITY, MYBATIS_MAPPER extends BaseMapper<ENTITY>>
return mybatisMapper.deleteBatchIds(ids) > 0; return mybatisMapper.deleteBatchIds(ids) > 0;
} }
@Override
public boolean deleteByCondition(ENTITY queryCondition) {
if (queryCondition == null) {
throw new IllegalArgumentException("queryCondition can not be null");
}
return mybatisMapper.delete(new QueryWrapper<>(queryCondition)) > 0;
}
} }

10
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/IDao.java

@ -41,6 +41,11 @@ public interface IDao<Entity> {
*/ */
List<Entity> queryByIds(Collection<? extends Serializable> ids); List<Entity> queryByIds(Collection<? extends Serializable> ids);
/**
* Query the entity by condition.
*/
List<Entity> queryByCondition(Entity queryCondition);
/** /**
* Insert the entity. * Insert the entity.
*/ */
@ -66,4 +71,9 @@ public interface IDao<Entity> {
*/ */
boolean deleteByIds(Collection<? extends Serializable> ids); boolean deleteByIds(Collection<? extends Serializable> ids);
/**
* Delete the entities by condition.
*/
boolean deleteByCondition(Entity queryCondition);
} }

30
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/SessionDao.java

@ -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.dao.repository;
import org.apache.dolphinscheduler.dao.entity.Session;
import java.util.List;
public interface SessionDao extends IDao<Session> {
void deleteByUserId(Integer userId);
List<Session> queryByUserId(Integer userId);
}

49
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/SessionDaoImpl.java

@ -0,0 +1,49 @@
/*
* 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.dao.repository.impl;
import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.mapper.SessionMapper;
import org.apache.dolphinscheduler.dao.repository.BaseDao;
import org.apache.dolphinscheduler.dao.repository.SessionDao;
import java.util.List;
import lombok.NonNull;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@Repository
public class SessionDaoImpl extends BaseDao<Session, SessionMapper> implements SessionDao {
public SessionDaoImpl(@NonNull SessionMapper sessionMapper) {
super(sessionMapper);
}
public void deleteByUserId(Integer userId) {
mybatisMapper.delete(new QueryWrapper<>(Session.builder().userId(userId).build()));
}
@Override
public List<Session> queryByUserId(Integer userId) {
return mybatisMapper.selectList(new QueryWrapper<>(Session.builder().userId(userId).build()));
}
}

37
dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/SessionMapper.xml

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ 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.
-->
<!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.SessionMapper">
<sql id="baseSql">
id, user_id, ip, last_login_time
</sql>
<select id="queryByUserId" resultType="org.apache.dolphinscheduler.dao.entity.Session">
select
<include refid="baseSql"/>
from t_ds_session
where user_id = #{userId}
</select>
<select id="queryByUserIdAndIp" resultType="org.apache.dolphinscheduler.dao.entity.Session">
select
<include refid="baseSql"/>
from t_ds_session
where user_id = #{userId} AND ip = #{ip}
</select>
</mapper>

5
dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/SessionMapperTest.java

@ -27,6 +27,8 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
public class SessionMapperTest extends BaseDaoTest { public class SessionMapperTest extends BaseDaoTest {
@Autowired @Autowired
@ -87,7 +89,8 @@ public class SessionMapperTest extends BaseDaoTest {
@Test @Test
public void testQueryByUserId() { public void testQueryByUserId() {
Session session = insertOne(); Session session = insertOne();
List<Session> sessions = sessionMapper.queryByUserId(session.getUserId()); List<Session> sessions =
sessionMapper.selectList(new QueryWrapper<>(Session.builder().userId(session.getUserId()).build()));
Assertions.assertNotEquals(0, sessions.size()); Assertions.assertNotEquals(0, sessions.size());
} }

Loading…
Cancel
Save