Merge branch 'develop' into feature/SUPPORT-8593_marker_verify

This commit is contained in:
Eduard Tihomirov 2024-11-26 16:19:56 +03:00
commit b6072d9bf4
172 changed files with 3812 additions and 6689 deletions

2
.gitignore vendored
View file

@ -65,3 +65,5 @@ npm-debug.log
#Sublime project files
*.sublime-project
*.sublime-workspace
sync-backend.ps1
sync-frontend.ps1

View file

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

View file

@ -1,11 +1,16 @@
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Root application context
@ -30,10 +35,15 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableWebMvc
@EnableScheduling
public class AppConfig {
public class AppConfig implements WebMvcConfigurer {
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ResourceHttpMessageConverter());
}
}

View file

@ -4,15 +4,15 @@ import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import ru.micord.ervu.converter.SummonsResponseDataConverter;
import ru.micord.ervu.dto.SubpoenaRequestDto;
import ru.micord.ervu.dto.SubpoenaResponseDto;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import proto.ervu.rp.summons.SummonsResponseData;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
/**
* @author gulnaz
@ -20,7 +20,6 @@ import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
@RestController
public class ErvuDataController {
private final JwtTokenService jwtTokenService;
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
private final SummonsResponseDataConverter converter;
@ -30,20 +29,23 @@ public class ErvuDataController {
private String recruitReplyTopic;
public ErvuDataController(
JwtTokenService jwtTokenService,
@Qualifier("subpoena") ReplyingKafkaService<Object, Bytes> replyingKafkaService,
@Qualifier("recruit") ReplyingKafkaService<Object, Bytes> replyingKafkaService,
SummonsResponseDataConverter converter) {
this.jwtTokenService = jwtTokenService;
this.replyingKafkaService = replyingKafkaService;
this.converter = converter;
}
@PostMapping(
value = "/get-data",
@GetMapping(
value = "/recruit",
produces = MediaType.APPLICATION_JSON_VALUE
)
public SubpoenaResponseDto getData() {
SubpoenaRequestDto subpoenaRequestDto = new SubpoenaRequestDto(jwtTokenService.getErvuId());
String ervuId = SecurityUtil.getErvuId();
if (ervuId == null) {
return new SubpoenaResponseDto.Builder().build();
}
SubpoenaRequestDto subpoenaRequestDto = new SubpoenaRequestDto(ervuId);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(recruitRequestTopic,
recruitReplyTopic, subpoenaRequestDto).get();

View file

@ -0,0 +1,66 @@
package ru.micord.ervu.controller;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData;
import ru.micord.ervu.dto.ExtractRequestDto;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
/**
* @author gulnaz
*/
@RestController
public class ExtractController {
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
@Value("${ervu.kafka.registry.extract.request.topic}")
private String registryExtractRequestTopic;
@Value("${ervu.kafka.registry.extract.reply.topic}")
private String registryExtractReplyTopic;
public ExtractController(ReplyingKafkaService<Object, Bytes> replyingKafkaService) {
this.replyingKafkaService = replyingKafkaService;
}
@GetMapping(value = "/extract/{formatRegistry}")
public ResponseEntity<Resource> getExtract(@PathVariable String formatRegistry) {
String ervuId = SecurityUtil.getErvuId();
if (ervuId == null) {
return ResponseEntity.noContent().build();
}
ExtractRequestDto request = new ExtractRequestDto(ervuId, formatRegistry);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic,
registryExtractReplyTopic, request).get();
try {
ResponseData responseData = ResponseData.parseFrom(reply);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
String encodedFilename = URLEncoder.encode(extractRegistry.getFileName(), StandardCharsets.UTF_8);
InputStreamResource resource = new InputStreamResource(extractRegistry.getFile().newInput());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedFilename)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Failed to parse data", e);
}
}
}

View file

@ -1,13 +1,10 @@
package ru.micord.ervu.converter;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import ru.micord.ervu.dto.SubpoenaResponseDto;
import ru.micord.ervu.util.DateUtil;
import org.springframework.stereotype.Component;
import proto.ervu.rp.summons.MeasuresTemporary;
import proto.ervu.rp.summons.ResponseDataAddress;
@ -16,6 +13,7 @@ import proto.ervu.rp.summons.SummonsResponseData;
import static ru.micord.ervu.util.DateUtil.convertToLocalDate;
import static java.util.Objects.requireNonNull;
import static ru.micord.ervu.util.DateUtil.getDaysTill;
/**
* @author gulnaz
@ -48,6 +46,8 @@ public class SummonsResponseDataConverter {
if (summonsInfoOpt.isPresent()) {
SummonsInfo summonsInfo = summonsInfoOpt.get();
String estimatedDate = summonsInfo.getEstimatedDateSummons();
builder.seriesAndNumber(summonsInfo.getSummonsSeries(), summonsInfo.getSummonsNumber())
.visitDateTime(summonsInfo.getVisitDate(), summonsInfo.getVisitTime())
.militaryCommissariatName(summonsInfo.getMilitaryCommissariatName())
@ -60,12 +60,11 @@ public class SummonsResponseDataConverter {
.reasonName(summonsInfo.getReasonName())
.summonsStatusName(summonsInfo.getSummonsStatusName())
.deliveryTypeSummonsName(summonsInfo.getDeliveryTypeSummonsName())
.estimatedDateSummons(summonsInfo.getEstimatedDateSummons())
.estimatedDateSummons(estimatedDate)
.recruitmentStatusCode(
Integer.parseInt(responseData.getRecruitmentInfo().getRecruitmentStatusCode()))
.recruitmentStartDate(responseData.getRecruitmentInfo().getRecruitmentStart())
.daysToAppearance((int) ChronoUnit.DAYS.between(
LocalDate.now(), DateUtil.convertToLocalDate(summonsInfo.getEstimatedDateSummons())))
.daysToAppearance(getDaysTill(convertToLocalDate(estimatedDate)))
.otherRestrictionCount(-1);
summonsInfo.getMeasuresTemporaryPackagesList()

View file

@ -1,7 +0,0 @@
package ru.micord.ervu.dto;
/**
* @author gulnaz
*/
public record FileData(String fileName, String fileType, byte[] file) {
}

