Merge branch 'feature/SUPPORT-8427_new' into feature/SUPPORT-8474_id_ervu

# Conflicts:
#	backend/src/main/java/esia/service/EsiaAuthService.java
This commit is contained in:
Eduard Tihomirov 2024-09-05 14:19:27 +03:00
commit 704b021816
24 changed files with 823 additions and 257 deletions

View file

@ -19,6 +19,10 @@
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>

View file

@ -33,6 +33,9 @@ public class EsiaConfig {
@Value("${sign-url:#{null}}")
private String signUrl;
@Value("${esia-uri.logout:#{null}}")
private String logoutUrl;
public String getEsiaCodeUri() {
return esiaCodePath;
}
@ -64,4 +67,8 @@ public class EsiaConfig {
public String getSignUrl() {
return signUrl;
}
public String getLogoutUrl() {
return logoutUrl;
}
}

View file

@ -11,6 +11,7 @@ import esia.model.EmployeeModel;
import esia.model.OrgInfoModel;
import esia.model.OrganizationModel;
import esia.service.EsiaAuthService;
import esia.service.EsiaDataService;
import esia.service.UlDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@ -28,7 +29,7 @@ public class EsiaController {
private EsiaAuthService esiaAuthService;
@Autowired
private UlDataService ulDataService;
private EsiaDataService esiaDataService;
@RequestMapping(value = "/esia/url")
public String getEsiaUrl() {
@ -47,58 +48,21 @@ public class EsiaController {
@RequestMapping(value = "/esia/org")
public OrgInfoModel getOrgInfo(HttpServletRequest request) {
String accessToken = null;
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("access_token")) {
accessToken = cookie.getValue();
}
}
}
if (accessToken == null) {
return null;
}
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
EmployeeModel chiefEmployeeModel = ulDataService.getChiefEmployeeModel(accessToken);
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
OrgInfoModel orgInfoModel = new OrgInfoModel();
return esiaDataService.getOrgInfo(request);
}
orgInfoModel.empFullname =
employeeModel.getPerson().getLastName() + " " + employeeModel.getPerson().getFirstName()
+ " " + employeeModel.getPerson().getMiddleName();
orgInfoModel.empPosition = employeeModel.getPosition();
orgInfoModel.fullName = organizationModel.getFullName();
orgInfoModel.shortName = organizationModel.getShortName();
Addresses addresses = organizationModel.getAddresses();
if (addresses != null) {
Arrays.stream(addresses.getElements()).forEach(addressModel -> {
if (addressModel.getType().equals("OLG")) {
orgInfoModel.olgAddress = addressModel.getAddressStr();
}
else if (addressModel.getType().equals("OPS")) {
orgInfoModel.opsAddress = addressModel.getAddressStr();
}
} );
}
orgInfoModel.chiefFullname =
chiefEmployeeModel.getPerson().getLastName() + " " + chiefEmployeeModel.getPerson()
.getFirstName() + " " + chiefEmployeeModel.getPerson().getMiddleName();
orgInfoModel.chiefPosition = chiefEmployeeModel.getPosition();
orgInfoModel.ogrn = organizationModel.getOgrn();
orgInfoModel.kpp = organizationModel.getKpp();
orgInfoModel.inn = organizationModel.getInn();
Contacts contacts = organizationModel.getContacts();
if (contacts != null) {
Arrays.stream(contacts.getElements()).forEach(contactModel -> {
if (contactModel.getType().equals("OPH") && contactModel.getVrfStu().equals("VERIFIED")) {
orgInfoModel.mobile = contactModel.getValue();
}
else if (contactModel.getType().equals("OEM") && contactModel.getVrfStu().equals("VERIFIED")) {
orgInfoModel.email = contactModel.getValue();
}
} );
}
return orgInfoModel;
@RequestMapping(value = "/esia/userfullname")
public String getUserFullname(HttpServletRequest request) {
return esiaDataService.getUserFullname(request);
}
@RequestMapping(value = "/esia/orgunitname")
public String getOrgUnitName(HttpServletRequest request) {
return esiaDataService.getOrgUnitName(request);
}
@RequestMapping(value = "/esia/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {
return esiaAuthService.logout(request, response);
}
}

