diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c872ff63 --- /dev/null +++ b/Dockerfile @@ -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//$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 diff --git a/Dockerfile.old b/Dockerfile.old new file mode 100644 index 00000000..a284efa0 --- /dev/null +++ b/Dockerfile.old @@ -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 diff --git a/backend/pom.xml b/backend/pom.xml index dce5ddb7..e050c246 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -3,11 +3,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - ervu_lkrp_ul - ervu_lkrp_ul + ru.micord.ervu.lkrp + ul 1.0.0-SNAPSHOT - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul backend war @@ -19,13 +19,17 @@ io.jsonwebtoken jjwt-api + + io.jsonwebtoken + jjwt-jackson + io.jsonwebtoken jjwt-impl runtime - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul resources runtime @@ -107,30 +111,6 @@ ru.cg.webbpm.modules inject - - ru.cg.webbpm.modules.bpmn - bpmn-workflow-api - - - ru.cg.webbpm.modules.bpmn - bpmn-workflow-jbpm-api - - - ru.cg.webbpm.modules.bpmn - bpmn-workflow-jbpm - - - ru.cg.webbpm.modules.bpmn - bpmn-beans - - - ru.cg.webbpm.modules.bpmn - bpmn-variable-condition-adapter - - - ru.cg.webbpm.modules.bpmn - bpmn-deploy - ru.cg.webbpm.modules webkit-rpc @@ -184,22 +164,6 @@ ru.cg.webbpm.modules.core metrics - - ru.cg.webbpm.modules.webkit - active-users-tracker - - - ru.cg.webbpm.modules.security - security-beans - - - ru.cg.webbpm.modules.security - security-api - - - ru.cg.webbpm.modules.security - security-esia - ru.cg.webbpm.modules.reporting reporting-api @@ -236,14 +200,6 @@ ru.cg.webbpm.modules webkit-base - - ru.cg.webbpm.modules.security - security-db-synchronization-api - - - ru.cg.webbpm.modules.security - security-db-synchronization-ldap-impl - xerces xercesImpl @@ -260,14 +216,6 @@ org.apache.tika tika-core - - org.springframework.security.kerberos - spring-security-kerberos-core - - - org.springframework.security.kerberos - spring-security-kerberos-web - org.bouncycastle bcprov-jdk15on @@ -294,6 +242,7 @@ + ${parent.artifactId} maven-compiler-plugin @@ -327,16 +276,6 @@ studio - - - - maven-war-plugin - - ${project.artifactId} - - - - ru.cg.webbpm.modules.resources diff --git a/backend/src/main/java/AppConfig.java b/backend/src/main/java/AppConfig.java index 4bbbdc98..ea4d661f 100644 --- a/backend/src/main/java/AppConfig.java +++ b/backend/src/main/java/AppConfig.java @@ -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 diff --git a/backend/src/main/java/controller/ProfileController.java b/backend/src/main/java/controller/ProfileController.java deleted file mode 100644 index c1a4e96c..00000000 --- a/backend/src/main/java/controller/ProfileController.java +++ /dev/null @@ -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; - } -} diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java index c6349421..a69a38b2 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java @@ -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, diff --git a/backend/src/main/java/esia/model/ChiefPersonModel.java b/backend/src/main/java/esia/model/ChiefPersonModel.java deleted file mode 100644 index b604b06c..00000000 --- a/backend/src/main/java/esia/model/ChiefPersonModel.java +++ /dev/null @@ -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; - } -} diff --git a/backend/src/main/java/esia/model/MillitaryRegistrationPersonModel.java b/backend/src/main/java/esia/model/MillitaryRegistrationPersonModel.java deleted file mode 100644 index 875df7f3..00000000 --- a/backend/src/main/java/esia/model/MillitaryRegistrationPersonModel.java +++ /dev/null @@ -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; - } -} diff --git a/backend/src/main/java/esia/model/OrganizationModel.java b/backend/src/main/java/esia/model/OrganizationModel.java deleted file mode 100644 index ae536592..00000000 --- a/backend/src/main/java/esia/model/OrganizationModel.java +++ /dev/null @@ -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; - } -} diff --git a/backend/src/main/java/esia/model/PersonModel.java b/backend/src/main/java/esia/model/PersonModel.java deleted file mode 100644 index 455fbfcc..00000000 --- a/backend/src/main/java/esia/model/PersonModel.java +++ /dev/null @@ -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; - } -} diff --git a/backend/src/main/java/esia/service/UlDataService.java b/backend/src/main/java/esia/service/UlDataService.java deleted file mode 100644 index 4ed8f86c..00000000 --- a/backend/src/main/java/esia/service/UlDataService.java +++ /dev/null @@ -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(); -} diff --git a/backend/src/main/java/esia/service/impl/DummyUlDataServiceImpl.java b/backend/src/main/java/esia/service/impl/DummyUlDataServiceImpl.java deleted file mode 100644 index 3aa253c0..00000000 --- a/backend/src/main/java/esia/service/impl/DummyUlDataServiceImpl.java +++ /dev/null @@ -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; - } -} diff --git a/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java b/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java new file mode 100644 index 00000000..44a4223b --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/SecurityConfig.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java new file mode 100644 index 00000000..1ed60af1 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java new file mode 100644 index 00000000..92197f85 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/controller/EsiaController.java @@ -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); + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/AddressModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/AddressModel.java new file mode 100644 index 00000000..c40f819c --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/AddressModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/Addresses.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/Addresses.java new file mode 100644 index 00000000..4ce2d1d0 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/Addresses.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/BrhsModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/BrhsModel.java new file mode 100644 index 00000000..a0f9b97a --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/BrhsModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/ContactModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/ContactModel.java new file mode 100644 index 00000000..89cf82f9 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/ContactModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/Contacts.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/Contacts.java new file mode 100644 index 00000000..b05ce57d --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/Contacts.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/EmployeeModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/EmployeeModel.java new file mode 100644 index 00000000..7c019e6a --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/EmployeeModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/EsiaAccessToken.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/EsiaAccessToken.java new file mode 100644 index 00000000..6c9c70f7 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/EsiaAccessToken.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/EsiaTokenResponse.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/EsiaTokenResponse.java new file mode 100644 index 00000000..c4919c87 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/EsiaTokenResponse.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/FormUrlencoded.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/FormUrlencoded.java new file mode 100644 index 00000000..60b54f42 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/FormUrlencoded.java @@ -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> ... + private final Map 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(); + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/OrgInfoModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/OrgInfoModel.java new file mode 100644 index 00000000..f3ad515d --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/OrgInfoModel.java @@ -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; +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/OrganizationModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/OrganizationModel.java new file mode 100644 index 00000000..f82d594d --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/OrganizationModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/PassportModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/PassportModel.java new file mode 100644 index 00000000..9b74cae8 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/PassportModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/PersonModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/PersonModel.java new file mode 100644 index 00000000..2375c650 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/PersonModel.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java new file mode 100644 index 00000000..0b07f973 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java @@ -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 parameters = new LinkedHashMap(); + 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 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 mapOf(String... params) { + if (params.length % 2 != 0) { + throw new IllegalArgumentException("Invalid params count"); + } + Map 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 params) { + StringBuilder uriBuilder = new StringBuilder(url.toString()); + uriBuilder.append('?'); + for (Map.Entry 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 parameters = new LinkedHashMap(); + 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 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 parameters = new LinkedHashMap(); + 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 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 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 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 params = mapOf( + "client_id", esiaConfig.getClientId(), + "redirect_uri", redirectUrlEncoded); + return makeRequest(url, params); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaDataService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaDataService.java new file mode 100644 index 00000000..8eac1efc --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaDataService.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java new file mode 100644 index 00000000..bce394ba --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java @@ -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); +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java new file mode 100644 index 00000000..bb3d071f --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java @@ -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 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 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 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 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 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 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 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 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); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthentication.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthentication.java new file mode 100644 index 00000000..47f65677 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthentication.java @@ -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 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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java new file mode 100644 index 00000000..494cf510 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/JwtAuthenticationProvider.java @@ -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); + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/UnauthorizedEntryPoint.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/UnauthorizedEntryPoint.java new file mode 100644 index 00000000..2fbdcec8 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/UnauthorizedEntryPoint.java @@ -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." + ); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java new file mode 100644 index 00000000..ef8af20c --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/filter/JwtAuthenticationFilter.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/model/Token.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/model/Token.java new file mode 100644 index 00000000..d7da8527 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/model/Token.java @@ -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; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java new file mode 100644 index 00000000..e075bf58 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/webbpm/jwt/service/JwtTokenService.java @@ -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); + } +} diff --git a/config/Dockerfile b/config/Dockerfile index ee0223e3..b33a7ea3 100644 --- a/config/Dockerfile +++ b/config/Dockerfile @@ -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 diff --git a/config/context.xml b/config/context.xml new file mode 100644 index 00000000..849be125 --- /dev/null +++ b/config/context.xml @@ -0,0 +1,36 @@ + + + + + + + + WEB-INF/web.xml + WEB-INF/tomcat-web.xml + ${catalina.base}/conf/web.xml + + + + + + diff --git a/config/patches/default.cli b/config/patches/default.cli index 6a4d30c4..d27e7a71 100644 --- a/config/patches/default.cli +++ b/config/patches/default.cli @@ -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"") diff --git a/config/standalone/dev/standalone.xml b/config/standalone/dev/standalone.xml index 2f742aa0..58bcd071 100644 --- a/config/standalone/dev/standalone.xml +++ b/config/standalone/dev/standalone.xml @@ -66,6 +66,17 @@ + + + + + + + + + + + diff --git a/config/tomcat/tomee/bin/setenv.sh b/config/tomcat/tomee/bin/setenv.sh new file mode 100755 index 00000000..8b69e674 --- /dev/null +++ b/config/tomcat/tomee/bin/setenv.sh @@ -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} \ +" diff --git a/config/tomcat/tomee/conf/context.xml b/config/tomcat/tomee/conf/context.xml new file mode 100644 index 00000000..92d999c7 --- /dev/null +++ b/config/tomcat/tomee/conf/context.xml @@ -0,0 +1,36 @@ + + + + + + + + WEB-INF/web.xml + WEB-INF/tomcat-web.xml + ${catalina.base}/conf/web.xml + + + + + + diff --git a/config/tomcat/tomee/conf/tomcat-users.xml b/config/tomcat/tomee/conf/tomcat-users.xml new file mode 100644 index 00000000..4a9631c8 --- /dev/null +++ b/config/tomcat/tomee/conf/tomcat-users.xml @@ -0,0 +1,39 @@ + + + + + + diff --git a/config/tomcat/tomee/conf/webbpm.properties b/config/tomcat/tomee/conf/webbpm.properties new file mode 100644 index 00000000..0fe1d606 --- /dev/null +++ b/config/tomcat/tomee/conf/webbpm.properties @@ -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 diff --git a/config/tomcat/tomee/webapps/manager/META-INF/context.xml b/config/tomcat/tomee/webapps/manager/META-INF/context.xml new file mode 100644 index 00000000..82a93f33 --- /dev/null +++ b/config/tomcat/tomee/webapps/manager/META-INF/context.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/distribution/pom.xml b/distribution/pom.xml index a7c0b9fc..6d789477 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -2,27 +2,27 @@ 4.0.0 - ervu_lkrp_ul - ervu_lkrp_ul + ru.micord.ervu.lkrp + ul 1.0.0-SNAPSHOT - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul distribution ear - /backend + /ul - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul backend war - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul frontend war @@ -37,16 +37,16 @@ - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul frontend / frontend.war - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul backend ${backendContext} - backend.war + ul.war @@ -59,7 +59,7 @@ enable-version-in-url - /backend-${project.version} + /ul-${project.version} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 13b7d14f..9e1465b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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" } diff --git a/frontend/package.json b/frontend/package.json index 96a1ff69..7e66c2ca 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/pom.xml b/frontend/pom.xml index f007ad0e..0f2c36fc 100644 --- a/frontend/pom.xml +++ b/frontend/pom.xml @@ -2,12 +2,12 @@ 4.0.0 - ervu_lkrp_ul - ervu_lkrp_ul + ru.micord.ervu.lkrp + ul 1.0.0-SNAPSHOT - ervu_lkrp_ul.ervu_lkrp_ul + ru.micord.ervu.lkrp.ul frontend war diff --git a/frontend/preview.html b/frontend/preview.html index 7d62682c..04126dc0 100644 --- a/frontend/preview.html +++ b/frontend/preview.html @@ -18,7 +18,7 @@ }); - +
diff --git a/frontend/src/resources/app-config.json b/frontend/src/resources/app-config.json index 2ccb6a6a..abae1b54 100644 --- a/frontend/src/resources/app-config.json +++ b/frontend/src/resources/app-config.json @@ -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": "", diff --git a/frontend/src/resources/template/app/component/admin_menu.html b/frontend/src/resources/template/app/component/admin_menu.html deleted file mode 100644 index f187ba8a..00000000 --- a/frontend/src/resources/template/app/component/admin_menu.html +++ /dev/null @@ -1,34 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/resources/template/app/component/app_header.html b/frontend/src/resources/template/app/component/app_header.html index 1f88b295..010761f7 100644 --- a/frontend/src/resources/template/app/component/app_header.html +++ b/frontend/src/resources/template/app/component/app_header.html @@ -1,17 +1,10 @@ -