diff --git a/backend/pom.xml b/backend/pom.xml index 41971dc..e13cb41 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -5,7 +5,7 @@ ru.micord.ervu.lkrp fl - 1.9.2 + 1.9.3-SNAPSHOT ru.micord.ervu.lkrp.fl backend diff --git a/backend/src/main/java/ru/micord/ervu/converter/SummonsResponseDataConverter.java b/backend/src/main/java/ru/micord/ervu/converter/SummonsResponseDataConverter.java index 63c3e4d..54d2623 100644 --- a/backend/src/main/java/ru/micord/ervu/converter/SummonsResponseDataConverter.java +++ b/backend/src/main/java/ru/micord/ervu/converter/SummonsResponseDataConverter.java @@ -39,9 +39,11 @@ public class SummonsResponseDataConverter { .issueDate(responseData.getIssueDate()) .issueOrg(responseData.getIssueOrg()) .issueIdCode(responseData.getIssueIdCode()) + .militaryCommissariatName(responseData.getRecruitmentInfo().getMilitaryCommissariatName()) .residenceAddress(getAddressByCode(responseData.getAddressesList(), RESIDENCE_ADDRESS_CODE)) .stayAddress(getAddressByCode(responseData.getAddressesList(), STAY_ADDRESS_CODE)) .actualAddress(getAddressByCode(responseData.getAddressesList(), ACTUAL_ADDRESS_CODE)); + Optional summonsInfoOpt = responseData.getSummonsInfosList().stream() // get last subpoena .max(Comparator.comparing( diff --git a/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java b/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java index 5f088ea..e497c26 100644 --- a/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java @@ -25,7 +25,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; @@ -105,10 +104,9 @@ public class SecurityConfig { @Bean public JwtAuthenticationFilter jwtAuthenticationFilter(SecurityHelper securityHelper, - AuthenticationManager manager, - JwtTokenService jwtTokenService) { + AuthenticationManager manager) { JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter( - new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper, jwtTokenService); + new JwtMatcher("/**", PERMIT_ALL), entryPoint(), securityHelper); jwtAuthenticationFilter.setAuthenticationManager(manager); return jwtAuthenticationFilter; } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java index 08c9f82..f8ae592 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java @@ -27,7 +27,7 @@ import ru.micord.ervu.kafka.model.Document; import ru.micord.ervu.kafka.model.Person; import ru.micord.ervu.kafka.model.Response; import ru.micord.ervu.kafka.service.ReplyingKafkaService; -import ru.micord.ervu.security.esia.token.TokensStore; +import ru.micord.ervu.security.esia.token.EsiaTokensStore; import ru.micord.ervu.security.esia.config.EsiaConfig; import ru.micord.ervu.security.esia.model.FormUrlencoded; import ru.micord.ervu.security.esia.model.EsiaAccessToken; @@ -199,8 +199,8 @@ public class EsiaAuthService { EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr); String prnOid = esiaAccessToken.getSbj_id(); Long expiresIn = tokenResponse.getExpires_in(); - TokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); - TokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn); + EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); + EsiaTokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn); Response ervuIdResponse = getErvuIdResponse(esiaAccessTokenStr); Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId()); int expiry = tokenResponse.getExpires_in().intValue(); @@ -276,8 +276,8 @@ public class EsiaAuthService { EsiaAccessToken esiaAccessToken = personalDataService.readToken(esiaAccessTokenStr); String prnOid = esiaAccessToken.getSbj_id(); Long expiresIn = tokenResponse.getExpires_in(); - TokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); - TokensStore.addRefreshToken(prnOid, esiaNewRefreshTokenStr, expiresIn); + EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); + EsiaTokensStore.addRefreshToken(prnOid, esiaNewRefreshTokenStr, expiresIn); Response ervuIdResponse = getErvuIdResponse(esiaAccessTokenStr); Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuIdResponse.getErvuId()); int expiry = tokenResponse.getExpires_in().intValue(); @@ -335,8 +335,8 @@ public class EsiaAuthService { try { securityHelper.clearAccessCookies(response); String userId = jwtTokenService.getUserAccountId(request); - TokensStore.removeAccessToken(userId); - TokensStore.removeRefreshToken(userId); + EsiaTokensStore.removeAccessToken(userId); + EsiaTokensStore.removeRefreshToken(userId); String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl(); String redirectUrl = esiaConfig.getRedirectUrl(); URL url = new URL(logoutUrl); diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/TokensStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java similarity index 73% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/TokensStore.java rename to backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java index 9804b80..7f56cc4 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/TokensStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java @@ -1,12 +1,17 @@ package ru.micord.ervu.security.esia.token; +import java.lang.invoke.MethodHandles; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * @author Eduard Tihomirov */ -public class TokensStore { +public class EsiaTokensStore { + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final Map accessTokensMap = new ConcurrentHashMap<>(); private static final Map refreshTokensMap = new ConcurrentHashMap<>(); @@ -21,6 +26,19 @@ public class TokensStore { return accessTokensMap.get(prnOid).getAccessToken(); } + public static boolean validateAccessToken(String prnOid) { + ExpiringToken token = accessTokensMap.get(prnOid); + if (token == null || token.getAccessToken() == null) { + LOGGER.error("No ESIA access token for prnOid: " + prnOid); + return false; + } + else if (token.isExpired()) { + LOGGER.error("ESIA access token expired for prnOid: " + prnOid); + return false; + } + return true; + } + public static void removeExpiredAccessToken() { for (String key : accessTokensMap.keySet()) { ExpiringToken token = accessTokensMap.get(key); diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/TokensClearShedulerService.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/TokensClearShedulerService.java index 4665295..89c9db4 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/TokensClearShedulerService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/TokensClearShedulerService.java @@ -14,7 +14,7 @@ public class TokensClearShedulerService { @SchedulerLock(name = "clearToken") @Transactional public void load() { - TokensStore.removeExpiredRefreshToken(); - TokensStore.removeExpiredAccessToken(); + EsiaTokensStore.removeExpiredRefreshToken(); + EsiaTokensStore.removeExpiredAccessToken(); } } diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java index f709679..05fb495 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java @@ -2,6 +2,8 @@ package ru.micord.ervu.security.webbpm.jwt; import java.util.Collections; +import javax.servlet.http.HttpServletRequest; + import io.jsonwebtoken.ExpiredJwtException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; @@ -11,9 +13,13 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; +import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST; + @Component public class JwtAuthenticationProvider implements AuthenticationProvider { @@ -42,16 +48,26 @@ public class JwtAuthenticationProvider implements AuthenticationProvider { throw new BadCredentialsException("Authentication Failed.", e); } - if (!jwtTokenService.isValid(token)) { - throw new BadCredentialsException("Auth token is not valid for user " + token.getUserAccountId()); + if (jwtTokenService.isValid(token)) { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference( + REFERENCE_REQUEST); + String[] ids = token.getUserAccountId().split(":"); + if (request == null) { + throw new IllegalStateException("No request found in request attributes"); + } + if (request.getRequestURI().endsWith("esia/logout") || ids.length == 2) { + UsernamePasswordAuthenticationToken pwdToken = + UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null, + Collections.emptyList() + ); + + return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue()); + } } - UsernamePasswordAuthenticationToken pwdToken = - UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null, - Collections.emptyList() - ); - - return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue()); + throw new BadCredentialsException( + "Auth token is not valid for user " + token.getUserAccountId()); } @Override diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java index c4f60f7..5ea0c82 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java @@ -16,6 +16,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.RequestMatcher; +import ru.micord.ervu.security.esia.token.EsiaTokensStore; import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication; import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper; import ru.micord.ervu.security.webbpm.jwt.model.Token; @@ -35,16 +36,12 @@ public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFil private final SecurityHelper securityHelper; - private final JwtTokenService jwtTokenService; - public JwtAuthenticationFilter(RequestMatcher requestMatcher, AuthenticationEntryPoint entryPoint, - SecurityHelper securityHelper, - JwtTokenService jwtTokenService) { + SecurityHelper securityHelper) { super(requestMatcher); this.entryPoint = entryPoint; this.securityHelper = securityHelper; - this.jwtTokenService = jwtTokenService; } @Override @@ -58,18 +55,11 @@ public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFil } try { authentication = getAuthenticationManager().authenticate(authentication); - if (!httpServletRequest.getRequestURI().endsWith("esia/logout")) { - Token token = jwtTokenService.getToken(tokenStr); - String[] ids = token.getUserAccountId().split(":"); - if (ids.length != 2) { - throw new CredentialsExpiredException("Invalid token. User has no ervuId"); - } - } } - catch (CredentialsExpiredException e) { + catch (AuthenticationException e) { + LOGGER.warn(e.getMessage()); securityHelper.clearAccessCookies(httpServletResponse); httpServletResponse.setStatus(401); - LOGGER.warn(e.getMessage()); return null; } return authentication; diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java index 5478da2..aab89b6 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java @@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import ru.micord.ervu.security.esia.token.TokensStore; +import ru.micord.ervu.security.esia.token.EsiaTokensStore; import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils; @@ -34,9 +34,6 @@ public class JwtTokenService { ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID; private final SecretKey SIGNING_KEY; - @Autowired - private HttpServletRequest request; - @Autowired public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}") String secretKey) { @@ -67,7 +64,8 @@ public class JwtTokenService { LOGGER.info("Token {} is expired ", token.getValue()); return false; } - return true; + String[] ids = token.getUserAccountId().split(":"); + return EsiaTokensStore.validateAccessToken(ids[0]); } public Token getToken(String token) { @@ -79,17 +77,12 @@ public class JwtTokenService { return new Token(claims.getSubject(), claims.getIssuer(), claims.getExpiration(), token); } - public String getErvuId() { - String extractAuthToken = extractAuthToken(request); - return getToken(extractAuthToken).getUserAccountId().split(":")[1]; - } - public String getAccessToken(HttpServletRequest request) { - return TokensStore.getAccessToken(getUserAccountId(request)); + return EsiaTokensStore.getAccessToken(getUserAccountId(request)); } public String getRefreshToken(HttpServletRequest request) { - return TokensStore.getRefreshToken(getUserAccountId(request)); + return EsiaTokensStore.getRefreshToken(getUserAccountId(request)); } public String getUserAccountId(HttpServletRequest request) { diff --git a/config/Dockerfile b/config/Dockerfile index 650380e..32531a5 100644 --- a/config/Dockerfile +++ b/config/Dockerfile @@ -1,6 +1,6 @@ ARG BUILDER_IMAGE=registry.altlinux.org/basealt/altsp:c10f1 ARG BACKEND_IMAGE=repo.micord.ru/alt/alt-tomcat:c10f1-9.0.59-20240903 -ARG FRONTEND_IMAGE=docker.angie.software/angie:latest +ARG FRONTEND_IMAGE=nginx:1.24-alpine-slim FROM $BUILDER_IMAGE AS builder @@ -45,5 +45,5 @@ COPY --from=builder /app/frontend/target/frontend*.war /var/lib/tomcat/webapps/R FROM $FRONTEND_IMAGE AS frontend -COPY config/angie.conf /etc/angie/angie.conf +COPY config/nginx.conf /etc/nginx/nginx.conf COPY --from=builder /app/frontend/dist /frontend diff --git a/config/angie.conf b/config/angie.conf deleted file mode 100644 index 18b7f40..0000000 --- a/config/angie.conf +++ /dev/null @@ -1,84 +0,0 @@ -include /etc/angie/modules-enabled.d/*.conf; - -worker_processes 10; - -error_log /var/log/angie/error.log; - -events { - worker_connections 1024; -} - -include /etc/angie/conf-enabled.d/*.conf; - -http { - include /etc/angie/mime.types; - default_type application/octet-stream; - - sendfile on; - - gzip on; - - # text/html doesn't need to be defined there, it's compressed always - gzip_types text/plain text/css text/xml application/x-javascript application/atom+xml; - - # gzip_comp_level 9; - include /etc/angie/sites-enabled.d/*.conf; - - log_format angie_main - '$remote_addr - $remote_user [$time_local] $request ' - '"$status" $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for" ' - '"$request_filename" "$gzip_ratio" $upstream_response_time server: $host : $document_root $fastcgi_script_name '; - - server { - listen 80 default; - - access_log /var/log/angie/access.log angie_main; - error_log /var/log/angie/error.log error; - - charset utf-8; - - client_max_body_size 32m; - - ## - # `gzip` Settings - # - # - gzip on; - gzip_disable "msie6"; - - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_min_length 256; - gzip_types - application/atom+xml - application/geo+json - application/javascript - application/x-javascript - application/json - application/ld+json - application/manifest+json - application/rdf+xml - application/rss+xml - application/xhtml+xml - application/xml - font/eot - font/otf - font/ttf - image/svg+xml - text/css - text/javascript - text/plain - text/xml; - - location / { - root /frontend; - index index.html; - expires -1; - try_files $uri $uri/ $uri/index.html; - } - } -} diff --git a/config/nginx.conf b/config/nginx.conf index 4dd7864..b55fbde 100644 --- a/config/nginx.conf +++ b/config/nginx.conf @@ -11,74 +11,105 @@ events { include /etc/nginx/conf-enabled.d/*.conf; http { - include /etc/nginx/mime.types; + include /etc/nginx/mime.types; default_type application/octet-stream; - sendfile on; + sendfile on; - gzip on; + gzip on; - # text/html doesn't need to be defined there, it's compressed always - gzip_types text/plain text/css text/xml application/x-javascript application/atom+xml; + # text/html doesn't need to be defined there, it's compressed always + gzip_types text/plain text/css text/xml application/x-javascript application/atom+xml; - # gzip_comp_level 9; - include /etc/nginx/sites-enabled.d/*.conf; + # gzip_comp_level 9; + include /etc/nginx/sites-enabled.d/*.conf; - log_format nginx_main - '$remote_addr - $remote_user [$time_local] $request ' - '"$status" $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for" ' - '"$request_filename" "$gzip_ratio" $upstream_response_time server: $host : $document_root $fastcgi_script_name '; + log_format nginx_main + '$remote_addr - $remote_user [$time_local] $request ' + '"$status" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for" ' + '"$request_filename" "$gzip_ratio" $upstream_response_time server: $host : $document_root $fastcgi_script_name '; - server { - listen 80 default; + server { + listen 80 default; access_log /var/log/nginx/access.log nginx_main; - error_log /var/log/nginx/error.log error; + error_log /var/log/nginx/error.log error; - charset utf-8; + charset utf-8; - client_max_body_size 32m; + client_max_body_size 32m; - ## - # `gzip` Settings - # - # - gzip on; - gzip_disable "msie6"; + ## + # `gzip` Settings + # + # + gzip on; + gzip_disable "msie6"; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_min_length 256; - gzip_types - application/atom+xml + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_min_length 256; + gzip_types + application/atom+xml application/geo+json application/javascript - application/x-javascript - application/json - application/ld+json - application/manifest+json - application/rdf+xml - application/rss+xml - application/xhtml+xml - application/xml - font/eot - font/otf - font/ttf - image/svg+xml - text/css - text/javascript - text/plain - text/xml; + application/x-javascript + application/json + application/ld+json + application/manifest+json + application/rdf+xml + application/rss+xml + application/xhtml+xml + application/xml + font/eot + font/otf + font/ttf + image/svg+xml + text/css + text/javascript + text/plain + text/xml; - location / { - root /frontend; - index index.html; - expires -1; - try_files $uri $uri/ $uri/index.html; - } - } + location / { + root /frontend; + index index.html; + try_files $uri @index; + + #Application config + location = /src/resources/app-config.json { + add_header Cache-Control "no-cache"; + expires 0; + } + # 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; + access_log off; + # max-age must be in seconds + add_header Cache-Control "max-age=2629746, public"; + } + # CSS and Javascript + location ~* \.(?:css|js)$ { + expires 1y; + access_log off; + add_header Cache-Control "max-age=31556952, public"; + } + } + + location @index { + root /frontend; + add_header Cache-Control "no-cache"; + expires 0; + try_files /index.html =404; + } + + location = /health { + access_log off; + add_header 'Content-Type' 'application/json'; + return 200 '{"status":"UP"}'; + } + } } diff --git a/distribution/pom.xml b/distribution/pom.xml index 7fac6f8..2a42d87 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -4,7 +4,7 @@ ru.micord.ervu.lkrp fl - 1.9.2 + 1.9.3-SNAPSHOT ru.micord.ervu.lkrp.fl diff --git a/frontend/pom.xml b/frontend/pom.xml index 101764d..2440862 100644 --- a/frontend/pom.xml +++ b/frontend/pom.xml @@ -4,7 +4,7 @@ ru.micord.ervu.lkrp fl - 1.9.2 + 1.9.3-SNAPSHOT ru.micord.ervu.lkrp.fl diff --git a/pom.xml b/pom.xml index 131490f..8738e22 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ru.micord.ervu.lkrp fl - 1.9.2 + 1.9.3-SNAPSHOT pom backend diff --git a/resources/pom.xml b/resources/pom.xml index e77f2c3..1516999 100644 --- a/resources/pom.xml +++ b/resources/pom.xml @@ -4,7 +4,7 @@ ru.micord.ervu.lkrp fl - 1.9.2 + 1.9.3-SNAPSHOT ru.micord.ervu.lkrp.fl