View file

@ -3,6 +3,7 @@ package ru.micord.ervu.dto;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.util.StringUtils.hasText;
import static ru.micord.ervu.util.DateUtil.convertToLocalDate;
import static ru.micord.ervu.util.DateUtil.convertToString;
@ -55,7 +56,15 @@ public record SubpoenaResponseDto(String personName, String birthDate, String do
}
public Builder personName(String firstName, String middleName, String lastName) {
this.personName = lastName + " " + firstName + " " + middleName;
StringBuilder stringBuilder = new StringBuilder(lastName)
.append(" ")
.append(firstName);
if (hasText(middleName)) {
stringBuilder.append(" ")
.append(middleName);
}
this.personName = stringBuilder.toString();
return this;
}
@ -70,7 +79,7 @@ public record SubpoenaResponseDto(String personName, String birthDate, String do
}
public Builder docNumber(String series, String number) {
this.docNumber = series + " " + number;
this.docNumber = hasText(series) ? series + " " + number : number;
return this;
}

View file

@ -147,7 +147,7 @@ public class ReplyingKafkaConfig {
return replyingKafkaTemplate;
}
@Bean("subpoena")
@Bean("recruit")
public ReplyingKafkaTemplate<String, Object, Bytes> subpoenaReplyingKafkaTemplate(
ProducerFactory<String, Object> subpoenaProducerFactory,
ConcurrentMessageListenerContainer<String, Bytes> subpoenaReplyContainer) {

View file

@ -16,7 +16,7 @@ import ru.micord.ervu.dto.SubpoenaRequestDto;
* @author gulnaz
*/
@Service
@Qualifier("subpoena")
@Qualifier("recruit")
public class SubpoenaReplyingKafkaService extends BaseReplyingKafkaService<Object, Bytes> {
private final ReplyingKafkaTemplate<String, Object, Bytes> replyingKafkaTemplate;
@ -26,7 +26,7 @@ public class SubpoenaReplyingKafkaService extends BaseReplyingKafkaService<Objec
@Value("${ervu.kafka.extract.header.class}")
private String extractHeaderClass;
public SubpoenaReplyingKafkaService(@Qualifier("subpoena")
public SubpoenaReplyingKafkaService(@Qualifier("recruit")
ReplyingKafkaTemplate<String, Object, Bytes> replyingKafkaTemplate) {
this.replyingKafkaTemplate = replyingKafkaTemplate;
}

View file

@ -59,6 +59,8 @@ public class SecurityConfig {
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
tokenRepository.setCookieName(TokenConstants.CSRF_TOKEN_NAME);
tokenRepository.setHeaderName(TokenConstants.CSRF_HEADER_NAME);
tokenRepository.setCookiePath("/");
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName(null);

View file

@ -0,0 +1,10 @@
package ru.micord.ervu.security;
public final class TokenConstants {
public static String CSRF_TOKEN_NAME = "XSRF-TOKEN-LKRP-FL";
public static String CSRF_HEADER_NAME = "X-XSRF-TOKEN-LKRP-FL";
private TokenConstants() {
//must be empty
}
}

View file

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

View file

@ -1,6 +1,7 @@
package ru.micord.ervu.security.esia.service;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
@ -14,21 +15,24 @@ import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContext;
import ru.micord.ervu.kafka.model.Document;
import ru.micord.ervu.kafka.model.ErrorData;
import ru.micord.ervu.kafka.model.Person;
import ru.micord.ervu.kafka.model.Response;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
@ -49,11 +53,16 @@ import ru.micord.ervu.security.esia.model.PersonModel;
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
*/
@Service
public class EsiaAuthService {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final Long EXPIRES_IN = 3600L;
@Autowired
private ObjectMapper objectMapper;
@ -150,8 +159,16 @@ 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) {
try {
if (error != null) {
createTokenAndAddCookie(response, null, null, EXPIRES_IN);
return new ResponseEntity<>(
"Произошла неизвестная ошибка. Обратитесь к системному администратору",
HttpStatus.FORBIDDEN
);
}
String clientId = esiaConfig.getClientId();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx");
ZonedDateTime dt = ZonedDateTime.now();
@ -215,19 +232,7 @@ public class EsiaAuthService {
TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn);
Response ervuIdResponse = getErvuIdResponse(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId());
int expiry = tokenResponse.getExpires_in().intValue();
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
response.addCookie(accessCookie);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
esiaAccessToken.getSbj_id(), token.getValue());
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
response.addCookie(authMarkerCookie);
createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuIdResponse.getErvuId(), expiresIn);
if (ervuIdResponse.getErrorData() != null) {
return new ResponseEntity<>(
"Доступ запрещен. " + ervuIdResponse.getErrorData().getName(),
@ -237,7 +242,14 @@ public class EsiaAuthService {
return ResponseEntity.ok("Authentication successful");
}
catch (Exception e) {
throw new RuntimeException(e);
createTokenAndAddCookie(response, null, null, EXPIRES_IN);
String messageId = getMessageId(e);
String messageWithId = String.format("[%s] %s", messageId, e.getMessage());
LOGGER.error(messageWithId, e);
return new ResponseEntity<>(
"Произошла ошибка " + messageId + ". Обратитесь к системному администратору",
HttpStatus.FORBIDDEN
);
}
}
@ -299,19 +311,7 @@ public class EsiaAuthService {
TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
TokensStore.addRefreshToken(prnOid, newRefreshToken, expiresIn);
Response ervuIdResponse = getErvuIdResponse(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId());
int expiry = tokenResponse.getExpires_in().intValue();
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
response.addCookie(accessCookie);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
SecurityContext context = SecurityContextHolder.createEmptyContext();
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
esiaAccessToken.getSbj_id(), token.getValue());
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
response.addCookie(authMarkerCookie);
createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuIdResponse.getErvuId(), expiresIn);
}
catch (Exception e) {
throw new RuntimeException(e);
@ -398,9 +398,32 @@ public class EsiaAuthService {
return person;
}
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,
Long expiresIn) {
Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId);
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiresIn.intValue());
response.addCookie(accessCookie);
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);
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiresIn.intValue());
response.addCookie(authMarkerCookie);
}
private String verifyToken(String accessToken) {
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
EsiaHeader esiaHeader = ulDataService.readHeader(accessToken);
EsiaAccessToken esiaAccessToken = personalDataService.readToken(accessToken);
EsiaHeader esiaHeader = personalDataService.readHeader(accessToken);
if (!esiaHeader.getSbt().equals("access")) {
return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid";
}

View file

@ -7,54 +7,54 @@ import java.util.concurrent.ConcurrentHashMap;
* @author Eduard Tihomirov
*/
public class TokensStore {
private static final Map<String, ExpiringToken> accessTokensMap = new ConcurrentHashMap<>();
private static final Map<String, ExpiringToken> refreshTokensMap = new ConcurrentHashMap<>();
private static final Map<String, ExpiringToken> ACCESS_TOKENS_MAP = new ConcurrentHashMap<>();
private static final Map<String, ExpiringToken> REFRESH_TOKENS_MAP = new ConcurrentHashMap<>();
public static void addAccessToken(String prnOid, String token, long expiresIn) {
if (token != null) {
long expiryTime = System.currentTimeMillis() + 1000L * expiresIn;
accessTokensMap.put(prnOid, new ExpiringToken(token, expiryTime));
ACCESS_TOKENS_MAP.put(prnOid, new ExpiringToken(token, expiryTime));
}
}
public static String getAccessToken(String prnOid) {
return accessTokensMap.get(prnOid).getAccessToken();
return ACCESS_TOKENS_MAP.get(prnOid).getAccessToken();
}
public static void removeExpiredAccessToken() {
for (String key : accessTokensMap.keySet()) {
ExpiringToken token = accessTokensMap.get(key);
for (String key : ACCESS_TOKENS_MAP.keySet()) {
ExpiringToken token = ACCESS_TOKENS_MAP.get(key);
if (token != null && token.isExpired()) {
accessTokensMap.remove(key);
ACCESS_TOKENS_MAP.remove(key);
}
}
}
public static void removeExpiredRefreshToken() {
for (String key : refreshTokensMap.keySet()) {
ExpiringToken token = refreshTokensMap.get(key);
for (String key : REFRESH_TOKENS_MAP.keySet()) {
ExpiringToken token = REFRESH_TOKENS_MAP.get(key);
if (token != null && token.isExpired()) {
refreshTokensMap.remove(key);
REFRESH_TOKENS_MAP.remove(key);
}
}
}
public static void removeAccessToken(String prnOid) {
accessTokensMap.remove(prnOid);
ACCESS_TOKENS_MAP.remove(prnOid);
}
public static void addRefreshToken(String prnOid, String token, long expiresIn) {
if (token != null) {
long expiryTime = System.currentTimeMillis() + 1000L * expiresIn;
refreshTokensMap.put(prnOid, new ExpiringToken(token, expiryTime));
REFRESH_TOKENS_MAP.put(prnOid, new ExpiringToken(token, expiryTime));
}
}
public static String getRefreshToken(String prnOid) {
return refreshTokensMap.get(prnOid).getAccessToken();
return REFRESH_TOKENS_MAP.get(prnOid).getAccessToken();
}
public static void removeRefreshToken(String prnOid) {
refreshTokensMap.remove(prnOid);
REFRESH_TOKENS_MAP.remove(prnOid);
}
}

View file

@ -32,7 +32,7 @@ public class JwtTokenService {
@Value("${webbpm.security.token.issuer:#{null}}")
private final String tokenIssuerName =
ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID;
private final SecretKey SIGNING_KEY;
private final SecretKey signingKey;
@Autowired
private HttpServletRequest request;
@ -41,7 +41,7 @@ public class JwtTokenService {
public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}")
String secretKey) {
byte[] encodedKey = Base64.getDecoder().decode(secretKey);
this.SIGNING_KEY = Keys.hmacShaKeyFor(encodedKey);
this.signingKey = Keys.hmacShaKeyFor(encodedKey);
}
public Token createAccessToken(String userAccountId, Long expiresIn, String ervuId) {
@ -52,7 +52,7 @@ public class JwtTokenService {
.setIssuer(tokenIssuerName)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(expirationDate)
.signWith(SIGNING_KEY)
.signWith(signingKey)
.compact();
return new Token(userAccountId + ":" + ervuId, tokenIssuerName, expirationDate, value);
}
@ -72,7 +72,7 @@ public class JwtTokenService {
public Token getToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SIGNING_KEY)
.setSigningKey(signingKey)
.parseClaimsJws(token)
.getBody();

View file

@ -2,12 +2,16 @@ package ru.micord.ervu.security.webbpm.jwt.util;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
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.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.util.WebUtils;
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
@ -42,4 +46,22 @@ public final class SecurityUtil {
Cookie cookie = WebUtils.getCookie(httpRequest, AUTH_TOKEN);
return cookie != null ? cookie.getValue() : null;
}
public static String getErvuId() {
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.map(a -> ((JwtAuthentication) a).getUserAccountId())
.map(userAccountId -> {
String ervuId = userAccountId.split(":")[1];
return "null".equals(ervuId) ? null : ervuId;
})
.orElse(null);
}
public static String getCurrentUsername() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
return auth.getName();
}
return null;
}
}

View file

@ -1,61 +0,0 @@
package ru.micord.ervu.service.rpc;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import ru.micord.ervu.dto.FileData;
import ru.micord.ervu.dto.ExtractRequestDto;
import rtl.pgs.ervu.proto.ExtractRegistry;
import rtl.pgs.ervu.proto.ResponseData;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.cg.webbpm.modules.standard_annotations.validation.NotNull;
import ru.cg.webbpm.modules.webkit.annotations.RpcCall;
import ru.cg.webbpm.modules.webkit.annotations.RpcService;
import ru.cg.webbpm.modules.webkit.beans.Behavior;
/**
* @author gulnaz
*/
@RpcService
public class ExtractRpcService extends Behavior {
private final JwtTokenService jwtTokenService;
private final ReplyingKafkaService<Object, Bytes> replyingKafkaService;
@Value("${ervu.kafka.registry.extract.request.topic}")
private String registryExtractRequestTopic;
@Value("${ervu.kafka.registry.extract.reply.topic}")
private String registryExtractReplyTopic;
@NotNull()
public String formatExtractRegistry;
public ExtractRpcService(
JwtTokenService jwtTokenService,
@Qualifier("subpoena") ReplyingKafkaService<Object, Bytes> replyingKafkaService) {
this.jwtTokenService = jwtTokenService;
this.replyingKafkaService = replyingKafkaService;
}
@RpcCall
public FileData getExtract() {
ExtractRequestDto request = new ExtractRequestDto(jwtTokenService.getErvuId(),
formatExtractRegistry);
byte[] reply = replyingKafkaService.sendMessageAndGetReply(registryExtractRequestTopic,
registryExtractReplyTopic, request).get();
try {
ResponseData responseData = ResponseData.parseFrom(reply);
ExtractRegistry extractRegistry = responseData.getDataRegistryInformation()
.getExtractRegistry();
return new FileData(extractRegistry.getFileName(), extractRegistry.getFileType(),
extractRegistry.getFile().toByteArray());
}
catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Failed to parse data", e);
}
}
}

View file

@ -2,6 +2,7 @@ package ru.micord.ervu.util;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import org.springframework.util.StringUtils;
@ -27,4 +28,9 @@ public final class DateUtil {
public static String convertToString(LocalDate date, DateTimeFormatter formatter) {
return date == null ? "" : date.format(formatter);
}
public static int getDaysTill(LocalDate end) {
return (int) ChronoUnit.DAYS.between(
LocalDate.now(), end == null ? LocalDate.now() : end);
}
}

View file

@ -1,11 +1,11 @@
TZ=Europe/Moscow
# App datasource
DB_APP_USERNAME=ervu-lkrp-fl
DB_APP_PASSWORD=ervu-lkrp-fl
DB_APP_USERNAME=ervu_lkrp_fl
DB_APP_PASSWORD=ervu_lkrp_fl
DB_APP_HOST=10.10.31.119
DB_APP_PORT=5432
DB_APP_NAME=ervu-lkrp-fl
DB_APP_NAME=ervu_lkrp_fl
ESIA_SCOPES=snils, fullname, birthdate, id_doc
ESIA_BASE_URI=https://esia-portal1.test.gosuslugi.ru/
@ -30,7 +30,7 @@ ERVU_KAFKA_RECRUIT_REPLY_TOPIC=ervu.recruit.info.response
ERVU_KAFKA_RECRUIT_HEADER_CLASS=Request@urn://rostelekom.ru/RP-SummonsTR/1.0.5
ERVU_KAFKA_REGISTRY_EXTRACT_REQUEST_TOPIC=ervu.extract.info.request
ERVU_KAFKA_REGISTRY_EXTRACT_REPLY_TOPIC=ervu.extract.info.response
ERVU_KAFKA_EXTRACT_HEADER_CLASS=Request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3
ERVU_KAFKA_EXTRACT_HEADER_CLASS=request@urn://rostelekom.ru/ERVU-extractFromRegistryTR/1.0.3
ERVU_KAFKA_DOC_LOGIN_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule
ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * *

View file

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

105
ervu_lkrp_fl-openapi.yaml Normal file
View file

@ -0,0 +1,105 @@
openapi: 3.0.3
info:
title: ervu-lkrp-fl API
description: API сервиса ervu-lkrp-fl
version: 1.9.1
servers:
- url: https://fl-lkrp-ervu-test.pgs.rtlabs.ru
paths:
/esia/auth:
get:
summary: Получение маркера доступа
operationId: esiaAuth
description: Получение маркера доступа в обмен на код от ЕСИА и создание внутреннего токена
parameters:
- name: code
in: query
required: true
description: Код, присланный ЕСИА после успешной авторизации
schema:
type: string
responses:
"200":
description: Authentication successful
/esia/person:
get:
summary: Получение информации о пользователе
operationId: getPersonModel
description: Получение информации о пользователе ЕСИА
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
/esia/refresh:
post:
summary: Обновление токена
operationId: refreshToken
description: Обновление токена
responses:
"200":
description: OK
/esia/url:
get:
summary: Получение URL ЕСИА
operationId: getEsiaUrl
description: Получение URL ЕСИА
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
/esia/userfullname:
get:
summary: Получение полного имени пользователя
operationId: getUserFullname
description: Получение полного имени пользователя ЕСИА
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
/extract/{formatRegistry}:
get:
summary: Получение выписки
operationId: getExtract
description: Получение выписки из реестров повесток и воинского учета
parameters:
- name: formatRegistry
in: path
required: true
description: Формат выписки (1 - по воинскому учету; 2 - по повесткам)
schema:
type: string
responses:
"200":
description: OK
headers:
Content-Disposition:
schema:
type: string
example: attachment; filename*=UTF-8''encodedfilename.zip
content:
application/octet-stream:
schema:
type: object
"204":
description: No Content
/recruit:
get:
summary: Получение информации о повестке, временных мерах и воинскому учету
operationId: getData
description: Получение информации о повестке, временных мерах и воинскому учету
responses:
"200":
description: OK
content:
application/json:
schema:
type: object