View file

@ -22,4 +22,5 @@ public class OrgInfoModel {
public String empPosition;
public String mobile;
public String email;
public String userRoles;
}

View file

@ -11,7 +11,9 @@ 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.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -24,18 +26,16 @@ import ervu.model.OrgInfo;
import ervu.service.kafka.KafkaProducerErvuService;
import esia.config.EsiaConfig;
import esia.config.FormUrlencoded;
import esia.model.EmployeeModel;
import esia.model.EsiaAccessToken;
import esia.model.EsiaTokenResponse;
import esia.model.OrganizationModel;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.common.utils.Bytes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;
import ru.micord.ervu.security.JwtTokenService;
import ru.micord.ervu.security.Token;
/**
* @author Eduard Tihomirov
@ -54,6 +54,12 @@ public class EsiaAuthService {
@Autowired
private UlDataService ulDataService;
@Autowired
private JwtTokenService jwtTokenService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private KafkaProducerErvuService kafkaProducerErvuService;
@ -63,7 +69,6 @@ public class EsiaAuthService {
@Autowired
private ResponseDataConverter converter;
public String generateAuthCodeUrl() {
try {
String clientId = esiaConfig.getClientId();
@ -205,9 +210,14 @@ public class EsiaAuthService {
cookieRefresh.setPath("/");
response.addCookie(cookieRefresh);
Cookie isAuthToken = new Cookie("is_auth", "true");
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), tokenResponse.getExpires_in());
Cookie isAuthToken = new Cookie("auth_token", token.getValue());
isAuthToken.setPath("/");
response.addCookie(isAuthToken);
SecurityContextHolder.getContext()
.setAuthentication(
new UsernamePasswordAuthenticationToken(esiaAccessToken.getSbj_id(), null));
return true;
}
catch (Exception e) {
@ -284,10 +294,14 @@ public class EsiaAuthService {
cookieRefresh.setHttpOnly(true);
cookieRefresh.setPath("/");
response.addCookie(cookieRefresh);
Cookie isAuthToken = new Cookie("is_auth", "true");
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), tokenResponse.getExpires_in());
Cookie isAuthToken = new Cookie("auth_token", token.getValue());
isAuthToken.setPath("/");
response.addCookie(isAuthToken);
SecurityContextHolder.getContext()
.setAuthentication(
new UsernamePasswordAuthenticationToken(esiaAccessToken.getSbj_id(), null));
}
catch (Exception e) {
throw new RuntimeException(e);
@ -326,6 +340,34 @@ public class EsiaAuthService {
}
}
public String logout(HttpServletRequest request, HttpServletResponse response) {
try {
Cookie[] cookies = request.getCookies();
if (cookies != null)
for (Cookie cookie : cookies) {
if (cookie.getName().equals("auth_token") || cookie.getName().equals("refresh_token")
|| cookie.getName().equals("access_token")) {
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
String logoutUrl = esiaConfig.getLogoutUrl();
String redirectUrl = esiaConfig.getRedirectUrl();
String redirectUrlEncoded = redirectUrl.replaceAll(":", "%3A")
.replaceAll("/", "%2F");
URL url = new URL(logoutUrl);
Map<String, String> params = mapOf(
"client_id", esiaConfig.getClientId(),
"redirect_uri", redirectUrlEncoded);
return makeRequest(url, params);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getErvuId(String accessToken, HttpServletResponse response) {
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);

View file

@ -0,0 +1,99 @@
package esia.service;
import java.util.Arrays;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import esia.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author Eduard Tihomirov
*/
@Service
public class EsiaDataService {
@Autowired
private UlDataService ulDataService;
public OrgInfoModel getOrgInfo(HttpServletRequest request) {
String accessToken = getAccessToken(request);
if (accessToken == null) {
return null;
}
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
EmployeeModel chiefEmployeeModel = ulDataService.getChiefEmployeeModel(accessToken);
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
OrgInfoModel orgInfoModel = new OrgInfoModel();
orgInfoModel.empFullname =
employeeModel.getPerson().getLastName() + " " + employeeModel.getPerson().getFirstName()
+ " " + employeeModel.getPerson().getMiddleName();
orgInfoModel.empPosition = employeeModel.getPosition();
orgInfoModel.fullName = organizationModel.getFullName();
orgInfoModel.shortName = organizationModel.getShortName();
Addresses addresses = organizationModel.getAddresses();
if (addresses != null) {
Arrays.stream(addresses.getElements()).forEach(addressModel -> {
if (addressModel.getType().equals("OLG")) {
orgInfoModel.olgAddress = addressModel.getAddressStr();
}
else if (addressModel.getType().equals("OPS")) {
orgInfoModel.opsAddress = addressModel.getAddressStr();
}
} );
}
orgInfoModel.chiefFullname =
chiefEmployeeModel.getPerson().getLastName() + " " + chiefEmployeeModel.getPerson()
.getFirstName() + " " + chiefEmployeeModel.getPerson().getMiddleName();
orgInfoModel.chiefPosition = chiefEmployeeModel.getPosition();
orgInfoModel.ogrn = organizationModel.getOgrn();
orgInfoModel.kpp = organizationModel.getKpp();
orgInfoModel.inn = organizationModel.getInn();
Contacts contacts = organizationModel.getContacts();
if (contacts != null) {
Arrays.stream(contacts.getElements()).forEach(contactModel -> {
if (contactModel.getType().equals("OPH") && contactModel.getVrfStu().equals("VERIFIED")) {
orgInfoModel.mobile = contactModel.getValue();
}
else if (contactModel.getType().equals("OEM") && contactModel.getVrfStu().equals("VERIFIED")) {
orgInfoModel.email = contactModel.getValue();
}
} );
}
orgInfoModel.userRoles = ulDataService.getAllUserRoles(accessToken);
return orgInfoModel;
}
public String getOrgUnitName(HttpServletRequest request) {
String accessToken = getAccessToken(request);
if (accessToken == null) {
return null;
}
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
return organizationModel.getFullName();
}
public String getUserFullname(HttpServletRequest request) {
String accessToken = getAccessToken(request);
if (accessToken == null) {
return null;
}
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
PersonModel personModel = ulDataService.getPersonData(esiaAccessToken.getSbj_id(), accessToken);
return personModel.getLastName() + " " + personModel.getFirstName().charAt(0) + ". " + personModel.getMiddleName().charAt(0) + ".";
}
private String getAccessToken(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("access_token")) {
return cookie.getValue();
}
}
}
return null;
}
}

