Merge branch 'feature/SUPPORT-8427_new' into feature/SUPPORT-8471_load_classifier

# Conflicts:
#	config/patches/default.cli
#	config/standalone/dev/standalone.xml
#	pom.xml
This commit is contained in:
Eduard Tihomirov 2024-09-09 10:37:04 +03:00
commit 91d842d25b
246 changed files with 3517 additions and 6487 deletions

22
Dockerfile Normal file
View file

@ -0,0 +1,22 @@
FROM maven:3-openjdk-17-slim AS build
RUN apt update \
&& apt upgrade -y \
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
&& apt install -y git nodejs \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
RUN mvn clean && mvn package -T4C
FROM gitlab.micord.ru:5050/common/base/webbpm/webbpm-tomcat-cprocsp:8.0.15-jre17-webprofile
ARG ADMIN_PASSWORD=Secr3t
COPY config/tomcat/tomee /usr/local/tomee
RUN rm -rf /usr/local/tomee/webapps/ROOT \
&& cat /usr/local/tomee/conf/webbpm.properties >> /usr/local/tomee/conf/catalina.properties \
&& sed -i -r "s/<must-be-changed>/$ADMIN_PASSWORD/g" /usr/local/tomee/conf/tomcat-users.xml
COPY --from=build /app/frontend/target/*.war /usr/local/tomee/webapps/ROOT.war
COPY --from=build /app/backend/target/*.war /usr/local/tomee/webapps/ul.war

18
Dockerfile.old Normal file
View file

@ -0,0 +1,18 @@
FROM tomee:8.0.15-jre17-webprofile
RUN \
rm -rf /usr/local/tomee/webapps/ROOT && \
echo "fias.enable=false" >> /usr/local/tomee/conf/catalina.properties && \
echo "webbpm.jbpm.hibernate_statistics.enabled=false" >> /usr/local/tomee/conf/catalina.properties && \
echo "webbpm.mode=production" >> /usr/local/tomee/conf/catalina.properties && \
echo "authentication.method=form" >> /usr/local/tomee/conf/catalina.properties && \
echo "webbpm.cache.hazelcast.hosts=127.0.0.1" >> /usr/local/tomee/conf/catalina.properties && \
echo "webbpm.cache.hazelcast.outbound_port_definitions=5801-5820" >> /usr/local/tomee/conf/catalina.properties && \
echo "gar.enable=false" >> /usr/local/tomee/conf/catalina.properties && \
echo "reset_password.mail.template.path=mail/reset_password.html" >> /usr/local/tomee/conf/catalina.properties && \
echo "security.password.regex=^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$" >> /usr/local/tomee/conf/catalina.properties && \
echo "bpmn.enable=false" >> /usr/local/tomee/conf/catalina.properties
COPY config/context.xml /usr/local/tomee/conf/
#COPY config/tomcat-users.xml /usr/local/tomee/conf/
COPY frontend/dist/ /usr/local/tomee/webapps/ROOT
COPY backend/target/*.war /usr/local/tomee/webapps/ul.war

View file

@ -3,11 +3,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ervu_lkrp_ul</groupId>
<artifactId>ervu_lkrp_ul</artifactId>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>ul</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>backend</artifactId>
<packaging>war</packaging>
<dependencies>
@ -19,13 +19,17 @@
<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>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>resources</artifactId>
<scope>runtime</scope>
</dependency>
@ -107,30 +111,6 @@
<groupId>ru.cg.webbpm.modules</groupId>
<artifactId>inject</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.bpmn</groupId>
<artifactId>bpmn-workflow-api</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.bpmn</groupId>
<artifactId>bpmn-workflow-jbpm-api</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.bpmn</groupId>
<artifactId>bpmn-workflow-jbpm</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.bpmn</groupId>
<artifactId>bpmn-beans</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.bpmn</groupId>
<artifactId>bpmn-variable-condition-adapter</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.bpmn</groupId>
<artifactId>bpmn-deploy</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules</groupId>
<artifactId>webkit-rpc</artifactId>
@ -184,22 +164,6 @@
<groupId>ru.cg.webbpm.modules.core</groupId>
<artifactId>metrics</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.webkit</groupId>
<artifactId>active-users-tracker</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.security</groupId>
<artifactId>security-beans</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.security</groupId>
<artifactId>security-api</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.security</groupId>
<artifactId>security-esia</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.reporting</groupId>
<artifactId>reporting-api</artifactId>
@ -236,14 +200,6 @@
<groupId>ru.cg.webbpm.modules</groupId>
<artifactId>webkit-base</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.security</groupId>
<artifactId>security-db-synchronization-api</artifactId>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.modules.security</groupId>
<artifactId>security-db-synchronization-ldap-impl</artifactId>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
@ -260,14 +216,6 @@
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-web</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
@ -294,6 +242,7 @@
</dependency>
</dependencies>
<build>
<finalName>${parent.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
@ -327,16 +276,6 @@
<profiles>
<profile>
<id>studio</id>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>ru.cg.webbpm.modules.resources</groupId>

View file

@ -11,6 +11,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableScheduling;
@ -31,13 +32,12 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
"i18n",
"errorhandling",
"database",
"security",
"component.addresses",
"gen",
"ru.cg",
"ru.micord",
"ervu",
"esia"
"ru.micord"
}, excludeFilters = {
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "security.WebSecurityConfig")
})
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableWebMvc

View file

@ -1,34 +0,0 @@
package controller;
import dto.jivoprofile.JivoProfileDto;
import org.hsqldb.lib.StringUtil;
import org.springframework.web.bind.annotation.*;
import ru.cg.webbpm.modules.security.api.model.User;
import ru.cg.webbpm.modules.security.api.service.UserService;
@RestController
public class ProfileController {
private UserService userService;
public ProfileController(UserService userService) {
this.userService = userService;
}
@RequestMapping(value = "/profile/jivo/{userId}", method = RequestMethod.GET)
public JivoProfileDto profile(@PathVariable("userId") String userId) {
if (StringUtil.isEmpty(userId)) {
return new JivoProfileDto();
}
User user = userService.get(userId);
JivoProfileDto profileDto = new JivoProfileDto();
profileDto.setUsername(user.firstName());
profileDto.setEmail(user.email());
profileDto.setPhone(user.phone());
return profileDto;
}
}

View file

@ -1,9 +1,10 @@
package ervu.service.fileupload;
import ervu.service.fileupload.model.*;
import esia.model.MillitaryRegistrationPersonModel;
import esia.model.OrganizationModel;
import esia.service.UlDataService;
import ervu.service.fileupload.model.EmployeeInfoKafkaMessage;
import ervu.service.fileupload.model.FileInfo;
import ervu.service.fileupload.model.OrgInfo;
import ervu.service.fileupload.model.SenderInfo;
import ru.micord.ervu.security.esia.service.UlDataService;
import org.springframework.stereotype.Service;
/**
@ -31,35 +32,38 @@ public class EmployeeInfoKafkaMessageService {
);
}
//TODO: refactor SUPPORT-8381
private OrgInfo getOrgInfo() {
OrganizationModel organizationModel = ulDataService.getOrganizationModel();
return new OrgInfo(
organizationModel.getShortName(),
organizationModel.getLeg(),
organizationModel.getLegName(),
organizationModel.getOgrn(),
organizationModel.getInn(),
organizationModel.getKpp()
);
// OrganizationModel organizationModel = ulDataService.getOrganizationModel();
// return new OrgInfo(
// organizationModel.getShortName(),
// organizationModel.getLeg(),
// organizationModel.getLegName(),
// organizationModel.getOgrn(),
// organizationModel.getInn(),
// organizationModel.getKpp()
// );
return null;
}
private SenderInfo getSenderInfo() {
MillitaryRegistrationPersonModel personModel = ulDataService.getPersonModel();
return new SenderInfo(
personModel.getLastName(),
personModel.getFirstName(),
personModel.getMiddleName(),
personModel.getBirthday(),
personModel.getRoleCode(),
personModel.getRoleName(),
personModel.getSnils(),
personModel.getIdERN(),
new PassportInfo(
personModel.getPassportSeries(),
personModel.getPassportNumber(),
personModel.getPassportIssueDate()
)
);
// MillitaryRegistrationPersonModel personModel = ulDataService.getPersonModel();
// return new SenderInfo(
// personModel.getLastName(),
// personModel.getFirstName(),
// personModel.getMiddleName(),
// personModel.getBirthday(),
// personModel.getRoleCode(),
// personModel.getRoleName(),
// personModel.getSnils(),
// personModel.getIdERN(),
// new PassportInfo(
// personModel.getPassportSeries(),
// personModel.getPassportNumber(),
// personModel.getPassportIssueDate()
// )
// );
return null;
}
private FileInfo getFileInfo(String fileNameBase, String fileName,

View file

@ -1,31 +0,0 @@
package esia.model;
import java.io.Serializable;
/**
* @author Eduard Tihomirov
*/
public class ChiefPersonModel extends PersonModel {
private static final long serialVersionUID = 1L;
private String position;
private String brhOid;
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public String getBrhOid() {
return brhOid;
}
public void setBrhOid(String brhOid) {
this.brhOid = brhOid;
}
}

View file

