Merge branch 'hotfix/1.9.5'
This commit is contained in:
commit
714374ae55
156 changed files with 1015 additions and 469 deletions
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<version>1.9.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
<artifactId>backend</artifactId>
|
||||
|
|
@ -89,6 +89,14 @@
|
|||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial.snappy</groupId>
|
||||
<artifactId>snappy-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.cg.webbpm.modules</groupId>
|
||||
<artifactId>inject</artifactId>
|
||||
|
|
@ -182,6 +190,10 @@
|
|||
<groupId>com.github.lookfirst</groupId>
|
||||
<artifactId>sardine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tika</groupId>
|
||||
<artifactId>tika-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${project.parent.artifactId}</finalName>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package ervu.client.fileupload;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Authenticator;
|
||||
import java.net.ConnectException;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
|
|
@ -10,14 +11,27 @@ import java.net.http.HttpClient;
|
|||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.github.sardine.DavResource;
|
||||
import com.github.sardine.Sardine;
|
||||
import com.github.sardine.SardineFactory;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import ervu.exception.WebDavException;
|
||||
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.Value;
|
||||
|
|
@ -36,34 +50,113 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
*/
|
||||
@Component
|
||||
public class WebDavClient {
|
||||
private static final Logger logger = LoggerFactory.getLogger(WebDavClient.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WebDavClient.class);
|
||||
|
||||
@Value("${file.webdav.upload.username}")
|
||||
@Value("${webdav.urls}")
|
||||
private String[] urls;
|
||||
@Value("${webdav.username}")
|
||||
private String username;
|
||||
@Value("${file.webdav.upload.password}")
|
||||
@Value("${webdav.password}")
|
||||
private String password;
|
||||
@Value("${webdav.bad_servers.cache.expire.seconds:120}")
|
||||
private long cacheExpireSec;
|
||||
|
||||
private List<Server> servers;
|
||||
private LoadingCache<String, String> badServersCache;
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
CacheLoader<String, String> loader = new CacheLoader<>() {
|
||||
@Override
|
||||
public String load(String key) {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
badServersCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(cacheExpireSec, TimeUnit.SECONDS)
|
||||
.build(loader);
|
||||
LOGGER.info("WebDAV urls: {}", Arrays.asList(urls));
|
||||
servers = Arrays.stream(urls)
|
||||
.map(url -> new Server(url, ZonedDateTime.now().toInstant().toEpochMilli()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Retryable(value = {IOException.class}, backoff = @Backoff(delayExpression = "${webdav.retry.delay:500}"))
|
||||
public boolean uploadFile(String url, MultipartFile multipartFile) {
|
||||
public String uploadFile(MultipartFile multipartFile) {
|
||||
String fileName = getNewFilename(multipartFile.getOriginalFilename());
|
||||
Sardine sardine = initClient(username, password);
|
||||
|
||||
try {
|
||||
sardine.put(url, multipartFile.getBytes());
|
||||
return sardine.exists(url);
|
||||
return putAndGetUrl(multipartFile.getBytes(), fileName, sardine);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to put or check file in WebDAV", e);
|
||||
throw new WebDavException("Failed to put file into WebDAV", e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
sardine.shutdown();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Failed to shutdown WebDAV client", e);
|
||||
LOGGER.error("Failed to shutdown WebDAV client", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getNewFilename(String filename) {
|
||||
int lastIndexOf = filename.lastIndexOf(".");
|
||||
String fileExtension = lastIndexOf == -1 ? "" : filename.substring(lastIndexOf);
|
||||
return UUID.randomUUID() + fileExtension;
|
||||
}
|
||||
|
||||
public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client) throws IOException {
|
||||
if (badServersCache.size() == urls.length) {
|
||||
return null;
|
||||
}
|
||||
Server server;
|
||||
|
||||
if (urls.length == 1) {
|
||||
server = servers.get(0);
|
||||
}
|
||||
else {
|
||||
Predicate<Server> isNotBad = s -> badServersCache.getIfPresent(s.getUrl()) == null;
|
||||
server = servers.stream()
|
||||
.filter(isNotBad.and(s -> servers.indexOf(s) == index()))
|
||||
.findFirst()
|
||||
.orElseGet(() -> servers.stream()
|
||||
.filter(isNotBad)
|
||||
.min(Comparator.comparing(Server::getLastCallTime))
|
||||
.orElse(null));
|
||||
}
|
||||
|
||||
if (server == null) {
|
||||
return null;
|
||||
}
|
||||
boolean isBad = false;
|
||||
String serverUrl = server.getUrl();
|
||||
String fileUploadUrl = serverUrl + "/" + fileName;
|
||||
|
||||
try {
|
||||
client.put(fileUploadUrl, fileBytes);
|
||||
server.setLastCallTime(System.currentTimeMillis());
|
||||
}
|
||||
catch (ConnectException | ClientProtocolException ignore) {
|
||||
isBad = true;
|
||||
}
|
||||
|
||||
if (isBad) {
|
||||
badServersCache.getUnchecked(serverUrl);
|
||||
return putAndGetUrl(fileBytes, fileName, client);
|
||||
}
|
||||
|
||||
return fileUploadUrl;
|
||||
}
|
||||
|
||||
private int index() {
|
||||
counter.compareAndSet(Integer.MAX_VALUE, 0);
|
||||
return counter.getAndIncrement() % urls.length;
|
||||
}
|
||||
|
||||
@Retryable(value = {IOException.class, InterruptedException.class},
|
||||
backoff = @Backoff(delayExpression = "${webdav.retry.delay:500}"))
|
||||
public ResponseEntity<Resource> webDavDownloadFile(String url) {
|
||||
|
|
@ -94,12 +187,12 @@ public class WebDavClient {
|
|||
.body(resource);
|
||||
}
|
||||
else {
|
||||
logger.error("Failed to download file: HTTP status code {}", response.statusCode());
|
||||
LOGGER.error("Failed to download file: HTTP status code {}", response.statusCode());
|
||||
return ResponseEntity.status(response.statusCode()).build();
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new WebDavException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +202,7 @@ public class WebDavClient {
|
|||
}
|
||||
|
||||
@Retryable(value = {IOException.class}, backoff = @Backoff(delayExpression = "${webdav.retry.delay:500}"))
|
||||
public void deleteFilesOlderThan(long seconds, String url, String username, String password,
|
||||
String... extensions) {
|
||||
public void deleteFilesOlderThan(long seconds, String url, String... extensions) throws IOException {
|
||||
Sardine sardine = initClient(username, password);
|
||||
|
||||
try {
|
||||
|
|
@ -124,20 +216,17 @@ public class WebDavClient {
|
|||
sardine.delete(url + davResource.getPath());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to delete file " + davResource.getName(), e);
|
||||
LOGGER.error("Failed to delete file {}", davResource.getName(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to delete old files from WebDAV", e);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
sardine.shutdown();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Failed to shutdown WebDAV client", e);
|
||||
LOGGER.error("Failed to shutdown WebDAV client", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package ervu.controller;
|
|||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.TimeZone;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ervu.service.fileupload.EmployeeInfoFileUploadService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
|
@ -13,6 +11,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
|||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
||||
|
||||
/**
|
||||
* @author Alexandr Shalaginov
|
||||
|
|
@ -26,26 +26,17 @@ public class EmployeeInfoFileUploadController {
|
|||
}
|
||||
|
||||
@RequestMapping(value = "/employee/document", method = RequestMethod.POST)
|
||||
public ResponseEntity<?> saveEmployeeInformationFile(@RequestParam("file") MultipartFile multipartFile,
|
||||
public ResponseEntity<?> saveEmployeeInformationFile(
|
||||
@RequestParam("file") MultipartFile multipartFile,
|
||||
@RequestHeader("X-Employee-Info-File-Form-Type") String formType,
|
||||
@RequestHeader("Client-Time-Zone") String clientTimeZone, HttpServletRequest request) {
|
||||
String accessToken = null;
|
||||
String authToken = null;
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals("auth_token")) {
|
||||
authToken = cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@RequestHeader("Client-Time-Zone") String clientTimeZone) {
|
||||
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
|
||||
|
||||
if (authToken != null) {
|
||||
if (userIdsPair != null) {
|
||||
String offset = ZonedDateTime.now(TimeZone.getTimeZone(clientTimeZone).toZoneId())
|
||||
.getOffset().getId();
|
||||
|
||||
if (this.fileUploadService.saveEmployeeInformationFile(multipartFile, formType,
|
||||
authToken, offset)) {
|
||||
if (this.fileUploadService.saveEmployeeInformationFile(multipartFile, formType, offset, userIdsPair)) {
|
||||
return ResponseEntity.ok("File successfully uploaded.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
backend/src/main/java/ervu/exception/WebDavException.java
Normal file
19
backend/src/main/java/ervu/exception/WebDavException.java
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package ervu.exception;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class WebDavException extends RuntimeException{
|
||||
|
||||
public WebDavException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WebDavException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public WebDavException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
27
backend/src/main/java/ervu/model/webdav/Server.java
Normal file
27
backend/src/main/java/ervu/model/webdav/Server.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package ervu.model.webdav;
|
||||
|
||||
/**
|
||||
* @author gulnaz
|
||||
*/
|
||||
public class Server {
|
||||
|
||||
private final String url;
|
||||
private long lastCallTime;
|
||||
|
||||
public Server(String url, long lastCallTime) {
|
||||
this.url = url;
|
||||
this.lastCallTime = lastCallTime;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public long getLastCallTime() {
|
||||
return lastCallTime;
|
||||
}
|
||||
|
||||
public void setLastCallTime(long lastCallTime) {
|
||||
this.lastCallTime = lastCallTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package ervu.service.fileupload;
|
||||
|
||||
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;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
|
@ -16,6 +18,11 @@ import ervu.model.fileupload.EmployeeInfoKafkaMessage;
|
|||
import ervu.model.fileupload.FileInfo;
|
||||
import ervu.model.fileupload.FileStatus;
|
||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||
import org.apache.tika.Tika;
|
||||
import org.apache.tika.mime.MediaType;
|
||||
import org.apache.tika.mime.MimeType;
|
||||
import org.apache.tika.mime.MimeTypeException;
|
||||
import org.apache.tika.mime.MimeTypes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
|
@ -24,15 +31,18 @@ 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.security.esia.token.EsiaTokensStore;
|
||||
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.webbpm.jwt.model.Token;
|
||||
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||
import ru.micord.ervu.service.InteractionService;
|
||||
|
||||
import static ervu.enums.FileStatusCode.*;
|
||||
import static ervu.enums.FileStatusCode.FILE_CLEAN;
|
||||
import static ervu.enums.FileStatusCode.FILE_INFECTED;
|
||||
import static ervu.enums.FileStatusCode.FILE_NOT_CHECKED;
|
||||
import static ervu.enums.FileStatusCode.FILE_UPLOADED;
|
||||
import static ru.micord.ervu.util.StringUtils.convertToFio;
|
||||
|
||||
/**
|
||||
|
|
@ -40,7 +50,7 @@ import static ru.micord.ervu.util.StringUtils.convertToFio;
|
|||
*/
|
||||
@Service
|
||||
public class EmployeeInfoFileUploadService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class);
|
||||
private static final String FORMAT = "dd.MM.yyyy HH:mm:ss";
|
||||
|
||||
private final WebDavClient webDavClient;
|
||||
|
|
@ -48,46 +58,50 @@ public class EmployeeInfoFileUploadService {
|
|||
private final KafkaTemplate<String, String> kafkaTemplate;
|
||||
private final InteractionService interactionService;
|
||||
private final UlDataService ulDataService;
|
||||
private final JwtTokenService jwtTokenService;
|
||||
|
||||
@Value("${av.kafka.message.topic.name}")
|
||||
private String kafkaTopicName;
|
||||
@Value("${file.webdav.upload.url:http://localhost:5757}")
|
||||
private String url;
|
||||
|
||||
public EmployeeInfoFileUploadService(
|
||||
WebDavClient webDavClient,
|
||||
EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService,
|
||||
@Qualifier("avTemplate") KafkaTemplate<String, String> kafkaTemplate,
|
||||
InteractionService interactionService,
|
||||
UlDataService ulDataService, JwtTokenService jwtTokenService) {
|
||||
UlDataService ulDataService) {
|
||||
this.webDavClient = webDavClient;
|
||||
this.employeeInfoKafkaMessageService = employeeInfoKafkaMessageService;
|
||||
this.kafkaTemplate = kafkaTemplate;
|
||||
this.interactionService = interactionService;
|
||||
this.ulDataService = ulDataService;
|
||||
this.jwtTokenService = jwtTokenService;
|
||||
}
|
||||
|
||||
public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType, String authToken, String offset) {
|
||||
String fileUploadUrl = this.url + "/" + getNewFilename(multipartFile.getOriginalFilename());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType,
|
||||
String offset, UserIdsPair userIdsPair) {
|
||||
|
||||
if (this.webDavClient.uploadFile(fileUploadUrl, multipartFile)) {
|
||||
FileStatus fileStatus = new FileStatus();
|
||||
fileStatus.setStatus("Загрузка");
|
||||
if (!isValid(multipartFile)) {
|
||||
return false;
|
||||
}
|
||||
String fileId = UUID.randomUUID().toString();
|
||||
String fileName = multipartFile.getOriginalFilename();
|
||||
EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(formType);
|
||||
String esiaUserId = userIdsPair.getEsiaUserId();
|
||||
String ervuId = userIdsPair.getErvuId();
|
||||
String accessToken = EsiaTokensStore.getAccessToken(esiaUserId);
|
||||
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
||||
PersonModel personModel = employeeModel.getPerson();
|
||||
|
||||
String fileUploadUrl = this.webDavClient.uploadFile(multipartFile);
|
||||
FileStatus fileStatus = new FileStatus();
|
||||
fileStatus.setStatus(fileUploadUrl == null ? "Невозможно проверить файл ЛК РП" : "Загрузка");
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
|
||||
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
|
||||
convertToFio(personModel.getFirstName(), personModel.getMiddleName(), personModel.getLastName()),
|
||||
ervuId);
|
||||
|
||||
if (fileUploadUrl != null) {
|
||||
fileStatus.setCode(FILE_UPLOADED.getCode());
|
||||
fileStatus.setDescription("Файл принят до проверки на вирусы");
|
||||
String fileId = UUID.randomUUID().toString();
|
||||
String fileName = multipartFile.getOriginalFilename();
|
||||
EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(formType);
|
||||
Token token = jwtTokenService.getToken(authToken);
|
||||
String[] ids = token.getUserAccountId().split(":");
|
||||
String userId = ids[0];
|
||||
String ervuId = ids[1];
|
||||
String accessToken = EsiaTokensStore.getAccessToken(userId);
|
||||
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
|
||||
PersonModel personModel = employeeModel.getPerson();
|
||||
String departureDateTime = now.format(DateTimeFormatter.ofPattern(FORMAT));
|
||||
String jsonMessage = getJsonKafkaMessage(
|
||||
employeeInfoKafkaMessageService.getKafkaMessage(
|
||||
|
|
@ -100,56 +114,74 @@ public class EmployeeInfoFileUploadService {
|
|||
offset,
|
||||
fileStatus,
|
||||
ervuId,
|
||||
userId,
|
||||
esiaUserId,
|
||||
personModel
|
||||
)
|
||||
)
|
||||
);
|
||||
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
|
||||
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
|
||||
convertToFio(personModel.getFirstName(), personModel.getMiddleName(), personModel.getLastName()),
|
||||
ervuId);
|
||||
return sendMessage(jsonMessage);
|
||||
}
|
||||
else {
|
||||
logger.error("Fail upload file: {}", multipartFile.getOriginalFilename());
|
||||
LOGGER.error("Failed to upload file: {}", fileName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValid(MultipartFile multipartFile) {
|
||||
|
||||
if (multipartFile == null || multipartFile.getOriginalFilename() == null) {
|
||||
return false;
|
||||
}
|
||||
String fileName = multipartFile.getOriginalFilename();
|
||||
try {
|
||||
String contentType = new Tika().detect(multipartFile.getBytes());
|
||||
MimeTypes defaultMimeTypes = MimeTypes.getDefaultMimeTypes();
|
||||
MimeType mimeType = defaultMimeTypes.forName(contentType);
|
||||
boolean isCsv = mimeType.getType().equals(MediaType.TEXT_PLAIN)
|
||||
&& fileName.toLowerCase(Locale.getDefault()).endsWith(".csv");
|
||||
|
||||
if (!isCsv) {
|
||||
LOGGER.info("Trying to upload file={} with wrong mime type={}",
|
||||
fileName, mimeType
|
||||
);
|
||||
}
|
||||
return isCsv;
|
||||
}
|
||||
catch (MimeTypeException e) {
|
||||
LOGGER.error(
|
||||
"Couldn't get mime type from bytes for file=" + fileName, e);
|
||||
return false;
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOGGER.error("Error while checking file type, file=" + fileName,
|
||||
e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendMessage(String message) {
|
||||
ProducerRecord<String, String> record = new ProducerRecord<>(this.kafkaTopicName, message);
|
||||
record.headers().add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||
record.headers()
|
||||
.add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
try {
|
||||
this.kafkaTemplate.send(record).get();
|
||||
logger.debug("Success send record: {}", record);
|
||||
LOGGER.debug("Successfully sent record: {}", record);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception) {
|
||||
logger.error("Fail send message.", exception);
|
||||
LOGGER.error("Failed to send message", exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getNewFilename(String oldFilename) {
|
||||
return UUID.randomUUID() + getFileExtension(oldFilename);
|
||||
}
|
||||
|
||||
private String getFileExtension(String filename) {
|
||||
int lastIndexOf = filename.lastIndexOf(".");
|
||||
if (lastIndexOf == -1) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(lastIndexOf);
|
||||
}
|
||||
|
||||
private String getJsonKafkaMessage(EmployeeInfoKafkaMessage employeeInfoKafkaMessage) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
return mapper.writeValueAsString(employeeInfoKafkaMessage);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(String.format("Fail get json from: %s", employeeInfoKafkaMessage), e);
|
||||
throw new JsonParsingException(String.format("Fail get json from: %s", employeeInfoKafkaMessage), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +205,7 @@ public class EmployeeInfoFileUploadService {
|
|||
}
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(String.format("Fail get json from: %s", kafkaMessage), e);
|
||||
throw new JsonParsingException(String.format("Fail get json from: %s", kafkaMessage), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
package ervu.service.scheduler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import ervu.client.fileupload.WebDavClient;
|
||||
import net.javacrumbs.shedlock.core.SchedulerLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -11,15 +16,11 @@ import org.springframework.stereotype.Service;
|
|||
*/
|
||||
@Service
|
||||
public class WebDavSchedulerService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WebDavSchedulerService.class);
|
||||
private final WebDavClient webDavClient;
|
||||
|
||||
@Value("${file.webdav.upload.url}")
|
||||
private String url;
|
||||
@Value("${file.webdav.upload.username}")
|
||||
private String username;
|
||||
@Value("${file.webdav.upload.password}")
|
||||
private String password;
|
||||
@Value("${webdav.urls}")
|
||||
private String[] urls;
|
||||
@Value("${file.webdav.lifetime.seconds:300}")
|
||||
private long fileLifetime;
|
||||
@Value("${file.webdav.extensions:}")
|
||||
|
|
@ -30,8 +31,15 @@ public class WebDavSchedulerService {
|
|||
}
|
||||
|
||||
@Scheduled(cron = "${webdav.cleanup.cron:0 0 0 * * *}")
|
||||
@SchedulerLock
|
||||
@SchedulerLock(name = "webDavOldFilesCleaning")
|
||||
public void deleteOldFiles() {
|
||||
webDavClient.deleteFilesOlderThan(fileLifetime, url, username, password, fileExtensions);
|
||||
Arrays.stream(urls).forEach(url -> {
|
||||
try {
|
||||
webDavClient.deleteFilesOlderThan(fileLifetime, url, fileExtensions);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOGGER.error("Failed to clean up WebDAV on {}", url, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package ru.micord.ervu.exception;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class JsonParsingException extends RuntimeException {
|
||||
|
||||
public JsonParsingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JsonParsingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public JsonParsingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package ru.micord.ervu.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
|
||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(
|
||||
MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
AccessDeniedException accessDeniedException) throws IOException {
|
||||
|
||||
if (response.isCommitted()) {
|
||||
LOGGER.trace("Did not write to response since already committed");
|
||||
return;
|
||||
}
|
||||
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write("\"" + HttpStatus.FORBIDDEN.getReasonPhrase() + "\"");
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import org.springframework.security.config.annotation.authentication.configurati
|
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
|
|
@ -25,7 +24,6 @@ import ru.micord.ervu.security.webbpm.jwt.JwtMatcher;
|
|||
import ru.micord.ervu.security.webbpm.jwt.UnauthorizedEntryPoint;
|
||||
import ru.micord.ervu.security.webbpm.jwt.filter.JwtAuthenticationFilter;
|
||||
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
||||
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
||||
|
||||
import static ru.micord.ervu.security.SecurityConstants.ESIA_LOGOUT;
|
||||
|
||||
|
|
@ -33,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"
|
||||
"/version", "/esia/url", "/esia/auth", "esia/refresh", "/esia/logout",
|
||||
};
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
|
@ -51,7 +49,8 @@ public class SecurityConfig {
|
|||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http,
|
||||
CookieCsrfTokenRepository tokenRepository)
|
||||
CookieCsrfTokenRepository tokenRepository,
|
||||
UnauthorizedEntryPoint entryPoint)
|
||||
throws Exception {
|
||||
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
|
||||
delegate.setCsrfRequestAttributeName(null);
|
||||
|
|
@ -69,7 +68,8 @@ public class SecurityConfig {
|
|||
.logout((logout) -> logout.logoutUrl(ESIA_LOGOUT)
|
||||
.logoutSuccessHandler(new LogoutSuccessHandler(tokenRepository, esiaAuthService)))
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(entryPoint())
|
||||
.authenticationEntryPoint(entryPoint)
|
||||
.accessDeniedHandler(new AccessDeniedHandlerImpl())
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
|
|
@ -88,8 +88,9 @@ public class SecurityConfig {
|
|||
return tokenRepository;
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint entryPoint() {
|
||||
return new UnauthorizedEntryPoint();
|
||||
@Bean
|
||||
public UnauthorizedEntryPoint entryPoint(SecurityHelper securityHelper) {
|
||||
return new UnauthorizedEntryPoint(securityHelper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -105,9 +106,10 @@ public class SecurityConfig {
|
|||
|
||||
@Bean
|
||||
public JwtAuthenticationFilter jwtAuthenticationFilter(SecurityHelper securityHelper,
|
||||
AuthenticationManager manager) {
|
||||
AuthenticationManager manager,
|
||||
UnauthorizedEntryPoint entryPoint) {
|
||||
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(
|
||||
new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper);
|
||||
new JwtMatcher("/**", PERMIT_ALL), entryPoint, securityHelper);
|
||||
jwtAuthenticationFilter.setAuthenticationManager(manager);
|
||||
return jwtAuthenticationFilter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@ public class EsiaController {
|
|||
return esiaAuthService.generateAuthCodeUrl();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/esia/auth", params = "code")
|
||||
public ResponseEntity<?> esiaAuth(@RequestParam("code") String code, HttpServletRequest request, HttpServletResponse response) {
|
||||
return esiaAuthService.getEsiaTokensByCode(code, request, response);
|
||||
@GetMapping(value = "/esia/auth")
|
||||
public ResponseEntity<?> esiaAuth(@RequestParam(value = "code", required = false) String code,
|
||||
@RequestParam(value = "error", required = false) String error, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return esiaAuthService.getEsiaTokensByCode(code, error, request, response);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/esia/refresh")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package ru.micord.ervu.security.esia.exception;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class EsiaException extends RuntimeException{
|
||||
|
||||
public EsiaException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public EsiaException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public EsiaException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,13 +14,14 @@ import java.time.format.DateTimeFormatter;
|
|||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.service.okopf.OkopfService;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import ru.micord.ervu.security.esia.exception.EsiaException;
|
||||
import ru.micord.ervu.security.esia.token.EsiaTokensStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -50,6 +51,8 @@ import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
|||
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
||||
import ru.micord.ervu.security.webbpm.jwt.model.Token;
|
||||
|
||||
import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
|
|
@ -70,9 +73,6 @@ public class EsiaAuthService {
|
|||
private OkopfService okopfService;
|
||||
@Autowired
|
||||
private SecurityHelper securityHelper;
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Value("${ervu.kafka.org.reply.topic}")
|
||||
private String requestReplyTopic;
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ public class EsiaAuthService {
|
|||
return buildUrl(url, params);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +152,18 @@ public class EsiaAuthService {
|
|||
return uriBuilder.toString();
|
||||
}
|
||||
|
||||
public ResponseEntity<?> getEsiaTokensByCode(String esiaAuthCode, HttpServletRequest request, HttpServletResponse response) {
|
||||
public ResponseEntity<?> getEsiaTokensByCode(String esiaAuthCode, String error,
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
if (error != null && !error.equals("null")) {
|
||||
return new ResponseEntity<>(
|
||||
"Произошла неизвестная ошибка. Обратитесь к системному администратору",
|
||||
HttpStatus.FORBIDDEN
|
||||
);
|
||||
}
|
||||
String esiaAccessTokenStr = null;
|
||||
String prnOid = null;
|
||||
Long expiresIn = null;
|
||||
boolean hasRole = false;
|
||||
try {
|
||||
String clientId = esiaConfig.getClientId();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
|
||||
|
|
@ -206,27 +217,21 @@ public class EsiaAuthService {
|
|||
tokenResponse != null ? tokenResponse.getError_description() : "response is empty";
|
||||
throw new IllegalStateException("Esia response error. " + errMsg);
|
||||
}
|
||||
String esiaAccessTokenStr = tokenResponse.getAccess_token();
|
||||
esiaAccessTokenStr = tokenResponse.getAccess_token();
|
||||
String esiaRefreshTokenStr = tokenResponse.getRefresh_token();
|
||||
|
||||
boolean hasRole = ulDataService.checkRole(esiaAccessTokenStr);
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
||||
String prnOid = esiaAccessToken.getSbj_id();
|
||||
String ervuId = getErvuId(esiaAccessTokenStr, prnOid);
|
||||
Long expiresIn = tokenResponse.getExpires_in();
|
||||
prnOid = esiaAccessToken.getSbj_id();
|
||||
expiresIn = tokenResponse.getExpires_in();
|
||||
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
EsiaTokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId, hasRole);
|
||||
int expiry = tokenResponse.getExpires_in().intValue();
|
||||
securityHelper.addAccessCookies(response,token.getValue(), expiry);
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
JwtAuthentication jwtAuthentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
|
||||
esiaAccessToken.getSbj_id(), token.getValue());
|
||||
authenticationManager.authenticate(jwtAuthentication);
|
||||
context.setAuthentication(jwtAuthentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
try {
|
||||
hasRole = ulDataService.checkRole(esiaAccessTokenStr);
|
||||
String ervuId = getErvuId(esiaAccessTokenStr, prnOid);
|
||||
createTokenAndAddCookie(response, prnOid, ervuId, hasRole, expiresIn);
|
||||
if (!hasRole) {
|
||||
LOGGER.error("The user with id = " + prnOid + " does not have the required role");
|
||||
return new ResponseEntity<>(
|
||||
|
|
@ -237,7 +242,14 @@ public class EsiaAuthService {
|
|||
return ResponseEntity.ok("Authentication successful");
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
createTokenAndAddCookie(response, prnOid, null, hasRole , expiresIn);
|
||||
String messageId = getMessageId(e);
|
||||
String messageWithId = String.format("[%s] %s", messageId, e.getMessage());
|
||||
LOGGER.error(messageWithId, e);
|
||||
return new ResponseEntity<>(
|
||||
"Произошла ошибка " + messageId + ". Обратитесь к системному администратору",
|
||||
HttpStatus.FORBIDDEN
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -302,20 +314,10 @@ public class EsiaAuthService {
|
|||
EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
EsiaTokensStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn);
|
||||
String ervuId = getErvuId(esiaAccessTokenStr, prnOid);
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId, true);
|
||||
int expiry = tokenResponse.getExpires_in().intValue();
|
||||
securityHelper.addAccessCookies(response, token.getValue(), expiry);
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
JwtAuthentication jwtAuthentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
|
||||
esiaAccessToken.getSbj_id(), token.getValue());
|
||||
authenticationManager.authenticate(jwtAuthentication);
|
||||
context.setAuthentication(jwtAuthentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuId, true, expiresIn);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -341,13 +343,13 @@ public class EsiaAuthService {
|
|||
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void errorHandler(HttpResponse<?> httpResponse) {
|
||||
if (httpResponse.statusCode() != 200) {
|
||||
throw new RuntimeException(httpResponse.statusCode() + " " + httpResponse.body());
|
||||
throw new EsiaException(httpResponse.statusCode() + " " + httpResponse.body());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +368,7 @@ public class EsiaAuthService {
|
|||
return buildUrl(url, params);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -384,12 +386,12 @@ public class EsiaAuthService {
|
|||
String ervuId = ervuOrgResponse.getData().getOrgId_ERVU();
|
||||
|
||||
if (!StringUtils.hasText(ervuId)) {
|
||||
throw new RuntimeException("No ervuId for prnOid = " + prnOid);
|
||||
throw new EsiaException("No ervuId for prnOid = " + prnOid);
|
||||
}
|
||||
return ervuId;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new EsiaException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -448,4 +450,24 @@ public class EsiaAuthService {
|
|||
employee.setOrgOid(employeeModel.getOrgOid());
|
||||
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);
|
||||
securityHelper.addAccessCookies(response, token.getValue(), expiresIn.intValue());
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
|
||||
userId, token.getValue());
|
||||
context.setAuthentication(authentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@ public class JwtAuthentication implements Authentication {
|
|||
private final Authentication authentication;
|
||||
private final String token;
|
||||
|
||||
private final UserIdsPair userIdsPair;
|
||||
|
||||
public JwtAuthentication(Authentication authentication, String userAccountId, String token) {
|
||||
this.userAccountId = userAccountId;
|
||||
this.userIdsPair = new UserIdsPair(userAccountId);
|
||||
this.authentication = authentication;
|
||||
this.token = token;
|
||||
}
|
||||
|
|
@ -31,6 +34,10 @@ public class JwtAuthentication implements Authentication {
|
|||
return userAccountId;
|
||||
}
|
||||
|
||||
public UserIdsPair getUserIdsPair() {
|
||||
return userIdsPair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return authentication.getAuthorities();
|
||||
|
|
|
|||
|
|
@ -48,22 +48,20 @@ public class JwtAuthenticationProvider implements AuthenticationProvider {
|
|||
throw new BadCredentialsException("Authentication Failed.", e);
|
||||
}
|
||||
|
||||
if (jwtTokenService.isValid(token)) {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(
|
||||
REFERENCE_REQUEST);
|
||||
if (request == null) {
|
||||
throw new IllegalStateException("No request found in request attributes");
|
||||
}
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(
|
||||
REFERENCE_REQUEST);
|
||||
if (request == null) {
|
||||
throw new IllegalStateException("No request found in request attributes");
|
||||
}
|
||||
|
||||
if (request.getRequestURI().endsWith("esia/logout") || token.getHasRole()) {
|
||||
UsernamePasswordAuthenticationToken pwdToken =
|
||||
UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
if (jwtTokenService.isValid(token) && token.getHasRole()) {
|
||||
UsernamePasswordAuthenticationToken pwdToken =
|
||||
UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue());
|
||||
}
|
||||
return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue());
|
||||
}
|
||||
|
||||
throw new BadCredentialsException(
|
||||
|
|
|
|||
|
|
@ -6,12 +6,19 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;
|
||||
|
||||
/**
|
||||
* {@link AuthenticationEntryPoint} that rejects all requests with an unauthorized error message.
|
||||
*/
|
||||
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
private final SecurityHelper securityHelper;
|
||||
|
||||
public UnauthorizedEntryPoint(SecurityHelper securityHelper) {
|
||||
this.securityHelper = securityHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException {
|
||||
|
|
@ -21,9 +28,11 @@ public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
|
|||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
else {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
|
||||
"Unauthorized: Authentication token was either missing or invalid."
|
||||
);
|
||||
securityHelper.clearAccessCookies(response);
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter()
|
||||
.write("\"Unauthorized: Authentication token was either missing or invalid.\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package ru.micord.ervu.security.webbpm.jwt;
|
||||
|
||||
public class UserIdsPair {
|
||||
private final String esiaUserId;
|
||||
private final String ervuId;
|
||||
|
||||
public UserIdsPair(String idsConcatenated) {
|
||||
|
||||
if (idsConcatenated == null) {
|
||||
this.esiaUserId = null;
|
||||
this.ervuId = null;
|
||||
}
|
||||
else {
|
||||
String[] ids = idsConcatenated.split(":");
|
||||
this.esiaUserId = ids[0];
|
||||
this.ervuId = ids.length == 2 ? ids[1] : null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getEsiaUserId() {
|
||||
return esiaUserId;
|
||||
}
|
||||
|
||||
public String getErvuId() {
|
||||
return ervuId;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
package ru.micord.ervu.security.webbpm.jwt.util;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
|
||||
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
|
||||
|
||||
|
||||
public final class SecurityUtil {
|
||||
public static final String AUTH_TOKEN = "auth_token";
|
||||
|
|
@ -18,4 +24,18 @@ public final class SecurityUtil {
|
|||
Cookie cookie = WebUtils.getCookie(httpRequest, AUTH_TOKEN);
|
||||
return cookie != null ? cookie.getValue() : null;
|
||||
}
|
||||
|
||||
public static UserIdsPair getUserIdsPair() {
|
||||
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
|
||||
.map(a -> ((JwtAuthentication) a).getUserIdsPair())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static String getCurrentUsername() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth != null && auth.isAuthenticated()) {
|
||||
return auth.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
config.md
11
config.md
|
|
@ -790,9 +790,14 @@ JBPM использует 3 корневых категории логирова
|
|||
- `ERVU_FILE_UPLOAD_MAX_FILE_SIZE` - определяет максимальный размер загружаемого файла в байтах. Указывает предел размера для каждого индивидуального файла, который может быть загружен. Если файл превышает этот размер, загрузка будет прервана, и может быть вызвано исключение.
|
||||
- `ERVU_FILE_UPLOAD_MAX_REQUEST_SIZE` - устанавливает максимальный общий размер всех файлов в одном многозадачном запросе в байтах. Это ограничение на весь запрос, включающий данные и файлы. Если общий размер запроса превышает этот параметр, загрузка файлов будет остановлена.
|
||||
- `ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD` - указывает размер (в байтах), при достижении которого файл будет записан во временное хранилище на диск. Это позволяет улучшить производительность, исключая непосредственную запись мелких файлов на диск, если они не превышают указанного порога. Файлы, меньшие этого значения, могут быть сохранены в памяти.
|
||||
- `FILE_WEBDAV_UPLOAD_URL` - url для подключения к WebDav
|
||||
- `FILE_WEBDAV_UPLOAD_USERNAME` - логин пользователя для подключения к WebDav
|
||||
- `FILE_WEBDAV_UPLOAD_PASSWORD` - пароль пользователя для подключения к WebDav
|
||||
- `WEBDAV_URLS` - список url для подключения к WebDav
|
||||
- `WEBDAV_USERNAME` - логин пользователя для подключения к WebDav
|
||||
- `WEBDAV_PASSWORD` - пароль пользователя для подключения к WebDav
|
||||
- `WEBDAV_BAD_SERVERS_CACHE_EXPIRE_SECONDS` - время жизни кэша адресов недоступных серверов WebDav (секунды)
|
||||
- `WEBDAV_CLEANUP_CRON` - крон по очистке WebDav
|
||||
- `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, используемый для клиентских подключений
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ DB_APP_HOST=10.10.31.119
|
|||
DB_APP_PORT=5432
|
||||
DB_APP_NAME=ervu_lkrp_ul
|
||||
|
||||
FILE_WEBDAV_UPLOAD_URL=https://ervu-webdav.k8s.micord.ru
|
||||
FILE_WEBDAV_UPLOAD_USERNAME=test
|
||||
FILE_WEBDAV_UPLOAD_PASSWORD=test
|
||||
AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload
|
||||
AV_KAFKA_BOOTSTRAP_SERVERS=http://10.10.31.11:32609
|
||||
AV_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
|
||||
|
|
@ -52,6 +49,10 @@ ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD=0
|
|||
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *
|
||||
COOKIE_PATH=/ul
|
||||
|
||||
WEBDAV_URLS=https://ervu-webdav.k8s.micord.ru
|
||||
WEBDAV_USERNAME=test
|
||||
WEBDAV_PASSWORD=test
|
||||
WEBDAV_BAD_SERVERS_CACHE_EXPIRE_SECONDS=120
|
||||
WEBDAV_CLEANUP_CRON=0 0 0 * * *
|
||||
WEBDAV_RETRY_DELAY=500
|
||||
FILE_WEBDAV_LIFETIME_SECONDS=300
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ http {
|
|||
|
||||
sendfile on;
|
||||
|
||||
server_tokens off;
|
||||
|
||||
gzip on;
|
||||
|
||||
# text/html doesn't need to be defined there, it's compressed always
|
||||
|
|
@ -79,6 +81,8 @@ http {
|
|||
index index.html;
|
||||
try_files $uri @index;
|
||||
|
||||
add_header Content-Security-Policy "frame-ancestors 'none'; default-src 'self'; script-src 'self'; style-src 'unsafe-inline' 'self' data:; font-src 'self' data:; img-src 'self' data:;";
|
||||
|
||||
# Media: images, icons, video, audio, HTC
|
||||
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|mp3|ogg|ogv|webm|htc|woff2|woff|ttf)$ {
|
||||
expires 1M;
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@
|
|||
<property name="security.password.regex" value="^((?=(.*\d){1,})(?=.*[a-zа-яё])(?=.*[A-ZА-ЯЁ]).{8,})$"/>
|
||||
<property name="fias.enable" value="false"/>
|
||||
<property name="com.arjuna.ats.arjuna.allowMultipleLastResources" value="true"/>
|
||||
<property name="file.webdav.upload.url" value="https://ervu-webdav.k8s.micord.ru"/>
|
||||
<property name="file.webdav.upload.username" value="test"/>
|
||||
<property name="file.webdav.upload.password" value="test"/>
|
||||
<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="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"/>
|
||||
|
|
@ -97,6 +97,7 @@
|
|||
<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"/>
|
||||
</system-properties>
|
||||
<management>
|
||||
<audit-log>
|
||||
|
|
|
|||
171
config/tomcat/etc/tomcat/server.xml
Normal file
171
config/tomcat/etc/tomcat/server.xml
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<!-- Note: A "Server" is not itself a "Container", so you may not
|
||||
define subcomponents such as "Valves" at this level.
|
||||
Documentation at /docs/config/server.html
|
||||
-->
|
||||
<Server port="8005" shutdown="SHUTDOWN">
|
||||
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
|
||||
<!-- Security listener. Documentation at /docs/config/listeners.html
|
||||
<Listener className="org.apache.catalina.security.SecurityListener" />
|
||||
-->
|
||||
<!-- APR library loader. Documentation at /docs/apr.html -->
|
||||
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
|
||||
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
|
||||
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
|
||||
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
|
||||
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
|
||||
|
||||
<!-- Global JNDI resources
|
||||
Documentation at /docs/jndi-resources-howto.html
|
||||
-->
|
||||
<GlobalNamingResources>
|
||||
<!-- Editable user database that can also be used by
|
||||
UserDatabaseRealm to authenticate users
|
||||
-->
|
||||
<Resource name="UserDatabase" auth="Container"
|
||||
type="org.apache.catalina.UserDatabase"
|
||||
description="User database that can be updated and saved"
|
||||
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
|
||||
pathname="conf/tomcat-users.xml" />
|
||||
</GlobalNamingResources>
|
||||
|
||||
<!-- A "Service" is a collection of one or more "Connectors" that share
|
||||
a single "Container" Note: A "Service" is not itself a "Container",
|
||||
so you may not define subcomponents such as "Valves" at this level.
|
||||
Documentation at /docs/config/service.html
|
||||
-->
|
||||
<Service name="Catalina">
|
||||
|
||||
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
|
||||
<!--
|
||||
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
|
||||
maxThreads="150" minSpareThreads="4"/>
|
||||
-->
|
||||
|
||||
|
||||
<!-- A "Connector" represents an endpoint by which requests are received
|
||||
and responses are returned. Documentation at :
|
||||
Java HTTP Connector: /docs/config/http.html
|
||||
Java AJP Connector: /docs/config/ajp.html
|
||||
APR (HTTP/AJP) Connector: /docs/apr.html
|
||||
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
|
||||
-->
|
||||
<Connector port="8080" protocol="HTTP/1.1"
|
||||
connectionTimeout="20000"
|
||||
redirectPort="8443" />
|
||||
<!-- A "Connector" using the shared thread pool-->
|
||||
<!--
|
||||
<Connector executor="tomcatThreadPool"
|
||||
port="8080" protocol="HTTP/1.1"
|
||||
connectionTimeout="20000"
|
||||
redirectPort="8443" />
|
||||
-->
|
||||
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443
|
||||
This connector uses the NIO implementation. The default
|
||||
SSLImplementation will depend on the presence of the APR/native
|
||||
library and the useOpenSSL attribute of the AprLifecycleListener.
|
||||
Either JSSE or OpenSSL style configuration may be used regardless of
|
||||
the SSLImplementation selected. JSSE style configuration is used below.
|
||||
-->
|
||||
<!--
|
||||
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
|
||||
maxThreads="150" SSLEnabled="true">
|
||||
<SSLHostConfig>
|
||||
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
|
||||
type="RSA" />
|
||||
</SSLHostConfig>
|
||||
</Connector>
|
||||
-->
|
||||
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
|
||||
This connector uses the APR/native implementation which always uses
|
||||
OpenSSL for TLS.
|
||||
Either JSSE or OpenSSL style configuration may be used. OpenSSL style
|
||||
configuration is used below.
|
||||
-->
|
||||
<!--
|
||||
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
|
||||
maxThreads="150" SSLEnabled="true" >
|
||||
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
|
||||
<SSLHostConfig>
|
||||
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
|
||||
certificateFile="conf/localhost-rsa-cert.pem"
|
||||
certificateChainFile="conf/localhost-rsa-chain.pem"
|
||||
type="RSA" />
|
||||
</SSLHostConfig>
|
||||
</Connector>
|
||||
-->
|
||||
|
||||
<!-- Define an AJP 1.3 Connector on port 8009 -->
|
||||
<!--
|
||||
<Connector protocol="AJP/1.3"
|
||||
address="::1"
|
||||
port="8009"
|
||||
redirectPort="8443" />
|
||||
-->
|
||||
|
||||
<!-- An Engine represents the entry point (within Catalina) that processes
|
||||
every request. The Engine implementation for Tomcat stand alone
|
||||
analyzes the HTTP headers included with the request, and passes them
|
||||
on to the appropriate Host (virtual host).
|
||||
Documentation at /docs/config/engine.html -->
|
||||
|
||||
<!-- You should set jvmRoute to support load-balancing via AJP ie :
|
||||
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
|
||||
-->
|
||||
<Engine name="Catalina" defaultHost="localhost">
|
||||
|
||||
<!--For clustering, please take a look at documentation at:
|
||||
/docs/cluster-howto.html (simple how to)
|
||||
/docs/config/cluster.html (reference documentation) -->
|
||||
<!--
|
||||
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
|
||||
-->
|
||||
|
||||
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
|
||||
via a brute-force attack -->
|
||||
<Realm className="org.apache.catalina.realm.LockOutRealm">
|
||||
<!-- This Realm uses the UserDatabase configured in the global JNDI
|
||||
resources under the key "UserDatabase". Any edits
|
||||
that are performed against this UserDatabase are immediately
|
||||
available for use by the Realm. -->
|
||||
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
|
||||
resourceName="UserDatabase"/>
|
||||
</Realm>
|
||||
|
||||
<Host name="localhost" appBase="webapps"
|
||||
errorReportValveClass="org.apache.catalina.valves.JsonErrorReportValve"
|
||||
unpackWARs="true" autoDeploy="true">
|
||||
|
||||
<!-- SingleSignOn valve, share authentication between web applications
|
||||
Documentation at: /docs/config/valve.html -->
|
||||
<!--
|
||||
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
|
||||
-->
|
||||
|
||||
<!-- Access log processes all example.
|
||||
Documentation at: /docs/config/valve.html
|
||||
Note: The pattern used is equivalent to using pattern="common" -->
|
||||
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
|
||||
prefix="localhost_access_log" suffix=".txt"
|
||||
pattern="%h %l %u %t "%r" %s %b" />
|
||||
|
||||
</Host>
|
||||
</Engine>
|
||||
</Service>
|
||||
</Server>
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<version>1.9.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
3
frontend/browser_check.js
Normal file
3
frontend/browser_check.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
document.getElementById("browser-check-info").hidden = navigator.userAgent.indexOf("Chromium GOST") > -1 || navigator.userAgent.indexOf("YaBrowser") > -1;
|
||||
});
|
||||
|
|
@ -4,6 +4,10 @@
|
|||
<link rel="stylesheet" type="text/css" href="src/resources/landing/home.css">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'unsafe-inline' 'self' data:; font-src 'self' data:; img-src 'self' data:"/>
|
||||
<meta name="referrer" content="strict-origin-when-cross-origin"/>
|
||||
<script src="browser_check.js"></script>
|
||||
</head>
|
||||
<body class="ul">
|
||||
|
||||
|
|
@ -25,9 +29,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById("browser-check-info").hidden = navigator.userAgent.indexOf("Chromium GOST") > -1 || navigator.userAgent.indexOf("YaBrowser") > -1;
|
||||
</script>
|
||||
<div class="container-inside">
|
||||
<div class="list-group lk-what">
|
||||
<div>
|
||||
|
|
@ -84,7 +85,7 @@
|
|||
<div class="section-group">
|
||||
<div class="icon-pers">
|
||||
Ежегодное предоставление списка граждан мужского пола, подлежащих первоначальной постановке на воинский учет в год достижения ими возраста 17 лет
|
||||
<div class="muted">Срок передачи сведений: <span class="detailed">ежегодно, в срок до 1 ноября</span></div>
|
||||
<div class="muted">Срок передачи сведений: <span class="detailed">ежегодно, в срок до 1 ноября</span></div>
|
||||
</div>
|
||||
<div class="icon-building">
|
||||
Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету
|
||||
|
|
@ -109,10 +110,10 @@
|
|||
<span class="info"></span>Если в файле будут ошибĸи, данные не будут приняты Реестром и выгрузĸу сведений придется повторить
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group lk-docs">
|
||||
<div class="list-group lk-docs">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -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.185.0",
|
||||
"@webbpm/base-package": "3.187.1",
|
||||
"ag-grid-angular": "29.0.0-micord.4",
|
||||
"ag-grid-community": "29.0.0-micord.4",
|
||||
"angular-calendar": "0.28.28",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ru.micord.ervu.lkrp</groupId>
|
||||
<artifactId>ul</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<version>1.9.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ru.micord.ervu.lkrp.ul</groupId>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
--size-text-title: 40px;
|
||||
--size-text-subtitle: 32px;
|
||||
--size-text-primary: 20px;
|
||||
--size-text-secondary: 16px;
|
||||
--size-text-secondary: 16px;
|
||||
|
||||
--indent-huge: 72px;
|
||||
--indent-big: 52px;
|
||||
|
|
@ -94,7 +94,7 @@ body {
|
|||
-ms-text-size-adjust: 100%;
|
||||
-moz-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
|
|
@ -102,17 +102,17 @@ body {
|
|||
}
|
||||
|
||||
a {
|
||||
color: var(--color-link);
|
||||
color: var(--color-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:is(:hover, :focus, :active) {
|
||||
color: var(--color-link-hover);
|
||||
color: var(--color-link-hover);
|
||||
}
|
||||
|
||||
button, a.btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
color: var(--white);
|
||||
font-family: 'InterL';
|
||||
font-size: var(--size-text-secondary);
|
||||
|
|
@ -120,12 +120,12 @@ button, a.btn {
|
|||
height: 48px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
background: var(--color-link);
|
||||
background: var(--color-link);
|
||||
}
|
||||
button:is(:hover, :focus, :active),
|
||||
button:is(:hover, :focus, :active),
|
||||
a.btn:is(:hover, :focus, :active) {
|
||||
background: var(--color-link-hover);
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn.btn-secondary {
|
||||
color: var(--color-link);
|
||||
|
|
@ -142,7 +142,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-family: 'InterSB';
|
||||
font-family: 'InterSB';
|
||||
min-height: var(--h-header);
|
||||
padding: 0 var(--w-screen);
|
||||
border: 0;
|
||||
|
|
@ -227,7 +227,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
font-family: 'GolosB';
|
||||
font-size: var(--size-text-subtitle);
|
||||
margin-top: var(--indent-mini);
|
||||
}
|
||||
}
|
||||
.container-inside .block .block-description {
|
||||
font-family: 'Golos';
|
||||
font-size: var(--size-text-primary);
|
||||
|
|
@ -307,7 +307,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .paragraph .icon-text {
|
||||
background: url(img/svg/text-32x32.svg) no-repeat 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
:is(.ul, .fl) .container-inside .list-group .list > div {
|
||||
position: relative;
|
||||
|
|
@ -323,7 +323,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
height: 24px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .list > div.esia::after {
|
||||
background: url(img/svg/esia-24x24.svg) no-repeat 0 0;
|
||||
}
|
||||
|
|
@ -426,7 +426,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
position: absolute;
|
||||
font-family: 'InterB';
|
||||
top: -50px;
|
||||
left: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .pass-list > div:nth-child(1)::after {
|
||||
content: "1";
|
||||
|
|
@ -588,7 +588,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
background-color: var(--bg-form);
|
||||
}
|
||||
:is(.fl) .container-inside .list-group.lk-msg span {
|
||||
background: url(img/svg/info.svg) no-repeat 0 4px;
|
||||
background: url(img/svg/info.svg) no-repeat 0 4px;
|
||||
}
|
||||
:is(.fl) .container-inside .list-group.lk-limits .subtitle {
|
||||
margin-bottom: 0;
|
||||
|
|
@ -626,7 +626,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
|
||||
.browser-check-content {
|
||||
font-family: 'Golos';
|
||||
font-size: var(--size-text-secondary);
|
||||
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);
|
||||
}
|
||||
|
|
@ -636,7 +636,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
}
|
||||
.browser-check-text::before {
|
||||
position: absolute;
|
||||
content: url(../img/svg/info.svg);
|
||||
content: url(img/svg/info.svg);
|
||||
left: 0;
|
||||
top: calc((100% - 24px) / 2);
|
||||
}
|
||||
|
|
@ -707,7 +707,7 @@ a.btn:is(:hover, :focus, :active) {
|
|||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .pass-list > div::after {
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
:is(.ul, .fl) .container-inside .list-group .pass-list > div + div {
|
||||
margin-left: 0;
|
||||
margin-top: var(--indent-mini);
|
||||
|
|
@ -736,5 +736,5 @@ a.btn:is(:hover, :focus, :active) {
|
|||
:is(.ul, .fl) .container-inside .list-group .docs-list > div + div {
|
||||
margin-left: 0;
|
||||
margin-top: var(--indent-mini);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
frontend/src/resources/landing/img/svg/info.svg
Normal file
5
frontend/src/resources/landing/img/svg/info.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<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="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" fill="#C64E1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 11C12.5523 11 13 11.4477 13 12V16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16V12C11 11.4477 11.4477 11 12 11Z" fill="#C64E1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 8C11 7.44772 11.4477 7 12 7H12.01C12.5623 7 13.01 7.44772 13.01 8C13.01 8.55228 12.5623 9 12.01 9H12C11.4477 9 11 8.55228 11 8Z" fill="#C64E1B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 787 B |
|
|
@ -34,18 +34,8 @@ export abstract class AuthGuard implements CanActivate {
|
|||
if (isAccess) {
|
||||
return true;
|
||||
}
|
||||
else if (error) {
|
||||
let userErrorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору';
|
||||
let errorCode = this.extractCode(errorDescription);
|
||||
if (errorCode) {
|
||||
userErrorMessage = EsiaErrorDetail.getDescription(errorCode);
|
||||
}
|
||||
let errorMessage = error + ', error description = ' + errorDescription;
|
||||
this.messageService.error(userErrorMessage)
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
else if (code) {
|
||||
const params = new HttpParams().set('code', code);
|
||||
if (code || error) {
|
||||
const params = new HttpParams().set('code', code).set('error', error);
|
||||
this.httpClient.get("esia/auth",
|
||||
{
|
||||
params: params, responseType: 'text', observe: 'response', headers: {
|
||||
|
|
@ -61,8 +51,20 @@ export abstract class AuthGuard implements CanActivate {
|
|||
let errorMessage = reason.error.messages != null
|
||||
? reason.error.messages
|
||||
: reason.error.replaceAll('\\', '');
|
||||
this.messageService.error(errorMessage);
|
||||
console.error(reason);
|
||||
if (error) {
|
||||
errorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору';
|
||||
let errorCode = this.extractCode(errorDescription);
|
||||
if (errorCode) {
|
||||
errorMessage = EsiaErrorDetail.getDescription(errorCode);
|
||||
}
|
||||
let consoleError = error + ', error description = ' + errorDescription;
|
||||
this.messageService.error(errorMessage);
|
||||
console.error(consoleError);
|
||||
}
|
||||
else {
|
||||
this.messageService.error(errorMessage);
|
||||
console.error(reason);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ module.exports = {
|
|||
new CopyWebpackPlugin([
|
||||
{from: 'index.webpack.html', to: 'index.html'},
|
||||
{from: 'home.html', to: 'home.html'},
|
||||
{from: 'browser_check.js', to: 'browser_check.js'},
|
||||
{from: 'src/resources/img/progress.gif', to: 'src/resources/img/progress.gif'},
|
||||
{from: 'src/resources/img/logo.png', to: 'src/resources/img/logo.png'},
|
||||
{from: 'src/resources/app-config.json', to: 'src/resources/app-config.json'},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ru.cg.webbpm.packages.base</groupId>
|
||||
<artifactId>resources</artifactId>
|
||||
<version>3.185.0</version>
|
||||
<version>3.187.1</version>
|
||||
<organization>
|
||||
<name>Micord</name>
|
||||
</organization>
|
||||
|
|
@ -28,13 +28,13 @@
|
|||
<jooq.version>3.19.3</jooq.version>
|
||||
<jupiter.version>5.10.2</jupiter.version>
|
||||
<enforcer.manageVersions>true</enforcer.manageVersions>
|
||||
<webbpm-platform.version>3.185.0</webbpm-platform.version>
|
||||
<webbpm-platform.version>3.187.1</webbpm-platform.version>
|
||||
<h2.version>1.4.200</h2.version>
|
||||
<build.timestamp>1107112530</build.timestamp>
|
||||
<build.timestamp>0113064203</build.timestamp>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<junit.platform.version>1.10.0</junit.platform.version>
|
||||
<enforcer.manageExclusions>true</enforcer.manageExclusions>
|
||||
<revision>3.185.0</revision>
|
||||
<revision>3.187.1</revision>
|
||||
<metadata.ts.filename>typescript.metadata.json</metadata.ts.filename>
|
||||
<package.repository.url>https://repo.micord.ru</package.repository.url>
|
||||
<maven.build.timestamp.format>MMddHHmmss</maven.build.timestamp.format>
|
||||
|
|
@ -47,19 +47,19 @@
|
|||
<dependency>
|
||||
<groupId>ru.cg.webbpm.packages.base</groupId>
|
||||
<artifactId>converters</artifactId>
|
||||
<version>3.185.0</version>
|
||||
<version>3.187.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.cg.webbpm.packages.base</groupId>
|
||||
<artifactId>backend</artifactId>
|
||||
<version>3.185.0</version>
|
||||
<version>3.187.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ru.cg.webbpm.packages.base</groupId>
|
||||
<artifactId>frontend</artifactId>
|
||||
<version>3.185.0</version>
|
||||
<version>3.187.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
<ul>
|
||||
|
||||
<li>Образец внешней ссылки: <code>https://www.wildberries.ru/catalog/${sku}/detail.aspx</code></li>
|
||||
<li>Образец внутренней ссылки: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.185.0</code></li>
|
||||
<li>Образец внутренней ссылки: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.187.1</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
<description>Base webbpm package</description>
|
||||
<groupId>ru.cg.webbpm.packages.base</groupId>
|
||||
<artifactId>resources</artifactId>
|
||||
<version>3.185.0</version>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<version>3.187.1</version>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
|
||||
<backendModule>
|
||||
<groupId>ru.cg.webbpm.packages.base</groupId>
|
||||
<artifactId>backend</artifactId>
|
||||
<version>3.185.0</version>
|
||||
<version>3.187.1</version>
|
||||
</backendModule>
|
||||
<frontendModule>
|
||||
<packageName>@webbpm/base-package</packageName>
|
||||
<version>3.185.0</version>
|
||||
<version>3.187.1</version>
|
||||
</frontendModule>
|
||||
</packageInfo>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_отмены.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_очистки_фильтра.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_удаления.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_загрузки.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_вызова_ошибки.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<documentation>component/buttons/Кнопка_выполнения_бизнес-процесса.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_выполнения_SQL.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_для_фильтрации.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_навигации.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_сохранения.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_выбора.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_подписи.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/Кнопка_запуска_бизнес-процесса.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/reporting/Кнопка_печати_из_графа_сущности.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/buttons/reporting/Кнопка_печати_отчета_из_формы.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Сворачиваемая_панель.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Диалог.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Контейнер_с_кнопками.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Группа_полей.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Набор_фильтров.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Форма.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Горизонтальный_контейнер.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Контейнер_вкладок.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Вкладка.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Вертикальный_контейнер.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/containers/Окно.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/editable-grids/EditableGrid.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/autocomplete</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/check-box</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/combo-box</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/date-time-picker</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/money-field</localization>
|
||||
<internal>true</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/number-field</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/one-to-many</localization>
|
||||
<internal>true</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/one-to-many</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/read-only</localization>
|
||||
<internal>true</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
<category>editable-grids</category>
|
||||
<internal>true</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>Статичный_выпадающий_список_колонки_таблицы.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/text-area</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/text-field</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
<localization>META-INF/components/localization/editable-grids/time-picker</localization>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/ФИАС.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
<value>
|
||||
<expanded>true</expanded>
|
||||
<implRef type="JAVA">
|
||||
<className>FiasAddressServiceImpl</className>
|
||||
<className>GarAddressServiceImpl</className>
|
||||
<packageName>service.field</packageName>
|
||||
</implRef>
|
||||
<complex>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Поле_ввода_с_подбором_значения.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Флаг.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Выпадающий_список.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Дата.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/EditableOneToMany.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Файл.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Файл.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/ManyToMany.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/ManyToManyField.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Денежное_поле.html</documentation>
|
||||
<internal>true</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Числовое_поле.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/OneToMany.html</documentation>
|
||||
<internal>true</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/OneToMany.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Переключатель.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/SignVerification.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Статичный_выпадающий_список.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Статичный_переключатель.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Текст.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Многострочное_поле.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Текстовое_поле.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/Время.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
<documentation>component/fields/TreeField.html</documentation>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.185.0</studioVersion>
|
||||
<studioVersion>3.187.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.185.0</value>
|
||||
<value>3.187.1</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue