From 8bfbd2052506e27f7080518d020621edb75fe831 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Tue, 19 Nov 2024 09:51:26 +0300 Subject: [PATCH 1/5] SUPPORT-8682: Fix --- .../esia/controller/EsiaController.java | 4 +- .../esia/service/EsiaAuthService.java | 51 ++++++++++++++++--- .../ts/modules/security/guard/auth.guard.ts | 22 ++++---- 3 files changed, 57 insertions(+), 20 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 2a3ab21..dac63e2 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 @@ -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") 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 302eef4..57c44f2 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 @@ -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; @@ -11,19 +12,19 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Base64; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; 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.Authentication; import org.springframework.security.core.context.SecurityContext; import ru.micord.ervu.kafka.model.Document; import ru.micord.ervu.kafka.model.Person; @@ -50,6 +51,7 @@ import ru.micord.ervu.security.webbpm.jwt.model.Token; */ @Service public class EsiaAuthService { + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @Autowired private ObjectMapper objectMapper; @@ -146,8 +148,12 @@ 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) { + throw new RuntimeException(error); + } String clientId = esiaConfig.getClientId(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss xx"); ZonedDateTime dt = ZonedDateTime.now(); @@ -229,7 +235,25 @@ public class EsiaAuthService { return ResponseEntity.ok("Authentication successful"); } catch (Exception e) { - throw new RuntimeException(e); + Token token = jwtTokenService.createAccessToken(null, 3600L, null); + Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), 3600); + response.addCookie(accessCookie); + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = + new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null); + SecurityContext context = SecurityContextHolder.createEmptyContext(); + JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken, + null, token.getValue()); + context.setAuthentication(authentication); + SecurityContextHolder.setContext(context); + Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", 3600); + response.addCookie(authMarkerCookie); + String messageId = getMessageId(e); + String messageWithId = String.format("[%s] %s", messageId, e.getMessage()); + LOGGER.error(messageWithId, e); + return new ResponseEntity<>( + "Произошла ошибка " + messageId + ". Обратитесь к системному администратору", + HttpStatus.FORBIDDEN + ); } } @@ -385,4 +409,19 @@ public class EsiaAuthService { person.setDocument(document); return person; } + + private String getMessageId(Exception exception) { + return Integer.toUnsignedString(Objects + .hashCode(getCurrentUsername()), 36) + + "-" + + Integer.toUnsignedString(exception.hashCode(), 36); + } + + private String getCurrentUsername() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.isAuthenticated()) { + return auth.getName(); + } + return null; + } } diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index c65b84a..ed18f16 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -29,18 +29,8 @@ export abstract class AuthGuard implements CanActivate { if (isAccess) { return true; } - else if (error) { - let userErrorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору'; - let errorCode = this.extractCode(errorDescription); - if (errorCode) { - userErrorMessage = EsiaErrorDetail.getDescription(errorCode); - } - let errorMessage = error + ', error description = ' + errorDescription; - this.messageService.error(userErrorMessage) - throw new Error(errorMessage); - } - else if (code) { - const params = new HttpParams().set('code', code); + if (code || error) { + const params = new HttpParams().set('code', code).set('error', error); this.httpClient.get("esia/auth", { params: params, responseType: 'text', observe: 'response', headers: { @@ -56,6 +46,14 @@ export abstract class AuthGuard implements CanActivate { let errorMessage = reason.error.messages != null ? reason.error.messages : reason.error.replaceAll('\\', ''); + if (error) { + reason = 'Произошла неизвестная ошибка. Обратитесь к системному администратору'; + let errorCode = this.extractCode(errorDescription); + if (errorCode) { + reason = EsiaErrorDetail.getDescription(errorCode); + } + errorMessage = error + ', error description = ' + errorDescription; + } this.messageService.error(errorMessage); console.error(reason); }); From 79adb832bbf120bf2afceba1fea21696b06d151e Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 20 Nov 2024 10:19:01 +0300 Subject: [PATCH 2/5] SUPPORT-8682: Fix --- .../esia/service/EsiaAuthService.java | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 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 57c44f2..0cfaa8f 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 @@ -12,7 +12,10 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +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; @@ -52,6 +55,7 @@ import ru.micord.ervu.security.webbpm.jwt.model.Token; @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; @@ -213,19 +217,7 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, 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(), @@ -235,18 +227,7 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, return ResponseEntity.ok("Authentication successful"); } catch (Exception e) { - Token token = jwtTokenService.createAccessToken(null, 3600L, null); - Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), 3600); - response.addCookie(accessCookie); - UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = - new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null); - SecurityContext context = SecurityContextHolder.createEmptyContext(); - JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken, - null, token.getValue()); - context.setAuthentication(authentication); - SecurityContextHolder.setContext(context); - Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", 3600); - response.addCookie(authMarkerCookie); + createTokenAndAddCookie(response, null, null, EXPIRES_IN); String messageId = getMessageId(e); String messageWithId = String.format("[%s] %s", messageId, e.getMessage()); LOGGER.error(messageWithId, e); @@ -311,19 +292,7 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, 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); @@ -424,4 +393,20 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, } return null; } + + 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); + } } From 9ac386a0a2a75525309052c60cd321acc46ce243 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 22 Nov 2024 10:26:29 +0300 Subject: [PATCH 3/5] SUPPORT-8682: Fix --- .../security/esia/service/EsiaAuthService.java | 6 +++++- .../src/ts/modules/security/guard/auth.guard.ts | 14 +++++++++----- 2 files changed, 14 insertions(+), 6 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 0cfaa8f..7386c9e 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 @@ -156,7 +156,11 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, HttpServletRequest request, HttpServletResponse response) { try { if (error != null) { - throw new RuntimeException(error); + 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"); diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index ed18f16..56c2339 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -47,15 +47,19 @@ export abstract class AuthGuard implements CanActivate { ? reason.error.messages : reason.error.replaceAll('\\', ''); if (error) { - reason = 'Произошла неизвестная ошибка. Обратитесь к системному администратору'; + errorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору'; let errorCode = this.extractCode(errorDescription); if (errorCode) { - reason = EsiaErrorDetail.getDescription(errorCode); + errorMessage = EsiaErrorDetail.getDescription(errorCode); } - errorMessage = error + ', error description = ' + errorDescription; + let consoleError = error + ', error description = ' + errorDescription; + this.messageService.error(errorMessage); + console.error(consoleError); + } + else { + this.messageService.error(errorMessage); + console.error(reason); } - this.messageService.error(errorMessage); - console.error(reason); }); return false; } From a2834123f623284d5fbd6420caf3bf6c1568c734 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 22 Nov 2024 10:42:46 +0300 Subject: [PATCH 4/5] SUPPORT-8682: Fix --- .../ru/micord/ervu/security/esia/service/EsiaAuthService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7386c9e..016c23f 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 @@ -158,7 +158,7 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, if (error != null) { createTokenAndAddCookie(response, null, null, EXPIRES_IN); return new ResponseEntity<>( - "Произошла ошибка неизвестная ошибка. Обратитесь к системному администратору", + "Произошла неизвестная ошибка. Обратитесь к системному администратору", HttpStatus.FORBIDDEN ); } From 172229fbec725bfb6e152cac20b4f5501826798a Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 22 Nov 2024 11:08:13 +0300 Subject: [PATCH 5/5] SUPPORT-8682: Fix --- .../ervu/security/esia/service/EsiaAuthService.java | 11 ++--------- .../ervu/security/webbpm/jwt/util/SecurityUtil.java | 10 ++++++++++ 2 files changed, 12 insertions(+), 9 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 016c23f..9198be6 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,6 @@ 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.Authentication; import org.springframework.security.core.context.SecurityContext; import ru.micord.ervu.kafka.model.Document; import ru.micord.ervu.kafka.model.Person; @@ -49,6 +48,8 @@ import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper; import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; import ru.micord.ervu.security.webbpm.jwt.model.Token; +import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.getCurrentUsername; + /** * @author Eduard Tihomirov */ @@ -390,14 +391,6 @@ public ResponseEntity getEsiaTokensByCode(String esiaAuthCode, String error, + Integer.toUnsignedString(exception.hashCode(), 36); } - private String getCurrentUsername() { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth != null && auth.isAuthenticated()) { - return auth.getName(); - } - return null; - } - private void createTokenAndAddCookie(HttpServletResponse response, String userId, String ervuId, Long expiresIn) { Token token = jwtTokenService.createAccessToken(userId, expiresIn, ervuId); diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/util/SecurityUtil.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/util/SecurityUtil.java index 730cca9..441f80d 100644 --- a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/util/SecurityUtil.java +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/util/SecurityUtil.java @@ -5,6 +5,8 @@ import java.nio.charset.StandardCharsets; 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; @@ -42,4 +44,12 @@ public final class SecurityUtil { Cookie cookie = WebUtils.getCookie(httpRequest, AUTH_TOKEN); return cookie != null ? cookie.getValue() : null; } + + public static String getCurrentUsername() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.isAuthenticated()) { + return auth.getName(); + } + return null; + } }