@ -1,46 +0,0 @@
package esia.model;
/**
* @author Eduard Tihomirov
*/
public class MillitaryRegistrationPersonModel extends PersonModel {
private static final long serialVersionUID = 1L;
private String email;
private String mobileNumber;
private String roleCode;
private String roleName;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public String getRoleCode() {
return roleCode;
}
public void setRoleCode(String roleCode) {
this.roleCode = roleCode;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}

View file

@ -1,140 +0,0 @@
package esia.model;
import java.io.Serializable;
/**
* @author Eduard Tihomirov
*/
public class OrganizationModel implements Serializable {
private static final long serialVersionUID = 1L;
private String orgOid;
private String fullName;
private String shortName;
private String inn;
private String ogrn;
private String leg;
private String legName;
private String kpp;
private String ulAddress;
private String actualAddress;
private String zipCode;
private String mobile;
private String email;
public String getOrgOid() {
return orgOid;
}
public void setOrgOid(String orgOid) {
this.orgOid = orgOid;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getOgrn() {
return ogrn;
}
public void setOgrn(String ogrn) {
this.ogrn = ogrn;
}
public String getLeg() {
return leg;
}
public void setLeg(String leg) {
this.leg = leg;
}
public String getKpp() {
return kpp;
}
public void setKpp(String kpp) {
this.kpp = kpp;
}
public String getUlAddress() {
return ulAddress;
}
public void setUlAddress(String ulAddress) {
this.ulAddress = ulAddress;
}
public String getActualAddress() {
return actualAddress;
}
public void setActualAddress(String actualAddress) {
this.actualAddress = actualAddress;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getLegName() {
return legName;
}
public void setLegName(String legName) {
this.legName = legName;
}
}

View file

@ -1,115 +0,0 @@
package esia.model;
import java.io.Serializable;
/**
* @author Eduard Tihomirov
*/
public class PersonModel implements Serializable {
private static final long serialVersionUID = 1L;
private String prsId;
private String orgOid;
private String lastName;
private String firstName;
private String middleName;
private String birthday;
private String snils;
private String idERN;
private String passportSeries;
private String passportNumber;
private String passportIssueDate;
public String getPrsId() {
return prsId;
}
public void setPrsId(String prsId) {
this.prsId = prsId;
}
public String getOrgOid() {
return orgOid;
}
public void setOrgOid(String orgOid) {
this.orgOid = orgOid;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getSnils() {
return snils;
}
public void setSnils(String snils) {
this.snils = snils;
}
public String getIdERN() {
return idERN;
}
public void setIdERN(String idERN) {
this.idERN = idERN;
}
public String getPassportSeries() {
return passportSeries;
}
public void setPassportSeries(String passportSeries) {
this.passportSeries = passportSeries;
}
public String getPassportNumber() {
return passportNumber;
}
public void setPassportNumber(String passportNumber) {
this.passportNumber = passportNumber;
}
public String getPassportIssueDate() {
return passportIssueDate;
}
public void setPassportIssueDate(String passportIssueDate) {
this.passportIssueDate = passportIssueDate;
}
}

View file

@ -1,17 +0,0 @@
package esia.service;
import esia.model.ChiefPersonModel;
import esia.model.MillitaryRegistrationPersonModel;
import esia.model.OrganizationModel;
/**
* @author Eduard Tihomirov
*/
public interface UlDataService {
MillitaryRegistrationPersonModel getPersonModel();
ChiefPersonModel getChiefPersonModel();
OrganizationModel getOrganizationModel();
}

View file

@ -1,59 +0,0 @@
package esia.service.impl;
import esia.model.ChiefPersonModel;
import esia.model.MillitaryRegistrationPersonModel;
import esia.model.OrganizationModel;
import esia.service.UlDataService;
import org.springframework.stereotype.Service;
/**
* @author Alexandr Shalaginov
*/
// TODO: 22.07.2024 delete this after real realisation of UlDataService
@Service
public class DummyUlDataServiceImpl implements UlDataService {
@Override
public MillitaryRegistrationPersonModel getPersonModel() {
MillitaryRegistrationPersonModel personModel = new MillitaryRegistrationPersonModel();
personModel.setPrsId("PrsId");
personModel.setOrgOid("OrgOid");
personModel.setLastName("Максимов");
personModel.setFirstName("Геннадий");
personModel.setMiddleName("Валентинович");
personModel.setBirthday("18.11.1966");
personModel.setSnils("56114152149");
personModel.setIdERN("111666898287");
personModel.setPassportSeries("1151");
personModel.setPassportNumber("166971");
personModel.setPassportIssueDate("13.10.2004");
personModel.setEmail("Email");
personModel.setMobileNumber("");
personModel.setRoleCode("admin_123");
personModel.setRoleName("Администратор 123");
return personModel;
}
@Override
public ChiefPersonModel getChiefPersonModel() {
return new ChiefPersonModel();
}
@Override
public OrganizationModel getOrganizationModel() {
OrganizationModel organizationModel = new OrganizationModel();
organizationModel.setOrgOid("OrgOid");
organizationModel.setFullName("FullName");
organizationModel.setShortName("Звезда");
organizationModel.setInn("123456789012");
organizationModel.setOgrn("1234567890123");
organizationModel.setLeg("2");
organizationModel.setLegName("Общество с ограниченной ответственностью");
organizationModel.setKpp("123456789012");
organizationModel.setUlAddress("UlAddress");
organizationModel.setActualAddress("ActualAddress");
organizationModel.setZipCode("ZipCode");
organizationModel.setMobile("Mobile");
organizationModel.setEmail("Email");
return organizationModel;
}
}

View file

@ -0,0 +1,62 @@
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;
import ru.micord.ervu.security.webbpm.jwt.filter.JwtAuthenticationFilter;
import ru.micord.ervu.security.webbpm.jwt.UnauthorizedEntryPoint;
@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,100 @@
package ru.micord.ervu.security.esia.config;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author Eduard Tihomirov
*/
@Component
public class EsiaConfig {
@Value("${esia-scopes:#{null}}")
private String esiaScopes;
@Value("${esia-org-scopes:#{null}}")
private String esiaOrgScopes;
@Value("${esia-org-scope-url:#{null}}")
private String orgScopeUrl;
@Value("${esia-uri.base-uri:#{null}}")
private String esiaBaseUri;
@Value("${esia-uri.code-path:#{null}}")
private String esiaCodePath;
@Value("${esia-uri.token-path:#{null}}")
private String esiaTokenPath;
@Value("${esia-client-id:#{null}}")
private String clientId;
@Value("${esia-redirect-url:#{null}}")
private String redirectUrl;
@Value("${sign-url:#{null}}")
private String signUrl;
@Value("${esia-uri.logout:#{null}}")
private String logoutUrl;
@Value("${client-cert-hash:#{null}}")
private String clientCertHash;
@Value("${esia.request-timeout:60}")
private long requestTimeout;
@Value("${esia.connection-timeout:30}")
private long connectionTimeout;
public String getEsiaCodeUri() {
return esiaCodePath;
}
public String getEsiaTokenUri() {
return esiaTokenPath;
}
public String getEsiaOrgScopes() {
String[] scopeItems = esiaOrgScopes.split(",");
return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new));
}
public String getEsiaScopes() {
String[] scopeItems = esiaScopes.split(",");
return String.join(" ", Arrays.stream(scopeItems).map(String::trim).toArray(String[]::new));
}
public String getClientId() {
return clientId;
}
public String getRedirectUrl() {
return redirectUrl;
}
public String getEsiaBaseUri() {
return esiaBaseUri;
}
public String getSignUrl() {
return signUrl;
}
public String getLogoutUrl() {
return logoutUrl;
}
public String getClientCertHash() {return clientCertHash;}
public long getRequestTimeout() {
return requestTimeout;
}
public long getConnectionTimeout() {
return connectionTimeout;
}
}

View file

@ -0,0 +1,61 @@
package ru.micord.ervu.security.esia.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ru.micord.ervu.security.esia.model.OrgInfoModel;
import ru.micord.ervu.security.esia.service.EsiaAuthService;
import ru.micord.ervu.security.esia.service.EsiaDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Eduard Tihomirov
*/
@RestController
public class EsiaController {
@Autowired
private EsiaAuthService esiaAuthService;
@Autowired
private EsiaDataService esiaDataService;
@RequestMapping(value = "/esia/url")
public String getEsiaUrl() {
return esiaAuthService.generateAuthCodeUrl();
}
@RequestMapping(value = "/esia/auth", params = "code", method = RequestMethod.GET)
public boolean esiaAuth(@RequestParam("code") String code, HttpServletResponse response) {
return esiaAuthService.getEsiaTokensByCode(code, response);
}
@RequestMapping(value = "/esia/refresh")
public void refreshToken(HttpServletRequest request, HttpServletResponse response) {
esiaAuthService.getEsiaTokensByRefreshToken(request, response);
}
@RequestMapping(value = "/esia/org")
public OrgInfoModel getOrgInfo(HttpServletRequest request) {
return esiaDataService.getOrgInfo(request);
}
@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

@ -0,0 +1,134 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class AddressModel implements Serializable {
private static final long serialVersionUID = 1L;
private String type;
private String zipCode;
private String countryId;
private String addressStr;
private String region;
private String city;
private String street;
private String house;
private String building;
private String frame;
private String flat;
private String fiasCode;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCountryId() {
return countryId;
}
public void setCountryId(String countryId) {
this.countryId = countryId;
}
public String getAddressStr() {
return addressStr;
}
public void setAddressStr(String addressStr) {
this.addressStr = addressStr;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getHouse() {
return house;
}
public void setHouse(String house) {
this.house = house;
}
public String getBuilding() {
return building;
}
public void setBuilding(String building) {
this.building = building;
}
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getFlat() {
return flat;
}
public void setFlat(String flat) {
this.flat = flat;
}
public String getFiasCode() {
return fiasCode;
}
public void setFiasCode(String fiasCode) {
this.fiasCode = fiasCode;
}
}

View file

@ -0,0 +1,23 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Addresses implements Serializable {
private static final long serialVersionUID = 1L;
private AddressModel[] elements;
public AddressModel[] getElements() {
return elements;
}
public void setElements(AddressModel[] elements) {
this.elements = elements;
}
}

View file

@ -0,0 +1,73 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class BrhsModel implements Serializable {
private static final long serialVersionUID = 1L;
private String brhOid;
private String name;
private String kpp;
private String leg;
private Addresses addresses;
private Contacts contacts;
public String getBrhOid() {
return brhOid;
}
public void setBrhOid(String brhOid) {
this.brhOid = brhOid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKpp() {
return kpp;
}
public void setKpp(String kpp) {
this.kpp = kpp;
}
public String getLeg() {
return leg;
}
public void setLeg(String leg) {
this.leg = leg;
}
public Addresses getAddresses() {
return addresses;
}
public void setAddresses(Addresses addresses) {
this.addresses = addresses;
}
public Contacts getContacts() {
return contacts;
}
public void setContacts(Contacts contacts) {
this.contacts = contacts;
}
}

View file

@ -0,0 +1,44 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ContactModel implements Serializable {
private static final long serialVersionUID = 1L;
private String type;
private String value;
private String vrfStu;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getVrfStu() {
return vrfStu;
}
public void setVrfStu(String vrfStu) {
this.vrfStu = vrfStu;
}
}

View file

@ -0,0 +1,23 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Contacts implements Serializable {
private static final long serialVersionUID = 1L;
private ContactModel[] elements;
public ContactModel[] getElements() {
return elements;
}
public void setElements(ContactModel[] elements) {
this.elements = elements;
}
}

View file

@ -0,0 +1,72 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EmployeeModel implements Serializable {
private String prnOid;
private String orgOid;
private Boolean chief;
private Boolean blocked;
private String position;
private PersonModel person;
public String getPrnOid() {
return prnOid;
}
public void setPrnOid(String prnOid) {
this.prnOid = prnOid;
}
public String getOrgOid() {
return orgOid;
}
public void setOrgOid(String orgOid) {
this.orgOid = orgOid;
}
public Boolean getChief() {
return chief;
}
public void setChief(Boolean chief) {
this.chief = chief;
}
public Boolean getBlocked() {
return blocked;
}
public void setBlocked(Boolean blocked) {
this.blocked = blocked;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public PersonModel getPerson() {
return person;
}
public void setPerson(PersonModel person) {
this.person = person;
}
}

View file

@ -0,0 +1,97 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EsiaAccessToken implements Serializable {
private static final long serialVersionUID = -8580344482588383113L;
private Long exp;
private String scope;
private String iss;
private Long nbf;
@JsonProperty("urn:esia:sid")
private String sid;
@JsonProperty("urn:esia:sbj_id")
private String sbj_id;
private String client_id;
private Long iat;
public Long getExp() {
return exp;
}
public void setExp(Long exp) {
this.exp = exp;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getIss() {
return iss;
}
public void setIss(String iss) {
this.iss = iss;
}
public Long getNbf() {
return nbf;
}
public void setNbf(Long nbf) {
this.nbf = nbf;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSbj_id() {
return sbj_id;
}
public void setSbj_id(String sbj_id) {
this.sbj_id = sbj_id;
}
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public Long getIat() {
return iat;
}
public void setIat(Long iat) {
this.iat = iat;
}
}

View file

@ -0,0 +1,94 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EsiaTokenResponse implements Serializable {
private static final long serialVersionUID = 7655328287602576975L;
private String id_token;
private String access_token;
private String refresh_token;
private String state;
private String token_type;
private Long expires_in;
private String error;
private String error_description;
public String getId_token() {
return id_token;
}
public void setId_token(String id_token) {
this.id_token = id_token;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
public Long getExpires_in() {
return expires_in;
}
public void setExpires_in(Long expires_in) {
this.expires_in = expires_in;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getError_description() {
return error_description;
}
public void setError_description(String error_description) {
this.error_description = error_description;
}
}

View file

@ -0,0 +1,42 @@
package ru.micord.ervu.security.esia.model;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* Данные HTML-формы для POST с Content-Type=application/x-www-form-urlencoded.
*/
public class FormUrlencoded {
// todo: private final Map<String, List<String>> ...
private final Map<String, String> formData = new HashMap<>();
/**
* Устанавливает параметр в форму.
*/
public FormUrlencoded setParameter(String name, String value) {
if (name != null) formData.put(name, value);
return this;
}
/**
* Собирает из формы и возвращает строку в виде application/x-www-form-urlencoded.
*/
public String toFormUrlencodedString() {
final StringBuilder sb = new StringBuilder();
formData.forEach((key, value) -> {
if (key != null) key = URLEncoder.encode(key, StandardCharsets.UTF_8);
if (value != null) value = URLEncoder.encode(value, StandardCharsets.UTF_8);
if (sb.length() > 0) sb.append("&");
sb.append(key).append("=").append(value);
});
return sb.toString();
}
@Override
public String toString() {
return toFormUrlencodedString();
}
}

View file

@ -0,0 +1,24 @@
package ru.micord.ervu.security.esia.model;
import ru.cg.webbpm.modules.webkit.annotations.Model;
/**
* @author Eduard Tihomirov
*/
@Model
public class OrgInfoModel {
public String empFullname;
public String fullName;
public String shortName;
public String olgAddress;
public String opsAddress;
public String chiefFullname;
public String chiefPosition;
public String ogrn;
public String kpp;
public String inn;
public String empPosition;
public String mobile;
public String email;
public String userRoles;
}

View file

@ -0,0 +1,174 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class OrganizationModel implements Serializable {
private static final long serialVersionUID = 1L;
private String oid;
private String fullName;
private String shortName;
private String type;
private String inn;
private String ogrn;
private String leg;
private String kpp;
private Addresses addresses;
private Contacts contacts;
private Long staffCount;
private Long branchesCount;
private BrhsModel[] brhs;
private Boolean isLiquidated;
private String agencyTerRange;
private String agencyType;
public String getOid() {
return oid;
}
public void setOid(String oid) {
this.oid = oid;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getOgrn() {
return ogrn;
}
public void setOgrn(String ogrn) {
this.ogrn = ogrn;
}
public String getLeg() {
return leg;
}
public void setLeg(String leg) {
this.leg = leg;
}
public String getKpp() {
return kpp;
}
public void setKpp(String kpp) {
this.kpp = kpp;
}
public Addresses getAddresses() {
return addresses;
}
public void setAddresses(Addresses addresses) {
this.addresses = addresses;
}
public Contacts getContacts() {
return contacts;
}
public void setContacts(Contacts contacts) {
this.contacts = contacts;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Long getStaffCount() {
return staffCount;
}
public void setStaffCount(Long staffCount) {
this.staffCount = staffCount;
}
public Long getBranchesCount() {
return branchesCount;
}
public void setBranchesCount(Long branchesCount) {
this.branchesCount = branchesCount;
}
public BrhsModel[] getBrhs() {
return brhs;
}
public void setBrhs(BrhsModel[] brhs) {
this.brhs = brhs;
}
public Boolean getIsLiquidated() {
return isLiquidated;
}
public void setIsLiquidated(Boolean liquidated) {
isLiquidated = liquidated;
}
public String getAgencyTerRange() {
return agencyTerRange;
}
public void setAgencyTerRange(String agencyTerRange) {
this.agencyTerRange = agencyTerRange;
}
public String getAgencyType() {
return agencyType;
}
public void setAgencyType(String agencyType) {
this.agencyType = agencyType;
}
}

View file

@ -0,0 +1,74 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PassportModel implements Serializable {
private static final long serialVersionUID = 1L;
private String series;
private String number;
@Field(type = FieldType.Date, store = true, format = DateFormat.basic_date)
@JsonSerialize(using = LocalDateSerializer.class)
@JsonFormat(pattern = "dd.MM.yyyy")
private Date issueDate;
private String issueId;
private String issuedBy;
public String getSeries() {
return series;
}
public void setSeries(String series) {
this.series = series;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Date getIssueDate() {
return issueDate;
}
public void setIssueDate(Date issueDate) {
this.issueDate = issueDate;
}
public String getIssueId() {
return issueId;
}
public void setIssueId(String issueId) {
this.issueId = issueId;
}
public String getIssuedBy() {
return issuedBy;
}
public void setIssuedBy(String issuedBy) {
this.issuedBy = issuedBy;
}
}

View file

@ -0,0 +1,139 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PersonModel implements Serializable {
private static final long serialVersionUID = 1L;
private String orgOid;
private String lastName;
private String firstName;
private String middleName;
@JsonFormat(pattern = "dd.MM.yyyy")
private Date birthDate;
private String gender;
private String citizenship;
private String snils;
private String inn;
private String birthPlace;
//Данные не доступны при входе юр лицом
private String email;
//Данные не доступны при входе юр лицом
private String mobileNumber;
public String getOrgOid() {
return orgOid;
}
public void setOrgOid(String orgOid) {
this.orgOid = orgOid;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getCitizenship() {
return citizenship;
}
public void setCitizenship(String citizenship) {
this.citizenship = citizenship;
}
public String getSnils() {
return snils;
}
public void setSnils(String snils) {
this.snils = snils;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getBirthPlace() {
return birthPlace;
}
public void setBirthPlace(String birthPlace) {
this.birthPlace = birthPlace;
}
}

View file

@ -0,0 +1,360 @@
package ru.micord.ervu.security.esia.service;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
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;
import com.fasterxml.jackson.databind.ObjectMapper;
import ru.micord.ervu.security.esia.config.EsiaConfig;
import ru.micord.ervu.security.esia.model.FormUrlencoded;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.EsiaTokenResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
import ru.micord.ervu.security.webbpm.jwt.model.Token;
/**
* @author Eduard Tihomirov
*/
@Service
public class EsiaAuthService {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private EsiaConfig esiaConfig;
@Autowired
private UlDataService ulDataService;
@Autowired
private JwtTokenService jwtTokenService;
public String generateAuthCodeUrl() {
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 redirectUrl = esiaConfig.getRedirectUrl();
String redirectUrlEncoded = redirectUrl.replaceAll(":", "%3A")
.replaceAll("/", "%2F");
String scope = esiaConfig.getEsiaScopes();
String scopeOrg = esiaConfig.getEsiaOrgScopes();
Map<String, String> parameters = new LinkedHashMap<String, String>();
parameters.put("client_id", clientId);
parameters.put("scope", scope);
parameters.put("scope_org", scopeOrg);
parameters.put("timestamp", timestamp);
parameters.put("state", state);
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
String clientSecret = signMap(parameters);
String responseType = "code";
String authUrl = esiaConfig.getEsiaCodeUri();
URL url = new URL(authUrl);
Map<String, String> params = mapOf("scope", scope,
"scope_org", scopeOrg.replaceAll(":", "%3A")
.replaceAll("/", "%2F"),
"timestamp", urlEncode(timestamp),
"state", state,
"client_id", clientId,
"client_secret", clientSecret,
"response_type", responseType,
"redirect_uri", redirectUrlEncoded,
"obj_type", "B L F A",
"client_certificate_hash", esiaConfig.getClientCertHash());
return makeRequest(url, params);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Map<String, String> mapOf(String... params) {
if (params.length % 2 != 0) {
throw new IllegalArgumentException("Invalid params count");
}
Map<String, String> map = new LinkedHashMap<>(params.length / 2);
for (int i = 0; i < params.length / 2; i++) {
map.put(params[i * 2], params[i * 2 + 1]);
}
return map;
}
private static String urlEncode(String s) {
try {
return URLEncoder.encode(s, "UTF-8")
.replace("+", "%20");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private static String makeRequest(URL url, Map<String, String> params) {
StringBuilder uriBuilder = new StringBuilder(url.toString());
uriBuilder.append('?');
for (Map.Entry<String, String> node : params.entrySet()) {
uriBuilder.append(node.getKey()).append('=').append(node.getValue()).append("&");
}
return uriBuilder.toString();
}
public boolean getEsiaTokensByCode(String esiaAuthCode, 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 redirectUrl = esiaConfig.getRedirectUrl();
String scope = esiaConfig.getEsiaScopes();
String scopeOrg = esiaConfig.getEsiaOrgScopes();
Map<String, String> parameters = new LinkedHashMap<String, String>();
parameters.put("client_id", clientId);
parameters.put("scope", scope);
parameters.put("scope_org", scopeOrg);
parameters.put("timestamp", timestamp);
parameters.put("state", state);
parameters.put("redirect_uri", redirectUrl);
parameters.put("code", esiaAuthCode);
String clientSecret = signMap(parameters);
String authUrl = esiaConfig.getEsiaTokenUri();
String postBody = new FormUrlencoded()
.setParameter("client_id", clientId)
.setParameter("code", esiaAuthCode)
.setParameter("grant_type", "authorization_code")
.setParameter("client_secret", clientSecret)
.setParameter("state", state)
.setParameter("redirect_uri", redirectUrl)
.setParameter("scope", scope)
.setParameter("scope_org", scopeOrg)
.setParameter("timestamp", timestamp)
.setParameter("token_type", "Bearer")
.setParameter("client_certificate_hash", esiaConfig.getClientCertHash())
.toFormUrlencodedString();
HttpRequest postReq = HttpRequest.newBuilder(URI.create(authUrl))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(postBody))
.timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> postResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(postReq, HttpResponse.BodyHandlers.ofString());
String responseString = postResp.body();
EsiaTokenResponse tokenResponse = objectMapper.readValue(responseString, EsiaTokenResponse.class);
if (tokenResponse != null && tokenResponse.getError() != null) {
throw new RuntimeException(tokenResponse.getError_description());
}
String accessToken = tokenResponse.getAccess_token();
boolean hasRole = ulDataService.checkRole(accessToken);
if (!hasRole) {
throw new RuntimeException("The user does not have the required role");
}
Cookie cookie = new Cookie("access_token", accessToken);
cookie.setHttpOnly(true);
cookie.setPath("/");
response.addCookie(cookie);
String refreshToken = tokenResponse.getRefresh_token();
Cookie cookieRefresh = new Cookie("refresh_token", refreshToken);
cookieRefresh.setHttpOnly(true);
cookieRefresh.setPath("/");
response.addCookie(cookieRefresh);
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), tokenResponse.getExpires_in());
Cookie authToken = new Cookie("auth_token", token.getValue());
authToken.setPath("/");
authToken.setHttpOnly(true);
response.addCookie(authToken);
SecurityContextHolder.getContext()
.setAuthentication(
new UsernamePasswordAuthenticationToken(esiaAccessToken.getSbj_id(), null));
Cookie isAuth = new Cookie("is_auth", "true");
isAuth.setPath("/");
response.addCookie(isAuth);
return true;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void getEsiaTokensByRefreshToken(HttpServletRequest request, HttpServletResponse response) {
try {
String refreshToken = null;
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("refresh_token")) {
refreshToken = cookie.getValue();
}
}
}
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 redirectUrl = esiaConfig.getRedirectUrl();
String scope = esiaConfig.getEsiaScopes();
String scopeOrg = esiaConfig.getEsiaOrgScopes();
Map<String, String> parameters = new LinkedHashMap<String, String>();
parameters.put("client_id", clientId);
parameters.put("scope", scope);
parameters.put("scope_org", scopeOrg);
parameters.put("timestamp", timestamp);
parameters.put("state", state);
parameters.put("redirect_uri", esiaConfig.getRedirectUrl());
parameters.put("refresh_token", refreshToken);
String clientSecret = signMap(parameters);
String authUrl = esiaConfig.getEsiaTokenUri();
String postBody = new FormUrlencoded()
.setParameter("client_id", clientId)
.setParameter("refresh_token", refreshToken)
.setParameter("grant_type", "refresh_token")
.setParameter("client_secret", clientSecret)
.setParameter("state", state)
.setParameter("redirect_uri", redirectUrl)
.setParameter("scope", scope)
.setParameter("scope_org", scopeOrg)
.setParameter("timestamp", timestamp)
.setParameter("token_type", "Bearer")
.setParameter("client_certificate_hash", esiaConfig.getClientCertHash())
.toFormUrlencodedString();
HttpRequest postReq = HttpRequest.newBuilder(URI.create(authUrl))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(postBody))
.timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> postResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(postReq, HttpResponse.BodyHandlers.ofString());
String responseString = postResp.body();
EsiaTokenResponse tokenResponse = objectMapper.readValue(responseString, EsiaTokenResponse.class);
if (tokenResponse != null && tokenResponse.getError() != null) {
throw new RuntimeException(tokenResponse.getError_description());
}
String accessToken = tokenResponse.getAccess_token();
Cookie cookie = new Cookie("access_token", accessToken);
cookie.setHttpOnly(true);
cookie.setPath("/");
response.addCookie(cookie);
String newRefreshToken = tokenResponse.getRefresh_token();
Cookie cookieRefresh = new Cookie("refresh_token", newRefreshToken);
cookieRefresh.setHttpOnly(true);
cookieRefresh.setPath("/");
response.addCookie(cookieRefresh);
EsiaAccessToken esiaAccessToken = ulDataService.readToken(accessToken);
Token token = jwtTokenService.createAccessToken(esiaAccessToken.getSbj_id(), tokenResponse.getExpires_in());
Cookie authToken = new Cookie("auth_token", token.getValue());
authToken.setPath("/");
authToken.setHttpOnly(true);
response.addCookie(authToken);
SecurityContextHolder.getContext()
.setAuthentication(
new UsernamePasswordAuthenticationToken(esiaAccessToken.getSbj_id(), null));
Cookie isAuth = new Cookie("is_auth", "true");
isAuth.setPath("/");
response.addCookie(isAuth);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private String signMap(Map<String, String> paramsToSign) {
try {
StringBuilder toSign = new StringBuilder();
for (String s : paramsToSign.values()) {
toSign.append(s);
}
String requestBody = toSign.toString();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(esiaConfig.getSignUrl()))
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString(requestBody, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(request, HttpResponse.BodyHandlers.ofString());
errorHandler(response);
return response.body();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private void errorHandler(HttpResponse httpResponse) {
if (httpResponse.statusCode() != 200) {
throw new RuntimeException(httpResponse.statusCode() + " " + httpResponse.body());
}
}
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.getName().equals("is_auth")) {
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);
}
}
}

View file

@ -0,0 +1,99 @@
package ru.micord.ervu.security.esia.service;
import java.util.Arrays;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.micord.ervu.security.esia.model.*;
/**
* @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

@ -0,0 +1,26 @@
package ru.micord.ervu.security.esia.service;
import ru.micord.ervu.security.esia.model.EmployeeModel;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.OrganizationModel;
import ru.micord.ervu.security.esia.model.PersonModel;
/**
* @author Eduard Tihomirov
*/
public interface UlDataService {
boolean checkRole(String accessToken);
EmployeeModel getEmployeeModel(String accessToken);
EmployeeModel getChiefEmployeeModel(String accessToken);
OrganizationModel getOrganizationModel(String accessToken);
PersonModel getPersonData(String prnsId, String accessToken);
EsiaAccessToken readToken(String accessToken);
String getAllUserRoles(String accessToken);
}

View file

@ -0,0 +1,257 @@
package ru.micord.ervu.security.esia.service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
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 ru.micord.ervu.security.esia.config.EsiaConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import ru.micord.ervu.security.esia.model.*;
/**
* @author Eduard Tihomirov
*/
@Service
public class UlDataServiceImpl implements UlDataService {
@Autowired
private EsiaConfig esiaConfig;
@Autowired
private ObjectMapper objectMapper;
@Override
public EmployeeModel getEmployeeModel(String accessToken) {
EsiaAccessToken esiaAccessToken = readToken(accessToken);
String scope = esiaAccessToken.getScope();
String orgOid = scope.substring(scope.indexOf('=') + 1, scope.indexOf(' '));
String prnsId = esiaAccessToken.getSbj_id();
EmployeeModel employeeModel = getEmplData(prnsId, orgOid, accessToken);
return employeeModel;
}
@Override
public EmployeeModel getChiefEmployeeModel(String accessToken) {
try {
EsiaAccessToken esiaAccessToken = readToken(accessToken);
String scope = esiaAccessToken.getScope();
String orgOid = scope.substring(scope.indexOf('=') + 1, scope.indexOf(' '));
String url = esiaConfig.getEsiaBaseUri() + "rs/orgs/" + orgOid + "/emps?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(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(getReq, HttpResponse.BodyHandlers.ofString());
errorHandler(getResp);
EmployeeModel[] employeeModels = objectMapper.readValue(
objectMapper.readTree(getResp.body()).get("elements").toString(), EmployeeModel[].class);
Optional<EmployeeModel> chief = Arrays.stream(employeeModels)
.filter(employee -> employee.getChief().equals(true) && employee.getBlocked().equals(false))
.findFirst();
if (chief.isPresent()) {
EmployeeModel chiefModel = chief.get();
PersonModel personModel = getPersonData(chiefModel.getPrnOid(), accessToken);
chiefModel.setPerson(personModel);
return chiefModel;
}
return null;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public OrganizationModel getOrganizationModel(String accessToken) {
EsiaAccessToken esiaAccessToken = readToken(accessToken);
String scope = esiaAccessToken.getScope();
String orgOid = scope.substring(scope.indexOf('=') + 1, scope.indexOf(' '));
OrganizationModel organizationModel = getOrgModel(orgOid, accessToken);
return organizationModel;
}
private EmployeeModel getEmplData(String prnsId, String orgOid, String accessToken) {
try {
String url = esiaConfig.getEsiaBaseUri() + "rs/orgs/" + orgOid + "/emps/" + prnsId + "?embed=(elements.person)";
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(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(getReq, HttpResponse.BodyHandlers.ofString());
errorHandler(getResp);
return objectMapper.readValue(getResp.body(), EmployeeModel.class);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public PersonModel getPersonData(String prnsId, String accessToken) {
try {
String url = esiaConfig.getEsiaBaseUri() + "rs/prns/" + prnsId;
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(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(getReq, HttpResponse.BodyHandlers.ofString());
errorHandler(getResp);
return objectMapper.readValue(getResp.body(), PersonModel.class);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public EsiaAccessToken readToken(String accessToken) {
try {
byte[] decodedBytes = Base64.getDecoder()
.decode(
accessToken.substring(accessToken.indexOf('.') + 1, accessToken.lastIndexOf('.'))
.replace('-', '+')
.replace('_', '/'));
String decodedString = new String(decodedBytes);
EsiaAccessToken esiaAccessToken = objectMapper.readValue(decodedString,
EsiaAccessToken.class
);
return esiaAccessToken;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private OrganizationModel getOrgModel(String orgOid, String accessToken) {
try {
String url = esiaConfig.getEsiaBaseUri() + "rs/orgs/" + orgOid;
HttpRequest getReq = HttpRequest.newBuilder(URI.create(url + "?embed=(contacts.elements,addresses.elements)"))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ".concat(accessToken))
.GET()
.timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(getReq, HttpResponse.BodyHandlers.ofString());
errorHandler(getResp);
OrganizationModel organizationModel = objectMapper.readValue(getResp.body(),
OrganizationModel.class
);
HttpRequest getReqBrhs = HttpRequest.newBuilder(
URI.create(url + "/brhs?embed=(elements,addresses.elements,contacts.elements)"))
.header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ".concat(accessToken))
.GET()
.timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getRespBrhs = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(getReqBrhs, HttpResponse.BodyHandlers.ofString());
errorHandler(getRespBrhs);
BrhsModel[] brhsModels = objectMapper.readValue(
objectMapper.readTree(getRespBrhs.body()).get("elements").toString(), BrhsModel[].class);
organizationModel.setBrhs(brhsModels);
return organizationModel;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private void errorHandler(HttpResponse httpResponse) {
if (httpResponse.statusCode() != 200) {
throw new RuntimeException(httpResponse.statusCode() + " " + httpResponse.body());
}
}
@Override
public boolean checkRole(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(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.build()
.send(getReq, HttpResponse.BodyHandlers.ofString());
errorHandler(getResp);
if (getResp.body().contains("MNSV89_UPLOAD_DATA")) {
return true;
}
else {
return false;
}
}
catch (Exception e) {
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(esiaConfig.getRequestTimeout()))
.build();
HttpResponse<String> getResp = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
.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.webbpm.jwt;
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,57 @@
package ru.micord.ervu.security.webbpm.jwt;
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;
import ru.micord.ervu.security.webbpm.jwt.model.Token;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
@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,29 @@
package ru.micord.ervu.security.webbpm.jwt;
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

@ -0,0 +1,84 @@
package ru.micord.ervu.security.webbpm.jwt.filter;
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;
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
/**
* @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,37 @@
package ru.micord.ervu.security.webbpm.jwt.model;
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,75 @@
package ru.micord.ervu.security.webbpm.jwt.service;
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.micord.ervu.security.webbpm.jwt.model.Token;
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

@ -41,4 +41,4 @@ RUN chmod -R +x patches && \
ENV SERVER_START=true
COPY --chown=jboss *.ear $JBOSS_HOME/standalone/deployments/
HEALTHCHECK --timeout=3s --start-period=3600s CMD curl --fail 127.0.0.1:8080/backend/version || exit 1
HEALTHCHECK --timeout=3s --start-period=3600s CMD curl --fail 127.0.0.1:8080/ul/version || exit 1

36
config/context.xml Normal file
View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
<Resource name="java:/webbpm/AppDS" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://10.10.31.119:5432/ervu-lkrp-ul"
username="ervu-lkrp-ul" password="ervu-lkrp-ul" maxTotal="20" maxIdle="10" maxWaitMillis="-1"/>
</Context>

View file

@ -16,41 +16,6 @@ xa-data-source add \
--query-timeout=300 \
--xa-datasource-properties=ServerName=${env.DB_APP_HOST:db},PortNumber=${env.DB_APP_PORT:5432},DatabaseName=${env.DB_APP_NAME:app}
xa-data-source add \
--name=JBPMDS \
--enabled=true \
--driver-name=postgresql \
--jndi-name=java:jboss/datasources/jbpmDS \
--user-name=${env.DB_JBPM_USERNAME:jbpm} \
--password=${env.DB_JBPM_PASSWORD:jbpmpassword} \
--use-ccm=true \
--valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker \
--validate-on-match=false \
--background-validation=true \
--background-validation-millis=5000 \
--exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter \
--statistics-enabled=true \
--max-pool-size=50 \
--query-timeout=300 \
--xa-datasource-properties=ServerName=${env.DB_JBPM_HOST:db},PortNumber=${env.DB_JBPM_PORT:5432},DatabaseName=${env.DB_JBPM_NAME:jbpm}
xa-data-source add \
--name=SECURITYDS \
--enabled=true \
--driver-name=postgresql \
--jndi-name=java:/webbpm/security-ds \
--user-name=${env.DB_SEC_USERNAME:security_user} \
--password=${env.DB_SEC_PASSWORD:secpassword} \
--max-pool-size=70 \
--valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker \
--validate-on-match=false \
--background-validation=true \
--background-validation-millis=5000 \
--exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter \
--statistics-enabled=true \
--query-timeout=300 \
--xa-datasource-properties=ServerName=${env.DB_SEC_HOST:db},PortNumber=${env.DB_SEC_PORT:5432},DatabaseName=${env.DB_SEC_NAME:app}
/system-property=ldap.mapping.login.param:add(value=${env.WEBBPM_LDAP_LOGIN_ATTR:uid})
/system-property=ldap.mapping.org.code.param:add(value=${env.WEBBPM_LDAP_ORGANIZATION_ATTR:ou})
/system-property=jboss.as.management.blocking.timeout:add(value=900)
@ -62,6 +27,7 @@ xa-data-source add \
/system-property=gar.enable:add(value=false)
/system-property=security.password.regex:add(value="^((?=(.*\\d){1,})(?=.*[a-zа-яё])(?=.*[A-ZА-ЯЁ]).{8,})$")
/system-property=fias.enable:add(value=false)
/system-property=bpmn.enable:add(value=false)
/system-property=file.webdav.upload.url:add(value="https://ervu-webdav.k8s.micord.ru")
/system-property=file.webdav.upload.username:add(value="test")
/system-property=file.webdav.upload.password:add(value="test")
@ -74,6 +40,17 @@ xa-data-source add \
/system-property=ervu.fileupload.max_file_size:add(value="5242880")
/system-property=ervu.fileupload.max_request_size:add(value="6291456")
/system-property=ervu.fileupload.file_size_threshold:add(value="0")
/system-property=esia-scopes:add(value="fullname, snils, id_doc, birthdate, usr_org, openid")
/system-property=esia-org-scopes:add(value="org_fullname, org_shortname, org_brhs, org_brhs_ctts, org_brhs_addrs, org_type, org_ogrn, org_inn, org_leg, org_kpp, org_ctts, org_addrs, org_grps, org_emps")
/system-property=esia-org-scope-url:add(value="http://esia.gosuslugi.ru/")
/system-property=esia-uri.base-uri:add(value="https://esia-portal1.test.gosuslugi.ru/")
/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-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")
/system-property=client-cert-hash:add(value="04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD")
/system-property=ervu.cron.load.enable(value="true")
/system-property=ervu.cron.load.time(value="0 0 */1 * * *")
/system-property=ervu.esnsi.classifier.url.load(value="https://esnsi.gosuslugi.ru/rest/ext/v1/classifiers/11465/file?extension=JSON&encoding=UTF_8"")

View file

@ -66,6 +66,17 @@
<property name="ervu.fileupload.max_file_size" value="5242880"/>
<property name="ervu.fileupload.max_request_size" value="6291456"/>
<property name="ervu.fileupload.file_size_threshold" value="0"/>
<property name="esia-scopes" value="fullname, snils, id_doc, birthdate, usr_org, openid"/>
<property name="esia-org-scopes" value="org_fullname, org_shortname, org_brhs, org_brhs_ctts, org_brhs_addrs, org_type, org_ogrn, org_inn, org_leg, org_kpp, org_ctts, org_addrs, org_grps, org_emps"/>
<property name="esia-org-scope-url" value="http://esia.gosuslugi.ru/"/>
<property name="esia-uri.base-uri" value="https://esia-portal1.test.gosuslugi.ru/"/>
<property name="esia-uri.code-path" value="https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v2/ac"/>
<property name="esia-uri.token-path" value="https://esia-portal1.test.gosuslugi.ru/aas/oauth2/v3/te"/>
<property name="esia-client-id" value="MNSV89"/>
<property name="esia-redirect-url" value="https://lkrp.micord.ru"/>
<property name="sign-url" value="https://ervu-sign-dev.k8s.micord.ru/sign"/>
<property name="sesia-uri.logout" value="https://esia-portal1.test.gosuslugi.ru/idp/ext/Logout"/>
<property name="client-cert-hash" value="04508B4B0B58776A954A0E15F574B4E58799D74C61EE020B3330716C203E3BDD"/>
<property name="ervu.cron.load.time" value="0 0 */1 * * *"/>
<property name="ervu.esnsi.classifier.url.load" value="https://esnsi.gosuslugi.ru/rest/ext/v1/classifiers/11465/file?extension=JSON&amp;encoding=UTF_8"/>
</system-properties>

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
export JAVA_OPTS="$JAVA_OPTS \
-Ddb.app.host=${DB_APP_HOST:-db} \
-Ddb.app.port=${DB_APP_PORT:-5432} \
-Ddb.app.name=${DB_APP_NAME:-app} \
-Ddb.app.username=${DB_APP_USERNAME:-app_user} \
-Ddb.app.password=${DB_APP_PASSWORD:-apppassword} \
"

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
<Resource name="java:/webbpm/AppDS" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://${db.app.host}:${db.app.port}/${db.app.name}"
username="${db.app.username}" password="${db.app.password}" maxTotal="20" maxIdle="10" maxWaitMillis="-1"/>
</Context>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<!--
By default, no user is included in the "manager-gui" role required
to operate the "/manager/html" web application. If you wish to use this app,
you must define such a user - the username and password are arbitrary.
Built-in Tomcat manager roles:
- manager-gui - allows access to the HTML GUI and the status pages
- manager-script - allows access to the HTTP API and the status pages
- manager-jmx - allows access to the JMX proxy and the status pages
- manager-status - allows access to the status pages only
The users below are wrapped in a comment and are therefore ignored. If you
wish to configure one or more of these users for use with the manager web
application, do not forget to remove the <!.. ..> that surrounds them. You
will also need to set the passwords to something appropriate.
-->
<user username="admin" password="<must-be-changed>" roles="manager-gui"/>
</tomcat-users>

View file

@ -0,0 +1,15 @@
# WebBPM properties
authentication.method=form
bpmn.enable=false
fias.enable=false
gar.enable=false
reset_password.mail.template.path=mail/reset_password.html
security.password.regex=^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$
webbpm.mode=production
webbpm.jbpm.hibernate_statistics.enabled=false
webbpm.cache.hazelcast.hosts=127.0.0.1
webbpm.cache.hazelcast.outbound_port_definitions=5801-5820

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Context antiResourceLocking="false" privileged="true" >
<CookieProcessor className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
sameSiteCookies="strict" />
<!--
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="d+\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
-->
<Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>

View file

@ -2,27 +2,27 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ervu_lkrp_ul</groupId>
<artifactId>ervu_lkrp_ul</artifactId>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>ul</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>distribution</artifactId>
<packaging>ear</packaging>
<properties>
<backendContext>/backend</backendContext>
<backendContext>/ul</backendContext>
</properties>
<dependencies>
<dependency>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>backend</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>frontend</artifactId>
<type>war</type>
</dependency>
@ -37,16 +37,16 @@
<configuration>
<modules>
<webModule>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>frontend</artifactId>
<contextRoot>/</contextRoot>
<bundleFileName>frontend.war</bundleFileName>
</webModule>
<webModule>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>backend</artifactId>
<contextRoot>${backendContext}</contextRoot>
<bundleFileName>backend.war</bundleFileName>
<bundleFileName>ul.war</bundleFileName>
</webModule>
</modules>
</configuration>
@ -59,7 +59,7 @@
<profile>
<id>enable-version-in-url</id>
<properties>
<backendContext>/backend-${project.version}</backendContext>
<backendContext>/ul-${project.version}</backendContext>
</properties>
</profile>
</profiles>

View file

@ -1,5 +1,5 @@
{
"name": "webbpm-frontend",
"name": "ervu_lkrp_ul",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
@ -1624,9 +1624,9 @@
}
},
"@webbpm/base-package": {
"version": "3.177.0",
"resolved": "https://repo.micord.ru/repository/npm-all/@webbpm/base-package/-/base-package-3.177.0.tgz",
"integrity": "sha512-YgjHHcIR0m/QHujiU17e4NmGmSRxUg88G609KkTGh8XreTXbpYwRxo+3Vr5ttfjk0ND1NMWt6L2TlTLlaCgpkQ==",
"version": "3.178.2",
"resolved": "https://repo.micord.ru/repository/npm-all/@webbpm/base-package/-/base-package-3.178.2.tgz",
"integrity": "sha512-ShqAmiaGCvrg7ffrhntshwcZJVW8cK10JsMy/OT36p7iK6B/IR0YCJZZ+GIhakLo0CZsEokhcpKBdORvfOI55g==",
"requires": {
"tslib": "^1.9.0"
}

View file

@ -1,5 +1,5 @@
{
"name": "webbpm-frontend",
"name": "ervu_lkrp_ul",
"version": "1.0.0",
"scripts": {
"lite": "node ./node_modules/lite-server/bin/lite-server",
@ -27,7 +27,7 @@
"@angular/platform-browser-dynamic": "7.2.15",
"@angular/router": "7.2.15",
"@ng-bootstrap/ng-bootstrap": "4.1.1",
"@webbpm/base-package": "3.177.0",
"@webbpm/base-package": "3.178.2",
"ag-grid-angular": "29.0.0-micord.4",
"ag-grid-community": "29.0.0-micord.4",
"angular-calendar": "0.28.28",

View file

@ -2,12 +2,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ervu_lkrp_ul</groupId>
<artifactId>ervu_lkrp_ul</artifactId>
<groupId>ru.micord.ervu.lkrp</groupId>
<artifactId>ul</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>ervu_lkrp_ul.ervu_lkrp_ul</groupId>
<groupId>ru.micord.ervu.lkrp.ul</groupId>
<artifactId>frontend</artifactId>
<packaging>war</packaging>

View file

@ -18,7 +18,7 @@
});
</script>
</head>
<body preview-container id="webbpm-angular-application-container ervu_lkrp_ul" class="webbpm">
<body preview-container id="webbpm-angular-application-container" class="webbpm ervu_lkrp_ul">
<div class="loader"></div>
</body>
</html>

View file

@ -5,6 +5,7 @@
"filter_cleanup_check_period_minutes": 30,
"auth_method": "form",
"enable.version.in.url": "%enable.version.in.url%",
"backend.context": "ul",
"guard.confirm_exit": false,
"message_service_error_timeout": "",
"message_service_warning_timeout": "",

View file

@ -1,34 +0,0 @@
<div *ngIf="(currentSession | async)?.authorities.includes('BPMN.ADMIN.PROCESS_INSTANCE.LIST') ||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.USER.LIST') ||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.GROUP.LIST') ||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ROLE.LIST') ||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ORG_UNIT.LIST') ||
(currentSession | async)?.authorities.includes('USER_MANAGEMENT.AUTHORITY.LIST')" ngbDropdown class="nav-item" [placement]="placement">
<button class="nav-link bi bi-gear-fill" id="adminDropdownMenu" ngbDropdownToggle title="Администрирование"></button>
<div ngbDropdownMenu aria-labelledby="adminDropdownMenu">
<button *ngIf="(currentSession | async)?.authorities.includes('BPMN.ADMIN.PROCESS_INSTANCE.LIST')"
routerLink="/process/instance" ngbDropdownItem>
Экземпляры процессов
</button>
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.USER.LIST')"
routerLink="/user-management/users" ngbDropdownItem>
Пользователи
</button>
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.GROUP.LIST')"
routerLink="/user-management/groups" ngbDropdownItem>
Группы
</button>
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ROLE.LIST')"
routerLink="/user-management/roles" ngbDropdownItem>
Роли
</button>
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.ORG_UNIT.LIST')"
routerLink="/user-management/org-units" ngbDropdownItem>
Организации
</button>
<button *ngIf="(currentSession | async)?.authorities.includes('USER_MANAGEMENT.AUTHORITY.LIST')"
routerLink="/user-management/authorities" ngbDropdownItem>
Безопасность действий
</button>
</div>
</div>

View file

@ -1,17 +1,10 @@
<nav class="header" *ngIf="currentSession | async as session" id="webbpm-header">
<nav class="header" id="webbpm-header">
<div class="header-logo">
<div class="logo"><a routerLink="/"></a></div>
<div class="main-page"><a routerLink="/">Главная</a></div>
<div class="header-menu-left"><a routerLink="/filesentlog">Журнал взаимодействий</a></div>
</div>
<div class="header-menu">
<!--
<process *ngIf="session.authorities.includes('BPMN.USER.START_PROCESS')"></process>
<div *ngIf="session.authorities.includes('BPMN.USER.TASK_LIST')">
<button class="nav-link bi bi-file-text-fill" (click)="openTaskList()" title="Задачи"></button>
</div>
<admin-menu [placement]="'bottom'"></admin-menu>
-->
<div ngbDropdown class="logout" log-out></div>
</div>
</nav>

View file

@ -1,27 +0,0 @@
<div class="confirm">
<div class="form-logo">
<div></div>
</div>
<div class="info">
<div>
<h2>Подтверждение почты</h2>
<div *ngIf="verificationStatus.toString() === 'VERIFYING'">
Подтверждение...
</div>
<div *ngIf="verificationStatus.toString() === 'VERIFIED'">
<div class="alert alert-success">
Адрес электронной почты успешно подтвержден
</div>
</div>
<div *ngIf="verificationStatus.toString() === 'FAILED'">
<div class="alert alert-danger">{{ errorMessage }}</div>
</div>
<div *ngIf="(currentSession | async) == null">
<a href="#/login"><span class="fa fa-lock"></span>Войти</a><br/>
<a href="#/registration">Зарегистрироваться</a>
</div>
</div>
</div>
</div>

View file

@ -1,11 +1,6 @@
<button class="user-info" ngbDropdownToggle *ngIf="currentSession | async as session">{{session.fullUserName}}</button>
<div ngbDropdownMenu *ngIf="currentSession | async as session">
<!--
<div class="user-info">
<div class="user-login">{{session.username}}</div>
</div>
-->
<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" *ngIf="isLogoutButtonVisible()" (click)="logout()">Выйти</button>
<button ngbDropdownItem class="exit" (click)="logout()">Выйти</button>
</div>

View file

@ -1,49 +0,0 @@
<div class="form-signin">
<form #formComponent="ngForm">
<div class="alert alert-success" [hidden]="!confirmationSent">На ваш почтовый адрес было отправлено письмо. Подтвердите почту, чтобы войти в систему
</div>
<div class="alert alert-danger" [hidden]="!errorMessage">{{errorMessage}}</div>
<div class="logo">
<h2>Воинский учет в организациях</h2>
</div>
<div class="row">
<div class="input-group">
<input type="text" name="username" class="form-control" placeholder=" " required autofocus [(ngModel)]="username" maxlength="100">
<label>Логин</label>
</div>
</div>
<div class="row">
<div class="input-group">
<input
[type]="passwordType ? 'text' : 'password'"
name="password"
class="form-control field-password-view"
placeholder=" " required
[(ngModel)]="password"
maxlength="100"
>
<label>Пароль</label>
<div class="input-group-append">
<span class="input-group-text">
<i
(click)="togglePasswordType()"
class="fa"
[ngClass]="{
'fa-eye': passwordType,
'fa-eye-slash': !passwordType
}"
></i>
</span>
</div>
</div>
<div class="msg-text"><a href="#/reset-password">Восстановить пароль</a></div>
</div>
<div class="btn-box">
<!--<esia-login-button></esia-login-button>-->
<button type="submit" class="btn btn-primary" (click)="formComponent.form.valid && login()">Войти</button>
</div>
</form>
</div>

View file

@ -1,103 +0,0 @@
<div class="form-signup">
<div class="form-logo">
<div></div>
</div>
<div class="form-new-password">
<form #formComponent="ngForm">
<div [hidden]="!errorMessage" class="alert alert-danger">{{ errorMessage }}</div>
<p class="has-account">Вспомнили пароль?
<a href="#/login"><span class="fa fa-lock"></span>Войти</a></p>
<p class="has-account">Задайте новый пароль</p>
<div class="row">
<label>Пароль</label>
<div class="input-group">
<input
#passwordInput="ngModel"
[(ngModel)]="password"
[type]="passwordType ? 'text' : 'password'"
class="form-control"
maxlength="32"
minlength="6"
name="password"
pattern="^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$"
required
(change)="validPasswords()"
>
<div class="input-group-append">
<span class="input-group-text">
<i
(click)="togglePasswordType()"
class="fa"
[ngClass]="{
'fa-eye': passwordType,
'fa-eye-slash': !passwordType
}"
></i>
</span>
</div>
</div>
<div *ngIf="passwordInput.invalid && (passwordInput.dirty || passwordInput.touched)">
<div *ngIf="passwordInput.errors.required" class="msg-alert">Поле обязательно
</div>
<div *ngIf="passwordInput.errors.minlength" class="msg-alert">Пароль должен
содержать как минимум 6 символов
</div>
<div *ngIf="passwordInput.errors.pattern" class="msg-alert">Пароль должен
содержать заглавные и прописные буквы и как минимум 1 цифру
</div>
</div>
</div>
<div class="row">
<label>Подтверждение пароля</label>
<div class="input-group">
<input
#confirmPasswordInput="ngModel"
[(ngModel)]="confirmPassword"
[type]="confirmPasswordType ? 'text' : 'password'"
class="form-control"
maxlength="32"
minlength="6"
name="confirmPassword"
pattern="^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$"
required
(change)="validPasswords()"
>
<div class="input-group-append">
<span class="input-group-text">
<i
(click)="toggleConfirmPasswordType()"
class="fa"
[ngClass]="{
'fa-eye': confirmPasswordType,
'fa-eye-slash': !confirmPasswordType
}"
></i>
</span>
</div>
</div>
<div *ngIf="confirmPasswordInput.invalid && (confirmPasswordInput.dirty || confirmPasswordInput.touched)">
<div *ngIf="confirmPasswordInput.errors.required" class="msg-alert">Поле обязательно
</div>
<div *ngIf="confirmPasswordInput.errors.minlength" class="msg-alert">Пароль должен
содержать как минимум 6 символов
</div>
<div *ngIf="confirmPasswordInput.errors.pattern" class="msg-alert">Пароль должен
содержать заглавные и прописные буквы и как минимум 1 цифру
</div>
</div>
</div>
<div class="reset-password-btn-box">
<button
(click)="formComponent.form.valid && validPasswords() && changePassword()"
[disabled]="!formComponent.form.valid && !validPasswords()"
class="btn btn-primary"
type="submit"
>
Изменить пароль
</button>
</div>
</form>
</div>
</div>

View file

@ -1,12 +0,0 @@
<div ngbDropdown class="nav-item">
<button class="nav-link bi bi-clipboard-plus-fill" id="startProcessDropdownMenu" ngbDropdownToggle title="Создать"></button>
<div ngbDropdownMenu aria-labelledby="startProcessDropdownMenu">
<div class="dropdown-menu-inner">
<div *ngFor="let process of processList">
<button (click)="startProcess(process.processDefId)" ngbDropdownItem>
{{ process.name }}
</button>
</div>
</div>
<div>
</div><!-- TODO: move to directive or something else -->

View file

@ -1,121 +0,0 @@
<div class="form-signup">
<div class="form-logo">
<div></div>
</div>
<div class="form-register">
<form #formComponent="ngForm">
<div [hidden]="!errorMessage" class="alert alert-danger">{{ errorMessage }}</div>
<h2>Регистрация</h2>
<p class="has-account">Уже зарегистрированы?
<a href="#/login"><span class="fa fa-lock"></span>Войти</a></p>
<div class="row">
<label>Имя</label>
<input
#name="ngModel"
[(ngModel)]="username"
class="form-control"
maxlength="100"
name="username"
required
type="text"
>
<div *ngIf="name.invalid && (name.dirty || name.touched)">
<div *ngIf="name.errors.required" class="msg-alert">Поле обязательно</div>
</div>
</div>
<div class="row">
<label>Адрес эл. почты</label>
<input
#emailInput="ngModel"
[(ngModel)]="email"
class="form-control"
email
maxlength="100"
name="email"
required
type="email"
>
<div *ngIf="emailInput.invalid && (emailInput.dirty || emailInput.touched)">
<div *ngIf="emailInput.errors.required" class="msg-alert">Поле обязательно</div>
<div *ngIf="emailInput.errors.email" class="msg-alert">Неверный формат адреса
эл. почты
</div>
</div>
</div>
<div class="row">
<label>Номер телефона</label>
<international-phone-number
#phoneInput="ngModel"
[(ngModel)]="phoneNumber"
[defaultCountry]="'ru'"
[pattern]="'^\\+(?!7 ?\\d{11})[0-9 ]+$'"
maxlength="20"
minlength="8"
name="phoneNumber"
placeholder="+79991112233"
(focusout)="phoneInputFocusOut()"
required
></international-phone-number>
<div *ngIf="phoneInput.invalid && (phoneInput.dirty || phoneIsTouched)">
<div *ngIf="phone.selectedCountry">
<div *ngIf="phoneHasOnlyDialCode()" class="msg-alert">Поле обязательно</div>
<div *ngIf="!phoneHasOnlyDialCode()" class="msg-alert">Введите корректный номер</div>
</div>
<div *ngIf="!phone.selectedCountry">
<div *ngIf="phoneInput.errors.required" class="msg-alert">Поле обязательно</div>
<div *ngIf="!phoneInput.errors.required" class="msg-alert">Введите код страны</div>
</div>
</div>
</div>
<div class="row">
<label>Пароль</label>
<div class="input-group">
<input
#passwordInput="ngModel"
[(ngModel)]="password"
[type]="fieldType ? 'text' : 'password'"
class="form-control"
maxlength="32"
minlength="8"
name="password"
[pattern]="passwordPattern"
required
>
<div class="input-group-append">
<span class="input-group-text">
<i
(click)="toggleFieldType()"
class="fa"
[ngClass]="{
'fa-eye': fieldType,
'fa-eye-slash': !fieldType
}"
></i>
</span>
</div>
</div>
<div *ngIf="passwordInput.invalid && (passwordInput.dirty || passwordInput.touched)">
<div *ngIf="passwordInput.errors.required" class="msg-alert">Поле обязательно
</div>
<div *ngIf="passwordInput.errors.minlength" class="msg-alert">Пароль должен
содержать как минимум 8 символов
</div>
<div *ngIf="passwordInput.errors.pattern" class="msg-alert" [innerText]="passwordPatternErrorMessage">
</div>
</div>
</div>
<div class="register-btn-box">
<button
(click)="formComponent.form.valid && register()"
[disabled]="!formComponent.form.valid"
class="btn btn-primary"
type="submit"
>
Зарегистрироваться
</button>
</div>
<div *ngIf="consent" [innerHTML]="consent" class="consent"></div>
</form>
</div>
</div>

View file

@ -1,45 +0,0 @@
<div class="form-signup">
<div class="form-logo">
<div></div>
</div>
<div class="form-reset-password">
<form #formComponent="ngForm">
<div [hidden]="!errorMessage" class="alert alert-danger">{{ errorMessage }}</div>
<p class="has-account">Вспомнили пароль?
<a href="#/login"><span class="fa fa-lock"></span>Войти</a></p>
<p class="has-account">Укажите адрес эл. почты, который был указан при регистрации,
на него пришлем временный пароль. Пароль сможете поменять в личном кабинете.
</p>
<div class="row">
<input
#emailInput="ngModel"
[(ngModel)]="email"
class="form-control"
email
maxlength="100"
name="email"
required
type="email"
>
<label>Адрес эл. почты</label>
<div *ngIf="emailInput.invalid && (emailInput.dirty || emailInput.touched)" class="msg-text">
<div *ngIf="emailInput.errors.required" class="msg-alert">Поле обязательно</div>
<div *ngIf="emailInput.errors.email" class="msg-alert">Неверный формат адреса эл. почты
</div>
</div>
</div>
<div class="btn-box">
<button
(click)="formComponent.form.valid && resetPassword()"
[disabled]="!formComponent.form.valid"
class="btn btn-primary"
type="submit"
>
Восстановить
</button>
</div>
</form>
</div>
</div>

View file

@ -1,3 +0,0 @@
<div id="page">
<router-outlet></router-outlet>
</div>

View file

@ -1,49 +0,0 @@
<div class="inner">
<div class="task-list">
<div class="task-list-tree-panel">
<div class="task-list-filter">
<ul>
<li>Фильтры</li>
<li>
<label>
<input type="radio" name="task-filter" value="All" [(ngModel)]="showMode" (ngModelChange)="filterVisibleTasks()">
<span>Все [{{tasks.length}}]</span>
</label></li>
<li class="ontime">
<label>
<input type="radio" name="task-filter" value="OnTime" [(ngModel)]="showMode" (ngModelChange)="filterVisibleTasks()">
<span><div></div>Без превышения срока [{{onTimeTasks.length}}]</span>
</label></li>
<li class="overdue">
<label>
<input type="radio" name="task-filter" value="Overdue" [(ngModel)]="showMode" (ngModelChange)="filterVisibleTasks()">
<span><div></div>С превышением срока [{{overdueTasks.length}}]</span>
</label></li>
</ul>
</div>
</div>
<div class="task-list-workplace">
<div class="alert alert-danger" [hidden]="!errorMessage">{{errorMessage}}</div>
<div class="table task-tbl">
<div class="thead">
<div class="tr">
<div class="th">Процесс</div>
<div class="th">Версия</div>
<div class="th">Задача</div>
<div class="th">Дата создания</div>
<div class="th">Срок</div>
</div>
</div>
<div *ngFor="let task of visibleTasks"
class="tr" [ngClass]="{'task-overdue': isOverdue(task), 'task-ontime': isOnTime(task)}"
(click)="startTask(task)">
<div class="td">{{ task.processName }}</div>
<div class="td">{{ task.processVersion }}</div>
<div class="td task">{{ task.name }}</div>
<div class="td">{{ task.createdOn | date:'dd.MM.yyyy HH:mm:ss' }}</div>
<div class="td">{{ task.expirationTime | date:'dd.MM.yyyy HH:mm:ss' }}</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,8 +0,0 @@
<div class="task-not-found-page">
<div class="task-not-found-container">
<div>:(</div>
<div><h2>Ошибка</h2>
Данная задача не существует.<br/>
Перейти к <a (click)="goToTaskList()" tabindex>списку задач</a></div>
</div>
</div>

View file

@ -0,0 +1,7 @@
import {Behavior, NotNull} from "@webbpm/base-package";
export class OrgData extends Behavior{
@NotNull()
public dataId: string;
}

View file

@ -0,0 +1,33 @@
import {AnalyticalScope, Behavior, Container, ControlWithValue} from "@webbpm/base-package";
import {HttpClient} from "@angular/common/http";
import {OrgData} from "./OrgData";
import {OrgInfoModel} from "../generated/ru/micord/ervu/security/esia/model/OrgInfoModel";
import {CookieService} from "ngx-cookie";
@AnalyticalScope(Container)
export class OrgDataRoot extends Behavior{
private container: Container;
initialize() {
super.initialize();
this.container = this.getScript(Container);
let orgScripts: OrgData[] = this.container.getScriptsInThisAndChildren(OrgData);
let httpClient = this.injector.get(HttpClient);
let cookieService = this.injector.get(CookieService);
if (cookieService.get("is_auth")) {
httpClient.get<OrgInfoModel>("esia/org")
.toPromise()
.then(orgInfoModel => {
if (orgInfoModel == null) {
return;
}
for (let orgData of orgScripts) {
let control: ControlWithValue = orgData.getScriptInObject(orgData.getObjectId(),
'component.ControlWithValue');
control.setValue(orgInfoModel[orgData.dataId]);
}
});
}
}
}

View file

@ -1,73 +1,24 @@
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {AccessDeniedComponent} from "./component/access-denied.component";
import {LoginComponent} from "./component/login.component";
import {AuthenticationGuard, ConfirmExitGuard, SignedInGuard, ProcessInstanceRouteResolver} from "@webbpm/base-package";
import {RegisterComponent} from "./component/register.component";
import {ConfirmUserEmailComponent} from "./component/confirm-user-email.component";
import {ResetPasswordComponent} from "./component/reset-password.component";
import {NewPasswordComponent} from "./component/new-password.component";
import {TaskListComponent} from "./component/task-list.component";
import {AuthGuard} from "../security/guard/auth.guard";
import {ConfirmExitGuard} from "@webbpm/base-package";
const appRoutes: Routes = [
{
path: 'login',
component: LoginComponent,
canActivate: [SignedInGuard]
},
{
path: 'access-denied',
component: AccessDeniedComponent,
canActivate: [AuthenticationGuard, ConfirmExitGuard]
},
{
path: 'registration',
component: RegisterComponent,
canActivate: [SignedInGuard]
},
{
path: 'confirm',
component: ConfirmUserEmailComponent
},
{
path: 'reset-password',
component: ResetPasswordComponent
},
{
path: 'new-password',
component: NewPasswordComponent
},
{
path: 'process',
canActivate: [AuthenticationGuard, ConfirmExitGuard],
children: [
{
path: 'instance',
loadChildren: 'generated-sources/page-process-instance-list.module#PageprocessinstancelistModule',
resolve: {
processInstanceId: ProcessInstanceRouteResolver
}
},
{
path: 'instance/:processInstanceId',
loadChildren: 'generated-sources/page-process-instance.module#PageprocessinstanceModule',
resolve: {
processInstanceId: ProcessInstanceRouteResolver
}
},
{
path: 'tasks',
component: TaskListComponent,
}
]
canActivate: [ConfirmExitGuard],
},
{
path: 'mydata',
loadChildren: 'generated-sources/page-mydata.module#PagemydataModule',
canActivate: [AuthGuard],
},
{
path: 'filesentlog',
loadChildren: 'generated-sources/page-filesentlog.module#PagefilesentlogModule',
canActivate: [AuthGuard],
}

View file

@ -11,25 +11,16 @@ import {
ProgressIndicationService,
SecurityModule
} from "@webbpm/base-package";
import {AdminMenuComponent} from "./component/admin-menu.component";
import {AppHeaderComponent} from "./component/app-header.component";
import {AppFooterComponent} from "./component/app-footer.component";
import {LogOutComponent} from "./component/logout.component";
import {LoginComponent} from "./component/login.component";
import {AccessDeniedComponent} from "./component/access-denied.component";
import {ApplicationVersionComponent} from "./component/application-version.component";
import {RouterModule} from "@angular/router";
import {RegisterComponent} from "./component/register.component";
import {ConfirmUserEmailComponent} from "./component/confirm-user-email.component";
import {InternationalPhoneNumberModule} from "ngx-international-phone-number";
import {ResetPasswordComponent} from "./component/reset-password.component";
import {NewPasswordComponent} from "./component/new-password.component";
import {AppProgressIndicationComponent} from "./component/app-progress-indication.component";
import {AppProgressIndicationService} from "./service/app-progress-indication.service";
import {TaskListComponent} from "./component/task-list.component";
import {ProcessListComponent} from "./component/process-list.component";
import {TaskComponent} from "./component/task.component";
import {TaskNotFoundComponent} from "./component/task-not-found.component";
import {FileUploadModule} from "ng2-file-upload";
import {ErvuFileUpload} from "../../ervu/component/fileupload/ErvuFileUpload";
@ -37,20 +28,10 @@ registerLocaleData(localeRu);
export const DIRECTIVES = [
forwardRef(() => AppHeaderComponent),
forwardRef(() => AppFooterComponent),
forwardRef(() => AdminMenuComponent),
forwardRef(() => ApplicationVersionComponent),
forwardRef(() => LogOutComponent),
forwardRef(() => LoginComponent),
forwardRef(() => AccessDeniedComponent),
forwardRef(() => RegisterComponent),
forwardRef(() => ConfirmUserEmailComponent),
forwardRef(() => ResetPasswordComponent),
forwardRef(() => NewPasswordComponent),
forwardRef(() => AppProgressIndicationComponent),
forwardRef(() => TaskListComponent),
forwardRef(() => ProcessListComponent),
forwardRef(() => TaskComponent),
forwardRef(() => TaskNotFoundComponent),
forwardRef(() => ErvuFileUpload)
];
@ -78,7 +59,7 @@ export const DIRECTIVES = [
{ provide: ProgressIndicationService, useClass: AppProgressIndicationService }
],
bootstrap: [],
entryComponents: [AppProgressIndicationComponent, TaskNotFoundComponent]
entryComponents: [AppProgressIndicationComponent]
})
export class AppModule {
}

View file

@ -1,23 +0,0 @@
import {ChangeDetectionStrategy, Component, Input} from "@angular/core";
import {UserService, Session} from "@webbpm/base-package";
import {NgbDropdownConfig, Placement} from "@ng-bootstrap/ng-bootstrap";
import {Observable} from "rxjs";
@Component({
moduleId: module.id,
selector: 'admin-menu',
templateUrl: '../../../../../src/resources/template/app/component/admin_menu.html',
providers: [NgbDropdownConfig],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AdminMenuComponent {
@Input()
public placement: Placement = 'bottom';
public currentSession: Observable<Session>;
constructor(protected userService: UserService, public config: NgbDropdownConfig) {
this.config.placement = this.placement;
this.currentSession = this.userService.getCurrentSession();
}
}

View file

@ -1,7 +1,5 @@
import {ChangeDetectionStrategy, Component} from "@angular/core";
import {Router} from "@angular/router";
import {UserService, Session} from "@webbpm/base-package";
import {Observable} from "rxjs";
@Component({
moduleId: module.id,
@ -11,14 +9,6 @@ import {Observable} from "rxjs";
})
export class AppHeaderComponent {
public currentSession: Observable<Session>;
constructor(protected userService: UserService,
protected router: Router) {
this.currentSession = this.userService.getCurrentSession();
}
public openTaskList(): void {
this.router.navigateByUrl("/process/tasks");
constructor(protected router: Router) {
}
}

View file

@ -1,51 +0,0 @@
import {ActivatedRoute, Router} from "@angular/router";
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input} from "@angular/core";
import {Session, UserService} from "@webbpm/base-package";
import {Observable} from "rxjs";
enum VerificationStatus {
VERIFYING = "VERIFYING",
VERIFIED = "VERIFIED",
FAILED = "FAILED"
}
@Component({
moduleId: module.id,
selector: "confirm",
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: "../../../../../src/resources/template/app/component/confirm-user-email.html"
})
export class ConfirmUserEmailComponent {
public verificationStatus = VerificationStatus.VERIFYING;
public currentSession: Observable<Session>;
@Input()
public errorMessage: string;
constructor(private router: Router, private userService: UserService,
private route: ActivatedRoute, private cd: ChangeDetectorRef) {
this.currentSession = this.userService.getCurrentSession();
}
ngOnInit() {
const link: string = this.route.snapshot.queryParamMap.get("link");
// remove link from url to prevent http referer leakage
this.router.navigate([], { relativeTo: this.route, replaceUrl: true });
this.userService.confirm(
link,
(reason) => {
this.verificationStatus = VerificationStatus.FAILED;
if (reason.status === 404) {
this.errorMessage = 'Ссылка недействительна. Требуется повторная регистрация.';
}
else {
this.errorMessage = 'Произошла ошибка, обратитесь в службу технической поддержки!';
}
this.cd.markForCheck();
})
.then(() => {
this.verificationStatus = VerificationStatus.VERIFIED;
this.cd.markForCheck();
});
}
}

View file

@ -1,68 +0,0 @@
import {Component, Input} from "@angular/core";
import {ActivatedRoute, Router} from "@angular/router";
import {UserService, Credentials} from "@webbpm/base-package";
@Component({
moduleId: module.id,
selector: "login",
templateUrl: "../../../../../src/resources/template/app/component/login.html"
})
export class LoginComponent {
@Input()
public username: string;
@Input()
public password: string;
public passwordType: boolean;
@Input()
public errorMessage: string;
@Input()
public confirmationSent: boolean;
constructor(private router: Router, private userService: UserService,
private route: ActivatedRoute) {
}
ngOnInit() {
this.confirmationSent = this.route.snapshot.queryParamMap.get('confirmationSent') === 'true';
this.router.navigate([], { relativeTo: this.route, replaceUrl: true });
}
public login(): void {
let credentials: Credentials = new Credentials();
credentials.username = this.username;
credentials.password = this.password;
this.userService.login(credentials, "Password")
.then(() => this.router.navigateByUrl("/"),
(reason: any) => {
switch (reason.status) {
case 401: {
this.errorMessage = "Неправильный логин или пароль";
break;
}
case 404: {
this.errorMessage = "Приложение стартует. Пожалуйста, подождите...";
break;
}
default: {
this.errorMessage =
"Произошла неизвестная ошибка, обратитесь в службу технической поддержки!";
break;
}
}
}
);
}
public goToRegister(): void {
this.router.navigateByUrl("/register");
}
togglePasswordType(): void {
this.passwordType = !this.passwordType;
}
}

View file

@ -1,37 +1,52 @@
import {Component} from "@angular/core";
import {UserService, Session, AuthenticationMethodService} from "@webbpm/base-package";
import {Observable} from "rxjs";
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 {
export class LogOutComponent implements OnInit{
public currentSession: Observable<Session>;
private userFullname: string;
private orgUnitName: string;
constructor(private userService: UserService, private authenticationMethodService: AuthenticationMethodService) {
this.currentSession = userService.getCurrentSession();
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.userService.logout();
this.httpClient.get<string>("esia/logout").toPromise().then(url => {
window.open(url, "_self");
})
}
public getCurrentUserName(): string {
return this.userService.getCurrentUserName();
public getUserFullname(): string {
return this.userFullname;
}
public getFullUserName(): string {
return this.userService.getFullUserName();
}
public isLogoutButtonVisible(): boolean {
return this.authenticationMethodService.isFormAuth();
public getIsAuth(): boolean {
return this.cookieService.get("is_auth") != null;
}
public getOrgUnitName(): string {
return this.userService.getOrgUnitName();
return this.orgUnitName;
}
}

View file

@ -1,79 +0,0 @@
import {ActivatedRoute, Router} from "@angular/router";
import {Component, Input} from "@angular/core";
import {Session, UserPasswordResetRequestDto, UserService} from "@webbpm/base-package";
import {Observable} from "rxjs";
@Component({
moduleId: module.id,
selector: "newPassword",
templateUrl: "../../../../../src/resources/template/app/component/new_password.html"
})
export class NewPasswordComponent {
public currentSession: Observable<Session>;
private token: string;
@Input()
public password: string;
public passwordType: boolean;
@Input()
public confirmPassword: string;
public confirmPasswordType: boolean;
@Input()
public errorMessage: string;
constructor(private router: Router, private userService: UserService,
private route: ActivatedRoute) {
this.currentSession = this.userService.getCurrentSession();
}
ngOnInit() {
this.token = this.route.snapshot.queryParamMap.get("token");
this.router.navigate([], {relativeTo: this.route, replaceUrl: true});
if (this.token == undefined || this.token === '') {
this.errorMessage = 'Ссылка недействительна. Требуется повторить восстановление пароля.';
return;
}
}
public changePassword(): void {
let dto: UserPasswordResetRequestDto = new UserPasswordResetRequestDto();
dto.password = this.password;
dto.passwordConfirm = this.confirmPassword;
this.userService.changePassword(dto, this.token)
.then(() => this.router.navigateByUrl("/"),
() => {
this.errorMessage =
'Произошла неизвестная ошибка, обратитесь в службу технической поддержки!';
});
}
togglePasswordType(): void {
this.passwordType = !this.passwordType;
}
toggleConfirmPasswordType(): void {
this.confirmPasswordType = !this.confirmPasswordType;
}
validPasswords(): boolean {
if (this.password === undefined || this.confirmPassword === undefined) {
return false;
}
let eq = this.password === this.confirmPassword;
if (!eq) {
this.errorMessage = 'Введенные пароли не совпадают. Убедитесь, что данные, ' +
'введенные в поле "Подтверждение пароля", совпадают с теми, ' +
'которые указаны в поле "Пароль".';
}
else {
this.errorMessage = '';
}
return eq;
}
}

View file

@ -1,44 +0,0 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input} from "@angular/core";
import {Process, TaskService, ProcessDefinitionResource, ProcessService} from "@webbpm/base-package";
@Component({
moduleId: module.id,
selector: 'process',
templateUrl: '../../../../../src/resources/template/app/component/process_list.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProcessListComponent {
@Input()
public processList: Process[];
@Input()
public errorMessage: string;
constructor(private processDefinitionResource: ProcessDefinitionResource,
private taskService: TaskService,
private cd: ChangeDetectorRef,
private processService: ProcessService) {
this.processList = [];
this.loadProcessList();
}
loadProcessList() {
this.processDefinitionResource.list().then(
(processList) => {
this.processList = processList;
this.cd.markForCheck();
},
() => {
this.errorMessage = "Error load process list";
this.cd.markForCheck();
})
}
startProcess(processDefinitionId: string) {
this.processService.start(processDefinitionId, null).then(
(processInstanceId: number) => {
this.taskService.startAndOpenNextTask(processInstanceId);
}
);
}
}

View file

@ -1,71 +0,0 @@
import {Component, Input, ViewChild} from "@angular/core";
import {AppConfigService, UserDto, UserService} from "@webbpm/base-package";
import {Router} from "@angular/router";
import {PhoneNumberComponent} from "ngx-international-phone-number";
@Component({
moduleId: module.id,
selector: "register",
templateUrl: "../../../../../src/resources/template/app/component/register.html"
})
export class RegisterComponent {
public passwordPattern: string;
public passwordPatternErrorMessage: string;
public errorMessage: string;
@Input()
public username: string;
@Input()
public email: string;
@ViewChild(PhoneNumberComponent)
public phone: PhoneNumberComponent;
public phoneNumber: string;
public phoneIsTouched: boolean = false;
@Input()
public password: string;
public fieldType: boolean;
@Input()
public consent: string;
constructor(private router: Router, private userService: UserService,private appConfigService: AppConfigService) {
this.passwordPattern = appConfigService.getParamValue("password_pattern");
this.passwordPatternErrorMessage = appConfigService.getParamValue("password_pattern_error");
}
public register(): void {
let user: UserDto = new UserDto();
user.username = this.username;
user.email = this.email;
user.name = this.username;
user.phone = this.phone.value;
user.password = this.password;
this.userService.register(user)
.then(() => this.router.navigateByUrl("/login?confirmationSent=true"),
(reason: any) => {
if (reason.status === 409) {
this.errorMessage = 'Пользователь с данным почтовым адресом уже существует';
}
else {
this.errorMessage = 'Произошла неизвестная ошибка, обратитесь в службу технической поддержки!';
}
});
}
toggleFieldType(): void {
this.fieldType = !this.fieldType;
}
phoneHasOnlyDialCode(): boolean {
return this.phone.phoneNumber.trim() === this.phone.getSelectedCountryDialCode().trim()
}
phoneInputFocusOut(): void {
this.phoneIsTouched = true;
}
}

View file

@ -1,30 +0,0 @@
import {Component, Input} from "@angular/core";
import {UserService} from "@webbpm/base-package";
import {Router} from "@angular/router";
@Component({
moduleId: module.id,
selector: "resetPassword",
templateUrl: "../../../../../src/resources/template/app/component/reset_password.html"
})
export class ResetPasswordComponent {
@Input()
public email: string;
@Input()
public errorMessage: string;
constructor(private router: Router, private userService: UserService) {
}
resetPassword(): void {
this.userService.resetPassword(this.email)
.then(() => this.router.navigateByUrl("/"),
(reason: any) => {
this.errorMessage =
'Произошла неизвестная ошибка, обратитесь в службу технической поддержки!';
});
}
}

View file

@ -1,121 +0,0 @@
import {Component, Input} from "@angular/core";
import {Task, TaskService, TaskStatus, TaskReference, TaskResource, ProcessVariable, UserService, RolesService, ProcessService} from "@webbpm/base-package";
@Component({
moduleId: module.id,
selector: "task-list",
templateUrl: "../../../../../src/resources/template/app/component/task_list.html"
})
export class TaskListComponent {
@Input()
public tasks: Task[];
@Input()
public onTimeTasks: Task[];
@Input()
public overdueTasks: Task[];
@Input()
public visibleTasks: Task[];
@Input()
public showMode: string;
@Input()
public errorMessage: string;
@Input()
public roles: string[];
constructor(private taskService: TaskService,
private $taskResource: TaskResource,
private userService: UserService,
private rolesService: RolesService,
private processService: ProcessService) {
this.tasks = [];
this.onTimeTasks = [];
this.overdueTasks = [];
this.visibleTasks = [];
this.roles = [];
this.showMode = 'All';
this.rolesService.getRoles().then((roles: string[]) => {
this.roles = roles;
});
this.loadTasks();
}
private loadTasks(): void {
this.$taskResource.list()
.then(
(tasks: Task[]) => this.initTasks(tasks),
() => this.errorMessage = "Error load tasks"
);
}
public startTask(task: Task): Promise<any> {
let taskRef: TaskReference = new TaskReference();
taskRef.processInstanceId = task.processInstanceId;
taskRef.taskId = task.id;
if (task.status == TaskStatus.InProgress) {
return this.taskService.openTask(taskRef);
}
else if (task.status == TaskStatus.Reserved || task.status == TaskStatus.Ready) {
return this.taskService.startAndOpenTask(taskRef);
}
}
private initTasks(tasks: Task[]): void {
this.tasks = tasks;
this.filterOnTimeTasks();
this.filterOverdueTasks();
this.filterVisibleTasks();
}
public isVisible(task: Task): boolean {
return this.showMode == 'All' ||
this.showMode == 'OnTime' && !this.isOverdue(task) ||
this.showMode == 'Overdue' && this.isOverdue(task);
}
public isOverdue(task: Task): boolean {
if (!task.expirationTime) {
return false;
}
return new Date(task.expirationTime).getTime() <= new Date().getTime();
}
public isOnTime(task: Task): boolean {
return !this.isOverdue(task);
}
public startProcess(processDefinitionId: string, processVars?: Array<ProcessVariable | any>) {
this.processService.start(processDefinitionId, processVars).then(
(processInstanceId: number) => {
this.taskService.startAndOpenNextTask(processInstanceId);
}
);
}
public filterOnTimeTasks() {
this.onTimeTasks = this.tasks
.filter((task: Task) => this.isOnTime(task));
}
public filterOverdueTasks() {
this.overdueTasks = this.tasks
.filter((task: Task) => this.isOverdue(task));
}
public filterVisibleTasks() {
this.visibleTasks = this.tasks
.filter((task: Task) => this.isVisible(task));
}
public hasRole(role: string) {
return this.roles.includes(role);
}
}

View file

@ -1,18 +0,0 @@
import {ChangeDetectionStrategy, Component} from "@angular/core";
import {Router} from "@angular/router";
@Component({
moduleId: module.id,
selector: 'task-not-found',
templateUrl: '../../../../../src/resources/template/app/component/task_not_found.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TaskNotFoundComponent {
constructor(private router: Router) {
}
goToTaskList() {
return this.router.navigateByUrl("/process/tasks");
}
}

View file

@ -1,9 +0,0 @@
import {Component} from "@angular/core";
@Component({
moduleId: module.id,
selector: "[task]",
templateUrl: "../../../../../src/resources/template/app/component/task.html"
})
export class TaskComponent {
}

View file

@ -1,298 +0,0 @@
import {Injectable, OnDestroy} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {UserService, AppConfigService, Session} from "@webbpm/base-package";
import {
JivoProfileDto
} from "../../../generated/dto/jivoprofile/JivoProfileDto";
import {Observable, Subscription} from "rxjs";
declare function jivo_InitProfile(name, email, phone);
declare function jivo_ClearHistory();
@Injectable()
export class AppJivoChatWidgetService implements OnDestroy {
public static LIVE_CHAT_WIDGET_API_URL: string = "jivo_chat_widget_api_url";
public static LIVE_CHAT_WIDGET_ENABLE: string = "jivo_chat_widget_enabled";
public static LIVE_CHAT_WIDGET_DEFAULT_VALUE: boolean = false;
private JIVO_CSS: string = `
/* hide the original widget - that there were no two labels on the screen*/
#jivo_chat_widget{
display: none;
}
/* the default style - for offline messages if no one is online */
#jivo_custom_widget{
position: fixed;
z-index: 300000;
cursor: pointer;
display: block;
right: 60px;
bottom: 10px;
width: 60px;
height: 60px;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAI1UlEQVR4nO2ca5AUVxXH/+f29MzsEkDiWkQeZRLKKgsIC6EqlJgHpCKG6G6mezL5kCqTaBmifkiV5AMp83A1QavUpEiq/BC0SqxUIjo7D2ah1oohPBQSKYnJApZgECGy4ZUlIbLz6O57/DBb5e6Ghe7e7t7H3N/HmXPPOff+p2/PvX36AgqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqEYJ9BYJ+AWzmS0/mp1pq7rsyTwWSHlDAiRBNBEUiYBgIWoAChDyooU4rwA3rcsq7c5kThN2awztj1wx7gUpJzJXCdsexmIbhBEC5j5BgCfA6D5dOkAOE5EByTzIQA9Usq/NBWL/w4s6YAYF4JwJvMZS8o2MK8AsALA3IhCnwCwC8w79FhsK2WzZyOKOyJjJgi3t8+0YrF7wWyC6Bb4//UHhQNgN4jyupS/p0LhzFgkEbkgtVRqKYRYA+B+AMmo47ukRkRbAGyM5XLbCeCoAkciCANUS6dNYn4KwKIoYgbIOwz8MJ7PF6MQJnRBKqbZLoCnMfGEGM47gvmJWKGwNcwgoQnSb5pzdGADA+mwYowFTLRVCvFIUzZ7LAz/ImiHDJBlmo/GgMOTTQwAIOavaY5z0Eqn13IIP+hAHXJbW0tN1zcR8NUg/Y5jXtM17euUzZ4KymFggtRSqaUkRImBWUH5nAgQcJKJ2uK53N+C8BfIlGUZxu0ger3RxAAABmaDebdlGHcG4W/UglRN02CibhBNCyKhCcpVTLSlYprto3U0qinLMozbmOgPGL8LvKipkBCr9M7OP/l14FuQaiYznxxnL4Dpfn1MSojOM7A8kcv9w09zX1MWr16dIMd5BUqMT8I8g5iz/OCDvmYNX4JYzc0/BdDqp22DsNC6cOEnfhp6nrJqqdRiCLEfISwqJxkSUt4ULxb3e2nkfVCF+Jmvdo2HGBgrj408YBnG7QDu8BqkgVlpGcZtXhp4EoSJ1njLR8FED3mxd30P4UzmastxTkKtObxS0avVWbRt23k3xq6vkJptr0K4YljM/CMbmKtrWgJSLgOweyRjBk4x0X16uTxdt+1pzHwvAScv4/9NEuJLermctGx7NhE9AaAaeC8+SbKWTH7ZrbFrQYhoub983MHMmUSh8IPmfP4/WLDAjheL+/RFi1YS0HUJ8w9s216ayOV+i+7uj3HjjRcThUI2pmlLGLjUzutruqbdrHd27sWyZdaUUqlXz+XWkxDtiOIpoJSux87LPWSZj1xcQUApUShs6b/77rk103zb6ump1NLp56ijQ1qa9h3UCxAGNaAnp5RKvXY6fZdlmmesAwfOVQ3DpGz2LBE9Nsw9Sym/TdmsUzWMZ6yennLNMA5VUql5emfnqwB+F1a/BuXreuzcXyHAHH/ZXBkGdgBATNPWor7g1MH8vWo6vaA5mz0J5sND7KXcCQCS+VkALWCeQUQbAMARYscw9yeSxeLRcip1LRE9DiAOovlC09bVO0bD7QOHPJQ1uRaEgU/7S+fKEHNiIMZHgz52JPN/6waUuJQ9htp/BAAk5RBbBhIAkCTqB2AN+rze9v++QsPL2LkShNes0THQsTBgohR3dIi4bT8L5t+AaD+YH2rK54/XUqnFAOYNszcAgJm/BebtAHYBeAAANCnNwbYEXGMZxhepUDjDRA8A+CuAV+KVyo8HHsEaYfVrEMmBMbwirv/21kzzQ4S4mUjA9/V8fsj+D6dSn7KEeB3AkmHmVQAr4vn8m0NyTKeXgPnPAJqHWDP/XQdupULhgyH2hvEIiJ4PrBMjczGez1/lxtCLIEcAfN53Si4goETMv5RCnAXzYgKeZGD2COYWM28QwDYQsQTuJGAtRriSGThF9XKktwTR1cz8zQiLMI7H8/lr3Rh6EWQnAE/bAIoBmHfGC4WVbky93NR9PwVrdBjY49bWtSAC+KO/dBQa0RtubV0LEmtpeQPMF/yl1ND0adOmbXdr7H5huHGjBeBXvlJqbF6mTZsqbo09bb87sdjzGLS4UlwRyUQvemngSZCmbPYEgM2eUmpkmF9K5HKHvDTx/ChWB9aByNXefoNTdmKxp7w28iwI5fPvQ8pHvbZrQNYNzCie8FUoxwDVTLOrgarcPcFEW+O5XLufN658VY8QwPFy+T4APX7aT3KOxGu1b/h9/c13OQ91d1+wbHs1gPf8+phsENDrSPkV6uo659fHqOqrppRKvcx8FwG9o/EzSTgtiVaP9jCCURe8JQqFg5am3YTGnr6OSaJbErncqMcgsDeoBsqEtgC4OSifE4Q9OrMZ1EEDgZWEUjbbp/f1rSTmx9AYq3kG0Qt6S8vKIE99COW1aOuee5azlC8BuD4M/+OAY8T8sF4oBL4DHkrRtN7ZuVfXtFZmXg+gP4wYY0SVgWd0TVsQhhhABCc59JvmnFj90en9mLhV8zaYX5bMTyeLxaNhBors8BnLMO5kou6o4gVEFcybpRDrk7ncP6MIGIsiCAA4sdi7wpkQh7oBwL8IeDGmab+O+gytyAQRUn43qlg+OQEgT8y5WGvrXurokGORRDTHM9Xrq04AmBpFPJd8SPXCjV0s5U69WHwrynOxRiKSK6SmaY8Ts1cxjoD5BSa6hup/n68HcB2AmR79nAXRKWJ+VwIHwXwAwMF4a+vhsboKLkf452WlUvOEEIfgrRT1oG7bd1CpdHr4F9zW1lxJJlvItpOCeSrFYlPJtpugacRAjYGP4Ti2I8S5ZLl8irq7o3gHJDBCv0I0IZ5jL2IQva0LsYry+UveTKmrqx/1+X5SEuq6oGaaDzPg+vwPAgq6ECvGw+mgY0VoU1bVMBYS0T4ATS7ML4J5XbxQ+EVY+UwUQrlCOJOZTvU3k9yIsdkGvqDEqBP4PYQzmSbLcUogmn8ZsxoRbWHH+Xm8WNwXdA4TmUAF4fb2qZbjFAHceomvzxCwh4GSrmklymb7gow9WQhUEFvTFgLoIuZXIYTFzH0kxHu2bR8dj+esKxQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUJxGf4HQacsFGPlFpMAAAAASUVORK5CYII=');
background-size: 60px 60px;
background-repeat: no-repeat;
}
#jivo_custom_widget:hover{
bottom: 13px;
}
/* if there are operators online - show other label*/
#jivo_custom_widget.jivo_online{
height: 60px;
width: 60px;
background-size: 60px 60px;
background-repeat: no-repeat;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAI/ElEQVR4nO2cbXBU1RnH/8+5m2wgAoLYZDcJbHZj2wGsOMzIlFYFx1K0hal0ygdnqtNOlbYfmJpkE8SXWYug7CaIzPiBtjNtx9FBnFoS6ehYUcCClharCB3B3Wxe7yaAYG0DSfbe8/RDsBNSAvfe3Lt52fP7kmT3f57nufe/59yce88eQKFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBTjBBrrAiyzi7WKdEdJAXxBFhwQLGZCcBEDU8BcBAAg6iPgAiT1SZLnSFImC0PvqKzowRoyx/gILDEuDZmb6K70wVwMphslYT4YNxJhLgDNYUiTGW0gfCQYxwE+aprir60PB1rdrNsNxoUhVZsz10ufXElESxlYCqAiR6nbCdjP4LdFVuxJbgiczlHeERkzQyq39ZSIAWONJFpNwK1w/ul3CxOEA8z8ioC2KxUtPTUWReTckEiDvkhKPEiE+wAU5Tq/RQaI0MTMv2qpDe4FEecqcW4MYaZwIrMaRI8D/LWc5HSPDwl4IlUb2J0LYzw3pLKxaxVJsXECGjGcD8Hi0Za60j1eJvHMkKotneVSiG0Avu9VjjGBeI/J2rq2aGnai/DC9YjMVNmQqZFCnMBkMwMAmL6rQR4LJ/RqMLv+gXY14Jcb9NkG43cAvuNm3HHMm5J9P2yt+1K3WwFdMyTSoC8CUzODg27FnCB0CcErkzVl/3AjmCtDViTRfQcz3spDMwCgTEo6EInrK9wINmpDIgn9HoZ8DcB0F+qZqFzDhKbKxq5Vow00qiErFM/cLohfx/id4OWaPmYsT9cF33EawLEhVQ2ZeZL5EIAZTmNMUs5JwpLW2uDHTho7GrKqtn/il8wvQplxOWYKxsuhWNrRqOHIELO/OA7gJidt84QFotj/lJOGtoesULxzoSBxBF5MKicXkgi3pGqDR+w0sn1SBYmEk3Z5iGAgYbuRHXEk0X0HgDvtJslbGMtC8cztdprYMkQSP2ivIoUAP2BHb/kaUr61Y1ahqXVBzTns0mcY2WD7w3PPWRFb7iF+UyyHt2ZkwfxLIWXFlN5zfpa0GMCBK+i7QXyv8PtnFMiC6cy0BkDXSGJifo8Ef0P4e4uy0ihj4FEA/W4fxGUo0nyF37Iq9lkVShJLiL17YCbAP0jWlTUBAGIsUE+HEeNl4eLu3QCvHKpl4FNDGos66ufoYCY8AUKMXq7anNknC/gogNJh4d9MhYIrsIZMxFggRjqATZVx/W9EeB0eP6gj5iUAdlnRWu4hBF7suKKrxWZqTkbLmiKNXRXhhP5BuDjTF47rWxEj6dOMnwEwh+kf66ifo4fjmbvDDZlT4eLMmXCDvjq5IXCaiNYPC8+CtZ9iDZmRhP5kuDhzIZzQj1fFeyLpuuAbAL3k1XH9r14b5866IUzlzsq5Okz8NgCwpGoMTjgLQHio6unM/JPVFV0ATgzVm8z7LhbVCGA2gJlgbAMANgZjDaE9WVeSCj2VCTHwCIBCAPOYzHoAIPo/veswkeVlTZYNYfB1zsqxFN1/8ee/hrxomj7+z8Xf/UPVRPKLv4fqB38X5iXaL9pqPjoPIDsk56Be8nC9F1g+d5YMWbSDCzDspLgJMb6HGIsCWdjIwO8BHAH4gXRtsC0U71wIIDJUL0D3AABB/ARMewnYLxj3DwYTq4eFL61KdH49FS09BeL7AfwdwItZw9gMZmIxGMtjii6ew6ti+WIWTuifwdubiRtaosFL7v+EnklfS4b/LQJuHqbtl0Isba0pfW/oi1WNXTdLSX8BMHWY/p+FWXnbxxvKPx36YiShr2PgWdeOYGR6W6LBa6wI7RhyEsANjkuyADE1M+jXknBag1zIwGMAykaQZxm8jVn8iQQzMVYAqMbIPbkbRBsl0fvC5Fkg/jFytwijrSUaDFkRWjYkktD3MWDrNoBiEAb2paPBZVa0li/qkuH4KVi+I4CDNrSWlX92VI0CDHrXqtayIbOmBd4F8LmjivKbs7K3b69VsWVDjqylLIDfOCopj2GiF1pjlX1W9fYeNJl4FpdMrhRXQWoGdthpYMuQlvXBdgJ22qspf2Hg+eT6wHE7bWw/ijV8BfUALN3bz3MukInH7TaybUjbQ9dnAK6x2y7fIOb6lvXBdtvtHGVjpnBD5lXkzyp3exDvaakJrnLyjStnq0eIWPj99wJ01FH7yc1JH+hHTr/+5ng5T3LddZ9nZfYuAB1OY0w2CKRLg759sjZ4xmmMUa2v6qifoxPE3QTSRxNnUsDokTDuGu1mBKNe8JaKlh7TNOOWPB++0oB5azpaMepz4NrD/YvLhJoAfNOtmBOEgwSx2q2NBlxbEtpZXXF2Tm9gGcDrkR+zeWZg+8zpgWVu7vrgyfKXSGPXEmnS80QIexF/HJBm4rXp2jLX74B7smg6VVN2aGqx7yYCNgE470WOMaKfGU8OaOZ8L8wAcrCTQ9WWznKp0UYw3YeJu2reYOAFjbWNybqSlJeJcrb5TCSur2DCa7nK5xL9DOwkNje11FV8kouElpeSjj6TmYQ51jswWYMZLQK8gwzx21zvoZUzQ9jQfj4+tksbkXYAr4Dwh3Rv4BBiJMeiiJycotAz6WuF4W8HMC0X+SzyGUDvMGG/AO9L1QTez+W+WCORkx6iGYWPsH0zTjLTdoBLiRAm5jCDKkEosRnnNBjdICQZOAamjyTzsbYLgROX9IJam1E9wvv/suI9EUnmcdhbinqMC7Q7078o6Rn+RjCmTy0qwmwUyCJiMY2YpjFhCksQSA5IaP/WSBoSOKMV9nYn192Qi++AuIbnPYQht8KeGR+ILC1PRksuezHVY8HzGBzvJyWezgsiCX0tE9vZ/+OPMIuWjofdQccKz4asSKJ7AUMeBjDFgryXmerTdYHnvKpnouBJDwk/fXYGQ74ES2bQTiHlV5UZg7h+DSnf2jEFZl8zgHlXkA0QoUma1JCuDxx2u4aJjKuGfGXL6WlZc2A3gNsu8/YpAAeJqbnfZzR3VlecdTP3ZMFVQ0zRvwAQrwL8BkBZYjrLmtkhB7TUeNxnXaFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBSKK/Bf/RI0U2U6gOkAAAAASUVORK5CYII=');
}`;
private JIVO_JS: string = `
/* Callback function that is called immediately after JivoChat is loaded */
function jivo_onLoadCallback() {
window.jivo_cstm_widget = document.createElement('div');
jivo_cstm_widget.setAttribute('id', 'jivo_custom_widget');
document.body.appendChild(jivo_cstm_widget);
/* Adds handlers click on the icon - to maximize the window when clicked */
jivo_cstm_widget.onclick = function () {
jivo_api.open();
}
/* Change the CSS class if there are agents online */
if (jivo_config.chat_mode === "online") {
jivo_cstm_widget.setAttribute("class", "jivo_online");
}
/* Show the user a shortcut */
window.jivo_cstm_widget.style.display = 'block';
}
/*
Callback function jivo_onOpen and jivo_onClose called whenever the chat window JivoChat
is expanded or collapsed by the user or by the proactive invitations rule.
*/
function jivo_onOpen() {
/* If chat is deployed - hide shortcut */
if (jivo_cstm_widget)
jivo_cstm_widget.style.display = 'none';
}
function jivo_onClose() {
/* If chat is minimized - show label */
if (jivo_cstm_widget)
jivo_cstm_widget.style.display = 'block';
}
function jivo_ClearHistory() {
jivo_api.clearHistory();
jivo_api.close();
}
function jivo_InitProfile(name, email, phone) {
let chatMode = jivo_api.chatMode();
let args = {
name: name ? name : '',
email: email ? email : '',
phone: phone ? phone : ''
};
if (chatMode === 'offline') {
jivo_api.sendOfflineMessage(args);
}
else {
jivo_api.setContactInfo(args);
}
}
`;
private currentUserName: string;
private currentEmail: string;
private currentPhone: string;
private jsLoaded = false;
private jivoWidgetApiUrl: string;
private currentSession: Observable<Session>;
private currentSessionSubscription: Subscription;
private session: Session;
private sessionInitJivoChatWidget: boolean;
constructor(private httpClient: HttpClient,
private appConfigService: AppConfigService,
private userService: UserService) {
this.currentSession = userService.getCurrentSession();
this.currentSessionSubscription =
this.currentSession.subscribe(val => {
if (!this.sessionInitJivoChatWidget || !this.session) {
this.initJivoChatWidget();
this.sessionInitJivoChatWidget = true;
}
if (this.session && !val) {
// logout
this.clearJivoProfile();
this.session = null;
this.sessionInitJivoChatWidget = false;
}
if (val) {
this.session = val;
}
});
}
private initJivoChatWidget() {
let liveChatWidgetEnabled = this.getValueFromAppConfig(
AppJivoChatWidgetService.LIVE_CHAT_WIDGET_ENABLE,
AppJivoChatWidgetService.LIVE_CHAT_WIDGET_DEFAULT_VALUE);
if (liveChatWidgetEnabled) {
this.jivoWidgetApiUrl = this.getValueFromAppConfig(
AppJivoChatWidgetService.LIVE_CHAT_WIDGET_API_URL,
'');
if (!this.jivoWidgetApiUrl) {
throw new Error(
`The configuration file does not contain the 'Jivo API url'. Please check that '${AppJivoChatWidgetService.LIVE_CHAT_WIDGET_API_URL}' has data`);
}
let userId = this.userService.getCurrentUserId();
if (userId) {
this.clearJivoProfile();
this.httpClient.get('profile/jivo/' + userId)
.toPromise()
.then((profile: JivoProfileDto) => {
this.currentUserName = profile.username;
this.currentEmail = profile.email;
this.currentPhone = profile.phone;
let initProfileFunction = () => this.initProfile(this.currentUserName,
this.currentEmail,
this.currentPhone);
this.loadScripts(initProfileFunction);
});
}
else {
let initProfileFunction = () => this.initProfile(this.currentUserName, this.currentEmail,
this.currentPhone);
this.loadScripts(initProfileFunction);
}
}
}
private clearJivoProfile() {
this.clearProfileData();
try {
jivo_ClearHistory();
}
catch (ignore) {
}
}
private clearProfileData() {
this.currentUserName = '';
this.currentEmail = '';
this.currentPhone = '';
}
private loadScripts(initProfileFunction): void {
let afterLoadMainJs = () => {
this.loadScriptJsContent('jivoCustomJs', this.JIVO_JS, initProfileFunction);
}
this.loadScript('jivoWidget', this.jivoWidgetApiUrl, true, afterLoadMainJs);
this.loadStyleContent('jivoCustomCss', this.JIVO_CSS);
}
private initProfile(username: string, email: string, phone: string): void {
try {
jivo_InitProfile(username, email, phone);
}
catch (e) {
if (e instanceof ReferenceError) {
if (!this.jsLoaded) {
this.jsLoaded = true;
setTimeout(() => {
jivo_InitProfile(username, email, phone);
}, 3000);
}
}
}
}
private loadScript(elementId, js, async, callback): void {
let jivoJsLink = document.getElementById(elementId);
if (!jivoJsLink) {
let node = document.createElement('script');
node.src = js;
node.type = 'text/javascript';
node.id = elementId;
node.async = async;
node.charset = 'utf-8';
if (callback) {
node.onload = function () {
callback();
};
}
document.getElementsByTagName('head')[0].appendChild(node);
}
else {
if (callback) {
callback();
}
}
}
private loadScriptJsContent(elementId, js, callback): void {
let jivoJs = document.getElementById(elementId);
if (!jivoJs) {
let node = document.createElement('script');
node.innerHTML = js;
node.type = 'text/javascript';
node.id = elementId;
node.charset = 'utf-8';
if (callback) {
callback();
}
document.getElementsByTagName('head')[0].appendChild(node);
}
else {
if (callback) {
callback();
}
}
}
private loadStyleContent(elementId, styleCss): void {
let jivoCss = document.getElementById(elementId);
if (!jivoCss) {
const style = document.createElement('style');
style.id = elementId;
style.innerHTML = styleCss;
document.getElementsByTagName('head')[0].appendChild(style);
}
}
private getValueFromAppConfig(key, defaultValue) {
let enabled = this.appConfigService.getParamValue(key);
if (enabled === undefined) {
return defaultValue;
}
return enabled;
}
ngOnDestroy(): void {
this.currentSessionSubscription.unsubscribe();
}
}

View file

@ -0,0 +1,61 @@
import {Injectable} from "@angular/core";
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
import {Observable} from "rxjs";
import {HttpClient, HttpParams} from "@angular/common/http";
import {CookieService} from "ngx-cookie";
@Injectable({providedIn:'root'})
export abstract class AuthGuard implements CanActivate {
protected constructor(
protected router: Router,
private httpClient: HttpClient,
private cookieService: CookieService
) {
}
public canActivate(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
let hasAccess: Promise<boolean> | boolean = this.checkAccess();
return Promise.resolve(hasAccess).then((isAccess) => {
let url = new URL(window.location.href);
let params = new URLSearchParams(url.search);
let code = params.get('code');
let error = params.get('error');
let errorDescription = params.get('error_description');
if (isAccess) {
return true;
}
else if (error) {
throw new Error(error + ', error description =' + errorDescription);
}
else if (code) {
const params = new HttpParams().set('code', code);
this.httpClient.get<boolean>("esia/auth", {params: params}).toPromise().then(
() => window.open(url.origin, "_self"))
.catch((reason) =>
console.error(reason)
);
return false;
}
else {
this.httpClient.get<string>("esia/url")
.toPromise()
.then(url => window.open(url, "_self"));
return true;
}
}).catch((reason) => {
console.error(reason);
return false
});
}
private checkAccess(): Promise<boolean> | boolean {
return this.getIsAuth() != null;
};
public getIsAuth(): string {
return this.cookieService.get('is_auth');
}
}

View file

@ -8,7 +8,6 @@ import {
Router
} from "@angular/router";
import {ProgressIndicationService} from "@webbpm/base-package";
import {AppJivoChatWidgetService} from "../../app/service/app-jivo-chat.service";
@Component({
moduleId: module.id,
@ -20,8 +19,7 @@ export class WebbpmComponent {
public footerVisible: boolean = true;
constructor(private router: Router,
private progressIndicationService: ProgressIndicationService,
private appJivoChatWidgetService: AppJivoChatWidgetService) {
private progressIndicationService: ProgressIndicationService) {
router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
progressIndicationService.showProgressBar();

View file

@ -1,88 +0,0 @@
import {RouterModule, Routes} from "@angular/router";
import {NgModule} from "@angular/core";
import {
AuthenticationGuard,
ConfirmExitGuard
} from "@webbpm/base-package";
const routes: Routes = [
{
path: 'user-management',
canActivate: [AuthenticationGuard],
children: [
{
path: 'users',
loadChildren: 'generated-sources/page-user-management-users.module#PageusermanagementusersModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'users/new',
loadChildren: 'generated-sources/page-user-management-user-create.module#PageusermanagementusercreateModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'users/:id',
loadChildren: 'generated-sources/page-user-management-user-edit.module#PageusermanagementusereditModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'org-units',
loadChildren: 'generated-sources/page-user-management-org-units.module#PageusermanagementorgunitsModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'org-units/new',
loadChildren: 'generated-sources/page-user-management-org-unit.module#PageusermanagementorgunitModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'org-units/:id',
loadChildren: 'generated-sources/page-user-management-org-unit.module#PageusermanagementorgunitModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'roles',
loadChildren: 'generated-sources/page-user-management-roles.module#PageusermanagementrolesModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'roles/new',
loadChildren: 'generated-sources/page-user-management-role.module#PageusermanagementroleModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'roles/:id',
loadChildren: 'generated-sources/page-user-management-role.module#PageusermanagementroleModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'groups',
loadChildren: 'generated-sources/page-user-management-groups.module#PageusermanagementgroupsModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'groups/new',
loadChildren: 'generated-sources/page-user-management-group-create.module#PageusermanagementgroupcreateModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'groups/:id',
loadChildren: 'generated-sources/page-user-management-group-edit.module#PageusermanagementgroupeditModule',
canActivate: [ConfirmExitGuard]
},
{
path: 'authorities',
loadChildren: 'generated-sources/page-user-management-authorities.module#PageusermanagementauthoritiesModule',
canActivate: [ConfirmExitGuard]
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UserManagementRoutingModule {
}

View file

@ -1,38 +1,14 @@
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {HomeComponent} from "./component/home.component";
import {
AuthenticationGuard,
HistoryLocationGuard,
TaskPageRouteResolver,
ConfirmExitGuard
} from "@webbpm/base-package";
import {DYNAMIC_ROUTING} from "../../page.routing";
import {TaskComponent} from "./../app/component/task.component";
import {TaskNotFoundComponent} from "./../app/component/task-not-found.component";
import {AuthGuard} from "../security/guard/auth.guard";
const webbpmRoutes: Routes = [
{
path: '',
loadChildren: 'generated-sources/page-lkul.module#PagelkulModule',
canActivate: [AuthenticationGuard, ConfirmExitGuard],
canActivate: [AuthGuard],
pathMatch: 'full',
},
{
path: 'process/:processInstanceId/task/:taskId',
component: TaskComponent,
children: DYNAMIC_ROUTING,
canActivate: [HistoryLocationGuard],
resolve: {
taskPage: TaskPageRouteResolver
},
runGuardsAndResolvers: "always"
},
{
path: 'process/task-not-found',
component: TaskNotFoundComponent,
canActivate: [AuthenticationGuard]
},
{
path: '**',
redirectTo: '',

View file

@ -13,13 +13,8 @@ import {
BpmnModule,
ComponentsModule,
CoreModule,
SecurityModule,
} from "@webbpm/base-package";
import {TaskParamsProvider} from "@webbpm/base-package";
import {ProcessInstanceParamsProvider} from "@webbpm/base-package";
import {AppRoutingModule} from "../app/app-routing.module";
import {AppJivoChatWidgetService} from "../app/service/app-jivo-chat.service";
import {UserManagementRoutingModule} from "./user-management-routing.module";
import {GlobalErrorHandler} from "./handler/global-error.handler.prod";
import {DEFAULT_HTTP_INTERCEPTOR_PROVIDERS} from "./interceptor/default-interceptors.prod";
@ -31,11 +26,9 @@ let IMPORTS = [
ToastNoAnimationModule.forRoot(),
AgGridModule,
AppRoutingModule,
UserManagementRoutingModule,
BpmnModule,
CoreModule,
ComponentsModule,
SecurityModule,
AppModule,
WebbpmRoutingModule
];
@ -48,9 +41,6 @@ let IMPORTS = [
],
exports: [],
providers: [
TaskParamsProvider,
ProcessInstanceParamsProvider,
AppJivoChatWidgetService,
{provide: ErrorHandler, useClass: GlobalErrorHandler},
DEFAULT_HTTP_INTERCEPTOR_PROVIDERS
],

View file

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>resources</artifactId>
<version>3.177.0</version>
<version>3.178.2</version>
<organization>
<name>Micord</name>
</organization>
@ -28,13 +28,13 @@
<jooq.version>3.19.3</jooq.version>
<jupiter.version>5.10.2</jupiter.version>
<enforcer.manageVersions>true</enforcer.manageVersions>
<webbpm-platform.version>3.177.0</webbpm-platform.version>
<webbpm-platform.version>3.178.2</webbpm-platform.version>
<h2.version>1.4.200</h2.version>
<build.timestamp>0812074142</build.timestamp>
<build.timestamp>0829133121</build.timestamp>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<junit.platform.version>1.10.0</junit.platform.version>
<enforcer.manageExclusions>true</enforcer.manageExclusions>
<revision>3.177.0</revision>
<revision>3.178.2</revision>
<metadata.ts.filename>typescript.metadata.json</metadata.ts.filename>
<package.repository.url>https://repo.micord.ru</package.repository.url>
<maven.build.timestamp.format>MMddHHmmss</maven.build.timestamp.format>
@ -47,19 +47,19 @@
<dependency>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>converters</artifactId>
<version>3.177.0</version>
<version>3.178.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>backend</artifactId>
<version>3.177.0</version>
<version>3.178.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>frontend</artifactId>
<version>3.177.0</version>
<version>3.178.2</version>
<scope>compile</scope>
</dependency>
</dependencies>

View file

@ -60,7 +60,7 @@
<ul>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x435;&#x448;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>https://www.wildberries.ru/catalog/${sku}/detail.aspx</code></li>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x443;&#x442;&#x440;&#x435;&#x43d;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.177.0</code></li>
<li>&#x41e;&#x431;&#x440;&#x430;&#x437;&#x435;&#x446; &#x432;&#x43d;&#x443;&#x442;&#x440;&#x435;&#x43d;&#x43d;&#x435;&#x439; &#x441;&#x441;&#x44b;&#x43b;&#x43a;&#x438;: <code>products/ru.cg.webbpm.packages.base:resources:jar:3.178.2</code></li>
</ul>
</li>
<li>

View file

@ -4,17 +4,17 @@
<description>Base webbpm package</description>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>resources</artifactId>
<version>3.177.0</version>
<studioVersion>3.177.0</studioVersion>
<version>3.178.2</version>
<studioVersion>3.178.2</studioVersion>
<backendModule>
<groupId>ru.cg.webbpm.packages.base</groupId>
<artifactId>backend</artifactId>
<version>3.177.0</version>
<version>3.178.2</version>
</backendModule>
<frontendModule>
<packageName>@webbpm/base-package</packageName>
<version>3.177.0</version>
<version>3.178.2</version>
</frontendModule>
</packageInfo>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_отмены.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_очистки_фильтра.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_удаления.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаагрузки.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_вызова_ошибки.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -7,11 +7,11 @@
<documentation>component/buttons/Кнопка_выполнения_бизнес-процесса.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_выполнения_SQL.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопка_для_фильтрации.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

View file

@ -8,11 +8,11 @@
<documentation>component/buttons/Кнопкаавигации.html</documentation>
<internal>false</internal>
<versions>
<studioVersion>3.177.0</studioVersion>
<studioVersion>3.178.2</studioVersion>
<packageVersions>
<entry>
<key>ru.cg.webbpm.packages.base.resources</key>
<value>3.177.0</value>
<value>3.178.2</value>
</entry>
</packageVersions>
</versions>

Some files were not shown because too many files have changed in this diff Show more