Merge remote-tracking branch 'origin/hotfix/1.9.8' into feature/SUPPORT-8897_fix_handler

This commit is contained in:
Eduard Tihomirov 2025-02-24 11:03:37 +03:00
commit cb6b472268
49 changed files with 1425 additions and 121 deletions

View file

@ -5,7 +5,7 @@
<parent>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId>
<version>1.9.5-SNAPSHOT</version>
<version>1.9.8-SNAPSHOT</version>
</parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId>
<artifactId>backend</artifactId>

View file

@ -0,0 +1,88 @@
package ru.micord.ervu.audit.config;
import java.util.HashMap;
import java.util.Map;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.config.SaslConfigs;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.TopicBuilder;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaAdmin;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
/**
* @author Adel Kalimullin
*/
@Configuration
public class AuditKafkaConfig {
@Value("${audit.kafka.bootstrap.servers}")
private String bootstrapServers;
@Value("${audit.kafka.security.protocol}")
private String securityProtocol;
@Value("${audit.kafka.login.module:org.apache.kafka.common.security.scram.ScramLoginModule}")
private String loginModule;
@Value("${audit.kafka.username}")
private String username;
@Value("${audit.kafka.password}")
private String password;
@Value("${audit.kafka.sasl.mechanism}")
private String saslMechanism;
@Value("${audit.kafka.authorization.topic}")
private String authorizationTopic;
@Value("${audit.kafka.action.topic}")
private String actionTopic;
@Value("${audit.kafka.file.download.topic}")
private String fileDownloadTopic;
@Bean("auditProducerFactory")
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol);
configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\""
+ username + "\" password=\"" + password + "\";");
configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism);
return new DefaultKafkaProducerFactory<>(configProps);
}
@Bean("auditTemplate")
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
@Bean
public KafkaAdmin auditKafkaAdmin() {
Map<String, Object> configs = new HashMap<>();
configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
configs.put(AdminClientConfig.SECURITY_PROTOCOL_CONFIG, securityProtocol);
configs.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\""
+ username + "\" password=\"" + password + "\";");
configs.put(SaslConfigs.SASL_MECHANISM, saslMechanism);
return new KafkaAdmin(configs);
}
@Bean
public NewTopic auditAuthTopic() {
return TopicBuilder.name(authorizationTopic).build();
}
@Bean
public NewTopic auditActionTopic() {
return TopicBuilder.name(actionTopic).build();
}
@Bean
public NewTopic auditDownloadTopic() {
return TopicBuilder.name(fileDownloadTopic).build();
}
}

View file

@ -0,0 +1,43 @@
package ru.micord.ervu.audit.constants;
import java.util.Map;
import java.util.Optional;
/**
* @author Adel Kalimullin
*/
public final class AuditConstants {
public static final String SUBSYSTEM_TYPE = "FL";
public static final String LOGOUT_EVENT_TYPE = "logout";
public static final String LOGIN_EVENT_TYPE = "login";
public static final String SUCCESS_STATUS = "success";
public static final String FAILURE_STATUS = "failure";
private static final Map<String, String> routeDescriptions = Map.of(
"/", "Главная",
"/mydata", "Мои данные",
"/subpoena", "Повестки",
"/restriction", "Временные меры"
);
private static final Map<String, String> downloadTypes = Map.of(
"1", "Выписка из реестра воинского учета ФЛ",
"2", "Выписка из реестра повесток ФЛ"
);
public static String getRouteDescription(String route) {
return Optional.ofNullable(routeDescriptions.get(route))
.orElseThrow(() -> new IllegalArgumentException("Invalid route :" + route));
}
public static String getDownloadType(String formatRegistry) {
return Optional.ofNullable(downloadTypes.get(formatRegistry))
.orElseThrow(
() -> new IllegalArgumentException("Invalid formatRegistry :" + formatRegistry));
}
private AuditConstants() {
}
}

View file

@ -0,0 +1,31 @@
package ru.micord.ervu.audit.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.micord.ervu.audit.model.AuditActionRequest;
import ru.micord.ervu.audit.service.AuditService;
/**
* @author Adel Kalimullin
*/
@RestController
@RequestMapping("/audit")
public class AuditController {
private final AuditService auditService;
public AuditController(AuditService auditService) {
this.auditService = auditService;
}
@PostMapping("/action")
public ResponseEntity<Void> auditAction(HttpServletRequest request,
@RequestBody AuditActionRequest actionEvent) {
auditService.processActionEvent(request, actionEvent);
return ResponseEntity.ok().build();
}
}

View file