View file

@ -1,10 +0,0 @@
{
"port": 8000,
"open": false,
"files": [
"./**/*.{html,htm,css,js}"
],
"server": {
"baseDir": "./"
}
}

View file

@ -13,6 +13,16 @@
</div>
<div class="container">
<div id="browser-check-info">
<div class="browser-check-content">
<div class="browser-check-text">
<div class="plain-text">Для обеспечения защищённого соединения с сайтом реестра повесток необходимо установить браузер Яндекс или Chromium GOST.</div>
</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>

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,6 @@
"name": "ervu_lkrp_fl",
"version": "1.0.0",
"scripts": {
"lite": "node ./node_modules/lite-server/bin/lite-server",
"cleanup": "npm run cleanup-ngc && node ./node_modules/rimraf/bin ./build ./dist",
"cleanup-ngc": "node ./node_modules/rimraf/bin ./src/ts/**/*.js ./src/ts/**/*.json ./src/ts/page.routing.ts",
"cleanup-and-ngc": "npm run cleanup && npm run ngc",
@ -26,8 +25,8 @@
"@angular/platform-browser": "7.2.15",
"@angular/platform-browser-dynamic": "7.2.15",
"@angular/router": "7.2.15",
"@ng-bootstrap/ng-bootstrap": "4.1.1",
"@webbpm/base-package": "3.182.0",
"@ng-bootstrap/ng-bootstrap": "4.2.2-micord.1",
"@webbpm/base-package": "3.185.0",
"ag-grid-angular": "29.0.0-micord.4",
"ag-grid-community": "29.0.0-micord.4",
"angular-calendar": "0.28.28",
@ -40,16 +39,16 @@
"core-js": "2.4.1",
"date-fns": "2.29.3",
"downloadjs": "1.4.8",
"eonasdan-bootstrap-datetimepicker": "4.17.47-micord.4",
"eonasdan-bootstrap-datetimepicker": "4.17.47-micord.5",
"esmarttokenjs": "2.2.1-cg",
"font-awesome": "4.7.0",
"google-libphonenumber": "3.0.9",
"inputmask": "5.0.5-cg.2",
"jquery": "3.3.1",
"jquery": "3.7.1",
"js-year-calendar": "1.0.0-cg.2",
"jsgantt-improved": "2.0.10-cg",
"moment": "2.17.1",
"moment-timezone": "0.5.11",
"moment": "2.30.1",
"moment-timezone": "0.5.46",
"ngx-cookie": "3.0.1",
"ngx-international-phone-number": "1.0.6",
"ngx-toastr": "10.2.0-cg",
@ -61,7 +60,7 @@
"systemjs": "0.21.4",
"systemjs-plugin-babel": "0.0.25",
"tslib": "1.9.3",
"zone.js": "0.8.29"
"zone.js": "0.11.8"
},
"devDependencies": {
"@angular-devkit/build-optimizer": "0.13.9",
@ -69,33 +68,33 @@
"@angular/cli": "7.3.9",
"@angular/compiler-cli": "7.2.15",
"@angular/platform-server": "7.2.15",
"@babel/core": "7.9.6",
"@babel/preset-env": "7.9.6",
"@babel/core": "7.18.10",
"@babel/preset-env": "7.18.10",
"@types/bootstrap": "3.3.39",
"@types/jquery": "2.0.49",
"@types/eslint": "7.2.5",
"@types/jquery": "3.5.5",
"@types/node": "7.0.5",
"@types/selectize": "0.12.33",
"ajv": "8.8.2",
"angular-router-loader": "0.8.5",
"angular2-template-loader": "0.6.2",
"babel-loader": "8.1.0",
"babel-loader": "9.1.2",
"codelyzer": "5.2.1",
"copy-webpack-plugin": "5.0.3",
"cross-env": "5.2.1",
"css-loader": "2.1.0",
"css-loader": "6.11.0",
"del": "2.2.2",
"file-loader": "3.0.1",
"html-webpack-plugin": "4.5.2",
"lite-server": "2.3.0",
"mini-css-extract-plugin": "0.6.0",
"mkdirp": "0.5.1",
"raw-loader": "1.0.0",
"style-loader": "0.23.1",
"terser-webpack-plugin": "1.2.4",
"file-loader": "6.2.0",
"html-webpack-plugin": "5.6.0",
"mini-css-extract-plugin": "2.9.1",
"mkdirp": "3.0.1",
"raw-loader": "4.0.2",
"style-loader": "3.3.4",
"terser-webpack-plugin": "5.3.10",
"tslint": "5.13.1",
"typescript": "3.2.4",
"typescript-parser": "2.6.1-cg-fork",
"webpack": "4.32.2",
"webpack-bundle-analyzer": "3.3.2",
"webpack-cli": "3.3.2"
"typescript-parser": "2.6.1-cg.2",
"webpack": "5.90.1",
"webpack-cli": "5.0.2"
}
}

