Merge branch 'master' into develop
# Conflicts: # backend/pom.xml # distribution/pom.xml # frontend/pom.xml # pom.xml # resources/pom.xml
This commit is contained in:
commit
67214fccdc
11 changed files with 194 additions and 57 deletions
|
|
@ -1,10 +1,13 @@
|
|||
package ru.micord.ervu.kafka.controller;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.client.fileupload.WebDavClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
|
@ -22,6 +25,7 @@ import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
|
|||
*/
|
||||
@RestController
|
||||
public class ErvuKafkaController {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Autowired
|
||||
private ReplyingKafkaService replyingKafkaService;
|
||||
|
|
@ -56,7 +60,14 @@ public class ErvuKafkaController {
|
|||
objectMapper.writeValueAsString(data)
|
||||
);
|
||||
ExcerptResponse excerptResponse = objectMapper.readValue(kafkaResponse, ExcerptResponse.class);
|
||||
return webDavClient.webDavDownloadFile(excerptResponse.getFileUrl());
|
||||
if (!excerptResponse.getSuccess()) {
|
||||
throw new RuntimeException("Error with getting excerpt url " + excerptResponse.getMessage());
|
||||
}
|
||||
else if (excerptResponse.getData() == null || excerptResponse.getData().getFileUrl() == null
|
||||
|| excerptResponse.getData().getFileUrl().isEmpty()) {
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
return webDavClient.webDavDownloadFile(excerptResponse.getData().getFileUrl());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package ru.micord.ervu.kafka.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ExcerptData implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String orgId;
|
||||
private String fileUrl;
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getFileUrl() {
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
public void setFileUrl(String fileUrl) {
|
||||
this.fileUrl = fileUrl;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,24 +9,31 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ExcerptResponse implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private boolean success;
|
||||
private String message;
|
||||
private ExcerptData data;
|
||||
|
||||
private String orgId;
|
||||
|
||||
private String fileUrl;
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
public boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getFileUrl() {
|
||||
return fileUrl;
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setFileUrl(String fileUrl) {
|
||||
this.fileUrl = fileUrl;
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ExcerptData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(ExcerptData data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
|||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
|
||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
|
||||
|
|
@ -49,31 +50,22 @@ public class SecurityConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
httpConfigure(http);
|
||||
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http.addFilterBefore(new RequestContextFilter(), LogoutFilter.class);
|
||||
http.addFilterAfter(filterChainExceptionHandler, RequestContextFilter.class);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
|
||||
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||
tokenRepository.setCookieName(TokenConstants.CSRF_TOKEN_NAME);
|
||||
tokenRepository.setHeaderName(TokenConstants.CSRF_HEADER_NAME);
|
||||
tokenRepository.setCookiePath("/");
|
||||
public SecurityFilterChain filterChain(HttpSecurity http,
|
||||
CookieCsrfTokenRepository tokenRepository)
|
||||
throws Exception {
|
||||
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
|
||||
delegate.setCsrfRequestAttributeName(null);
|
||||
// Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
|
||||
// default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
|
||||
CsrfTokenRequestHandler requestHandler = delegate::handle;
|
||||
httpSecurity.authorizeHttpRequests(
|
||||
http.authorizeHttpRequests(
|
||||
(authorizeHttpRequests) -> authorizeHttpRequests.requestMatchers(PERMIT_ALL)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.csrf((csrf) -> csrf.csrfTokenRepository(tokenRepository)
|
||||
.csrfTokenRequestHandler(requestHandler))
|
||||
.csrfTokenRequestHandler(requestHandler)
|
||||
.sessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy()))
|
||||
.logout((logout) -> logout.logoutUrl(ESIA_LOGOUT)
|
||||
.logoutSuccessHandler(new LogoutSuccessHandler(tokenRepository, esiaAuthService)))
|
||||
.exceptionHandling()
|
||||
|
|
@ -81,6 +73,19 @@ public class SecurityConfig {
|
|||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http.addFilterBefore(new RequestContextFilter(), LogoutFilter.class);
|
||||
http.addFilterAfter(filterChainExceptionHandler, RequestContextFilter.class);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CookieCsrfTokenRepository cookieCsrfTokenRepository() {
|
||||
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||
tokenRepository.setCookieName(TokenConstants.CSRF_TOKEN_NAME);
|
||||
tokenRepository.setHeaderName(TokenConstants.CSRF_HEADER_NAME);
|
||||
tokenRepository.setCookiePath("/");
|
||||
return tokenRepository;
|
||||
}
|
||||
|
||||
public AuthenticationEntryPoint entryPoint() {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ervu.service.okopf.OkopfService;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import ru.micord.ervu.security.esia.token.TokensStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -50,7 +51,6 @@ import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
|
|||
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 ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
|
||||
|
||||
/**
|
||||
* @author Eduard Tihomirov
|
||||
|
|
@ -72,6 +72,8 @@ public class EsiaAuthService {
|
|||
private OkopfService okopfService;
|
||||
@Autowired
|
||||
private SecurityHelper securityHelper;
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Value("${ervu.kafka.org.reply.topic}")
|
||||
private String requestReplyTopic;
|
||||
|
|
@ -211,16 +213,16 @@ public class EsiaAuthService {
|
|||
if (tokenResponse.getError() != null) {
|
||||
throw new RuntimeException(tokenResponse.getError_description());
|
||||
}
|
||||
String accessToken = tokenResponse.getAccess_token();
|
||||
String esiaAccessTokenStr = tokenResponse.getAccess_token();
|
||||
String esiaRefreshTokenStr = tokenResponse.getRefresh_token();
|
||||
|
||||
boolean hasRole = ulDataService.checkRole(accessToken);
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
|
||||
boolean hasRole = ulDataService.checkRole(esiaAccessTokenStr);
|
||||
EsiaAccessToken esiaAccessToken = ulDataService.readToken(esiaAccessTokenStr);
|
||||
String prnOid = esiaAccessToken.getSbj_id();
|
||||
String refreshToken = tokenResponse.getRefresh_token();
|
||||
String ervuId = getErvuId(accessToken, prnOid);
|
||||
String ervuId = getErvuId(esiaAccessTokenStr, prnOid);
|
||||
Long expiresIn = tokenResponse.getExpires_in();
|
||||
TokensStore.addAccessToken(prnOid, accessToken, expiresIn);
|
||||
TokensStore.addRefreshToken(prnOid, refreshToken, expiresIn);
|
||||
TokensStore.addAccessToken(prnOid, esiaAccessTokenStr, expiresIn);
|
||||
TokensStore.addRefreshToken(prnOid, esiaRefreshTokenStr, expiresIn);
|
||||
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), expiresIn, ervuId, hasRole);
|
||||
int expiry = tokenResponse.getExpires_in().intValue();
|
||||
Cookie accessCookie = securityHelper.createAccessCookie(token.getValue(), expiry);
|
||||
|
|
@ -228,9 +230,10 @@ public class EsiaAuthService {
|
|||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
JwtAuthentication authentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
|
||||
JwtAuthentication jwtAuthentication = new JwtAuthentication(usernamePasswordAuthenticationToken,
|
||||
esiaAccessToken.getSbj_id(), token.getValue());
|
||||
context.setAuthentication(authentication);
|
||||
authenticationManager.authenticate(jwtAuthentication);
|
||||
context.setAuthentication(jwtAuthentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
Cookie authMarkerCookie = securityHelper.createAuthMarkerCookie("true", expiry);
|
||||
response.addCookie(authMarkerCookie);
|
||||
|
|
|
|||
|
|
@ -245,8 +245,10 @@ public class UlDataServiceImpl implements UlDataService {
|
|||
JsonNode elementsNode = rootNode.path("elements");
|
||||
StringBuilder names = new StringBuilder();
|
||||
for (JsonNode element : elementsNode) {
|
||||
String name = element.path("name").asText();
|
||||
names.append(name).append("\n");
|
||||
if (element.path("itSystem").asText().equals(esiaConfig.getClientId())) {
|
||||
String name = element.path("name").asText();
|
||||
names.append(name).append("\n");
|
||||
}
|
||||
}
|
||||
return names.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package ru.micord.ervu.security.listener;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||
import org.springframework.security.web.csrf.DeferredCsrfToken;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
|
||||
|
||||
|
||||
@Component
|
||||
public class JwtUpdateListener implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final CsrfTokenRepository tokenRepository;
|
||||
private final Set<AntPathRequestMatcher> csrfUpdateRequiredPathMatchers;
|
||||
|
||||
private CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestAttributeHandler();
|
||||
|
||||
@Autowired
|
||||
public JwtUpdateListener(CsrfTokenRepository tokenRepository) {
|
||||
Assert.notNull(tokenRepository, "tokenRepository cannot be null");
|
||||
this.tokenRepository = tokenRepository;
|
||||
this.csrfUpdateRequiredPathMatchers = Arrays.stream(new String[] {"/esia/auth"})
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent event) {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes)
|
||||
.resolveReference(
|
||||
REFERENCE_REQUEST);
|
||||
HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
|
||||
|
||||
//if csrf cookie update is not required return
|
||||
if (this.csrfUpdateRequiredPathMatchers.stream()
|
||||
.noneMatch(matcher -> matcher.matches(request))) {
|
||||
return;
|
||||
}
|
||||
boolean containsToken = this.tokenRepository.loadToken(request) != null;
|
||||
|
||||
if (containsToken) {
|
||||
this.tokenRepository.saveToken(null, request, response);
|
||||
DeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request,
|
||||
response
|
||||
);
|
||||
this.requestHandler.handle(request, response, deferredCsrfToken::get);
|
||||
this.logger.debug("Replaced CSRF Token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
.webbpm.ervu_lkrp_ul .title {
|
||||
font-size: var(--size-text-title);
|
||||
font-family: 'InterSB';
|
||||
font-family: 'InterB';
|
||||
padding-top: 0;
|
||||
padding-bottom: var(--indent-medium);
|
||||
}
|
||||
|
|
@ -279,6 +279,10 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .data-group .description {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul .pin + .pin {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
|
@ -922,3 +926,13 @@
|
|||
.webbpm.ervu_lkrp_ul .dialog-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.webbpm.ervu_lkrp_ul #mydata .data-group:first-child .subtitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul #mydata .right-block field-set:first-child .fieldset {
|
||||
padding-top: 24px;
|
||||
}
|
||||
.webbpm.ervu_lkrp_ul #mydata .right-block field-set:first-child .fieldset::before {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
class EsiaErrorDetail {
|
||||
export class EsiaErrorDetail {
|
||||
private static errors: { [code: string]: string } = {
|
||||
'ESIA-007071': 'Запрос персональных данных по физическим лицам может быть выполнен только с указанием согласий',
|
||||
'ESIA-007055': 'Вход в систему осуществляется с неподтвержденной учетной записью',
|
||||
|
|
@ -6,7 +6,7 @@ class EsiaErrorDetail {
|
|||
'ESIA-007008': 'Сервис авторизации в настоящее время не может выполнить запрос из-за большой нагрузки или технических работ на сервере',
|
||||
};
|
||||
|
||||
static getDescription(code: string): string {
|
||||
public static getDescription(code: string): string {
|
||||
return this.errors[code] || 'Доступ запрещен. Обратитесь к системному администратору. Ошибка ' + code;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import {Observable} from "rxjs";
|
|||
import {HttpClient, HttpParams} from "@angular/common/http";
|
||||
import {MessagesService} from "@webbpm/base-package";
|
||||
import {AuthenticationService} from "../authentication.service";
|
||||
import {EsiaErrorDetail} from "../EsiaErrorDetail";
|
||||
|
||||
@Injectable({providedIn:'root'})
|
||||
export abstract class AuthGuard implements CanActivate {
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@
|
|||
<value>
|
||||
<item id="7a2b1d18-0ea1-49c8-b6bf-5d91761ca0a0" removed="false">
|
||||
<value>
|
||||
<simple>"font-bold"</simple>
|
||||
<simple>"subtitle"</simple>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
|
|
@ -338,7 +338,7 @@
|
|||
<value>
|
||||
<item id="7a2b1d18-0ea1-49c8-b6bf-5d91761ca0a0" removed="false">
|
||||
<value>
|
||||
<simple>"font-bold"</simple>
|
||||
<simple>"description"</simple>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
|
|
@ -432,16 +432,7 @@
|
|||
<name>HB</name>
|
||||
<container>true</container>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
|
||||
<properties>
|
||||
<entry>
|
||||
<key>visible</key>
|
||||
<value>
|
||||
<simple>false</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
|
||||
<scripts id="b6068710-0f31-48ec-8e03-c0c1480a40c0"/>
|
||||
<scripts id="fe04d7fb-6c5b-46c4-b723-667732d81f4f"/>
|
||||
<scripts id="5c566210-2a60-4048-a2d1-84c7dd023248"/>
|
||||
|
|
@ -610,7 +601,7 @@
|
|||
<value>
|
||||
<item id="ddf92f21-aac9-4378-b2e7-3884fa0b1164" removed="false">
|
||||
<value>
|
||||
<simple>"font-bold"</simple>
|
||||
<simple>"subtitle"</simple>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
|
|
@ -1161,7 +1152,7 @@
|
|||
<value>
|
||||
<item id="ddf92f21-aac9-4378-b2e7-3884fa0b1164" removed="false">
|
||||
<value>
|
||||
<simple>"font-bold"</simple>
|
||||
<simple>"subtitle"</simple>
|
||||
</value>
|
||||
</item>
|
||||
</value>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue