From cefcd14fda37d34452cf65b6e5181a6a6615b32b Mon Sep 17 00:00:00 2001 From: ialbert Date: Fri, 16 Sep 2022 19:11:07 +0300 Subject: [PATCH] http://jira.mfd.msk:8088/browse/CLS-63 --- .../clearing/backendapi/config/WebConfig.java | 6 + .../request/cud/utilities/UserAuthAction.java | 43 +++++ .../backendapi/errors/BackEndError.java | 3 +- ...oakRestTemplateAuthenticationProvider.java | 21 ++- .../backendapi/service/UserAuthProcessor.java | 75 ++++++++ clearing-parent/utility-service/pom.xml | 26 +++ .../clearing/utility/config/KafkaConfig.java | 3 + .../clearing/utility/service/UserService.java | 176 ++++++++++++++++++ .../src/main/resources/application.properties | 2 +- .../service/UserServiceChangeRolesTest.java | 94 ++++++++++ platform-parent/platform-enum/pom.xml | 29 +++ .../platform/enumeration/ConnectionState.java | 18 ++ .../ru/spcex/platform/enumeration/Status.java | 18 ++ .../platform/messaging/domain/ActionType.java | 3 +- .../platform/messaging/domain/Consts.java | 2 + .../domain/cud/utilities/UserAuthRequest.java | 76 ++++++++ .../platform/utils/enumeration/IEnumKey.java | 64 +++++++ .../spcex/platform/utils/time/TimeUtil.java | 8 + platform-parent/pom.xml | 1 + pom.xml | 6 + 20 files changed, 663 insertions(+), 11 deletions(-) create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/utilities/UserAuthAction.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/UserAuthProcessor.java create mode 100644 clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/service/UserService.java create mode 100644 clearing-parent/utility-service/src/test/java/ru/spcex/clearing/utility/service/UserServiceChangeRolesTest.java create mode 100644 platform-parent/platform-enum/pom.xml create mode 100644 platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ConnectionState.java create mode 100644 platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/Status.java create mode 100644 platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/utilities/UserAuthRequest.java create mode 100644 platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumKey.java diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java index 2efd544b8..005fa63aa 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java @@ -10,6 +10,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.context.request.RequestContextListener; import org.springframework.web.servlet.config.annotation.*; import java.util.List; @@ -69,4 +70,9 @@ public class WebConfig implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("*"); } + + @Bean + public RequestContextListener requestContextListener() { + return new RequestContextListener(); + } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/utilities/UserAuthAction.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/utilities/UserAuthAction.java new file mode 100644 index 000000000..260c7b901 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/utilities/UserAuthAction.java @@ -0,0 +1,43 @@ +package ru.spcex.clearing.backendapi.controller.request.cud.utilities; + + +import ru.spcex.clearing.backendapi.domain.actions.IAction; +import ru.spcex.clearing.backendapi.errors.BackEndError; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserAuthRequest; +import ru.spcex.platform.utils.enumeration.EnumMessage; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class UserAuthAction implements IAction { + private final UserAuthRequest authRequest; + + public UserAuthAction() { + this.authRequest = new UserAuthRequest(); + } + + public UserAuthRequest getAuthRequest() { + return authRequest; + } + + @Override + public Collection validate() { + long count = authRequest.getRoles().stream().distinct().count(); + if (count != authRequest.getRoles().size()) { + return List.of(new EnumMessage(BackEndError.KeycloakRepeatedRoles)); + } + return Collections.emptyList(); + } + + @Override + public UserAuthRequest toRequest() { + return authRequest; + } + + @Override + public ActionType getActionType() { + return ActionType.SYSTEM; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java index b297b07a9..5808d3e7e 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java @@ -5,7 +5,8 @@ import ru.spcex.platform.utils.enumeration.IEnumId; public enum BackEndError implements IEnumId { ValidationError(3000L), UnknownJsonProperty(3001L), - FailedToReadHttpMessage(3002L); + FailedToReadHttpMessage(3002L), + KeycloakRepeatedRoles(3003L); private final Long id; BackEndError(Long id) { diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/KeycloakRestTemplateAuthenticationProvider.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/KeycloakRestTemplateAuthenticationProvider.java index 67e5c07f1..a8cff0b1f 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/KeycloakRestTemplateAuthenticationProvider.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/KeycloakRestTemplateAuthenticationProvider.java @@ -11,6 +11,8 @@ import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.common.VerificationException; import org.keycloak.representations.AccessToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.*; import org.springframework.security.authentication.AuthenticationProvider; @@ -24,7 +26,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import ru.spcex.clearing.backendapi.service.UserAutoCreateService; +import ru.spcex.clearing.backendapi.service.UserAuthProcessor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -36,14 +38,15 @@ import java.util.stream.Collectors; @Component("keycloakRestAuthenticationProvider") public class KeycloakRestTemplateAuthenticationProvider implements AuthenticationProvider { + private final Logger log = LoggerFactory.getLogger(getClass()); private final KeycloakConfigResolver resolver; private final HttpServletRequest currentHttpRequest; private final HttpServletResponse currentHttpResponse; private final RestTemplate restTemplate; - private final UserAutoCreateService userAutoCreation; + private final UserAuthProcessor userAutoCreation; public KeycloakRestTemplateAuthenticationProvider(KeycloakConfigResolver resolver, HttpServletRequest currentHttpRequest, HttpServletResponse currentHttpResponse, - @Qualifier("clearing-rest") RestTemplate restTemplate, UserAutoCreateService userAutoCreation) { + @Qualifier("clearing-rest") RestTemplate restTemplate, UserAuthProcessor userAutoCreation) { this.resolver = resolver; this.currentHttpRequest = currentHttpRequest; this.currentHttpResponse = currentHttpResponse; @@ -79,8 +82,6 @@ public class KeycloakRestTemplateAuthenticationProvider implements Authenticatio AccessToken accessToken = AdapterTokenVerifier.verifyToken(accessTokenString, deployment); //todo cors settings accessToken.setAllowedOrigins(Collections.singleton("*")); - - System.out.println("login successful"); List realmRoles = accessToken.getRealmAccess().getRoles().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); RefreshableKeycloakSecurityContext skSession = new RefreshableKeycloakSecurityContext(deployment, null, accessTokenString, accessToken, null, null, refreshTokenString); @@ -90,7 +91,7 @@ public class KeycloakRestTemplateAuthenticationProvider implements Authenticatio final KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, skSession); KeycloakAuthenticationToken keycloakAuthenticationToken = new KeycloakAuthenticationToken(account, false, realmRoles); keycloakAuthenticationToken.setAuthenticated(true); - checkRightsAndCreate(principalName, roles); + checkRightsAndCreate(principalName, accessToken.getId(), roles); return keycloakAuthenticationToken; } catch (VerificationException vex) { @@ -100,10 +101,14 @@ public class KeycloakRestTemplateAuthenticationProvider implements Authenticatio } } - private void checkRightsAndCreate(String username, Set roles) { + private void checkRightsAndCreate(String username, String id, Set roles) { + + log.debug("login successful: login {}, roles {}", username, String.join(";", roles)); //todo remove hardcode if (roles.contains("admin") || roles.contains("default-roles-master")) { - userAutoCreation.createUserIfNeeded(username); + String clientIp = currentHttpRequest != null ? currentHttpRequest.getRemoteAddr() : null; + String serverIp = currentHttpRequest != null ? currentHttpRequest.getLocalAddr() : null; + userAutoCreation.sendUserInfo(username, id, roles, clientIp, serverIp); } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/UserAuthProcessor.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/UserAuthProcessor.java new file mode 100644 index 000000000..2e152ff49 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/UserAuthProcessor.java @@ -0,0 +1,75 @@ +package ru.spcex.clearing.backendapi.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import ru.spcex.clearing.backendapi.controller.request.cud.utilities.UserAuthAction; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserAuthRequest; +import ru.spcex.platform.utils.log.ExceptionUtils; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; + +@Service +public class UserAuthProcessor { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final IOperator operator; + private String serverAddress; + + @Value("${server.port}") + private String serverPort; + + @Autowired + public UserAuthProcessor(IOperator operator) { + this.operator = operator; + } + + public void sendUserInfo(String username, String identifier, Collection roles, String userIp, String serverIp) { + UserAuthAction authEvent = new UserAuthAction(); + UserAuthRequest requestData = authEvent.getAuthRequest(); + requestData.setUsername(username); + requestData.setIdentifier(identifier); + requestData.setTime(Instant.now()); + requestData.setRoles(new ArrayList<>(roles)); + requestData.setServerIp(serverIp + ":" + serverPort); //getServerAddress() + ":" + serverPort + requestData.setClientIp(userIp); + try { + operator.sendRequestToQueue(Consts.USER_AUTH_SUCCESS, authEvent); + } catch (Throwable e) { //ExecutionException | InterruptedException + log.error(ExceptionUtils.getStackTrace(e)); + } + } + + private String getServerAddress() { + if (serverAddress != null) { + return serverAddress; + } + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + Enumeration inetAddresses = networkInterface.getInetAddresses(); + while(inetAddresses.hasMoreElements()) { + InetAddress inetAddress = inetAddresses.nextElement(); + if (inetAddress != null) { + serverAddress = inetAddress.getHostAddress(); + return serverAddress; + } + } + } + serverAddress = "unknown"; + return serverAddress; + } catch (Throwable e) { + log.error("cannot retrieve server address {}", ExceptionUtils.getStackTrace(e)); + serverAddress = "unknown"; + return serverAddress; + } + } +} diff --git a/clearing-parent/utility-service/pom.xml b/clearing-parent/utility-service/pom.xml index 2aed17aef..5ee1f0564 100644 --- a/clearing-parent/utility-service/pom.xml +++ b/clearing-parent/utility-service/pom.xml @@ -20,6 +20,10 @@ ru.spcex.platform platform-messaging + + ru.spcex.platform + platform-enum + ru.spcex.platform platform-imdg-api-hazelcast-impl @@ -36,6 +40,28 @@ com.fasterxml.jackson.core jackson-databind + + + + + + + + + + + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + diff --git a/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/config/KafkaConfig.java b/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/config/KafkaConfig.java index e9284ce25..bc8a2546a 100644 --- a/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/config/KafkaConfig.java +++ b/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/config/KafkaConfig.java @@ -2,8 +2,10 @@ package ru.spcex.clearing.utility.config; import org.apache.kafka.clients.consumer.Consumer; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; import ru.spcex.clearing.platform.messaging.config.KafkaConsumerFactory; import ru.spcex.clearing.utility.config.settings.UtilityServiceSettings; @@ -11,6 +13,7 @@ import ru.spcex.clearing.utility.config.settings.UtilityServiceSettings; public class KafkaConfig { @Autowired @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Consumer createProducer(UtilityServiceSettings settings) { return KafkaConsumerFactory.consumer(settings.getKafka()); } diff --git a/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/service/UserService.java b/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/service/UserService.java new file mode 100644 index 000000000..f66629a65 --- /dev/null +++ b/clearing-parent/utility-service/src/main/java/ru/spcex/clearing/utility/service/UserService.java @@ -0,0 +1,176 @@ +package ru.spcex.clearing.utility.service; + +import org.apache.kafka.clients.consumer.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.user.User; +import ru.clearing.classes.statics.data.user.UserConnect; +import ru.clearing.classes.statics.data.user.UserRoleSession; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.utilities.UserAuthRequest; +import ru.spcex.clearing.platform.messaging.service.QueueConsumer; +import ru.spcex.platform.enumeration.ConnectionState; +import ru.spcex.platform.enumeration.Status; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.utils.time.TimeUtil; + +import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Stream; + +@Service +public class UserService extends QueueConsumer implements InitializingBean { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final Imdg userMap; + private final Imdg userRoleSessionImdg; + private final Imdg userConnectImdg; + + @Autowired + public UserService(Consumer kafkaQueue, ImdgProvider imdgProvider) { + super(kafkaQueue); + this.userMap = imdgProvider.getImdg(IMDGDistributedNames.Map_User, User.class); + this.userRoleSessionImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_UserRoleSession, UserRoleSession.class); + this.userConnectImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_UserConnect, UserConnect.class); + } + + @Override + public void afterPropertiesSet() { + callback(UserAuthRequest.class) + .setConsumer(this::userAuthSuccess) + .forDestination(Consts.USER_AUTH_SUCCESS, callbacks::put); + init(); + } + + private void userAuthSuccess(BaseRequest authEvent) { + UserAuthRequest authInfo = authEvent.getRequestPayload(); + log.debug("user id={} authenticated", authInfo.getUsername()); + UserInfo userInfo = createUserIfNeeded(authInfo.getUsername()); + changeRoles(userInfo.userId, userInfo.isNew, authInfo.getRoles()); + connected(userInfo.userId, authInfo.getTime(), authInfo.getClientIp(), authInfo.getServerIp()); + } + + private UserInfo createUserIfNeeded(String username) { + User user = userMap.getSingleObjectByFieldValues(Map.of("identifier", username)); + if (user == null) { + user = new User(); + user.setCreated(Instant.now()); + user.setIdentifier(username); + userMap.insert(user); + return new UserInfo(true, user.getId()); + } else { + return new UserInfo(false, user.getId()); + } + } + + private void changeRoles(Long userId, boolean isNew, Collection authRoles) { + Collection presentRoles; + if (isNew) { + presentRoles = Collections.emptyList(); + } else { + presentRoles = userRoleSessionImdg.getCollectionObjectsByFieldValues(Map.of("userId", userId)); + } + + Collection roleActions = defineAllRoleChanges(presentRoles, authRoles); + + if (roleActions.size() > 0) { + for (RoleAction roleAction : roleActions) { + UserRoleSession role; + if (roleAction.create()) { + role = new UserRoleSession(); + role.setUserRole(roleAction.roleName); + role.setUserId(userId); + role.setStatus(Status.Active.getKey()); + userRoleSessionImdg.insert(role); + } else { + role = roleAction.roleToChange; + role.setStatus(roleAction.newStatus.getKey()); + userRoleSessionImdg.update(role); + } + } + } + } + + private void connected(Long userId, Instant time, String clientIp, String serverIp) { + UserConnect userConnect = userConnectImdg.getSingleObjectByFieldValues(Map.of("userId", userId)); + boolean isNew = false; + if (userConnect == null) { + userConnect = new UserConnect(); + userConnect.setUserId(userId); + isNew = true; + } + userConnect.setConnectionTime(time); + userConnect.setServerIP(serverIp); + userConnect.setClientIP(clientIp); + userConnect.setConnectionState(ConnectionState.Connected.getKey()); + userConnect.setClearingDate(TimeUtil.today()); + if (isNew) { + userConnectImdg.insert(userConnect); + } else { + userConnectImdg.update(userConnect); + } + } + + static Collection defineAllRoleChanges(Collection presentRoles, Collection authRoles) { + return Stream.concat( + presentRoles + .stream() + .filter(presentRole -> !(Status.Active.equalsByKey(presentRole.getStatus()) && contain(presentRole, authRoles))) + .filter(presentRole -> !(Status.Blocked.equalsByKey(presentRole.getStatus()) && !contain(presentRole, authRoles))) + .map(role -> { + RoleAction roleAction = new RoleAction(); + roleAction.roleToChange = role; + if (Status.Active.equalsByKey(role.getStatus())) { + roleAction.newStatus = Status.Blocked; + } else { + roleAction.newStatus = Status.Active; + } + return roleAction; + }), + authRoles + .stream() + .filter(authRole -> !contain(authRole, presentRoles)) + .map(authRole -> { + RoleAction action = new RoleAction(); + action.newStatus = Status.Active; + action.roleName = authRole; + return action; + }) + ).toList(); + } + + private static boolean contain(UserRoleSession role, Collection authRoles) { + return authRoles.stream().anyMatch(authRole -> authRole.equalsIgnoreCase(role.getUserRole())); + } + + private static boolean contain(String authRole, Collection presentRoles) { + return presentRoles.stream().anyMatch(role -> role.getUserRole().equalsIgnoreCase(authRole)); + } + + public static class RoleAction { + String roleName; + //------поля при обновления----- + Status newStatus; + UserRoleSession roleToChange; + boolean create() { + return roleToChange == null; + } + } + + private static class UserInfo { + boolean isNew; + Long userId; + + public UserInfo(boolean isNew, Long userId) { + this.isNew = isNew; + this.userId = userId; + } + } +} diff --git a/clearing-parent/utility-service/src/main/resources/application.properties b/clearing-parent/utility-service/src/main/resources/application.properties index f3f6bd027..aab233b1f 100644 --- a/clearing-parent/utility-service/src/main/resources/application.properties +++ b/clearing-parent/utility-service/src/main/resources/application.properties @@ -5,7 +5,7 @@ utility-service.hazelcast.login=dev utility-service.hazelcast.password=dev-pass utility-service.kafka.bootstrap-servers=localhost:9092 -utility-service.kafka.group-id=dev-group +utility-service.kafka.group-id=dev-group-utility-service utility-service.kafka.enable-auto-commit=false utility-service.kafka.session-timeout-ms=30000 utility-service.kafka.auto-offset-reset=latest diff --git a/clearing-parent/utility-service/src/test/java/ru/spcex/clearing/utility/service/UserServiceChangeRolesTest.java b/clearing-parent/utility-service/src/test/java/ru/spcex/clearing/utility/service/UserServiceChangeRolesTest.java new file mode 100644 index 000000000..5239ea78b --- /dev/null +++ b/clearing-parent/utility-service/src/test/java/ru/spcex/clearing/utility/service/UserServiceChangeRolesTest.java @@ -0,0 +1,94 @@ +package ru.spcex.clearing.utility.service; + +import org.junit.jupiter.api.Test; +import ru.clearing.classes.statics.data.user.UserRoleSession; +import ru.spcex.platform.enumeration.Status; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class UserServiceChangeRolesTest { + private static final AtomicLong idGenerator = new AtomicLong(); + + @Test + public void testTwoNewRoles() { + Collection userRoles = new ArrayList<>(); + userRoles.add(generate("admin", Status.Active)); + userRoles.add(generate("broker", Status.Active)); + userRoles.add(generate("manager", Status.Blocked)); + Collection methodsFromAuth = List.of("admin", "broker", "makler", "user"); + + Collection roleActions = UserService.defineAllRoleChanges(userRoles, methodsFromAuth); + assertEquals(2, roleActions.size()); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "makler".equals(roleAction.roleName))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "user".equals(roleAction.roleName))); + } + + @Test + public void testOneMissingTwoNew() { + Collection userRoles = new ArrayList<>(); + userRoles.add(generate("admin", Status.Active)); + userRoles.add(generate("broker", Status.Active)); + userRoles.add(generate("manager", Status.Active)); + Collection methodsFromAuth = List.of("admin", "broker", "makler", "user"); + + Collection roleActions = UserService.defineAllRoleChanges(userRoles, methodsFromAuth); + assertEquals(3, roleActions.size()); + assertTrue(roleActions.stream().anyMatch(roleAction -> !roleAction.create() + && roleAction.roleToChange.getUserRole().equals("manager") + && roleAction.newStatus.equals(Status.Blocked))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "makler".equals(roleAction.roleName))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "user".equals(roleAction.roleName))); + } + + @Test + public void testAllNew() { + Collection userRoles = new ArrayList<>(); + Collection methodsFromAuth = List.of("admin", "broker", "makler", "user"); + + Collection roleActions = UserService.defineAllRoleChanges(userRoles, methodsFromAuth); + assertEquals(4, roleActions.size()); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "admin".equals(roleAction.roleName))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "broker".equals(roleAction.roleName))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "makler".equals(roleAction.roleName))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "user".equals(roleAction.roleName))); + } + + @Test + public void testTwoNewRolesDifferentCase() { + Collection userRoles = new ArrayList<>(); + userRoles.add(generate("AdMin", Status.Active)); + userRoles.add(generate("broKer", Status.Active)); + userRoles.add(generate("MANAGER", Status.Blocked)); + Collection methodsFromAuth = List.of("admin", "broker", "makler", "user"); + + Collection roleActions = UserService.defineAllRoleChanges(userRoles, methodsFromAuth); + assertEquals(2, roleActions.size()); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "makler".equalsIgnoreCase(roleAction.roleName))); + assertTrue(roleActions.stream().anyMatch(roleAction -> roleAction.create() + && "user".equalsIgnoreCase(roleAction.roleName))); + } + + private static UserRoleSession generate(String roleName, Status roleStatus) { + UserRoleSession userRoleSession = new UserRoleSession(); + userRoleSession.setId(idGenerator.incrementAndGet()); + userRoleSession.setUserId(1L); + userRoleSession.setUserRole(roleName); + userRoleSession.setStatus(roleStatus.getKey()); + return userRoleSession; + } +} \ No newline at end of file diff --git a/platform-parent/platform-enum/pom.xml b/platform-parent/platform-enum/pom.xml new file mode 100644 index 000000000..c780fdbfe --- /dev/null +++ b/platform-parent/platform-enum/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + platform-enum + Platform enumerations + jar + 1.0.0 + + + platform-parent + ru.spcex.platform + 1.0.0 + + + + 17 + 17 + + + + + ru.spcex.platform + platform-utils + + + + \ No newline at end of file diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ConnectionState.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ConnectionState.java new file mode 100644 index 000000000..6e4713114 --- /dev/null +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ConnectionState.java @@ -0,0 +1,18 @@ +package ru.spcex.platform.enumeration; + +import ru.spcex.platform.utils.enumeration.IEnumKey; + +public enum ConnectionState implements IEnumKey { + Connected("CNCT"), Disconnected("DSBL"); + + private final String key; + + ConnectionState(String key) { + this.key = key; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/Status.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/Status.java new file mode 100644 index 000000000..fb90d4022 --- /dev/null +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/Status.java @@ -0,0 +1,18 @@ +package ru.spcex.platform.enumeration; + +import ru.spcex.platform.utils.enumeration.IEnumKey; + +public enum Status implements IEnumKey { + Active("ACTV"), Blocked("BLKD"); + + private final String key; + + Status(String key) { + this.key = key; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java index 87367525f..2f5bb615f 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java @@ -1,5 +1,6 @@ package ru.spcex.clearing.platform.messaging.domain; public enum ActionType { - NEW, UPDATE, DELETE; + NEW, UPDATE, DELETE, + SYSTEM; } diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java index 98532fa97..b2e32711d 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java @@ -22,4 +22,6 @@ public interface Consts { String DESTINATION_SDF08_NEW = "s-df-08-new"; String DESTINATION_SDF02_NEW = "s-df-02-new"; + + String USER_AUTH_SUCCESS = "user-auth-success"; } diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/utilities/UserAuthRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/utilities/UserAuthRequest.java new file mode 100644 index 000000000..114e4b998 --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/utilities/UserAuthRequest.java @@ -0,0 +1,76 @@ +package ru.spcex.clearing.platform.messaging.domain.cud.utilities; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import ru.spcex.clearing.platform.messaging.domain.json.deserialize.InstantDeserializer; +import ru.spcex.clearing.platform.messaging.domain.json.serialize.InstantSerializer; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public class UserAuthRequest { + @JsonProperty + private String username; + @JsonProperty + private String identifier; + @JsonProperty + private List roles = new ArrayList<>(); + @JsonSerialize(using = InstantSerializer.class) + @JsonDeserialize(using = InstantDeserializer.class) + @JsonProperty + private Instant time; + @JsonProperty + private String serverIp; + @JsonProperty + private String clientIp; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public Instant getTime() { + return time; + } + + public void setTime(Instant time) { + this.time = time; + } + + public String getServerIp() { + return serverIp; + } + + public void setServerIp(String serverIp) { + this.serverIp = serverIp; + } + + public String getClientIp() { + return clientIp; + } + + public void setClientIp(String clientIp) { + this.clientIp = clientIp; + } +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumKey.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumKey.java new file mode 100644 index 000000000..1542928db --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumKey.java @@ -0,0 +1,64 @@ +package ru.spcex.platform.utils.enumeration; + +import java.io.Serializable; +import java.util.Objects; + +public interface IEnumKey extends Serializable { + String getKey(); + + default boolean equalsByKey(String key) { + return key != null && getKey().equalsIgnoreCase(key); + } + + /** + * Проверяет что среди данного набора Enum, присутствует элемент с данным id + * id может быть null + */ + static & IEnumKey> boolean contains(String id, T... enumSet) { + for (T e : enumSet) { + if (Objects.equals(id, e.getKey())) + return true; + } + return false; + } + + static & IEnumKey> boolean contains(T e, T... enumSet) { + for (T enumEl : enumSet) { + if (enumEl.equals(e)) + return true; + } + return false; + } + + /** + * Проверяет что среди всех Enum данного класса, присутствует элемент с данным id + * id может быть null + */ + static & IEnumKey> boolean contains(Class enumClass, String key) { + for (T e : enumClass.getEnumConstants()) { + if (Objects.equals(key, e.getKey())) + return true; + } + return false; + } + + /** + * Возвращает Enum по id, если в заданном классе такой определен + * id может быть null + * + * @return Enum если нашел, иначе null + */ + static & IEnumKey> T getEnumByKey(Class enumClass, String key) { + for (T e : enumClass.getEnumConstants()) { + if (Objects.equals(key, e.getKey())) + return e; + } + return null; + } + + static String getKeyOrNull(IEnumKey enumVal) { + return enumVal == null ? null : enumVal.getKey(); + } + + String UNDEFINED_VALUE = "_Undefined"; +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/time/TimeUtil.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/time/TimeUtil.java index 94a33704c..a45d2564f 100644 --- a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/time/TimeUtil.java +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/time/TimeUtil.java @@ -19,4 +19,12 @@ public class TimeUtil { if (date == null) return null; return formatter.format(date.atZone(zone)); } + + public static Instant today() { + return localDateToInstant(LocalDate.now()); + } + + public static Instant localDateToInstant(LocalDate date) { + return date.atStartOfDay(zone).toInstant(); + } } diff --git a/platform-parent/pom.xml b/platform-parent/pom.xml index 18e173035..7c8c82ee9 100644 --- a/platform-parent/pom.xml +++ b/platform-parent/pom.xml @@ -22,6 +22,7 @@ platform-classes-base platform-imdg-api-hazelcast-impl platform-utils + platform-enum platform-messaging \ No newline at end of file diff --git a/pom.xml b/pom.xml index a2946836b..c0f8a8fe6 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,12 @@ 1.0.0 + + ru.spcex.platform + platform-enum + 1.0.0 + + ru.spcex.platform platform-imdg-api