View file

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

View file

@ -4,7 +4,7 @@
"filter_cleanup_interval_hours": 720,
"filter_cleanup_check_period_minutes": 30,
"auth_method": "form",
"enable.version.in.url": "false",
"enable.version.in.url": "%enable.version.in.url%",
"backend.context": "fl",
"guard.confirm_exit": false,
"message_service_error_timeout": "",

View file

@ -1 +1 @@
1.8.2-SNAPSHOT
%project.version%

View file

@ -530,6 +530,10 @@
margin-top: 16px;
}
.webbpm.ervu_lkrp_fl #restriction in-memory-static-grid .ag-header {
display: none;
}
@media only screen and (max-width: 480px) {
.webbpm.ervu_lkrp_fl .warning-text .horizontal-container {
flex-wrap: wrap !important;

View file

@ -187,12 +187,15 @@ body.webbpm.ervu_lkrp_fl {
bottom: 0;
}
.webbpm.ervu_lkrp_fl .container-inside {
display: flex;
flex-direction: column;
font-family: 'Inter';
height: 100%;
padding: 0;
overflow: auto;
}
.webbpm.ervu_lkrp_fl .container-inside > div {
flex: 1;
padding: var(--indent-huge) var(--w-screen);
}

View file

@ -624,6 +624,29 @@ a.btn:is(:hover, :focus, :active) {
color: var(--color-link);
}
.browser-check-content {
font-family: 'Golos';
font-size: var(--size-text-secondary);
padding: var(--indent-mini) var(--w-screen) var(--indent-mini) calc(var(--w-screen) + 38px);
background-color: var(--bg-warn);
}
.browser-check-text {
position: relative;
padding-left: 40px;
}
.browser-check-text::before {
position: absolute;
content: url(../img/svg/info.svg);
left: 0;
top: calc((100% - 24px) / 2);
}
.text-header {
color: var(--color-link);
font-family: 'GolosB';
font-size: var(--size-text-primary);
margin-bottom: 4px;
}
/*@media ((max-width: 780px) or ((orientation: landscape) and (max-device-width : 1024px))) {*/
@media (max-width: 1024px) {
body {

View file

@ -3,9 +3,10 @@ import {
AnalyticalScope,
Behavior,
Event,
NotNull,
Visible
} from "@webbpm/base-package";
import {ExtractRpcService} from "../../../generated/ru/micord/ervu/service/rpc/ExtractRpcService";
import {HttpClient} from "@angular/common/http";
@AnalyticalScope(AbstractButton)
export class ExtractLoadService extends Behavior {
@ -14,34 +15,34 @@ export class ExtractLoadService extends Behavior {
public successEvent: Event<boolean> = new Event<boolean>();
@Visible("false")
public errorEvent: Event<boolean> = new Event<boolean>();
@NotNull()
public formatRegistry: string;
private button: AbstractButton;
private rpc: ExtractRpcService;
private httpClient: HttpClient;
private onClickFunction: Function;
initialize() {
super.initialize();
this.button = this.getScript(AbstractButton);
this.rpc = this.getScript(ExtractRpcService);
this.httpClient = this.injector.get(HttpClient);
this.onClickFunction = () => {
this.rpc.getExtract()
.then(fileData => {
const newBlob = new Blob([fileData['file']],
{ type: fileData['fileType'] });
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(newBlob);
}
else {
const data = window.URL.createObjectURL(newBlob);
const link = document.createElement("a");
link.href = data;
link.download = fileData['fileName'];
link.click();
URL.revokeObjectURL(data);
link.remove();
this.successEvent.trigger();
}
this.httpClient.get('extract/' + this.formatRegistry, {
responseType: 'blob',
observe: 'response'
}).toPromise()
.then((response) => {
const data = window.URL.createObjectURL(response.body);
const link = document.createElement("a");
link.href = data;
const contentDisposition = response.headers.get('Content-Disposition');
const fileNameMatch = contentDisposition.match(/filename\*=?UTF-8''(.+)/i);
link.download = decodeURIComponent(fileNameMatch[1].replace(/\+/g, '%20'));
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(data);
link.remove();
this.successEvent.trigger();
})
.catch(() => {
this.errorEvent.trigger();

View file

@ -12,7 +12,8 @@ import {
PinnedType
} from "@webbpm/base-package";
import {Moment} from "moment";
import moment from "moment-timezone";
import * as moment_ from "moment-timezone";
const moment = moment_;
import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn";
export class StaticColumnInitializer {

View file

@ -13,6 +13,6 @@ export class ValueWithPrefixRenderer implements GridCellValueRenderer {
render(params: ICellRendererParams): HTMLElement | string {
let value = params.valueFormatted ? params.valueFormatted : params.value;
return `${this.label} ${value}`;
return value ? `${this.label} ${value}` : value;
}
}

View file

@ -11,8 +11,7 @@ export class ErvuDataService {
}
public getData(): any {
this.httpClient
.post("get-data", null,
this.httpClient.get("recruit",
{
headers: {
"Content-type": "application/json"

View file

@ -0,0 +1,12 @@
export class EsiaErrorDetail {
private static errors: { [code: string]: string } = {
'ESIA-007071': 'Запрос персональных данных по физическим лицам может быть выполнен только с указанием согласий',
'ESIA-007055': 'Вход в систему осуществляется с неподтвержденной учетной записью',
'ESIA-007036': 'Учетная запись заблокирована',
'ESIA-007008': 'Сервис авторизации в настоящее время не может выполнить запрос из-за большой нагрузки или технических работ на сервере',
};
public static getDescription(code: string): string {
return this.errors[code] || 'Доступ запрещен. Обратитесь к системному администратору. Ошибка ' + code;
}
}

View file

@ -1,4 +1,4 @@
export class TokenConstants {
public static readonly CSRF_TOKEN_NAME = "XSRF-TOKEN";
public static readonly CSRF_HEADER_NAME = "X-XSRF-TOKEN";
public static readonly CSRF_TOKEN_NAME = "XSRF-TOKEN-LKRP-FL";
public static readonly CSRF_HEADER_NAME = "X-XSRF-TOKEN-LKRP-FL";
}

View file

@ -1,8 +1,6 @@
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from "rxjs";
import {CookieService} from "ngx-cookie";
import {tap} from "rxjs/operators";
import {AppConfigService} from "@webbpm/base-package";
@Injectable({providedIn: 'root'})
@ -14,7 +12,7 @@ export class AuthenticationService {
}
checkAuthentication(): Promise<any>{
return this.appConfigService.load().then(value => this.http.get<any>("version").toPromise())
return this.appConfigService.load().then(() => this.http.get<any>("version").toPromise())
}
logout(): Promise<string> {

View file

@ -4,6 +4,7 @@ import {Observable} from "rxjs";
import {HttpClient, HttpParams} from "@angular/common/http";
import {MessagesService} from "@webbpm/base-package";
import {AuthenticationService} from "../authentication.service";
import {EsiaErrorDetail} from "../EsiaErrorDetail";
@Injectable({providedIn:'root'})
export abstract class AuthGuard implements CanActivate {
@ -29,13 +30,8 @@ export abstract class AuthGuard implements CanActivate {
if (isAccess) {
return true;
}
else if (error) {
let errorMessage = error + ', error description =' + errorDescription;
this.messageService.error(errorMessage)
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: {
@ -51,8 +47,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;
}
@ -71,4 +79,10 @@ export abstract class AuthGuard implements CanActivate {
private checkAccess(): boolean {
return this.authenticationService.isAuthenticated();
}
private extractCode(message: string): string | null {
const regex = /ESIA-\d{6}/;
const match = message.match(regex);
return match ? match[0] : null;
}
}

View file

@ -1,26 +1,33 @@
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpXsrfTokenExtractor
} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {CookieService} from "ngx-cookie";
import {TokenConstants} from "../../security/TokenConstants";
@Injectable()
export class AbsoluteUrlCsrfInterceptor implements HttpInterceptor {
constructor(private cookieService: CookieService) {
constructor(private extractor: HttpXsrfTokenExtractor) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let requestToForward = req;
let token = this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) as string;
if (token != null) {
let headers = {};
let headerName = TokenConstants.CSRF_HEADER_NAME;
headers[headerName] = token;
requestToForward = req.clone({setHeaders: headers});
if (req.method === 'GET' || req.method === 'HEAD') {
return next.handle(req);
}
return next.handle(requestToForward);
let token = this.extractor.getToken();
let headerName = TokenConstants.CSRF_HEADER_NAME;
if (token != null && !req.headers.has(headerName)) {
let headers = {};
headers[headerName] = token;
req = req.clone({setHeaders: headers});
}
return next.handle(req);
}
}

View file

@ -12,6 +12,7 @@
"skipLibCheck": true,
"noImplicitAny": false,
"outDir": "build_dev/js",
"allowSyntheticDefaultImports": true,
"typeRoots": [
"node_modules/@types"
],

View file

@ -3,7 +3,6 @@ const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserPlugin = require('terser-webpack-plugin');
@ -14,9 +13,9 @@ function _path(p) {
module.exports = {
mode: 'production',
entry: {
'polyfills': './build/scripts/polyfills.js',
'vendor': './build/scripts/vendor.js',
'main': './build/scripts/main.aot.js'
polyfills: './build/scripts/polyfills.js',
vendor: './build/scripts/vendor.js',
main: './build/scripts/main.aot.js',
},
context: process.cwd(),
@ -24,26 +23,33 @@ module.exports = {
output: {
path: path.join(process.cwd(), './dist'),
filename: '[name].[chunkhash].bundle.js',
chunkFilename: '[id].[chunkhash].chunk.js'
chunkFilename: '[id].[chunkhash].chunk.js',
assetModuleFilename: 'src/resources/[base]',
publicPath: 'auto'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
include: [path.resolve(__dirname, "node_modules")],
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.js$/,
use: {
loader: 'angular-router-loader?aot=true'
}
},
{
test: /\.html$/,
use: {
loader: 'raw-loader'
}
},
{
test: /\.css$/,
@ -56,12 +62,14 @@ module.exports = {
// publicPath: '../'
}
},
"css-loader"
{
loader: 'css-loader'
}
]
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf)$/,
loader: 'file-loader?name=src/resources/[name].[hash].[ext]'
type: 'asset/resource'
}
]
},
@ -69,7 +77,6 @@ module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
@ -83,7 +90,6 @@ module.exports = {
},
plugins: [
// new BundleAnalyzerPlugin(),
new HtmlWebpackPlugin({
template: 'index.webpack.html',
filename: 'index.html',
@ -100,8 +106,8 @@ module.exports = {
]),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
filename: '[name].[fullhash].css',
chunkFilename: '[id].[fullhash].css'
}),
new webpack.ProvidePlugin({
$: "jquery",

View file

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>resources</artifactId>
<version>3.182.0</version>
<version>3.185.0</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.182.0</webbpm-platform.version>
<webbpm-platform.version>3.185.0</webbpm-platform.version>
<h2.version>1.4.200</h2.version>
<build.timestamp>1004163046</build.timestamp>
<build.timestamp>1107112530</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.182.0</revision>
<revision>3.185.0</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.182.0</version>
<version>3.185.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>backend</artifactId>
<version>3.182.0</version>
<version>3.185.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>frontend</artifactId>
<version>3.182.0</version>
<version>3.185.0</version>
<scope>compile</scope>
</dependency>
</dependencies>

View file

@ -60,7 +60,7 @@
<ul>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x435;&#x448;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>https://www.wildberries.ru/catalog/${sku}/detail.aspx</code></li>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x443;&#x442;&#x440;&#x435;&#x43d;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.182.0</code></li>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x443;&#x442;&#x440;&#x435;&#x43d;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.185.0</code></li>
</ul>
</li>
<li>

View file

@ -4,17 +4,17 @@
<description>Base webbpm package</description>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>resources</artifactId>
<version>3.182.0</version>
<studioVersion>3.182.0</studioVersion>
<version>3.185.0</version>
<studioVersion>3.185.0</studioVersion>
<backendModule>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>backend</artifactId>
<version>3.182.0</version>
<version>3.185.0</version>
</backendModule>
<frontendModule>
<packageName>@webbpm/base-package</packageName>
<version>3.182.0</version>
<version>3.185.0</version>
</frontendModule>
</packageInfo>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_отмены.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_очистки_фильтра.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_удаления.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаагрузки.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_вызова_ошибки.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<documentation>component/buttons/Кнопка_выполнения_бизнес-процесса.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_выполнения_SQL.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_для_фильтрации.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаавигации.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_сохранения.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_выбора.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_подписи.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаапуска_бизнес-процесса.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/reporting/Кнопка_печати_из_графа_сущности.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/reporting/Кнопка_печати_отчета_из_формы.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Сворачиваемая_панель.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Диалог.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Контейнер_с_кнопками.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Группа_полей.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Набор_фильтров.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Форма.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Горизонтальный_контейнер.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Контейнер_вкладок.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Вкладка.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Вертикальный_контейнер.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/containers/Окно.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/editable-grids/EditableGrid.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/autocomplete</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/check-box</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/combo-box</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/date-time-picker</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/money-field</localization>
<internal>true</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/number-field</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/one-to-many</localization>
<internal>true</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/one-to-many</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -6,11 +6,11 @@
<localization>META-INF/components/localization/editable-grids/read-only</localization>
<internal>true</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -5,11 +5,11 @@
<category>editable-grids</category>
<internal>true</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>Статичный_выпадающий_список_колонки_таблицы.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/text-area</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/text-field</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<localization>META-INF/components/localization/editable-grids/time-picker</localization>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/ФИАС.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Поле_ввода_с_подбором_значения.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Флаг.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Выпадающий_список.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Дата.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/EditableOneToMany.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Файл.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Файл.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/ManyToMany.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/ManyToManyField.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Денежное_поле.html</documentation>
<internal>true</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/fields/Числовое_поле.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.182.0</studioVersion>
<studioVersion>3.185.0</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.182.0</value>
<value>3.185.0</value>
</entry>
</packageVersions>
</versions>

Some files were not shown because too many files have changed in this diff Show more