Merge branch 'feature/SUPPORT-8817_1_9_5_adding_extract_request_with_empty_ervu_id' into hotfix/1.9.4

# Conflicts:
#	resources/src/main/resources/business-model/LK RP FL/screen-form-fl.page
This commit is contained in:
gulnaz 2025-01-31 11:23:48 +03:00
commit f57847eeea
12 changed files with 144 additions and 25 deletions

View file

@ -16,8 +16,13 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import rtl.pgs.ervu.proto.ExtractRegistry; import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData; import rtl.pgs.ervu.proto.ResponseData;
import ru.micord.ervu.dto.ExtractEmptyRequestDto;
import ru.micord.ervu.dto.ExtractRequestDto; import ru.micord.ervu.dto.ExtractRequestDto;
import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.esia.model.PersonModel;
import ru.micord.ervu.security.esia.service.PersonalDataService;
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil; import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
/** /**
@ -25,28 +30,48 @@ import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
*/ */
@RestController @RestController
public class ExtractController { public class ExtractController {
private final PersonalDataService personalDataService;
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService; private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
@Value("${ervu.kafka.registry.extract.empty.request.topic}")
private String registryExtractEmptyRequestTopic;
@Value("${ervu.kafka.registry.extract.request.topic}") @Value("${ervu.kafka.registry.extract.request.topic}")
private String registryExtractRequestTopic; private String registryExtractRequestTopic;
@Value("${ervu.kafka.registry.extract.reply.topic}") @Value("${ervu.kafka.registry.extract.reply.topic}")
private String registryExtractReplyTopic; private String registryExtractReplyTopic;
public ExtractController(ReplyingKafkaService<Object, Bytes> replyingKafkaService) { public ExtractController(PersonalDataService personalDataService, ReplyingKafkaService<Object, Bytes> replyingKafkaService) {
this.personalDataService = personalDataService;
this.replyingKafkaService = replyingKafkaService; this.replyingKafkaService = replyingKafkaService;
} }
@GetMapping(value = "/extract/{formatRegistry}") @GetMapping(value = "/extract/{formatRegistry}")
public ResponseEntity<Resource> getExtract(@PathVariable String formatRegistry) { public ResponseEntity<Resource> getExtract(@PathVariable String formatRegistry) {
String ervuId = SecurityUtil.getErvuId(); UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
if (ervuId == null) { String ervuId = userIdsPair.getErvuId();
return ResponseEntity.noContent().build(); byte[] reply;
}
if (ervuId != null) {
ExtractRequestDto request = new ExtractRequestDto(ervuId, formatRegistry); ExtractRequestDto request = new ExtractRequestDto(ervuId, formatRegistry);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic, reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic,
registryExtractReplyTopic, request).get(); registryExtractReplyTopic, request
).get();
}
else {
String esiaUserId = userIdsPair.getEsiaUserId(); // esiaUserid is not null here
String esiaAccessToken = EsiaTokensStore.getAccessToken(esiaUserId);
PersonModel personModel = personalDataService.getPersonModel(esiaAccessToken);
ExtractEmptyRequestDto emptyRequest = new ExtractEmptyRequestDto(
personModel.getLastName(),
personModel.getFirstName(), personModel.getMiddleName(), personModel.getBirthDate(),
personModel.getSnils(), formatRegistry
);
reply = replyingKafkaService.sendMessageAndGetReply(registryExtractEmptyRequestTopic,
registryExtractReplyTopic, emptyRequest
).get();
}
try { try {
ResponseData responseData = ResponseData.parseFrom(reply); ResponseData responseData = ResponseData.parseFrom(reply);

View file

@ -0,0 +1,8 @@
package ru.micord.ervu.dto;
/**
* @author r.latypov
*/
public record ExtractEmptyRequestDto(String lastName, String firstName, String middleName,
String birthDate, String snils, String formatExtractRegistry) {
}

View file

@ -48,7 +48,7 @@ import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.micord.ervu.security.webbpm.jwt.model.Token;
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername; import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUserEsiaId;
/** /**
* @author Eduard Tihomirov * @author Eduard Tihomirov
@ -412,7 +412,7 @@ public class EsiaAuthService {
private String getMessageId(Exception exception) { private String getMessageId(Exception exception) {
return Integer.toUnsignedString(Objects return Integer.toUnsignedString(Objects
.hashCode(getCurrentUsername()), 36) .hashCode(getCurrentUserEsiaId()), 36)
+ "-" + "-"
+ Integer.toUnsignedString(exception.hashCode(), 36); + Integer.toUnsignedString(exception.hashCode(), 36);
} }

View file

@ -17,8 +17,11 @@ public class JwtAuthentication implements Authentication {
private final Authentication authentication; private final Authentication authentication;
private final String token; private final String token;
private final UserIdsPair userIdsPair;
public JwtAuthentication(Authentication authentication, String userAccountId, String token) { public JwtAuthentication(Authentication authentication, String userAccountId, String token) {
this.userAccountId = userAccountId; this.userAccountId = userAccountId;
this.userIdsPair = new UserIdsPair(userAccountId);
this.authentication = authentication; this.authentication = authentication;
this.token = token; this.token = token;
} }
@ -31,6 +34,10 @@ public class JwtAuthentication implements Authentication {
return userAccountId; return userAccountId;
} }
public UserIdsPair getUserIdsPair() {
return userIdsPair;
}
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return authentication.getAuthorities(); return authentication.getAuthorities();

View file

@ -0,0 +1,36 @@
package ru.micord.ervu.security.webbpm.jwt;
public class UserIdsPair {
private final String esiaUserId;
private final String ervuId;
public UserIdsPair(String idsConcatenated) {
if (idsConcatenated == null) {
this.esiaUserId = null;
this.ervuId = null;
}
else {
String[] ids = idsConcatenated.split(":");
this.esiaUserId = ids[0];
this.ervuId = ids.length == 2 ? ids[1] : null;
}
}
public UserIdsPair(String esiaUserId, String ervuId) {
this.esiaUserId = esiaUserId;
this.ervuId = ervuId;
}
public String getEsiaUserId() {
return esiaUserId;
}
public String getErvuId() {
return ervuId;
}
public String getIdsConcatenated() {
return esiaUserId + (ervuId == null ? "" : ":" + ervuId);
}
}

View file

@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ru.micord.ervu.security.esia.token.EsiaTokensStore; import ru.micord.ervu.security.esia.token.EsiaTokensStore;
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.micord.ervu.security.webbpm.jwt.model.Token;
import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils; import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils;
@ -42,16 +43,17 @@ public class JwtTokenService {
} }
public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId) { public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId) {
String idsConcatenated = new UserIdsPair(userAccountId, ervuId).getIdsConcatenated();
Date expirationDate = new Date(System.currentTimeMillis() + 1000L * expiresIn); Date expirationDate = new Date(System.currentTimeMillis() + 1000L * expiresIn);
String value = Jwts.builder() String value = Jwts.builder()
.setSubject(userAccountId + ":" + ervuId) .setSubject(idsConcatenated)
.setIssuer(tokenIssuerName) .setIssuer(tokenIssuerName)
.setIssuedAt(new Date(System.currentTimeMillis())) .setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(expirationDate) .setExpiration(expirationDate)
.signWith(SIGNING_KEY) .signWith(SIGNING_KEY)
.compact(); .compact();
return new Token(userAccountId + ":" + ervuId, tokenIssuerName, expirationDate, value); return new Token(idsConcatenated, tokenIssuerName, expirationDate, value);
} }
public boolean isValid(Token token) { public boolean isValid(Token token) {
@ -64,8 +66,8 @@ public class JwtTokenService {
LOGGER.info("Token {} is expired ", token.getValue()); LOGGER.info("Token {} is expired ", token.getValue());
return false; return false;
} }
String[] ids = token.getUserAccountId().split(":"); String esiaUserId = new UserIdsPair(token.getUserAccountId()).getEsiaUserId();
return EsiaTokensStore.validateAccessToken(ids[0]); return EsiaTokensStore.validateAccessToken(esiaUserId);
} }
public Token getToken(String token) { public Token getToken(String token) {
@ -89,8 +91,8 @@ public class JwtTokenService {
String authToken = extractAuthToken(request); String authToken = extractAuthToken(request);
if (authToken != null) { if (authToken != null) {
String[] ids = getToken(authToken).getUserAccountId().split(":"); String esiaUserId = new UserIdsPair(getToken(authToken).getUserAccountId()).getEsiaUserId();
return ids[0]; return esiaUserId;
} }
else { else {
throw new RuntimeException("Failed to get auth data. User unauthorized."); throw new RuntimeException("Failed to get auth data. User unauthorized.");

View file

@ -8,6 +8,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication; import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
public final class SecurityUtil { public final class SecurityUtil {
public static final String AUTH_TOKEN = "auth_token"; public static final String AUTH_TOKEN = "auth_token";
@ -23,17 +24,13 @@ public final class SecurityUtil {
return cookie != null ? cookie.getValue() : null; return cookie != null ? cookie.getValue() : null;
} }
public static String getErvuId() { public static UserIdsPair getUserIdsPair() {
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.map(a -> ((JwtAuthentication) a).getUserAccountId()) .map(a -> ((JwtAuthentication) a).getUserIdsPair())
.map(userAccountId -> {
String ervuId = userAccountId.split(":")[1];
return "null".equals(ervuId) ? null : ervuId;
})
.orElse(null); .orElse(null);
} }
public static String getCurrentUsername() { public static String getCurrentUserEsiaId() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) { if (auth != null && auth.isAuthenticated()) {
return auth.getName(); return auth.getName();

View file

@ -31,7 +31,9 @@ public class SubpoenaService {
} }
public SubpoenaResponseDto getSubpoenaData() { public SubpoenaResponseDto getSubpoenaData() {
String ervuId = SecurityUtil.getErvuId(); String ervuId = SecurityUtil.getUserIdsPair() == null
? null
: SecurityUtil.getUserIdsPair().getErvuId();
if (ervuId == null) { if (ervuId == null) {
return new SubpoenaResponseDto.Builder().build(); return new SubpoenaResponseDto.Builder().build();

View file

@ -784,7 +784,8 @@ JBPM использует 3 корневых категории логирова
- `ERVU_KAFKA_RECRUIT_HEADER_CLASS` - класс для идентификации в заголовке запроса на получение данных о повестке, временных мерах и воинском учете - `ERVU_KAFKA_RECRUIT_HEADER_CLASS` - класс для идентификации в заголовке запроса на получение данных о повестке, временных мерах и воинском учете
- `ERVU_KAFKA_SUBPOENA_EXTRACT_REQUEST_TOPIC` - топик для отправки запроса на получение выписки из Реестра повесток - `ERVU_KAFKA_SUBPOENA_EXTRACT_REQUEST_TOPIC` - топик для отправки запроса на получение выписки из Реестра повесток
- `ERVU_KAFKA_SUBPOENA_EXTRACT_REPLY_TOPIC` - топик для получения выписки из Реестра повесток - `ERVU_KAFKA_SUBPOENA_EXTRACT_REPLY_TOPIC` - топик для получения выписки из Реестра повесток
- `ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC` - топик для отправки запроса на получение выписки из Реестра воинского учета - `ERVU_KAFKA_REGISTRY_EXTRACT_EMPTY_REQUEST_TOPIC` - топик для отправки запроса на получение выписки из Реестра воинского учета при отсутствии ErvuId
- `ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC` - топик для отправки запроса на получение выписки из Реестра воинского учета при наличии ErvuId
- `ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC` - топик для получения выписки из Реестра воинского учета - `ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC` - топик для получения выписки из Реестра воинского учета
- `ERVU_KAFKA_EXTRACT_HEADER_CLASS` - класс для идентификации в заголовке запроса на получение выписки из Реестра повесток/Реестра воинского учета - `ERVU_KAFKA_EXTRACT_HEADER_CLASS` - класс для идентификации в заголовке запроса на получение выписки из Реестра повесток/Реестра воинского учета

39
config/local.env Normal file
View file

@ -0,0 +1,39 @@
TZ=Europe/Moscow
# App datasource
DB_APP_USERNAME=ervu_lkrp_fl
DB_APP_PASSWORD=ervu_lkrp_fl
DB_APP_HOST=10.10.31.119
DB_APP_PORT=5432
DB_APP_NAME=ervu_lkrp_fl
ESIA_SCOPES=snils, fullname, birthdate, id_doc
ESIA_BASE_URI=https://esia-portal1.test.gosuslugi.ru/
ESIA_ISSUER_URL=http://esia-portal1.test.gosuslugi.ru/
ESIA_CLIENT_ID=MNSV93
ESIA_CLIENT_CERT_HASH=CF35A98C48E48665EA73530537BAFBB51F911C434ADC89215C2F86DCD04E28C5
ESIA_REDIRECT_URL=http://localhost:8080/
SIGN_URL=https://ervu-sign-dev.k8s.micord.ru/sign
SIGN_VERIFY_URL=https://ervu-sign-dev.k8s.micord.ru/verify
ERVU_KAFKA_BOOTSTRAP_SERVERS=local-kafka:9094
ERVU_KAFKA_USERNAME=user2
ERVU_KAFKA_PASSWORD=Blfi9d2OFG
ERVU_KAFKA_SASL_MECHANISM=PLAIN
ERVU_KAFKA_SECURITY_PROTOCOL=PLAINTEXT
ERVU_KAFKA_GROUP_ID=ervu-lkrp-fl-new
ERVU_KAFKA_REPLY_TOPIC=ervu.lkpr.person.search.response
ERVU_KAFKA_REQUEST_TOPIC=ervu.lkpr.person.search.request
ERVU_KAFKA_REPLY_TIMEOUT=5
ERVU_KAFKA_RECRUIT_REQUEST_TOPIC=ervu.recruit.info.request
ERVU_KAFKA_RECRUIT_REPLY_TOPIC=ervu.recruit.info.response
ERVU_KAFKA_RECRUIT_HEADER_CLASS=Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5
ERVU_KAFKA_REGISTRY_EXTRACT_EMPTY_REQUEST_TOPIC=ervu.extract.empty.request
ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC=ervu.extract.info.request
ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC=ervu.extract.info.response
ERVU_KAFKA_EXTRACT_HEADER_CLASS=request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3
ERVU_KAFKA_DOC_LOGIN_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
COOKIE_PATH=/fl

View file

@ -28,6 +28,7 @@ ERVU_KAFKA_REPLY_TIMEOUT=30
ERVU_KAFKA_RECRUIT_REQUEST_TOPIC=ervu.recruit.info.request ERVU_KAFKA_RECRUIT_REQUEST_TOPIC=ervu.recruit.info.request
ERVU_KAFKA_RECRUIT_REPLY_TOPIC=ervu.recruit.info.response ERVU_KAFKA_RECRUIT_REPLY_TOPIC=ervu.recruit.info.response
ERVU_KAFKA_RECRUIT_HEADER_CLASS=Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5 ERVU_KAFKA_RECRUIT_HEADER_CLASS=Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5
ERVU_KAFKA_REGISTRY_EXTRACT_EMPTY_REQUEST_TOPIC=ervu.extract.empty.request
ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC=ervu.extract.info.request ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC=ervu.extract.info.request
ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC=ervu.extract.info.response ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC=ervu.extract.info.response
ERVU_KAFKA_EXTRACT_HEADER_CLASS=request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3 ERVU_KAFKA_EXTRACT_HEADER_CLASS=request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3

View file

@ -75,6 +75,7 @@
<property name="ervu.kafka.recruit.request.topic" value="ervu.recruit.info.request"/> <property name="ervu.kafka.recruit.request.topic" value="ervu.recruit.info.request"/>
<property name="ervu.kafka.recruit.reply.topic" value="ervu.recruit.info.response"/> <property name="ervu.kafka.recruit.reply.topic" value="ervu.recruit.info.response"/>
<property name="ervu.kafka.recruit.header.class" value="Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5"/> <property name="ervu.kafka.recruit.header.class" value="Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5"/>
<property name="ervu.kafka.registry.extract.empty.request.topic" value="ervu.extract.empty.request"/>
<property name="ervu.kafka.registry.extract.request.topic" value="ervu.extract.info.request"/> <property name="ervu.kafka.registry.extract.request.topic" value="ervu.extract.info.request"/>
<property name="ervu.kafka.registry.extract.reply.topic" value="ervu.extract.info.response"/> <property name="ervu.kafka.registry.extract.reply.topic" value="ervu.extract.info.response"/>
<property name="ervu.kafka.extract.header.class" value="request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3"/> <property name="ervu.kafka.extract.header.class" value="request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3"/>