View file

@ -3,7 +3,9 @@ package esia.service;
import java.util.Map;
import esia.model.EmployeeModel;
import esia.model.EsiaAccessToken;
import esia.model.OrganizationModel;
import esia.model.PersonModel;
/**
* @author Eduard Tihomirov
@ -17,4 +19,10 @@ public interface UlDataService {
EmployeeModel getChiefEmployeeModel(String accessToken);
OrganizationModel getOrganizationModel(String accessToken);
PersonModel getPersonData(String prnsId, String accessToken);
EsiaAccessToken readToken(String accessToken);
String getAllUserRoles(String accessToken);
}

View file

@ -9,6 +9,7 @@ import java.util.Arrays;
import java.util.Base64;
import java.util.Optional;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import esia.config.EsiaConfig;
import esia.model.*;
@ -104,7 +105,8 @@ public class UlDataServiceImpl implements UlDataService {
}
}
private PersonModel getPersonData(String prnsId, String accessToken) {
@Override
public PersonModel getPersonData(String prnsId, String accessToken) {
try {
String url = esiaConfig.getEsiaBaseUri() + "rs/prns/" + prnsId;
HttpRequest getReq = HttpRequest.newBuilder(URI.create(url))
@ -125,7 +127,8 @@ public class UlDataServiceImpl implements UlDataService {
}
}
private EsiaAccessToken readToken(String accessToken) {
@Override
public EsiaAccessToken readToken(String accessToken) {
try {
byte[] decodedBytes = Base64.getDecoder()
.decode(
@ -218,4 +221,37 @@ public class UlDataServiceImpl implements UlDataService {
throw new RuntimeException(e);
}
}
@Override
public String getAllUserRoles(String accessToken) {
try {
EsiaAccessToken esiaAccessToken = readToken(accessToken);
String scope = esiaAccessToken.getScope();
String orgOid = scope.substring(scope.indexOf('=') + 1, scope.indexOf(' '));
String prnsId = esiaAccessToken.getSbj_id();
String url = esiaConfig.getEsiaBaseUri() + "rs/orgs/" + orgOid + "/emps/" + prnsId + "/grps?embed=(elements)";
HttpRequest getReq = HttpRequest.newBuilder(URI.create(url))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ".concat(accessToken))
.GET()
.timeout(Duration.ofSeconds(60))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build()
.send(getReq, HttpResponse.BodyHandlers.ofString());
errorHandler(getResp);
JsonNode rootNode = objectMapper.readTree(getResp.body());
JsonNode elementsNode = rootNode.path("elements");
StringBuilder names = new StringBuilder();
for (JsonNode element : elementsNode) {
String name = element.path("name").asText();
names.append(name).append("\n");
}
return names.toString();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,77 @@
package ru.micord.ervu.security;
import java.util.Collection;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
/**
* @author zamaliev
*/
public class JwtAuthentication implements Authentication {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final String userAccountId;
private final Authentication authentication;
private final String token;
public JwtAuthentication(Authentication authentication, String userAccountId, String token) {
this.userAccountId = userAccountId;
this.authentication = authentication;
this.token = token;
}
public JwtAuthentication(Authentication authentication, String userAccountId) {
this(authentication, userAccountId, null);
}
public String getUserAccountId() {
return userAccountId;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authentication.getAuthorities();
}
@Override
public Object getCredentials() {
return authentication.getCredentials();
}
@Override
public Object getDetails() {
return authentication.getDetails();
}
@Override
public Object getPrincipal() {
return authentication.getPrincipal();
}
@Override
public boolean isAuthenticated() {
if (authentication == null) {
return false;
}
else {
return authentication.isAuthenticated();
}
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
authentication.setAuthenticated(isAuthenticated);
}
@Override
public String getName() {
return authentication.getName();
}
public String getToken() {
return this.token;
}
}

