From 553ae85d262ad21e61a76826ab8592748851369e Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 19 Feb 2025 15:44:06 +0300 Subject: [PATCH 01/18] SUPPORT-8643: Fix (cherry picked from commit a3f898037002c277866768e196edadb377ae4678) --- .../esia/controller/EsiaController.java | 3 +- .../esia/service/EsiaAuthService.java | 6 +-- .../ts/modules/security/guard/auth.guard.ts | 39 +++++++------------ 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java index 2c74293b..26bdd4c6 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java @@ -31,9 +31,8 @@ public class EsiaController { @GetMapping(value = "/esia/auth") public void esiaAuth(@RequestParam(value = "code", required = false) String code, - @RequestParam(value = "error", required = false) String error, HttpServletRequest request, HttpServletResponse response) { - esiaAuthService.authEsiaTokensByCode(code, error, request, response); + esiaAuthService.authEsiaTokensByCode(code, response); } @PostMapping(value = "/esia/refresh") 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 ce1da444..bf1be7a6 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 @@ -159,11 +159,7 @@ public class EsiaAuthService { return uriBuilder.toString(); } - public void authEsiaTokensByCode(String esiaAuthCode, String error, - HttpServletRequest request, HttpServletResponse response) { - if (error != null && !error.equals("null")) { - throw new EsiaException(error); - } + public void authEsiaTokensByCode(String esiaAuthCode, HttpServletResponse response) { String esiaAccessTokenStr = null; String prnOid = null; Long expiresIn = null; diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index 5cad4aa3..f3d0d56b 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -47,8 +47,19 @@ export abstract class AuthGuard implements CanActivate { if (isAccess) { return true; } - if (code || error) { - const params = new HttpParams().set('code', code).set('error', error); + if (error) { + let 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); + } + if (code) { + const params = new HttpParams().set('code', code); this.httpClient.get("esia/auth", { params: params, @@ -60,29 +71,9 @@ export abstract class AuthGuard implements CanActivate { }) .toPromise() .then( - (response) => { + () => { window.open(url.origin + url.pathname, "_self"); - }) - .catch((reason) => { - let errorMessage = reason.error.messages != null - ? reason.error.messages - : reason.error.replaceAll('\\', ''); - 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; } else { From 813ba4c4d5730cedf751b6f0c0968af4d34ffb91 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 21 Feb 2025 10:45:30 +0300 Subject: [PATCH 02/18] SUPPORT-8643: Fix (cherry picked from commit 574d67cd3cddae828f5f0c67132f680c78359752) --- .../ervu/component/button/ErvuDownloadFileButton.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts b/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts index 80c65d64..7ae1aa22 100644 --- a/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts +++ b/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts @@ -60,7 +60,7 @@ export class ErvuDownloadFileButton extends AbstractButton { public doClickActions(): Promise { return this.httpClient.get('kafka/excerpt', { responseType: 'blob', - headers: new HttpHeaders({'Client-Time-Zone' : Intl.DateTimeFormat().resolvedOptions().timeZone}), + headers: new HttpHeaders({'Client-Time-Zone' : Intl.DateTimeFormat().resolvedOptions().timeZone, 'Error-intercept-skip': 'true'}), observe: 'response' }).toPromise().then((response) => { if (response.status == 204) { @@ -79,6 +79,17 @@ export class ErvuDownloadFileButton extends AbstractButton { a.click(); window.URL.revokeObjectURL(url); a.remove(); + }).catch((reason) => { + this.getJSONFromBlob(reason); }); } + + private getJSONFromBlob(error: any) { + error.text().then((text) => { + const json = JSON.parse(text); + json.messages.forEach((errorMessage) => { + this.messageService.error(errorMessage, json); + }); + }) + } } From a965b4cd833734476ee64b6f03878a9ab58c9aa3 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 21 Feb 2025 11:45:00 +0300 Subject: [PATCH 03/18] SUPPORT-8897: Fix (cherry picked from commit 0b20270fd5da2d20576cd40cdf3a9fd52807f894) --- frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts b/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts index 7ae1aa22..3fdd47f5 100644 --- a/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts +++ b/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts @@ -80,7 +80,7 @@ export class ErvuDownloadFileButton extends AbstractButton { window.URL.revokeObjectURL(url); a.remove(); }).catch((reason) => { - this.getJSONFromBlob(reason); + this.getJSONFromBlob(reason.error); }); } From 186f3d5d40b1eec8fe7d88223d591d951e24969e Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 21 Feb 2025 12:23:09 +0300 Subject: [PATCH 04/18] SUPPORT-8643: Fix --- frontend/src/ts/modules/security/guard/auth.guard.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index f3d0d56b..62e571dc 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -73,7 +73,13 @@ export abstract class AuthGuard implements CanActivate { .then( () => { window.open(url.origin + url.pathname, "_self"); - }); + }) + .catch(reason => { + const json = JSON.parse(reason.error); + json.messages.forEach((errorMessage) => { + this.messageService.error(errorMessage, json); + }); + }); return false; } else { From 2b874412d6914288564d3b418390bbf2c7fb40d8 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 24 Feb 2025 15:01:27 +0300 Subject: [PATCH 05/18] SUPPORT-8643: Fix --- .../ervu/component/button/ErvuDownloadFileButton.ts | 13 +------------ .../src/ts/modules/security/guard/auth.guard.ts | 9 --------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts b/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts index 3fdd47f5..80c65d64 100644 --- a/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts +++ b/frontend/src/ts/ervu/component/button/ErvuDownloadFileButton.ts @@ -60,7 +60,7 @@ export class ErvuDownloadFileButton extends AbstractButton { public doClickActions(): Promise { return this.httpClient.get('kafka/excerpt', { responseType: 'blob', - headers: new HttpHeaders({'Client-Time-Zone' : Intl.DateTimeFormat().resolvedOptions().timeZone, 'Error-intercept-skip': 'true'}), + headers: new HttpHeaders({'Client-Time-Zone' : Intl.DateTimeFormat().resolvedOptions().timeZone}), observe: 'response' }).toPromise().then((response) => { if (response.status == 204) { @@ -79,17 +79,6 @@ export class ErvuDownloadFileButton extends AbstractButton { a.click(); window.URL.revokeObjectURL(url); a.remove(); - }).catch((reason) => { - this.getJSONFromBlob(reason.error); }); } - - private getJSONFromBlob(error: any) { - error.text().then((text) => { - const json = JSON.parse(text); - json.messages.forEach((errorMessage) => { - this.messageService.error(errorMessage, json); - }); - }) - } } diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index 62e571dc..62c47da5 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -65,21 +65,12 @@ export abstract class AuthGuard implements CanActivate { params: params, responseType: 'text', observe: 'response', - headers: { - "Error-intercept-skip": "true" - } }) .toPromise() .then( () => { window.open(url.origin + url.pathname, "_self"); }) - .catch(reason => { - const json = JSON.parse(reason.error); - json.messages.forEach((errorMessage) => { - this.messageService.error(errorMessage, json); - }); - }); return false; } else { From da11e5a3a4453233672a479a21e916f3b591b2dd Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 24 Feb 2025 15:02:49 +0300 Subject: [PATCH 06/18] SUPPORT-8643: Fix --- frontend/src/ts/modules/security/guard/auth.guard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index 62c47da5..e7f66ce8 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -70,7 +70,7 @@ export abstract class AuthGuard implements CanActivate { .then( () => { window.open(url.origin + url.pathname, "_self"); - }) + }); return false; } else { From a648214f5133427474e803e534a16daf2c1d9c8b Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Tue, 25 Feb 2025 08:44:48 +0300 Subject: [PATCH 07/18] SUPPORT-8942: Fix --- .../ervu/security/esia/config/EsiaConfig.java | 7 ++ .../esia/controller/EsiaController.java | 11 +-- .../esia/service/EsiaAuthService.java | 75 ++++++++++++------- .../security/esia/token/EsiaTokensStore.java | 13 ++++ .../webbpm/jwt/helper/SecurityHelper.java | 2 +- .../ts/modules/security/guard/auth.guard.ts | 5 +- 6 files changed, 79 insertions(+), 34 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index 5a0ddc6c..b084e1ff 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -62,6 +62,9 @@ public class EsiaConfig { @Value("${esia.issuer.url}") private String esiaIssuerUrl; + @Value("${esia.marker.ver}") + private String esiaMarkerVer; + public String getEsiaOrgScopes() { String[] scopeItems = esiaOrgScopes.split(","); return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new)); @@ -125,4 +128,8 @@ public class EsiaConfig { public String getEsiaIssuerUrl() { return esiaIssuerUrl; } + + public String getEsiaMarkerVer() { + return esiaMarkerVer; + } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java index 26bdd4c6..4e2fffce 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java @@ -25,14 +25,15 @@ public class EsiaController { private EsiaDataService esiaDataService; @GetMapping(value = "/esia/url") - public String getEsiaUrl() { - return esiaAuthService.generateAuthCodeUrl(); + public String getEsiaUrl(HttpServletResponse response) { + return esiaAuthService.generateAuthCodeUrl(response); } @GetMapping(value = "/esia/auth") - public void esiaAuth(@RequestParam(value = "code", required = false) String code, - HttpServletResponse response) { - esiaAuthService.authEsiaTokensByCode(code, response); + public void esiaAuth(@RequestParam(value = "code") String code, + @RequestParam(value = "state") String state, + HttpServletResponse response, HttpServletRequest request) { + esiaAuthService.authEsiaTokensByCode(code, state, response, request); } @PostMapping(value = "/esia/refresh") 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 bf1be7a6..0f8847a5 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 @@ -10,6 +10,9 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; @@ -17,6 +20,7 @@ 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; @@ -24,7 +28,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import ervu.service.okopf.OkopfService; import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Service; +import org.springframework.web.util.WebUtils; import ru.micord.ervu.security.esia.exception.EsiaException; import ru.micord.ervu.security.esia.model.EmployeeModel; import ru.micord.ervu.security.esia.model.EsiaAccessToken; @@ -56,8 +62,6 @@ import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.cg.webbpm.modules.core.runtime.api.LocalizedException; import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils; -import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername; - /** * @author Eduard Tihomirov */ @@ -66,6 +70,7 @@ public class EsiaAuthService { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor( "messages/common_errors_messages"); + private static final String PRNS_UUID = "prns_uuid"; @Autowired private ObjectMapper objectMapper; @Autowired @@ -86,13 +91,14 @@ public class EsiaAuthService { @Value("${ervu.kafka.org.request.topic}") private String requestTopic; - public String generateAuthCodeUrl() { + public String generateAuthCodeUrl(HttpServletResponse response) { try { String clientId = esiaConfig.getClientId(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx"); ZonedDateTime dt = ZonedDateTime.now(); String timestamp = dt.format(formatter); String state = UUID.randomUUID().toString(); + String prnsUUID = UUID.randomUUID().toString(); String redirectUrl = esiaConfig.getRedirectUrl(); String redirectUrlEncoded = redirectUrl.replaceAll(":", "%3A") .replaceAll("/", "%2F"); @@ -108,7 +114,9 @@ public class EsiaAuthService { parameters.put("redirect_uri", esiaConfig.getRedirectUrl()); String clientSecret = signMap(parameters); - + EsiaTokensStore.addState(prnsUUID, state); + ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/").build(); + securityHelper.addResponseCookie(response, prnsCookie); String responseType = "code"; String authUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaCodeUrl(); @@ -159,18 +167,22 @@ public class EsiaAuthService { return uriBuilder.toString(); } - public void authEsiaTokensByCode(String esiaAuthCode, HttpServletResponse response) { + public void authEsiaTokensByCode(String esiaAuthCode, String state, HttpServletResponse response, HttpServletRequest request) { String esiaAccessTokenStr = null; String prnOid = null; Long expiresIn = null; boolean hasRole = false; long timeSignSecret = 0, timeRequestAccessToken = 0, timeVerifySecret = 0; + String verifyStateResult = verifyStateFromCookie(request, state); + if (verifyStateResult != null) { + throw new EsiaException(verifyStateResult); + } try { String clientId = esiaConfig.getClientId(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx"); ZonedDateTime dt = ZonedDateTime.now(); String timestamp = dt.format(formatter); - String state = UUID.randomUUID().toString(); + String newState = UUID.randomUUID().toString(); String redirectUrl = esiaConfig.getRedirectUrl(); String scope = esiaConfig.getEsiaScopes(); String scopeOrg = esiaConfig.getEsiaOrgScopes(); @@ -180,7 +192,7 @@ public class EsiaAuthService { parameters.put("scope", scope); parameters.put("scope_org", scopeOrg); parameters.put("timestamp", timestamp); - parameters.put("state", state); + parameters.put("state", newState); parameters.put("redirect_uri", redirectUrl); parameters.put("code", esiaAuthCode); @@ -193,7 +205,7 @@ public class EsiaAuthService { .setParameter("code", esiaAuthCode) .setParameter("grant_type", "authorization_code") .setParameter("client_secret", clientSecret) - .setParameter("state", state) + .setParameter("state", newState) .setParameter("redirect_uri", redirectUrl) .setParameter("scope", scope) .setParameter("scope_org", scopeOrg) @@ -222,6 +234,9 @@ public class EsiaAuthService { tokenResponse != null ? tokenResponse.getError_description() : "response is empty"; throw new IllegalStateException("Esia response error. " + errMsg); } + if (!tokenResponse.getState().equals(newState)) { + throw new EsiaException("Token invalid. State from request not equals with state from response."); + } esiaAccessTokenStr = tokenResponse.getAccess_token(); String esiaRefreshTokenStr = tokenResponse.getRefresh_token(); startTime = System.currentTimeMillis(); @@ -477,13 +492,6 @@ public class EsiaAuthService { return employee; } - private String getMessageId(Exception exception) { - return Integer.toUnsignedString(Objects - .hashCode(getCurrentUsername()), 36) - + "-" - + Integer.toUnsignedString(exception.hashCode(), 36); - } - private void createTokenAndAddCookie(HttpServletResponse response, String userId, String ervuId, Boolean hasRole, Long expiresIn) { Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId, hasRole); @@ -503,6 +511,9 @@ public class EsiaAuthService { if (!esiaHeader.getSbt().equals("access")) { return "Token invalid. Token sbt: " + esiaHeader.getSbt() + " invalid"; } + if (!esiaHeader.getVer().equals(esiaConfig.getEsiaMarkerVer())) { + return "Token invalid. Token ver: " + esiaHeader.getVer() + " invalid"; + } if (!esiaHeader.getTyp().equals("JWT")) { return "Token invalid. Token type: " + esiaHeader.getTyp() + " invalid"; } @@ -512,17 +523,16 @@ public class EsiaAuthService { if (!esiaAccessToken.getIss().equals(esiaConfig.getEsiaIssuerUrl())) { return "Token invalid. Token issuer:" + esiaAccessToken.getIss() + " invalid"; } - //TODO SUPPORT-8750 -// LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()), -// ZoneId.systemDefault() -// ); -// LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()), -// ZoneId.systemDefault() -// ); -// LocalDateTime currentTime = LocalDateTime.now(); -// if (!currentTime.isAfter(iatTime) || !expTime.isAfter(iatTime)) { -// return "Token invalid. Token expired"; -// } + LocalDateTime iatTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getIat()), + ZoneId.systemDefault() + ); + LocalDateTime expTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(esiaAccessToken.getExp()), + ZoneId.systemDefault() + ); + LocalDateTime currentTime = LocalDateTime.now(); + if (!currentTime.isAfter(iatTime) || !expTime.isAfter(iatTime)) { + return "Token invalid. Token expired"; + } HttpResponse response = signVerify(accessToken); if (response.statusCode() != 200) { if (response.statusCode() == 401) { @@ -550,4 +560,17 @@ public class EsiaAuthService { throw new EsiaException(e); } } + + private String verifyStateFromCookie(HttpServletRequest request, String state) { + Cookie cookie = WebUtils.getCookie(request, PRNS_UUID); + if (cookie == null) { + return "State invalid. Cookie not found"; + } + String prnsUUID = cookie.getValue(); + String oldState = EsiaTokensStore.getState(prnsUUID); + if (oldState == null || !oldState.equals(state)) { + return "State invalid. State from ESIA not equals with state before"; + } + return null; + } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java index f1384900..7b3e1960 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java @@ -15,6 +15,7 @@ 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<>(); + private static final Map prnsUUIDStateMap = new ConcurrentHashMap<>(); public static void addAccessToken(String prnOid, String token, long expiresIn) { if (token != null) { @@ -77,4 +78,16 @@ public class EsiaTokensStore { refreshTokensMap.remove(prnOid); } + public static void addState(String prnsUUID, String state) { + prnsUUIDStateMap.put(prnsUUID, state); + } + + public static String getState(String prnsUUID) { + return prnsUUIDStateMap.get(prnsUUID); + } + + public static String removeState(String prnsUUID) { + return prnsUUIDStateMap.remove(prnsUUID); + } + } diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java index 9c966f60..f04b0adc 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java @@ -48,7 +48,7 @@ public final class SecurityHelper { addResponseCookie(response, emptyAuthMarker); } - private void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) { + public void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) { response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); } diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index e7f66ce8..24a4aefd 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -42,6 +42,7 @@ export abstract class AuthGuard implements CanActivate { }).then((isAccess) => { let params = new URLSearchParams(url.search); let code = params.get('code'); + let state = params.get('state'); let error = params.get('error'); let errorDescription = params.get('error_description'); if (isAccess) { @@ -58,8 +59,8 @@ export abstract class AuthGuard implements CanActivate { this.messageService.error(errorMessage); console.error(consoleError); } - if (code) { - const params = new HttpParams().set('code', code); + if (code && state) { + const params = new HttpParams().set('code', code).set('state', state); this.httpClient.get("esia/auth", { params: params, From a26b95a35505e1b771a3d27b83e067d973b374b6 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Tue, 25 Feb 2025 11:35:39 +0300 Subject: [PATCH 08/18] SUPPORT-8942: Fix --- .../ru/micord/ervu/security/esia/service/EsiaAuthService.java | 1 + 1 file changed, 1 insertion(+) 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 0f8847a5..9cdc0cce 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 @@ -571,6 +571,7 @@ public class EsiaAuthService { if (oldState == null || !oldState.equals(state)) { return "State invalid. State from ESIA not equals with state before"; } + EsiaTokensStore.removeState(prnsUUID); return null; } } From b8ddb3b6aedeedf07552457e2d7984799ed85594 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 09:49:56 +0300 Subject: [PATCH 09/18] SUPPORT-8942: Fix --- .../ervu/security/esia/controller/EsiaController.java | 4 ++-- .../ervu/security/esia/service/EsiaAuthService.java | 1 - .../micord/ervu/security/esia/token/EsiaTokensStore.java | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java index 4e2fffce..42056fa4 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java @@ -30,8 +30,8 @@ public class EsiaController { } @GetMapping(value = "/esia/auth") - public void esiaAuth(@RequestParam(value = "code") String code, - @RequestParam(value = "state") String state, + public void esiaAuth(@RequestParam String code, + @RequestParam String state, HttpServletResponse response, HttpServletRequest request) { esiaAuthService.authEsiaTokensByCode(code, state, response, request); } 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 9cdc0cce..44dcf53f 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 @@ -18,7 +18,6 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Objects; import java.util.UUID; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java index 7b3e1960..08c6d09e 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java @@ -15,7 +15,7 @@ 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<>(); - private static final Map prnsUUIDStateMap = new ConcurrentHashMap<>(); + private static final Map prnsUuidStateMap = new ConcurrentHashMap<>(); public static void addAccessToken(String prnOid, String token, long expiresIn) { if (token != null) { @@ -79,15 +79,15 @@ public class EsiaTokensStore { } public static void addState(String prnsUUID, String state) { - prnsUUIDStateMap.put(prnsUUID, state); + prnsUuidStateMap.put(prnsUUID, state); } public static String getState(String prnsUUID) { - return prnsUUIDStateMap.get(prnsUUID); + return prnsUuidStateMap.get(prnsUUID); } public static String removeState(String prnsUUID) { - return prnsUUIDStateMap.remove(prnsUUID); + return prnsUuidStateMap.remove(prnsUUID); } } From 2ec132d30c470688b0baf0d166bfa00ca1d17f1b Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 10:17:18 +0300 Subject: [PATCH 10/18] SUPPORT-8942: Fix --- .../ervu/security/esia/config/EsiaConfig.java | 7 ++++ .../esia/service/EsiaAuthService.java | 6 ++-- .../security/esia/token/EsiaTokensStore.java | 21 ++++++++---- .../security/esia/token/ExpiringState.java | 34 +++++++++++++++++++ .../token/TokensClearShedulerService.java | 1 + 5 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index b084e1ff..1955d7fe 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -65,6 +65,9 @@ public class EsiaConfig { @Value("${esia.marker.ver}") private String esiaMarkerVer; + @Value("${esia.state.cookie.life.time:60}") + private long esiaStateCookieLifeTime; + public String getEsiaOrgScopes() { String[] scopeItems = esiaOrgScopes.split(","); return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new)); @@ -132,4 +135,8 @@ public class EsiaConfig { public String getEsiaMarkerVer() { return esiaMarkerVer; } + + public long getEsiaStateCookieLifeTime() { + return esiaStateCookieLifeTime * 60000L; + } } 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 44dcf53f..c1874cf3 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 @@ -113,8 +113,10 @@ public class EsiaAuthService { parameters.put("redirect_uri", esiaConfig.getRedirectUrl()); String clientSecret = signMap(parameters); - EsiaTokensStore.addState(prnsUUID, state); - ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/").build(); + EsiaTokensStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime()); + ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/") + .maxAge(esiaConfig.getEsiaStateCookieLifeTime()) + .build(); securityHelper.addResponseCookie(response, prnsCookie); String responseType = "code"; diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java index 08c6d09e..30e7bc6c 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java @@ -15,7 +15,7 @@ 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<>(); - private static final Map prnsUuidStateMap = new ConcurrentHashMap<>(); + private static final Map prnsUuidStateMap = new ConcurrentHashMap<>(); public static void addAccessToken(String prnOid, String token, long expiresIn) { if (token != null) { @@ -78,16 +78,25 @@ public class EsiaTokensStore { refreshTokensMap.remove(prnOid); } - public static void addState(String prnsUUID, String state) { - prnsUuidStateMap.put(prnsUUID, state); + public static void addState(String prnsUUID, String state, long expiresIn) { + long expiryTime = System.currentTimeMillis() + expiresIn; + prnsUuidStateMap.put(prnsUUID, new ExpiringState(state, expiryTime)); } public static String getState(String prnsUUID) { - return prnsUuidStateMap.get(prnsUUID); + return prnsUuidStateMap.get(prnsUUID).getState(); } - public static String removeState(String prnsUUID) { - return prnsUuidStateMap.remove(prnsUUID); + public static void removeState(String prnsUUID) { + prnsUuidStateMap.remove(prnsUUID); } + public static void removeExpiredState() { + for (String key : prnsUuidStateMap.keySet()) { + ExpiringState state = prnsUuidStateMap.get(key); + if (state != null && state.isExpired()) { + prnsUuidStateMap.remove(key); + } + } + } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java new file mode 100644 index 00000000..74662236 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java @@ -0,0 +1,34 @@ +package ru.micord.ervu.security.esia.token; + +/** + * @author Eduard Tihomirov + */ +public class ExpiringState { + private String state; + private long expiryTime; + + public ExpiringState(String state, long expiryTime) { + this.state = state; + this.expiryTime = expiryTime; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public long getExpiryTime() { + return expiryTime; + } + + public void setExpiryTime(long expiryTime) { + this.expiryTime = expiryTime; + } + + boolean isExpired() { + return System.currentTimeMillis() > expiryTime; + } +} 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 89c9db4c..3421107d 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 @@ -16,5 +16,6 @@ public class TokensClearShedulerService { public void load() { EsiaTokensStore.removeExpiredRefreshToken(); EsiaTokensStore.removeExpiredAccessToken(); + EsiaTokensStore.removeExpiredState(); } } From 01ebd167109a36c79209ee7760c3801063f40f0e Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 11:16:35 +0300 Subject: [PATCH 11/18] SUPPORT-8942: Fix --- .../micord/ervu/security/esia/service/EsiaAuthService.java | 5 +++-- .../ervu/security/webbpm/jwt/helper/SecurityHelper.java | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) 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 c1874cf3..d377a2a1 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 @@ -174,7 +174,7 @@ public class EsiaAuthService { Long expiresIn = null; boolean hasRole = false; long timeSignSecret = 0, timeRequestAccessToken = 0, timeVerifySecret = 0; - String verifyStateResult = verifyStateFromCookie(request, state); + String verifyStateResult = verifyStateFromCookie(request, state, response); if (verifyStateResult != null) { throw new EsiaException(verifyStateResult); } @@ -562,7 +562,7 @@ public class EsiaAuthService { } } - private String verifyStateFromCookie(HttpServletRequest request, String state) { + private String verifyStateFromCookie(HttpServletRequest request, String state, HttpServletResponse response) { Cookie cookie = WebUtils.getCookie(request, PRNS_UUID); if (cookie == null) { return "State invalid. Cookie not found"; @@ -573,6 +573,7 @@ public class EsiaAuthService { return "State invalid. State from ESIA not equals with state before"; } EsiaTokensStore.removeState(prnsUUID); + securityHelper.clearCookie(response, PRNS_UUID, "/"); return null; } } diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java index f04b0adc..6ce0411e 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/helper/SecurityHelper.java @@ -86,4 +86,10 @@ public final class SecurityHelper { .secure(accessCookieSecure) .sameSite(accessCookieSameSite); } + + public void clearCookie(HttpServletResponse response, String name, String path) { + ResponseCookie emptyCookie = createCookie(name, null, path) + .maxAge(0).build(); + addResponseCookie(response, emptyCookie); + } } From df37c970d473c45c17d2e07729d8c9ae765d34fc Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 11:22:03 +0300 Subject: [PATCH 12/18] SUPPORT-8942: Fix --- .../java/ru/micord/ervu/security/esia/config/EsiaConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index 1955d7fe..710d5c37 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -65,7 +65,7 @@ public class EsiaConfig { @Value("${esia.marker.ver}") private String esiaMarkerVer; - @Value("${esia.state.cookie.life.time:60}") + @Value("${esia.state.cookie.life.time.min:60}") private long esiaStateCookieLifeTime; public String getEsiaOrgScopes() { From f2fbc71020650f1b51a6f480fc2fb2989694bd0f Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 12:13:45 +0300 Subject: [PATCH 13/18] SUPPORT-8942: Fix --- .../java/ru/micord/ervu/security/esia/config/EsiaConfig.java | 2 +- .../ru/micord/ervu/security/esia/token/EsiaTokensStore.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index 710d5c37..d73d9415 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -137,6 +137,6 @@ public class EsiaConfig { } public long getEsiaStateCookieLifeTime() { - return esiaStateCookieLifeTime * 60000L; + return esiaStateCookieLifeTime * 60L; } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java index 30e7bc6c..a01d9906 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java @@ -79,7 +79,7 @@ public class EsiaTokensStore { } public static void addState(String prnsUUID, String state, long expiresIn) { - long expiryTime = System.currentTimeMillis() + expiresIn; + long expiryTime = System.currentTimeMillis() + expiresIn * 1000L; prnsUuidStateMap.put(prnsUUID, new ExpiringState(state, expiryTime)); } From c36dee97649d878797eb05f8b27a9a0708359344 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 12:16:16 +0300 Subject: [PATCH 14/18] SUPPORT-8942: Fix --- .../ervu/security/esia/token/TokensClearShedulerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3421107d..fdc98349 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 @@ -13,7 +13,7 @@ public class TokensClearShedulerService { @Scheduled(cron = "${esia.token.clear.cron:0 0 */1 * * *}") @SchedulerLock(name = "clearToken") @Transactional - public void load() { + public void clear() { EsiaTokensStore.removeExpiredRefreshToken(); EsiaTokensStore.removeExpiredAccessToken(); EsiaTokensStore.removeExpiredState(); From ab430db08f3ce5de9ac9fea076dcf8c79fbd4ad7 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 12:29:36 +0300 Subject: [PATCH 15/18] SUPPORT-8942: Fix --- .../EmployeeInfoFileUploadService.java | 4 ++-- .../ervu/security/esia/config/EsiaConfig.java | 4 ++-- .../esia/service/EsiaAuthService.java | 20 +++++++++---------- ... => EsiaAuthInfoClearShedulerService.java} | 8 ++++---- ...okensStore.java => EsiaAuthInfoStore.java} | 3 +-- .../webbpm/jwt/service/JwtTokenService.java | 8 ++++---- 6 files changed, 23 insertions(+), 24 deletions(-) rename backend/src/main/java/ru/micord/ervu/security/esia/token/{TokensClearShedulerService.java => EsiaAuthInfoClearShedulerService.java} (70%) rename backend/src/main/java/ru/micord/ervu/security/esia/token/{EsiaTokensStore.java => EsiaAuthInfoStore.java} (96%) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 4289785a..a479012c 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -35,7 +35,7 @@ import ru.micord.ervu.exception.JsonParsingException; import ru.micord.ervu.security.esia.model.EmployeeModel; import ru.micord.ervu.security.esia.model.PersonModel; import ru.micord.ervu.security.esia.service.UlDataService; -import ru.micord.ervu.security.esia.token.EsiaTokensStore; +import ru.micord.ervu.security.esia.token.EsiaAuthInfoStore; import ru.micord.ervu.security.webbpm.jwt.UserIdsPair; import ru.micord.ervu.service.InteractionService; @@ -86,7 +86,7 @@ public class EmployeeInfoFileUploadService { EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(formType); String esiaUserId = userIdsPair.getEsiaUserId(); String ervuId = userIdsPair.getErvuId(); - String accessToken = EsiaTokensStore.getAccessToken(esiaUserId); + String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId); EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken); PersonModel personModel = employeeModel.getPerson(); diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index d73d9415..43bc96aa 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -65,7 +65,7 @@ public class EsiaConfig { @Value("${esia.marker.ver}") private String esiaMarkerVer; - @Value("${esia.state.cookie.life.time.min:60}") + @Value("${esia.state.cookie.life.time:300}") private long esiaStateCookieLifeTime; public String getEsiaOrgScopes() { @@ -137,6 +137,6 @@ public class EsiaConfig { } public long getEsiaStateCookieLifeTime() { - return esiaStateCookieLifeTime * 60L; + return esiaStateCookieLifeTime; } } 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 d377a2a1..01594a00 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 @@ -37,7 +37,7 @@ import ru.micord.ervu.security.esia.model.EsiaHeader; import ru.micord.ervu.security.esia.model.EsiaTokenResponse; import ru.micord.ervu.security.esia.model.FormUrlencoded; import ru.micord.ervu.security.esia.model.OrganizationModel; -import ru.micord.ervu.security.esia.token.EsiaTokensStore; +import ru.micord.ervu.security.esia.token.EsiaAuthInfoStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContext; @@ -113,7 +113,7 @@ public class EsiaAuthService { parameters.put("redirect_uri", esiaConfig.getRedirectUrl()); String clientSecret = signMap(parameters); - EsiaTokensStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime()); + EsiaAuthInfoStore.addState(prnsUUID, state, esiaConfig.getEsiaStateCookieLifeTime()); ResponseCookie prnsCookie = securityHelper.createCookie(PRNS_UUID, prnsUUID, "/") .maxAge(esiaConfig.getEsiaStateCookieLifeTime()) .build(); @@ -249,8 +249,8 @@ public class EsiaAuthService { EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr); prnOid = esiaAccessToken.getSbj_id(); expiresIn = tokenResponse.getExpires_in(); - EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); - EsiaTokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn); + EsiaAuthInfoStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); + EsiaAuthInfoStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn); } catch (Exception e) { throw new EsiaException(e); @@ -340,8 +340,8 @@ public class EsiaAuthService { EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr); String prnOid = esiaAccessToken.getSbj_id(); Long expiresIn = tokenResponse.getExpires_in(); - EsiaTokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); - EsiaTokensStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn); + EsiaAuthInfoStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn); + EsiaAuthInfoStore.addRefreshToken(prnOid, esiaNewRefreshToken, expiresIn); OrgInfo orgInfo = getOrgInfo(esiaAccessTokenStr); String ervuId = getErvuId(prnOid, orgInfo); createTokenAndAddCookie(response, esiaAccessToken.getSbj_id(), ervuId, true, expiresIn); @@ -388,8 +388,8 @@ public class EsiaAuthService { try { securityHelper.clearAccessCookies(response); String userId = jwtTokenService.getUserAccountId(request); - EsiaTokensStore.removeAccessToken(userId); - EsiaTokensStore.removeRefreshToken(userId); + EsiaAuthInfoStore.removeAccessToken(userId); + EsiaAuthInfoStore.removeRefreshToken(userId); String logoutUrl = esiaConfig.getEsiaBaseUri() + esiaConfig.getEsiaLogoutUrl(); String redirectUrl = esiaConfig.getLogoutRedirectUrl(); URL url = new URL(logoutUrl); @@ -568,11 +568,11 @@ public class EsiaAuthService { return "State invalid. Cookie not found"; } String prnsUUID = cookie.getValue(); - String oldState = EsiaTokensStore.getState(prnsUUID); + String oldState = EsiaAuthInfoStore.getState(prnsUUID); if (oldState == null || !oldState.equals(state)) { return "State invalid. State from ESIA not equals with state before"; } - EsiaTokensStore.removeState(prnsUUID); + EsiaAuthInfoStore.removeState(prnsUUID); securityHelper.clearCookie(response, PRNS_UUID, "/"); return null; } 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/EsiaAuthInfoClearShedulerService.java similarity index 70% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/TokensClearShedulerService.java rename to backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java index fdc98349..fccce36c 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/EsiaAuthInfoClearShedulerService.java @@ -9,13 +9,13 @@ import org.springframework.transaction.annotation.Transactional; * @author Eduard Tihomirov */ @Service -public class TokensClearShedulerService { +public class EsiaAuthInfoClearShedulerService { @Scheduled(cron = "${esia.token.clear.cron:0 0 */1 * * *}") @SchedulerLock(name = "clearToken") @Transactional public void clear() { - EsiaTokensStore.removeExpiredRefreshToken(); - EsiaTokensStore.removeExpiredAccessToken(); - EsiaTokensStore.removeExpiredState(); + EsiaAuthInfoStore.removeExpiredRefreshToken(); + EsiaAuthInfoStore.removeExpiredAccessToken(); + EsiaAuthInfoStore.removeExpiredState(); } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoStore.java similarity index 96% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java rename to backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoStore.java index a01d9906..ee8ea8ac 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaTokensStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoStore.java @@ -6,12 +6,11 @@ import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.CredentialsExpiredException; /** * @author Eduard Tihomirov */ -public class EsiaTokensStore { +public class EsiaAuthInfoStore { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final Map accessTokensMap = new ConcurrentHashMap<>(); private static final Map refreshTokensMap = new ConcurrentHashMap<>(); 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 62686b82..19dfd99c 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.EsiaTokensStore; +import ru.micord.ervu.security.esia.token.EsiaAuthInfoStore; import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils; @@ -67,7 +67,7 @@ public class JwtTokenService { return false; } String[] ids = token.getUserAccountId().split(":"); - return EsiaTokensStore.validateAccessToken(ids[0]); + return EsiaAuthInfoStore.validateAccessToken(ids[0]); } public Token getToken(String token) { @@ -80,11 +80,11 @@ public class JwtTokenService { } public String getAccessToken(HttpServletRequest request) { - return EsiaTokensStore.getAccessToken(getUserAccountId(request)); + return EsiaAuthInfoStore.getAccessToken(getUserAccountId(request)); } public String getRefreshToken(HttpServletRequest request) { - return EsiaTokensStore.getRefreshToken(getUserAccountId(request)); + return EsiaAuthInfoStore.getRefreshToken(getUserAccountId(request)); } public String getUserAccountId(HttpServletRequest request) { From 3a7e507861e1bca3bac4720fbcb27394aefaf2e0 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 12:32:38 +0300 Subject: [PATCH 16/18] SUPPORT-8942: Fix --- .../security/esia/token/EsiaAuthInfoClearShedulerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java index fccce36c..caea622d 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java @@ -13,7 +13,7 @@ public class EsiaAuthInfoClearShedulerService { @Scheduled(cron = "${esia.token.clear.cron:0 0 */1 * * *}") @SchedulerLock(name = "clearToken") @Transactional - public void clear() { + public void run() { EsiaAuthInfoStore.removeExpiredRefreshToken(); EsiaAuthInfoStore.removeExpiredAccessToken(); EsiaAuthInfoStore.removeExpiredState(); From be04288309497c017993b8c0edb49f9c92b6792f Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 12:43:59 +0300 Subject: [PATCH 17/18] SUPPORT-8942: Fix --- .../service/fileupload/EmployeeInfoFileUploadService.java | 2 +- .../ervu/security/esia/{token => }/EsiaAuthInfoStore.java | 4 +++- .../ervu/security/esia/{token => model}/ExpiringState.java | 2 +- .../ervu/security/esia/{token => model}/ExpiringToken.java | 2 +- .../EsiaAuthInfoClearShedulerService.java | 7 ++++--- .../micord/ervu/security/esia/service/EsiaAuthService.java | 2 +- .../ervu/security/webbpm/jwt/service/JwtTokenService.java | 2 +- config/standalone/dev/standalone.xml | 2 +- 8 files changed, 13 insertions(+), 10 deletions(-) rename backend/src/main/java/ru/micord/ervu/security/esia/{token => }/EsiaAuthInfoStore.java (95%) rename backend/src/main/java/ru/micord/ervu/security/esia/{token => model}/ExpiringState.java (93%) rename backend/src/main/java/ru/micord/ervu/security/esia/{token => model}/ExpiringToken.java (93%) rename backend/src/main/java/ru/micord/ervu/security/esia/{token => service}/EsiaAuthInfoClearShedulerService.java (70%) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index a479012c..09fd48d4 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -35,7 +35,7 @@ import ru.micord.ervu.exception.JsonParsingException; import ru.micord.ervu.security.esia.model.EmployeeModel; import ru.micord.ervu.security.esia.model.PersonModel; import ru.micord.ervu.security.esia.service.UlDataService; -import ru.micord.ervu.security.esia.token.EsiaAuthInfoStore; +import ru.micord.ervu.security.esia.EsiaAuthInfoStore; import ru.micord.ervu.security.webbpm.jwt.UserIdsPair; import ru.micord.ervu.service.InteractionService; diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoStore.java b/backend/src/main/java/ru/micord/ervu/security/esia/EsiaAuthInfoStore.java similarity index 95% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoStore.java rename to backend/src/main/java/ru/micord/ervu/security/esia/EsiaAuthInfoStore.java index ee8ea8ac..a127eec5 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoStore.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/EsiaAuthInfoStore.java @@ -1,4 +1,4 @@ -package ru.micord.ervu.security.esia.token; +package ru.micord.ervu.security.esia; import java.lang.invoke.MethodHandles; import java.util.Map; @@ -6,6 +6,8 @@ import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ru.micord.ervu.security.esia.model.ExpiringState; +import ru.micord.ervu.security.esia.model.ExpiringToken; /** * @author Eduard Tihomirov diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/ExpiringState.java similarity index 93% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java rename to backend/src/main/java/ru/micord/ervu/security/esia/model/ExpiringState.java index 74662236..3b1bf1b1 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringState.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/ExpiringState.java @@ -1,4 +1,4 @@ -package ru.micord.ervu.security.esia.token; +package ru.micord.ervu.security.esia.model; /** * @author Eduard Tihomirov diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringToken.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/ExpiringToken.java similarity index 93% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringToken.java rename to backend/src/main/java/ru/micord/ervu/security/esia/model/ExpiringToken.java index f6a476e4..9ce0d415 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/ExpiringToken.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/ExpiringToken.java @@ -1,4 +1,4 @@ -package ru.micord.ervu.security.esia.token; +package ru.micord.ervu.security.esia.model; /** * @author Eduard Tihomirov diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthInfoClearShedulerService.java similarity index 70% rename from backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java rename to backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthInfoClearShedulerService.java index caea622d..299876f7 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/token/EsiaAuthInfoClearShedulerService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthInfoClearShedulerService.java @@ -1,17 +1,18 @@ -package ru.micord.ervu.security.esia.token; +package ru.micord.ervu.security.esia.service; import net.javacrumbs.shedlock.core.SchedulerLock; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import ru.micord.ervu.security.esia.EsiaAuthInfoStore; /** * @author Eduard Tihomirov */ @Service public class EsiaAuthInfoClearShedulerService { - @Scheduled(cron = "${esia.token.clear.cron:0 0 */1 * * *}") - @SchedulerLock(name = "clearToken") + @Scheduled(cron = "${esia.auth.info.clear.cron:0 0 */1 * * *}") + @SchedulerLock(name = "clearAuthInfo") @Transactional public void run() { EsiaAuthInfoStore.removeExpiredRefreshToken(); 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 01594a00..f8aa9400 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 @@ -37,7 +37,7 @@ import ru.micord.ervu.security.esia.model.EsiaHeader; import ru.micord.ervu.security.esia.model.EsiaTokenResponse; import ru.micord.ervu.security.esia.model.FormUrlencoded; import ru.micord.ervu.security.esia.model.OrganizationModel; -import ru.micord.ervu.security.esia.token.EsiaAuthInfoStore; +import ru.micord.ervu.security.esia.EsiaAuthInfoStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContext; 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 19dfd99c..0744ef7a 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.EsiaAuthInfoStore; +import ru.micord.ervu.security.esia.EsiaAuthInfoStore; import ru.micord.ervu.security.webbpm.jwt.model.Token; import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils; diff --git a/config/standalone/dev/standalone.xml b/config/standalone/dev/standalone.xml index b595148b..cd7fd6b2 100644 --- a/config/standalone/dev/standalone.xml +++ b/config/standalone/dev/standalone.xml @@ -93,7 +93,7 @@ - + From 51c3f18824795cda98a2d5fa4d30c4cb9c38bb8a Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 26 Feb 2025 14:09:58 +0300 Subject: [PATCH 18/18] SUPPORT-8942: Fix --- config/micord.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/micord.env b/config/micord.env index f097e315..36beffb5 100644 --- a/config/micord.env +++ b/config/micord.env @@ -50,7 +50,7 @@ ERVU_FILE_UPLOAD_MAX_FILE_SIZE=5242880 ERVU_FILE_UPLOAD_MAX_REQUEST_SIZE=6291456 ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD=0 -ESIA_TOKEN_CLEAR_CRON=0 0 */1 * * * +ESIA_AUTH_INFO_CLEAR_CRON=0 0 */1 * * * COOKIE_PATH=/ul WEBDAV_URLS=https://ervu-webdav.k8s.micord.ru