Merge branch 'develop' into feature/SUPPORT-8735_autosize
|
|
@ -24,17 +24,17 @@ import org.springframework.kafka.core.ProducerFactory;
|
|||
*/
|
||||
@Configuration
|
||||
public class AvKafkaConfig {
|
||||
@Value("${av.kafka.bootstrap.servers}")
|
||||
@Value("${kafka.hosts}")
|
||||
private String kafkaUrl;
|
||||
@Value("${av.kafka.security.protocol}")
|
||||
@Value("${kafka.auth_sec_proto}")
|
||||
private String securityProtocol;
|
||||
@Value("${av.kafka.login.module:org.apache.kafka.common.security.scram.ScramLoginModule}")
|
||||
@Value("${kafka.auth_sasl_module}")
|
||||
private String loginModule;
|
||||
@Value("${av.kafka.username}")
|
||||
@Value("${kafka.user}")
|
||||
private String username;
|
||||
@Value("${av.kafka.password}")
|
||||
@Value("${kafka.pass}")
|
||||
private String password;
|
||||
@Value("${av.kafka.sasl.mechanism}")
|
||||
@Value("${kafka.auth_sasl_mech}")
|
||||
private String saslMechanism;
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import java.net.http.HttpClient;
|
|||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
|
@ -34,6 +35,7 @@ import ervu.model.webdav.Server;
|
|||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
|
@ -44,6 +46,8 @@ import org.springframework.retry.annotation.Backoff;
|
|||
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
|
||||
|
|
@ -60,6 +64,10 @@ public class WebDavClient {
|
|||
private String password;
|
||||
@Value("${webdav.bad_servers.cache.expire.seconds:120}")
|
||||
private long cacheExpireSec;
|
||||
@Value("${request.timeout:20}")
|
||||
private long requestTimeout;
|
||||
@Value("${connection.timeout:10}")
|
||||
private long connectionTimeout;
|
||||
|
||||
private List<Server> servers;
|
||||
private LoadingCache<String, String> badServersCache;
|
||||
|
|
@ -167,22 +175,29 @@ public class WebDavClient {
|
|||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password.toCharArray());
|
||||
}
|
||||
}).build();
|
||||
})
|
||||
.connectTimeout(Duration.ofSeconds(connectionTimeout))
|
||||
.build();
|
||||
|
||||
HttpRequest httpRequest = HttpRequest.newBuilder().uri(URI.create(url))
|
||||
.GET().build();
|
||||
.GET()
|
||||
.timeout(Duration.ofSeconds(requestTimeout))
|
||||
.build();
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -196,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,14 +30,16 @@ 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;
|
||||
import ru.micord.ervu.security.esia.service.UlDataService;
|
||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
||||
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;
|
||||
|
|
@ -52,13 +53,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;
|
||||
|
|
@ -68,12 +68,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,
|
||||
|
|
@ -89,7 +91,7 @@ public class EmployeeInfoFileUploadService {
|
|||
formType);
|
||||
String esiaUserId = userIdsPair.getEsiaUserId();
|
||||
String ervuId = userIdsPair.getErvuId();
|
||||
String accessToken = EsiaTokensStore.getAccessToken(esiaUserId);
|
||||
String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId);
|
||||
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
||||
PersonModel personModel = employeeModel.getPerson();
|
||||
|
||||
|
|
@ -105,29 +107,34 @@ public class EmployeeInfoFileUploadService {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -206,6 +213,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,18 @@
|
|||
package ru.micord.ervu.audit.config;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class AuditDisableCondition implements Condition {
|
||||
private static final String AUDIT_ENABLED_PROPERTY_NAME = "audit.kafka.enabled";
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
String propertyValue = context.getEnvironment().getProperty(AUDIT_ENABLED_PROPERTY_NAME);
|
||||
return !Boolean.parseBoolean(propertyValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package ru.micord.ervu.audit.config;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class AuditEnabledCondition implements Condition {
|
||||
private static final String AUDIT_ENABLED_PROPERTY_NAME = "audit.kafka.enabled";
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
Environment env = context.getEnvironment();
|
||||
return Boolean.parseBoolean(env.getProperty(AUDIT_ENABLED_PROPERTY_NAME));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
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.Conditional;
|
||||
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
|
||||
@Conditional(AuditEnabledCondition.class)
|
||||
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:ervu.lkrp.auth.events}")
|
||||
private String authorizationTopic;
|
||||
@Value("${audit.kafka.action.topic:ervu.lkrp.action.events}")
|
||||
private String actionTopic;
|
||||
@Value("${audit.kafka.file.download.topic:ervu.lkrp.import.file}")
|
||||
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,38 @@
|
|||
package ru.micord.ervu.audit.constants;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @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", "Журнал взаимодействия",
|
||||
"/home", "Домашняя страница ЛК РП ЮЛ"
|
||||
);
|
||||
|
||||
private static final Map<Integer, String> downloadTypes = Map.of(
|
||||
1, "Выписка из журнала взаимодействия ЮЛ"
|
||||
);
|
||||
|
||||
|
||||
private AuditConstants() {
|
||||
}
|
||||
|
||||
public static String getRouteDescription(String route) {
|
||||
return routeDescriptions.getOrDefault(route, "Неизвестный маршрут");
|
||||
}
|
||||
|
||||
public static String getDownloadType(int formatRegistry) {
|
||||
return downloadTypes.getOrDefault(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,38 @@
|
|||
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.context.annotation.Conditional;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.audit.config.AuditEnabledCondition;
|
||||
import ru.micord.ervu.audit.service.AuditKafkaPublisher;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@Service
|
||||
@Conditional(AuditEnabledCondition.class)
|
||||
public class BaseAuditKafkaPublisher implements AuditKafkaPublisher {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BaseAuditKafkaPublisher.class);
|
||||
private final KafkaTemplate<String, String> kafkaTemplate;
|
||||
|
||||
public BaseAuditKafkaPublisher(
|
||||
@Qualifier("auditTemplate") KafkaTemplate<String, String> kafkaTemplate) {
|
||||
this.kafkaTemplate = kafkaTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishEvent(String topic, String message) {
|
||||
kafkaTemplate.send(topic, message)
|
||||
.addCallback(
|
||||
result -> {
|
||||
},
|
||||
ex -> LOGGER.error("Failed to send message to topic {}: {}", topic, ex.getMessage(),
|
||||
ex
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
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.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.audit.config.AuditEnabledCondition;
|
||||
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
|
||||
@Conditional(AuditEnabledCondition.class)
|
||||
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:ervu.lkrp.auth.events}")
|
||||
private String authorizationTopic;
|
||||
@Value("${audit.kafka.action.topic:ervu.lkrp.action.events}")
|
||||
private String actionTopic;
|
||||
@Value("${audit.kafka.file.upload.topic:ervu.lkrp.download.request}")
|
||||
private String fileUploadTopic;
|
||||
@Value("${audit.kafka.file.download.topic:ervu.lkrp.import.file}")
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package ru.micord.ervu.audit.service.impl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ervu.model.fileupload.FileInfo;
|
||||
import ervu.model.fileupload.UploadOrgInfo;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.audit.config.AuditDisableCondition;
|
||||
import ru.micord.ervu.audit.model.AuditActionRequest;
|
||||
import ru.micord.ervu.audit.service.AuditService;
|
||||
import ru.micord.ervu.kafka.model.OrgInfo;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@Service
|
||||
@Conditional(AuditDisableCondition.class)
|
||||
public class StubAuditService implements AuditService {
|
||||
@Override
|
||||
public void processActionEvent(HttpServletRequest request,
|
||||
AuditActionRequest auditActionRequest) {}
|
||||
|
||||
@Override
|
||||
public void processAuthEvent(HttpServletRequest request, OrgInfo orgInfo, String prnOid,
|
||||
String status, String eventType) {}
|
||||
|
||||
@Override
|
||||
public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo) {}
|
||||
|
||||
@Override
|
||||
public void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName,
|
||||
int formatRegistry, String status, String s3FileUrl) {}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import java.util.UUID;
|
|||
@EnableKafka
|
||||
public class ReplyingKafkaConfig {
|
||||
|
||||
@Value("${ervu.kafka.bootstrap.servers}")
|
||||
@Value("${kafka.hosts}")
|
||||
private String bootstrapServers;
|
||||
@Value("${ervu.kafka.org.reply.topic}")
|
||||
private String orgReplyTopic;
|
||||
|
|
@ -42,15 +42,15 @@ public class ReplyingKafkaConfig {
|
|||
private long replyTimeout;
|
||||
@Value("${ervu.kafka.excerpt.reply.topic}")
|
||||
private String excerptReplyTopic;
|
||||
@Value("${ervu.kafka.security.protocol}")
|
||||
@Value("${kafka.auth_sec_proto}")
|
||||
private String securityProtocol;
|
||||
@Value("${ervu.kafka.login.module:org.apache.kafka.common.security.scram.ScramLoginModule}")
|
||||
@Value("${kafka.auth_sasl_module}")
|
||||
private String loginModule;
|
||||
@Value("${ervu.kafka.username}")
|
||||
@Value("${kafka.user}")
|
||||
private String username;
|
||||
@Value("${ervu.kafka.password}")
|
||||
@Value("${kafka.pass}")
|
||||
private String password;
|
||||
@Value("${ervu.kafka.sasl.mechanism}")
|
||||
@Value("${kafka.auth_sasl_mech}")
|
||||
private String saslMechanism;
|
||||
|
||||
@Bean("ervuProducerFactory")
|
||||
|
|
@ -71,6 +71,7 @@ public class ReplyingKafkaConfig {
|
|||
Map<String, Object> configProps = new HashMap<>();
|
||||
configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
|
||||
configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId + "-" + UUID.randomUUID());
|
||||
configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
|
||||
configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||
configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||
configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol);
|
||||
|
|
|
|||
|
|
@ -3,23 +3,26 @@ package ru.micord.ervu.kafka.controller;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
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.exception.JsonParsingException;
|
||||
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
|
||||
|
|
@ -30,6 +33,9 @@ public class ErvuKafkaController {
|
|||
@Autowired
|
||||
private ReplyingKafkaService replyingKafkaService;
|
||||
|
||||
@Autowired
|
||||
private AuditService auditService;
|
||||
|
||||
@Autowired
|
||||
private WebDavClient webDavClient;
|
||||
|
||||
|
|
@ -43,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());
|
||||
|
|
@ -59,16 +67,30 @@ public class ErvuKafkaController {
|
|||
ExcerptResponse excerptResponse = objectMapper.readValue(kafkaResponse, ExcerptResponse.class);
|
||||
|
||||
if (!excerptResponse.getSuccess()) {
|
||||
throw new ExcerptException("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 (JsonProcessingException e) {
|
||||
throw new JsonParsingException(e);
|
||||
catch (Exception e) {
|
||||
auditService.processDownloadEvent(request, fileSize, fileName, 1,
|
||||
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
|
||||
);
|
||||
throw new ExcerptException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ package ru.micord.ervu.kafka.exception;
|
|||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class ExcerptException extends RuntimeException {
|
||||
public ExcerptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExcerptException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
|
@ -16,4 +12,7 @@ public class ExcerptException extends RuntimeException {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
package ru.micord.ervu.kafka.service.impl;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||
import org.apache.kafka.common.header.internals.RecordHeader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.kafka.requestreply.ReplyingKafkaTemplate;
|
||||
import org.springframework.kafka.requestreply.RequestReplyFuture;
|
||||
import org.springframework.kafka.support.KafkaHeaders;
|
||||
|
|
@ -19,7 +22,7 @@ import ru.micord.ervu.kafka.service.ReplyingKafkaService;
|
|||
*/
|
||||
@Service
|
||||
public class BaseReplyingKafkaServiceImpl implements ReplyingKafkaService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private final ReplyingKafkaTemplate<String, String, String> replyingKafkaTemplate;
|
||||
|
||||
public BaseReplyingKafkaServiceImpl(
|
||||
|
|
@ -30,16 +33,22 @@ public class BaseReplyingKafkaServiceImpl implements ReplyingKafkaService {
|
|||
public String sendMessageAndGetReply(String requestTopic,
|
||||
String replyTopic,
|
||||
String requestMessage) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
ProducerRecord<String, String> record = new ProducerRecord<>(requestTopic, requestMessage);
|
||||
record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes()));
|
||||
RequestReplyFuture<String, String, String> replyFuture = replyingKafkaTemplate.sendAndReceive(record);
|
||||
|
||||
try {
|
||||
return Optional.ofNullable(replyFuture.get())
|
||||
String result = Optional.ofNullable(replyFuture.get())
|
||||
.map(ConsumerRecord::value)
|
||||
.orElseThrow(() -> new KafkaMessageException("Kafka return result is null."));
|
||||
LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms",
|
||||
Thread.currentThread().getId(), System.currentTimeMillis() - startTime);
|
||||
return result;
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms",
|
||||
Thread.currentThread().getId(), System.currentTimeMillis() - startTime);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
package ru.micord.ervu.security.esia.token;
|
||||
package ru.micord.ervu.security.esia;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import ru.micord.ervu.security.esia.model.ExpiringState;
|
||||
import ru.micord.ervu.security.esia.model.ExpiringToken;
|
||||
|
||||
import ru.cg.webbpm.modules.core.runtime.api.LocalizedException;
|
||||
import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
public class EsiaTokensStore {
|
||||
public class EsiaAuthInfoStore {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private static final Map<String, ExpiringToken> ACCESS_TOKENS_MAP = new ConcurrentHashMap<>();
|
||||
private static final Map<String, ExpiringToken> REFRESH_TOKENS_MAP = new ConcurrentHashMap<>();
|
||||
private static final Map<String, List<ExpiringState>> PRNS_UUID_STATE_MAP = new ConcurrentHashMap<>();
|
||||
private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor(
|
||||
"messages/common_errors_messages");
|
||||
|
||||
public static void addAccessToken(String prnOid, String token, long expiresIn) {
|
||||
if (token != null) {
|
||||
|
|
@ -77,4 +87,49 @@ public class EsiaTokensStore {
|
|||
REFRESH_TOKENS_MAP.remove(prnOid);
|
||||
}
|
||||
|
||||
public static void addState(String prnsUUID, String state, long expiresIn, long attemptsCount) {
|
||||
long expiryTime = System.currentTimeMillis() + expiresIn * 1000L;
|
||||
ExpiringState newState = new ExpiringState(state, expiryTime);
|
||||
PRNS_UUID_STATE_MAP.compute(prnsUUID, (key, states) -> {
|
||||
if (states == null) {
|
||||
states = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
else {
|
||||
states.removeIf(ExpiringState::isExpired);
|
||||
}
|
||||
if (states.size() >= attemptsCount) {
|
||||
throw new LocalizedException("login_attempts_exceeded", MESSAGE_SOURCE);
|
||||
}
|
||||
states.add(newState);
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean containsState(String prnsUUID, String state) {
|
||||
List<ExpiringState> states = PRNS_UUID_STATE_MAP.get(prnsUUID);
|
||||
if (states == null) {
|
||||
return false;
|
||||
}
|
||||
long currentTime = System.currentTimeMillis();
|
||||
states.removeIf(expiringState -> expiringState.getExpiryTime() < currentTime);
|
||||
for (ExpiringState expiringState : states) {
|
||||
if (expiringState.getState().equals(state)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void removeState(String prnsUUID) {
|
||||
PRNS_UUID_STATE_MAP.remove(prnsUUID);
|
||||
}
|
||||
|
||||
public static void removeExpiredState() {
|
||||
for (String key : PRNS_UUID_STATE_MAP.keySet()) {
|
||||
PRNS_UUID_STATE_MAP.computeIfPresent(key, (k, states) -> {
|
||||
states.removeIf(ExpiringState::isExpired);
|
||||
return states.isEmpty() ? null : states;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,10 +38,10 @@ public class EsiaConfig {
|
|||
@Value("${esia.client.cert.hash}")
|
||||
private String clientCertHash;
|
||||
|
||||
@Value("${esia.request.timeout:60}")
|
||||
@Value("${request.timeout:20}")
|
||||
private long requestTimeout;
|
||||
|
||||
@Value("${esia.connection.timeout:30}")
|
||||
@Value("${connection.timeout:10}")
|
||||
private long connectionTimeout;
|
||||
|
||||
@Value("${esia.logout.url:idp/ext/Logout}")
|
||||
|
|
@ -62,6 +62,15 @@ public class EsiaConfig {
|
|||
@Value("${esia.issuer.url}")
|
||||
private String esiaIssuerUrl;
|
||||
|
||||
@Value("${esia.marker.ver}")
|
||||
private String esiaMarkerVer;
|
||||
|
||||
@Value("${esia.state.cookie.life.time:300}")
|
||||
private long esiaStateCookieLifeTime;
|
||||
|
||||
@Value("${esia.login.attempts.count:5}")
|
||||
private long esiaLoginAttemptsCount;
|
||||
|
||||
public String getEsiaOrgScopes() {
|
||||
String[] scopeItems = esiaOrgScopes.split(",");
|
||||
return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new));
|
||||
|
|
@ -125,4 +134,16 @@ public class EsiaConfig {
|
|||
public String getEsiaIssuerUrl() {
|
||||
return esiaIssuerUrl;
|
||||
}
|
||||
|
||||
public String getEsiaMarkerVer() {
|
||||
return esiaMarkerVer;
|
||||
}
|
||||
|
||||
public long getEsiaStateCookieLifeTime() {
|
||||
return esiaStateCookieLifeTime;
|
||||
}
|
||||
|
||||
public long getEsiaLoginAttemptsCount() {
|
||||
return esiaLoginAttemptsCount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -27,15 +25,15 @@ public class EsiaController {
|
|||
private EsiaDataService esiaDataService;
|
||||
|
||||
@GetMapping(value = "/esia/url")
|
||||
public String getEsiaUrl() {
|
||||
return esiaAuthService.generateAuthCodeUrl();
|
||||
public String getEsiaUrl(HttpServletResponse response, HttpServletRequest request) {
|
||||
return esiaAuthService.generateAuthCodeUrl(response, request);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/esia/auth")
|
||||
public void esiaAuth(@RequestParam(value = "code", required = false) String code,
|
||||
@RequestParam(value = "error", required = false) String error, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
esiaAuthService.authEsiaTokensByCode(code, error, request, response);
|
||||
public void esiaAuth(@RequestParam String code,
|
||||
@RequestParam String state,
|
||||
HttpServletResponse response, HttpServletRequest request) {
|
||||
esiaAuthService.authEsiaTokensByCode(code, state, response, request);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/esia/refresh")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package ru.micord.ervu.security.esia.model;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
public class ExpiringState {
|
||||
private String state;
|
||||
private long expiryTime;
|
||||
|
||||
public ExpiringState(String state, long expiryTime) {
|
||||
this.state = state;
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public long getExpiryTime() {
|
||||
return expiryTime;
|
||||
}
|
||||
|
||||
public void setExpiryTime(long expiryTime) {
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return System.currentTimeMillis() > expiryTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.micord.ervu.security.esia.token;
|
||||
package ru.micord.ervu.security.esia.model;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
|
|
@ -28,7 +28,7 @@ public class ExpiringToken {
|
|||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
boolean isExpired() {
|
||||
public boolean isExpired() {
|
||||
return System.currentTimeMillis() > expiryTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package ru.micord.ervu.security.esia.service;
|
||||
|
||||
import net.javacrumbs.shedlock.core.SchedulerLock;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
@Service
|
||||
public class EsiaAuthInfoClearShedulerService {
|
||||
@Scheduled(cron = "${esia.auth.info.clear.cron:0 0 */1 * * *}")
|
||||
@SchedulerLock(name = "clearAuthInfo")
|
||||
@Transactional
|
||||
public void run() {
|
||||
EsiaAuthInfoStore.removeExpiredRefreshToken();
|
||||
EsiaAuthInfoStore.removeExpiredAccessToken();
|
||||
EsiaAuthInfoStore.removeExpiredState();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,17 @@ import java.net.http.HttpRequest;
|
|||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
|
@ -23,10 +28,20 @@ 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.http.ResponseCookie;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.micord.ervu.audit.constants.AuditConstants;
|
||||
import ru.micord.ervu.audit.service.AuditService;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
import ru.micord.ervu.security.esia.exception.EsiaException;
|
||||
import ru.micord.ervu.security.esia.model.*;
|
||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import ru.micord.ervu.security.esia.model.EmployeeModel;
|
||||
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
|
||||
import ru.micord.ervu.security.esia.model.EsiaHeader;
|
||||
import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
|
||||
import ru.micord.ervu.security.esia.model.FormUrlencoded;
|
||||
import ru.micord.ervu.security.esia.model.OrganizationModel;
|
||||
import ru.micord.ervu.security.esia.model.SignResponse;
|
||||
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
|
|
@ -50,8 +65,6 @@ 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;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
|
|
@ -60,6 +73,7 @@ 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");
|
||||
private static final String PRNS_UUID = "prns_uuid";
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@Autowired
|
||||
|
|
@ -74,18 +88,26 @@ public class EsiaAuthService {
|
|||
private OkopfService okopfService;
|
||||
@Autowired
|
||||
private SecurityHelper securityHelper;
|
||||
@Autowired
|
||||
private AuditService auditService;
|
||||
@Value("${ervu.kafka.org.reply.topic}")
|
||||
private String requestReplyTopic;
|
||||
|
||||
@Value("${ervu.kafka.org.request.topic}")
|
||||
private String requestTopic;
|
||||
|
||||
public String generateAuthCodeUrl() {
|
||||
public String generateAuthCodeUrl(HttpServletResponse response, HttpServletRequest request) {
|
||||
try {
|
||||
String clientId = esiaConfig.getClientId();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||
ZonedDateTime dt = ZonedDateTime.now();
|
||||
String timestamp = dt.format(formatter);
|
||||
String state = UUID.randomUUID().toString();
|
||||
String prnsUUID = UUID.randomUUID().toString();
|
||||
Cookie oldPrnsCookie = WebUtils.getCookie(request, PRNS_UUID);
|
||||
if (oldPrnsCookie != null) {
|
||||
prnsUUID = oldPrnsCookie.getValue();
|
||||
}
|
||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||
String redirectUrlEncoded = redirectUrl.replaceAll(":", "%3A")
|
||||
.replaceAll("/", "%2F");
|
||||
|
|
@ -97,13 +119,15 @@ public class EsiaAuthService {
|
|||
parameters.put("scope", scope);
|
||||
parameters.put("scope_org", scopeOrg);
|
||||
parameters.put("timestamp", timestamp);
|
||||
parameters.put("state", "%s");
|
||||
parameters.put("state", state);
|
||||
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
||||
|
||||
SignResponse signResponse = signMap(parameters);
|
||||
String state = signResponse.getState();
|
||||
String clientSecret = signResponse.getSignature();
|
||||
|
||||
String clientSecret = signMap(parameters);
|
||||
EsiaAuthInfoStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime(), esiaConfig.getEsiaLoginAttemptsCount());
|
||||
ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/")
|
||||
.maxAge(esiaConfig.getEsiaStateCookieLifeTime())
|
||||
.build();
|
||||
securityHelper.addResponseCookie(response, prnsCookie);
|
||||
String responseType = "code";
|
||||
|
||||
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaCodeUrl();
|
||||
|
|
@ -154,21 +178,22 @@ public class EsiaAuthService {
|
|||
return uriBuilder.toString();
|
||||
}
|
||||
|
||||
public void authEsiaTokensByCode(String esiaAuthCode, String error,
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
if (error != null && !error.equals("null")) {
|
||||
throw new EsiaException(error);
|
||||
}
|
||||
public void authEsiaTokensByCode(String esiaAuthCode, String state, HttpServletResponse response, HttpServletRequest request) {
|
||||
String esiaAccessTokenStr = null;
|
||||
String prnOid = null;
|
||||
Long expiresIn = null;
|
||||
boolean hasRole = false;
|
||||
long timeSignSecret = 0, timeRequestAccessToken = 0, timeVerifySecret = 0;
|
||||
String verifyStateResult = verifyStateFromCookie(request, state, response);
|
||||
if (verifyStateResult != null) {
|
||||
throw new EsiaException(verifyStateResult);
|
||||
}
|
||||
try {
|
||||
String clientId = esiaConfig.getClientId();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||
ZonedDateTime dt = ZonedDateTime.now();
|
||||
String timestamp = dt.format(formatter);
|
||||
String newState = UUID.randomUUID().toString();
|
||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||
String scope = esiaConfig.getEsiaScopes();
|
||||
String scopeOrg = esiaConfig.getEsiaOrgScopes();
|
||||
|
|
@ -178,22 +203,20 @@ public class EsiaAuthService {
|
|||
parameters.put("scope", scope);
|
||||
parameters.put("scope_org", scopeOrg);
|
||||
parameters.put("timestamp", timestamp);
|
||||
parameters.put("state", "%s");
|
||||
parameters.put("state", newState);
|
||||
parameters.put("redirect_uri", redirectUrl);
|
||||
parameters.put("code", esiaAuthCode);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
SignResponse signResponse = signMap(parameters);
|
||||
String clientSecret = signMap(parameters);
|
||||
timeSignSecret = System.currentTimeMillis() - startTime;
|
||||
String state = signResponse.getState();
|
||||
String clientSecret = signResponse.getSignature();
|
||||
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaTokenUrl();
|
||||
String postBody = new FormUrlencoded()
|
||||
.setParameter("client_id", clientId)
|
||||
.setParameter("code", esiaAuthCode)
|
||||
.setParameter("grant_type", "authorization_code")
|
||||
.setParameter("client_secret", clientSecret)
|
||||
.setParameter("state", state)
|
||||
.setParameter("state", newState)
|
||||
.setParameter("redirect_uri", redirectUrl)
|
||||
.setParameter("scope", scope)
|
||||
.setParameter("scope_org", scopeOrg)
|
||||
|
|
@ -222,6 +245,9 @@ public class EsiaAuthService {
|
|||
tokenResponse != null ? tokenResponse.getErrorDescription() : "response is empty";
|
||||
throw new IllegalStateException("Esia response error. " + errMsg);
|
||||
}
|
||||
if (!tokenResponse.getState().equals(newState)) {
|
||||
throw new EsiaException("Token invalid. State from request not equals with state from response.");
|
||||
}
|
||||
esiaAccessTokenStr = tokenResponse.getAccessToken();
|
||||
String esiaRefreshTokenStr = tokenResponse.getRefreshToken();
|
||||
startTime = System.currentTimeMillis();
|
||||
|
|
@ -233,29 +259,39 @@ public class EsiaAuthService {
|
|||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
||||
prnOid = esiaAccessToken.getSbjId();
|
||||
expiresIn = tokenResponse.getExpiresIn();
|
||||
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
EsiaTokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
||||
EsiaAuthInfoStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
EsiaAuthInfoStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
finally {
|
||||
LOGGER.info("Thread {}: SignSecret: {}ms RequestAccessToken: {}ms VerifySecret: {}ms",
|
||||
LOGGER.info("Thread {} - SignSecret: {} ms RequestAccessToken: {} ms VerifySecret: {} ms",
|
||||
Thread.currentThread().getId(), timeSignSecret, timeRequestAccessToken, timeVerifySecret);
|
||||
}
|
||||
String ervuId = null;
|
||||
OrgInfo orgInfo = null;
|
||||
String status = null, ervuId = null;
|
||||
try {
|
||||
orgInfo = getOrgInfo(esiaAccessTokenStr);
|
||||
hasRole = ulDataService.checkRole(esiaAccessTokenStr);
|
||||
ervuId = getErvuId(esiaAccessTokenStr, prnOid);
|
||||
ervuId = getErvuId(prnOid, orgInfo);
|
||||
if (!hasRole) {
|
||||
LOGGER.error("The user with id = " + prnOid + " does not have the required role");
|
||||
throw new LocalizedException("access_denied", MESSAGE_SOURCE);
|
||||
}
|
||||
status = AuditConstants.SUCCESS_STATUS_TYPE;
|
||||
}
|
||||
catch (EsiaException | JsonProcessingException e) {
|
||||
throw new EsiaException(e);
|
||||
catch (Exception e) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -267,6 +303,7 @@ public class EsiaAuthService {
|
|||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||
ZonedDateTime dt = ZonedDateTime.now();
|
||||
String timestamp = dt.format(formatter);
|
||||
String state = UUID.randomUUID().toString();
|
||||
String redirectUrl = esiaConfig.getRedirectUrl();
|
||||
String scope = esiaConfig.getEsiaScopes();
|
||||
String scopeOrg = esiaConfig.getEsiaOrgScopes();
|
||||
|
|
@ -276,13 +313,11 @@ public class EsiaAuthService {
|
|||
parameters.put("scope", scope);
|
||||
parameters.put("scope_org", scopeOrg);
|
||||
parameters.put("timestamp", timestamp);
|
||||
parameters.put("state", "%s");
|
||||
parameters.put("state", state);
|
||||
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
|
||||
parameters.put("refresh_token", refreshToken);
|
||||
|
||||
SignResponse signResponse = signMap(parameters);
|
||||
String state = signResponse.getState();
|
||||
String clientSecret = signResponse.getSignature();
|
||||
String clientSecret = signMap(parameters);
|
||||
String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaTokenUrl();
|
||||
String postBody = new FormUrlencoded()
|
||||
.setParameter("client_id", clientId)
|
||||
|
|
@ -314,18 +349,22 @@ public class EsiaAuthService {
|
|||
tokenResponse != null ? tokenResponse.getErrorDescription() : "response is empty";
|
||||
throw new IllegalStateException("Esia response error. " + errMsg);
|
||||
}
|
||||
if (!tokenResponse.getState().equals(state)) {
|
||||
throw new EsiaException("Token invalid. State from request not equals with state from response.");
|
||||
}
|
||||
String esiaAccessTokenStr = tokenResponse.getAccessToken();
|
||||
String esiaNewRefreshToken = tokenResponse.getRefreshToken();
|
||||
String verifyResult = verifyToken(esiaAccessTokenStr);
|
||||
if (verifyResult != null) {
|
||||
throw new EsiaException(verifyResult);
|
||||
}
|
||||
String esiaNewRefreshToken = tokenResponse.getRefreshToken();
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
||||
String prnOid = esiaAccessToken.getSbjId();
|
||||
Long expiresIn = tokenResponse.getExpiresIn();
|
||||
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
EsiaTokensStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn);
|
||||
String ervuId = getErvuId(esiaAccessTokenStr, prnOid);
|
||||
EsiaAuthInfoStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
EsiaAuthInfoStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn);
|
||||
OrgInfo orgInfo = getOrgInfo(esiaAccessTokenStr);
|
||||
String ervuId = getErvuId(prnOid, orgInfo);
|
||||
createTokenAndAddCookie(response, esiaAccessToken.getSbjId(), ervuId, true, expiresIn);
|
||||
}
|
||||
catch (EsiaException | IOException | InterruptedException e) {
|
||||
|
|
@ -333,7 +372,7 @@ public class EsiaAuthService {
|
|||
}
|
||||
}
|
||||
|
||||
private SignResponse signMap(Map<String, String> paramsToSign) {
|
||||
private String signMap(Map<String, String> paramsToSign) {
|
||||
try {
|
||||
StringBuilder toSign = new StringBuilder();
|
||||
for (String s : paramsToSign.values()) {
|
||||
|
|
@ -345,13 +384,14 @@ public class EsiaAuthService {
|
|||
.uri(URI.create(esiaConfig.getSignUrl()))
|
||||
.header("Content-Type", "text/plain")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody, StandardCharsets.UTF_8))
|
||||
.timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
|
||||
.build();
|
||||
HttpResponse<String> response = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
|
||||
.build()
|
||||
.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
errorHandler(response);
|
||||
return objectMapper.readValue(response.body(), SignResponse.class);
|
||||
return response.body();
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
|
@ -366,28 +406,56 @@ public class EsiaAuthService {
|
|||
}
|
||||
|
||||
public String logout(HttpServletRequest request, HttpServletResponse response) {
|
||||
OrgInfo orgInfo = null;
|
||||
String userId = null;
|
||||
String status = null;
|
||||
try {
|
||||
userId = jwtTokenService.getUserAccountId(request);
|
||||
String accessToken = EsiaAuthInfoStore.getAccessToken(userId);
|
||||
orgInfo = getOrgInfo(accessToken);
|
||||
securityHelper.clearAccessCookies(response);
|
||||
String userId = jwtTokenService.getUserAccountId(request);
|
||||
EsiaTokensStore.removeAccessToken(userId);
|
||||
EsiaTokensStore.removeRefreshToken(userId);
|
||||
EsiaAuthInfoStore.removeAccessToken(userId);
|
||||
EsiaAuthInfoStore.removeRefreshToken(userId);
|
||||
String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl();
|
||||
String redirectUrl = esiaConfig.getLogoutRedirectUrl();
|
||||
URL url = new URL(logoutUrl);
|
||||
Map<String, String> params = mapOf(
|
||||
"client_id", esiaConfig.getClientId(),
|
||||
"redirect_url", redirectUrl);
|
||||
"redirect_url", redirectUrl
|
||||
);
|
||||
status = AuditConstants.SUCCESS_STATUS_TYPE;
|
||||
return buildUrl(url, params);
|
||||
}
|
||||
catch (Exception e) {
|
||||
status = AuditConstants.FAILURE_STATUS_TYPE;
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
finally {
|
||||
if (orgInfo != null) {
|
||||
auditService.processAuthEvent(request, orgInfo, userId, status,
|
||||
AuditConstants.LOGOUT_EVENT_TYPE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getErvuId(String accessToken, String prnOid) throws JsonProcessingException {
|
||||
long timeRequestPersonDataOrg = 0, timeRequestPersonDataEmployee = 0, timeRequestPersonDataChief = 0, timeRequestIdERVU = 0;
|
||||
public String getErvuId(String prnOid, OrgInfo orgInfo) throws JsonProcessingException {
|
||||
orgInfo.setOrgTypeName(okopfService.findTitleByLeg(orgInfo.getOrgTypeLeg()));
|
||||
String kafkaResponse = replyingKafkaService.sendMessageAndGetReply(requestTopic,
|
||||
requestReplyTopic, objectMapper.writeValueAsString(orgInfo)
|
||||
);
|
||||
ErvuOrgResponse ervuOrgResponse = objectMapper.readValue(kafkaResponse, ErvuOrgResponse.class);
|
||||
String ervuId = ervuOrgResponse.getData().getErvuId();
|
||||
if (!StringUtils.hasText(ervuId)) {
|
||||
throw new EsiaException("No ervuId for prnOid = " + prnOid);
|
||||
}
|
||||
return ervuId;
|
||||
}
|
||||
|
||||
private OrgInfo getOrgInfo(String accessToken) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
long timeRequestPersonDataOrg = 0, timeRequestPersonDataEmployee = 0, timeRequestPersonDataChief = 0;
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
|
||||
timeRequestPersonDataOrg = System.currentTimeMillis() - startTime;
|
||||
startTime = System.currentTimeMillis();
|
||||
|
|
@ -396,24 +464,12 @@ public class EsiaAuthService {
|
|||
startTime = System.currentTimeMillis();
|
||||
EmployeeModel chiefModel = ulDataService.getChiefEmployeeModel(accessToken);
|
||||
timeRequestPersonDataChief = System.currentTimeMillis() - startTime;
|
||||
OrgInfo orgInfo = copyToOrgInfo(organizationModel, employeeModel, chiefModel);
|
||||
orgInfo.setOrgTypeName(okopfService.findTitleByLeg(orgInfo.getOrgTypeLeg()));
|
||||
startTime = System.currentTimeMillis();
|
||||
String kafkaResponse = replyingKafkaService.sendMessageAndGetReply(requestTopic,
|
||||
requestReplyTopic, objectMapper.writeValueAsString(orgInfo)
|
||||
);
|
||||
timeRequestIdERVU = System.currentTimeMillis() - startTime;
|
||||
ErvuOrgResponse ervuOrgResponse = objectMapper.readValue(kafkaResponse, ErvuOrgResponse.class);
|
||||
String ervuId = ervuOrgResponse.getData().getErvuId();
|
||||
|
||||
if (!StringUtils.hasText(ervuId)) {
|
||||
throw new EsiaException("No ervuId for prnOid = " + prnOid);
|
||||
}
|
||||
return ervuId;
|
||||
return copyToOrgInfo(organizationModel, employeeModel, chiefModel);
|
||||
}
|
||||
finally {
|
||||
LOGGER.info("Thread {}: RequestPersonDataOrg: {}ms RequestPersonDataEmployee: {}ms RequestPersonDataChief: {}ms RequestIdERVU: {}ms",
|
||||
Thread.currentThread().getId(), timeRequestPersonDataOrg, timeRequestPersonDataEmployee, timeRequestPersonDataChief, timeRequestIdERVU);
|
||||
LOGGER.info("Thread {} - RequestPersonDataOrg: {} ms RequestPersonDataEmployee: {} ms RequestPersonDataChief: {} ms",
|
||||
Thread.currentThread().getId(), timeRequestPersonDataOrg, timeRequestPersonDataEmployee, timeRequestPersonDataChief
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -473,13 +529,6 @@ public class EsiaAuthService {
|
|||
return employee;
|
||||
}
|
||||
|
||||
private String getMessageId(Exception exception) {
|
||||
return Integer.toUnsignedString(Objects
|
||||
.hashCode(getCurrentUsername()), 36)
|
||||
+ "-"
|
||||
+ Integer.toUnsignedString(exception.hashCode(), 36);
|
||||
}
|
||||
|
||||
private void createTokenAndAddCookie(HttpServletResponse response, String userId, String ervuId,
|
||||
Boolean hasRole, Long expiresIn) {
|
||||
Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId, hasRole);
|
||||
|
|
@ -499,6 +548,9 @@ public class EsiaAuthService {
|
|||
if (!esiaHeader.getSbt().equals("access")) {
|
||||
return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid";
|
||||
}
|
||||
if (!esiaHeader.getVer().equals(esiaConfig.getEsiaMarkerVer())) {
|
||||
return "Token invalid. Token ver: " + esiaHeader.getVer() + " invalid";
|
||||
}
|
||||
if (!esiaHeader.getTyp().equals("JWT")) {
|
||||
return "Token invalid. Token type: " + esiaHeader.getTyp() + " invalid";
|
||||
}
|
||||
|
|
@ -508,17 +560,19 @@ public class EsiaAuthService {
|
|||
if (!esiaAccessToken.getIss().equals(esiaConfig.getEsiaIssuerUrl())) {
|
||||
return "Token invalid. Token issuer:" + esiaAccessToken.getIss() + " invalid";
|
||||
}
|
||||
//TODO SUPPORT-8750
|
||||
// LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()),
|
||||
// ZoneId.systemDefault()
|
||||
// );
|
||||
// LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()),
|
||||
// ZoneId.systemDefault()
|
||||
// );
|
||||
// LocalDateTime currentTime = LocalDateTime.now();
|
||||
// if (!currentTime.isAfter(iatTime) || !expTime.isAfter(iatTime)) {
|
||||
// return "Token invalid. Token expired";
|
||||
// }
|
||||
LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()),
|
||||
ZoneId.systemDefault()
|
||||
);
|
||||
LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()),
|
||||
ZoneId.systemDefault()
|
||||
);
|
||||
LocalDateTime currentTime = LocalDateTime.now();
|
||||
if (currentTime.getNano() > 0) {
|
||||
currentTime = currentTime.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS);
|
||||
}
|
||||
if (currentTime.isBefore(iatTime) || expTime.isBefore(iatTime) || currentTime.isAfter(expTime)) {
|
||||
return "Token invalid. Token expired, current: " + currentTime + " iat: " + iatTime + " exp: " + expTime;
|
||||
}
|
||||
HttpResponse<String> response = signVerify(accessToken);
|
||||
if (response.statusCode() != 200) {
|
||||
if (response.statusCode() == 401) {
|
||||
|
|
@ -535,6 +589,7 @@ public class EsiaAuthService {
|
|||
.uri(URI.create(esiaConfig.getSignVerifyUrl()))
|
||||
.header("Content-Type", "text/plain")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(accessToken, StandardCharsets.UTF_8))
|
||||
.timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
|
||||
.build();
|
||||
return HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
|
||||
|
|
@ -545,4 +600,18 @@ public class EsiaAuthService {
|
|||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String verifyStateFromCookie(HttpServletRequest request, String state, HttpServletResponse response) {
|
||||
Cookie cookie = WebUtils.getCookie(request, PRNS_UUID);
|
||||
if (cookie == null) {
|
||||
return "State invalid. Cookie not found";
|
||||
}
|
||||
String prnsUUID = cookie.getValue();
|
||||
if (!EsiaAuthInfoStore.containsState(prnsUUID, state)) {
|
||||
return "State invalid. State from ESIA not equals with state before";
|
||||
}
|
||||
EsiaAuthInfoStore.removeState(prnsUUID);
|
||||
securityHelper.clearCookie(response, PRNS_UUID, "/");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
package ru.micord.ervu.security.esia.token;
|
||||
|
||||
import net.javacrumbs.shedlock.core.SchedulerLock;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
@Service
|
||||
public class TokensClearShedulerService {
|
||||
@Scheduled(cron = "${esia.token.clear.cron:0 0 */1 * * *}")
|
||||
@SchedulerLock(name = "clearToken")
|
||||
@Transactional
|
||||
public void load() {
|
||||
EsiaTokensStore.removeExpiredRefreshToken();
|
||||
EsiaTokensStore.removeExpiredAccessToken();
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ public final class SecurityHelper {
|
|||
addResponseCookie(response, emptyAuthMarker);
|
||||
}
|
||||
|
||||
private void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) {
|
||||
public void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) {
|
||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||
}
|
||||
|
||||
|
|
@ -86,4 +86,10 @@ public final class SecurityHelper {
|
|||
.secure(accessCookieSecure)
|
||||
.sameSite(accessCookieSameSite);
|
||||
}
|
||||
|
||||
public void clearCookie(HttpServletResponse response, String name, String path) {
|
||||
ResponseCookie emptyCookie = createCookie(name, null, path)
|
||||
.maxAge(0).build();
|
||||
addResponseCookie(response, emptyCookie);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import ru.micord.ervu.security.exception.UnauthorizedException;
|
||||
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
|
||||
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||
|
||||
import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils;
|
||||
|
|
@ -68,7 +68,7 @@ public class JwtTokenService {
|
|||
return false;
|
||||
}
|
||||
String[] ids = token.getUserAccountId().split(":");
|
||||
return EsiaTokensStore.validateAccessToken(ids[0]);
|
||||
return EsiaAuthInfoStore.validateAccessToken(ids[0]);
|
||||
}
|
||||
|
||||
public Token getToken(String token) {
|
||||
|
|
@ -81,11 +81,11 @@ public class JwtTokenService {
|
|||
}
|
||||
|
||||
public String getAccessToken(HttpServletRequest request) {
|
||||
return EsiaTokensStore.getAccessToken(getUserAccountId(request));
|
||||
return EsiaAuthInfoStore.getAccessToken(getUserAccountId(request));
|
||||
}
|
||||
|
||||
public String getRefreshToken(HttpServletRequest request) {
|
||||
return EsiaTokensStore.getRefreshToken(getUserAccountId(request));
|
||||
return EsiaAuthInfoStore.getRefreshToken(getUserAccountId(request));
|
||||
}
|
||||
|
||||
public String getUserAccountId(HttpServletRequest request) {
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
kafka_reply_timeout=Превышено время ожидания ответа от сервера. Попробуйте повторить запрос позже или обратитесь к системному администратору
|
||||
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА
|
||||
kafka_reply_timeout=Превышено время ожидания ответа от сервера.
|
||||
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА
|
||||
login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию.
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
kafka_reply_timeout=Превышено время ожидания ответа от сервера. Попробуйте повторить запрос позже или обратитесь к системному администратору
|
||||
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА
|
||||
kafka_reply_timeout=Превышено время ожидания ответа от сервера.
|
||||
access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА
|
||||
login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию.
|
||||
33
config.md
|
|
@ -789,6 +789,15 @@ JBPM использует 3 корневых категории логирова
|
|||
справочник окопф с задержкой. По умолчанию задержка по времени 30000 ms
|
||||
- `ESNSI_OKOPF_RETRY_MAX_ATTEMPTS_LOAD` - настройка, которая указывет на максимальное кол-во попыток
|
||||
повторно загрузить справочник окопф. По умолчанию 3 попытки
|
||||
|
||||
#### Общие параметры Kafka
|
||||
|
||||
- `KAFKA_HOSTS` - список пар хост:порт, использующихся для установки первоначального соединения с кластером Kafka
|
||||
- `KAFKA_AUTH_SEC_PROTO` - протокол, используемый для взаимодействия с брокерами
|
||||
- `KAFKA_AUTH_SASL_MECH` - механизм SASL, используемый для клиентских подключений
|
||||
- `KAFKA_AUTH_SASL_MODULE` - имя класса для входа в систему для SASL-соединений в формате, используемом конфигурационными файлами JAAS
|
||||
- `KAFKA_USER` - пользователь для подключения к Kafka
|
||||
- `KAFKA_PASS` - пароль для подключения к Kafka
|
||||
|
||||
#### Взаимодействие с WebDav
|
||||
|
||||
|
|
@ -803,22 +812,12 @@ JBPM использует 3 корневых категории логирова
|
|||
- `WEBDAV_RETRY_DELAY` - количество попыток по операциям с файлами WebDav
|
||||
- `FILE_WEBDAV_LIFETIME_SECONDS` - время жизни файла в WebDav (секунды)
|
||||
- `FILE_WEBDAV_EXTENSIONS` - список расширений файлов, удаляемых с WebDav
|
||||
- `AV_KAFKA_BOOTSTRAP_SERVERS` - список пар хост:порт, использующихся для установки первоначального соединения с кластером Kafka
|
||||
- `AV_KAFKA_SECURITY_PROTOCOL` - протокол, используемый для взаимодействия с брокерами
|
||||
- `AV_KAFKA_SASL_MECHANISM` - механизм SASL, используемый для клиентских подключений
|
||||
- `AV_KAFKA_USERNAME` - пользователь для подключения к Kafka
|
||||
- `AV_KAFKA_PASSWORD` - пароль для подключения к Kafka
|
||||
- `AV_KAFKA_GROUP_ID` - идентификатор группы потребителей, который отвечает за создание группы для объединения нескольких потребителей
|
||||
- `AV_KAFKA_MESSAGE_TOPIC_NAME` - топик для записи данных по файлу для перекладчика.
|
||||
- `AV_KAFKA_DOWNLOAD_RESPONSE` - топик для чтения статусов файла, полученных от перекладчика.
|
||||
|
||||
#### Взаимодействие с Kafka ERVU
|
||||
|
||||
- `ERVU_KAFKA_BOOTSTRAP_SERVERS` - список пар хост:порт, использующихся для установки первоначального соединения с кластером Kafka
|
||||
- `ERVU_KAFKA_SECURITY_PROTOCOL` - протокол, используемый для взаимодействия с брокерами
|
||||
- `ERVU_KAFKA_SASL_MECHANISM` - механизм SASL, используемый для клиентских подключений
|
||||
- `ERVU_KAFKA_USERNAME` - пользователь для подключения к Kafka
|
||||
- `ERVU_KAFKA_PASSWORD` - пароль для подключения к Kafka
|
||||
- `ERVU_KAFKA_GROUP_ID` - идентификатор группы потребителей, который отвечает за создание группы для объединения нескольких потребителей
|
||||
- `ERVU_KAFKA_REPLY_TIMEOUT` - определяет, сколько времени Kafka будет ожидать ответа от потребителя после отправки сообщения. Значение задается в секундах
|
||||
- `ERVU_KAFKA_ORG_REQUEST_TOPIC` - топик для записи данных об организации, для получения id организации из ЕРВУ.
|
||||
|
|
@ -828,3 +827,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` - флажок для включения записи аудита в кафку
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
ARG BUILDER_IMAGE=registry.altlinux.org/basealt/altsp:c10f1
|
||||
ARG BACKEND_IMAGE=repo.micord.ru/alt/alt-tomcat:c10f1-9.0.59-20240903
|
||||
ARG FRONTEND_IMAGE=nginx:1.24-alpine-slim
|
||||
ARG FRONTEND_IMAGE=nginx:1.26.2-alpine-slim
|
||||
|
||||
FROM $BUILDER_IMAGE AS builder
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
ARG BUILDER_IMAGE=registry.altlinux.org/basealt/altsp:c10f1
|
||||
ARG RUNTIME_IMAGE=nginx:1.24-alpine-slim
|
||||
ARG RUNTIME_IMAGE=nginx:1.26.2-alpine-slim
|
||||
|
||||
FROM $BUILDER_IMAGE AS builder
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +1,21 @@
|
|||
AV_KAFKA_BOOTSTRAP_SERVERS=local-kafka:9094
|
||||
#AV_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
AV_KAFKA_SECURITY_PROTOCOL=PLAINTEXT
|
||||
#AV_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
|
||||
AV_KAFKA_SASL_MECHANISM=PLAIN
|
||||
AV_KAFKA_USERNAME=user2
|
||||
AV_KAFKA_PASSWORD=Blfi9d2OFG
|
||||
KAFKA_HOSTS=local-kafka:9094
|
||||
#KAFKA_AUTH_SEC_PROTO=SASL_PLAINTEXT
|
||||
KAFKA_AUTH_SEC_PROTO=PLAINTEXT
|
||||
#KAFKA_AUTH_SASL_MECH=SCRAM-SHA-256
|
||||
KAFKA_AUTH_SASL_MECH=PLAIN
|
||||
#KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule
|
||||
KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule
|
||||
KAFKA_USER=user1
|
||||
KAFKA_PASS=Blfi9d2OFG
|
||||
|
||||
AV_KAFKA_GROUP_ID=local-ervu-lkrp-av1
|
||||
AV_KAFKA_TOPIC_NAME=file-to-upload
|
||||
AV_KAFKA_STATUS_TOPIC_NAME=ervu.lkrp.av-fileupload-status
|
||||
AV_KAFKA_LOGIN_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule
|
||||
|
||||
ERVU_KAFKA_BOOTSTRAP_SERVERS=local-kafka:9094
|
||||
#ERVU_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
ERVU_KAFKA_SECURITY_PROTOCOL=PLAINTEXT
|
||||
#ERVU_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
|
||||
ERVU_KAFKA_SASL_MECHANISM=PLAIN
|
||||
ERVU_KAFKA_USERNAME=user2
|
||||
ERVU_KAFKA_PASSWORD=Blfi9d2OFG
|
||||
ERVU_KAFKA_GROUP_ID=local-ervu-lkrp-av2
|
||||
ERVU_KAFKA_ERROR_TOPIC_NAME=ervu.lkrp.download.request
|
||||
ERVU_KAFKA_SUCCESS_TOPIC_NAME=ervu.lkrp.download.request
|
||||
ERVU_KAFKA_RESPONSE_TOPIC_NAME=ervu.lkrp.download.response
|
||||
ERVU_KAFKA_LOGIN_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule
|
||||
|
||||
AV_CHECK_ENABLED=true
|
||||
AV_REST_ADDRESS=http://10.10.30.120:8085/scans
|
||||
|
|
@ -40,4 +34,4 @@ S3_PATH_STYLE_ACCESS_ENABLED=true
|
|||
FILE_WEBDAV_UPLOAD_USERNAME=test
|
||||
FILE_WEBDAV_UPLOAD_PASSWORD=test
|
||||
WEBDAV_USERNAME=test
|
||||
WEBDAV_PASSWORD=test
|
||||
WEBDAV_PASSWORD=test
|
||||
|
|
|
|||
|
|
@ -6,14 +6,17 @@ DB_APP_HOST=10.10.31.119
|
|||
DB_APP_PORT=5432
|
||||
DB_APP_NAME=ervu_lkrp_ul
|
||||
|
||||
KAFKA_HOSTS=local-kafka:9094
|
||||
#KAFKA_AUTH_SEC_PROTO=SASL_PLAINTEXT
|
||||
KAFKA_AUTH_SEC_PROTO=PLAINTEXT
|
||||
#KAFKA_AUTH_SASL_MECH=SCRAM-SHA-256
|
||||
KAFKA_AUTH_SASL_MECH=PLAIN
|
||||
#KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule
|
||||
KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule
|
||||
KAFKA_USER=user1
|
||||
KAFKA_PASS=Blfi9d2OFG
|
||||
|
||||
AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload
|
||||
AV_KAFKA_BOOTSTRAP_SERVERS=http://local-kafka:9094
|
||||
#AV_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
AV_KAFKA_SECURITY_PROTOCOL=PLAINTEXT
|
||||
#AV_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
|
||||
AV_KAFKA_SASL_MECHANISM=PLAIN
|
||||
AV_KAFKA_USERNAME=user2
|
||||
AV_KAFKA_PASSWORD=Blfi9d2OFG
|
||||
AV_KAFKA_GROUP_ID=local-ervu-lkrp-ul-backend
|
||||
AV_KAFKA_DOWNLOAD_RESPONSE=ervu.lkrp.av-fileupload-status
|
||||
|
||||
|
|
@ -28,24 +31,17 @@ ESIA_REDIRECT_URL=http://localhost:8080/
|
|||
ESIA_UPLOAD_DATA_ROLE=MNSV89_UPLOAD_DATA
|
||||
#ESIA_CLIENT_CERT_HASH=04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD
|
||||
ESIA_CLIENT_CERT_HASH=CF35A98C48E48665EA73530537BAFBB51F911C434ADC89215C2F86DCD04E28C5
|
||||
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
|
||||
ESIA_AUTH_INFO_CLEAR_CRON=0 0 */1 * * *
|
||||
|
||||
SIGN_URL=https://ervu-sign-dev.k8s.micord.ru/sign
|
||||
SIGN_VERIFY_URL=https://ervu-sign-dev.k8s.micord.ru/verify
|
||||
|
||||
ERVU_KAFKA_BOOTSTRAP_SERVERS=local-kafka:9094
|
||||
ERVU_KAFKA_ORG_REPLY_TOPIC=ervu.organization.response
|
||||
ERVU_KAFKA_GROUP_ID=local-ervu-lkrp-ul-backend
|
||||
ERVU_KAFKA_ORG_REQUEST_TOPIC=ervu.organization.request
|
||||
ERVU_KAFKA_REPLY_TIMEOUT=5
|
||||
ERVU_KAFKA_JOURNAL_REQUEST_TOPIC=ervu.organization.journal.request
|
||||
ERVU_KAFKA_JOURNAL_REPLY_TOPIC=ervu.organization.journal.response
|
||||
#ERVU_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
ERVU_KAFKA_SECURITY_PROTOCOL=PLAINTEXT
|
||||
#ERVU_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
|
||||
ERVU_KAFKA_SASL_MECHANISM=PLAIN
|
||||
ERVU_KAFKA_USERNAME=user2
|
||||
ERVU_KAFKA_PASSWORD=Blfi9d2OFG
|
||||
ERVU_KAFKA_EXCERPT_REPLY_TOPIC=ervu.lkrp.excerpt.response
|
||||
ERVU_KAFKA_EXCERPT_REQUEST_TOPIC=ervu.lkrp.excerpt.request
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ DB_APP_HOST=10.10.31.119
|
|||
DB_APP_PORT=5432
|
||||
DB_APP_NAME=ervu_lkrp_ul
|
||||
|
||||
KAFKA_HOSTS=10.10.31.11:32609
|
||||
KAFKA_AUTH_SEC_PROTO=SASL_PLAINTEXT
|
||||
KAFKA_AUTH_SASL_MECH=SCRAM-SHA-256
|
||||
KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule
|
||||
KAFKA_USER=user1
|
||||
KAFKA_PASS=Blfi9d2OFG
|
||||
|
||||
AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload
|
||||
AV_KAFKA_BOOTSTRAP_SERVERS=http://10.10.31.11:32609
|
||||
AV_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
AV_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
|
||||
AV_KAFKA_USERNAME=user1
|
||||
AV_KAFKA_PASSWORD=Blfi9d2OFG
|
||||
AV_KAFKA_GROUP_ID=1
|
||||
AV_KAFKA_DOWNLOAD_RESPONSE=ervu.lkrp.av-fileupload-status
|
||||
|
||||
|
|
@ -25,25 +27,18 @@ ESIA_REDIRECT_URL=https://lkrp-dev.micord.ru/ul/
|
|||
ESIA_LOGOUT_REDIRECT_URL=https://lkrp-dev.micord.ru/ul/home.html
|
||||
ESIA_UPLOAD_DATA_ROLE=MNSV89_UPLOAD_DATA
|
||||
ESIA_CLIENT_CERT_HASH=04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD
|
||||
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
|
||||
|
||||
ESIA_AUTH_INFO_CLEAR_CRON=0 0 */1 * * *
|
||||
ESIA_MARKER_VER=1
|
||||
|
||||
SIGN_URL=https://ervu-sign-dev.k8s.micord.ru/sign
|
||||
SIGN_VERIFY_URL=https://ervu-sign-dev.k8s.micord.ru/verify
|
||||
|
||||
|
||||
ERVU_KAFKA_BOOTSTRAP_SERVERS=10.10.31.11:32609
|
||||
ERVU_KAFKA_ORG_REPLY_TOPIC=ervu.organization.response
|
||||
ERVU_KAFKA_GROUP_ID=1
|
||||
ERVU_KAFKA_ORG_REQUEST_TOPIC=ervu.organization.request
|
||||
ERVU_KAFKA_REPLY_TIMEOUT=30
|
||||
ERVU_KAFKA_JOURNAL_REQUEST_TOPIC=ervu.organization.journal.request
|
||||
ERVU_KAFKA_JOURNAL_REPLY_TOPIC=ervu.organization.journal.response
|
||||
DB.JOURNAL.EXCLUDED.STATUSES=Направлено в ЕРВУ,Получен ЕРВУ
|
||||
ERVU_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
ERVU_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
|
||||
ERVU_KAFKA_USERNAME=user1
|
||||
ERVU_KAFKA_PASSWORD=Blfi9d2OFG
|
||||
ERVU_KAFKA_EXCERPT_REPLY_TOPIC=ervu.lkrp.excerpt.response
|
||||
ERVU_KAFKA_EXCERPT_REQUEST_TOPIC=ervu.lkrp.excerpt.request
|
||||
ESNSI_OKOPF_URL=https://esnsi.gosuslugi.ru/rest/ext/v1/classifiers/16271/file?extension=JSON&encoding=UTF_8
|
||||
|
|
@ -53,6 +48,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
|
||||
|
|
|
|||
|
|
@ -57,12 +57,13 @@
|
|||
<property name="webdav.urls" value="https://ervu-webdav.k8s.micord.ru"/>
|
||||
<property name="webdav.username" value="test"/>
|
||||
<property name="webdav.password" value="test"/>
|
||||
<property name="kafka.hosts" value="localhost:9092"/>
|
||||
<property name="kafka.auth_sec_proto" value="SASL_PLAINTEXT"/>
|
||||
<property name="kafka.auth_sasl_mech" value="SCRAM-SHA-256"/>
|
||||
<property name="kafka.auth_sasl_module" value="org.apache.kafka.common.security.scram.ScramLoginModule"/>
|
||||
<property name="kafka.user" value="user1"/>
|
||||
<property name="kafka.pass" value="Blfi9d2OFG"/>
|
||||
<property name="av.kafka.download-request-topic" value="ervu.lkrp.download.request"/>
|
||||
<property name="av.kafka.bootstrap.servers" value="localhost:9092"/>
|
||||
<property name="av.kafka.security.protocol" value="SASL_PLAINTEXT"/>
|
||||
<property name="av.kafka.sasl.mechanism" value="SCRAM-SHA-256"/>
|
||||
<property name="av.kafka.username" value="user1"/>
|
||||
<property name="av.kafka.password" value="Blfi9d2OFG"/>
|
||||
<property name="av.kafka.message.topic.name" value="file-to-upload"/>
|
||||
<property name="esia.scopes" value="fullname, snils, id_doc, birthdate, usr_org, openid"/>
|
||||
<property name="esia.org.scopes" value="org_fullname, org_shortname, org_brhs, org_brhs_ctts, org_brhs_addrs, org_type, org_ogrn, org_inn, org_leg, org_kpp, org_ctts, org_addrs, org_grps, org_emps"/>
|
||||
|
|
@ -72,17 +73,12 @@
|
|||
<property name="esia.client.id" value="MNSV89"/>
|
||||
<property name="esia.redirect.url" value="https://lkrp.micord.ru"/>
|
||||
<property name="sign.url" value="https://ervu-sign-dev.k8s.micord.ru/sign"/>
|
||||
<property name="ervu.kafka.bootstrap.servers" value="localhost:9092"/>
|
||||
<property name="ervu.kafka.org.reply.topic" value="ervu.organization.response"/>
|
||||
<property name="ervu.kafka.group.id" value="1"/>
|
||||
<property name="ervu.kafka.org.request.topic" value="ervu.organization.request"/>
|
||||
<property name="ervu.kafka.reply.timeout" value="30"/>
|
||||
<property name="esia.client.cert.hash" value="04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD"/>
|
||||
<property name="bpmn.enable" value="false"/>
|
||||
<property name="ervu.kafka.security.protocol" value="SASL_PLAINTEXT"/>
|
||||
<property name="ervu.kafka.sasl.mechanism" value="SCRAM-SHA-256"/>
|
||||
<property name="ervu.kafka.username" value="user1"/>
|
||||
<property name="ervu.kafka.password" value="Blfi9d2OFG"/>
|
||||
<property name="esnsi.okopf.cron.load" value="0 0 */1 * * *"/>
|
||||
<property name="esnsi.okopf.url" value="https://esnsi.gosuslugi.ru/rest/ext/v1/classifiers/16271/file?extension=JSON&encoding=UTF_8"/>
|
||||
<property name="esnsi.okop.retry.delay.load" value="30000"/>
|
||||
|
|
@ -95,13 +91,24 @@
|
|||
<property name="av.kafka.group.id" value="1"/>
|
||||
<property name="av.kafka.download.response" value="ervu.lkrp.av-fileupload-status"/>
|
||||
<property name="sign.verify.url" value="https://ervu-sign-dev.k8s.micord.ru/verify"/>
|
||||
<property name="esia.token.clear.cron" value="0 0 */1 * * *"/>
|
||||
<property name="esia.auth.info.clear.cron" value="0 0 */1 * * *"/>
|
||||
<property name="esia.upload.data.role" value="MNSV89_UPLOAD_DATA"/>
|
||||
<property name="webdav.cleanup.cron" value="0 0 0 * * *"/>
|
||||
<property name="webdav.retry.delay" value="500"/>
|
||||
<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>
|
||||
|
|
|
|||
12
frontend/package-lock.json
generated
|
|
@ -1748,9 +1748,9 @@
|
|||
}
|
||||
},
|
||||
"@webbpm/base-package": {
|
||||
"version": "3.187.2",
|
||||
"resolved": "https://repo.micord.ru/repository/npm-all/@webbpm/base-package/-/base-package-3.187.2.tgz",
|
||||
"integrity": "sha512-qDW+Yjm/gyTIM/4N7uQasQR1zk2tGGAF6rJFpSUSb1A7PYreXPqSAShzWJJJ1YZ9CCz2dAXSQzm6JjUJKu2VUg==",
|
||||
"version": "3.192.9",
|
||||
"resolved": "https://repo.micord.ru/repository/npm-all/@webbpm/base-package/-/base-package-3.192.9.tgz",
|
||||
"integrity": "sha512-W93LcQTm/OUGwRLJqyffNOqs0Uv9F4bdV26SqZr4QWngPeWKQ0pO8Jg5Lsy7d0xTVN4rfMM/NEaU2FYCC/yKzw==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
"@angular/platform-browser-dynamic": "7.2.15",
|
||||
"@angular/router": "7.2.15",
|
||||
"@ng-bootstrap/ng-bootstrap": "4.2.2-micord.1",
|
||||
"@webbpm/base-package": "3.187.2",
|
||||
"@webbpm/base-package": "3.192.9",
|
||||
"ag-grid-angular": "29.0.0-micord.4",
|
||||
"ag-grid-community": "29.0.0-micord.4",
|
||||
"angular-calendar": "0.28.28",
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@
|
|||
"password_pattern": "^((?=(.*\\d){1,})(?=.*[a-zа-яё])(?=.*[A-ZА-ЯЁ]).{8,})$",
|
||||
"password_pattern_error": "Пароль должен содержать заглавные или прописные буквы и как минимум 1 цифру",
|
||||
"show.client.errors": false,
|
||||
"available_task.single_fetch": true
|
||||
"available_task.single_fetch": true,
|
||||
"unknown.error.msg": "Система временно недоступна. Пожалуйста, повторите попытку позже."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -944,104 +944,104 @@
|
|||
}
|
||||
|
||||
/*---------------- HOME ----------------*/
|
||||
.webbpm .header-landing {
|
||||
.webbpm.ervu_lkrp_ul .header-landing {
|
||||
background-color: var(--color-text-primary) !important;
|
||||
z-index: 1;
|
||||
}
|
||||
.webbpm .header-landing > div {
|
||||
.webbpm.ervu_lkrp_ul .header-landing > div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-family: 'InterSB';
|
||||
}
|
||||
.webbpm .header-landing .header-logo {
|
||||
.webbpm.ervu_lkrp_ul .header-landing .header-logo {
|
||||
width: 62px;
|
||||
height: 40px;
|
||||
background: url(../img/svg/mil-logo.svg) no-repeat 0 50%;
|
||||
}
|
||||
.webbpm .header-landing .header-title {
|
||||
.webbpm.ervu_lkrp_ul .header-landing .header-title {
|
||||
color: var(--white);
|
||||
font-size: var(--size-text-secondary);
|
||||
margin-left: var(--indent-mini);
|
||||
}
|
||||
|
||||
.webbpm .container-inside .short-text {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .short-text {
|
||||
max-width: 60%;
|
||||
}
|
||||
.webbpm .container-inside .paragraph-left .short-text {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .paragraph-left .short-text {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group {
|
||||
position: relative;
|
||||
font-size: var(--size-text-secondary);
|
||||
line-height: normal;
|
||||
padding: 0 var(--w-screen);
|
||||
}
|
||||
.webbpm .container-inside .list-group .btn {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .btn {
|
||||
width: max-content;
|
||||
}
|
||||
.webbpm .container-inside .list-group .title {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .title {
|
||||
font-size: var(--l-size-text-maintitle);
|
||||
font-family: 'GolosB';
|
||||
margin-bottom: var(--l-indent-huge);
|
||||
}
|
||||
.webbpm .container-inside .list-group .subtitle {
|
||||
.webbpm.ervu_lkrp_ul.ervu_lkrp_ul .container-inside .list-group .subtitle {
|
||||
font-size: var(--l-size-text-title);
|
||||
font-family: 'GolosDB';
|
||||
margin-bottom: var(--l-indent-big);
|
||||
}
|
||||
.webbpm .container-inside .list-group .muted {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .muted {
|
||||
color: var(--color-light);
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .paragraph {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .paragraph-left {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .paragraph-left {
|
||||
width: 40%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .paragraph-right {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .paragraph-right {
|
||||
width: 60%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .paragraph-half {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .paragraph-half {
|
||||
width: 50%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .paragraph-third {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .paragraph-third {
|
||||
width: 33.33%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph [class*="paragraph-"] + [class*="paragraph-"] {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph [class*="paragraph-"] + [class*="paragraph-"] {
|
||||
margin-left: 40px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .text {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .text {
|
||||
font-family: 'InterSB';
|
||||
font-size: var(--l-size-text-primary);
|
||||
margin-bottom: var(--indent-mini);
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .icon-checklist,
|
||||
.webbpm .container-inside .list-group .paragraph .icon-clock,
|
||||
.webbpm .container-inside .list-group .paragraph .icon-text {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .icon-checklist,
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .icon-clock,
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .icon-text {
|
||||
padding-top: 44px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .icon-checklist {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .icon-checklist {
|
||||
background: url(../img/svg/checklist-32x32.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .icon-clock {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .icon-clock {
|
||||
background: url(../img/svg/clock-32x32.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph .icon-text {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph .icon-text {
|
||||
background: url(../img/svg/text-32x32.svg) no-repeat 0 0;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .list > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div {
|
||||
position: relative;
|
||||
padding-left: 36px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .list > div + div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div + div {
|
||||
margin-top: var(--indent-mini);
|
||||
}
|
||||
.webbpm .container-inside .list-group .list > div::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
|
|
@ -1049,23 +1049,26 @@
|
|||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .list > div.esia::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div.esia::after {
|
||||
background: url(../img/svg/esia-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .list > div.case::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div.case::after {
|
||||
background: url(../img/svg/case-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .list > div.user::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div.user::after {
|
||||
background: url(../img/svg/user-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .list > div.romb::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div.docs::after {
|
||||
background: url(../img/svg/docs-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list > div.romb::after {
|
||||
background: url(../img/svg/romb-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .list ~ .btn-group {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .list ~ .btn-group {
|
||||
margin-top: var(--indent-medium);
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .section-group > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 80px;
|
||||
|
|
@ -1075,10 +1078,10 @@
|
|||
border-radius: 4px;
|
||||
background-color: var(--bg-form);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div:last-child {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
|
|
@ -1089,39 +1092,39 @@
|
|||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div.icon-user::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div.icon-user::before {
|
||||
background-image: url(../img/svg/pers-wt.svg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div.icon-case::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div.icon-case::before {
|
||||
background-image: url(../img/svg/case-wt.svg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div.icon-shield::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div.icon-shield::before {
|
||||
background-image: url(../img/svg/shield-wt.svg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div.icon-clip::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div.icon-clip::before {
|
||||
background-image: url(../img/svg/clip-wt.svg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div.icon-pers::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div.icon-pers::before {
|
||||
background-image: url(../img/svg/pers-wt.svg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div.icon-building::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div.icon-building::before {
|
||||
background-image: url(../img/svg/building-wt.svg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div .muted {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div .muted {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .section-group > div .muted .detailed {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .section-group > div .muted .detailed {
|
||||
color: var(--color-text-primary);
|
||||
font-family: 'InterB';
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .pass-list {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-top: 60px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: calc(80% + 40px);
|
||||
|
|
@ -1130,11 +1133,11 @@
|
|||
left: 0;
|
||||
background-color: var(--color-link-hover);
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div {
|
||||
position: relative;
|
||||
width: 20%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
|
|
@ -1146,73 +1149,73 @@
|
|||
background-color: var(--bg-light);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
font-family: 'InterB';
|
||||
top: -50px;
|
||||
left: 15px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div:nth-child(1)::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div:nth-child(1)::after {
|
||||
content: "1";
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div:nth-child(2)::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div:nth-child(2)::after {
|
||||
content: "2";
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div:nth-child(3)::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div:nth-child(3)::after {
|
||||
content: "3";
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div:nth-child(4)::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div:nth-child(4)::after {
|
||||
content: "4";
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div:nth-child(5)::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div:nth-child(5)::after {
|
||||
content: "5";
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div + div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div + div {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .msg-list {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .msg-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .msg-list span {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .msg-list span {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 16px 0 0;
|
||||
background: url(../img/svg/info-gr.svg) no-repeat 0 0;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .docs-list {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.webbpm .container-inside .list-group .docs-list > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list > div {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 20%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .docs-list > div a {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list > div a {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding-right: 8px;
|
||||
background: url(../img/svg/download-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .docs-list > div + div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list > div + div {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group.lk-what {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-what {
|
||||
padding-top: var(--l-indent-huge);
|
||||
padding-bottom: var(--l-indent-huge);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-what::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-what::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
@ -1223,211 +1226,147 @@
|
|||
background: url(../img/bg-star.png) no-repeat calc(100% + 200px) 0px transparent;
|
||||
z-index: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-what > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-what > div {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group.lk-access {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-access {
|
||||
color: var(--white);
|
||||
padding-top: var(--l-indent-big);
|
||||
padding-bottom: var(--l-indent-big);
|
||||
background-color: var(--color-bg-main);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-info {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-info {
|
||||
padding-top: var(--l-indent-big);
|
||||
padding-bottom: var(--l-indent-big);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-pass {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-pass {
|
||||
padding-top: var(--l-indent-big);
|
||||
padding-bottom: var(--l-indent-big);
|
||||
background-color: var(--bg-light);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-when {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-when {
|
||||
color: var(--white);
|
||||
padding-top: var(--l-indent-big);
|
||||
padding-bottom: var(--indentl--big);
|
||||
background-color: var(--color-bg-main);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-msg {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-msg {
|
||||
background-color: var(--border-light);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-limits {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-limits {
|
||||
padding-top: var(--l-indent-big);
|
||||
padding-bottom: var(--l-indent-big);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-docs {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-docs {
|
||||
flex: 1;
|
||||
color: var(--white);
|
||||
padding-top: var(--l-indent-huge);
|
||||
padding-bottom: var(--l-indent-huge);
|
||||
background-color: var(--color-text-primary);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-alert {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-alert {
|
||||
padding-top: var(--l-indent-big);
|
||||
padding-bottom: var(--l-indent-big);
|
||||
background-color: var(--bg-light);
|
||||
}
|
||||
.webbpm .container-inside .list-group.lk-footer {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group.lk-footer {
|
||||
padding-top: var(--indent-small);
|
||||
padding-bottom: var(--indent-small);
|
||||
background-color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-what .title {
|
||||
color: var(--color-link);
|
||||
margin-bottom: var(--indent-small);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-what .title::after {
|
||||
content: url(../img/svg/star.svg);
|
||||
top: 18px;
|
||||
position: relative;
|
||||
margin-left: var(--l-indent-big);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-what .title + .short-text {
|
||||
max-width: 25%;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-what .title ~ .subtitle {
|
||||
margin-top: var(--l-indent-big);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-info .section-group > div {
|
||||
justify-content: center;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-pass .subtitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-pass .subtitle + div {
|
||||
margin-top: var(--indent-small);
|
||||
margin-bottom: var(--l-indent-big);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-pass .pass-list::before {
|
||||
display: none;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-pass .pass-list > div {
|
||||
position: relative;
|
||||
width: 33.33%;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-msg {
|
||||
color: var(--color-link);
|
||||
font-family: 'InterSB';
|
||||
background-color: var(--bg-form);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-msg span {
|
||||
background: url(../img/svg/info.svg) no-repeat 0 4px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-limits .subtitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-limits .subtitle + div {
|
||||
margin-top: var(--indent-small);
|
||||
margin-bottom: var(--l-indent-big);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-limits .scheme {
|
||||
width: 100%;
|
||||
height: 204px;
|
||||
background: url(../img/svg/scheme.svg) no-repeat 0 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-alert > .short-text {
|
||||
margin-bottom: var(--l-indent-big);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-alert .alert-block {
|
||||
position: relative;
|
||||
padding: var(--indent-small) 64px var(--indent-small) var(--indent-small);
|
||||
border-radius: 4px;
|
||||
border: 2px solid var(--border-light);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-alert .alert-block::after {
|
||||
content: url(../img/svg/info.svg);
|
||||
position: absolute;
|
||||
top: var(--indent-small);
|
||||
right: var(--indent-small);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-alert .alert-block > div + div {
|
||||
margin-top: var(--indent-small);
|
||||
}
|
||||
.webbpm.ervu_lkrp_fl .container-inside .list-group.lk-alert .alert-block > div:last-child {
|
||||
color: var(--color-link);
|
||||
}
|
||||
|
||||
/*@media ((max-width: 780px) or ((orientation: landscape) and (max-device-width : 1024px))) {*/
|
||||
@media (max-width: 1024px) {
|
||||
.container-inside .short-text {
|
||||
.webbpm.ervu_lkrp_ul .landing .container {
|
||||
top: var(--h-header);
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .landing .container-inside {
|
||||
overflow: visible;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .landing .browser-check-content {
|
||||
padding-left: var(--w-screen);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .container-inside .short-text {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.webbpm .container-inside .list-group .paragraph {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph {
|
||||
flex-direction: column;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph [class*="paragraph-"] {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph [class*="paragraph-"] {
|
||||
width: auto;
|
||||
margin-left: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .paragraph [class*="paragraph-"] + [class*="paragraph-"] {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .paragraph [class*="paragraph-"] + [class*="paragraph-"] {
|
||||
margin-top: var(--indent-mini);
|
||||
margin-left: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list {
|
||||
flex-direction: column;
|
||||
padding-top: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list::before {
|
||||
display: none;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: auto !important;
|
||||
padding-left: 60px;
|
||||
min-height: 40px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div::before {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div::before {
|
||||
top: 0;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div::after {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div::after {
|
||||
top: 10px;
|
||||
}
|
||||
.webbpm .container-inside .list-group .pass-list > div + div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .pass-list > div + div {
|
||||
margin-left: 0;
|
||||
margin-top: var(--indent-mini);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.webbpm .container-inside .list-group .docs-list {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list {
|
||||
flex-direction: column;
|
||||
}
|
||||
.webbpm .container-inside .list-group .docs-list > div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list > div {
|
||||
width: 100%;
|
||||
}
|
||||
.webbpm .container-inside .list-group .docs-list > div + div {
|
||||
.webbpm.ervu_lkrp_ul .container-inside .list-group .docs-list > div + div {
|
||||
margin-left: 0;
|
||||
margin-top: var(--indent-mini);
|
||||
}
|
||||
}
|
||||
/*------------- end - HOME -------------*/
|
||||
|
||||
.browser-check-content {
|
||||
.webbpm.ervu_lkrp_ul .browser-check-content {
|
||||
font-family: 'Golos';
|
||||
font-size: var(--size-text-secondary);
|
||||
padding: var(--indent-mini) var(--w-screen) var(--indent-mini) calc(var(--w-screen) + 38px);
|
||||
background-color: var(--bg-warn);
|
||||
}
|
||||
.browser-check-text {
|
||||
.webbpm.ervu_lkrp_ul .browser-check-text {
|
||||
position: relative;
|
||||
padding-left: 40px;
|
||||
}
|
||||
.browser-check-text::before {
|
||||
.webbpm.ervu_lkrp_ul .browser-check-text::before {
|
||||
position: absolute;
|
||||
content: url(../img/svg/info.svg);
|
||||
left: 0;
|
||||
top: calc((100% - 24px) / 2);
|
||||
}
|
||||
.text-header {
|
||||
.webbpm.ervu_lkrp_ul .text-header {
|
||||
color: var(--color-link);
|
||||
font-family: 'GolosB';
|
||||
font-size: var(--size-text-primary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.plain-text {
|
||||
.webbpm.ervu_lkrp_ul .plain-text {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
@ -47,6 +47,12 @@
|
|||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Golos';
|
||||
src: url('../fonts/GolosText-Regular.ttf');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body.webbpm.ervu_lkrp_ul {
|
||||
-ms-text-size-adjust: 100%;
|
||||
|
|
@ -125,13 +131,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);
|
||||
}
|
||||
|
|
@ -202,25 +211,32 @@ body.webbpm.ervu_lkrp_ul {
|
|||
top: auto;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: auto;
|
||||
right: auto;
|
||||
height: var(--h-footer);
|
||||
border: 0;
|
||||
background-color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: var(--h-header);
|
||||
bottom: 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .container-inside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'Inter';
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .container-inside > div {
|
||||
padding: var(--indent-huge) var(--w-screen);
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul .container-inside home-landing + app-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*@media ((max-width: 780px) or ((orientation: landscape) and (max-device-width : 1024px))) {*/
|
||||
@media (max-width: 1024px) {
|
||||
|
|
|
|||
7
frontend/src/resources/img/svg/docs-24x24.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.82986 2.7988C5.36122 2.28734 6.08189 2 6.83333 2H14.3889C14.6394 2 14.8796 2.09578 15.0567 2.26627L20.7234 7.72081C20.9005 7.8913 21 8.12253 21 8.36364V19.2727C21 19.996 20.7015 20.6897 20.1701 21.2012C19.6388 21.7127 18.9181 22 18.1667 22H6.83333C6.08189 22 5.36122 21.7127 4.82986 21.2012C4.29851 20.6897 4 19.996 4 19.2727V4.72727C4 4.00396 4.29851 3.31026 4.82986 2.7988ZM6.83333 3.81818C6.58285 3.81818 6.34263 3.91396 6.16551 4.08445C5.98839 4.25494 5.88889 4.48617 5.88889 4.72727V19.2727C5.88889 19.5138 5.98839 19.7451 6.16551 19.9156C6.34263 20.086 6.58285 20.1818 6.83333 20.1818H18.1667C18.4171 20.1818 18.6574 20.086 18.8345 19.9156C19.0116 19.7451 19.1111 19.5138 19.1111 19.2727V8.74019L13.9977 3.81818H6.83333Z" fill="#FA773F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3889 2C14.9105 2 15.3333 2.40701 15.3333 2.90909V7.45454H20.0556C20.5772 7.45454 21 7.86156 21 8.36364C21 8.86571 20.5772 9.27273 20.0556 9.27273H14.3889C13.8673 9.27273 13.4445 8.86571 13.4445 8.36364V2.90909C13.4445 2.40701 13.8673 2 14.3889 2Z" fill="#FA773F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.77783 12.9091C7.77783 12.407 8.20067 12 8.72228 12H16.2778C16.7994 12 17.2223 12.407 17.2223 12.9091C17.2223 13.4112 16.7994 13.8182 16.2778 13.8182H8.72228C8.20067 13.8182 7.77783 13.4112 7.77783 12.9091Z" fill="#FA773F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.77783 16.5458C7.77783 16.0437 8.20067 15.6367 8.72228 15.6367H16.2778C16.7994 15.6367 17.2223 16.0437 17.2223 16.5458C17.2223 17.0479 16.7994 17.4549 16.2778 17.4549H8.72228C8.20067 17.4549 7.77783 17.0479 7.77783 16.5458Z" fill="#FA773F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.77783 9.27335C7.77783 8.77127 8.20067 8.36426 8.72228 8.36426H10.6112C11.1328 8.36426 11.5556 8.77127 11.5556 9.27335C11.5556 9.77543 11.1328 10.1824 10.6112 10.1824H8.72228C8.20067 10.1824 7.77783 9.77543 7.77783 9.27335Z" fill="#FA773F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
|
|
@ -122,6 +122,11 @@ button, a.btn {
|
|||
border-radius: 4px;
|
||||
background: var(--color-link);
|
||||
}
|
||||
a.btn.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
button:is(:hover, :focus, :active),
|
||||
a.btn:is(:hover, :focus, :active) {
|
||||
background: var(--color-link-hover);
|
||||
|
|
@ -333,6 +338,9 @@ a.btn:is(:hover, :focus, :active) {
|
|||
:is(.ul, .fl) .container-inside .list-group .list > div.user::after {
|
||||
background: url(img/svg/user-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .list > div.docs::after {
|
||||
background: url(img/svg/docs-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .list > div.romb::after {
|
||||
background: url(img/svg/romb-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.7158 3C9.35852 3 8.20421 3.9903 7.99782 5.33184L7.89503 6H4C2.89543 6 2 6.89543 2 8V18C2 19.6569 3.34315 21 5 21H19C20.6569 21 22 19.6569 22 18V8C22 6.89543 21.1046 6 20 6H16.105L16.0022 5.33184C15.7958 3.99031 14.6415 3 13.2842 3H10.7158ZM14.5873 6L14.5196 5.55993C14.4258 4.95014 13.9011 4.5 13.2842 4.5H10.7158C10.0989 4.5 9.57419 4.95014 9.48038 5.55993L9.41268 6H14.5873ZM4 7.5H20C20.2761 7.5 20.5 7.72386 20.5 8V10.9297C18.8456 11.9818 16.9613 12.684 15 13.0362V13C15 11.8954 14.1046 11 13 11H11C9.89543 11 9 11.8954 9 13V13.0362C7.03871 12.684 5.1544 11.9818 3.5 10.9297V8C3.5 7.72386 3.72386 7.5 4 7.5ZM9 14.5582C7.06675 14.2404 5.19401 13.6122 3.5 12.6736V18C3.5 18.8284 4.17157 19.5 5 19.5H19C19.8284 19.5 20.5 18.8284 20.5 18V12.6736C18.806 13.6122 16.9332 14.2404 15 14.5582V15C15 16.1046 14.1046 17 13 17H11C9.89543 17 9 16.1046 9 15V14.5582ZM11 12.5H13C13.2761 12.5 13.5 12.7239 13.5 13V15C13.5 15.2761 13.2761 15.5 13 15.5H11C10.7239 15.5 10.5 15.2761 10.5 15V13C10.5 12.7239 10.7239 12.5 11 12.5Z" fill="#FA773F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.7158 3C9.35852 3 8.20421 3.9903 7.99782 5.33184L7.89503 6H4C2.89543 6 2 6.89543 2 8V18C2 19.6569 3.34315 21 5 21H19C20.6569 21 22 19.6569 22 18V8C22 6.89543 21.1046 6 20 6H16.105L16.0022 5.33184C15.7958 3.99031 14.6415 3 13.2842 3H10.7158ZM14.5873 6L14.5196 5.55993C14.4258 4.95014 13.9011 4.5 13.2842 4.5H10.7158C10.0989 4.5 9.57419 4.95014 9.48038 5.55993L9.41268 6H14.5873ZM4 7.5H20C20.2761 7.5 20.5 7.72386 20.5 8V10.9297C18.8456 11.9818 16.9613 12.684 15 13.0362V13C15 11.8954 14.1046 11 13 11H11C9.89543 11 9 11.8954 9 13V13.0362C7.03871 12.684 5.1544 11.9818 3.5 10.9297V8C3.5 7.72386 3.72386 7.5 4 7.5ZM9 14.5582C7.06675 14.2404 5.19401 13.6122 3.5 12.6736V18C3.5 18.8284 4.17157 19.5 5 19.5H19C19.8284 19.5 20.5 18.8284 20.5 18V12.6736C18.806 13.6122 16.9332 14.2404 15 14.5582V15C15 16.1046 14.1046 17 13 17H11C9.89543 17 9 16.1046 9 15V14.5582ZM11 12.5H13C13.2761 12.5 13.5 12.7239 13.5 13V15C13.5 15.2761 13.2761 15.5 13 15.5H11C10.7239 15.5 10.5 15.2761 10.5 15V13C10.5 12.7239 10.7239 12.5 11 12.5Z" fill="#C64E1B"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
7
frontend/src/resources/landing/img/svg/docs-24x24.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.82986 2.7988C5.36122 2.28734 6.08189 2 6.83333 2H14.3889C14.6394 2 14.8796 2.09578 15.0567 2.26627L20.7234 7.72081C20.9005 7.8913 21 8.12253 21 8.36364V19.2727C21 19.996 20.7015 20.6897 20.1701 21.2012C19.6388 21.7127 18.9181 22 18.1667 22H6.83333C6.08189 22 5.36122 21.7127 4.82986 21.2012C4.29851 20.6897 4 19.996 4 19.2727V4.72727C4 4.00396 4.29851 3.31026 4.82986 2.7988ZM6.83333 3.81818C6.58285 3.81818 6.34263 3.91396 6.16551 4.08445C5.98839 4.25494 5.88889 4.48617 5.88889 4.72727V19.2727C5.88889 19.5138 5.98839 19.7451 6.16551 19.9156C6.34263 20.086 6.58285 20.1818 6.83333 20.1818H18.1667C18.4171 20.1818 18.6574 20.086 18.8345 19.9156C19.0116 19.7451 19.1111 19.5138 19.1111 19.2727V8.74019L13.9977 3.81818H6.83333Z" fill="#C64E1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3889 2C14.9105 2 15.3333 2.40701 15.3333 2.90909V7.45454H20.0556C20.5772 7.45454 21 7.86156 21 8.36364C21 8.86571 20.5772 9.27273 20.0556 9.27273H14.3889C13.8673 9.27273 13.4445 8.86571 13.4445 8.36364V2.90909C13.4445 2.40701 13.8673 2 14.3889 2Z" fill="#C64E1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.77783 12.9091C7.77783 12.407 8.20067 12 8.72228 12H16.2778C16.7994 12 17.2223 12.407 17.2223 12.9091C17.2223 13.4112 16.7994 13.8182 16.2778 13.8182H8.72228C8.20067 13.8182 7.77783 13.4112 7.77783 12.9091Z" fill="#C64E1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.77783 16.5458C7.77783 16.0437 8.20067 15.6367 8.72228 15.6367H16.2778C16.7994 15.6367 17.2223 16.0437 17.2223 16.5458C17.2223 17.0479 16.7994 17.4549 16.2778 17.4549H8.72228C8.20067 17.4549 7.77783 17.0479 7.77783 16.5458Z" fill="#C64E1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.77783 9.27335C7.77783 8.77127 8.20067 8.36426 8.72228 8.36426H10.6112C11.1328 8.36426 11.5556 8.77127 11.5556 9.27335C11.5556 9.77543 11.1328 10.1824 10.6112 10.1824H8.72228C8.20067 10.1824 7.77783 9.77543 7.77783 9.27335Z" fill="#C64E1B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
|
|
@ -1,4 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.44531 9.5C3.0311 9.5 2.69531 9.83579 2.69531 10.25C2.69531 10.6642 3.0311 11 3.44531 11H9.83731C10.2515 11 10.5873 10.6642 10.5873 10.25C10.5873 9.83579 10.2515 9.5 9.83731 9.5H3.44531ZM3.44531 13C3.0311 13 2.69531 13.3358 2.69531 13.75C2.69531 14.1642 3.0311 14.5 3.44531 14.5H15.4853C15.8995 14.5 16.2353 14.1642 16.2353 13.75C16.2353 13.3358 15.8995 13 15.4853 13H3.44531Z" fill="#FA773F"/>
|
||||
<path d="M3.72852 16.9899C4.03952 17.3879 4.42752 17.7289 4.87852 17.9889L9.99952 20.9489C11.2375 21.6639 12.7615 21.6639 13.9995 20.9489L18.7495 18.2069C19.9875 17.4919 20.7495 16.1719 20.7495 14.7429V9.25788C20.7495 7.82888 19.9875 6.50788 18.7495 5.79388L13.9995 3.05188C12.7615 2.33688 11.2375 2.33688 9.99952 3.05188L4.87852 6.01188C4.42752 6.27288 4.03952 6.61387 3.72852 7.01088" stroke="#FA773F" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.44531 9.5C3.0311 9.5 2.69531 9.83579 2.69531 10.25C2.69531 10.6642 3.0311 11 3.44531 11H9.83731C10.2515 11 10.5873 10.6642 10.5873 10.25C10.5873 9.83579 10.2515 9.5 9.83731 9.5H3.44531ZM3.44531 13C3.0311 13 2.69531 13.3358 2.69531 13.75C2.69531 14.1642 3.0311 14.5 3.44531 14.5H15.4853C15.8995 14.5 16.2353 14.1642 16.2353 13.75C16.2353 13.3358 15.8995 13 15.4853 13H3.44531Z" fill="#C64E1B"/>
|
||||
<path d="M3.72852 16.9899C4.03952 17.3879 4.42752 17.7289 4.87852 17.9889L9.99952 20.9489C11.2375 21.6639 12.7615 21.6639 13.9995 20.9489L18.7495 18.2069C19.9875 17.4919 20.7495 16.1719 20.7495 14.7429V9.25788C20.7495 7.82888 19.9875 6.50788 18.7495 5.79388L13.9995 3.05188C12.7615 2.33688 11.2375 2.33688 9.99952 3.05188L4.87852 6.01188C4.42752 6.27288 4.03952 6.61387 3.72852 7.01088" stroke="#C64E1B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1,021 B After Width: | Height: | Size: 1,021 B |
|
|
@ -1,4 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.76172 20.9419C4.76172 18.9559 5.58268 17.1559 6.90161 15.8529C7.56958 15.1929 8.36654 14.6609 9.24849 14.2969L11.9974 16.4647L14.7462 14.2969C15.6292 14.6609 16.4251 15.1929 17.0931 15.8529C18.412 17.1559 19.233 18.9559 19.233 20.9419" stroke="#FA773F" stroke-width="1.49996" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.2375 6.87359C15.2375 9.33359 13.6176 10.5636 11.9976 10.5636C10.3777 10.5636 8.75781 9.33359 8.75781 6.87359C8.75781 4.41359 10.3777 3.18359 11.9976 3.18359C13.6176 3.18359 15.2375 4.42359 15.2375 6.87359Z" stroke="#FA773F" stroke-width="1.49996" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.76172 20.9419C4.76172 18.9559 5.58268 17.1559 6.90161 15.8529C7.56958 15.1929 8.36654 14.6609 9.24849 14.2969L11.9974 16.4647L14.7462 14.2969C15.6292 14.6609 16.4251 15.1929 17.0931 15.8529C18.412 17.1559 19.233 18.9559 19.233 20.9419" stroke="#C64E1B" stroke-width="1.49996" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.2375 6.87359C15.2375 9.33359 13.6176 10.5636 11.9976 10.5636C10.3777 10.5636 8.75781 9.33359 8.75781 6.87359C8.75781 4.41359 10.3777 3.18359 11.9976 3.18359C13.6176 3.18359 15.2375 4.42359 15.2375 6.87359Z" stroke="#C64E1B" stroke-width="1.49996" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 749 B |
|
|
@ -1,12 +1,12 @@
|
|||
<nav class="header" id="webbpm-header" [ngClass]="{'header-landing': isLanding}">
|
||||
<div *ngIf="isLanding">
|
||||
<div class="header-logo"></div>
|
||||
<div class="header-title">Реестр повесток юридических лиц</div>
|
||||
<div class="header-title">Личный кабинет для юридических лиц</div>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -23,9 +23,12 @@
|
|||
<div class="esia short-text">Необходимо авторизоваться</div>
|
||||
<div class="case short-text">Потребуется подтвержденная учетная запись организации</div>
|
||||
<div class="user short-text">Доступ предоставляется тольĸо сотрудниĸу, наделенному соответствующими полномочиями (ролью) на ведения воинсĸого учета внутри организации</div>
|
||||
<div class="docs short-text">
|
||||
<a href="src/resources/landing/pdf/Инструкция_для_технических_специалистов.pdf" download="Инструкция_для_технических_специалистов.pdf">Информация для технических специалистов</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a href="." class="btn">Войти в Личный кабинет</a>
|
||||
<a id="login-button" href="." class="btn">Войти в Личный кабинет</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -77,6 +80,4 @@
|
|||
<div class="msg-list">
|
||||
<span class="info"></span>Если в файле будут ошибĸи, данные не будут приняты Реестром и выгрузĸу сведений придется повторить
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group lk-docs">
|
||||
</div>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<div class="wrapper">
|
||||
<div class="wrapper" [ngClass]="{'landing': isLanding}">
|
||||
<app-header *ngIf="headerVisible">
|
||||
</app-header>
|
||||
<div class="container">
|
||||
|
|
@ -6,10 +6,10 @@
|
|||
<div class="browser-check-content">
|
||||
<div class="browser-check-text">
|
||||
<p class="plain-text text-header">
|
||||
Доступ к личному кабинету для юридических лиц осуществляется исключительно через браузеры Яндекс или Chromium gost, а также необходимо наличие установленного приложения КриптоПро CSP.
|
||||
Доступ к личному кабинету для юридических лиц осуществляется исключительно через браузеры Яндекс или Chromium GOST, а также необходимо наличие установленного приложения КриптоПро версии CSP 5.0 R2 и выше с расширением КриптоПро ЭЦП Browser plug-in
|
||||
</p>
|
||||
<p class="plain-text">
|
||||
Пожалуйста, попробуйте снова, выбрав один из этих браузеров и установив КриптоПро CSP.
|
||||
Пожалуйста, попробуйте снова, выбрав один из этих браузеров и установив <a href="https://www.cryptopro.ru/products/csp">КриптоПро CSP</a>, <a href="https://www.cryptopro.ru/products/cades/plugin">КриптоПро ЭЦП Browser plug-in</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
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
|
|
@ -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,
|
||||
|
|
@ -25,11 +31,9 @@ import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class InMemoryStaticGrid extends GridV2 {
|
||||
// todo: remove on updating platform version up to 3.188
|
||||
@Visible("false")
|
||||
public columnFiltersChanged: Event<any> = new Event<any>();
|
||||
|
||||
private rpcService: InMemoryStaticGridRpcService;
|
||||
private auditService: AuditService;
|
||||
|
||||
getRowModelType(): string {
|
||||
return GridRowModelType.CLIENT_SIDE;
|
||||
|
|
@ -37,6 +41,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 +52,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));
|
||||
|
|
@ -113,17 +151,4 @@ export class InMemoryStaticGrid extends GridV2 {
|
|||
public getRowDataSize(): number {
|
||||
return this.rowData ? this.rowData.length : 0;
|
||||
}
|
||||
|
||||
// todo: remove on updating platform version up to 3.188
|
||||
@Visible()
|
||||
public hasColumnFilters(): boolean {
|
||||
const filterModel: { [key: string]: any; } = this.gridApi.getFilterModel();
|
||||
return !!filterModel && Object.keys(filterModel).length > 0;
|
||||
}
|
||||
|
||||
// todo: remove on updating platform version up to 3.188
|
||||
public columnFilterChanged(event: FilterChangedEvent) {
|
||||
super.columnFilterChanged(event);
|
||||
this.columnFiltersChanged.trigger(event);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
frontend/src/ts/ervu/service/AuditService.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import {Injectable} from "@angular/core";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {Router} from "@angular/router";
|
||||
import {AuthenticationService} from "../../modules/security/authentication.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root"
|
||||
})
|
||||
export class AuditService {
|
||||
constructor(private httpClient: HttpClient,
|
||||
private router: Router,
|
||||
private authService: AuthenticationService) {
|
||||
}
|
||||
|
||||
public logActionAudit(eventType: string, filterInfo?: Record<string, FilterInfo>,
|
||||
fileName?: string): void {
|
||||
if (this.authService.isAuthenticated()) {
|
||||
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
|
|
@ -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,16 +9,22 @@ 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;
|
||||
}
|
||||
else {
|
||||
document.getElementById("login-button").classList.add('disabled');
|
||||
}
|
||||
});
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
|
|
|
|||