View file

@ -0,0 +1,83 @@
package ru.micord.ervu.security;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
/**
* @author Flyur Karimov
*/
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final AuthenticationEntryPoint entryPoint;
public JwtAuthenticationFilter(String securityPath, AuthenticationEntryPoint entryPoint) {
super(securityPath);
this.entryPoint = entryPoint;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws AuthenticationException {
String token = extractAuthTokenFromRequest(httpServletRequest);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
authentication = new JwtAuthentication(null, null, token);
}
try {
authentication = getAuthenticationManager().authenticate(authentication);
}
catch (CredentialsExpiredException e) {
httpServletResponse.setStatus(401);
LOGGER.warn(e.getMessage());
}
return authentication;
}
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return extractAuthTokenFromRequest(request) != null;
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authentication) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
LOGGER.error("Jwt unsuccessful authentication exception", exception);
SecurityContextHolder.clearContext();
entryPoint.commence(request, response, exception);
}
public String extractAuthTokenFromRequest(HttpServletRequest httpRequest) {
String token = null;
Cookie[] cookies = httpRequest.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("auth_token")) {
token = cookie.getValue();
}
}
}
return token;
}
}

View file

@ -0,0 +1,55 @@
package ru.micord.ervu.security;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
private final JwtTokenService jwtTokenService;
@Autowired
public JwtAuthenticationProvider(JwtTokenService jwtTokenService) {
this.jwtTokenService = jwtTokenService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JwtAuthentication jwtAuthentication = (JwtAuthentication) authentication;
String tokenStr = jwtAuthentication.getToken();
Token token;
try {
token = jwtTokenService.getToken(tokenStr);
}
catch (ExpiredJwtException e) {
throw new CredentialsExpiredException("The access token is expired and not valid anymore.",
e
);
}
catch (Exception e) {
throw new BadCredentialsException("Authentication Failed.", e);
}
if (!jwtTokenService.isValid(token)) {
throw new BadCredentialsException("Auth token is not valid for user " + token.getUserAccountId());
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(token.getUserAccountId(), null);
return new JwtAuthentication(usernamePasswordAuthenticationToken, token.getUserAccountId());
}
@Override
public boolean supports(Class<?> aClass) {
return JwtAuthentication.class.isAssignableFrom(aClass);
}
}

View file

@ -0,0 +1,74 @@
package ru.micord.ervu.security;
import java.lang.invoke.MethodHandles;
import java.util.Base64;
import java.util.Date;
import javax.crypto.SecretKey;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
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.stereotype.Component;
import ru.cg.webbpm.modules.resources.api.ResourceMetadataUtils;
/**
* @author Flyur Karimov
*/
@Component
public class JwtTokenService {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Value("${webbpm.security.token.issuer:#{null}}")
private final String tokenIssuerName =
ResourceMetadataUtils.PROJECT_GROUP_ID + "." + ResourceMetadataUtils.PROJECT_ARTIFACT_ID;
private final SecretKey SIGNING_KEY;
@Autowired
public JwtTokenService(@Value("${webbpm.security.token.secret.key:ZjE5ZjMxNmYtODViZC00ZTQ5LWIxZmYtOGEzYzE3Yjc1MDVk}")
String secretKey) {
byte[] encodedKey = Base64.getDecoder().decode(secretKey);
this.SIGNING_KEY = Keys.hmacShaKeyFor(encodedKey);
}
public Token createAccessToken(String userAccountId, Long expiresIn) {
Date expirationDate = new Date(System.currentTimeMillis() + 1000L * expiresIn);
String value = Jwts.builder()
.setSubject(userAccountId)
.setIssuer(tokenIssuerName)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(expirationDate)
.signWith(SIGNING_KEY)
.compact();
return new Token(userAccountId, tokenIssuerName, expirationDate, value);
}
public boolean isValid(Token token) {
if (!tokenIssuerName.equals(token.getIssuer())) {
LOGGER.warn("Unknown token issuer {}", token.getIssuer());
return false;
}
if (token.isExpired()) {
LOGGER.info("Token {} is expired ", token.getValue());
return false;
}
return true;
}
public Token getToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SIGNING_KEY)
.parseClaimsJws(token)
.getBody();
return new Token(claims.getSubject(), claims.getIssuer(), claims.getExpiration(), token);
}
}

View file

@ -0,0 +1,60 @@
package ru.micord.ervu.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
httpConfigure(http);
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
protected void httpConfigure(HttpSecurity httpSecurity) throws Exception {
String[] permitAll = {"/esia/url", "/esia/auth", "esia/refresh"};
httpSecurity.authorizeRequests()
.antMatchers(permitAll).permitAll()
.antMatchers("/**").authenticated()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(entryPoint())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
public AuthenticationEntryPoint entryPoint() {
return new UnauthorizedEntryPoint();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter("/**",
entryPoint()
);
jwtAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
return jwtAuthenticationFilter;
}
}

View file

@ -0,0 +1,37 @@
package ru.micord.ervu.security;
import java.util.Date;
public class Token {
private final String userAccountId;
private final String issuer;
private final Date expirationDate;
private final String value;
public Token(String userAccountId, String issuer, Date expirationDate, String value) {
this.userAccountId = userAccountId;
this.issuer = issuer;
this.expirationDate = expirationDate;
this.value = value;
}
public String getUserAccountId() {
return userAccountId;
}
public String getIssuer() {
return issuer;
}
public Date getExpirationDate() {
return expirationDate;
}
public boolean isExpired() {
return expirationDate.before(new Date());
}
public String getValue() {
return value;
}
}

View file

@ -0,0 +1,29 @@
package ru.micord.ervu.security;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
* {@link AuthenticationEntryPoint} that rejects all requests with an unauthorized error message.
*/
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
//for correct work with cross domain request
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
}
else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized: Authentication token was either missing or invalid."
);
}
}
}

View file

@ -1,25 +0,0 @@
package ru.micord.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
//@EnableAuthorizationServer
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests()
.antMatchers("/**")
.permitAll();
return http.build();
}
}

View file

@ -46,5 +46,6 @@ xa-data-source add \
/system-property=esia-uri.code-path:add(value="https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v2/ac")
/system-property=esia-uri.token-path:add(value="https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v3/te")
/system-property=esia-client-id:add(value="MNSV89")
/system-property=esia-redirect-url:add(value="https://lkrp.micord.ru")
/system-property=esia-redirect-url:add(value="https://lkrp-dev.micord.ru/ul/")
/system-property=sign-url:add(value="https://ervu-sign-dev.k8s.micord.ru/sign")
/system-property=esia-uri.logout:add(value="https://esia-portal1.test.gosuslugi.ru/idp/ext/Logout")

View file

@ -1,5 +1,5 @@
{
"name": "webbpm-frontend",
"name": "ervu_lkrp_ul",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,

View file

@ -0,0 +1,6 @@
<button class="user-info" ngbDropdownToggle *ngIf="getIsAuth()">{{getUserFullname()}}</button>
<div ngbDropdownMenu *ngIf="getIsAuth()">
<div class="user-department">{{getOrgUnitName()}}</div>
<a routerLink="/mydata" class="data">Данные организации</a>
<button ngbDropdownItem class="exit" (click)="logout()">Выйти</button>
</div>

View file

@ -2,6 +2,7 @@ import {AnalyticalScope, Behavior, Container, ControlWithValue} from "@webbpm/ba
import {HttpClient} from "@angular/common/http";
import {OrgData} from "./OrgData";
import {OrgInfoModel} from "../generated/esia/model/OrgInfoModel";
import {CookieService} from "ngx-cookie";
@AnalyticalScope(Container)
export class OrgDataRoot extends Behavior{
@ -13,80 +14,88 @@ export class OrgDataRoot extends Behavior{
this.container = this.getScript(Container);
let orgScripts: OrgData[] = this.container.getScriptsInThisAndChildren(OrgData);
let httpClient = this.injector.get(HttpClient);
httpClient.get<OrgInfoModel>("esia/org")
.toPromise()
.then(orgInfoModel => {
if (orgInfoModel == null) {
return;
}
for (let orgData of orgScripts) {
if (orgData.dataId == 'empFullname') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.empFullname)
}
else if (orgData.dataId == 'fullName') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.fullName)
}
else if (orgData.dataId == 'shortName') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.shortName)
}
else if (orgData.dataId == 'olgAddress') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.olgAddress)
}
else if (orgData.dataId == 'opsAddress') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.opsAddress)
}
else if (orgData.dataId == 'chiefFullname') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.chiefFullname)
}
else if (orgData.dataId == 'chiefPosition') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.chiefPosition)
}
else if (orgData.dataId == 'ogrn') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.ogrn)
}
else if (orgData.dataId == 'kpp') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.kpp)
}
else if (orgData.dataId == 'inn') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.inn)
}
else if (orgData.dataId == 'empPosition') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.empPosition)
}
else if (orgData.dataId == 'mobile') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.mobile)
}
else if (orgData.dataId == 'email') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.email)
let cookieService = this.injector.get(CookieService);
if (cookieService.get("auth_token")) {
httpClient.get<OrgInfoModel>("esia/org")
.toPromise()
.then(orgInfoModel => {
if (orgInfoModel == null) {
return;
}
for (let orgData of orgScripts) {
if (orgData.dataId == 'empFullname') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.empFullname)
}
else if (orgData.dataId == 'fullName') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.fullName)
}
else if (orgData.dataId == 'shortName') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.shortName)
}
else if (orgData.dataId == 'olgAddress') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.olgAddress)
}
else if (orgData.dataId == 'opsAddress') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.opsAddress)
}
else if (orgData.dataId == 'chiefFullname') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.chiefFullname)
}
else if (orgData.dataId == 'chiefPosition') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.chiefPosition)
}
else if (orgData.dataId == 'ogrn') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.ogrn)
}
else if (orgData.dataId == 'kpp') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.kpp)
}
else if (orgData.dataId == 'inn') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.inn)
}
else if (orgData.dataId == 'empPosition') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.empPosition)
}
else if (orgData.dataId == 'mobile') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.mobile)
}
else if (orgData.dataId == 'email') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.email)
}
else if (orgData.dataId == 'userRoles') {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel.userRoles)
}
}
});
}
});
}
}
}

View file

@ -13,6 +13,7 @@ import {
} from "@webbpm/base-package";
import {AppHeaderComponent} from "./component/app-header.component";
import {AppFooterComponent} from "./component/app-footer.component";
import {LogOutComponent} from "./component/logout.component";
import {AccessDeniedComponent} from "./component/access-denied.component";
import {ApplicationVersionComponent} from "./component/application-version.component";
import {RouterModule} from "@angular/router";
@ -28,6 +29,7 @@ export const DIRECTIVES = [
forwardRef(() => AppHeaderComponent),
forwardRef(() => AppFooterComponent),
forwardRef(() => ApplicationVersionComponent),
forwardRef(() => LogOutComponent),
forwardRef(() => AccessDeniedComponent),
forwardRef(() => AppProgressIndicationComponent),
forwardRef(() => ErvuFileUpload)

View file

@ -0,0 +1,52 @@
import {Component, OnInit} from "@angular/core";
import {Router} from "@angular/router";
import {HttpClient} from "@angular/common/http";
import {CookieService} from "ngx-cookie";
import {Deferred} from "@webbpm/base-package";
@Component({
moduleId: module.id,
selector: "[log-out]",
templateUrl: "../../../../../src/resources/template/app/component/log_out.html"
})
export class LogOutComponent implements OnInit{
private userFullname: string;
private orgUnitName: string;
constructor(private router: Router, private httpClient: HttpClient,
private cookieService: CookieService) {
}
ngOnInit(): void {
let isAuth = this.getIsAuth();
if (isAuth) {
Promise.all([
this.httpClient.get<string>("esia/userfullname").toPromise(),
this.httpClient.get<string>("esia/orgunitname").toPromise()
]).then(([userFullname, orgUnitName]) => {
this.userFullname = userFullname;
this.orgUnitName = orgUnitName;
});
}
}
public logout(): void {
this.httpClient.get<string>("esia/logout").toPromise().then(url => {
window.open(url, "_self");
})
}
public getUserFullname(): string {
return this.userFullname;
}
public getIsAuth(): boolean {
return this.cookieService.get("auth_token") != null;
}
public getOrgUnitName(): string {
return this.orgUnitName;
}
}

View file

@ -33,7 +33,7 @@ export abstract class AuthGuard implements CanActivate {
else if (code) {
const params = new HttpParams().set('code', code);
this.httpClient.get<boolean>("esia/auth", {params: params}).toPromise().then(
() => this.router.navigateByUrl("/"))
() => window.open(url.origin, "_self"))
.catch((reason) =>
console.error(reason)
);
@ -56,6 +56,6 @@ export abstract class AuthGuard implements CanActivate {
};
public getIsAuth(): string {
return this.cookieService.get('is_auth');
return this.cookieService.get('auth_token');
}
}

View file

@ -323,117 +323,62 @@
</properties>
</scripts>
</children>
<children id="5e633339-5878-4b19-9f56-8b97b5f0f5aa">
<prototypeId>16071adb-3bdf-4c33-b29b-886876016415</prototypeId>
<componentRootId>5e633339-5878-4b19-9f56-8b97b5f0f5aa</componentRootId>
<name>Таблица</name>
<container>true</container>
<children id="9d823f8b-f6c0-4767-a262-717d2d80f69f">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>9d823f8b-f6c0-4767-a262-717d2d80f69f</componentRootId>
<name>Роли пользователя</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="07201df9-ff33-4c71-9aae-a2cfdd028234">
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>autoStretchColumns</key>
<value>
<simple>true</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="a3216735-fb23-4653-a3f7-226a28b9a127" removed="false">
<item id="7a2b1d18-0ea1-49c8-b6bf-5d91761ca0a0" removed="false">
<value>
<simple>"okved-list"</simple>
</value>
</item>
<item id="1bca3aba-624d-4edf-a3c3-7f9de8b21fdc" removed="false">
<value>
<simple>"mute"</simple>
<simple>"font-bold"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>disabled</key>
<value>
<simple>true</simple>
</value>
</entry>
<entry>
<key>rowClickSelectionType</key>
<key>initialValue</key>
<value>
<simple>null</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="1996166f-7922-4f28-a571-9646d956ef37">
<scripts id="737b67e2-295f-4356-a1e1-9419344d8c85"/>
<scripts id="a6ccccd9-354c-4725-9d34-c716cf626048"/>
<scripts id="d38c1af5-2bfe-41cd-ab0f-67040f498127"/>
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
<scripts id="899a1e40-2d2f-492c-ba74-2d04c4d02e73">
<classRef type="TS">
<className>OrgData</className>
<packageName>esia</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>gridService</key>
<key>dataId</key>
<value>
<complex>
<entry>
<key>loadDao</key>
<value>
<complex>
<entry>
<key>graph</key>
<value>
<simple>{"conditionGroup":{"operator":"AND","conditions":[],"groups":[]},"nodeByIndex":{"0":{"tableName":"org_okved","schemaName":"public","x":387.20000000000005,"y":282.4,"alias":"org_okved","conditionGroup":{"operator":"AND","conditions":[{"column":{"schema":"public","table":"org_okved","entity":"org_okved","name":"code"},"operation":"EQUAL","typeCode":"CONST","values":["\"5\""]}],"groups":[]},"emptyEntityAction":"IGNORE_OR_DELETE"}},"nodes":[{"tableName":"org_okved","schemaName":"public","x":387.20000000000005,"y":282.4,"alias":"org_okved","conditionGroup":{"operator":"AND","conditions":[{"column":{"schema":"public","table":"org_okved","entity":"org_okved","name":"code"},"operation":"EQUAL","typeCode":"CONST","values":["\"5\""]}],"groups":[]},"emptyEntityAction":"IGNORE_OR_DELETE"}],"nodeByEntityName":{"org_okved":{"tableName":"org_okved","schemaName":"public","x":387.20000000000005,"y":282.4,"alias":"org_okved","conditionGroup":{"operator":"AND","conditions":[{"column":{"schema":"public","table":"org_okved","entity":"org_okved","name":"code"},"operation":"EQUAL","typeCode":"CONST","values":["\"5\""]}],"groups":[]},"emptyEntityAction":"IGNORE_OR_DELETE"}},"matrix":[[null]],"mainNodeIndex":0}</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
<simple>"userRoles"</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="be8fe0e1-4909-4224-8664-be55168595c6"/>
<children id="2d713b90-9e5a-428c-a40f-b3d164790032">
<prototypeId>364c8faa-5e56-46cd-9203-d2ec6ef2dc74</prototypeId>
<componentRootId>2d713b90-9e5a-428c-a40f-b3d164790032</componentRootId>
<name>Столбец</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="9c5c7a86-dc40-4b30-a5a7-5e7b4c7ea1e1"/>
<scripts id="fd653fca-12f9-4e35-baa4-b6b5dd3f6d59">
<properties>
<entry>
<key>disableHiding</key>
<value>
<simple>true</simple>
</value>
</entry>
<entry>
<key>displayColumns</key>
<value>
<item id="1fcc8084-6f93-42fd-80f4-61defe451897" removed="true"/>
<item id="4b348c07-e14d-46cc-8c1d-0e079ebebc4c" removed="true"/>
</value>
</entry>
<entry>
<key>displayPopup</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>displayType</key>
<value>
<simple>"ONE_COLUMN"</simple>
</value>
</entry>
<entry>
<key>field</key>
<value>
<simple>{"schema":"public","table":"org_okved","entity":"org_okved","name":"okved"}</simple>
</value>
</entry>
</properties>
</scripts>
</children>
</children>
<children id="5e633339-5878-4b19-9f56-8b97b5f0f5aa">
<prototypeId>16071adb-3bdf-4c33-b29b-886876016415</prototypeId>
<componentRootId>5e633339-5878-4b19-9f56-8b97b5f0f5aa</componentRootId>
<name>Таблица</name>
<container>true</container>
<removed>true</removed>
</children>
<children id="20801b92-c2bb-4410-bb65-148130805f1c">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>