@ -0,0 +1,53 @@
package ru.micord.ervu.audit.model;
/**
* @author Adel Kalimullin
*/
public class AuditActionEvent extends AuditEvent {
private String eventType;
private String description;
private String sourceUrl;
private String fileName;
public AuditActionEvent(
String esiaPersonId, String eventTime, String eventType,
String description, String sourceUrl, String fileName) {
super(esiaPersonId, eventTime);
this.eventType = eventType;
this.description = description;
this.sourceUrl = sourceUrl;
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSourceUrl() {
return sourceUrl;
}
public void setSourceUrl(String sourceUrl) {
this.sourceUrl = sourceUrl;
}
}

View file

@ -0,0 +1,43 @@
package ru.micord.ervu.audit.model;
/**
* @author Adel Kalimullin
*/
public class AuditActionRequest {
private String route;
private String sourceUrl;
private String eventType;
private String fileName;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getRoute() {
return route;
}
public void setRoute(String route) {
this.route = route;
}
public String getSourceUrl() {
return sourceUrl;
}
public void setSourceUrl(String sourceUrl) {
this.sourceUrl = sourceUrl;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
}

View file

@ -0,0 +1,113 @@
package ru.micord.ervu.audit.model;
public class AuditAuthorizationEvent extends AuditEvent {
private String status;
private String eventType;
private String firstName;
private String lastName;
private String middleName;
private String snils;
private String serverIp;
private String serverHostName;
private String clientIp;
private String clientHostName;
public AuditAuthorizationEvent(
String esiaPersonId, String eventTime, String firstName,
String lastName, String middleName, String snils,
String status, String eventType, String serverIp,
String serverHostName, String clientIp, String clientHostName) {
super(esiaPersonId, eventTime);
this.status = status;
this.firstName = firstName;
this.lastName = lastName;
this.middleName = middleName;
this.snils = snils;
this.eventType = eventType;
this.serverIp = serverIp;
this.serverHostName = serverHostName;
this.clientIp = clientIp;
this.clientHostName = clientHostName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getSnils() {
return snils;
}
public void setSnils(String snils) {
this.snils = snils;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public String getServerIp() {
return serverIp;
}
public void setServerIp(String serverIp) {
this.serverIp = serverIp;
}
public String getServerHostName() {
return serverHostName;
}
public void setServerHostName(String serverHostName) {
this.serverHostName = serverHostName;
}
public String getClientIp() {
return clientIp;
}
public void setClientIp(String clientIp) {
this.clientIp = clientIp;
}
public String getClientHostName() {
return clientHostName;
}
public void setClientHostName(String clientHostName) {
this.clientHostName = clientHostName;
}
}

View file

@ -0,0 +1,65 @@
package ru.micord.ervu.audit.model;
/**
* @author Adel Kalimullin
*/
public class AuditDownloadEvent extends AuditEvent {
private String downloadType;
private String fileName;
private String s3FileUrl;
private String fileSize;
private String status;
public AuditDownloadEvent(
String esiaPersonId, String eventTime, String downloadType,
String fileName, String s3FileUrl, String fileSize,
String status) {
super(esiaPersonId, eventTime);
this.downloadType = downloadType;
this.fileName = fileName;
this.s3FileUrl = s3FileUrl;
this.fileSize = fileSize;
this.status = status;
}
public String getS3FileUrl() {
return s3FileUrl;
}
public void setS3FileUrl(String s3FileUrl) {
this.s3FileUrl = s3FileUrl;
}
public String getDownloadType() {
return downloadType;
}
public void setDownloadType(String downloadType) {
this.downloadType = downloadType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileSize() {
return fileSize;
}
public void setFileSize(String fileSize) {
this.fileSize = fileSize;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View file

@ -0,0 +1,38 @@
package ru.micord.ervu.audit.model;
import ru.micord.ervu.audit.constants.AuditConstants;
/**
* @author Adel Kalimullin
*/
public abstract class AuditEvent {
protected final String subsystem = AuditConstants.SUBSYSTEM_TYPE;
protected String esiaPersonId;
protected String eventTime;
protected AuditEvent(String esiaPersonId, String eventTime) {
this.esiaPersonId = esiaPersonId;
this.eventTime = eventTime;
}
public String getEsiaPersonId() {
return esiaPersonId;
}
public void setEsiaPersonId(String esiaPersonId) {
this.esiaPersonId = esiaPersonId;
}
public String getSubsystem() {
return subsystem;
}
public String getEventTime() {
return eventTime;
}
public void setEventTime(String eventTime) {
this.eventTime = eventTime;
}
}

View file

@ -0,0 +1,8 @@
package ru.micord.ervu.audit.service;
/**
* @author Adel Kalimullin
*/
public interface AuditKafkaPublisher {
void publishEvent(String topicName, String message);
}

View file

@ -0,0 +1,19 @@
package ru.micord.ervu.audit.service;
import javax.servlet.http.HttpServletRequest;
import ru.micord.ervu.audit.model.AuditActionRequest;
import ru.micord.ervu.security.esia.model.PersonModel;
/**
* @author Adel Kalimullin
*/
public interface AuditService {
void processActionEvent(HttpServletRequest request, AuditActionRequest auditActionRequest);
void processAuthEvent(HttpServletRequest request, PersonModel personModel, String status,
String eventType);
void processDownloadEvent(HttpServletRequest request, int fileSize, String fileName,
String formatRegistry, String status);
}

View file

@ -0,0 +1,42 @@
package ru.micord.ervu.audit.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import ru.micord.ervu.audit.service.AuditKafkaPublisher;
/**
* @author Adel Kalimullin
*/
@Service
public class BaseAuditKafkaPublisher implements AuditKafkaPublisher {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseAuditKafkaPublisher.class);
private final KafkaTemplate<String, String> kafkaTemplate;
@Value("${audit.kafka.enabled}")
private boolean auditEnabled;
public BaseAuditKafkaPublisher(
@Qualifier("auditTemplate") KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
@Override
public void publishEvent(String topic, String message) {
if (auditEnabled) {
kafkaTemplate.send(topic, message)
.addCallback(
result -> {
},
ex -> LOGGER.error("Failed to send message to topic {}: {}", topic, ex.getMessage(),
ex
)
);
}
else {
LOGGER.info("Audit is disabled. Event not published.");
}
}
}

View file

@ -0,0 +1,114 @@
package ru.micord.ervu.audit.service.impl;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.micord.ervu.audit.constants.AuditConstants;
import ru.micord.ervu.audit.model.AuditActionEvent;
import ru.micord.ervu.audit.model.AuditActionRequest;
import ru.micord.ervu.audit.model.AuditAuthorizationEvent;
import ru.micord.ervu.audit.model.AuditDownloadEvent;
import ru.micord.ervu.audit.service.AuditKafkaPublisher;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.security.esia.model.PersonModel;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.util.DateUtils;
import ru.micord.ervu.util.NetworkUtils;
/**
* @author Adel Kalimullin
*/
@Service
public class BaseAuditService implements AuditService {
private final AuditKafkaPublisher auditPublisher;
private final JwtTokenService jwtTokenService;
private final ObjectMapper objectMapper;
@Value("${audit.kafka.authorization.topic}")
private String authorizationTopic;
@Value("${audit.kafka.action.topic}")
private String actionTopic;
@Value("${audit.kafka.file.download.topic}")
private String fileDownloadTopic;
public BaseAuditService(AuditKafkaPublisher auditPublisher, JwtTokenService jwtTokenService,
ObjectMapper objectMapper) {
this.auditPublisher = auditPublisher;
this.jwtTokenService = jwtTokenService;
this.objectMapper = objectMapper;
}
@Override
public void processActionEvent(
HttpServletRequest request, AuditActionRequest auditActionRequest) {
String userAccountId = jwtTokenService.getUserAccountId(request);
String description = AuditConstants.getRouteDescription(auditActionRequest.getRoute());
AuditActionEvent event = new AuditActionEvent(
userAccountId,
DateUtils.getClientTimeFromRequest(request),
auditActionRequest.getEventType(),
description,
auditActionRequest.getSourceUrl(),
auditActionRequest.getFileName()
);
String message = convertToMessage(event);
auditPublisher.publishEvent(actionTopic, message);
}
@Override
public void processAuthEvent(HttpServletRequest request, PersonModel personModel, String status,
String eventType) {
String serverIp = NetworkUtils.getServerIp();
String clientIp = NetworkUtils.getClientIp(request);
String serverHostName = NetworkUtils.getHostName(serverIp);
String clientHostName = NetworkUtils.getHostName(clientIp);
AuditAuthorizationEvent event = new AuditAuthorizationEvent(
personModel.getPrnsId(),
DateUtils.getClientTimeFromRequest(request),
personModel.getFirstName(),
personModel.getLastName(),
personModel.getMiddleName(),
personModel.getSnils(),
status,
eventType,
serverIp,
serverHostName,
clientIp,
clientHostName
);
String message = convertToMessage(event);
auditPublisher.publishEvent(authorizationTopic, message);
}
@Override
public void processDownloadEvent(
HttpServletRequest request, int fileSize, String fileName, String formatRegistry,
String status) {
String userAccountId = jwtTokenService.getUserAccountId(request);
AuditDownloadEvent event = new AuditDownloadEvent(
userAccountId,
DateUtils.getClientTimeFromRequest(request),
AuditConstants.getDownloadType(formatRegistry),
fileName,
null,
String.valueOf(fileSize),
status
);
String message = convertToMessage(event);
auditPublisher.publishEvent(fileDownloadTopic, message);
}
private String convertToMessage(Object event) {
try {
return objectMapper.writeValueAsString(event);
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -2,9 +2,11 @@ package ru.micord.ervu.controller;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
@ -15,10 +17,13 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData;
import ru.micord.ervu.audit.constants.AuditConstants;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.dto.ExtractEmptyRequestDto;
import ru.micord.ervu.dto.ExtractRequestDto;
import ru.micord.ervu.kafka.dto.EmptyExtract;
import ru.micord.ervu.kafka.dto.Extract;
import ru.micord.ervu.kafka.dto.FullExtract;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.esia.model.PersonModel;
import ru.micord.ervu.security.esia.service.PersonalDataService;
@ -26,6 +31,8 @@ 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 javax.servlet.http.HttpServletRequest;
/**
* @author gulnaz
*/
@ -33,6 +40,7 @@ import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
public class ExtractController {
private final PersonalDataService personalDataService;
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
private final AuditService auditService;
@Value("${ervu.kafka.registry.extract.empty.request.topic}")
private String registryExtractEmptyRequestTopic;
@ -40,59 +48,72 @@ public class ExtractController {
private String registryExtractRequestTopic;
@Value("${ervu.kafka.registry.extract.reply.topic}")
private String registryExtractReplyTopic;
@Value("${ervu.kafka.registry.extract.type.header:empty}")
private String registryExtractTypeHeader;
public ExtractController(PersonalDataService personalDataService, ReplyingKafkaService<Object, Bytes> replyingKafkaService) {
public ExtractController(
PersonalDataService personalDataService,
ReplyingKafkaService<Object, Bytes> replyingKafkaService,
AuditService auditService
) {
this.personalDataService = personalDataService;
this.replyingKafkaService = replyingKafkaService;
this.auditService = auditService;
}
@GetMapping(value = "/extract/{formatRegistry}")
public ResponseEntity<Resource> getExtract(@PathVariable String formatRegistry) {
public ResponseEntity<Resource> getExtract(HttpServletRequest servletRequest, @PathVariable String formatRegistry) {
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
String ervuId = userIdsPair.getErvuId();
String fileName;
ByteString file;
ConsumerRecord<String, Bytes> record;
boolean isEmpty = true;
if (ervuId != null) {
ExtractRequestDto request = new ExtractRequestDto(ervuId, formatRegistry);
record = replyingKafkaService.sendMessageAndGetReply(
registryExtractRequestTopic, registryExtractReplyTopic, request);
isEmpty = Arrays.stream(record.headers().toArray())
.filter(header -> header.key().equals(registryExtractTypeHeader))
.findFirst()
.map(header -> Boolean.parseBoolean(new String(header.value(), StandardCharsets.UTF_8)))
.orElseThrow();
}
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
);
record = replyingKafkaService.sendMessageAndGetReply(registryExtractEmptyRequestTopic,
registryExtractReplyTopic, emptyRequest);
}
byte[] bytes = record.value().get();
String fileName = null;
int size = 0;
try {
if (ervuId != null) {
ExtractRequestDto request = new ExtractRequestDto(ervuId, formatRegistry);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic,
registryExtractReplyTopic, request).get();
ResponseData responseData = ResponseData.parseFrom(reply);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
fileName = extractRegistry.getFileName();
file = extractRegistry.getFile();
}
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
);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractEmptyRequestTopic,
registryExtractReplyTopic, emptyRequest).get();
rtl.pgs.ervu.proto.emptyrequest.ResponseData responseData = rtl.pgs.ervu.proto.emptyrequest.ResponseData
.parseFrom(reply);
rtl.pgs.ervu.proto.emptyrequest.ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
fileName = extractRegistry.getFileName();
file = extractRegistry.getFile();
}
Extract extract = ervuId == null || isEmpty ? new EmptyExtract(bytes) : new FullExtract(bytes);
fileName = extract.getFileName();
String encodedFilename = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
ByteString file = extract.getFile();
InputStreamResource resource = new InputStreamResource(file.newInput());
size = file.size();
auditService.processDownloadEvent(servletRequest, size, fileName, formatRegistry,
AuditConstants.SUCCESS_STATUS
);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedFilename)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
catch (InvalidProtocolBufferException e) {
auditService.processDownloadEvent(servletRequest, size, fileName, formatRegistry,
AuditConstants.FAILURE_STATUS
);
throw new RuntimeException("Failed to parse data", e);
}
}

View file

@ -10,15 +10,14 @@ import proto.ervu.rp.summons.RecruitmentInfo;
import ru.micord.ervu.dto.Restriction;
import ru.micord.ervu.dto.SubpoenaResponseDto;
import org.springframework.stereotype.Component;
import proto.ervu.rp.summons.MeasuresTemporary;
import proto.ervu.rp.summons.ResponseDataAddress;
import proto.ervu.rp.summons.SummonsInfo;
import proto.ervu.rp.summons.SummonsResponseData;
import static ru.micord.ervu.util.DateUtil.convertToLocalDate;
import static ru.micord.ervu.util.DateUtils.convertToLocalDate;
import static java.util.Objects.requireNonNull;
import static ru.micord.ervu.util.DateUtil.convertToString;
import static ru.micord.ervu.util.DateUtil.getDaysTill;
import static ru.micord.ervu.util.DateUtils.convertToString;
import static ru.micord.ervu.util.DateUtils.getDaysTill;
/**
* @author gulnaz

View file

@ -4,8 +4,8 @@ import java.util.ArrayList;
import java.util.List;
import static org.springframework.util.StringUtils.hasText;
import static ru.micord.ervu.util.DateUtil.convertToLocalDate;
import static ru.micord.ervu.util.DateUtil.convertToString;
import static ru.micord.ervu.util.DateUtils.convertToLocalDate;
import static ru.micord.ervu.util.DateUtils.convertToString;
/**
* @author gulnaz

View file

@ -0,0 +1,19 @@
package ru.micord.ervu.kafka.dto;
import com.google.protobuf.InvalidProtocolBufferException;
import rtl.pgs.ervu.proto.emptyrequest.ExtractRegistry;
import rtl.pgs.ervu.proto.emptyrequest.ResponseData;
/**
* @author gulnaz
*/
public class EmptyExtract extends Extract {
public EmptyExtract(byte[] bytes) throws InvalidProtocolBufferException {
ResponseData responseData = ResponseData.parseFrom(bytes);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
fileName = extractRegistry.getFileName();
file = extractRegistry.getFile();
}
}

View file

@ -0,0 +1,20 @@
package ru.micord.ervu.kafka.dto;
import com.google.protobuf.ByteString;
/**
* @author gulnaz
*/
public abstract class Extract {
protected String fileName;
protected ByteString file;
public String getFileName() {
return fileName;
}
public ByteString getFile() {
return file;
}
}

View file

@ -0,0 +1,19 @@
package ru.micord.ervu.kafka.dto;
import com.google.protobuf.InvalidProtocolBufferException;
import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData;
/**
* @author gulnaz
*/
public class FullExtract extends Extract {
public FullExtract(byte[] bytes) throws InvalidProtocolBufferException {
ResponseData responseData = ResponseData.parseFrom(bytes);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
fileName = extractRegistry.getFileName();
file = extractRegistry.getFile();
}
}

View file

@ -0,0 +1,18 @@
package ru.micord.ervu.kafka.exception;
import org.springframework.context.support.MessageSourceAccessor;
import ru.cg.webbpm.modules.core.runtime.api.LocalizedException;
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
/**
* @author Emir Suleimanov
*/
public class KafkaMessageReplyTimeoutException extends LocalizedException {
private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor("messages/common_errors_messages");
private static final String KAFKA_REPLY_TIMEOUT = "kafka_reply_timeout";
public KafkaMessageReplyTimeoutException(Throwable cause) {
super(KAFKA_REPLY_TIMEOUT, MESSAGE_SOURCE, cause);
}
}

View file

@ -1,8 +1,10 @@
package ru.micord.ervu.kafka.service;
import org.apache.kafka.clients.consumer.ConsumerRecord;
public interface ReplyingKafkaService<T, V> {
V sendMessageAndGetReply(String requestTopic,
String replyTopic,
T requestMessage);
ConsumerRecord<String, V> sendMessageAndGetReply(String requestTopic,
String replyTopic,
T requestMessage);
}

View file

@ -10,6 +10,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.requestreply.ReplyingKafkaTemplate;
import org.springframework.kafka.requestreply.RequestReplyFuture;
import ru.micord.ervu.kafka.exception.KafkaMessageReplyTimeoutException;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
/**
@ -19,14 +20,13 @@ public abstract class BaseReplyingKafkaService<T, V> implements ReplyingKafkaSer
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
public V sendMessageAndGetReply(String requestTopic, String replyTopic, T requestMessage) {
public ConsumerRecord<String, V> sendMessageAndGetReply(String requestTopic, String replyTopic, T requestMessage) {
long startTime = System.currentTimeMillis();
RequestReplyFuture<String, T, V> replyFuture = getTemplate().sendAndReceive(
getProducerRecord(requestTopic, replyTopic, requestMessage));
try {
V result = Optional.ofNullable(replyFuture.get())
.map(ConsumerRecord::value)
ConsumerRecord<String, V> result = Optional.ofNullable(replyFuture.get())
.orElseThrow(() -> new RuntimeException("Kafka return result is null"));
LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms",
Thread.currentThread().getId(), System.currentTimeMillis() - startTime);
@ -35,7 +35,7 @@ public abstract class BaseReplyingKafkaService<T, V> implements ReplyingKafkaSer
catch (InterruptedException | ExecutionException e) {
LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms",
Thread.currentThread().getId(), System.currentTimeMillis() - startTime);
throw new RuntimeException("Failed to get kafka response", e);
throw new KafkaMessageReplyTimeoutException(e);
}
}
protected abstract ReplyingKafkaTemplate<String, T, V> getTemplate();

View file

@ -39,10 +39,10 @@ public class EsiaController {
}
@GetMapping(value = "/esia/auth")
public ResponseEntity<?> esiaAuth(@RequestParam(value = "code", required = false) String code,
public void esiaAuth(@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "error", required = false) String error, HttpServletRequest request,
HttpServletResponse response) {
return esiaAuthService.getEsiaTokensByCode(code, error, request, response);
esiaAuthService.authEsiaTokensByCode(code, error, request, response);
}
@RequestMapping(value = "/esia/refresh")

View file

@ -24,9 +24,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.core.context.SecurityContext;
import ru.micord.ervu.audit.constants.AuditConstants;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.kafka.model.Document;
import ru.micord.ervu.kafka.model.Person;
import ru.micord.ervu.kafka.model.Response;
@ -50,6 +51,7 @@ import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.security.webbpm.jwt.model.Token;
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUserEsiaId;
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
/**
* @author Eduard Tihomirov
@ -57,7 +59,8 @@ import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUse
@Service
public class EsiaAuthService {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor(
"messages/common_errors_messages");
@Autowired
private ObjectMapper objectMapper;
@ -74,6 +77,9 @@ public class EsiaAuthService {
@Autowired
private PersonalDataService personalDataService;
@Autowired
private AuditService auditService;
@Autowired
private SecurityHelper securityHelper;
@ -151,13 +157,10 @@ public class EsiaAuthService {
return uriBuilder.toString();
}
public ResponseEntity<?> getEsiaTokensByCode(String esiaAuthCode, String error,
public void authEsiaTokensByCode(String esiaAuthCode, String error,
HttpServletRequest request, HttpServletResponse response) {
if (error != null && !error.equals("null")) {
return new ResponseEntity<>(
"Произошла неизвестная ошибка. Обратитесь к системному администратору",
HttpStatus.FORBIDDEN
);
throw new EsiaException(error);
}
String esiaAccessTokenStr = null;
String prnOid = null;
@ -239,21 +242,26 @@ public class EsiaAuthService {
Thread.currentThread().getId(), signSecret, requestAccessToken, verifySecret);
}
PersonModel personModel = null;
String ervuId = null, status = null;
try {
personModel = personalDataService.getPersonModel(esiaAccessTokenStr);
Response ervuIdResponse = getErvuIdResponse(personModel);
createTokenAndAddCookie(response, prnOid, ervuIdResponse.getErvuId(), expiresIn);
return ResponseEntity.ok("Authentication successful");
ervuId = ervuIdResponse.getErvuId();
status = AuditConstants.SUCCESS_STATUS;
}
catch (Exception e) {
createTokenAndAddCookie(response, prnOid, null, expiresIn);
String messageId = getMessageId(e);
String messageWithId = String.format("[%s] %s", messageId, e.getMessage());
LOGGER.error(messageWithId, e);
return new ResponseEntity<>(
"Произошла ошибка " + messageId + ". Обратитесь к системному администратору",
HttpStatus.FORBIDDEN
);
status = AuditConstants.FAILURE_STATUS;
if (e instanceof EsiaException || e instanceof JsonProcessingException) {
throw new EsiaException(e);
}
}
finally {
if (personModel != null) {
auditService.processAuthEvent(
request, personModel, status, AuditConstants.LOGIN_EVENT_TYPE
);
}
createTokenAndAddCookie(response, prnOid, ervuId, expiresIn);
}
}
@ -359,9 +367,12 @@ public class EsiaAuthService {
}
public String logout(HttpServletRequest request, HttpServletResponse response) {
PersonModel personModel = null;
try {
securityHelper.clearAccessCookies(response);
String userId = jwtTokenService.getUserAccountId(request);
String accessToken = EsiaTokensStore.getAccessToken(userId);
personModel = personalDataService.getPersonModel(accessToken);
securityHelper.clearAccessCookies(response);
EsiaTokensStore.removeAccessToken(userId);
EsiaTokensStore.removeRefreshToken(userId);
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
@ -369,10 +380,19 @@ public class EsiaAuthService {
URL url = new URL(logoutUrl);
Map<String, String> params = mapOf(
"client_id", esiaConfig.getClientId(),
"redirect_url", redirectUrl);
"redirect_url", redirectUrl
);
auditService.processAuthEvent(
request, personModel, AuditConstants.SUCCESS_STATUS, AuditConstants.LOGOUT_EVENT_TYPE
);
return buildUrl(url, params);
}
catch (Exception e) {
if (personModel != null){
auditService.processAuthEvent(
request, personModel, AuditConstants.FAILURE_STATUS, AuditConstants.LOGOUT_EVENT_TYPE
);
}
throw new EsiaException(e);
}
}
@ -381,7 +401,7 @@ public class EsiaAuthService {
Person person = copyToPerson(personModel);
String kafkaResponse = replyingKafkaService.sendMessageAndGetReply(requestTopic,
requestReplyTopic, objectMapper.writeValueAsString(person)
);
).value();
return objectMapper.readValue(kafkaResponse, Response.class);
}

View file

@ -40,7 +40,7 @@ public class SubpoenaService {
}
SubpoenaRequestDto subpoenaRequestDto = new SubpoenaRequestDto(ervuId);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(recruitRequestTopic,
recruitReplyTopic, subpoenaRequestDto).get();
recruitReplyTopic, subpoenaRequestDto).value().get();
try {
SummonsResponseData responseData = SummonsResponseData.parseFrom(reply);

View file

@ -1,19 +1,39 @@
package ru.micord.ervu.util;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* @author gulnaz
*/
public final class DateUtil {
public final class DateUtils {
public static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private DateUtil() {}
private static final DateTimeFormatter DATE_TIME_WITH_TIMEZONE_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
private DateUtils() {
}
public static String getClientTimeFromRequest(HttpServletRequest request) {
String clientTimeZone = request.getHeader("Client-Time-Zone");
ZoneId zoneId;
try {
zoneId = ZoneId.of(clientTimeZone);
}
catch (Exception e) {
zoneId = ZoneId.systemDefault();
}
return ZonedDateTime.now(zoneId).format(DATE_TIME_WITH_TIMEZONE_FORMATTER);
}
public static LocalDate convertToLocalDate(String date) {
return StringUtils.hasText(date)

View file

@ -0,0 +1,53 @@
package ru.micord.ervu.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/**
* @author Adel Kalimullin
*/
public final class NetworkUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(NetworkUtils.class);
private static final String IP_HEADER = "X-Forwarded-For";
private static final String UNKNOWN = "unknown";
private NetworkUtils() {
}
public static String getServerIp() {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
return inetAddress.getHostAddress();
}
catch (UnknownHostException e) {
LOGGER.error("Failed to get local IP address", e);
return UNKNOWN;
}
}
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader(IP_HEADER);
if (StringUtils.hasText(ip) && !ip.equalsIgnoreCase(UNKNOWN)) {
return ip.split(",")[0].trim();
}
else {
return request.getRemoteAddr();
}
}
public static String getHostName(String ip) {
try {
InetAddress inetAddress = InetAddress.getByName(ip);
return inetAddress.getHostName();
}
catch (UnknownHostException e) {
LOGGER.error("Unknown host for IP {}", ip, e);
return UNKNOWN;
}
}
}

View file

@ -0,0 +1 @@
error.unknown=Система временно недоступна. Пожалуйста, повторите попытку позже.

View file

@ -0,0 +1 @@
error.unknown=The system is temporarily unavailable. Please try again later.

View file

@ -0,0 +1,2 @@
kafka_reply_timeout=Превышено время ожидания ответа от сервера. Попробуйте повторить запрос позже или обратитесь к системному администратору
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА

View file

@ -0,0 +1,2 @@
kafka_reply_timeout=Превышено время ожидания ответа от сервера. Попробуйте повторить запрос позже или обратитесь к системному администратору
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА

View file

@ -788,6 +788,16 @@ JBPM использует 3 корневых категории логирова
- `ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC` - топик для отправки запроса на получение выписки из Реестра воинского учета при наличии ErvuId
- `ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC` - топик для получения выписки из Реестра воинского учета
- `ERVU_KAFKA_EXTRACT_HEADER_CLASS` - класс для идентификации в заголовке запроса на получение выписки из Реестра повесток/Реестра воинского учета
- `AUDIT_KAFKA_AUTHORIZATION_TOPIC` - топик для отправки аудита в журнал авторизации
- `AUDIT_KAFKA_ACTION_TOPIC` - топик для отправки аудита в журнал действий пользователя
- `AUDIT_KAFKA_FILE_DOWNLOAD_TOPIC` - топик для отправки аудита в журнал загрузки ЮЛ и ФЛ
- `AUDIT_KAFKA_BOOTSTRAP_SERVERS` - список пар хост:порт, использующихся для установки первоначального соединения с кластером Kafka
- `AUDIT_KAFKA_SECURITY_PROTOCOL` - протокол, используемый для взаимодействия с брокерами
- `AUDIT_KAFKA_DOC_LOGIN_MODULE` - имя класса для входа в систему для SASL-соединений в формате, используемом конфигурационными файлами JAAS
- `AUDIT_KAFKA_USERNAME` - пользователь для подключения к Kafka
- `AUDIT_KAFKA_PASSWORD` - пароль для подключения к Kafka
- `AUDIT_KAFKA_SASL_MECHANISM` - механизм SASL, используемый для клиентских подключений
- `AUDIT_KAFKA_ENABLED` - флажок для включения записи аудита в кафку
# Прочее

View file

@ -33,6 +33,16 @@ 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.scram.ScramLoginModule
AUDIT_KAFKA_AUTHORIZATION_TOPIC=ervu.lkrp.auth.events
AUDIT_KAFKA_ACTION_TOPIC=ervu.lkrp.action.events
AUDIT_KAFKA_FILE_DOWNLOAD_TOPIC=ervu.lkrp.import.file
AUDIT_KAFKA_BOOTSTRAP_SERVERS=
AUDIT_KAFKA_SECURITY_PROTOCOL=
AUDIT_KAFKA_DOC_LOGIN_MODULE=
AUDIT_KAFKA_USERNAME=
AUDIT_KAFKA_PASSWORD=
AUDIT_KAFKA_SASL_MECHANISM=
AUDIT_KAFKA_ENABLED=false
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
COOKIE_PATH=/fl

View file

@ -80,6 +80,16 @@
<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="esia.token.clear.cron" value="0 0 */1 * * *"/>
<property name="audit.kafka.bootstrap.servers" value="localhost:9092"/>
<property name="audit.kafka.authorization.topic" value="ervu.lkrp.auth.events"/>
<property name="audit.kafka.file.download.topic" value="ervu.lkrp.import.file"/>
<property name="audit.kafka.action.topic" value="ervu.lkrp.action.events"/>
<property name="audit.kafka.security.protocol" value="SASL_PLAINTEXT"/>
<property name="audit.kafka.doc.login.module" value="org.apache.kafka.common.security.scram.ScramLoginModule"/>
<property name="audit.kafka.sasl.mechanism" value="SCRAM-SHA-256"/>
<property name="audit.kafka.username" value="user1"/>
<property name="audit.kafka.password" value="Blfi9d2OFG"/>
<property name="audit.kafka.enabled" value="false"/>
</system-properties>
<management>
<audit-log>

View file

@ -4,7 +4,7 @@
<parent>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId>
<version>1.9.5-SNAPSHOT</version>
<version>1.9.8-SNAPSHOT</version>
</parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId>

View file

@ -4,7 +4,7 @@
<parent>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId>
<version>1.9.5-SNAPSHOT</version>
<version>1.9.8-SNAPSHOT</version>
</parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId>

View file

@ -0,0 +1,42 @@
import {AnalyticalScope, Behavior, Control, NotNull} from "@webbpm/base-package";
import {ElementRef} from "@angular/core";
import {AuditService} from "./service/AuditService";
import {LinkEventTypeEnum} from "./component/enum/LinkEventTypeEnum";
@AnalyticalScope(Control)
export class LinkClickHandler extends Behavior {
@NotNull()
public eventType: LinkEventTypeEnum;
private control: Control;
private auditService: AuditService;
private el: ElementRef;
initialize() {
this.control = this.getScript(Control);
this.el = this.control.getEl();
super.initialize();
}
bindEvents() {
super.bindEvents();
if (this.el) {
this.el.nativeElement.addEventListener('click',
(event: MouseEvent) => this.onClickFunction(event));
}
}
unbindEvents() {
super.unbindEvents()
if (this.el) {
this.el.nativeElement.removeEventListener('click',
(event: MouseEvent) => this.onClickFunction(event));
}
}
private onClickFunction(event: MouseEvent) {
const target = event.target as HTMLElement;
if (target.tagName === 'A') {
this.auditService.logActionAudit(this.eventType);
}
}
}

View file

@ -29,8 +29,12 @@ export class ExtractLoadService extends Behavior {
this.errorEvent.subscribe(() => console.log("error event occurred", this.errorEvent));
this.onClickFunction = () => {
console.log("click event occurred");
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
this.httpClient.get('extract/' + this.formatRegistry, {
responseType: 'blob',
headers: {
"Client-Time-Zone": timeZone,
},
observe: 'response'
}).toPromise()
.then((response) => {

View file

@ -0,0 +1,5 @@
export enum LinkEventTypeEnum {
NAVIGATION_TO_SOURCE = "Переход на другие источники",
DOWNLOAD_TEMPLATE = "Скачивание шаблона",
DOWNLOAD_EXAMPLE = "Скачивание примера заполнения формы"
}

View file

@ -36,6 +36,9 @@ export class InMemoryStaticGrid extends GridV2 {
super.initGrid();
this.subscription = this.injector.get(ErvuDataService).message.subscribe(value => {
this.rowData = value ? value[this.dataList] : null;
this.initDeferred.promise.then(() => {
this.refreshData();
});
});
}

View file

@ -0,0 +1,43 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Router} from "@angular/router";
@Injectable({
providedIn: 'root'
})
export class AuditService {
constructor(private httpClient: HttpClient, private router: Router) {
}
public logActionAudit(eventType: string, fileName?: string): void {
const currentRoute = this.router.url;
const sourceUrl = window.location.href;
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const data: AuditAction = {
eventType: eventType,
sourceUrl: sourceUrl,
route: currentRoute,
fileName: fileName
};
this.httpClient.post("audit/action", data, {
headers: {
"Client-Time-Zone": timeZone,
}
}).toPromise();
}
}
export interface AuditAction {
eventType: string;
sourceUrl: string;
route: string;
fileName?: string;
}
export class AuditConstants {
public static readonly OPEN_PAGE_EVENT = "Открытие страницы";
}

View file

@ -24,6 +24,7 @@ import {LogOutComponent} from "./component/logout.component";
import {LoadForm} from "../../ervu/component/container/LoadForm";
import {InMemoryStaticGrid} from "../../ervu/component/grid/InMemoryStaticGrid";
import {AuthenticationService} from "../security/authentication.service";
import {AuditService} from "../../ervu/service/AuditService";
import {HomeLandingComponent} from "./component/home-landing.component";
registerLocaleData(localeRu);
@ -64,7 +65,7 @@ export function checkAuthentication(authService: AuthenticationService): () => P
DIRECTIVES
],
providers: [
AuthenticationService,
AuthenticationService, AuditService,
{
provide: APP_INITIALIZER,
useFactory: checkAuthentication,

View file

@ -8,6 +8,7 @@ import {
Router
} from "@angular/router";
import {ProgressIndicationService} from "@webbpm/base-package";
import {AuditConstants, AuditService} from "../../../ervu/service/AuditService";
@Component({
moduleId: module.id,
@ -21,7 +22,8 @@ export class WebbpmComponent {
constructor(private router: Router,
private progressIndicationService: ProgressIndicationService,
private cd: ChangeDetectorRef) {
private cd: ChangeDetectorRef,
private auditService: AuditService) {
router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
progressIndicationService.showProgressBar();
@ -29,9 +31,15 @@ export class WebbpmComponent {
this.cd.markForCheck();
}
else if (event instanceof NavigationEnd
|| event instanceof NavigationError
|| event instanceof NavigationCancel) {
|| event instanceof NavigationError
|| event instanceof NavigationCancel) {
progressIndicationService.hideProgressBar();
if (event instanceof NavigationEnd
&& event.url != '/home'
&& event.url != '/access-denied') {
this.auditService.logActionAudit(AuditConstants.OPEN_PAGE_EVENT);
}
}
})
}

View file

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId>
<version>1.9.5-SNAPSHOT</version>
<version>1.9.8-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>backend</module>

View file

@ -4,7 +4,7 @@
<parent>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>fl</artifactId>
<version>1.9.5-SNAPSHOT</version>
<version>1.9.8-SNAPSHOT</version>
</parent>
<groupId>ru.micord.ervu.lkrp.fl</groupId>

View file

@ -1034,6 +1034,7 @@
<componentRootId>6f1b4ecf-7311-41ea-87f7-56f77ea66251</componentRootId>
<name>Гиперссылка - на Госуслугах</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="2fe08181-8a1e-4cda-be3a-0b0ddcb21603">
<properties>
@ -1072,6 +1073,22 @@
</value>
</entry>
</properties>
</scripts>
<scripts id="53f844ea-7cd7-4bd3-b9fa-0ba97d517b5f">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
</children>

View file

@ -223,7 +223,6 @@
<componentRootId>a674ce01-eadd-4297-b782-53e45e059310</componentRootId>
<name>HB - (сценарий) в связи с неявкой в военкомат без уважительной причины в течение 20 календарных</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -325,7 +324,6 @@
<componentRootId>854640d1-9de8-4d5a-8f00-5d33cdf097f3</componentRootId>
<name>HB - (сценарий) в качестве ограничения, направленного на обеспечение явки в военкомат</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -450,7 +448,6 @@
<componentRootId>0276d338-dbbc-406a-a356-96f7fd02a5a6</componentRootId>
<name>Таблица - временные меры</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="fe8cfe1c-5381-411e-aee6-cf4b38fcea07">
<properties>
@ -623,7 +620,6 @@
<componentRootId>040fa808-ccbf-4844-b179-d63f54dc220a</componentRootId>
<name>HB - запрет на выезд из России</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -869,7 +865,6 @@
<componentRootId>573a720b-91f7-4e25-a441-7fd3133fdf31</componentRootId>
<name>HB - запрет на постановку в налоговом органе физического лица в качестве налогоплательщика, применяющего спец</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -1683,7 +1678,6 @@
<componentRootId>52dc50c2-9c0c-44a8-94fb-ff190bdce295</componentRootId>
<name>VB - правый</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -1818,7 +1812,6 @@
<componentRootId>ffa21c64-1030-45c8-9901-db59f298fc11</componentRootId>
<name>Диалоговые окна (информационные)</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>
@ -2045,6 +2038,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="a1579d08-139a-429e-8601-2bf95aa91147">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="56f58b69-0324-4823-bb69-98682542e514">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -2456,6 +2465,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="f2041b5d-0cd4-4b7f-a790-98edc48e9d86">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="8ae2b128-9044-428e-b88e-7095f1207c97">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -2891,6 +2916,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="9d29fb9f-020d-46eb-a8c4-efd831aae46a">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="67caa536-dc5e-49c6-a8b9-c3c4cf9d0459">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -3002,7 +3043,6 @@
<componentRootId>6a606277-7950-41b1-a59f-7d1deb5cccd2</componentRootId>
<name>Уважительные причины неявки в военкомат</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -3174,6 +3214,7 @@
<componentRootId>613a502f-2276-454e-bb91-4596be5e94cf</componentRootId>
<name>Гиперссылка - Закон о воинской обязанности и военной службе, ст. 7.</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="2fe08181-8a1e-4cda-be3a-0b0ddcb21603">
<properties>
@ -3215,6 +3256,7 @@
<componentRootId>81c3d5d2-0f2e-401d-b107-4ad7467f7b30</componentRootId>
<name>Текст(гссылка) - Закон о воинской обязанности и военной службе, ст. 7.</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -3232,6 +3274,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="9cc0c0c5-77a3-410f-874b-4d3879a36124">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="6dd87957-9a07-4c23-b203-90d8fb8bc181">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -3344,7 +3402,6 @@
<componentRootId>07d89523-77ea-40f3-96d0-952c74b082b0</componentRootId>
<name>AC - для вызова диалогов</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>

View file

@ -513,7 +513,6 @@
<componentRootId>4e247261-22c9-4b75-bc42-1214d6478193</componentRootId>
<name>HB заголовок - Повестки</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -573,7 +572,6 @@
<componentRootId>3962166c-e12b-4866-b6fc-e53c9f233272</componentRootId>
<name>VB - 1.1.1.1 (на имя выписана повестка) сценарий</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -640,7 +638,6 @@
<componentRootId>54ec4ded-0f1d-44b1-beea-1d10f2c65d6b</componentRootId>
<name>HB - явитесь по повестке в военкомат</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="b6068710-0f31-48ec-8e03-c0c1480a40c0"/>
@ -783,7 +780,6 @@
<componentRootId>54755bcb-801b-450d-aa2e-046e2a405538</componentRootId>
<name>VB - 1.1.1.1 (на ? дату нет сформированных повесток) сценарий</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -919,7 +915,6 @@
<componentRootId>ae6bbd9c-b3f6-458a-9c19-07e06a4716da</componentRootId>
<name>VB - 1.1.1.1 (в реестре нет информации о сформированных повестках) сценарий</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -1773,7 +1768,6 @@
<componentRootId>8ed0924a-73dc-4732-8426-0a7217654309</componentRootId>
<name>HB - данные загружаются</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -2443,7 +2437,6 @@
<componentRootId>935a9f9f-4d12-4813-b313-919627c0e642</componentRootId>
<name>VB - 1.1.2.2 (применены временные меры) сценарий</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -3000,7 +2993,6 @@
<componentRootId>4b46dfb0-e372-48e7-96cb-6d488cbfbc55</componentRootId>
<name>VB - 1.1.2.2 (на "дата" нет действующих ограничений) сценарий</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -3153,7 +3145,6 @@
<componentRootId>751006da-a8d6-45c6-8eee-0895c30f8035</componentRootId>
<name>VB - 1.1.2.2 (В реестре нет информации о примененных временных мерах) сценарий</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -3216,7 +3207,6 @@
<componentRootId>a4bb1eb3-c05a-4c96-acc4-885a99e4e28d</componentRootId>
<name>HB - данные загружаются</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -7326,7 +7316,6 @@
<componentRootId>cc277790-aa36-4798-9a37-97d2a381e5e8</componentRootId>
<name>Реестр воинского учета</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -7524,6 +7513,7 @@
<componentRootId>61e47a39-6f30-4d52-92db-fce8f0df062c</componentRootId>
<name>Текст(гссылка) - Закон о воинской обязанности и военной службе, ст. 8.2.</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -7541,6 +7531,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="ed835461-d64e-44b3-aa26-63abb48473ad">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="757294ad-d1dd-4a9f-987d-40a666b2ad9d">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -7652,7 +7658,6 @@
<componentRootId>018ebef5-5b82-464e-ae64-b8a38c11a244</componentRootId>
<name>Реестр повесток</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -7970,6 +7975,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="f5574121-d46d-4ce1-a314-515f81571e49">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="7281e53c-3e98-453f-935a-cd5f93420620">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -8088,7 +8109,6 @@
<componentRootId>e40b27e3-8df0-4e8c-adeb-6e24de6f18d2</componentRootId>
<name>Временные меры</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -8441,6 +8461,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="72fc7c7d-ab13-40f5-a79d-75619e3af934">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="52473ef0-1993-4be5-a86f-7cbc6dc8e75a">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -8566,7 +8602,6 @@
<componentRootId>dce4d66d-4b4f-4883-a54c-e44e96a06a53</componentRootId>
<name>Уважительные причины неявки в военкомат</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -8796,6 +8831,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="9b7eead5-b15c-4e63-a6c2-ef164b092268">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="3e33975b-5bde-4d58-a4d5-a7acb9633beb">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -8907,7 +8958,6 @@
<componentRootId>5777f90d-5b51-4c1b-b0cc-4ace05faa8f3</componentRootId>
<name>Диалог - Отрицательное количество дней до явки в военкомат</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -9221,7 +9271,6 @@
<componentRootId>2452bb18-95f0-4bfc-a40d-52b59e3c5cd6</componentRootId>
<name>AC - на вызов диалоговых окон</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -10210,7 +10259,6 @@
<componentRootId>5443b358-365f-4bfd-bb4c-f2854dd96da4</componentRootId>
<name>Диалоговые окна отправки на электронную почту</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>
@ -10373,6 +10421,22 @@
<value>
<simple>"https://www.gosuslugi.ru"</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="a5b10562-a190-4409-bbc9-afc6e9dcf26d">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
@ -10507,7 +10571,6 @@
<componentRootId>f25ca4de-d55b-4a06-bb4c-5eedd65d4095</componentRootId>
<name>Диалог - Отправка выписки на электронную почту - к удалению</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -10684,6 +10747,22 @@
<value>
<simple>"https://www.gosuslugi.ru"</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="d727881a-0a95-45a3-ad0d-e9d6d58e816a">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
@ -10903,7 +10982,6 @@
<componentRootId>673d4fd7-0527-41df-abcd-2ab522bb161c</componentRootId>
<name>Диалог - Выписка отправлена - к удалению</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -11083,7 +11161,6 @@
<componentRootId>d9788c7d-ebeb-413a-969a-a33f167a2bd9</componentRootId>
<name>Диалог - не удалось отправить выписку</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -11276,7 +11353,6 @@
<componentRootId>8aff628b-a0a4-4296-854d-8a03dbef5937</componentRootId>
<name>AC - на вызов диалоговых окон</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>

View file

@ -195,7 +195,6 @@
<componentRootId>2d0083b3-0994-4131-9cfc-de84cab46dab</componentRootId>
<name>HB - явитесь по повестке в военкомат. Ели не придёте в срок, к Вам применят временные меры</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -338,6 +337,7 @@
<componentRootId>fc2ddccc-d84c-4b0b-a9fc-5aa1391d590b</componentRootId>
<name>FS - информация по повестке</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
<properties>
@ -1101,6 +1101,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="bf78cf16-a0a1-41ee-9945-8670cd247a15">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
</children>
<children id="7aabbeed-3e64-4cdd-8fe3-841b134da135">
@ -1133,6 +1149,12 @@
</item>
</value>
</entry>
<entry>
<key>visible</key>
<value>
<simple>false</simple>
</value>
</entry>
</properties>
</scripts>
</children>
@ -2099,6 +2121,7 @@
<componentRootId>713e58f9-9106-4e75-bf08-23761e541e51</componentRootId>
<name>VB - правый</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
@ -2412,7 +2435,6 @@
<componentRootId>c9c74406-f8f4-4a9a-8a62-57fe026f5fa5</componentRootId>
<name>FS - (если вы не можете явиться по уважительной причине)</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
<properties>
@ -2505,7 +2527,6 @@
<componentRootId>2f73cbfa-c121-49b0-994a-1bc17d654e1a</componentRootId>
<name>FS - (дней до применения всех временных мер)</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
<properties>
@ -2611,7 +2632,6 @@
<componentRootId>d63ff6bf-f971-4a5e-8b36-e4c5a2e48771</componentRootId>
<name>Диалоговые окна (информационные)</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>
@ -2838,6 +2858,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="11e50082-7200-4ad6-bd54-ee10c3c0b5a2">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="b6aa0a33-f198-4846-9105-64aa80094be6">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -3249,6 +3285,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="3faadefb-4757-4f48-8b41-ffde6981f883">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="e78fddf7-4f36-45cf-89a7-29c68e3616e8">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -3360,7 +3412,6 @@
<componentRootId>43f57db1-0f42-4d3c-8166-45e7691b124a</componentRootId>
<name>Временные меры</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -3684,6 +3735,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="ce2084ca-ce7d-4789-9ba2-e78cc8df9bef">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="c90d9a4f-0c35-47a1-a07c-345240c3b7d3">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -3802,7 +3869,6 @@
<componentRootId>76ac701a-62f6-493e-8ab3-9391e956c3f1</componentRootId>
<name>Уважительные причины неявки в военкомат</name>
<container>true</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
@ -4032,6 +4098,22 @@
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="29b36664-c038-438c-b4a4-41eb37d0225f">
<classRef type="TS">
<className>LinkClickHandler</className>
<packageName>ervu</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>eventType</key>
<value>
<simple>"NAVIGATION_TO_SOURCE"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="72183341-d63b-4e4b-85a5-0187806b8158">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
@ -4448,5 +4530,87 @@
</properties>
</scripts>
</children>
<children id="66d254b2-bf2b-40eb-b175-ccc50b9dceed">
<prototypeId>98594cec-0a9b-4cef-af09-e1b71cb2ad9e</prototypeId>
<componentRootId>66d254b2-bf2b-40eb-b175-ccc50b9dceed</componentRootId>
<name>AC - временная мера</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="37dff5c8-1599-4984-b107-c44a87b6da2e">
<properties>
<entry>
<key>elseActions</key>
<value>
<item id="8d3cc8e0-b29f-465f-bfd5-5b04403944b8" removed="true"/>
</value>
</entry>
<entry>
<key>eventRefs</key>
<value>
<item id="8bf6b67c-543f-4637-abc9-abc82dab3401" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"05e0b1a0-7d08-4480-8532-b19692e8812a","packageName":"component.button","className":"Button","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>propertyName</key>
<value>
<simple>"clickEvent"</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
<entry>
<key>ifCondition</key>
<value>
<complex>
<entry>
<key>logicalOperation</key>
<value>
<simple>null</simple>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>thenActions</key>
<value>
<item id="d67b987f-e771-4551-808b-28c56b13febc" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"43f57db1-0f42-4d3c-8166-45e7691b124a","packageName":"component","className":"Dialog","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"show"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<simple>null</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
</properties>
</scripts>
</children>
</rootObjects>
</xmlPage>