Merge branch 'hotfix/1.9.8'
This commit is contained in:
commit
dc48f3dce7
67 changed files with 2384 additions and 159 deletions
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.9.8-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
<artifactId>backend</artifactId>
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import org.springframework.retry.annotation.Retryable;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.micord.ervu.security.esia.config.EsiaConfig;
|
||||
import ru.micord.ervu.util.UrlUtils;
|
||||
|
||||
/**
|
||||
* @author Alexandr Shalaginov
|
||||
|
|
@ -186,14 +187,17 @@ public class WebDavClient {
|
|||
HttpResponse<InputStream> response = httpClient.send(httpRequest,
|
||||
HttpResponse.BodyHandlers.ofInputStream()
|
||||
);
|
||||
|
||||
if (response.statusCode() == 200) {
|
||||
long contentLength = response.headers()
|
||||
.firstValueAsLong(HttpHeaders.CONTENT_LENGTH)
|
||||
.orElse(0);
|
||||
InputStreamResource resource = new InputStreamResource(response.body());
|
||||
String encodedFilename = URLEncoder.encode(getFilenameFromUrl(url), StandardCharsets.UTF_8);
|
||||
String encodedFilename = URLEncoder.encode(UrlUtils.extractFileNameFromUrl(url), StandardCharsets.UTF_8);
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename*=UTF-8''" + encodedFilename
|
||||
)
|
||||
.contentLength(contentLength)
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.body(resource);
|
||||
}
|
||||
|
|
@ -207,10 +211,6 @@ public class WebDavClient {
|
|||
}
|
||||
}
|
||||
|
||||
private String getFilenameFromUrl(String url) {
|
||||
String path = URI.create(url).getPath();
|
||||
return path.substring(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
@Retryable(value = {IOException.class}, backoff = @Backoff(delayExpression = "${webdav.retry.delay:500}"))
|
||||
public void deleteFilesOlderThan(long seconds, String url, String... extensions) throws IOException {
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ package ervu.model.fileupload;
|
|||
/**
|
||||
* @author r.latypov
|
||||
*/
|
||||
public record DownloadResponse(OrgInfo orgInfo, FileInfo fileInfo) {
|
||||
public record DownloadResponse(UploadOrgInfo orgInfo, FileInfo fileInfo) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ import java.util.Objects;
|
|||
* @author Alexandr Shalaginov
|
||||
*/
|
||||
public class EmployeeInfoKafkaMessage {
|
||||
private final OrgInfo orgInfo;
|
||||
private final UploadOrgInfo orgInfo;
|
||||
private final FileInfo fileInfo;
|
||||
|
||||
public EmployeeInfoKafkaMessage(OrgInfo orgInfo, FileInfo fileInfo) {
|
||||
public EmployeeInfoKafkaMessage(UploadOrgInfo orgInfo, FileInfo fileInfo) {
|
||||
this.orgInfo = orgInfo;
|
||||
this.fileInfo = fileInfo;
|
||||
}
|
||||
|
||||
public OrgInfo getOrgInfo() {
|
||||
public UploadOrgInfo getOrgInfo() {
|
||||
return orgInfo;
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ public class EmployeeInfoKafkaMessage {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "KafkaMessage{" +
|
||||
"orgInfo=" + orgInfo +
|
||||
"uploadOrgInfo=" + orgInfo +
|
||||
", fileInfo=" + fileInfo +
|
||||
'}';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public class FileInfo {
|
|||
private String fileName;
|
||||
private String filePatternCode;
|
||||
private String filePatternName;
|
||||
private String fileSize;
|
||||
private String departureDateTime;
|
||||
private String timeZone;
|
||||
private FileStatus fileStatus;
|
||||
|
|
@ -19,12 +20,14 @@ public class FileInfo {
|
|||
}
|
||||
|
||||
public FileInfo(String fileId, String fileUrl, String fileName, String filePatternCode,
|
||||
String filePatternName, String departureDateTime, String timeZone, FileStatus fileStatus) {
|
||||
String filePatternName, String fileSize, String departureDateTime, String timeZone,
|
||||
FileStatus fileStatus) {
|
||||
this.fileId = fileId;
|
||||
this.fileUrl = fileUrl;
|
||||
this.fileName = fileName;
|
||||
this.filePatternCode = filePatternCode;
|
||||
this.filePatternName = filePatternName;
|
||||
this.fileSize = fileSize;
|
||||
this.departureDateTime = departureDateTime;
|
||||
this.timeZone = timeZone;
|
||||
this.fileStatus = fileStatus;
|
||||
|
|
@ -50,6 +53,10 @@ public class FileInfo {
|
|||
return filePatternName;
|
||||
}
|
||||
|
||||
public String getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public String getDepartureDateTime() {
|
||||
return departureDateTime;
|
||||
}
|
||||
|
|
@ -73,13 +80,14 @@ public class FileInfo {
|
|||
fileName, fileInfo.fileName) && Objects.equals(filePatternCode,
|
||||
fileInfo.filePatternCode
|
||||
) && Objects.equals(filePatternName, fileInfo.filePatternName)
|
||||
&& Objects.equals(departureDateTime, fileInfo.departureDateTime);
|
||||
&& Objects.equals(departureDateTime, fileInfo.departureDateTime) &&
|
||||
Objects.equals(fileSize, fileInfo.getFileSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fileId, fileUrl, fileName, filePatternCode, filePatternName,
|
||||
departureDateTime
|
||||
departureDateTime, fileSize
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +100,7 @@ public class FileInfo {
|
|||
", filePatternCode='" + filePatternCode + '\'' +
|
||||
", filePatternName='" + filePatternName + '\'' +
|
||||
", departureDateTime='" + departureDateTime + '\'' +
|
||||
", fileSize='" + fileSize + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package ervu.model.fileupload;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
public class FileStatus {
|
||||
|
||||
private String code;
|
||||
private String status;
|
||||
private String description;
|
||||
|
|
|
|||
|
|
@ -7,18 +7,24 @@ import ru.micord.ervu.journal.SenderInfo;
|
|||
/**
|
||||
* @author Alexandr Shalaginov
|
||||
*/
|
||||
public class OrgInfo {
|
||||
public class UploadOrgInfo {
|
||||
private String orgName;
|
||||
private String orgId;
|
||||
private SenderInfo senderInfo;
|
||||
private String esiaOrgId;
|
||||
|
||||
public OrgInfo() {
|
||||
public UploadOrgInfo() {
|
||||
}
|
||||
|
||||
public OrgInfo(String orgName, String orgId, SenderInfo senderInfo) {
|
||||
public UploadOrgInfo(String orgName, String orgId, SenderInfo senderInfo, String esiaOrgId) {
|
||||
this.orgName = orgName;
|
||||
this.orgId = orgId;
|
||||
this.senderInfo = senderInfo;
|
||||
this.esiaOrgId = esiaOrgId;
|
||||
}
|
||||
|
||||
public String getEsiaOrgId() {
|
||||
return esiaOrgId;
|
||||
}
|
||||
|
||||
public String getOrgName() {
|
||||
|
|
@ -37,23 +43,25 @@ public class OrgInfo {
|
|||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
OrgInfo orgInfo = (OrgInfo) o;
|
||||
return Objects.equals(orgName, orgInfo.orgName) && Objects.equals(orgId,
|
||||
orgInfo.orgId
|
||||
) && Objects.equals(senderInfo, orgInfo.senderInfo);
|
||||
UploadOrgInfo uploadOrgInfo = (UploadOrgInfo) o;
|
||||
return Objects.equals(orgName, uploadOrgInfo.orgName) && Objects.equals(orgId,
|
||||
uploadOrgInfo.orgId
|
||||
) && Objects.equals(senderInfo, uploadOrgInfo.senderInfo)
|
||||
&& Objects.equals(esiaOrgId, uploadOrgInfo.esiaOrgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(orgName, orgId, senderInfo);
|
||||
return Objects.hash(orgName, orgId, senderInfo, esiaOrgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OrgInfo{" +
|
||||
return "UploadOrgInfo{" +
|
||||
"orgName='" + orgName + '\'' +
|
||||
", orgId='" + orgId + '\'' +
|
||||
", senderInfo='" + senderInfo + '\'' +
|
||||
", esiaOrgId='" + esiaOrgId + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import java.io.IOException;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
|
@ -31,6 +30,7 @@ import org.springframework.kafka.annotation.KafkaListener;
|
|||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.micord.ervu.audit.service.AuditService;
|
||||
import ru.micord.ervu.exception.JsonParsingException;
|
||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||
import ru.micord.ervu.security.esia.model.PersonModel;
|
||||
|
|
@ -38,6 +38,7 @@ import ru.micord.ervu.security.esia.service.UlDataService;
|
|||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||
import ru.micord.ervu.service.InteractionService;
|
||||
import ru.micord.ervu.util.DateUtils;
|
||||
|
||||
import static ervu.enums.FileStatusCode.FILE_CLEAN;
|
||||
import static ervu.enums.FileStatusCode.FILE_INFECTED;
|
||||
|
|
@ -51,13 +52,12 @@ import static ru.micord.ervu.util.StringUtils.convertToFio;
|
|||
@Service
|
||||
public class EmployeeInfoFileUploadService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class);
|
||||
private static final String FORMAT = "dd.MM.yyyy HH:mm:ss";
|
||||
|
||||
private final WebDavClient webDavClient;
|
||||
private final EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService;
|
||||
private final KafkaTemplate<String, String> kafkaTemplate;
|
||||
private final InteractionService interactionService;
|
||||
private final UlDataService ulDataService;
|
||||
private final AuditService auditService;
|
||||
|
||||
@Value("${av.kafka.message.topic.name}")
|
||||
private String kafkaTopicName;
|
||||
|
|
@ -67,12 +67,14 @@ public class EmployeeInfoFileUploadService {
|
|||
EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService,
|
||||
@Qualifier("avTemplate") KafkaTemplate<String, String> kafkaTemplate,
|
||||
InteractionService interactionService,
|
||||
UlDataService ulDataService) {
|
||||
UlDataService ulDataService,
|
||||
AuditService auditService) {
|
||||
this.webDavClient = webDavClient;
|
||||
this.employeeInfoKafkaMessageService = employeeInfoKafkaMessageService;
|
||||
this.kafkaTemplate = kafkaTemplate;
|
||||
this.interactionService = interactionService;
|
||||
this.ulDataService = ulDataService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType,
|
||||
|
|
@ -99,29 +101,34 @@ public class EmployeeInfoFileUploadService {
|
|||
convertToFio(personModel.getFirstName(), personModel.getMiddleName(), personModel.getLastName()),
|
||||
ervuId);
|
||||
|
||||
long fileSize = multipartFile.getSize();
|
||||
String departureDateTime = DateUtils.convertToString(now);
|
||||
EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage(
|
||||
fileId,
|
||||
fileUploadUrl,
|
||||
fileName,
|
||||
employeeInfoFileFormType,
|
||||
departureDateTime,
|
||||
accessToken,
|
||||
offset,
|
||||
fileStatus,
|
||||
ervuId,
|
||||
esiaUserId,
|
||||
personModel,
|
||||
fileSize
|
||||
);
|
||||
|
||||
if (fileUploadUrl != null) {
|
||||
fileStatus.setCode(FILE_UPLOADED.getCode());
|
||||
fileStatus.setDescription("Файл принят до проверки на вирусы");
|
||||
String departureDateTime = now.format(DateTimeFormatter.ofPattern(FORMAT));
|
||||
String jsonMessage = getJsonKafkaMessage(
|
||||
employeeInfoKafkaMessageService.getKafkaMessage(
|
||||
fileId,
|
||||
fileUploadUrl,
|
||||
fileName,
|
||||
employeeInfoFileFormType,
|
||||
departureDateTime,
|
||||
accessToken,
|
||||
offset,
|
||||
fileStatus,
|
||||
ervuId,
|
||||
esiaUserId,
|
||||
personModel
|
||||
)
|
||||
);
|
||||
String jsonMessage = getJsonKafkaMessage(kafkaMessage);
|
||||
return sendMessage(jsonMessage);
|
||||
}
|
||||
else {
|
||||
LOGGER.error("Failed to upload file: {}", fileName);
|
||||
fileStatus.setCode(FILE_NOT_CHECKED.getCode());
|
||||
fileStatus.setDescription("Невозможно проверить файл по причине недоступности или ошибки в работе антивируса");
|
||||
auditService.processUploadEvent(kafkaMessage.getOrgInfo(), kafkaMessage.getFileInfo());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -199,6 +206,7 @@ public class EmployeeInfoFileUploadService {
|
|||
interactionService.delete(fileInfo.getFileId(), downloadResponse.orgInfo().getOrgId());
|
||||
}
|
||||
else if (statusCode.equals(FILE_NOT_CHECKED.getCode())) {
|
||||
auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.fileInfo());
|
||||
interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(),
|
||||
downloadResponse.orgInfo().getOrgId()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ervu.model.fileupload.EmployeeInfoFileFormType;
|
|||
import ervu.model.fileupload.EmployeeInfoKafkaMessage;
|
||||
import ervu.model.fileupload.FileInfo;
|
||||
import ervu.model.fileupload.FileStatus;
|
||||
import ervu.model.fileupload.OrgInfo;
|
||||
import ervu.model.fileupload.UploadOrgInfo;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.journal.SenderInfo;
|
||||
import ru.micord.ervu.security.esia.model.OrganizationModel;
|
||||
|
|
@ -25,7 +25,8 @@ public class EmployeeInfoKafkaMessageService {
|
|||
|
||||
public EmployeeInfoKafkaMessage getKafkaMessage(String fileId, String fileUrl, String fileName,
|
||||
EmployeeInfoFileFormType formType, String departureDateTime, String accessToken,
|
||||
String offset, FileStatus fileStatus, String ervuId, String prnOid, PersonModel personModel) {
|
||||
String offset, FileStatus fileStatus, String ervuId, String prnOid,
|
||||
PersonModel personModel, long fileSize) {
|
||||
return new EmployeeInfoKafkaMessage(
|
||||
getOrgInfo(accessToken, ervuId, prnOid, personModel),
|
||||
getFileInfo(
|
||||
|
|
@ -35,33 +36,36 @@ public class EmployeeInfoKafkaMessageService {
|
|||
formType,
|
||||
departureDateTime,
|
||||
offset,
|
||||
fileStatus
|
||||
fileStatus,
|
||||
fileSize
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private FileInfo getFileInfo(String fileId, String fileUrl, String fileName,
|
||||
EmployeeInfoFileFormType formType, String departureDateTime, String offset,
|
||||
FileStatus fileStatus) {
|
||||
FileStatus fileStatus, long fileSize) {
|
||||
return new FileInfo(
|
||||
fileId,
|
||||
fileUrl,
|
||||
fileName,
|
||||
formType.getFilePatternCode(),
|
||||
formType.getFilePatternName(),
|
||||
String.valueOf(fileSize),
|
||||
departureDateTime,
|
||||
offset,
|
||||
fileStatus
|
||||
);
|
||||
}
|
||||
|
||||
private OrgInfo getOrgInfo(String accessToken, String ervuId, String prnOid, PersonModel personModel) {
|
||||
private UploadOrgInfo getOrgInfo(String accessToken, String ervuId, String prnOid, PersonModel personModel) {
|
||||
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
|
||||
SenderInfo senderInfo = new SenderInfo();
|
||||
senderInfo.setFirstName(personModel.getFirstName());
|
||||
senderInfo.setLastName(personModel.getLastName());
|
||||
senderInfo.setMiddleName(personModel.getMiddleName());
|
||||
senderInfo.setPrnOid(prnOid);
|
||||
return new OrgInfo(organizationModel.getFullName(), ervuId, senderInfo);
|
||||
return new UploadOrgInfo(organizationModel.getFullName(), ervuId, senderInfo,
|
||||
organizationModel.getOid());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
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}")
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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 = "UL";
|
||||
public static final String LOGOUT_EVENT_TYPE = "logout";
|
||||
public static final String LOGIN_EVENT_TYPE = "login";
|
||||
public static final String SUCCESS_STATUS_TYPE = "success";
|
||||
public static final String FAILURE_STATUS_TYPE = "failure";
|
||||
|
||||
private static final Map<String, String> routeDescriptions = Map.of(
|
||||
"/", "Личный кабинет ЮР лица",
|
||||
"/mydata", "Информация об организации",
|
||||
"/filesentlog", "Журнал взаимодействия"
|
||||
);
|
||||
|
||||
private static final Map<Integer, String> downloadTypes = Map.of(
|
||||
1, "Выписка из журнала взаимодействия ЮЛ"
|
||||
);
|
||||
|
||||
|
||||
private AuditConstants() {
|
||||
}
|
||||
|
||||
public static String getRouteDescription(String route) {
|
||||
return Optional.ofNullable(routeDescriptions.get(route))
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid route :" + route));
|
||||
}
|
||||
|
||||
public static String getDownloadType(int formatRegistry) {
|
||||
return Optional.ofNullable(downloadTypes.get(formatRegistry))
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("Invalid formatRegistry :" + formatRegistry));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package ru.micord.ervu.audit.controller;
|
||||
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
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;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/action", method = RequestMethod.POST)
|
||||
public ResponseEntity<Void> auditAction(
|
||||
HttpServletRequest request, @RequestBody AuditActionRequest actionEvent) {
|
||||
auditService.processActionEvent(request, actionEvent);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class AuditActionEvent extends AuditEvent {
|
||||
private String eventType;
|
||||
private String description;
|
||||
private String sourceUrl;
|
||||
private List<SearchCriteria> searchAttributes;
|
||||
private String fileName;
|
||||
|
||||
public AuditActionEvent(
|
||||
String esiaOrgId, String esiaPersonId, String eventTime,
|
||||
String eventType, String description, String sourceUrl,
|
||||
List<SearchCriteria> searchAttributes, String fileName) {
|
||||
super(esiaOrgId, esiaPersonId, eventTime);
|
||||
this.eventType = eventType;
|
||||
this.description = description;
|
||||
this.sourceUrl = sourceUrl;
|
||||
this.searchAttributes = searchAttributes;
|
||||
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;
|
||||
}
|
||||
|
||||
public List<SearchCriteria> getSearchAttributes() {
|
||||
return searchAttributes;
|
||||
}
|
||||
|
||||
public void setSearchAttributes(List<SearchCriteria> searchAttributes) {
|
||||
this.searchAttributes = searchAttributes;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class AuditActionRequest {
|
||||
private String eventType;
|
||||
private String route;
|
||||
private String sourceUrl;
|
||||
private Map<String, FilterInfo> filterInfo;
|
||||
private String fileName;
|
||||
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public void setEventType(String eventType) {
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
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 getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public Map<String, FilterInfo> getFilterInfo() {
|
||||
return filterInfo;
|
||||
}
|
||||
|
||||
public void setFilterInfo(
|
||||
Map<String, FilterInfo> filterInfo) {
|
||||
this.filterInfo = filterInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
|
||||
public class AuditAuthorizationEvent extends AuditEvent {
|
||||
private String status;
|
||||
private String eventType;
|
||||
private String organizationName;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String middleName;
|
||||
private String inn;
|
||||
private String serverIp;
|
||||
private String serverHostName;
|
||||
private String clientIp;
|
||||
private String clientHostName;
|
||||
|
||||
public AuditAuthorizationEvent(
|
||||
String esiaOrgId, String esiaPersonId, String eventTime,
|
||||
String organizationName, String firstName, String lastName,
|
||||
String middleName, String inn, String status,
|
||||
String eventType, String serverIp, String serverHostName,
|
||||
String clientIp, String clientHostName) {
|
||||
super(esiaOrgId, esiaPersonId, eventTime);
|
||||
this.status = status;
|
||||
this.eventType = eventType;
|
||||
this.organizationName = organizationName;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.middleName = middleName;
|
||||
this.inn = inn;
|
||||
this.serverIp = serverIp;
|
||||
this.serverHostName = serverHostName;
|
||||
this.clientIp = clientIp;
|
||||
this.clientHostName = clientHostName;
|
||||
}
|
||||
|
||||
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 getOrganizationName() {
|
||||
return organizationName;
|
||||
}
|
||||
|
||||
public void setOrganizationName(String organizationName) {
|
||||
this.organizationName = organizationName;
|
||||
}
|
||||
|
||||
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 getInn() {
|
||||
return inn;
|
||||
}
|
||||
|
||||
public void setInn(String inn) {
|
||||
this.inn = inn;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
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 esiaOrgId, String esiaPersonId, String eventTime,
|
||||
String downloadType, String fileName, String s3FileUrl,
|
||||
String fileSize, String status) {
|
||||
super(esiaOrgId, esiaPersonId, eventTime);
|
||||
this.downloadType = downloadType;
|
||||
this.fileName = fileName;
|
||||
this.s3FileUrl = s3FileUrl;
|
||||
this.fileSize = fileSize;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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 getS3FileUrl() {
|
||||
return s3FileUrl;
|
||||
}
|
||||
|
||||
public void setS3FileUrl(String s3FileUrl) {
|
||||
this.s3FileUrl = s3FileUrl;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
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 esiaOrgId;
|
||||
protected String esiaPersonId;
|
||||
protected String eventTime;
|
||||
|
||||
public AuditEvent(
|
||||
String esiaOrgId, String esiaPersonId, String eventTime) {
|
||||
this.esiaOrgId = esiaOrgId;
|
||||
this.esiaPersonId = esiaPersonId;
|
||||
this.eventTime = eventTime;
|
||||
}
|
||||
|
||||
public String getSubsystem() {
|
||||
return subsystem;
|
||||
}
|
||||
|
||||
public String getEsiaOrgId() {
|
||||
return esiaOrgId;
|
||||
}
|
||||
|
||||
public void setEsiaOrgId(String esiaOrgId) {
|
||||
this.esiaOrgId = esiaOrgId;
|
||||
}
|
||||
|
||||
public String getEsiaPersonId() {
|
||||
return esiaPersonId;
|
||||
}
|
||||
|
||||
public void setEsiaPersonId(String esiaPersonId) {
|
||||
this.esiaPersonId = esiaPersonId;
|
||||
}
|
||||
|
||||
public String getEventTime() {
|
||||
return eventTime;
|
||||
}
|
||||
|
||||
public void setEventTime(String eventTime) {
|
||||
this.eventTime = eventTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
|
||||
import ervu.model.fileupload.FileInfo;
|
||||
import ervu.model.fileupload.UploadOrgInfo;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
|
||||
public class AuditUploadEvent {
|
||||
private UploadOrgInfo orgInfo;
|
||||
private FileInfo fileInfo;
|
||||
|
||||
public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) {
|
||||
this.orgInfo = orgInfo;
|
||||
this.fileInfo = fileInfo;
|
||||
}
|
||||
|
||||
public UploadOrgInfo getOrgInfo() {
|
||||
return orgInfo;
|
||||
}
|
||||
|
||||
public void setOrgInfo(UploadOrgInfo orgInfo) {
|
||||
this.orgInfo = orgInfo;
|
||||
}
|
||||
|
||||
public FileInfo getFileInfo() {
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
public void setFileInfo(FileInfo fileInfo) {
|
||||
this.fileInfo = fileInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class FilterCondition {
|
||||
private String filterValue;
|
||||
private String filterType;
|
||||
|
||||
public String getFilterValue() {
|
||||
return filterValue;
|
||||
}
|
||||
|
||||
public void setFilterValue(String filterValue) {
|
||||
this.filterValue = filterValue;
|
||||
}
|
||||
|
||||
public String getFilterType() {
|
||||
return filterType;
|
||||
}
|
||||
|
||||
public void setFilterType(String filterType) {
|
||||
this.filterType = filterType;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
|
||||
public class FilterInfo {
|
||||
private String conditionOperator;
|
||||
private List<FilterCondition> conditions;
|
||||
|
||||
public String getConditionOperator() {
|
||||
return conditionOperator;
|
||||
}
|
||||
|
||||
public void setConditionOperator(String conditionOperator) {
|
||||
this.conditionOperator = conditionOperator;
|
||||
}
|
||||
|
||||
public List<FilterCondition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public void setConditions(List<FilterCondition> conditions) {
|
||||
this.conditions = conditions;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package ru.micord.ervu.audit.model;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class SearchCriteria {
|
||||
private String searchAttribute;
|
||||
private String searchValue;
|
||||
|
||||
public SearchCriteria(String searchAttribute, String searchValue) {
|
||||
this.searchAttribute = searchAttribute;
|
||||
this.searchValue = searchValue;
|
||||
}
|
||||
|
||||
public String getSearchAttribute() {
|
||||
return searchAttribute;
|
||||
}
|
||||
|
||||
public void setSearchAttribute(String searchAttribute) {
|
||||
this.searchAttribute = searchAttribute;
|
||||
}
|
||||
|
||||
public String getSearchValue() {
|
||||
return searchValue;
|
||||
}
|
||||
|
||||
public void setSearchValue(String searchValue) {
|
||||
this.searchValue = searchValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.micord.ervu.audit.service;
|
||||
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public interface AuditKafkaPublisher {
|
||||
void publishEvent(String topicName, String message);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package ru.micord.ervu.audit.service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ervu.model.fileupload.FileInfo;
|
||||
import ervu.model.fileupload.UploadOrgInfo;
|
||||
import ru.micord.ervu.audit.model.AuditActionRequest;
|
||||
import ru.micord.ervu.kafka.model.OrgInfo;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public interface AuditService {
|
||||
void processActionEvent(HttpServletRequest request, AuditActionRequest auditActionRequest);
|
||||
|
||||
void processAuthEvent(HttpServletRequest request, OrgInfo orgInfo, String prnOid, String status,
|
||||
String eventType);
|
||||
|
||||
void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo);
|
||||
|
||||
void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName,
|
||||
int formatRegistry, String status, String s3FileUrl);
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
package ru.micord.ervu.audit.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.model.fileupload.FileInfo;
|
||||
import ervu.model.fileupload.UploadOrgInfo;
|
||||
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.*;
|
||||
import ru.micord.ervu.audit.service.AuditKafkaPublisher;
|
||||
import ru.micord.ervu.audit.service.AuditService;
|
||||
import ru.micord.ervu.exception.JsonParsingException;
|
||||
import ru.micord.ervu.kafka.model.OrgInfo;
|
||||
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
||||
import ru.micord.ervu.security.esia.service.UlDataService;
|
||||
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 auditKafkaPublisher;
|
||||
private final JwtTokenService jwtTokenService;
|
||||
private final UlDataService ulDataService;
|
||||
private final ObjectMapper objectMapper;
|
||||
@Value("${audit.kafka.authorization.topic}")
|
||||
private String authorizationTopic;
|
||||
@Value("${audit.kafka.action.topic}")
|
||||
private String actionTopic;
|
||||
@Value("${audit.kafka.file.upload.topic}")
|
||||
private String fileUploadTopic;
|
||||
@Value("${audit.kafka.file.download.topic}")
|
||||
private String fileDownloadTopic;
|
||||
|
||||
public BaseAuditService(AuditKafkaPublisher auditKafkaPublisher, JwtTokenService jwtTokenService,
|
||||
UlDataService ulDataService, ObjectMapper objectMapper) {
|
||||
this.auditKafkaPublisher = auditKafkaPublisher;
|
||||
this.jwtTokenService = jwtTokenService;
|
||||
this.ulDataService = ulDataService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processActionEvent(HttpServletRequest request,
|
||||
AuditActionRequest auditActionRequest) {
|
||||
String orgId = getEsiaOrgId(request);
|
||||
String userAccountId = jwtTokenService.getUserAccountId(request);
|
||||
String description = AuditConstants.getRouteDescription(auditActionRequest.getRoute());
|
||||
|
||||
List<SearchCriteria> searchAttributes = null;
|
||||
if (auditActionRequest.getFilterInfo() != null && !auditActionRequest.getFilterInfo().isEmpty()) {
|
||||
searchAttributes = getSearchCriteriaList(auditActionRequest.getFilterInfo());
|
||||
}
|
||||
|
||||
AuditActionEvent event = new AuditActionEvent(
|
||||
orgId,
|
||||
userAccountId,
|
||||
DateUtils.getClientDateTimeWithZoneFromRequest(request),
|
||||
auditActionRequest.getEventType(),
|
||||
description,
|
||||
auditActionRequest.getSourceUrl(),
|
||||
searchAttributes,
|
||||
auditActionRequest.getFileName()
|
||||
);
|
||||
|
||||
String message = convertToMessage(event);
|
||||
auditKafkaPublisher.publishEvent(actionTopic, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processAuthEvent(HttpServletRequest request, OrgInfo orgInfo, String prnOid,
|
||||
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(
|
||||
orgInfo.getOrgOid(),
|
||||
prnOid,
|
||||
DateUtils.getClientDateTimeWithZoneFromRequest(request),
|
||||
orgInfo.getOrgFullName(),
|
||||
orgInfo.getSenderInfo().getFirstName(),
|
||||
orgInfo.getSenderInfo().getLastName(),
|
||||
orgInfo.getSenderInfo().getMiddleName(),
|
||||
orgInfo.getInn(),
|
||||
status,
|
||||
eventType,
|
||||
serverIp,
|
||||
serverHostName,
|
||||
clientIp,
|
||||
clientHostName
|
||||
);
|
||||
|
||||
String message = convertToMessage(event);
|
||||
auditKafkaPublisher.publishEvent(authorizationTopic, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) {
|
||||
AuditUploadEvent auditUploadEvent = new AuditUploadEvent(
|
||||
orgInfo,
|
||||
fileInfo
|
||||
);
|
||||
|
||||
String message = convertToMessage(auditUploadEvent);
|
||||
auditKafkaPublisher.publishEvent(fileUploadTopic, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDownloadEvent(
|
||||
HttpServletRequest request, long fileSize, String fileName, int formatRegistry,
|
||||
String status, String s3FileUrl) {
|
||||
String userAccountId = jwtTokenService.getUserAccountId(request);
|
||||
|
||||
AuditDownloadEvent event = new AuditDownloadEvent(
|
||||
getEsiaOrgId(request),
|
||||
userAccountId,
|
||||
DateUtils.getClientDateTimeWithZoneFromRequest(request),
|
||||
AuditConstants.getDownloadType(formatRegistry),
|
||||
fileName,
|
||||
s3FileUrl,
|
||||
String.valueOf(fileSize),
|
||||
status
|
||||
);
|
||||
|
||||
String message = convertToMessage(event);
|
||||
auditKafkaPublisher.publishEvent(fileDownloadTopic, message);
|
||||
}
|
||||
|
||||
private String getEsiaOrgId(HttpServletRequest request) {
|
||||
String accessToken = jwtTokenService.getAccessToken(request);
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
|
||||
String scope = esiaAccessToken.getScope();
|
||||
return scope.substring(scope.indexOf('=') + 1, scope.indexOf(' '));
|
||||
}
|
||||
|
||||
public List<SearchCriteria> getSearchCriteriaList(Map<String, FilterInfo> filterInfoMap) {
|
||||
List<SearchCriteria> searchCriteriaList = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, FilterInfo> entry : filterInfoMap.entrySet()) {
|
||||
String searchAttribute = entry.getKey();
|
||||
FilterInfo filterInfo = entry.getValue();
|
||||
String searchValue = filterInfo.getConditions().stream()
|
||||
.map(condition -> condition.getFilterValue() + " " + condition.getFilterType())
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
if (filterInfo.getConditionOperator() != null) {
|
||||
searchValue += "| Operator: " + filterInfo.getConditionOperator();
|
||||
}
|
||||
|
||||
SearchCriteria searchCriteria = new SearchCriteria(searchAttribute, searchValue);
|
||||
searchCriteriaList.add(searchCriteria);
|
||||
}
|
||||
|
||||
return searchCriteriaList;
|
||||
}
|
||||
|
||||
private String convertToMessage(Object event) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(event);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new JsonParsingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SenderInfo {
|
||||
|
||||
@JsonProperty("prnOid")
|
||||
private String prnOid; // идентификатор сотрудника в ЕСИА
|
||||
@JsonProperty("lastName")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import java.time.LocalDateTime;
|
|||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import ru.micord.ervu.util.DateUtil;
|
||||
import ru.micord.ervu.util.DateUtils;
|
||||
|
||||
public class DepartureDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
|
||||
|
||||
|
|
@ -14,6 +14,6 @@ public class DepartureDateTimeDeserializer extends JsonDeserializer<LocalDateTim
|
|||
public LocalDateTime deserialize(JsonParser jsonParser,
|
||||
DeserializationContext deserializationContext) throws IOException {
|
||||
String dateTimeString = jsonParser.getText();
|
||||
return DateUtil.convertToLocalDateTime(dateTimeString);
|
||||
return DateUtils.convertToLocalDateTime(dateTimeString);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,26 @@ package ru.micord.ervu.kafka.controller;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.client.fileupload.WebDavClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.micord.ervu.audit.constants.AuditConstants;
|
||||
import ru.micord.ervu.audit.service.AuditService;
|
||||
import ru.micord.ervu.kafka.exception.ExcerptException;
|
||||
import ru.micord.ervu.kafka.exception.ExcerptResponseException;
|
||||
import ru.micord.ervu.kafka.model.Data;
|
||||
import ru.micord.ervu.kafka.model.ExcerptResponse;
|
||||
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
|
||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
||||
import ru.micord.ervu.util.UrlUtils;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
|
|
@ -27,6 +33,9 @@ public class ErvuKafkaController {
|
|||
@Autowired
|
||||
private ReplyingKafkaService replyingKafkaService;
|
||||
|
||||
@Autowired
|
||||
private AuditService auditService;
|
||||
|
||||
@Autowired
|
||||
private WebDavClient webDavClient;
|
||||
|
||||
|
|
@ -40,10 +49,12 @@ public class ErvuKafkaController {
|
|||
private ObjectMapper objectMapper;
|
||||
|
||||
@RequestMapping(value = "/kafka/excerpt")
|
||||
public ResponseEntity<Resource> getExcerptFile(
|
||||
@RequestHeader("Client-Time-Zone") String clientTimeZone) {
|
||||
|
||||
public ResponseEntity<Resource> getExcerptFile(HttpServletRequest request) {
|
||||
String fileUrl = null;
|
||||
String fileName = null;
|
||||
long fileSize = 0;
|
||||
try {
|
||||
String clientTimeZone = request.getHeader("Client-Time-Zone");
|
||||
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
|
||||
Data data = new Data();
|
||||
data.setErvuId(userIdsPair.getErvuId());
|
||||
|
|
@ -56,16 +67,30 @@ public class ErvuKafkaController {
|
|||
ExcerptResponse excerptResponse = objectMapper.readValue(kafkaResponse, ExcerptResponse.class);
|
||||
|
||||
if (!excerptResponse.getSuccess()) {
|
||||
throw new RuntimeException("Error with getting excerpt url " + excerptResponse.getMessage());
|
||||
throw new ExcerptResponseException(
|
||||
"Error with getting excerpt url " + excerptResponse.getMessage());
|
||||
}
|
||||
else if (excerptResponse.getData() == null || excerptResponse.getData().getFileUrl() == null
|
||||
|| excerptResponse.getData().getFileUrl().isEmpty()) {
|
||||
auditService.processDownloadEvent(request, fileSize, fileName, 1,
|
||||
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
|
||||
);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
return webDavClient.webDavDownloadFile(excerptResponse.getData().getFileUrl());
|
||||
fileUrl = excerptResponse.getData().getFileUrl();
|
||||
fileName = UrlUtils.extractFileNameFromUrl(excerptResponse.getData().getFileUrl());
|
||||
ResponseEntity<Resource> responseEntity = webDavClient.webDavDownloadFile(fileUrl);
|
||||
fileSize = responseEntity.getHeaders().getContentLength();
|
||||
auditService.processDownloadEvent(request, fileSize, fileName, 1,
|
||||
AuditConstants.SUCCESS_STATUS_TYPE, fileUrl
|
||||
);
|
||||
return responseEntity;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
auditService.processDownloadEvent(request, fileSize, fileName, 1,
|
||||
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
|
||||
);
|
||||
throw new ExcerptException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package ru.micord.ervu.kafka.exception;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class ExcerptException extends RuntimeException {
|
||||
public ExcerptException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ExcerptException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ExcerptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package ru.micord.ervu.kafka.exception;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class ExcerptResponseException extends RuntimeException {
|
||||
public ExcerptResponseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import org.springframework.kafka.requestreply.ReplyingKafkaTemplate;
|
|||
import org.springframework.kafka.requestreply.RequestReplyFuture;
|
||||
import org.springframework.kafka.support.KafkaHeaders;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.kafka.exception.KafkaMessageReplyTimeoutException;
|
||||
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
|
||||
|
||||
/**
|
||||
|
|
@ -47,7 +48,7 @@ public class BaseReplyingKafkaServiceImpl implements ReplyingKafkaService {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import static ru.micord.ervu.security.SecurityConstants.ESIA_LOGOUT;
|
|||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
private static final String[] PERMIT_ALL = new String[] {
|
||||
"/version", "/esia/url", "/esia/auth", "esia/refresh", "/esia/logout",
|
||||
"/version", "/esia/url", "/esia/auth", "esia/refresh", "/esia/logout"
|
||||
};
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import ru.micord.ervu.security.SecurityConstants;
|
||||
import ru.micord.ervu.security.esia.model.OrgInfoModel;
|
||||
import ru.micord.ervu.security.esia.service.EsiaAuthService;
|
||||
import ru.micord.ervu.security.esia.service.EsiaDataService;
|
||||
|
|
@ -32,10 +30,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);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/esia/refresh")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package ru.micord.ervu.security.esia.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
|
@ -22,7 +23,10 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.service.okopf.OkopfService;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.audit.constants.AuditConstants;
|
||||
import ru.micord.ervu.audit.service.AuditService;
|
||||
import ru.micord.ervu.security.esia.exception.EsiaException;
|
||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
||||
|
|
@ -33,8 +37,6 @@ import ru.micord.ervu.security.esia.model.OrganizationModel;
|
|||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.micord.ervu.security.esia.config.EsiaConfig;
|
||||
|
|
@ -53,6 +55,9 @@ 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.model.Token;
|
||||
|
||||
import ru.cg.webbpm.modules.core.runtime.api.LocalizedException;
|
||||
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
|
||||
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername;
|
||||
|
||||
/**
|
||||
|
|
@ -61,6 +66,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;
|
||||
@Autowired
|
||||
|
|
@ -75,6 +82,8 @@ public class EsiaAuthService {
|
|||
private OkopfService okopfService;
|
||||
@Autowired
|
||||
private SecurityHelper securityHelper;
|
||||
@Autowired
|
||||
private AuditService auditService;
|
||||
@Value("${ervu.kafka.org.reply.topic}")
|
||||
private String requestReplyTopic;
|
||||
|
||||
|
|
@ -154,13 +163,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;
|
||||
|
|
@ -246,29 +252,29 @@ public class EsiaAuthService {
|
|||
Thread.currentThread().getId(), timeSignSecret, timeRequestAccessToken, timeVerifySecret);
|
||||
}
|
||||
OrgInfo orgInfo = null;
|
||||
String status = null, ervuId = null;
|
||||
try {
|
||||
orgInfo = getOrgInfo(esiaAccessTokenStr);
|
||||
hasRole = ulDataService.checkRole(esiaAccessTokenStr);
|
||||
String ervuId = getErvuId(prnOid, orgInfo);
|
||||
createTokenAndAddCookie(response, prnOid, ervuId, hasRole, expiresIn);
|
||||
ervuId = getErvuId(prnOid, orgInfo);
|
||||
if (!hasRole) {
|
||||
LOGGER.error("The user with id = " + prnOid + " does not have the required role");
|
||||
return new ResponseEntity<>(
|
||||
"Доступ запрещен. Пользователь должен быть включен в группу \"Сотрудник, ответственный за военно-учетную работу\" в ЕСИА",
|
||||
HttpStatus.FORBIDDEN
|
||||
);
|
||||
throw new LocalizedException("access_denied", MESSAGE_SOURCE);
|
||||
}
|
||||
return ResponseEntity.ok("Authentication successful");
|
||||
status = AuditConstants.SUCCESS_STATUS_TYPE;
|
||||
}
|
||||
catch (Exception e) {
|
||||
createTokenAndAddCookie(response, prnOid, null, hasRole , 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_TYPE;
|
||||
if (e instanceof EsiaException || e instanceof JsonProcessingException) {
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (orgInfo!= null){
|
||||
auditService.processAuthEvent(request, orgInfo, prnOid, status,
|
||||
AuditConstants.LOGIN_EVENT_TYPE);
|
||||
}
|
||||
createTokenAndAddCookie(response, prnOid, ervuId, hasRole , expiresIn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -340,7 +346,7 @@ public class EsiaAuthService {
|
|||
String ervuId = getErvuId(prnOid, orgInfo);
|
||||
createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuId, true, expiresIn);
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (EsiaException | IOException | InterruptedException e) {
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -379,9 +385,13 @@ public class EsiaAuthService {
|
|||
}
|
||||
|
||||
public String logout(HttpServletRequest request, HttpServletResponse response) {
|
||||
OrgInfo orgInfo = null;
|
||||
String userId = null;
|
||||
try {
|
||||
userId = jwtTokenService.getUserAccountId(request);
|
||||
String accessToken = EsiaTokensStore.getAccessToken(userId);
|
||||
orgInfo = getOrgInfo(accessToken);
|
||||
securityHelper.clearAccessCookies(response);
|
||||
String userId = jwtTokenService.getUserAccountId(request);
|
||||
EsiaTokensStore.removeAccessToken(userId);
|
||||
EsiaTokensStore.removeRefreshToken(userId);
|
||||
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
||||
|
|
@ -390,9 +400,17 @@ public class EsiaAuthService {
|
|||
Map<String, String> params = mapOf(
|
||||
"client_id", esiaConfig.getClientId(),
|
||||
"redirect_url", redirectUrl);
|
||||
auditService.processAuthEvent(request, orgInfo, userId, AuditConstants.SUCCESS_STATUS_TYPE,
|
||||
AuditConstants.LOGOUT_EVENT_TYPE
|
||||
);
|
||||
return buildUrl(url, params);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (orgInfo != null) {
|
||||
auditService.processAuthEvent(request, orgInfo, userId, AuditConstants.FAILURE_STATUS_TYPE,
|
||||
AuditConstants.LOGOUT_EVENT_TYPE
|
||||
);
|
||||
}
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package ru.micord.ervu.util;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.util.StringUtils.hasText;
|
||||
|
|
@ -11,12 +12,31 @@ import static org.springframework.util.StringUtils.hasText;
|
|||
/**
|
||||
* @author gulnaz
|
||||
*/
|
||||
public final class DateUtil {
|
||||
|
||||
public final class DateUtils {
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_WITH_TIMEZONE_FORMATTER = DateTimeFormatter.ofPattern(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSX");
|
||||
|
||||
private DateUtil() {}
|
||||
private DateUtils() {
|
||||
}
|
||||
|
||||
public static String getCurrentFormattedDateTimeWithZone(){
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
return now.format(DATE_TIME_WITH_TIMEZONE_FORMATTER);
|
||||
}
|
||||
|
||||
public static String getClientDateTimeWithZoneFromRequest(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)
|
||||
|
|
@ -30,6 +50,10 @@ public final class DateUtil {
|
|||
: null;
|
||||
}
|
||||
|
||||
public static String convertToString(LocalDateTime dateTime) {
|
||||
return dateTime == null ? "" : dateTime.format(DATE_TIME_FORMATTER);
|
||||
}
|
||||
|
||||
public static String convertToString(LocalDate date) {
|
||||
return date == null ? "" : date.format(DATE_FORMATTER);
|
||||
}
|
||||
53
backend/src/main/java/ru/micord/ervu/util/NetworkUtils.java
Normal file
53
backend/src/main/java/ru/micord/ervu/util/NetworkUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,13 @@
|
|||
package ru.micord.ervu.util;
|
||||
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.capitalize;
|
||||
import static org.apache.commons.lang3.StringUtils.substring;
|
||||
|
||||
public class StringUtils {
|
||||
public final class StringUtils {
|
||||
|
||||
private StringUtils() {
|
||||
}
|
||||
|
||||
public static String convertToFio(String firstName, String middleName, String lastName) {
|
||||
String firstNameInitial = substring(firstName, 0, 1).toUpperCase();
|
||||
|
|
|
|||
18
backend/src/main/java/ru/micord/ervu/util/UrlUtils.java
Normal file
18
backend/src/main/java/ru/micord/ervu/util/UrlUtils.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package ru.micord.ervu.util;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public final class UrlUtils {
|
||||
|
||||
private UrlUtils(){
|
||||
}
|
||||
|
||||
public static String extractFileNameFromUrl(String url) {
|
||||
String path = URI.create(url).getPath();
|
||||
return path.substring(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
error.unknown=Система временно недоступна. Пожалуйста, повторите попытку позже.
|
||||
|
|
@ -0,0 +1 @@
|
|||
error.unknown=The system is temporarily unavailable. Please try again later.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
kafka_reply_timeout=Превышено время ожидания ответа от сервера. Попробуйте повторить запрос позже или обратитесь к системному администратору
|
||||
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
kafka_reply_timeout=Превышено время ожидания ответа от сервера. Попробуйте повторить запрос позже или обратитесь к системному администратору
|
||||
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА
|
||||
14
config.md
14
config.md
|
|
@ -828,3 +828,17 @@ JBPM использует 3 корневых категории логирова
|
|||
- `ERVU_KAFKA_EXCERPT_REQUEST_TOPIC` - топик для записи запроса для получения выписки по журналу взаимодействия
|
||||
- `ERVU_KAFKA_EXCERPT_REPLY_TOPIC` - топик для чтения выписки по журналу взаимодействия. Содержит ссылку на S3 с файлом выписки
|
||||
- `DB.JOURNAL.EXCLUDED.STATUSES` - статусы файла, которые необходимо исключить при получении данных по журналу взаимодействия из базы данных приложения
|
||||
|
||||
|
||||
#### Взаимодействие с Kafka audit
|
||||
- `AUDIT_KAFKA_AUTHORIZATION_TOPIC` - топик для отправки аудита в журнал авторизации
|
||||
- `AUDIT_KAFKA_ACTION_TOPIC` - топик для отправки аудита в журнал действий пользователя
|
||||
- `AUDIT_KAFKA_FILE_UPLOAD_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` - флажок для включения записи аудита в кафку
|
||||
|
|
|
|||
|
|
@ -46,6 +46,18 @@ ESNSI_OKOPF_RETRY_DELAY_LOAD=3000
|
|||
ESNSI_OKOPF_CONNECT_TIMEOUT=2
|
||||
ESNSI_OKOPF_READ_TIMEOUT=4
|
||||
|
||||
AUDIT_KAFKA_AUTHORIZATION_TOPIC=ervu.lkrp.auth.events
|
||||
AUDIT_KAFKA_ACTION_TOPIC=ervu.lkrp.action.events
|
||||
AUDIT_KAFKA_FILE_UPLOAD_TOPIC=ervu.lkrp.download.request
|
||||
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
|
||||
|
||||
ERVU_FILE_UPLOAD_MAX_FILE_SIZE=5242880
|
||||
ERVU_FILE_UPLOAD_MAX_REQUEST_SIZE=6291456
|
||||
ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD=0
|
||||
|
|
|
|||
|
|
@ -100,6 +100,17 @@
|
|||
<property name="file.webdav.lifetime.seconds" value="300"/>
|
||||
<property name="file.webdav.extensions" value="csv,xlsx"/>
|
||||
<property name="webdav.bad_servers.cache.expire.seconds" value="120"/>
|
||||
<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.file.upload.topic" value="ervu.lkrp.download.request"/>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.9.8-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
6
frontend/package-lock.json
generated
6
frontend/package-lock.json
generated
|
|
@ -2462,9 +2462,9 @@
|
|||
}
|
||||
},
|
||||
"cadesplugin_api": {
|
||||
"version": "2.0.4-micord.1",
|
||||
"resolved": "https://repo.micord.ru/repository/npm-all/cadesplugin_api/-/cadesplugin_api-2.0.4-micord.1.tgz",
|
||||
"integrity": "sha512-FyGVi1VWIyJOW1zOOQN0IkTH/Z/8g7pNWH7A71nf0h21FCX9SacUfgRwID+gl+NlpYiT3m+yZGdlEJsiDeV8JA=="
|
||||
"version": "2.1.1-micord.2",
|
||||
"resolved": "https://repo.micord.ru/repository/npm-all/cadesplugin_api/-/cadesplugin_api-2.1.1-micord.2.tgz",
|
||||
"integrity": "sha512-+j8RfbL7t2YMlSOC9Oa6+NoNLMYC3ZHkc9W6JQnV5+NBUqKLPAlLL1DF6llmW8coRdmgH6nZU8skvdk6M6qaBg=="
|
||||
},
|
||||
"calendar-utils": {
|
||||
"version": "0.8.5",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.9.8-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
|
|
@ -606,7 +606,7 @@
|
|||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul div[page-filesentlog] {
|
||||
height: calc(100% - var(--h-footer));
|
||||
flex: 1;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul #filesentlog,
|
||||
.webbpm.ervu_lkrp_ul .journal,
|
||||
|
|
|
|||
|
|
@ -125,13 +125,16 @@ body.webbpm.ervu_lkrp_ul {
|
|||
background: url(../img/svg/logo.svg) no-repeat 0 50%;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .header .header-logo .main-page {
|
||||
font-family: 'InterSB';
|
||||
font-family: 'InterL';
|
||||
margin-left: calc(62px + 16px);
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .header .header-logo .header-menu-left {
|
||||
font-family: 'InterL';
|
||||
margin-left: 24px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .header .header-logo .active {
|
||||
font-family: 'InterSB';
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .header .header-menu {
|
||||
margin-right: var(--w-screen);
|
||||
}
|
||||
|
|
@ -213,6 +216,8 @@ body.webbpm.ervu_lkrp_ul {
|
|||
bottom: 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .container-inside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'Inter';
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
</div>
|
||||
<div *ngIf="!isLanding" class="header-logo">
|
||||
<div class="logo"><a routerLink="/"></a></div>
|
||||
<div class="main-page"><a routerLink="/">Главная</a></div>
|
||||
<div class="header-menu-left"><a routerLink="/filesentlog">Журнал взаимодействий</a></div>
|
||||
<div class="main-page" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}"><a routerLink="/">Главная</a></div>
|
||||
<div class="header-menu-left" routerLinkActive="active"><a routerLink="/filesentlog">Журнал взаимодействий</a></div>
|
||||
</div>
|
||||
<div *ngIf="!isLanding" class="header-menu">
|
||||
<div ngbDropdown class="logout" log-out></div>
|
||||
|
|
|
|||
61
frontend/src/ts/ervu/LinkClickHandler.ts
Normal file
61
frontend/src/ts/ervu/LinkClickHandler.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import {AnalyticalScope, Behavior, Control, NotNull} from "@webbpm/base-package";
|
||||
import {AuditService} from "./service/AuditService";
|
||||
import {ElementRef, Input} from "@angular/core";
|
||||
import {LinkEventTypeEnum} from "./component/enum/LinkEventTypeEnum";
|
||||
|
||||
@AnalyticalScope(Control)
|
||||
export class LinkClickHandler extends Behavior {
|
||||
@Input()
|
||||
@NotNull()
|
||||
public eventType: LinkEventTypeEnum;
|
||||
private control: Control;
|
||||
private auditService: AuditService;
|
||||
private el: ElementRef
|
||||
|
||||
public initialize() {
|
||||
super.initialize();
|
||||
this.control = this.getScript(Control);
|
||||
this.injector.get(AuditService);
|
||||
this.auditService = this.injector.get(AuditService);
|
||||
this.el = this.control.getEl();
|
||||
}
|
||||
|
||||
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') {
|
||||
if (this.eventType === LinkEventTypeEnum.DOWNLOAD_EXAMPLE
|
||||
|| this.eventType === LinkEventTypeEnum.DOWNLOAD_TEMPLATE) {
|
||||
const href = target.getAttribute('href');
|
||||
if (href) {
|
||||
const fileName = this.extractFileNameFromHref(href);
|
||||
this.auditService.logActionAudit(this.eventType, null, fileName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.auditService.logActionAudit(this.eventType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extractFileNameFromHref(href: string): string {
|
||||
const parts = href.split('/');
|
||||
const fileNameWithExtension = parts[parts.length - 1];
|
||||
return fileNameWithExtension.split('?')[0];
|
||||
}
|
||||
}
|
||||
5
frontend/src/ts/ervu/component/enum/LinkEventTypeEnum.ts
Normal file
5
frontend/src/ts/ervu/component/enum/LinkEventTypeEnum.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export enum LinkEventTypeEnum {
|
||||
NAVIGATION_TO_SOURCE = "Переход на другие источники",
|
||||
DOWNLOAD_TEMPLATE = "Скачивание шаблона",
|
||||
DOWNLOAD_EXAMPLE = "Скачивание примера заполнения формы"
|
||||
}
|
||||
|
|
@ -4,11 +4,14 @@ import {
|
|||
GridRow,
|
||||
GridRowModelType,
|
||||
GridV2,
|
||||
GridV2Column, Visible
|
||||
GridV2Column,
|
||||
Visible
|
||||
} from "@webbpm/base-package";
|
||||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
import {
|
||||
ColDef, FilterChangedEvent,
|
||||
ColDef,
|
||||
GridReadyEvent,
|
||||
FilterChangedEvent,
|
||||
ICellRendererParams,
|
||||
ITooltipParams,
|
||||
ValueFormatterParams,
|
||||
|
|
@ -17,6 +20,9 @@ import {
|
|||
import {StaticColumnInitializer} from "./StaticColumnInitializer";
|
||||
import {InMemoryStaticGridRpcService} from "../../../generated/ru/micord/ervu/service/rpc/InMemoryStaticGridRpcService";
|
||||
import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn";
|
||||
import { FilterService } from "../../service/FilterService";
|
||||
import {AuditConstants, AuditService, FilterInfo} from "../../service/AuditService";
|
||||
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
|
|
@ -30,6 +36,7 @@ export class InMemoryStaticGrid extends GridV2 {
|
|||
public columnFiltersChanged: Event<any> = new Event<any>();
|
||||
|
||||
private rpcService: InMemoryStaticGridRpcService;
|
||||
private auditService: AuditService;
|
||||
|
||||
getRowModelType(): string {
|
||||
return GridRowModelType.CLIENT_SIDE;
|
||||
|
|
@ -37,6 +44,7 @@ export class InMemoryStaticGrid extends GridV2 {
|
|||
|
||||
protected initGrid() {
|
||||
super.initGrid();
|
||||
this.auditService = this.injector.get(AuditService);
|
||||
this.rpcService = this.getScript(InMemoryStaticGridRpcService);
|
||||
if (this.rpcService) {
|
||||
this.rpcService.loadData().then(response => {
|
||||
|
|
@ -47,6 +55,39 @@ export class InMemoryStaticGrid extends GridV2 {
|
|||
}
|
||||
}
|
||||
|
||||
onGridReady(event: GridReadyEvent) {
|
||||
super.onGridReady(event);
|
||||
this.addColumnFilterChangeListener(() => {
|
||||
this.auditActiveFilters();
|
||||
})
|
||||
}
|
||||
|
||||
private auditActiveFilters() {
|
||||
const filterModel = this.gridApi.getFilterModel();
|
||||
if (!filterModel || Object.keys(filterModel).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filterMap: Record<string, FilterInfo> = {};
|
||||
Object.entries(filterModel).forEach(([column, agFilter]) => {
|
||||
const columnDef = this.gridApi.getColumnDef(column);
|
||||
if (!columnDef) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = FilterService.getFilterData(columnDef, agFilter);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
filterMap[columnDef.headerName] = data;
|
||||
});
|
||||
|
||||
if (Object.keys(filterMap).length > 0) {
|
||||
this.auditService.logActionAudit(AuditConstants.FILTER_EVENT, filterMap);
|
||||
}
|
||||
}
|
||||
|
||||
getColumns(): any[] {
|
||||
return this.getScriptsInChildren(GridV2Column)
|
||||
.map(columnV2 => columnV2.getScript(StaticGridColumn));
|
||||
|
|
|
|||
54
frontend/src/ts/ervu/service/AuditService.ts
Normal file
54
frontend/src/ts/ervu/service/AuditService.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
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, filterInfo?: Record<string, FilterInfo>,
|
||||
fileName?: string): void {
|
||||
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
const route = this.router.url;
|
||||
const sourceUrl = window.location.href;
|
||||
const auditEvent: AuditAction = {
|
||||
eventType: eventType,
|
||||
route: route,
|
||||
sourceUrl: sourceUrl,
|
||||
filterInfo: filterInfo,
|
||||
fileName: fileName
|
||||
}
|
||||
this.httpClient.post("audit/action", auditEvent, {
|
||||
headers: {
|
||||
"Client-Time-Zone": timeZone,
|
||||
}
|
||||
}).toPromise();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class AuditConstants {
|
||||
public static readonly OPEN_PAGE_EVENT = "Открытие страницы";
|
||||
public static readonly FILTER_EVENT = "Поиск по фильтру";
|
||||
}
|
||||
|
||||
export interface AuditAction {
|
||||
eventType: string;
|
||||
route: string;
|
||||
sourceUrl: string;
|
||||
filterInfo?: Record<string, FilterInfo>;
|
||||
fileName?: string
|
||||
}
|
||||
|
||||
export interface FilterInfo {
|
||||
conditionOperator?: string;
|
||||
conditions: FilterCondition[];
|
||||
}
|
||||
|
||||
export interface FilterCondition {
|
||||
filterValue: string;
|
||||
filterType: string;
|
||||
}
|
||||
119
frontend/src/ts/ervu/service/FilterService.ts
Normal file
119
frontend/src/ts/ervu/service/FilterService.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community";
|
||||
import {SetFilter} from "../component/grid/filter/SetFilter";
|
||||
import {FilterInfo} from "./AuditService";
|
||||
|
||||
export class FilterService {
|
||||
static getFilterData(columnDef: any, agFilter: any): FilterInfo {
|
||||
if (!agFilter) {
|
||||
return;
|
||||
}
|
||||
switch (columnDef.filter) {
|
||||
case DateFilter:
|
||||
case NumberFilter:
|
||||
return this.processDateOrNumberFilter(agFilter);
|
||||
case SetFilter:
|
||||
return this.processSetFilter(agFilter);
|
||||
case TextFilter:
|
||||
return this.processTextFilter(agFilter);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static processDateOrNumberFilter(agFilter: any): FilterInfo {
|
||||
if (!agFilter.condition1 && !agFilter.condition2) {
|
||||
if (agFilter.type === "inRange") {
|
||||
return this.createSingleConditionData(
|
||||
this.formatFilterValue(agFilter.dateFrom, agFilter.filter, agFilter.filterType),
|
||||
agFilter.type,
|
||||
this.formatFilterValue(agFilter.dateTo, agFilter.filterTo, agFilter.filterType)
|
||||
);
|
||||
}
|
||||
if (agFilter.type === "blank" || agFilter.type === "notBlank") {
|
||||
return this.createSingleConditionData(null, agFilter.type);
|
||||
}
|
||||
return this.createSingleConditionData(
|
||||
this.formatFilterValue(agFilter.dateFrom, agFilter.filter, agFilter.filterType),
|
||||
agFilter.type,
|
||||
);
|
||||
}
|
||||
return this.createDualConditionData(agFilter);
|
||||
}
|
||||
|
||||
private static processSetFilter(agFilter: any): FilterInfo {
|
||||
if (agFilter.value) {
|
||||
return this.createSingleConditionData(
|
||||
agFilter.value.join(", "),
|
||||
"in",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private static processTextFilter(agFilter: any): FilterInfo {
|
||||
if (!agFilter.condition1 && !agFilter.condition2) {
|
||||
if (agFilter.type === "blank" || agFilter.type === "notBlank") {
|
||||
return this.createSingleConditionData(null, agFilter.type);
|
||||
}
|
||||
return this.createSingleConditionData(
|
||||
agFilter.filter,
|
||||
agFilter.type
|
||||
);
|
||||
}
|
||||
return this.createDualConditionData(agFilter);
|
||||
}
|
||||
|
||||
private static createSingleConditionData(
|
||||
filterValue: string,
|
||||
filterType: string,
|
||||
endValue?: string
|
||||
): FilterInfo {
|
||||
return {
|
||||
conditionOperator: undefined,
|
||||
conditions: [{
|
||||
filterValue: endValue ? `${filterValue} to ${endValue}` : filterValue,
|
||||
filterType: filterType,
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
private static createDualConditionData(agFilter: any): FilterInfo {
|
||||
const condition1 = agFilter.condition1
|
||||
? {
|
||||
filterValue: this.getConditionValue(agFilter.condition1),
|
||||
filterType: agFilter.condition1.type,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const condition2 = agFilter.condition2
|
||||
? {
|
||||
filterValue: this.getConditionValue(agFilter.condition2),
|
||||
filterType: agFilter.condition2.type,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
conditionOperator: agFilter.operator,
|
||||
conditions: [condition1, condition2]
|
||||
};
|
||||
}
|
||||
|
||||
private static getConditionValue(condition: any): string {
|
||||
if (condition.type === "inRange") {
|
||||
return `${this.formatFilterValue(condition.dateFrom, condition.filter, condition.filterType)}
|
||||
to ${this.formatFilterValue(condition.dateTo, condition.filterTo, condition.filterType)}`;
|
||||
}
|
||||
if (condition.type === "blank" || condition.type === "notBlank") {
|
||||
return null;
|
||||
}
|
||||
return this.formatFilterValue(condition.dateFrom, condition.filter, condition.filterType);
|
||||
}
|
||||
|
||||
private static formatFilterValue(dateValue: any, defaultValue: any, filterType: string): string {
|
||||
if (filterType === "date" && dateValue) {
|
||||
return new Date(dateValue).toISOString();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import {InMemoryStaticGrid} from "../../ervu/component/grid/InMemoryStaticGrid";
|
|||
import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFileButton";
|
||||
import {AuthenticationService} from "../security/authentication.service";
|
||||
import {HomeLandingComponent} from "./component/home-landing.component";
|
||||
import {AuditService} from "../../ervu/service/AuditService";
|
||||
|
||||
registerLocaleData(localeRu);
|
||||
export const DIRECTIVES = [
|
||||
|
|
@ -67,7 +68,7 @@ export function checkAuthentication(authService: AuthenticationService): () => P
|
|||
DIRECTIVES
|
||||
],
|
||||
providers: [
|
||||
AuthenticationService,
|
||||
AuthenticationService, AuditService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: checkAuthentication,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {ChangeDetectorRef, Component, OnInit} from "@angular/core";
|
||||
import {CadesHelper} from "@webbpm/base-package";
|
||||
import {AccessChecker} from "../../security/AccessChecker";
|
||||
import {AppConfigService} from "@webbpm/base-package";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
|
|
@ -8,12 +9,15 @@ import {AccessChecker} from "../../security/AccessChecker";
|
|||
templateUrl: "../../../../../src/resources/template/app/component/home_landing.html",
|
||||
})
|
||||
export class HomeLandingComponent implements OnInit {
|
||||
private cspTimeout: number;
|
||||
|
||||
constructor(private cd: ChangeDetectorRef) {
|
||||
constructor(private cd: ChangeDetectorRef,
|
||||
private appConfigService: AppConfigService) {
|
||||
this.cspTimeout = this.appConfigService.getParamValue("csp_load_timeout");
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
AccessChecker.checkCsp()
|
||||
AccessChecker.checkCsp(this.cspTimeout)
|
||||
.then((cspCheckPassed) => {
|
||||
if (AccessChecker.checkBrowser() && cspCheckPassed) {
|
||||
document.getElementById("browser-check-info").hidden = true;
|
||||
|
|
|
|||
|
|
@ -7,32 +7,49 @@ export class AccessChecker {
|
|||
return userAgent.indexOf("Chromium GOST") > -1 || userAgent.indexOf("YaBrowser") > -1;
|
||||
}
|
||||
|
||||
public static checkCsp(): Promise<boolean> {
|
||||
return CadesHelper.plugin()
|
||||
.then(() => {
|
||||
return CadesHelper.plugin().CreateObjectAsync("CAdESCOM.About");
|
||||
})
|
||||
.then((oAbout) => {
|
||||
return oAbout.CSPVersion()
|
||||
.then((cspVersion) => {
|
||||
return Promise.all([
|
||||
cspVersion.MajorVersion,
|
||||
cspVersion.MinorVersion,
|
||||
cspVersion.BuildVersion
|
||||
])
|
||||
.then(([majorVersion, minorVersion, buildVersion]) => {
|
||||
if (majorVersion === 5 && minorVersion === 0 && buildVersion >= 12000) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
console.error(
|
||||
`Версия CSP не подходит. Необходима версия 5.0.12000 и выше, текущая версия: ${majorVersion}.${minorVersion}.${buildVersion}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
public static checkCsp(cspTimeout: number): Promise<boolean> {
|
||||
let timeoutMillis: number;
|
||||
if (cspTimeout) {
|
||||
timeoutMillis = cspTimeout * 1000;
|
||||
}
|
||||
else {
|
||||
timeoutMillis = 5000;
|
||||
}
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error(`Истекло время ожидания загрузки плагина`));
|
||||
}, timeoutMillis);
|
||||
return CadesHelper.plugin()
|
||||
.then(() => {
|
||||
clearTimeout(timeout);
|
||||
return CadesHelper.plugin().CreateObjectAsync("CAdESCOM.About");
|
||||
})
|
||||
.then((oAbout) => {
|
||||
return oAbout.CSPVersion()
|
||||
.then((cspVersion) => {
|
||||
return Promise.all([
|
||||
cspVersion.MajorVersion,
|
||||
cspVersion.MinorVersion,
|
||||
cspVersion.BuildVersion
|
||||
])
|
||||
.then(([majorVersion, minorVersion, buildVersion]) => {
|
||||
if (majorVersion === 5 && minorVersion === 0 && buildVersion >= 12000) {
|
||||
resolve(true);
|
||||
}
|
||||
else {
|
||||
console.error(
|
||||
`Версия CSP не подходит. Необходима версия 5.0.12000 и выше, текущая версия: ${majorVersion}.${minorVersion}.${buildVersion}`
|
||||
);
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
clearTimeout(timeout);
|
||||
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Ошибка при получении версии CSP:", error);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -2,30 +2,37 @@ import {Injectable} from "@angular/core";
|
|||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
|
||||
import {Observable} from "rxjs";
|
||||
import {HttpClient, HttpParams} from "@angular/common/http";
|
||||
import {MessagesService} from "@webbpm/base-package";
|
||||
import {AppConfigService, MessagesService, ProgressIndicationService} from "@webbpm/base-package";
|
||||
import {AuthenticationService} from "../authentication.service";
|
||||
import {EsiaErrorDetail} from "../EsiaErrorDetail";
|
||||
import {AccessChecker} from "../AccessChecker";
|
||||
|
||||
@Injectable({providedIn:'root'})
|
||||
export abstract class AuthGuard implements CanActivate {
|
||||
private cspTimeout: number;
|
||||
|
||||
protected constructor(
|
||||
protected router: Router,
|
||||
private httpClient: HttpClient,
|
||||
private authenticationService: AuthenticationService,
|
||||
private messageService: MessagesService
|
||||
private messageService: MessagesService,
|
||||
private appConfigService: AppConfigService,
|
||||
private progressIndicationService: ProgressIndicationService
|
||||
) {
|
||||
this.cspTimeout = this.appConfigService.getParamValue("csp_load_timeout");
|
||||
}
|
||||
|
||||
public canActivate(route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||
this.progressIndicationService.showProgressBar();
|
||||
let url = new URL(window.location.href);
|
||||
if (!AccessChecker.checkBrowser()) {
|
||||
this.progressIndicationService.hideProgressBar();
|
||||
this.router.navigateByUrl('/home');
|
||||
return false;
|
||||
}
|
||||
return AccessChecker.checkCsp()
|
||||
|
||||
return AccessChecker.checkCsp(this.cspTimeout)
|
||||
.then((cspCheckPassed) => {
|
||||
if (!cspCheckPassed) {
|
||||
this.router.navigateByUrl('/home');
|
||||
|
|
@ -94,7 +101,8 @@ export abstract class AuthGuard implements CanActivate {
|
|||
console.error(reason);
|
||||
}
|
||||
return false
|
||||
});
|
||||
})
|
||||
.finally(() => this.progressIndicationService.hideProgressBar());
|
||||
}
|
||||
|
||||
private checkAccess(): boolean {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
2
pom.xml
2
pom.xml
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.9.8-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>backend</module>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.9.8-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
|
|
@ -1670,6 +1670,12 @@
|
|||
<simple>"Запросите выписку, чтобы посмотреть журнал взаимодействий за весь период"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>visible</key>
|
||||
<value>
|
||||
<simple>false</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="737b67e2-295f-4356-a1e1-9419344d8c85"/>
|
||||
|
|
@ -1736,6 +1742,206 @@
|
|||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
<children id="78640b5d-37d1-4940-aafc-fc7a38b907cf">
|
||||
<prototypeId>98594cec-0a9b-4cef-af09-e1b71cb2ad9e</prototypeId>
|
||||
<componentRootId>78640b5d-37d1-4940-aafc-fc7a38b907cf</componentRootId>
|
||||
<name>Action Controller</name>
|
||||
<container>false</container>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="37dff5c8-1599-4984-b107-c44a87b6da2e">
|
||||
<properties>
|
||||
<entry>
|
||||
<key>elseActions</key>
|
||||
<value>
|
||||
<item id="80889965-e277-41cc-bbc5-ea7948ad8b60" removed="false">
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>behavior</key>
|
||||
<value>
|
||||
<simple>{"objectId":"097a2141-13d0-4b15-ac5f-5c310b3753f7","packageName":"component","className":"Text","type":"TS"}</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>method</key>
|
||||
<value>
|
||||
<simple>"setVisible"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>value</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>staticValue</key>
|
||||
<value>
|
||||
<implRef type="TS">
|
||||
<className>boolean</className>
|
||||
<packageName></packageName>
|
||||
</implRef>
|
||||
<simple>false</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>eventRefs</key>
|
||||
<value>
|
||||
<item id="6f361a45-7ce6-4ca5-b927-ed4db7b2e39c" removed="false">
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>behavior</key>
|
||||
<value>
|
||||
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>propertyName</key>
|
||||
<value>
|
||||
<simple>"visibleChangeEvent"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>ifCondition</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>conditions</key>
|
||||
<value>
|
||||
<item id="558f60e6-3e60-4fc8-b790-dc7ebd17b4d4" removed="false">
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>_isGroupSelected</key>
|
||||
<value>
|
||||
<simple>false</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>one</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>conditionFirstPart</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>objectValue</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>behavior</key>
|
||||
<value>
|
||||
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>method</key>
|
||||
<value>
|
||||
<simple>"isVisible"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>conditionSecondPart</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>staticValue</key>
|
||||
<value>
|
||||
<implRef type="TS">
|
||||
<className>boolean</className>
|
||||
<packageName></packageName>
|
||||
</implRef>
|
||||
<simple>true</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>operation</key>
|
||||
<value>
|
||||
<simple>"EQUALS"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>logicalOperation</key>
|
||||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>thenActions</key>
|
||||
<value>
|
||||
<item id="db6a9474-f16f-4015-8103-655173944b7e" removed="false">
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>behavior</key>
|
||||
<value>
|
||||
<simple>{"objectId":"097a2141-13d0-4b15-ac5f-5c310b3753f7","packageName":"component","className":"Text","type":"TS"}</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>method</key>
|
||||
<value>
|
||||
<simple>"setVisible"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>value</key>
|
||||
<value>
|
||||
<complex>
|
||||
<entry>
|
||||
<key>staticValue</key>
|
||||
<value>
|
||||
<implRef type="TS">
|
||||
<className>boolean</className>
|
||||
<packageName></packageName>
|
||||
</implRef>
|
||||
<simple>true</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</entry>
|
||||
</complex>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
<children id="f4d7dfb7-67e6-41bd-bef6-ae9751ce2088">
|
||||
<prototypeId>3057d447-6d17-48a8-b096-b14ea88d17e8</prototypeId>
|
||||
<componentRootId>f4d7dfb7-67e6-41bd-bef6-ae9751ce2088</componentRootId>
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@
|
|||
<componentRootId>3ed7cd92-3c7a-4d6f-a22c-1f3c4031bb61</componentRootId>
|
||||
<name>VB - левый</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
|
||||
<properties>
|
||||
|
|
@ -1472,6 +1473,22 @@
|
|||
</item>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="69b73946-ac9d-48be-aaa9-21629ec156b9">
|
||||
<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>
|
||||
|
|
@ -1495,6 +1512,7 @@
|
|||
<componentRootId>2f05ef7d-9092-4180-a361-8fecb3dd7542</componentRootId>
|
||||
<name>Date time picker</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="5274357c-ce77-4621-8965-bd9c80700008">
|
||||
<properties>
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@
|
|||
<componentRootId>3e78f422-3db3-45b9-b531-f4aec5314dab</componentRootId>
|
||||
<name>Группа полей</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
|
||||
<properties>
|
||||
|
|
@ -207,7 +206,6 @@
|
|||
<componentRootId>c9898352-a317-4117-bfcc-28b5c4d9a0d1</componentRootId>
|
||||
<name>Горизонтальный контейнер</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
|
||||
<properties>
|
||||
|
|
@ -312,7 +310,6 @@
|
|||
<componentRootId>1be5e2cd-f42e-40c6-971c-e92f997a7139</componentRootId>
|
||||
<name>Горизонтальный контейнер</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
|
||||
<scripts id="b6068710-0f31-48ec-8e03-c0c1480a40c0"/>
|
||||
|
|
@ -480,6 +477,7 @@
|
|||
<componentRootId>22ffee0b-eb21-48c8-829c-da9f5dfa9459</componentRootId>
|
||||
<name>Inner html_xls</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="2dbb6708-68d5-4eff-a760-a0f091ba1380">
|
||||
<properties>
|
||||
|
|
@ -500,6 +498,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="3891ca34-850d-47c8-aa2f-1eda4bbad60e">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -509,6 +523,7 @@
|
|||
<componentRootId>ddd8bddd-4b83-4caa-a29f-7d028aaebdc5</componentRootId>
|
||||
<name>Inner html_csv</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="2dbb6708-68d5-4eff-a760-a0f091ba1380">
|
||||
<properties>
|
||||
|
|
@ -530,6 +545,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="d916fc3c-5e97-4553-8760-db4ca8659e34">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -604,6 +635,7 @@
|
|||
<componentRootId>c341ed51-c07a-4c5b-bc13-42acc337f639</componentRootId>
|
||||
<name>Inner html_xls</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="2dbb6708-68d5-4eff-a760-a0f091ba1380">
|
||||
<properties>
|
||||
|
|
@ -624,6 +656,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="637f4816-331a-4d8b-90e4-b5f090386eea">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -633,6 +681,7 @@
|
|||
<componentRootId>74bf561e-8f86-4dd9-a6ed-eccd89ca0086</componentRootId>
|
||||
<name>Inner html_csv</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="2dbb6708-68d5-4eff-a760-a0f091ba1380">
|
||||
<properties>
|
||||
|
|
@ -653,6 +702,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="c29421c8-b3fa-44de-b823-b4e620eccf2e">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -1155,6 +1220,7 @@
|
|||
<componentRootId>9b4bd780-47ec-4e2c-b859-33a4100a7539</componentRootId>
|
||||
<name>Inner html_xls</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="2dbb6708-68d5-4eff-a760-a0f091ba1380">
|
||||
<properties>
|
||||
|
|
@ -1170,6 +1236,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="1b927f46-31ce-4347-a3e7-299b574881cf">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -1193,6 +1275,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="ebddd83d-e10d-4f84-97e9-032cac81992c">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -2273,7 +2371,6 @@
|
|||
<componentRootId>9138d81a-f635-42f6-915c-b3c7be4e2160</componentRootId>
|
||||
<name>Группа полей</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
|
||||
<properties>
|
||||
|
|
@ -2572,6 +2669,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="a09fb7b7-808a-4801-8ae8-0c832246ba63">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -2601,6 +2714,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="acf698fd-bcbd-470f-a4fa-280f67c42f86">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -2695,6 +2824,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="19593dfe-ab78-489c-b80a-7007f5991b1b">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -2724,6 +2869,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="f582e649-3607-4179-a37b-082bb7f9177e">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -3206,6 +3367,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="212ce5b2-a27b-4d0e-902c-10336f8222d7">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -3229,6 +3406,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="f249497c-7ef5-4558-a31a-55fbeb73a513">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -4309,7 +4502,6 @@
|
|||
<componentRootId>ae731885-3bdd-433d-a29c-37d5811585a7</componentRootId>
|
||||
<name>Группа полей</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
|
||||
<properties>
|
||||
|
|
@ -4608,6 +4800,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="747f7ebb-298d-4a8a-a187-ceb32f77f853">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -4637,6 +4845,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="6947e1b6-e289-4319-93ba-0cb2c3480c34">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -4724,6 +4948,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="1c5e65a2-c50e-4314-bc91-e58f919ce658">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -4753,6 +4993,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="8273436d-cfa6-4d84-8992-a0343f3ecf7e">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -5185,6 +5441,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="1b9401b0-c043-48bc-adb7-3a5540bcfd82">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -5208,6 +5480,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="6c736c38-7ea1-4e21-8a74-4d8839137c39">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -6341,7 +6629,6 @@
|
|||
<componentRootId>7057bbcb-cff2-4e31-812d-ba7e043a4bcc</componentRootId>
|
||||
<name>Группа полей</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
|
||||
<properties>
|
||||
|
|
@ -6640,6 +6927,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="b2eabc2b-e7a8-4965-b7c0-73580f45f5a9">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -6669,6 +6972,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="6bc9f53c-e89b-4923-ad4d-cc301606ec32">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -6756,6 +7075,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="3f1e0672-6273-4451-a9b9-c732419b0aa6">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -6785,6 +7120,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="12c6f230-ed4c-44f5-9918-0d0ca2586050">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -7268,6 +7619,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="23d925ca-0f1f-4055-a2e3-43cece1c1d02">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -7291,6 +7658,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="46f4dd2d-a8cc-4eb2-b75e-4aaeb9701f6a">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -8371,7 +8754,6 @@
|
|||
<componentRootId>991237d3-8cb9-48af-8501-030a3c8c6cfc</componentRootId>
|
||||
<name>Группа полей</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
|
||||
<properties>
|
||||
|
|
@ -8670,6 +9052,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="2d8f08ad-2b85-40f9-bf1e-5dd557315d8e">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -8699,6 +9097,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="c8cbb214-e1d3-48d0-bf32-d1338bfef1b9">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_TEMPLATE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -8786,6 +9200,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="1d4169a0-5fac-4536-9206-58c559668cd7">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -8815,6 +9245,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="919e9427-c9e6-422b-8aa6-fdfe2a985100">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -9298,6 +9744,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="bc0feeb4-3fa0-47f9-9c38-57db8e4c6015">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -9321,6 +9783,22 @@
|
|||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="87030d5b-ea86-459b-8126-4798176a4ab7">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</children>
|
||||
|
|
@ -10401,7 +10879,6 @@
|
|||
<componentRootId>f4eafa61-1ea3-440a-806b-7b05ec416871</componentRootId>
|
||||
<name>Диалог - сведения направлены</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
|
||||
<properties>
|
||||
|
|
@ -10565,7 +11042,6 @@
|
|||
<componentRootId>894c4e19-0bd3-4e13-9bd6-d40ab378ca21</componentRootId>
|
||||
<name>Диалог - что-то пошло не так</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
|
||||
<properties>
|
||||
|
|
@ -10759,7 +11235,6 @@
|
|||
<componentRootId>a2201fe8-183a-40c7-88ed-bbb07bf2c919</componentRootId>
|
||||
<name>Группа полей</name>
|
||||
<container>true</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="46f20297-81d1-4786-bb17-2a78ca6fda6f">
|
||||
<properties>
|
||||
|
|
@ -10804,6 +11279,22 @@
|
|||
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
|
||||
<enabled>false</enabled>
|
||||
</scripts>
|
||||
<scripts id="cbe4f18e-3eb5-4f83-84fb-94e420bada94">
|
||||
<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="8152b078-7230-4fb8-994f-023809b95e44">
|
||||
<prototypeId>fe6407f3-4a81-4b9e-8861-49483cb708a4</prototypeId>
|
||||
|
|
@ -10828,6 +11319,7 @@
|
|||
<componentRootId>8152b078-7230-4fb8-994f-023809b95e44</componentRootId>
|
||||
<name>Inner html_xls</name>
|
||||
<container>false</container>
|
||||
<expanded>false</expanded>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="2dbb6708-68d5-4eff-a760-a0f091ba1380">
|
||||
<properties>
|
||||
|
|
@ -10848,6 +11340,22 @@
|
|||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="a761cf7a-5fbb-43c0-a136-696c1dd3c77e">
|
||||
<classRef type="TS">
|
||||
<className>LinkClickHandler</className>
|
||||
<packageName>ervu</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>eventType</key>
|
||||
<value>
|
||||
<simple>"DOWNLOAD_EXAMPLE"</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
|
|
@ -10991,6 +11499,22 @@
|
|||
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
|
||||
<enabled>false</enabled>
|
||||
</scripts>
|
||||
<scripts id="66b61fd0-4da5-44b7-84dc-9f2cfb60e2f1">
|
||||
<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="093b8b33-a959-43e3-9562-249f123585cb">
|
||||
<prototypeId>e32ae1f5-5b14-45f1-abb6-f52c34b3b570</prototypeId>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue