From c1a35722a60f86e0d3e85ccad3817a4cf9c8b7a2 Mon Sep 17 00:00:00 2001 From: David Lemaignent Date: Mon, 31 May 2021 15:30:46 +0200 Subject: [PATCH 1/3] 1.13-SNAPSOT --- pom.xml | 16 +- .../config/GlobalProperties.java | 9 + .../esupsignature/config/WebAppConfig.java | 1 + .../esupsignature/config/pdf/PdfConfig.java | 4 +- .../config/security/WebSecurityConfig.java | 1 - .../config/sign/SignProperties.java | 9 + .../dss/config/DSSBeanConfig.java | 197 +--- .../dss/config/DSSProperties.java | 10 - .../esupsignature/dss/service/FOPService.java | 15 +- .../esupsignature/dss/service/OJService.java | 30 +- .../dss/service/XSLTService.java | 28 +- .../esupsignature/dss/tsp/MockTSPSource.java | 111 +++ .../esupsignature/entity/Certificat.java | 71 ++ .../esupsignature/entity/Form.java | 10 + .../esupsignature/entity/User.java | 11 + .../esupsignature/entity/Workflow.java | 13 + .../repository/CertificatRepository.java | 10 + .../repository/FormRepository.java | 14 +- .../repository/UserRepository.java | 1 + .../repository/WorkflowRepository.java | 4 +- .../service/CertificatService.java | 110 +++ .../esupsignature/service/FormService.java | 49 +- .../service/SignBookService.java | 9 +- .../service/SignRequestService.java | 142 ++- .../service/UserKeystoreService.java | 11 +- .../esupsignature/service/UserService.java | 11 + .../service/ValidationService.java | 25 +- .../service/WorkflowService.java | 21 +- .../esupsignature/service/dss/DSSService.java | 1 - .../service/event/EventService.java | 2 +- .../interfaces/fs/smb/SmbAccessImpl.java | 4 +- .../service/ldap/LdapPersonService.java | 2 +- .../service/{utils => }/mail/MailService.java | 2 +- .../service/security/PreAuthorizeService.java | 18 + .../service/security/SessionService.java | 23 + .../cas/CasAuthenticationSuccessHandler.java | 2 +- .../service/security/otp/OtpService.java | 2 +- .../service/utils/pdf/PdfService.java | 48 +- .../service/utils/sign/SignService.java | 79 +- .../GlobalAttributsControllerAdvice.java | 24 +- .../admin/CertificatController.java | 68 ++ .../admin/CurrentSessionsController.java | 13 +- .../web/controller/admin/DSSController.java | 16 +- .../controller/admin/FormAdminController.java | 5 +- .../admin/MessageAdminController.java | 2 +- .../admin/RolesManagersController.java | 60 ++ .../admin/WorkflowAdminController.java | 10 +- .../manager/ManagerFormController.java | 276 ++++++ .../ManagerRolesManagersController.java | 64 ++ .../manager/ManagerWorkflowController.java | 220 +++++ .../user/SignRequestController.java | 32 +- .../controller/user/ValidationController.java | 189 +++- .../web/controller/user/WizardController.java | 11 +- .../controller/user/WorkflowController.java | 11 +- .../web/otp/WsOtpSignController.java | 8 +- .../web/ws/WorkflowWsController.java | 5 +- .../web/ws/WsExportController.java | 4 +- src/main/resources/application.yml | 1 + src/main/resources/i18n/messages.properties | 37 + src/main/resources/keystore.p12 | Bin 0 -> 15906 bytes src/main/resources/policy/constraint.xml | 407 +++++--- .../resources/policy/custom-constraint.xml | 38 +- src/main/resources/self_signed_tsa.p12 | Bin 0 -> 4268 bytes src/main/resources/static/css/app.css | 39 +- src/main/resources/static/css/flags.css | 149 +++ src/main/resources/static/images/flags/at.svg | 6 + src/main/resources/static/images/flags/be.svg | 7 + src/main/resources/static/images/flags/bg.svg | 7 + src/main/resources/static/images/flags/cy.svg | 6 + src/main/resources/static/images/flags/cz.svg | 12 + src/main/resources/static/images/flags/de.svg | 7 + src/main/resources/static/images/flags/dk.svg | 5 + src/main/resources/static/images/flags/ee.svg | 7 + src/main/resources/static/images/flags/el.svg | 22 + src/main/resources/static/images/flags/es.svg | 581 ++++++++++++ src/main/resources/static/images/flags/eu.svg | 46 + src/main/resources/static/images/flags/fi.svg | 5 + src/main/resources/static/images/flags/fr.svg | 7 + src/main/resources/static/images/flags/hr.svg | 61 ++ src/main/resources/static/images/flags/hu.svg | 7 + src/main/resources/static/images/flags/ie.svg | 7 + src/main/resources/static/images/flags/is.svg | 9 + src/main/resources/static/images/flags/it.svg | 7 + src/main/resources/static/images/flags/li.svg | 279 ++++++ src/main/resources/static/images/flags/lt.svg | 7 + src/main/resources/static/images/flags/lu.svg | 5 + src/main/resources/static/images/flags/lv.svg | 6 + src/main/resources/static/images/flags/mt.svg | 49 + src/main/resources/static/images/flags/nl.svg | 7 + src/main/resources/static/images/flags/no.svg | 13 + .../static/images/flags/no_country.svg | 26 + src/main/resources/static/images/flags/pl.svg | 6 + src/main/resources/static/images/flags/pt.svg | 57 ++ src/main/resources/static/images/flags/ro.svg | 7 + src/main/resources/static/images/flags/se.svg | 16 + src/main/resources/static/images/flags/si.svg | 18 + src/main/resources/static/images/flags/sk.svg | 9 + src/main/resources/static/images/flags/uk.svg | 15 + .../static/js/modules/ui/GlobalUi.js | 6 +- .../static/js/modules/ui/forms/FormUi.js | 14 +- .../static/js/modules/ui/signrequests/Nexu.js | 93 +- .../modules/ui/signrequests/SignPosition.js | 35 +- .../js/modules/ui/signrequests/SignUi.js | 113 ++- .../modules/ui/signrequests/WorkspacePdf.js | 19 +- .../static/js/modules/utils/PdfViewer.js | 36 +- .../static/js/modules/utils/SseDispatcher.js | 13 - .../static/js/modules/utils/SseSubscribe.js | 53 -- .../templates/admin/certificats/list.html | 93 ++ .../templates/admin/currentsessions.html | 2 +- .../templates/admin/dss/lotl-info.html | 67 +- .../templates/admin/dss/oj-certificates.html | 46 - .../templates/admin/dss/pivot-changes.html | 218 +++-- .../templates/admin/dss/tl-info-country.html | 98 +- .../templates/admin/dss/tl-summary.html | 229 ++--- .../templates/admin/export/list.html | 2 +- .../templates/admin/forms/create.html | 4 +- .../resources/templates/admin/forms/list.html | 2 +- .../resources/templates/admin/forms/show.html | 4 +- .../templates/admin/forms/update.html | 4 +- .../resources/templates/admin/logs/list.html | 2 +- .../resources/templates/admin/messages.html | 2 +- .../templates/admin/roles-managers.html | 65 ++ .../templates/admin/signrequests/list.html | 2 +- .../templates/admin/signrequests/show.html | 2 +- src/main/resources/templates/admin/su.html | 2 +- .../templates/admin/workflows/list.html | 2 +- .../templates/admin/workflows/show-class.html | 2 +- .../templates/admin/workflows/show.html | 2 +- .../templates/admin/workflows/update.html | 6 +- .../resources/templates/fragments/head.html | 4 + .../resources/templates/fragments/nav.html | 8 + .../resources/templates/fragments/new.html | 4 +- .../fragments/{ => sides}/side-admin.html | 14 +- .../fragments/{ => sides}/side-dashboard.html | 14 +- .../fragments/{ => sides}/side-home.html | 0 .../fragments/sides/side-manager.html | 24 + .../fragments/{ => sides}/side-sign.html | 0 .../fragments/{ => sides}/side-user.html | 0 .../templates/fragments/{ => sides}/side.html | 0 .../fragments/tl-info-fragments.html | 28 +- .../templates/fragments/tl-info.html | 374 ++++---- .../resources/templates/fragments/tools.html | 13 +- src/main/resources/templates/index.html | 2 +- .../templates/managers/forms/create.html | 46 + .../templates/managers/forms/list.html | 188 ++++ .../templates/managers/forms/show.html | 128 +++ .../templates/managers/forms/update.html | 160 ++++ .../templates/managers/managersRoles.html | 65 ++ .../workflows/cards/stepscard-light.html | 44 + .../managers/workflows/cards/stepscard.html | 159 ++++ .../templates/managers/workflows/list.html | 161 ++++ .../managers/workflows/show-class.html | 72 ++ .../templates/managers/workflows/show.html | 161 ++++ .../templates/managers/workflows/update.html | 199 ++++ .../resources/templates/user/home/index.html | 2 +- .../templates/user/manage/details.html | 2 +- .../resources/templates/user/manage/list.html | 2 +- .../templates/user/reports/list.html | 2 +- .../templates/user/signrequests/details.html | 2 +- .../user/signrequests/includes/list-elem.html | 169 ++-- .../user/signrequests/includes/workspace.html | 8 + .../templates/user/signrequests/list.html | 21 +- .../signrequests/nexu-signature-process.html | 6 +- .../templates/user/signrequests/show.html | 54 +- .../templates/user/signrequests/update.html | 2 +- .../templates/user/users/properties.html | 2 +- .../templates/user/users/shares/list.html | 2 +- .../templates/user/users/shares/update.html | 2 +- .../templates/user/users/update.html | 2 +- .../templates/user/validation/form.html | 2 +- .../templates/user/validation/result.html | 282 +++++- .../templates/user/workflows/show.html | 2 +- .../xslt/html/detailed-report-bootstrap4.xslt | 444 ++++++--- .../resources/xslt/html/detailedReport.xml | 532 +++++++++++ .../xslt/html/short-report-bootstrap4.xslt | 303 ++++++ .../xslt/html/simple-report-bootstrap4.xslt | 142 ++- src/main/resources/xslt/html/simpleReport.xml | 20 + .../resources/xslt/pdf/simple-report.xslt | 870 ++++++++++++------ .../esupsignature/MailServiceTest.java | 2 +- 179 files changed, 8154 insertions(+), 1973 deletions(-) create mode 100644 src/main/java/org/esupportail/esupsignature/dss/tsp/MockTSPSource.java create mode 100644 src/main/java/org/esupportail/esupsignature/entity/Certificat.java create mode 100644 src/main/java/org/esupportail/esupsignature/repository/CertificatRepository.java create mode 100644 src/main/java/org/esupportail/esupsignature/service/CertificatService.java rename src/main/java/org/esupportail/esupsignature/service/{utils => }/mail/MailService.java (99%) create mode 100644 src/main/java/org/esupportail/esupsignature/service/security/SessionService.java create mode 100644 src/main/java/org/esupportail/esupsignature/web/controller/admin/CertificatController.java create mode 100644 src/main/java/org/esupportail/esupsignature/web/controller/admin/RolesManagersController.java create mode 100644 src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerFormController.java create mode 100644 src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerRolesManagersController.java create mode 100644 src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerWorkflowController.java create mode 100644 src/main/resources/keystore.p12 create mode 100644 src/main/resources/self_signed_tsa.p12 create mode 100644 src/main/resources/static/css/flags.css create mode 100644 src/main/resources/static/images/flags/at.svg create mode 100644 src/main/resources/static/images/flags/be.svg create mode 100644 src/main/resources/static/images/flags/bg.svg create mode 100644 src/main/resources/static/images/flags/cy.svg create mode 100644 src/main/resources/static/images/flags/cz.svg create mode 100644 src/main/resources/static/images/flags/de.svg create mode 100644 src/main/resources/static/images/flags/dk.svg create mode 100644 src/main/resources/static/images/flags/ee.svg create mode 100644 src/main/resources/static/images/flags/el.svg create mode 100644 src/main/resources/static/images/flags/es.svg create mode 100644 src/main/resources/static/images/flags/eu.svg create mode 100644 src/main/resources/static/images/flags/fi.svg create mode 100644 src/main/resources/static/images/flags/fr.svg create mode 100644 src/main/resources/static/images/flags/hr.svg create mode 100644 src/main/resources/static/images/flags/hu.svg create mode 100644 src/main/resources/static/images/flags/ie.svg create mode 100644 src/main/resources/static/images/flags/is.svg create mode 100644 src/main/resources/static/images/flags/it.svg create mode 100644 src/main/resources/static/images/flags/li.svg create mode 100644 src/main/resources/static/images/flags/lt.svg create mode 100644 src/main/resources/static/images/flags/lu.svg create mode 100644 src/main/resources/static/images/flags/lv.svg create mode 100644 src/main/resources/static/images/flags/mt.svg create mode 100644 src/main/resources/static/images/flags/nl.svg create mode 100644 src/main/resources/static/images/flags/no.svg create mode 100644 src/main/resources/static/images/flags/no_country.svg create mode 100644 src/main/resources/static/images/flags/pl.svg create mode 100644 src/main/resources/static/images/flags/pt.svg create mode 100644 src/main/resources/static/images/flags/ro.svg create mode 100644 src/main/resources/static/images/flags/se.svg create mode 100644 src/main/resources/static/images/flags/si.svg create mode 100644 src/main/resources/static/images/flags/sk.svg create mode 100644 src/main/resources/static/images/flags/uk.svg delete mode 100644 src/main/resources/static/js/modules/utils/SseDispatcher.js delete mode 100644 src/main/resources/static/js/modules/utils/SseSubscribe.js create mode 100644 src/main/resources/templates/admin/certificats/list.html delete mode 100644 src/main/resources/templates/admin/dss/oj-certificates.html create mode 100644 src/main/resources/templates/admin/roles-managers.html rename src/main/resources/templates/fragments/{ => sides}/side-admin.html (77%) rename src/main/resources/templates/fragments/{ => sides}/side-dashboard.html (76%) rename src/main/resources/templates/fragments/{ => sides}/side-home.html (100%) create mode 100644 src/main/resources/templates/fragments/sides/side-manager.html rename src/main/resources/templates/fragments/{ => sides}/side-sign.html (100%) rename src/main/resources/templates/fragments/{ => sides}/side-user.html (100%) rename src/main/resources/templates/fragments/{ => sides}/side.html (100%) create mode 100644 src/main/resources/templates/managers/forms/create.html create mode 100644 src/main/resources/templates/managers/forms/list.html create mode 100644 src/main/resources/templates/managers/forms/show.html create mode 100644 src/main/resources/templates/managers/forms/update.html create mode 100644 src/main/resources/templates/managers/managersRoles.html create mode 100644 src/main/resources/templates/managers/workflows/cards/stepscard-light.html create mode 100644 src/main/resources/templates/managers/workflows/cards/stepscard.html create mode 100644 src/main/resources/templates/managers/workflows/list.html create mode 100644 src/main/resources/templates/managers/workflows/show-class.html create mode 100644 src/main/resources/templates/managers/workflows/show.html create mode 100644 src/main/resources/templates/managers/workflows/update.html create mode 100644 src/main/resources/xslt/html/detailedReport.xml create mode 100644 src/main/resources/xslt/html/short-report-bootstrap4.xslt create mode 100644 src/main/resources/xslt/html/simpleReport.xml diff --git a/pom.xml b/pom.xml index 7805105d2..52816299f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.esupportail esup-signature - 1.12.3 + 1.13-SNAPSHOT esup-signature org.esupportail.esupsignature.EsupSignatureApplication @@ -179,6 +179,16 @@ + + org.webjars.bower + google-code-prettify + 1.0.5 + + + org.webjars + popper.js + 2.5.4 + org.webjars jquery @@ -639,9 +649,9 @@ 2.4.0 - com.github.AgNO3 + eu.agno3.jcifs jcifs-ng - 3db3e62157 + 2.1.6 org.apache.chemistry.opencmis diff --git a/src/main/java/org/esupportail/esupsignature/config/GlobalProperties.java b/src/main/java/org/esupportail/esupsignature/config/GlobalProperties.java index b8301cb37..0bffc5e46 100644 --- a/src/main/java/org/esupportail/esupsignature/config/GlobalProperties.java +++ b/src/main/java/org/esupportail/esupsignature/config/GlobalProperties.java @@ -32,6 +32,7 @@ public class GlobalProperties implements Cloneable { private String applicationEmail = "esup.signature@univ-ville.fr"; private int hoursBeforeRefreshNotif = 24; private Boolean infiniteScrolling = true; + private Boolean returnToHomeAfterSign = true; /** * Choisir le fonctionnement des délégations : *
    @@ -202,4 +203,12 @@ public Boolean getInfiniteScrolling() { public void setInfiniteScrolling(Boolean infiniteScrolling) { this.infiniteScrolling = infiniteScrolling; } + + public Boolean getReturnToHomeAfterSign() { + return returnToHomeAfterSign; + } + + public void setReturnToHomeAfterSign(Boolean returnToHomeAfterSign) { + this.returnToHomeAfterSign = returnToHomeAfterSign; + } } diff --git a/src/main/java/org/esupportail/esupsignature/config/WebAppConfig.java b/src/main/java/org/esupportail/esupsignature/config/WebAppConfig.java index c0e745f16..8542da9ec 100644 --- a/src/main/java/org/esupportail/esupsignature/config/WebAppConfig.java +++ b/src/main/java/org/esupportail/esupsignature/config/WebAppConfig.java @@ -91,6 +91,7 @@ public FilterRegistrationBean registerOpenEntityManagerInViewFilterBean() { registrationBean.addUrlPatterns( "/user/", "/user/*", "/admin/", "/admin/*", + "/manager/", "/manager/*", "/public/", "/public/*", "/ws/", "/ws/*" ); diff --git a/src/main/java/org/esupportail/esupsignature/config/pdf/PdfConfig.java b/src/main/java/org/esupportail/esupsignature/config/pdf/PdfConfig.java index a0659584f..270d95992 100644 --- a/src/main/java/org/esupportail/esupsignature/config/pdf/PdfConfig.java +++ b/src/main/java/org/esupportail/esupsignature/config/pdf/PdfConfig.java @@ -2,9 +2,9 @@ import org.esupportail.esupsignature.exception.EsupSignatureRuntimeException; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.annotation.PostConstruct; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -25,7 +25,7 @@ public PdfProperties getPdfProperties() { return pdfProperties; } - @Bean + @PostConstruct public void setPdfColorProfileUrl() { try { Path iccPath = Path.of(PdfConfig.class.getResource("/srgb.icc").getPath()); diff --git a/src/main/java/org/esupportail/esupsignature/config/security/WebSecurityConfig.java b/src/main/java/org/esupportail/esupsignature/config/security/WebSecurityConfig.java index af397ed61..4cd260a3a 100644 --- a/src/main/java/org/esupportail/esupsignature/config/security/WebSecurityConfig.java +++ b/src/main/java/org/esupportail/esupsignature/config/security/WebSecurityConfig.java @@ -109,7 +109,6 @@ private void setAuthorizeRequests(HttpSecurity http) throws Exception { .antMatchers("/").permitAll() .antMatchers("/admin/", "/admin/**").access("hasRole('ROLE_ADMIN')") .antMatchers("/user/", "/user/**").access("hasAnyRole('ROLE_USER', 'ROLE_OTP')") - .antMatchers("/sse/", "/sse/**").access("hasAnyRole('ROLE_USER', 'ROLE_OTP')") .antMatchers("/public/", "/public/**").permitAll() .antMatchers("/h2-console/**").access("hasRole('ROLE_ADMIN')") .antMatchers("/webjars/**").permitAll(); diff --git a/src/main/java/org/esupportail/esupsignature/config/sign/SignProperties.java b/src/main/java/org/esupportail/esupsignature/config/sign/SignProperties.java index e4bc1eed2..f0b541289 100644 --- a/src/main/java/org/esupportail/esupsignature/config/sign/SignProperties.java +++ b/src/main/java/org/esupportail/esupsignature/config/sign/SignProperties.java @@ -16,6 +16,7 @@ public class SignProperties { private ASiCContainerType containerType; private SignaturePackaging signaturePackaging; private Long passwordTimeout; + private String aesKey; public SignatureForm getDefaultSignatureForm() { return defaultSignatureForm; @@ -95,4 +96,12 @@ public Long getPasswordTimeout() { public void setPasswordTimeout(Long passwordTimeout) { this.passwordTimeout = passwordTimeout; } + + public String getAesKey() { + return aesKey; + } + + public void setAesKey(String aesKey) { + this.aesKey = aesKey; + } } diff --git a/src/main/java/org/esupportail/esupsignature/dss/config/DSSBeanConfig.java b/src/main/java/org/esupportail/esupsignature/dss/config/DSSBeanConfig.java index ff8ee46aa..413bae407 100644 --- a/src/main/java/org/esupportail/esupsignature/dss/config/DSSBeanConfig.java +++ b/src/main/java/org/esupportail/esupsignature/dss/config/DSSBeanConfig.java @@ -1,30 +1,22 @@ package org.esupportail.esupsignature.dss.config; import com.zaxxer.hikari.HikariDataSource; -import eu.europa.esig.dss.DomUtils; +import eu.europa.esig.dss.alert.ExceptionOnStatusAlert; import eu.europa.esig.dss.asic.cades.signature.ASiCWithCAdESService; import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService; import eu.europa.esig.dss.cades.signature.CAdESService; -import eu.europa.esig.dss.jaxb.ValidatorConfigurator; -import eu.europa.esig.dss.jaxb.XmlDefinerUtils; -import eu.europa.esig.dss.model.x509.CertificateToken; +import eu.europa.esig.dss.model.DSSException; import eu.europa.esig.dss.pades.signature.PAdESService; import eu.europa.esig.dss.service.crl.JdbcCacheCRLSource; import eu.europa.esig.dss.service.crl.OnlineCRLSource; -import eu.europa.esig.dss.service.http.commons.CommonsDataLoader; -import eu.europa.esig.dss.service.http.commons.FileCacheDataLoader; -import eu.europa.esig.dss.service.http.commons.OCSPDataLoader; -import eu.europa.esig.dss.service.http.commons.TimestampDataLoader; +import eu.europa.esig.dss.service.http.commons.*; import eu.europa.esig.dss.service.http.proxy.ProxyConfig; import eu.europa.esig.dss.service.ocsp.JdbcCacheOCSPSource; import eu.europa.esig.dss.service.ocsp.OnlineOCSPSource; import eu.europa.esig.dss.service.tsp.OnlineTSPSource; -import eu.europa.esig.dss.spi.DSSUtils; import eu.europa.esig.dss.spi.client.http.DSSFileLoader; import eu.europa.esig.dss.spi.client.http.IgnoreDataLoader; import eu.europa.esig.dss.spi.tsl.TrustedListsCertificateSource; -import eu.europa.esig.dss.spi.x509.CertificateSource; -import eu.europa.esig.dss.spi.x509.CommonTrustedCertificateSource; import eu.europa.esig.dss.spi.x509.KeyStoreCertificateSource; import eu.europa.esig.dss.spi.x509.tsp.TSPSource; import eu.europa.esig.dss.tsl.alerts.LOTLAlert; @@ -37,21 +29,16 @@ import eu.europa.esig.dss.tsl.alerts.handlers.log.LogOJUrlChangeAlertHandler; import eu.europa.esig.dss.tsl.alerts.handlers.log.LogTLExpirationAlertHandler; import eu.europa.esig.dss.tsl.alerts.handlers.log.LogTLSignatureErrorAlertHandler; -import eu.europa.esig.dss.tsl.function.GrantedTrustService; import eu.europa.esig.dss.tsl.function.OfficialJournalSchemeInformationURI; import eu.europa.esig.dss.tsl.job.TLValidationJob; import eu.europa.esig.dss.tsl.source.LOTLSource; import eu.europa.esig.dss.validation.CertificateVerifier; import eu.europa.esig.dss.validation.CommonCertificateVerifier; -import eu.europa.esig.dss.ws.signature.common.RemoteDocumentSignatureServiceImpl; -import eu.europa.esig.dss.ws.signature.common.RemoteMultipleDocumentsSignatureServiceImpl; -import eu.europa.esig.dss.ws.validation.common.RemoteDocumentValidationService; import eu.europa.esig.dss.xades.signature.XAdESService; import org.apache.http.conn.ssl.TrustAllStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -60,19 +47,12 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.sql.DataSource; -import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.net.URL; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; @Configuration @EnableConfigurationProperties(DSSProperties.class) -@ConditionalOnProperty({"dss.tsp-server"}) public class DSSBeanConfig { private static final Logger logger = LoggerFactory.getLogger(DSSBeanConfig.class); @@ -96,50 +76,51 @@ public void cachedCRLSourceInitialization() throws SQLException { jdbcCacheCRLSource.initTable(); } - @PostConstruct - public void cachedOCSPSourceInitialization() throws SQLException { - JdbcCacheOCSPSource jdbcCacheOCSPSource = cachedOCSPSource(); - jdbcCacheOCSPSource.initTable(); - } - @PreDestroy public void cachedCRLSourceClean() throws SQLException { JdbcCacheCRLSource jdbcCacheCRLSource = cachedCRLSource(); jdbcCacheCRLSource.destroyTable(); } - @PreDestroy - public void cachedOCSPSourceClean() throws SQLException { - JdbcCacheOCSPSource jdbcCacheOCSPSource = cachedOCSPSource(); - jdbcCacheOCSPSource.destroyTable(); + @Bean + public TSPSource tspSource() { + OnlineTSPSource tspSource = new OnlineTSPSource(dssProperties.getTspServer()); + TimestampDataLoader timestampDataLoader = new TimestampDataLoader(); + timestampDataLoader.setTimeoutConnection(10000); + timestampDataLoader.setTrustStrategy(new TrustAllStrategy()); + if(proxyConfig != null) { + timestampDataLoader.setProxyConfig(proxyConfig); + } + tspSource.setDataLoader(timestampDataLoader); + return tspSource; } @Bean public CommonsDataLoader dataLoader() { CommonsDataLoader dataLoader = new CommonsDataLoader(); - if(proxyConfig != null) { - dataLoader.setProxyConfig(proxyConfig); - } - dataLoader.setTimeoutConnection(10000); - dataLoader.setTrustStrategy(new TrustAllStrategy()); + dataLoader.setProxyConfig(proxyConfig); + return dataLoader; + } + + @Bean + public CommonsDataLoader trustAllDataLoader() { + CommonsDataLoader dataLoader = new CommonsDataLoader(); + dataLoader.setProxyConfig(proxyConfig); + dataLoader.setTrustStrategy(TrustAllStrategy.INSTANCE); return dataLoader; } @Bean public OCSPDataLoader ocspDataLoader() { OCSPDataLoader ocspDataLoader = new OCSPDataLoader(); - if(proxyConfig != null) { - ocspDataLoader.setProxyConfig(proxyConfig); - } - ocspDataLoader.setTimeoutConnection(10000); - ocspDataLoader.setTrustStrategy(new TrustAllStrategy()); + ocspDataLoader.setProxyConfig(proxyConfig); return ocspDataLoader; } @Bean public OnlineCRLSource onlineCRLSource() { OnlineCRLSource onlineCRLSource = new OnlineCRLSource(); - onlineCRLSource.setDataLoader(dataLoader()); + onlineCRLSource.setDataLoader(trustAllDataLoader()); return onlineCRLSource; } @@ -169,25 +150,19 @@ public OnlineOCSPSource onlineOcspSource() { } @Bean - public KeyStoreCertificateSource ojContentKeyStore() throws IOException { - File keystoreFile = new File(dssProperties.getKsFilename()); - KeyStoreCertificateSource keyStoreCertificateSource = null; - if(keystoreFile.exists()) { - logger.info("delete old oj file"); - keystoreFile.delete(); - } - logger.info("creating oj file in " + keystoreFile.getAbsolutePath()); - if(keystoreFile.createNewFile()) { - keyStoreCertificateSource = new KeyStoreCertificateSource((InputStream) null, dssProperties.getKsType(), dssProperties.getKsPassword()); + public KeyStoreCertificateSource ojContentKeyStore() { + try { + return new KeyStoreCertificateSource(new ClassPathResource("keystore.p12").getFile(), "PKCS12", "dss-password"); + } catch (IOException e) { + throw new DSSException("Unable to load the file " + "keystore.p12", e); } - return keyStoreCertificateSource; } @Bean public DSSFileLoader onlineLoader() { FileCacheDataLoader onlineFileLoader = new FileCacheDataLoader(); onlineFileLoader.setCacheExpirationTime(0); - onlineFileLoader.setDataLoader(dataLoader()); + onlineFileLoader.setDataLoader(trustAllDataLoader()); onlineFileLoader.setFileCacheDirectory(tlCacheDirectory()); return onlineFileLoader; } @@ -201,16 +176,19 @@ public DSSFileLoader offlineLoader() { return offlineFileLoader; } + @Bean(name = "european-trusted-list-certificate-source") + public TrustedListsCertificateSource trustedListSource() { + return new TrustedListsCertificateSource(); + } + @Bean - public TLValidationJob job() throws IOException { + public TLValidationJob job() { TLValidationJob job = new TLValidationJob(); - LOTLSource lotlSource = europeanLOTL(); job.setTrustedListCertificateSource(trustedListSource()); - job.setListOfTrustedListSources(lotlSource); + job.setListOfTrustedListSources(europeanLOTL()); job.setOfflineDataLoader(offlineLoader()); job.setOnlineDataLoader(onlineLoader()); - job.setLOTLAlerts(Arrays.asList(ojUrlAlert(lotlSource), lotlLocationAlert(lotlSource))); - job.setTLAlerts(Arrays.asList(tlSigningAlert(), tlExpirationDetection())); + job.setDebug(false); return job; } @@ -225,66 +203,32 @@ public File tlCacheDirectory() { } @Bean(name = "european-lotl-source") - public LOTLSource europeanLOTL() throws IOException { + public LOTLSource europeanLOTL() { LOTLSource lotlSource = new LOTLSource(); lotlSource.setUrl(dssProperties.getLotlUrl()); lotlSource.setCertificateSource(ojContentKeyStore()); lotlSource.setSigningCertificatesAnnouncementPredicate(new OfficialJournalSchemeInformationURI(dssProperties.getOjUrl())); - lotlSource.setTrustServicePredicate(new GrantedTrustService()); lotlSource.setPivotSupport(true); return lotlSource; } - @Bean(name = "european-trusted-list-certificate-source") - public TrustedListsCertificateSource trustedListSource() { - return new TrustedListsCertificateSource(); - } - - @Bean - public CommonTrustedCertificateSource myTrustedCertificateSource() { - CommonTrustedCertificateSource certSource = new CommonTrustedCertificateSource(); - for(String trustedCertificatUrl : dssProperties.getTrustedCertificatUrlList()) { - logger.info("adding trusted certificat : " + trustedCertificatUrl); - try { - InputStream in = new URL(trustedCertificatUrl).openStream(); - CertificateToken certificateToken = DSSUtils.loadCertificate(in); - certSource.addCertificate(certificateToken); - } catch (IOException e) { - logger.warn("unable to add trusted certificat : " + trustedCertificatUrl, e); - } - } - return certSource; - } - @Bean public CertificateVerifier certificateVerifier() { - List trustedCertSources = new ArrayList<>(); - trustedCertSources.add(trustedListSource()); - trustedCertSources.add(myTrustedCertificateSource()); - CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier(trustedCertSources, cachedCRLSource(), cachedOCSPSource(), dataLoader()); - return commonCertificateVerifier; + CommonCertificateVerifier certificateVerifier = new CommonCertificateVerifier(); + certificateVerifier.setCrlSource(cachedCRLSource()); + certificateVerifier.setOcspSource(onlineOcspSource()); + certificateVerifier.setDataLoader(dataLoader()); + certificateVerifier.setTrustedCertSources(trustedListSource()); + certificateVerifier.setAlertOnMissingRevocationData(new ExceptionOnStatusAlert()); + certificateVerifier.setCheckRevocationForUntrustedChains(false); + return certificateVerifier; } @Bean - public ClassPathResource defaultPolicy() throws IOException { - ClassPathResource classPathResource = new ClassPathResource(dssProperties.getDefaultValidationPolicy()); - if(!classPathResource.exists()) { - logger.error("Default Validation Policy doesn't exist"); - } + public ClassPathResource defaultPolicy() { return new ClassPathResource(dssProperties.getDefaultValidationPolicy()); } - @Bean - public TSPSource tspSource() { - OnlineTSPSource tspSource = new OnlineTSPSource(dssProperties.getTspServer()); - TimestampDataLoader timestampDataLoader = new TimestampDataLoader(); - if(proxyConfig != null) { - timestampDataLoader.setProxyConfig(proxyConfig); - } - tspSource.setDataLoader(timestampDataLoader); - return tspSource; - } - @Bean public CAdESService cadesService() { CAdESService service = new CAdESService(certificateVerifier()); @@ -320,42 +264,6 @@ public ASiCWithXAdESService asicWithXadesService() { return service; } - @Bean - public RemoteDocumentSignatureServiceImpl remoteSignatureService() { - RemoteDocumentSignatureServiceImpl service = new RemoteDocumentSignatureServiceImpl(); - service.setAsicWithCAdESService(asicWithCadesService()); - service.setAsicWithXAdESService(asicWithXadesService()); - service.setCadesService(cadesService()); - service.setXadesService(xadesService()); - service.setPadesService(padesService()); - return service; - } - - @Bean - public RemoteMultipleDocumentsSignatureServiceImpl remoteMultipleDocumentsSignatureService() { - RemoteMultipleDocumentsSignatureServiceImpl service = new RemoteMultipleDocumentsSignatureServiceImpl(); - service.setAsicWithCAdESService(asicWithCadesService()); - service.setAsicWithXAdESService(asicWithXadesService()); - service.setXadesService(xadesService()); - return service; - } - - @Bean - public XmlDefinerUtils xmlDefinerUtils() throws ParserConfigurationException { - XmlDefinerUtils xmlDefinerUtils = XmlDefinerUtils.getInstance(); - ValidatorConfigurator.getSecureValidatorConfigurator(); - DomUtils.enableFeature("http://xml.org/sax/features/external-general-entities"); - DomUtils.disableFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd"); - return xmlDefinerUtils; - } - - @Bean - public RemoteDocumentValidationService remoteValidationService() throws Exception { - RemoteDocumentValidationService service = new RemoteDocumentValidationService(); - service.setVerifier(certificateVerifier()); - return service; - } - public DataSource cacheDataSource() { HikariDataSource ds = new HikariDataSource(); ds.setPoolName("DSS-Hikari-Pool"); @@ -364,11 +272,6 @@ public DataSource cacheDataSource() { ds.setUsername(dssProperties.getCacheUsername()); ds.setPassword(dssProperties.getCachePassword()); ds.setAutoCommit(false); - try { - ds.getConnection(); - } catch (SQLException throwables) { - throwables.printStackTrace(); - } return ds; } @@ -396,4 +299,4 @@ public LOTLAlert lotlLocationAlert(LOTLSource source) { return new LOTLAlert(lotlLocationDetection, handler); } -} \ No newline at end of file +} diff --git a/src/main/java/org/esupportail/esupsignature/dss/config/DSSProperties.java b/src/main/java/org/esupportail/esupsignature/dss/config/DSSProperties.java index 5bf25420f..cd1b37f9b 100644 --- a/src/main/java/org/esupportail/esupsignature/dss/config/DSSProperties.java +++ b/src/main/java/org/esupportail/esupsignature/dss/config/DSSProperties.java @@ -2,8 +2,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; -import java.util.List; - @ConfigurationProperties(prefix="dss") public class DSSProperties { @@ -22,7 +20,6 @@ public class DSSProperties { private String lotlUrl; private String lotlCountryCode; private String ojUrl; - private List trustedCertificatUrlList; public String getCacheUsername() { return cacheUsername; @@ -144,11 +141,4 @@ public void setOjUrl(String ojUrl) { this.ojUrl = ojUrl; } - public List getTrustedCertificatUrlList() { - return trustedCertificatUrlList; - } - - public void setTrustedCertificatUrlList(List trustedCertificatUrlList) { - this.trustedCertificatUrlList = trustedCertificatUrlList; - } } diff --git a/src/main/java/org/esupportail/esupsignature/dss/service/FOPService.java b/src/main/java/org/esupportail/esupsignature/dss/service/FOPService.java index 955a111e8..66a39e0c9 100644 --- a/src/main/java/org/esupportail/esupsignature/dss/service/FOPService.java +++ b/src/main/java/org/esupportail/esupsignature/dss/service/FOPService.java @@ -2,6 +2,8 @@ import eu.europa.esig.dss.DSSXmlErrorListener; import eu.europa.esig.dss.DomUtils; +import eu.europa.esig.dss.detailedreport.DetailedReportFacade; +import eu.europa.esig.dss.simplereport.SimpleReportFacade; import eu.europa.esig.dss.utils.Utils; import org.apache.fop.apps.*; import org.springframework.stereotype.Component; @@ -18,7 +20,6 @@ import java.io.File; import java.io.InputStream; import java.io.OutputStream; -import java.io.StringReader; @Component public class FOPService { @@ -53,10 +54,8 @@ public void init() throws Exception { public void generateSimpleReport(String simpleReport, OutputStream os) throws Exception { Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, os); - Result res = new SAXResult(fop.getDefaultHandler()); - Transformer transformer = templateSimpleReport.newTransformer(); - transformer.setErrorListener(new DSSXmlErrorListener()); - transformer.transform(new StreamSource(new StringReader(simpleReport)), res); + Result result = new SAXResult(fop.getDefaultHandler()); + SimpleReportFacade.newFacade().generatePdfReport(simpleReport, result); } public void generateSimpleReport(Document dom, OutputStream os) throws Exception { @@ -69,10 +68,8 @@ public void generateSimpleReport(Document dom, OutputStream os) throws Exception public void generateDetailedReport(String detailedReport, OutputStream os) throws Exception { Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, os); - Result res = new SAXResult(fop.getDefaultHandler()); - Transformer transformer = templateDetailedReport.newTransformer(); - transformer.setErrorListener(new DSSXmlErrorListener()); - transformer.transform(new StreamSource(new StringReader(detailedReport)), res); + Result result = new SAXResult(fop.getDefaultHandler()); + DetailedReportFacade.newFacade().generatePdfReport(detailedReport, result); } } diff --git a/src/main/java/org/esupportail/esupsignature/dss/service/OJService.java b/src/main/java/org/esupportail/esupsignature/dss/service/OJService.java index ac40ec31c..443cb82a7 100644 --- a/src/main/java/org/esupportail/esupsignature/dss/service/OJService.java +++ b/src/main/java/org/esupportail/esupsignature/dss/service/OJService.java @@ -6,7 +6,6 @@ import eu.europa.esig.dss.spi.tsl.TrustedListsCertificateSource; import eu.europa.esig.dss.spi.x509.CommonTrustedCertificateSource; import eu.europa.esig.dss.spi.x509.KeyStoreCertificateSource; -import eu.europa.esig.dss.utils.Utils; import org.esupportail.esupsignature.dss.config.DSSBeanConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,15 +14,13 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; @Service @ConditionalOnBean(DSSBeanConfig.class) public class OJService { - private static final Logger log = LoggerFactory.getLogger(OJService.class); + private static final Logger logger = LoggerFactory.getLogger(OJService.class); @Resource private DSSBeanConfig dssBeanConfig; @@ -39,36 +36,27 @@ public class OJService { private CommonTrustedCertificateSource myTrustedCertificateSource; public void getCertificats() { - try { - log.info("start offline refreshing oj keystore"); - dssBeanConfig.job().offlineRefresh(); - refresh(); - ojContentKeyStore.addAllCertificatesToKeyStore(trustedListsCertificateSource.getCertificates()); - ojContentKeyStore.addAllCertificatesToKeyStore(myTrustedCertificateSource.getCertificates()); - OutputStream fos = new FileOutputStream(dssBeanConfig.getDssProperties().getKsFilename()); - ojContentKeyStore.store(fos); - Utils.closeQuietly(fos); - log.info("init trusted lists OK"); - } catch(IOException e) { - log.error("Error getting certificats", e); - } + ojContentKeyStore.addAllCertificatesToKeyStore(myTrustedCertificateSource.getCertificates()); + dssBeanConfig.job().offlineRefresh(); + dssBeanConfig.job().onlineRefresh(); } public void refresh() { try { if(checkOjFreshness()) { - log.info("start online refreshing oj keystore"); + logger.info("start online refreshing oj keystore"); dssBeanConfig.job().onlineRefresh(); } else { - log.info("no online refresh needed for trusted lists"); + logger.info("no online refresh needed for trusted lists"); } } catch(IOException e) { - log.error("Error refreshing dss", e); + logger.error("Error refreshing dss", e); } } - public boolean checkOjFreshness() { + public boolean checkOjFreshness() throws IOException { TLValidationJobSummary summary = trustedListsCertificateSource.getSummary(); + if(summary == null) return true; LOTLInfo lotlInfo = summary.getLOTLInfos().get(0); return !lotlInfo.getValidationCacheInfo().isValid() || lotlInfo.getValidationCacheInfo().isRefreshNeeded() diff --git a/src/main/java/org/esupportail/esupsignature/dss/service/XSLTService.java b/src/main/java/org/esupportail/esupsignature/dss/service/XSLTService.java index f2a0134ec..cf84fd79b 100644 --- a/src/main/java/org/esupportail/esupsignature/dss/service/XSLTService.java +++ b/src/main/java/org/esupportail/esupsignature/dss/service/XSLTService.java @@ -2,17 +2,17 @@ import eu.europa.esig.dss.DSSXmlErrorListener; import eu.europa.esig.dss.DomUtils; +import eu.europa.esig.dss.diagnostic.DiagnosticDataFacade; +import eu.europa.esig.dss.diagnostic.jaxb.XmlDiagnosticData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import org.w3c.dom.Document; import javax.annotation.PostConstruct; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.*; @@ -22,6 +22,7 @@ public class XSLTService { private static final Logger logger = LoggerFactory.getLogger(XSLTService.class); + private Templates templateShortReport; private Templates templateSimpleReport; private Templates templateDetailedReport; @@ -29,6 +30,10 @@ public class XSLTService { public void init() throws TransformerConfigurationException, IOException { TransformerFactory transformerFactory = DomUtils.getSecureTransformerFactory(); + try (InputStream is = XSLTService.class.getResourceAsStream("/xslt/html/short-report-bootstrap4.xslt")) { + templateShortReport = transformerFactory.newTemplates(new StreamSource(is)); + } + try (InputStream is = XSLTService.class.getResourceAsStream("/xslt/html/simple-report-bootstrap4.xslt")) { templateSimpleReport = transformerFactory.newTemplates(new StreamSource(is)); } @@ -38,10 +43,10 @@ public void init() throws TransformerConfigurationException, IOException { } } - public String generateSimpleReport(String simpleReport) { + public String generateShortReport(String simpleReport) { Writer writer = new StringWriter(); try { - Transformer transformer = templateSimpleReport.newTransformer(); + Transformer transformer = templateShortReport.newTransformer(); transformer.setErrorListener(new DSSXmlErrorListener()); transformer.transform(new StreamSource(new StringReader(simpleReport)), new StreamResult(writer)); } catch (Exception e) { @@ -50,12 +55,12 @@ public String generateSimpleReport(String simpleReport) { return writer.toString(); } - public String generateSimpleReport(Document dom) { + public String generateSimpleReport(String simpleReport) { Writer writer = new StringWriter(); try { Transformer transformer = templateSimpleReport.newTransformer(); transformer.setErrorListener(new DSSXmlErrorListener()); - transformer.transform(new DOMSource(dom), new StreamResult(writer)); + transformer.transform(new StreamSource(new StringReader(simpleReport)), new StreamResult(writer)); } catch (Exception e) { logger.error("Error while generating simple report : " + e.getMessage(), e); } @@ -74,4 +79,15 @@ public String generateDetailedReport(String detailedReport) { return writer.toString(); } + public String generateSVG(String diagnosticDataXml) { + try (Writer writer = new StringWriter()) { + XmlDiagnosticData diagnosticData = DiagnosticDataFacade.newFacade().unmarshall(diagnosticDataXml); + DiagnosticDataFacade.newFacade().generateSVG(diagnosticData, new StreamResult(writer)); + return writer.toString(); + } catch (Exception e) { + logger.error("Error while generating the SVG : " + e.getMessage(), e); + return null; + } + } + } \ No newline at end of file diff --git a/src/main/java/org/esupportail/esupsignature/dss/tsp/MockTSPSource.java b/src/main/java/org/esupportail/esupsignature/dss/tsp/MockTSPSource.java new file mode 100644 index 000000000..68d976638 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/dss/tsp/MockTSPSource.java @@ -0,0 +1,111 @@ +package org.esupportail.esupsignature.dss.tsp; + +import eu.europa.esig.dss.enumerations.DigestAlgorithm; +import eu.europa.esig.dss.model.DSSException; +import eu.europa.esig.dss.model.TimestampBinary; +import eu.europa.esig.dss.model.x509.CertificateToken; +import eu.europa.esig.dss.spi.DSSASN1Utils; +import eu.europa.esig.dss.spi.x509.tsp.TSPSource; +import eu.europa.esig.dss.token.KSPrivateKeyEntry; +import eu.europa.esig.dss.token.KeyStoreSignatureTokenConnection; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator; +import org.bouncycastle.cms.SignerInfoGenerator; +import org.bouncycastle.cms.SignerInfoGeneratorBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.tsp.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.*; + +public class MockTSPSource implements TSPSource { + + private static final long serialVersionUID = 8863748492343274842L; + + private final Logger LOG = LoggerFactory.getLogger(MockTSPSource.class); + + private static SecureRandom random = new SecureRandom(); + + private KeyStoreSignatureTokenConnection token; + + private String alias; + + public void setToken(KeyStoreSignatureTokenConnection token) { + this.token = token; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + @Override + public TimestampBinary getTimeStampResponse(DigestAlgorithm digestAlgorithm, byte[] digest) { + if (token == null) { + throw new DSSException("KeyStore token is not defined!"); + } + try { + TimeStampRequestGenerator requestGenerator = new TimeStampRequestGenerator(); + requestGenerator.setCertReq(true); + TimeStampRequest request = requestGenerator.generate(new ASN1ObjectIdentifier(digestAlgorithm.getOid()), digest); + + KSPrivateKeyEntry ksPK = (KSPrivateKeyEntry) token.getKey(alias); + if (ksPK == null) { + throw new DSSException("Unable to initialize the MockTSPSource"); + } + + LOG.info("Timestamping with {}", ksPK.getCertificate()); + + X509CertificateHolder certificate = new X509CertificateHolder(ksPK.getCertificate().getEncoded()); + List chain = new ArrayList(); + CertificateToken[] certificateChain = ksPK.getCertificateChain(); + for (CertificateToken token : certificateChain) { + chain.add(token.getCertificate()); + } + + Set accepted = new HashSet(); + accepted.add(TSPAlgorithms.SHA1); + accepted.add(TSPAlgorithms.SHA256); + accepted.add(TSPAlgorithms.SHA512); + + AlgorithmIdentifier digestAlgorithmIdentifier = new AlgorithmIdentifier(new ASN1ObjectIdentifier(digestAlgorithm.getOid())); + AlgorithmIdentifier encryptionAlg = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption); + + DefaultCMSSignatureAlgorithmNameGenerator sigAlgoGenerator = new DefaultCMSSignatureAlgorithmNameGenerator(); + String sigAlgoName = sigAlgoGenerator.getSignatureName(digestAlgorithmIdentifier, encryptionAlg); + + ContentSigner signer = new JcaContentSignerBuilder(sigAlgoName).build(ksPK.getPrivateKey()); + + SignerInfoGenerator infoGenerator = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider()).build(signer, certificate); + DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(digestAlgorithmIdentifier); + + TimeStampTokenGenerator tokenGenerator = new TimeStampTokenGenerator(infoGenerator, digestCalculator, new ASN1ObjectIdentifier("1.2.3.4")); + tokenGenerator.addCertificates(new JcaCertStore(chain)); + + TimeStampResponseGenerator responseGenerator = new TimeStampResponseGenerator(tokenGenerator, accepted); + TimeStampResponse response = responseGenerator.generate(request, new BigInteger(128, random), new Date()); + TimeStampToken timeStampToken = response.getTimeStampToken(); + + return new TimestampBinary(DSSASN1Utils.getDEREncoded(timeStampToken)); + + } catch (IOException | TSPException | OperatorException | CertificateException e) { + throw new DSSException("Unable to generate a timestamp from the Mock", e); + + } + } + +} diff --git a/src/main/java/org/esupportail/esupsignature/entity/Certificat.java b/src/main/java/org/esupportail/esupsignature/entity/Certificat.java new file mode 100644 index 000000000..00c10a549 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/entity/Certificat.java @@ -0,0 +1,71 @@ +package org.esupportail.esupsignature.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Entity +@Table(name = "certificat") +public class Certificat { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @JsonIgnore + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true) + private Document keystore = new Document(); + + private String password; + + @Temporal(TemporalType.TIMESTAMP) + @DateTimeFormat(pattern = "dd/MM/yyyy HH:mm") + private Date expireDate; + + @ElementCollection(targetClass=String.class) + private List roles = new ArrayList<>(); + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public Document getKeystore() { + return keystore; + } + + public void setKeystore(Document keystore) { + this.keystore = keystore; + } + + public String getPassword() { + return password; + } + + public Date getExpireDate() { + return expireDate; + } + + public void setExpireDate(Date expireDate) { + this.expireDate = expireDate; + } + + public void setPassword(String password) { + this.password = password; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } +} diff --git a/src/main/java/org/esupportail/esupsignature/entity/Form.java b/src/main/java/org/esupportail/esupsignature/entity/Form.java index 209905e2a..b23d95890 100644 --- a/src/main/java/org/esupportail/esupsignature/entity/Form.java +++ b/src/main/java/org/esupportail/esupsignature/entity/Form.java @@ -49,6 +49,8 @@ public class Form { private Boolean activeVersion = false; + private Boolean deleted = false; + @JsonIgnore @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) private Document document = new Document(); @@ -172,6 +174,14 @@ public void setActiveVersion(Boolean activeVersion) { this.activeVersion = activeVersion; } + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + public Document getDocument() { return document; } diff --git a/src/main/java/org/esupportail/esupsignature/entity/User.java b/src/main/java/org/esupportail/esupsignature/entity/User.java index 3a6e30efb..26e32a066 100644 --- a/src/main/java/org/esupportail/esupsignature/entity/User.java +++ b/src/main/java/org/esupportail/esupsignature/entity/User.java @@ -32,6 +32,9 @@ public class User { @Column(unique=true) private String email; + @ElementCollection(targetClass=String.class) + private List managersRoles = new ArrayList<>(); + @ElementCollection @JsonIgnore private Map uiParams = new LinkedHashMap<>(); @@ -137,6 +140,14 @@ public void setEmail(String email) { this.email = email; } + public List getManagersRoles() { + return managersRoles; + } + + public void setManagersRoles(List managersRoles) { + this.managersRoles = managersRoles; + } + public Map getUiParams() { return uiParams; } diff --git a/src/main/java/org/esupportail/esupsignature/entity/Workflow.java b/src/main/java/org/esupportail/esupsignature/entity/Workflow.java index 48f378970..1638455c8 100644 --- a/src/main/java/org/esupportail/esupsignature/entity/Workflow.java +++ b/src/main/java/org/esupportail/esupsignature/entity/Workflow.java @@ -67,6 +67,8 @@ public class Workflow { private Boolean fromCode; + private Boolean visibility = false; + public Long getId() { return id; } @@ -242,4 +244,15 @@ public Boolean getFromCode() { public void setFromCode(Boolean fromCode) { this.fromCode = fromCode; } + + public Boolean getVisibility() { + if (this.visibility == null) { + return false; + } + return visibility; + } + + public void setVisibility(Boolean hidden) { + this.visibility = hidden; + } } diff --git a/src/main/java/org/esupportail/esupsignature/repository/CertificatRepository.java b/src/main/java/org/esupportail/esupsignature/repository/CertificatRepository.java new file mode 100644 index 000000000..a67954548 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/repository/CertificatRepository.java @@ -0,0 +1,10 @@ +package org.esupportail.esupsignature.repository; + +import org.esupportail.esupsignature.entity.Certificat; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +public interface CertificatRepository extends CrudRepository { + List findByRolesIn(List roles); +} diff --git a/src/main/java/org/esupportail/esupsignature/repository/FormRepository.java b/src/main/java/org/esupportail/esupsignature/repository/FormRepository.java index 03e4d746c..484f54dc3 100644 --- a/src/main/java/org/esupportail/esupsignature/repository/FormRepository.java +++ b/src/main/java/org/esupportail/esupsignature/repository/FormRepository.java @@ -8,12 +8,14 @@ import java.util.List; public interface FormRepository extends CrudRepository { - List
    findFormByNameAndActiveVersion(String name, Boolean activeVersion); - @Query("select distinct f from Form f join f.managers m where m = :email") - List findFormByManagersContains(@Param("email") String email); - List findDistinctByAuthorizedShareTypesIsNotNull(); - List findFormByName(String name); - @Query("select distinct f from Form f where f.activeVersion = true and (f.publicUsage = true or :role member of f.roles) order by f.name") + List findFormByDeletedIsNullOrDeletedIsFalse(); + List findFormByNameAndActiveVersionAndDeletedIsNullOrDeletedIsFalse(String name, Boolean activeVersion); + @Query("select distinct f from Form f join f.managers m where m = :email and (f.deleted is null or f.deleted = false)") + List findFormByManagersContainsAndDeletedIsNullOrDeletedIsFalse(@Param("email") String email); + List findDistinctByAuthorizedShareTypesIsNotNullAndDeletedIsNullOrDeletedIsFalse(); + List findFormByNameAndDeletedIsNullOrDeletedIsFalse(String name); + @Query("select distinct f from Form f where (f.deleted is null or f.deleted = false) and f.activeVersion = true and (f.publicUsage = true or :role member of f.roles) order by f.name") List findAuthorizedForms(String role); + List findByRolesInAndDeletedIsNullOrDeletedIsFalse(List role); } diff --git a/src/main/java/org/esupportail/esupsignature/repository/UserRepository.java b/src/main/java/org/esupportail/esupsignature/repository/UserRepository.java index 8f3b01448..c83f8d7ff 100644 --- a/src/main/java/org/esupportail/esupsignature/repository/UserRepository.java +++ b/src/main/java/org/esupportail/esupsignature/repository/UserRepository.java @@ -14,6 +14,7 @@ public interface UserRepository extends CrudRepository { List findByEmailStartingWith(String email); @Query(value = "select distinct roles from user_roles", nativeQuery = true) List getAllRoles(); + List findByManagersRolesIn(List role); Long countByEppn(String eppn); Long countByEmail(String email); } diff --git a/src/main/java/org/esupportail/esupsignature/repository/WorkflowRepository.java b/src/main/java/org/esupportail/esupsignature/repository/WorkflowRepository.java index 4509212d3..5244bc1fa 100644 --- a/src/main/java/org/esupportail/esupsignature/repository/WorkflowRepository.java +++ b/src/main/java/org/esupportail/esupsignature/repository/WorkflowRepository.java @@ -1,6 +1,7 @@ package org.esupportail.esupsignature.repository; import org.esupportail.esupsignature.entity.Workflow; +import org.hibernate.jdbc.Work; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -15,8 +16,9 @@ public interface WorkflowRepository extends CrudRepository { @Query("select w from Workflow w join w.managers m where m = :email") List findByManagersContains(@Param("email") String email); List findDistinctByAuthorizedShareTypesIsNotNull(); + List findByRolesIn(List roles); Long countByName(String name); Long countByNameAndCreateByEppn(String name, String userEppn); - @Query("select distinct w from Workflow w where w.publicUsage = true or :role member of w.roles order by w.name") + @Query("select distinct w from Workflow w where w.publicUsage = true or (w.visibility = true and :role member of w.roles) order by w.name") List findAuthorizedForms(String role); } diff --git a/src/main/java/org/esupportail/esupsignature/service/CertificatService.java b/src/main/java/org/esupportail/esupsignature/service/CertificatService.java new file mode 100644 index 000000000..d9c1509b9 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/service/CertificatService.java @@ -0,0 +1,110 @@ +package org.esupportail.esupsignature.service; + +import eu.europa.esig.dss.model.x509.CertificateToken; +import eu.europa.esig.dss.token.Pkcs12SignatureToken; +import org.esupportail.esupsignature.config.sign.SignProperties; +import org.esupportail.esupsignature.entity.Certificat; +import org.esupportail.esupsignature.entity.User; +import org.esupportail.esupsignature.exception.EsupSignatureKeystoreException; +import org.esupportail.esupsignature.repository.CertificatRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.Key; +import java.util.*; + +@Service +public class CertificatService { + + private static final Logger logger = LoggerFactory.getLogger(CertificatService.class); + + @Resource + private UserService userService; + + @Resource + private UserKeystoreService userKeystoreService; + + @Resource + private CertificatRepository certificatRepository; + + @Resource + private DocumentService documentService; + + @Resource + private SignProperties signProperties; + + + public Certificat getById(Long id) { + return certificatRepository.findById(id).get(); + } + + public Certificat getCertificatByRole(String role) { + return certificatRepository.findByRolesIn(Collections.singletonList(role)).get(0); + } + + public List getCertificatByUser(String userEppn) { + User user = userService.getUserByEppn(userEppn); + Set certificats = new HashSet<>(certificatRepository.findByRolesIn(user.getRoles())); + return new ArrayList<>(certificats); + } + + public List getAllCertificats() { + List certificats = new ArrayList<>(); + certificatRepository.findAll().forEach(certificats::add); + return certificats; + } + + @Transactional + public void addCertificat(MultipartFile keystore, List roles, String password) throws IOException, EsupSignatureKeystoreException { + Certificat certificat = new Certificat(); + byte[] keystoreBytes = keystore.getBytes(); + Pkcs12SignatureToken pkcs12SignatureToken = userKeystoreService.getPkcs12Token(new ByteArrayInputStream(keystoreBytes), password); + CertificateToken certificateToken = userKeystoreService.getCertificateToken(pkcs12SignatureToken); + certificat.setKeystore(documentService.createDocument(new ByteArrayInputStream(keystoreBytes), certificateToken.getSubject().getPrincipal().getName("CANONICAL"), keystore.getContentType())); + certificat.setRoles(roles); + certificat.setPassword(encryptPassword(password)); + certificat.setExpireDate(certificateToken.getNotAfter()); + certificatRepository.save(certificat); + } + + @Transactional + public void delete(Long id) { + Certificat certificat = getById(id); + certificatRepository.delete(certificat); + } + + public String encryptPassword(String password) { + try { + Key aesKey = new SecretKeySpec(signProperties.getAesKey().getBytes(), "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, aesKey); + String charSet = "UTF-8"; + byte[] encrypted = cipher.doFinal(password.getBytes(charSet)); + return new String(Base64.getEncoder().encode(encrypted)); + } catch (Exception e) { + logger.error(e.getMessage()); + } + return null; + } + + public String decryptPassword(String key) { + try { + Key aesKey = new SecretKeySpec(signProperties.getAesKey().getBytes(), "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, aesKey); + return new String(cipher.doFinal(Base64.getDecoder().decode(key))); + } catch (Exception e) { + logger.error(e.getMessage()); + } + return null; + } + +} diff --git a/src/main/java/org/esupportail/esupsignature/service/FormService.java b/src/main/java/org/esupportail/esupsignature/service/FormService.java index bb69830be..5bde810e3 100644 --- a/src/main/java/org/esupportail/esupsignature/service/FormService.java +++ b/src/main/java/org/esupportail/esupsignature/service/FormService.java @@ -83,7 +83,7 @@ public List getFormsByUser(String userEppn, String authUserEppn){ } else { List userShares = userShareService.getUserShares(userEppn, Collections.singletonList(authUserEppn), ShareType.create); for(UserShare userShare : userShares) { - if(userShare.getForm() != null){ + if(userShare.getForm() != null && !userShare.getForm().getDeleted()){ forms.add(userShare.getForm()); } } @@ -107,12 +107,12 @@ public Boolean isFormAuthorized(String userEppn, String authUserEppn, Long id) { public List getAllForms(){ List list = new ArrayList<>(); - formRepository.findAll().forEach(e -> list.add(e)); + formRepository.findFormByDeletedIsNullOrDeletedIsFalse().forEach(e -> list.add(e)); return list; } public List getAuthorizedToShareForms() { - return formRepository.findDistinctByAuthorizedShareTypesIsNotNull(); + return formRepository.findDistinctByAuthorizedShareTypesIsNotNullAndDeletedIsNullOrDeletedIsFalse(); } @Transactional @@ -134,6 +134,7 @@ public void updateForm(Long id, Form updateForm, List managers, String[] form.setPublicUsage(updateForm.getPublicUsage()); form.setAction(updateForm.getAction()); form.getAuthorizedShareTypes().clear(); + form.setActiveVersion(updateForm.getActiveVersion()); List shareTypes = new ArrayList<>(); if(types != null) { for (String type : types) { @@ -175,21 +176,25 @@ public void updateFormModel(Long id, MultipartFile multipartModel) throws EsupSi @Transactional public void deleteForm(Long formId) { Form form = formRepository.findById(formId).get(); - List userShares = userShareService.getUserSharesByForm(form); - for(UserShare userShare : userShares) { - userShareService.delete(userShare); - } - dataService.nullifyForm(form); - List fieldToDelete = form.getFields().stream().map(Field::getId).collect(Collectors.toList()); - form.getFields().clear(); - for(Long fieldId : fieldToDelete) { - List fieldProperties = fieldPropertieService.getFieldPropertie(fieldId); - for(FieldPropertie fieldPropertie : fieldProperties) { - fieldPropertieService.delete(fieldPropertie.getId()); + if(form.getDeleted() != null && form.getDeleted()) { + List userShares = userShareService.getUserSharesByForm(form); + for (UserShare userShare : userShares) { + userShareService.delete(userShare); } - fieldService.deleteField(fieldId); + dataService.nullifyForm(form); + List fieldToDelete = form.getFields().stream().map(Field::getId).collect(Collectors.toList()); + form.getFields().clear(); + for (Long fieldId : fieldToDelete) { + List fieldProperties = fieldPropertieService.getFieldPropertie(fieldId); + for (FieldPropertie fieldPropertie : fieldProperties) { + fieldPropertieService.delete(fieldPropertie.getId()); + } + fieldService.deleteField(fieldId); + } + formRepository.delete(form); + } else { + form.setDeleted(true); } - formRepository.delete(form); } private Map getPageNumberByAnnotDict(PDDocumentCatalog docCatalog) throws IOException { @@ -208,7 +213,7 @@ private Map getPageNumberByAnnotDict(PDDocumentCatalog d @Transactional public Form createForm(Document document, String name, String title, Workflow workflow, String prefillType, List roleNames, Boolean publicUsage, String... fieldNames) throws IOException, EsupSignatureException { - List testForms = formRepository.findFormByNameAndActiveVersion(name, true); + List testForms = formRepository.findFormByNameAndActiveVersionAndDeletedIsNullOrDeletedIsFalse(name, true); Form form = new Form(); form.setName(name); form.setTitle(title); @@ -386,17 +391,17 @@ private void computeActions(Field field, String actionsString) { } public List getFormByNameAndActiveVersion(String name, boolean activeVersion) { - return formRepository.findFormByNameAndActiveVersion(name, activeVersion); + return formRepository.findFormByNameAndActiveVersionAndDeletedIsNullOrDeletedIsFalse(name, activeVersion); } public List getFormByName(String name) { - return formRepository.findFormByName(name); + return formRepository.findFormByNameAndDeletedIsNullOrDeletedIsFalse(name); } public List getFormByManagersContains(String eppn) { User user = userService.getUserByEppn(eppn); - return formRepository.findFormByManagersContains(user.getEmail()); + return formRepository.findFormByManagersContainsAndDeletedIsNullOrDeletedIsFalse(user.getEmail()); } @Transactional @@ -425,4 +430,8 @@ public Map getModel(Long id) throws SQLException, IOException { } return null; } + + public List getByRoles(String role) { + return formRepository.findByRolesInAndDeletedIsNullOrDeletedIsFalse(Collections.singletonList(role)); + } } diff --git a/src/main/java/org/esupportail/esupsignature/service/SignBookService.java b/src/main/java/org/esupportail/esupsignature/service/SignBookService.java index 85fb7ee01..91af91d3f 100644 --- a/src/main/java/org/esupportail/esupsignature/service/SignBookService.java +++ b/src/main/java/org/esupportail/esupsignature/service/SignBookService.java @@ -8,10 +8,9 @@ import org.esupportail.esupsignature.service.interfaces.workflow.DefaultWorkflow; import org.esupportail.esupsignature.service.security.otp.OtpService; import org.esupportail.esupsignature.service.utils.WebUtilsService; -import org.esupportail.esupsignature.service.utils.mail.MailService; +import org.esupportail.esupsignature.service.mail.MailService; import org.esupportail.esupsignature.service.utils.sign.SignService; import org.esupportail.esupsignature.web.ws.json.JsonExternalUserInfo; -import org.esupportail.esupsignature.web.ws.json.JsonMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -382,9 +381,9 @@ public void pendingSignBook(SignBook signBook, Data data, String userEppn, Strin } } for (Recipient recipient : signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getRecipients()) { - if(!signRequest.getCreateBy().getEppn().equals(userEppn)) { - eventService.publishEvent(new JsonMessage("info", "Vous avez une nouvelle demande", null), "user", eventService.getClientIdByEppn(recipient.getUser().getEppn())); - } +// if(!signRequest.getCreateBy().getEppn().equals(userEppn)) { +// eventService.publishEvent(new JsonMessage("info", "Vous avez une nouvelle demande", null), "user", eventService.getClientIdByEppn(recipient.getUser().getEppn())); +// } if(recipient.getUser().getUserType().equals(UserType.external)) { try { otpService.generateOtpForSignRequest(signRequest.getId(), recipient.getUser()); diff --git a/src/main/java/org/esupportail/esupsignature/service/SignRequestService.java b/src/main/java/org/esupportail/esupsignature/service/SignRequestService.java index 7218f0edd..910180b52 100644 --- a/src/main/java/org/esupportail/esupsignature/service/SignRequestService.java +++ b/src/main/java/org/esupportail/esupsignature/service/SignRequestService.java @@ -1,6 +1,5 @@ package org.esupportail.esupsignature.service; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import eu.europa.esig.dss.AbstractSignatureParameters; import eu.europa.esig.dss.asic.cades.ASiCWithCAdESSignatureParameters; @@ -30,7 +29,7 @@ import org.esupportail.esupsignature.service.interfaces.prefill.PreFillService; import org.esupportail.esupsignature.service.security.otp.OtpService; import org.esupportail.esupsignature.service.utils.file.FileService; -import org.esupportail.esupsignature.service.utils.mail.MailService; +import org.esupportail.esupsignature.service.mail.MailService; import org.esupportail.esupsignature.service.utils.metric.CustomMetricsService; import org.esupportail.esupsignature.service.utils.pdf.PdfService; import org.esupportail.esupsignature.service.utils.sign.SignService; @@ -51,8 +50,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; @@ -154,6 +151,9 @@ public class SignRequestService { @Resource private SignRequestParamsService signRequestParamsService; + @Resource + private CertificatService certificatService; + @Resource private ReportService reportService; @@ -369,7 +369,7 @@ public void pendingSignRequest(SignRequest signRequest, String authUserEppn) { } @Transactional - public boolean initSign(Long signRequestId, String sseId, String signRequestParamsJsonString, String comment, String formData, Boolean visual, String password, Long userShareId, String userEppn, String authUserEppn) { + public boolean initSign(Long signRequestId, String signRequestParamsJsonString, String comment, String formData, Boolean visual, String password, String certType, Long userShareId, String userEppn, String authUserEppn) throws EsupSignatureMailException, IOException, InterruptedException, EsupSignatureException { SignRequest signRequest = getSignRequestsFullById(signRequestId, userEppn, authUserEppn); Map formDataMap = null; List toRemoveKeys = new ArrayList<>(); @@ -394,48 +394,34 @@ public boolean initSign(Long signRequestId, String sseId, String signRequestPara toRemoveKeys.add(entry.getKey()); } } + for (String toRemoveKey : toRemoveKeys) { + formDataMap.remove(toRemoveKey); + } + } else { +// formDataMap.clear(); } } catch (IOException e) { logger.error("form datas error", e); } } - for (String toRemoveKey : toRemoveKeys) { - formDataMap.remove(toRemoveKey); + List signRequestParamses; + if (signRequestParamsJsonString == null) { + signRequestParamses = signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignRequestParams(); + } else { + signRequestParamses = signRequestParamsService.getSignRequestParamsFromJson(signRequestParamsJsonString); } - try { - List signRequestParamses; - if (signRequestParamsJsonString == null) { - signRequestParamses = signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignRequestParams(); - } else { - signRequestParamses = signRequestParamsService.getSignRequestParamsFromJson(signRequestParamsJsonString); - } - if (signRequest.getCurrentSignType().equals(SignType.nexuSign)) { - signRequestParamsService.copySignRequestParams(signRequest, signRequestParamses); - eventService.publishEvent(new JsonMessage("initNexu", "Démarrage de l'application NexU", null), "sign", sseId); - return true; - } - eventService.publishEvent(new JsonMessage("step", "Démarrage de la signature", null), "sign", sseId); - User user = userService.getByEppn(userEppn); - User authUser = userService.getByEppn(authUserEppn); - sign(signRequest, password, visual, signRequestParamses, formDataMap, sseId, user, authUser, userShareId, comment); - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - public void afterCommit() { - eventService.publishEvent(new JsonMessage("end", "Signature terminée", null), "sign", sseId); - } - }); - return true; - } catch (EsupSignatureKeystoreException e) { - logger.error(e.getMessage()); - return true; - } catch (Exception e) { - logger.error(e.getMessage()); - return false; + if (signRequest.getCurrentSignType().equals(SignType.nexuSign)) { + signRequestParamsService.copySignRequestParams(signRequest, signRequestParamses); + throw new EsupSignatureException("initNexu"); } + User user = userService.getByEppn(userEppn); + User authUser = userService.getByEppn(authUserEppn); + sign(signRequest, password, certType, visual, signRequestParamses, formDataMap, user, authUser, userShareId, comment); + return true; } @Transactional - public void initMassSign(String userEppn, String authUserEppn, String ids, HttpSession httpSession, String sseId, String password) throws JsonProcessingException, InterruptedException { - eventService.publishEvent(new JsonMessage("", "Démarrage de la signature en masse", null), "massSign", sseId); + public void initMassSign(String userEppn, String authUserEppn, String ids, HttpSession httpSession, String password, String certType) throws IOException, InterruptedException, EsupSignatureMailException, EsupSignatureException { List idsString = objectMapper.readValue(ids, List.class); List idsLong = new ArrayList<>(); idsString.forEach(s -> idsLong.add(Long.parseLong(s))); @@ -447,7 +433,6 @@ public void initMassSign(String userEppn, String authUserEppn, String ids, HttpS } for (Long id : idsLong) { SignRequest signRequest = getById(id); - eventService.publishEvent(new JsonMessage("nextSign", "Document : " + signRequest.getTitle(), null), "massSign", sseId); String error = ""; if (!signRequest.getStatus().equals(SignRequestStatus.pending)) { reportService.addSignRequestToReport(report.getId(), signRequest, ReportStatus.badStatus); @@ -460,7 +445,7 @@ public void initMassSign(String userEppn, String authUserEppn, String ids, HttpS reportService.addSignRequestToReport(report.getId(), signRequest, ReportStatus.noSignField); error = messageSource.getMessage("report.reportstatus." + ReportStatus.noSignField, null, Locale.FRENCH); } - else if (signRequest.getStatus().equals(SignRequestStatus.pending) && initSign(id, sseId, null, null, null, true, password, userShareId, userEppn, authUserEppn)) { + else if (signRequest.getStatus().equals(SignRequestStatus.pending) && initSign(id,null, null, null, true, password, certType, userShareId, userEppn, authUserEppn)) { reportService.addSignRequestToReport(report.getId(), signRequest, ReportStatus.signed); error = null; } @@ -468,15 +453,12 @@ else if (signRequest.getStatus().equals(SignRequestStatus.pending) && initSign(i reportService.addSignRequestToReport(report.getId(), signRequest, ReportStatus.error); } if(error != null) { - eventService.publishEvent(new JsonMessage("nextError", error, null), "massSign", sseId); } else { - eventService.publishEvent(new JsonMessage("nextSuccess", "Succès", null), "massSign", sseId); } } - eventService.publishEvent(new JsonMessage("end", "Signatures terminées", null), "massSign", sseId); } - public void sign(SignRequest signRequest, String password, boolean visual, List signRequestParamses, Map formDataMap, String sseId, User user, User authUser, Long userShareId, String comment) throws EsupSignatureException, IOException, InterruptedException, EsupSignatureMailException { + public void sign(SignRequest signRequest, String password, String certType, boolean visual, List signRequestParamses, Map formDataMap, User user, User authUser, Long userShareId, String comment) throws EsupSignatureException, IOException, InterruptedException, EsupSignatureMailException { User signerUser = user; if(userShareId != null) { UserShare userShare = userShareService.getById(userShareId); @@ -501,8 +483,8 @@ public void sign(SignRequest signRequest, String password, boolean visual, List< } } } - if(formDataMap != null && formDataMap.size() > 0 && toSignDocuments.get(0).getContentType().equals("application/pdf")) { - eventService.publishEvent(new JsonMessage("step", "Remplissage du document", null), "sign", sseId); + byte[] bytes = toSignDocuments.get(0).getInputStream().readAllBytes(); + if(formDataMap != null && formDataMap.size() > 0 && toSignDocuments.get(0).getContentType().equals("application/pdf") && validationService.validate(new ByteArrayInputStream(bytes), null).getSimpleReport().getSignatureIdList().size() == 0) { filledInputStream = pdfService.fill(toSignDocuments.get(0).getInputStream(), formDataMap); } else { filledInputStream = toSignDocuments.get(0).getInputStream(); @@ -514,7 +496,6 @@ public void sign(SignRequest signRequest, String password, boolean visual, List< List lastSignLogs = new ArrayList<>(); if (toSignDocuments.size() == 1 && toSignDocuments.get(0).getContentType().equals("application/pdf") && visual) { - eventService.publishEvent(new JsonMessage("step", "Apposition de la signature", null), "sign", sseId); for(SignRequestParams signRequestParams : signRequestParamses) { signedInputStream = pdfService.stampImage(signedInputStream, signRequest, signRequestParams, signerUser); lastSignLogs.add(updateStatus(signRequest, signRequest.getStatus(), "Apposition de la signature", "SUCCESS", signRequestParams.getSignPageNumber(), signRequestParams.getxPos(), signRequestParams.getyPos(), signRequest.getParentSignBook().getLiveWorkflow().getCurrentStepNumber(), user.getEppn(), authUser.getEppn())); @@ -529,9 +510,9 @@ public void sign(SignRequest signRequest, String password, boolean visual, List< if (toSignDocuments.size() == 1 && toSignDocuments.get(0).getContentType().equals("application/pdf")) { signRequestParamsService.copySignRequestParams(signRequest, signRequestParamses); toSignDocuments.get(0).setTransientInputStream(filledInputStream); + visual = true; } - certSign(signRequest, signerUser, password, visual, sseId, "sign"); - eventService.publishEvent(new JsonMessage("step", "Paramétrage de la prochaine étape", null), "sign", sseId); + certSign(signRequest, signerUser, password, certType, visual); applyEndOfSignRules(signRequest, user.getEppn(), authUser.getEppn(), SignType.certSign, comment); } customMetricsService.incValue("esup-signature.signrequests", "signed"); @@ -595,24 +576,26 @@ public void sign(SignRequest signRequest, String password, boolean visual, List< // // } - public void certSign(SignRequest signRequest, User user, String password, boolean visual, String sseId, String channel) throws EsupSignatureException, InterruptedException { + public void certSign(SignRequest signRequest, User user, String password, String certType, boolean visual) throws EsupSignatureException, InterruptedException { SignatureForm signatureForm; List toSignDocuments = new ArrayList<>(); for(Document document : getToSignDocuments(signRequest.getId())) { toSignDocuments.add(document); } - eventService.publishEvent(new JsonMessage("step", "Initialisation de la procédure", null), channel, sseId); Pkcs12SignatureToken pkcs12SignatureToken = null; try { - eventService.publishEvent(new JsonMessage("step", "Déverouillage du keystore", null), channel, sseId); - pkcs12SignatureToken = userKeystoreService.getPkcs12Token(user.getKeystore().getInputStream(), password); + if(user.getKeystore() != null && certType.equals("profil")) { + pkcs12SignatureToken = userKeystoreService.getPkcs12Token(user.getKeystore().getInputStream(), password); + } else { + Certificat certificat = certificatService.getCertificatByUser(user.getEppn()).get(0); + pkcs12SignatureToken = userKeystoreService.getPkcs12Token(certificat.getKeystore().getInputStream(), certificatService.decryptPassword(certificat.getPassword())); + + } CertificateToken certificateToken = userKeystoreService.getCertificateToken(pkcs12SignatureToken); CertificateToken[] certificateTokenChain = userKeystoreService.getCertificateTokenChain(pkcs12SignatureToken); - eventService.publishEvent(new JsonMessage("step", "Formatage des documents", null), channel, sseId); AbstractSignatureForm signatureDocumentForm = signService.getSignatureDocumentForm(toSignDocuments, signRequest, visual); signatureForm = signatureDocumentForm.getSignatureForm(); signatureDocumentForm.setEncryptionAlgorithm(EncryptionAlgorithm.RSA); - eventService.publishEvent(new JsonMessage("step", "Préparation de la signature", null), channel, sseId); signatureDocumentForm.setBase64Certificate(Base64.encodeBase64String(certificateToken.getEncoded())); List base64CertificateChain = new ArrayList<>(); for (CertificateToken token : certificateTokenChain) { @@ -633,12 +616,6 @@ public void certSign(SignRequest signRequest, User user, String password, boolea } else { parameters = signService.fillVisibleParameters((SignatureDocumentForm) signatureDocumentForm, signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignRequestParams().get(0), new ByteArrayInputStream(((SignatureDocumentForm) signatureDocumentForm).getDocumentToSign()), new Color(214, 0, 128), user); } - - if(signatureForm.equals(SignatureForm.PAdES)) { - eventService.publishEvent(new JsonMessage("step", "Signature du document", null), channel, sseId); - } else { - eventService.publishEvent(new JsonMessage("step", "Signature des documents", null), channel, sseId); - } parameters.setSigningCertificate(certificateToken); parameters.setCertificateChain(certificateTokenChain); parameters.setSignatureLevel(signatureDocumentForm.getSignatureLevel()); @@ -650,15 +627,11 @@ public void certSign(SignRequest signRequest, User user, String password, boolea dssDocument = signService.certSignDocument((SignatureDocumentForm) signatureDocumentForm, parameters, pkcs12SignatureToken); } pkcs12SignatureToken.close(); -// InMemoryDocument signedPdfDocument = new InMemoryDocument(DSSUtils.toByteArray(dssDocument), dssDocument.getName(), dssDocument.getMimeType()); - eventService.publishEvent(new JsonMessage("step", "Enregistrement du/des documents(s)", null), channel, sseId); documentService.addSignedFile(signRequest, dssDocument.openStream(), fileService.getNameOnly(signRequest.getTitle()) + "." + fileService.getExtension(dssDocument.getName()), Files.probeContentType(Path.of(dssDocument.getName()))); } catch (EsupSignatureKeystoreException e) { - eventService.publishEvent(new JsonMessage("sign_system_error", "Mauvais mot de passe", null), channel, sseId); if(pkcs12SignatureToken != null) pkcs12SignatureToken.close(); throw new EsupSignatureKeystoreException(e.getMessage(), e); } catch (Exception e) { - eventService.publishEvent(new JsonMessage("sign_system_error", e.getMessage(), null), channel, sseId); if(pkcs12SignatureToken != null) pkcs12SignatureToken.close(); throw new EsupSignatureException(e.getMessage(), e); } @@ -932,7 +905,7 @@ public boolean checkUserViewRights(SignRequest signRequest, String userEppn, Str List signRequests = signRequestRepository.findByIdAndRecipient(signRequest.getId(), userEppn); Data data = dataService.getBySignBook(signRequest.getParentSignBook()); User authUser = userService.getUserByEppn(authUserEppn); - if((data != null && data.getForm().getManagers().contains(authUser.getEmail())) ||signRequest.getCreateBy().getEppn().equals(userEppn) || signRequest.getParentSignBook().getViewers().contains(userService.getUserByEppn(authUserEppn)) || signRequests.size() > 0) { + if((data != null && (data.getForm() != null && data.getForm().getManagers().contains(authUser.getEmail()))) ||signRequest.getCreateBy().getEppn().equals(userEppn) || signRequest.getParentSignBook().getViewers().contains(userService.getUserByEppn(authUserEppn)) || signRequests.size() > 0) { return true; } } @@ -956,13 +929,17 @@ public long generateUniqueId() { public void delete(Long signRequestId, String userEppn) { //TODO critères de suppression ou en conf (if deleteDefinitive) SignRequest signRequest = getById(signRequestId); - signRequest.getOriginalDocuments().clear(); - if(signRequest.getStatus().equals(SignRequestStatus.exported) || signRequest.getStatus().equals(SignRequestStatus.archived)) { - signRequest.getSignedDocuments().clear(); + if(signRequest.getStatus().equals(SignRequestStatus.deleted)) { + deleteDefinitive(signRequestId); + } else { + signRequest.getOriginalDocuments().clear(); + if (signRequest.getStatus().equals(SignRequestStatus.exported) || signRequest.getStatus().equals(SignRequestStatus.archived)) { + signRequest.getSignedDocuments().clear(); + } + signRequest.setStatus(SignRequestStatus.deleted); + logService.create(signRequest, SignRequestStatus.deleted, "DELETE", "", "SUCCESS", null, null, null, null, userEppn, userEppn); + otpService.deleteOtpBySignRequestId(signRequestId); } - signRequest.setStatus(SignRequestStatus.deleted); - logService.create(signRequest, SignRequestStatus.deleted, "DELETE", "", "SUCCESS", null, null, null, null, userEppn, userEppn); - otpService.deleteOtpBySignRequestId(signRequestId); } @Transactional @@ -1270,7 +1247,10 @@ public List getSignImagesForSignRequest(SignRequest signRequestRef, Stri } } if (user.getSignImages().size() > 0 && user.getSignImages().get(0) != null && user.getSignImages().get(0).getSize() > 0) { - if (checkUserSignRights(signRequest, userEppn, authUserEppn) && user.getKeystore() == null && signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType().equals(SignType.certSign)) { + if (checkUserSignRights(signRequest, userEppn, authUserEppn) + && user.getKeystore() == null + && certificatService.getCertificatByUser(userEppn).size() == 0 + && signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType().equals(SignType.certSign)) { signRequestRef.setSignable(false); throw new EsupSignatureUserException("Pour signer ce document merci d’ajouter un certificat à votre profil Mes paramètres"); } @@ -1278,7 +1258,7 @@ public List getSignImagesForSignRequest(SignRequest signRequestRef, Stri signImages.add(fileService.getBase64Image(signImage)); } } else { - if (signRequest.getSignable() && signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType() != null && (signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType().equals(SignType.pdfImageStamp) || signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType().equals(SignType.certSign))) { + if (signRequest.getSignable() && signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType() != null && signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignType().equals(SignType.pdfImageStamp)) { signRequestRef.setSignable(false); throw new EsupSignatureUserException("Pour signer ce document merci d'ajouter une image de votre signature dans Mes paramètres"); @@ -1304,7 +1284,7 @@ public AbstractMap.SimpleEntry, List> checkUserResponse(SignReq return new AbstractMap.SimpleEntry<>(usersHasRefused, usersHasSigned); } - public Long getNbByCreateAndStatus(String userEppn) { + public Long getNbPendingSignRequests(String userEppn) { return signRequestRepository.countByCreateByEppnAndStatus(userEppn, SignRequestStatus.pending); } @@ -1418,7 +1398,9 @@ public void replayNotif(Long id) throws EsupSignatureMailException { SignRequest signRequest = this.getById(id); List recipientEmails = new ArrayList<>(); signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getRecipients().stream().filter(r -> !r.getSigned()).collect(Collectors.toList()).forEach(r -> recipientEmails.add(r.getUser().getEmail())); - mailService.sendSignRequestAlert(recipientEmails, signRequest); + if(recipientEmails.size() > 0) { + mailService.sendSignRequestAlert(recipientEmails, signRequest); + } } public SignRequest getSignRequestByComment(Comment comment) { @@ -1438,6 +1420,16 @@ public static Predicate distinctByKey(Function keyExtr return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; } + @Transactional + public SignRequestParams getToUseSignRequestParams(long id) { + SignRequest signRequest = getById(id); + int index = signRequest.getParentSignBook().getSignRequests().indexOf(signRequest); + if(signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignRequestParams().size() > index) { + return signRequest.getParentSignBook().getLiveWorkflow().getCurrentStep().getSignRequestParams().get(index); + } + return null; + } + @Transactional public File getToValidateFile(long id) throws IOException { SignRequest signRequest = getById(id); diff --git a/src/main/java/org/esupportail/esupsignature/service/UserKeystoreService.java b/src/main/java/org/esupportail/esupsignature/service/UserKeystoreService.java index 96e3df418..372df21f4 100644 --- a/src/main/java/org/esupportail/esupsignature/service/UserKeystoreService.java +++ b/src/main/java/org/esupportail/esupsignature/service/UserKeystoreService.java @@ -10,6 +10,7 @@ import org.esupportail.esupsignature.exception.EsupSignatureKeystoreException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,7 +28,7 @@ public class UserKeystoreService { private static final Logger logger = LoggerFactory.getLogger(UserKeystoreService.class); - @Resource + @Autowired private CertificateVerifier certificateVerifier; @Resource @@ -43,7 +44,7 @@ public Pkcs12SignatureToken getPkcs12Token(InputStream keyStoreFile, String pass } else { logger.error("open keystore fail :" + e.getMessage()); } - throw new EsupSignatureKeystoreException("open keystore fail", e); + throw new EsupSignatureKeystoreException("Mot de passe incorrect", e); } } @@ -66,7 +67,11 @@ public CertificateToken[] getCertificateTokenChain(Pkcs12SignatureToken token) { @Transactional public String checkKeystore(String authUserEppn, String password) throws EsupSignatureKeystoreException { User authUser = userService.getByEppn(authUserEppn); - InputStream keyStoreFile = authUser.getKeystore().getInputStream(); + return checkKeystore(authUser.getKeystore().getInputStream(), password); + } + + @Transactional + public String checkKeystore(InputStream keyStoreFile, String password) throws EsupSignatureKeystoreException { String certInfo = ""; Pkcs12SignatureToken pkcs12SignatureToken = getPkcs12Token(keyStoreFile, password); CertificateToken certificateToken = getCertificateToken(pkcs12SignatureToken); diff --git a/src/main/java/org/esupportail/esupsignature/service/UserService.java b/src/main/java/org/esupportail/esupsignature/service/UserService.java index 93b1b4d48..d259fe940 100644 --- a/src/main/java/org/esupportail/esupsignature/service/UserService.java +++ b/src/main/java/org/esupportail/esupsignature/service/UserService.java @@ -506,7 +506,18 @@ public List getJsonExternalUserInfos(List emails, return externalUsersInfos; } + @Transactional + public void updateRoles(String userEppn, List roles) { + User user = getUserByEppn(userEppn); + user.getRoles().clear(); + user.getRoles().addAll(roles); + } + public List getAllRoles() { return userRepository.getAllRoles(); } + + public List getByManagersRoles(String role) { + return userRepository.findByManagersRolesIn(Collections.singletonList(role)); + } } diff --git a/src/main/java/org/esupportail/esupsignature/service/ValidationService.java b/src/main/java/org/esupportail/esupsignature/service/ValidationService.java index e6303ac66..a6854827b 100644 --- a/src/main/java/org/esupportail/esupsignature/service/ValidationService.java +++ b/src/main/java/org/esupportail/esupsignature/service/ValidationService.java @@ -1,5 +1,6 @@ package org.esupportail.esupsignature.service; +import eu.europa.esig.dss.enumerations.TokenExtractionStrategy; import eu.europa.esig.dss.model.DSSDocument; import eu.europa.esig.dss.model.DSSException; import eu.europa.esig.dss.validation.CertificateVerifier; @@ -9,10 +10,13 @@ import org.esupportail.esupsignature.dss.DssUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -25,12 +29,21 @@ public class ValidationService { private static final Logger logger = LoggerFactory.getLogger(ValidationService.class); - @Resource + @Autowired private CertificateVerifier certificateVerifier; + @Resource + private SignRequestService signRequestService; + @Resource private org.springframework.core.io.Resource defaultPolicy; + @Transactional + public Reports validate(long signRequestId) throws IOException { + byte[] bytes = signRequestService.getToSignDocuments(signRequestId).get(0).getInputStream().readAllBytes(); + return validate(new ByteArrayInputStream(bytes), null); + } + public Reports validate(InputStream docInputStream, InputStream signInputStream) { try { List detachedContents = new ArrayList<>(); @@ -38,20 +51,18 @@ public Reports validate(InputStream docInputStream, InputStream signInputStream) if(signInputStream != null && signInputStream.available() > 0) { detachedContents.add(DssUtils.toDSSDocument(docInputStream)); documentValidator = SignedDocumentValidator.fromDocument(DssUtils.toDSSDocument(signInputStream)); + documentValidator.setDetachedContents(detachedContents); } else { documentValidator = SignedDocumentValidator.fromDocument(DssUtils.toDSSDocument(docInputStream)); } logger.info("validate with : " + documentValidator.getClass()); documentValidator.setCertificateVerifier(certificateVerifier); + documentValidator.setTokenExtractionStrategy(TokenExtractionStrategy.NONE); documentValidator.setLocale(Locale.FRENCH); - documentValidator.setValidationLevel(ValidationLevel.BASIC_SIGNATURES); - documentValidator.setDetachedContents(detachedContents); + documentValidator.setValidationLevel(ValidationLevel.LONG_TERM_DATA); Reports reports = null; try (InputStream is = defaultPolicy.getInputStream()) { - reports = documentValidator.validateDocument(); - for(String id : reports.getSimpleReport().getSignatureIdList()) { - reports.getSimpleReport().getErrors(id).remove("Unable to build a certificate chain until a trusted list!"); - } + reports = documentValidator.validateDocument(is); } catch (IOException e) { logger.error("Unable to parse policy : " + e.getMessage(), e); } diff --git a/src/main/java/org/esupportail/esupsignature/service/WorkflowService.java b/src/main/java/org/esupportail/esupsignature/service/WorkflowService.java index 0c92e8203..f72434fd7 100644 --- a/src/main/java/org/esupportail/esupsignature/service/WorkflowService.java +++ b/src/main/java/org/esupportail/esupsignature/service/WorkflowService.java @@ -425,7 +425,6 @@ public Workflow computeWorkflow(Long workflowId, List recipientEmails, S } } - @Transactional public List getFavoriteRecipientEmail(int stepNumber, List recipientEmails) { List users = new ArrayList<>(); if (recipientEmails != null && recipientEmails.size() > 0) { @@ -454,7 +453,7 @@ public void replaceStepSystemUsers(String userEppn, Long workflowStepId) { } } - public boolean delete(Workflow workflow) { + public void delete(Workflow workflow) throws EsupSignatureException { List signBooks = signBookService.getSignBooksByWorkflow(workflow); if(signBooks.stream().allMatch(signBook -> signBook.getStatus() == SignRequestStatus.draft || signBook.getStatus() == SignRequestStatus.deleted)) { List liveWorkflows = liveWorkflowService.getByWorkflow(workflow); @@ -463,9 +462,9 @@ public boolean delete(Workflow workflow) { liveWorkflow.getLiveWorkflowSteps().forEach(lws -> lws.setWorkflowStep(null)); } workflowRepository.delete(workflow); - return true; + } else { + throw new EsupSignatureException("Le circuit ne peut pas être supprimé car il est en court d'utilisation"); } - return false; } public List getWorkflowsByDisplayWorkflowType(DisplayWorkflowType displayWorkflowType) { @@ -556,5 +555,19 @@ public void deleteTarget(Long id, Long targetId) { workflow.getTargets().remove(target); targetService.delete(target); } + + public List getWorkflowsByRoles(String role) { + return workflowRepository.findByRolesIn(Collections.singletonList(role)); + } + + public Set getManagerWorkflows(String userEppn) { + User manager = userService.getByEppn(userEppn); + Set workflowsManaged = new HashSet<>(); + for (String role : manager.getManagersRoles()) { + workflowsManaged.addAll(this.getWorkflowsByRoles(role)); + } + workflowsManaged.addAll(this.getWorkflowsByUser(manager.getEppn(), manager.getEppn())); + return workflowsManaged; + } } diff --git a/src/main/java/org/esupportail/esupsignature/service/dss/DSSService.java b/src/main/java/org/esupportail/esupsignature/service/dss/DSSService.java index d9a71dd7f..e53cd1c03 100644 --- a/src/main/java/org/esupportail/esupsignature/service/dss/DSSService.java +++ b/src/main/java/org/esupportail/esupsignature/service/dss/DSSService.java @@ -21,7 +21,6 @@ public class DSSService { @Qualifier("european-trusted-list-certificate-source") private TrustedListsCertificateSource trustedListsCertificateSource; - @Resource @Qualifier("european-lotl-source") private LOTLSource lotlSource; diff --git a/src/main/java/org/esupportail/esupsignature/service/event/EventService.java b/src/main/java/org/esupportail/esupsignature/service/event/EventService.java index 0c3491b98..fdd85623e 100644 --- a/src/main/java/org/esupportail/esupsignature/service/event/EventService.java +++ b/src/main/java/org/esupportail/esupsignature/service/event/EventService.java @@ -30,7 +30,7 @@ public String getClientIdByEppn(String eppn) { return sseClientIds.get(eppn.split("@")[0]); } - public void publishEvent(JsonMessage jsonMessage, String channel, String id) { + private void publishEvent(JsonMessage jsonMessage, String channel, String id) { ObjectMapper mapper = new ObjectMapper(); String jsonMessageString= ""; try { diff --git a/src/main/java/org/esupportail/esupsignature/service/interfaces/fs/smb/SmbAccessImpl.java b/src/main/java/org/esupportail/esupsignature/service/interfaces/fs/smb/SmbAccessImpl.java index bf24238f3..90fad4aa4 100644 --- a/src/main/java/org/esupportail/esupsignature/service/interfaces/fs/smb/SmbAccessImpl.java +++ b/src/main/java/org/esupportail/esupsignature/service/interfaces/fs/smb/SmbAccessImpl.java @@ -140,10 +140,10 @@ public SmbFile cd(String path) { } @NotNull - private SmbFile getSmbFileFromPath(String path) throws URISyntaxException, MalformedURLException { + private SmbFile getSmbFileFromPath(String path) throws URISyntaxException, MalformedURLException, UnsupportedEncodingException { SmbFile smbFile; int pos = path.lastIndexOf('/') + 1; - String path2 = path.substring(0, pos) + URLEncoder.encode(path.substring(pos)); + String path2 = path.substring(0, pos) + URLEncoder.encode(path.substring(pos), "UTF-8"); URI uri = new URI(path2); // URI uri = new URI(path.replace(" ", "%20")); diff --git a/src/main/java/org/esupportail/esupsignature/service/ldap/LdapPersonService.java b/src/main/java/org/esupportail/esupsignature/service/ldap/LdapPersonService.java index fd7bcc292..7d8c62db5 100644 --- a/src/main/java/org/esupportail/esupsignature/service/ldap/LdapPersonService.java +++ b/src/main/java/org/esupportail/esupsignature/service/ldap/LdapPersonService.java @@ -41,7 +41,7 @@ public PersonLdapRepository getPersonLdapRepository() { } public List getPersonLdap(String authName) { - String formattedFilter = MessageFormat.format(ldapProperties.getUserIdSearchFilter(), new String[] { authName }); + String formattedFilter = MessageFormat.format(ldapProperties.getUserIdSearchFilter(), (Object[]) new String[] { authName }); return ldapTemplate.search(ldapProperties.getSearchBase(), formattedFilter, new PersonLdapAttributesMapper()); } diff --git a/src/main/java/org/esupportail/esupsignature/service/utils/mail/MailService.java b/src/main/java/org/esupportail/esupsignature/service/mail/MailService.java similarity index 99% rename from src/main/java/org/esupportail/esupsignature/service/utils/mail/MailService.java rename to src/main/java/org/esupportail/esupsignature/service/mail/MailService.java index 37475ec20..69c837e4d 100644 --- a/src/main/java/org/esupportail/esupsignature/service/utils/mail/MailService.java +++ b/src/main/java/org/esupportail/esupsignature/service/mail/MailService.java @@ -1,4 +1,4 @@ -package org.esupportail.esupsignature.service.utils.mail; +package org.esupportail.esupsignature.service.mail; import org.esupportail.esupsignature.config.GlobalProperties; import org.esupportail.esupsignature.config.mail.MailConfig; diff --git a/src/main/java/org/esupportail/esupsignature/service/security/PreAuthorizeService.java b/src/main/java/org/esupportail/esupsignature/service/security/PreAuthorizeService.java index 1a32d0e80..0d24c4354 100644 --- a/src/main/java/org/esupportail/esupsignature/service/security/PreAuthorizeService.java +++ b/src/main/java/org/esupportail/esupsignature/service/security/PreAuthorizeService.java @@ -1,11 +1,13 @@ package org.esupportail.esupsignature.service.security; +import org.apache.commons.collections.CollectionUtils; import org.esupportail.esupsignature.entity.*; import org.esupportail.esupsignature.service.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.util.Collections; @Service @Transactional @@ -87,4 +89,20 @@ public boolean workflowOwner(Long id, String userEppn) { return userEppn.equals(workflow.getCreateBy().getEppn()) || workflow.getCreateBy().equals(userService.getSystemUser()); } + public boolean workflowManager(Long id, String userEppn) { + Workflow workflow = workflowService.getById(id); + User manager = userService.getByEppn(userEppn); + return workflow.getCreateBy().getEppn().equals(manager.getEppn()) || CollectionUtils.containsAny(manager.getManagersRoles(), workflow.getRoles()); + } + + public boolean formManager(Long id, String userEppn) { + Form form = formService.getById(id); + User manager = userService.getByEppn(userEppn); + return CollectionUtils.containsAny(manager.getManagersRoles(), form.getRoles()); + } + + public boolean roleManager(String role, String userEppn) { + User manager = userService.getByEppn(userEppn); + return manager.getManagersRoles().contains(role); + } } diff --git a/src/main/java/org/esupportail/esupsignature/service/security/SessionService.java b/src/main/java/org/esupportail/esupsignature/service/security/SessionService.java new file mode 100644 index 000000000..f8eef480c --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/service/security/SessionService.java @@ -0,0 +1,23 @@ +package org.esupportail.esupsignature.service.security; + +import org.springframework.session.Session; +import org.springframework.session.SessionRepository; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +@Service +public class SessionService { + + @Resource + private SessionRepository sessionRepository; + + public Session getSessionById(String id) { + return sessionRepository.findById(id); + } + + public void deleteSessionById(String id) { + sessionRepository.deleteById(id); + } + +} diff --git a/src/main/java/org/esupportail/esupsignature/service/security/cas/CasAuthenticationSuccessHandler.java b/src/main/java/org/esupportail/esupsignature/service/security/cas/CasAuthenticationSuccessHandler.java index ff044dad2..8810b3e46 100644 --- a/src/main/java/org/esupportail/esupsignature/service/security/cas/CasAuthenticationSuccessHandler.java +++ b/src/main/java/org/esupportail/esupsignature/service/security/cas/CasAuthenticationSuccessHandler.java @@ -28,7 +28,7 @@ public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpS httpServletRequest.getSession().setAttribute("securityServiceName", "CasSecurityServiceImpl"); DefaultSavedRequest defaultSavedRequest = (DefaultSavedRequest) httpServletRequest.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST"); String queryString = defaultSavedRequest.getQueryString(); - if(queryString.split("=")[0].equals("redirect")) { + if(queryString != null && queryString.split("=")[0].equals("redirect")) { this.redirectStrategy.sendRedirect(httpServletRequest, httpServletResponse, queryString.split("=")[1]); } else { this.redirectStrategy.sendRedirect(httpServletRequest, httpServletResponse, "/"); diff --git a/src/main/java/org/esupportail/esupsignature/service/security/otp/OtpService.java b/src/main/java/org/esupportail/esupsignature/service/security/otp/OtpService.java index 0e8575074..d503ac951 100644 --- a/src/main/java/org/esupportail/esupsignature/service/security/otp/OtpService.java +++ b/src/main/java/org/esupportail/esupsignature/service/security/otp/OtpService.java @@ -10,7 +10,7 @@ import org.esupportail.esupsignature.entity.User; import org.esupportail.esupsignature.exception.EsupSignatureMailException; import org.esupportail.esupsignature.service.SignRequestService; -import org.esupportail.esupsignature.service.utils.mail.MailService; +import org.esupportail.esupsignature.service.mail.MailService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; diff --git a/src/main/java/org/esupportail/esupsignature/service/utils/pdf/PdfService.java b/src/main/java/org/esupportail/esupsignature/service/utils/pdf/PdfService.java index a36f6dc85..ad2d57d3e 100644 --- a/src/main/java/org/esupportail/esupsignature/service/utils/pdf/PdfService.java +++ b/src/main/java/org/esupportail/esupsignature/service/utils/pdf/PdfService.java @@ -93,25 +93,25 @@ public InputStream stampImage(InputStream inputStream, SignRequest signRequest, Date newDate = new Date(); DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.FRENCH); InputStream signImage; - if ( signType.equals(SignType.visa) || signType.equals(SignType.hiddenVisa) ) { - File fileSignImage = fileService.getEmptyImage(); - signImage = fileService.addTextToImage(new FileInputStream(fileSignImage), signRequestParams, signType); - File fileWithWatermark = fileService.getTempFile("sign_with_mark.png"); - fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, new Color(137, 137, 137)); - signImage = new FileInputStream(fileWithWatermark); - } else if (signRequestParams.getAddExtra()) { - signImage = fileService.addTextToImage(user.getSignImages().get(signRequestParams.getSignImageNumber()).getInputStream(), signRequestParams, signType); - if(signRequestParams.getAddWatermark()) { + if(signRequestParams.getSignImageNumber() < 0) { + signImage = fileService.getFaImageByIndex(signRequestParams.getSignImageNumber()); + } else { + if (signType.equals(SignType.visa) || signType.equals(SignType.hiddenVisa)) { + File fileSignImage = fileService.getEmptyImage(); + signImage = fileService.addTextToImage(new FileInputStream(fileSignImage), signRequestParams, signType); File fileWithWatermark = fileService.getTempFile("sign_with_mark.png"); - fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, new Color(141, 198, 64)); + fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, new Color(137, 137, 137)); signImage = new FileInputStream(fileWithWatermark); - } - } else { - if(signRequestParams.getSignImageNumber() < 0) { - signImage = fileService.getFaImageByIndex(signRequestParams.getSignImageNumber()); + } else if (signRequestParams.getAddExtra()) { + signImage = fileService.addTextToImage(user.getSignImages().get(signRequestParams.getSignImageNumber()).getInputStream(), signRequestParams, signType); + if (signRequestParams.getAddWatermark()) { + File fileWithWatermark = fileService.getTempFile("sign_with_mark.png"); + fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, new Color(141, 198, 64)); + signImage = new FileInputStream(fileWithWatermark); + } } else { signImage = user.getSignImages().get(signRequestParams.getSignImageNumber()).getInputStream(); - if(signRequestParams.getAddWatermark()) { + if (signRequestParams.getAddWatermark()) { File fileWithWatermark = fileService.getTempFile("sign_with_mark.png"); fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, new Color(141, 198, 64)); signImage = new FileInputStream(fileWithWatermark); @@ -358,7 +358,7 @@ public InputStream writeMetadatas(InputStream inputStream, String fileName, Sign public InputStream convertGS(InputStream inputStream, String UUID) throws IOException, EsupSignatureException { File file = fileService.inputStreamToTempFile(inputStream, "temp.pdf"); - if (!isPdfAComplient(file) && pdfConfig.getPdfProperties().isConvertToPdfA()) { + if (!isPdfAComplient(new FileInputStream(file)) && pdfConfig.getPdfProperties().isConvertToPdfA()) { File targetFile = fileService.getTempFile("afterconvert_tmp.pdf"); String defFile = PdfService.class.getResource("/PDFA_def.ps").getFile(); String cmd = pdfConfig.getPdfProperties().getPathToGS() + " -dPDFA=" + pdfConfig.getPdfProperties().getPdfALevel() + " -dBATCH -dNOPAUSE -dSubsetFonts=true -dPreserveAnnots=true -dShowAnnots=true -dPrinted=false -dNOSAFER -sColorConversionStrategy=UseDeviceIndependentColor -sDEVICE=pdfwrite -dPDFACompatibilityPolicy=1 -dCompatibilityLevel=1.7 -sDocumentUUID=" + UUID + " -d -sOutputFile='" + targetFile.getAbsolutePath() + "' '" + defFile + "' '" + file.getAbsolutePath() + "'"; @@ -406,21 +406,21 @@ public InputStream convertGS(InputStream inputStream, String UUID) throws IOExce } } - public boolean isPdfAComplient(File pdfFile) throws EsupSignatureException { + public boolean isPdfAComplient(InputStream pdfFile) throws EsupSignatureException { if ("success".equals(checkPDFA(pdfFile, false).get(0))) { return true; } return false; } - public List checkPDFA(InputStream inputStream, boolean fillResults) throws IOException, EsupSignatureException { - File file = fileService.inputStreamToTempFile(inputStream, "tmp.pdf"); - List checkResult = checkPDFA(file, fillResults); - file.delete(); - return checkResult; - } +// public List checkPDFA(InputStream inputStream, boolean fillResults) throws IOException, EsupSignatureException { +// File file = fileService.inputStreamToTempFile(inputStream, "tmp.pdf"); +// List checkResult = checkPDFA(inputStream, fillResults); +// file.delete(); +// return checkResult; +// } - public List checkPDFA(File pdfFile, boolean fillResults) throws EsupSignatureException { + public List checkPDFA(InputStream pdfFile, boolean fillResults) throws EsupSignatureException { List result = new ArrayList<>(); VeraGreenfieldFoundryProvider.initialise(); try { diff --git a/src/main/java/org/esupportail/esupsignature/service/utils/sign/SignService.java b/src/main/java/org/esupportail/esupsignature/service/utils/sign/SignService.java index f5c2dc1b0..f78ffb0fc 100644 --- a/src/main/java/org/esupportail/esupsignature/service/utils/sign/SignService.java +++ b/src/main/java/org/esupportail/esupsignature/service/utils/sign/SignService.java @@ -199,45 +199,46 @@ public PAdESSignatureParameters fillVisibleParameters(SignatureDocumentForm form PAdESSignatureParameters pAdESSignatureParameters = new PAdESSignatureParameters(); SignatureImageParameters imageParameters = new SignatureImageParameters(); InMemoryDocument fileDocumentImage; - InputStream signImage; - signImage = fileService.addTextToImage(user.getSignImages().get(signRequestParams.getSignImageNumber()).getInputStream(), signRequestParams, SignType.nexuSign); - if(signRequestParams.getAddWatermark()) { - File fileWithWatermark = fileService.getTempFile("sign_with_mark.png"); - fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, color); - signImage = new FileInputStream(fileWithWatermark); - } - BufferedImage bufferedSignImage = ImageIO.read(signImage); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ImageIO.write(bufferedSignImage, "png", os); - fileDocumentImage = new InMemoryDocument(new ByteArrayInputStream(os.toByteArray()), "sign.png"); - fileDocumentImage.setMimeType(MimeType.PNG); - imageParameters.setImage(fileDocumentImage); - SignatureFieldParameters signatureFieldParameters = imageParameters.getFieldParameters(); - signatureFieldParameters.setPage(signRequestParams.getSignPageNumber()); - imageParameters.setRotation(VisualSignatureRotation.AUTOMATIC); - PdfParameters pdfParameters = pdfService.getPdfParameters(toSignFile); - if(signRequestParams.getAddExtra()) { - signRequestParams.setSignWidth(signRequestParams.getSignWidth() + 200); - } - int widthAdjusted = Math.round((float) (bufferedSignImage.getWidth() / 3 * 0.75)); - int heightAdjusted = Math.round((float) (bufferedSignImage.getHeight() / 3 * 0.75)); + if(user.getSignImages().size() > signRequestParams.getSignImageNumber()) { + InputStream signImage = fileService.addTextToImage(user.getSignImages().get(signRequestParams.getSignImageNumber()).getInputStream(), signRequestParams, SignType.nexuSign); + if(signRequestParams.getAddWatermark()) { + File fileWithWatermark = fileService.getTempFile("sign_with_mark.png"); + fileService.addImageWatermark(PdfService.class.getResourceAsStream("/static/images/watermark.png"), signImage, fileWithWatermark, color); + signImage = new FileInputStream(fileWithWatermark); + } + BufferedImage bufferedSignImage = ImageIO.read(signImage); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ImageIO.write(bufferedSignImage, "png", os); + fileDocumentImage = new InMemoryDocument(new ByteArrayInputStream(os.toByteArray()), "sign.png"); + fileDocumentImage.setMimeType(MimeType.PNG); + imageParameters.setImage(fileDocumentImage); + SignatureFieldParameters signatureFieldParameters = imageParameters.getFieldParameters(); + signatureFieldParameters.setPage(signRequestParams.getSignPageNumber()); + imageParameters.setRotation(VisualSignatureRotation.AUTOMATIC); + PdfParameters pdfParameters = pdfService.getPdfParameters(toSignFile); + if(signRequestParams.getAddExtra()) { + signRequestParams.setSignWidth(signRequestParams.getSignWidth() + 200); + } + int widthAdjusted = Math.round((float) (bufferedSignImage.getWidth() / 3 * 0.75)); + int heightAdjusted = Math.round((float) (bufferedSignImage.getHeight() / 3 * 0.75)); - if(pdfParameters.getRotation() == 0) { - signatureFieldParameters.setWidth(widthAdjusted); - signatureFieldParameters.setHeight(heightAdjusted); - signatureFieldParameters.setOriginX(signRequestParams.getxPos()); - } else { - signatureFieldParameters.setWidth(heightAdjusted); - signatureFieldParameters.setHeight(widthAdjusted); - signatureFieldParameters.setOriginX(signRequestParams.getxPos() - 50); + if(pdfParameters.getRotation() == 0) { + signatureFieldParameters.setWidth(widthAdjusted); + signatureFieldParameters.setHeight(heightAdjusted); + signatureFieldParameters.setOriginX(signRequestParams.getxPos()); + } else { + signatureFieldParameters.setWidth(heightAdjusted); + signatureFieldParameters.setHeight(widthAdjusted); + signatureFieldParameters.setOriginX(signRequestParams.getxPos() - 50); + } + int yPos = Math.round(signRequestParams.getyPos() - ((heightAdjusted - signRequestParams.getSignHeight())) / 0.75f); + signatureFieldParameters.setOriginY(yPos); + imageParameters.setFieldParameters(signatureFieldParameters); + imageParameters.setDpi(300); + imageParameters.setAlignmentHorizontal(VisualSignatureAlignmentHorizontal.LEFT); + imageParameters.setAlignmentVertical(VisualSignatureAlignmentVertical.TOP); + pAdESSignatureParameters.setImageParameters(imageParameters); } - int yPos = Math.round(signRequestParams.getyPos() - ((heightAdjusted - signRequestParams.getSignHeight())) / 0.75f); - signatureFieldParameters.setOriginY(yPos); - imageParameters.setFieldParameters(signatureFieldParameters); - imageParameters.setDpi(300); - imageParameters.setAlignmentHorizontal(VisualSignatureAlignmentHorizontal.LEFT); - imageParameters.setAlignmentVertical(VisualSignatureAlignmentVertical.TOP); - pAdESSignatureParameters.setImageParameters(imageParameters); //signature size 32767 is max for PDF/A-2B pAdESSignatureParameters.setContentSize(32767); pAdESSignatureParameters.setSignaturePackaging(form.getSignaturePackaging()); @@ -336,7 +337,7 @@ public AbstractSignatureForm getSignatureDocumentForm(List documents, } else { inputStream = toSignFile.getInputStream(); } - if(signRequest.getSignedDocuments().size() == 0) { + if(signRequest.getSignedDocuments().size() == 0 && !pdfService.isPdfAComplient(toSignFile.getInputStream())) { inputStream = pdfService.convertGS(pdfService.writeMetadatas(inputStream, toSignFile.getFileName(), signRequest, new ArrayList<>()), signRequest.getToken()); } } else { @@ -579,7 +580,7 @@ public Document nexuSign(SignRequest signRequest, String userEppn, AbstractSigna public boolean checkSignTypeDocType(SignType signType, MultipartFile multipartFile) { boolean check = true; if(!multipartFile.getContentType().toLowerCase().contains("pdf") && !multipartFile.getContentType().toLowerCase().contains("image")) { - if(signType.equals(SignType.pdfImageStamp)) { + if(signType.equals(SignType.pdfImageStamp) || signType.equals(SignType.visa)) { check = false; } } diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/GlobalAttributsControllerAdvice.java b/src/main/java/org/esupportail/esupsignature/web/controller/GlobalAttributsControllerAdvice.java index 07c3f7524..4fa8245b8 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/GlobalAttributsControllerAdvice.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/GlobalAttributsControllerAdvice.java @@ -1,11 +1,16 @@ package org.esupportail.esupsignature.web.controller; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.beanutils.BeanUtils; import org.esupportail.esupsignature.config.GlobalProperties; +import org.esupportail.esupsignature.dss.service.OJService; import org.esupportail.esupsignature.entity.User; import org.esupportail.esupsignature.entity.enums.ShareType; import org.esupportail.esupsignature.entity.enums.SignType; import org.esupportail.esupsignature.service.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.info.BuildProperties; import org.springframework.core.env.Environment; @@ -14,6 +19,7 @@ import org.springframework.web.bind.annotation.ModelAttribute; import javax.annotation.Resource; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Collections; @@ -22,6 +28,8 @@ @ControllerAdvice(basePackages = {"org.esupportail.esupsignature.web.controller"}) public class GlobalAttributsControllerAdvice { + private static final Logger logger = LoggerFactory.getLogger(GlobalAttributsControllerAdvice.class); + @Resource private GlobalProperties globalProperties; @@ -43,6 +51,9 @@ public class GlobalAttributsControllerAdvice { @Resource private ReportService reportService; + @Resource + private OJService ojService; + @Autowired private Environment environment; @@ -63,7 +74,7 @@ public GlobalAttributsControllerAdvice(@Autowired(required = false) BuildPropert } @ModelAttribute - public void globalAttributes(@ModelAttribute("userEppn") String userEppn, @ModelAttribute("authUserEppn") String authUserEppn, Model model) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + public void globalAttributes(@ModelAttribute("userEppn") String userEppn, @ModelAttribute("authUserEppn") String authUserEppn, Model model) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, JsonProcessingException { this.myGlobalProperties = (GlobalProperties) BeanUtils.cloneBean(globalProperties); User user = userService.getUserByEppn(userEppn); model.addAttribute("user", user); @@ -80,6 +91,8 @@ public void globalAttributes(@ModelAttribute("userEppn") String userEppn, @Model model.addAttribute("infiniteScrolling", globalProperties.getInfiniteScrolling()); model.addAttribute("validationToolsEnabled", validationService != null); model.addAttribute("globalProperties", this.myGlobalProperties); + ObjectMapper objectMapper = new ObjectMapper(); + model.addAttribute("globalPropertiesJson", objectMapper.writer().writeValueAsString(this.myGlobalProperties)); model.addAttribute("reportNumber", reportService.countByUser(authUserEppn)); model.addAttribute("hoursBeforeRefreshNotif", this.myGlobalProperties.getHoursBeforeRefreshNotif()); if(environment.getActiveProfiles().length > 0 && environment.getActiveProfiles()[0].equals("dev")) { @@ -87,6 +100,8 @@ public void globalAttributes(@ModelAttribute("userEppn") String userEppn, @Model } if (buildProperties != null) { model.addAttribute("version", buildProperties.getVersion()); + } else { + model.addAttribute("version", "dev"); } List signTypes = Arrays.asList(SignType.values()); if(userKeystoreService == null) { @@ -94,8 +109,13 @@ public void globalAttributes(@ModelAttribute("userEppn") String userEppn, @Model signTypes.remove(SignType.nexuSign); } model.addAttribute("nbDatas", dataService.getNbCreateByAndStatus(userEppn)); - model.addAttribute("nbSignRequests", signRequestService.getNbByCreateAndStatus(userEppn)); + model.addAttribute("nbSignRequests", signRequestService.getNbPendingSignRequests(userEppn)); model.addAttribute("nbToSign", signRequestService.nbToSignSignRequests(userEppn)); + try { + model.addAttribute("dssStatus", ojService.checkOjFreshness()); + } catch (IOException e) { + logger.debug("enable to get dss status"); + } } public void parseRoles(User user) { diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/CertificatController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/CertificatController.java new file mode 100644 index 000000000..1b3bdb0b5 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/CertificatController.java @@ -0,0 +1,68 @@ +package org.esupportail.esupsignature.web.controller.admin; + +import org.esupportail.esupsignature.exception.EsupSignatureKeystoreException; +import org.esupportail.esupsignature.service.CertificatService; +import org.esupportail.esupsignature.service.UserService; +import org.esupportail.esupsignature.web.ws.json.JsonMessage; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.List; + +@RequestMapping("/admin/certificats") +@Controller +public class CertificatController { + + @ModelAttribute("adminMenu") + String getCurrentMenu() { + return "active"; + } + + @ModelAttribute("activeMenu") + public String getActiveMenu() { + return "certificats"; + } + + @Resource + private CertificatService certificatService; + + @Resource + private UserService userService; + + @GetMapping + public String list(Model model) { + model.addAttribute("certificats", certificatService.getAllCertificats()); + model.addAttribute("roles", userService.getAllRoles()); + return "admin/certificats/list"; + } + + @PostMapping + public String addCertificat( + @RequestParam MultipartFile keystore, + @RequestParam List roleNames, + @RequestParam String password, + RedirectAttributes redirectAttributes) { + try { + certificatService.addCertificat(keystore, roleNames, password); + redirectAttributes.addFlashAttribute("message", new JsonMessage("success", "Certificat ajouté")); + } catch (IOException | EsupSignatureKeystoreException e) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", "Erreur lors de l'ajout du keystore :
    " + e.getMessage())); + } + + return "redirect:/admin/certificats"; + } + + @DeleteMapping + public String deleteCertificat(@RequestParam Long id, + RedirectAttributes redirectAttributes) throws IOException { + certificatService.delete(id); + redirectAttributes.addFlashAttribute("message", new JsonMessage("success", "Certificat supprimé")); + return "redirect:/admin/certificats"; + } + +} diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/CurrentSessionsController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/CurrentSessionsController.java index 057e0b988..f99331818 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/admin/CurrentSessionsController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/CurrentSessionsController.java @@ -18,16 +18,15 @@ package org.esupportail.esupsignature.web.controller.admin; import org.apache.commons.io.FileUtils; +import org.esupportail.esupsignature.service.security.SessionService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.session.Session; -import org.springframework.session.SessionRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.annotation.Resource; import java.util.ArrayList; @@ -54,7 +53,7 @@ public String getActiveMenu() { private SessionRegistry sessionRegistry; @Resource - private SessionRepository sessionRepository; + private SessionService sessionService; @GetMapping public String getCurrentSessions(Model model) throws NoSuchMethodException { @@ -65,7 +64,7 @@ public String getCurrentSessions(Model model) throws NoSuchMethodException { List sessions = new ArrayList<>(); List sessionInformations = sessionRegistry.getAllSessions(principal, true); for(SessionInformation sessionInformation : sessionInformations) { - Session session = sessionRepository.findById(sessionInformation.getSessionId()); + Session session = sessionService.getSessionById(sessionInformation.getSessionId()); if(session != null) { for(String attr : session.getAttributeNames()) { sessionSize += session.getAttribute(attr).toString().getBytes().length; @@ -85,10 +84,10 @@ public String getCurrentSessions(Model model) throws NoSuchMethodException { } @DeleteMapping - public String deleteSessions(@RequestParam String sessionId, RedirectAttributes redirectAttributes) { - Session session = sessionRepository.findById(sessionId); + public String deleteSessions(@RequestParam String sessionId) { + Session session = sessionService.getSessionById(sessionId); if(session != null) { - sessionRepository.deleteById(session.getId()); + sessionService.deleteSessionById(session.getId()); } return "redirect:/admin/currentsessions"; } diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/DSSController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/DSSController.java index 74fe6004a..a04f375ad 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/admin/DSSController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/DSSController.java @@ -3,11 +3,13 @@ import eu.europa.esig.dss.spi.tsl.LOTLInfo; import eu.europa.esig.dss.spi.tsl.TLInfo; import eu.europa.esig.dss.spi.tsl.TLValidationJobSummary; +import eu.europa.esig.dss.spi.tsl.TrustedListsCertificateSource; import eu.europa.esig.dss.tsl.function.OfficialJournalSchemeInformationURI; import org.esupportail.esupsignature.dss.config.DSSBeanConfig; import org.esupportail.esupsignature.dss.service.KeystoreService; import org.esupportail.esupsignature.exception.EsupSignatureException; import org.esupportail.esupsignature.service.dss.DSSService; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -40,21 +42,19 @@ public String getActiveMenu() { @Resource private KeystoreService keystoreService; + @Resource + @Qualifier("european-trusted-list-certificate-source") + private TrustedListsCertificateSource trustedListsCertificateSource; + @GetMapping public String tlInfoPage(Model model) { - TLValidationJobSummary summary = dssService.getTrustedListsCertificateSource().getSummary(); + TLValidationJobSummary summary = trustedListsCertificateSource.getSummary(); model.addAttribute("summary", summary); - return "admin/dss/tl-summary"; - } - - @GetMapping(value = "/oj") - public String showCertificates(Model model) { model.addAttribute("keystoreCertificates", keystoreService.getCertificatesDTOFromKeyStore(dssService.getTrustedListsCertificateSource().getCertificates())); OfficialJournalSchemeInformationURI ojUriInfo = (OfficialJournalSchemeInformationURI) dssService.getLotlSource().getSigningCertificatesAnnouncementPredicate(); model.addAttribute("currentOjUrl", ojUriInfo.getOfficialJournalURL()); model.addAttribute("actualOjUrl", dssService.getActualOjUrl()); - model.addAttribute("customCertificates", keystoreService.getCertificatesDTOFromKeyStore(dssService.getMyTrustedCertificateSource().getCertificates())); - return "admin/dss/oj-certificates"; + return "admin/dss/tl-summary"; } @GetMapping(value = "/lotl/{id}") diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/FormAdminController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/FormAdminController.java index e83e684e6..f4036f625 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/admin/FormAdminController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/FormAdminController.java @@ -65,9 +65,6 @@ public String getActiveMenu() { @Resource private FieldService fieldService; - @Resource - private TargetService targetService; - @GetMapping() public String list(Model model) { List forms = formService.getAllForms(); @@ -162,7 +159,7 @@ public String updateForm(@ModelAttribute Form updateForm, } @PostMapping("/update-model/{id}") - public String updateFormmodel(@PathVariable("id") Long id, + public String updateFormModel(@PathVariable("id") Long id, @RequestParam(value = "multipartModel", required=false) MultipartFile multipartModel, RedirectAttributes redirectAttributes) { try { if(multipartModel.getSize() > 0) { diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/MessageAdminController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/MessageAdminController.java index bf046ffe7..bcaf72b63 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/admin/MessageAdminController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/MessageAdminController.java @@ -59,7 +59,7 @@ public String messages(Pageable pageable, Model model) { @PostMapping("/add") public String addMessage(@RequestParam String text, @RequestParam String endDate) throws ParseException, InterruptedException { Message message = messageService.createMessage(endDate, text); - eventService.publishEvent(new JsonMessage("custom", message.getText()), "global", null); +// eventService.publishEvent(new JsonMessage("custom", message.getText()), "global", null); return "redirect:/admin/messages"; } diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/RolesManagersController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/RolesManagersController.java new file mode 100644 index 000000000..5464964c9 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/RolesManagersController.java @@ -0,0 +1,60 @@ +package org.esupportail.esupsignature.web.controller.admin; + +import org.esupportail.esupsignature.entity.User; +import org.esupportail.esupsignature.service.UserService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequestMapping("/admin/roles-managers") +@Controller +public class RolesManagersController { + + @ModelAttribute("adminMenu") + String getCurrentMenu() { + return "active"; + } + + @ModelAttribute("activeMenu") + public String getActiveMenu() { + return "rolesManagers"; + } + + @Resource + UserService userService; + + @GetMapping + public String getRoles(Model model) { + Map> roleManagers = new HashMap<>(); + List allRoles = userService.getAllRoles(); + for (String role : allRoles) { + roleManagers.put(role, userService.getByManagersRoles(role)); + } + model.addAttribute("roleManagers", roleManagers); + return "admin/roles-managers"; + } + + @PostMapping("/editRole") + public String editRoles(@RequestParam String role, @RequestParam List rolesManagers) { + for (User user : userService.getByManagersRoles(role)) { + if (!rolesManagers.contains(user.getEmail())) { + user.getRoles().remove(role); + } + } + + for (String mail : rolesManagers) { + User user = userService.getUserByEmail(mail); + if (!user.getManagersRoles().contains(role)) { + user.getManagersRoles().add(role); + } + } + + return "redirect:/admin/roles-managers"; + + } +} diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/admin/WorkflowAdminController.java b/src/main/java/org/esupportail/esupsignature/web/controller/admin/WorkflowAdminController.java index b7d018ce0..969afdf4f 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/admin/WorkflowAdminController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/admin/WorkflowAdminController.java @@ -107,10 +107,14 @@ public String update(@ModelAttribute("authUserEppn") String authUserEppn, } @DeleteMapping(value = "/{id}", produces = "text/html") - public String delete(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id) { + public String delete(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, RedirectAttributes redirectAttributes) { Workflow workflow = workflowService.getById(id); - workflowService.delete(workflow); - return "redirect:/admin/workflows"; + try { + workflowService.delete(workflow); + } catch (EsupSignatureException e) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); + } + return "redirect:/admin/workflows"; } @PostMapping(value = "/add-step/{id}") diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerFormController.java b/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerFormController.java new file mode 100644 index 000000000..b9bb94ede --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerFormController.java @@ -0,0 +1,276 @@ +package org.esupportail.esupsignature.web.controller.manager; + +import org.apache.commons.io.IOUtils; +import org.esupportail.esupsignature.entity.Form; +import org.esupportail.esupsignature.entity.User; +import org.esupportail.esupsignature.entity.enums.DocumentIOType; +import org.esupportail.esupsignature.entity.enums.FieldType; +import org.esupportail.esupsignature.entity.enums.ShareType; +import org.esupportail.esupsignature.exception.EsupSignatureException; +import org.esupportail.esupsignature.service.FieldService; +import org.esupportail.esupsignature.service.FormService; +import org.esupportail.esupsignature.service.UserService; +import org.esupportail.esupsignature.service.WorkflowService; +import org.esupportail.esupsignature.service.export.DataExportService; +import org.esupportail.esupsignature.service.interfaces.prefill.PreFill; +import org.esupportail.esupsignature.service.interfaces.prefill.PreFillService; +import org.esupportail.esupsignature.web.ws.json.JsonMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +@Controller +@RequestMapping("/manager/forms") +public class ManagerFormController { + + private static final Logger logger = LoggerFactory.getLogger(ManagerFormController.class); + + @ModelAttribute("managerMenu") + public String getmanagersMenu() { + return "active"; + } + + @ModelAttribute("activeMenu") + public String getActiveMenu() { + return "forms"; + } + + @Resource + private FormService formService; + + @Resource + private WorkflowService workflowService; + + @Resource + private UserService userService; + + @Resource + private PreFillService preFillService; + + @Resource + private DataExportService dataExportService; + + @Resource + private FieldService fieldService; + + @GetMapping() + public String list(@ModelAttribute("authUserEppn") String authUserEppn, Model model) { + Set forms = new HashSet<>(); + User manager = userService.getByEppn(authUserEppn); + for (String role : manager.getManagersRoles()) { + forms.addAll(formService.getByRoles(role)); + } + model.addAttribute("forms", forms); + model.addAttribute("roles", manager.getManagersRoles()); + model.addAttribute("targetTypes", DocumentIOType.values()); + model.addAttribute("workflowTypes", workflowService.getManagerWorkflows(authUserEppn)); + model.addAttribute("preFillTypes", preFillService.getPreFillValues()); + return "managers/forms/list"; + } + + @GetMapping("{id}") + @PreAuthorize("@preAuthorizeService.formManager(#id, #authUserEppn)") + public String show(@PathVariable("id") Long id, Model model, @ModelAttribute("authUserEppn") String authUserEppn) { + Form form = formService.getById(id); + model.addAttribute("form", form); + model.addAttribute("workflow", form.getWorkflow()); + PreFill preFill = preFillService.getPreFillServiceByName(form.getPreFillType()); + if(preFill != null) { + model.addAttribute("preFillTypes", preFill.getTypes()); + } else { + model.addAttribute("preFillTypes", new HashMap<>()); + } + model.addAttribute("document", form.getDocument()); + return "managers/forms/show"; + } + + @PostMapping() + public String postForm(@RequestParam("name") String name, + @RequestParam("fieldNames[]") String[] fieldNames, + @RequestParam(required = false) Boolean publicUsage, RedirectAttributes redirectAttributes) throws IOException { + try { + Form form = formService.createForm(null, name, null, null, null, null, publicUsage, fieldNames); + return "redirect:/manager/forms/" + form.getId(); + + } catch (EsupSignatureException e) { + logger.error(e.getMessage()); + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); + return "redirect:/manager/forms/"; + } + } + + @PostMapping("generate") + public String generateForm( + @RequestParam("multipartFile") MultipartFile multipartFile, + @RequestParam String name, + @RequestParam String title, + @RequestParam Long workflowId, + @RequestParam String prefillType, + @RequestParam(required = false) List roleNames, + @RequestParam(required = false) Boolean publicUsage, + RedirectAttributes redirectAttributes) throws IOException { + try { + Form form = formService.generateForm(multipartFile, name, title, workflowId, prefillType, roleNames, publicUsage); + return "redirect:/manager/forms/" + form.getId(); + } catch (EsupSignatureException e) { + logger.error(e.getMessage()); + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); + return "redirect:/manager/forms/"; + } + } + + @GetMapping("update/{id}") + @PreAuthorize("@preAuthorizeService.formManager(#id, #authUserEppn)") + public String updateForm(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") long id, Model model) { + Form form = formService.getById(id); + User manager = userService.getUserByEppn(authUserEppn); + model.addAttribute("form", form); + model.addAttribute("fields", form.getFields()); + model.addAttribute("roles", manager.getManagersRoles()); + model.addAttribute("document", form.getDocument()); + model.addAttribute("workflowTypes", workflowService.getManagerWorkflows(authUserEppn)); + List preFillTypes = preFillService.getPreFillValues(); + model.addAttribute("preFillTypes", preFillTypes); + model.addAttribute("shareTypes", ShareType.values()); + model.addAttribute("targetTypes", DocumentIOType.values()); + model.addAttribute("model", form.getDocument()); + return "managers/forms/update"; + } + + @GetMapping("create") + public String createForm(Model model) { + model.addAttribute("form", new Form()); + return "managers/forms/create"; + } + + @PutMapping + @PreAuthorize("@preAuthorizeService.formManager(#updateForm.id, #authUserEppn)") + public String updateForm(@ModelAttribute Form updateForm, + @RequestParam(required = false) List managers, + @RequestParam(value = "types", required = false) String[] types, + @ModelAttribute("authUserEppn") String authUserEppn, + RedirectAttributes redirectAttributes) { + updateForm.setPublicUsage(false); + updateForm.setAction(""); + formService.updateForm(updateForm.getId(), updateForm, managers, types); + redirectAttributes.addFlashAttribute("message", new JsonMessage("success", "Modifications enregistrées")); + return "redirect:/manager/forms/update/" + updateForm.getId(); + } + + @PostMapping("/update-model/{id}") + @PreAuthorize("@preAuthorizeService.formManager(#id, #authUserEppn)") + public String updateFormModel(@PathVariable("id") Long id, + @ModelAttribute("authUserEppn") String authUserEppn, + @RequestParam(value = "multipartModel", required=false) MultipartFile multipartModel, RedirectAttributes redirectAttributes) { + try { + if(multipartModel.getSize() > 0) { + formService.updateFormModel(id, multipartModel); + } + } catch (EsupSignatureException e) { + logger.error(e.getMessage()); + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); + return "redirect:/manager/forms/"; + } + redirectAttributes.addFlashAttribute("message", new JsonMessage("success", "Modifications enregistrées")); + return "redirect:/manager/forms/update/" + id; + } + + @DeleteMapping("{id}") + @PreAuthorize("@preAuthorizeService.formManager(#id, #authUserEppn)") + public String deleteForm(@PathVariable("id") Long id, @ModelAttribute("authUserEppn") String authUserEppn, RedirectAttributes redirectAttributes) { + formService.deleteForm(id); + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Le formulaire à bien été supprimé")); + return "redirect:/manager/forms"; + } + + @GetMapping(value = "/{name}/datas/csv", produces="text/csv") + public ResponseEntity getFormDatasCsv(@PathVariable String name, HttpServletResponse response) { + List forms = formService.getFormByName(name); + if (forms.size() > 0) { + try { + response.setContentType("text/csv; charset=utf-8"); + response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(forms.get(0).getName(), StandardCharsets.UTF_8.toString()) + ".csv"); + InputStream csvInputStream = dataExportService.getCsvDatasFromForms(forms); + IOUtils.copy(csvInputStream, response.getOutputStream()); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + logger.error("get file error", e); + } + } else { + logger.warn("form " + name + " not found"); + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ResponseBody + @PostMapping("/field/{id}/update") + @PreAuthorize("@preAuthorizeService.formManager(#id, #authUserEppn)") + public ResponseEntity updateField(@PathVariable("id") Long id, + @RequestParam(value = "description", required = false) String description, + @RequestParam(value = "fieldType", required = false, defaultValue = "text") FieldType fieldType, + @RequestParam(value = "required", required = false, defaultValue = "false") Boolean required, + @RequestParam(value = "favorisable", required = false, defaultValue = "false") Boolean favorisable, + @RequestParam(value = "readOnly", required = false, defaultValue = "false") Boolean readOnly, + @RequestParam(value = "prefill", required = false, defaultValue = "false") Boolean prefill, + @RequestParam(value = "search", required = false, defaultValue = "false") Boolean search, + @RequestParam(value = "valueServiceName", required = false) String valueServiceName, + @RequestParam(value = "valueType", required = false) String valueType, + @RequestParam(value = "valueReturn", required = false) String valueReturn, + @RequestParam(value = "stepZero", required = false, defaultValue = "false") Boolean stepZero, + @RequestParam(value = "workflowStepsIds", required = false) List workflowStepsIds, + @ModelAttribute("authUserEppn") String authUserEppn) { + + String extValueServiceName = ""; + String extValueType = ""; + String extValueReturn = ""; + String searchServiceName = ""; + String searchType = ""; + String searchReturn = ""; + if(prefill) { + extValueServiceName = valueServiceName; + extValueType = valueType; + extValueReturn = valueReturn; + } + if(search) { + searchServiceName = valueServiceName; + searchType = valueType; + searchReturn = valueReturn; + } + fieldService.updateField(id, description, fieldType, favorisable, required, readOnly, extValueServiceName, extValueType, extValueReturn, searchServiceName, searchType, searchReturn, stepZero, workflowStepsIds); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping(value = "/get-file/{id}") + public void getFile(@ModelAttribute("userEppn") String userEppn, @ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, HttpServletResponse httpServletResponse, RedirectAttributes redirectAttributes) throws IOException { + try { + Map attachmentResponse = formService.getModel(id); + if (attachmentResponse != null) { + httpServletResponse.setContentType(attachmentResponse.get("contentType").toString()); + httpServletResponse.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(attachmentResponse.get("fileName").toString(), StandardCharsets.UTF_8.toString())); + IOUtils.copyLarge((InputStream) attachmentResponse.get("inputStream"), httpServletResponse.getOutputStream()); + } else { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", "Modèle non trouvée ...")); + httpServletResponse.sendRedirect("/user/signsignrequests/" + id); + } + } catch (Exception e) { + logger.error("get file error", e); + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerRolesManagersController.java b/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerRolesManagersController.java new file mode 100644 index 000000000..edf8912c2 --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerRolesManagersController.java @@ -0,0 +1,64 @@ +package org.esupportail.esupsignature.web.controller.manager; + +import org.esupportail.esupsignature.entity.User; +import org.esupportail.esupsignature.service.UserService; +import org.esupportail.esupsignature.service.security.PreAuthorizeService; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequestMapping("/manager/rolesManagers") +@Controller +public class ManagerRolesManagersController { + + @ModelAttribute("managerMenu") + String getCurrentMenu() { + return "active"; + } + + @ModelAttribute("activeMenu") + public String getActiveMenu() { + return "rolesManagers"; + } + + @Resource + UserService userService; + + @GetMapping + public String getRoles(@ModelAttribute("authUserEppn") String authUserEppn, Model model) { + Map> roleManagers = new HashMap<>(); + User manager = userService.getByEppn(authUserEppn); + List allRoles = manager.getManagersRoles(); + for (String role : allRoles) { + roleManagers.put(role, userService.getByManagersRoles(role)); + } + model.addAttribute("roleManagers", roleManagers); + return "managers/managerRoles"; + } + + @PostMapping("/editRole") + @PreAuthorize("@preAuthorizeService.roleManager(#role, #authUserEppn)") + public String editRoles(@RequestParam String role, @RequestParam List rolesManagers, @ModelAttribute("authUserEppn") String authUserEppn) { + for (User user : userService.getByManagersRoles(role)) { + if (!rolesManagers.contains(user.getEmail())) { + user.getManagersRoles().remove(role); + } + } + + for (String mail : rolesManagers) { + User user = userService.getUserByEmail(mail); + if (!user.getManagersRoles().contains(role)) { + user.getManagersRoles().add(role); + } + } + + return "redirect:/manager/rolesManagers"; + + } +} diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerWorkflowController.java b/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerWorkflowController.java new file mode 100644 index 000000000..0c120a5ae --- /dev/null +++ b/src/main/java/org/esupportail/esupsignature/web/controller/manager/ManagerWorkflowController.java @@ -0,0 +1,220 @@ +package org.esupportail.esupsignature.web.controller.manager; + +import org.esupportail.esupsignature.entity.User; +import org.esupportail.esupsignature.entity.Workflow; +import org.esupportail.esupsignature.entity.WorkflowStep; +import org.esupportail.esupsignature.entity.enums.DocumentIOType; +import org.esupportail.esupsignature.entity.enums.ShareType; +import org.esupportail.esupsignature.entity.enums.SignType; +import org.esupportail.esupsignature.exception.EsupSignatureException; +import org.esupportail.esupsignature.service.UserService; +import org.esupportail.esupsignature.service.WorkflowService; +import org.esupportail.esupsignature.service.WorkflowStepService; +import org.esupportail.esupsignature.web.ws.json.JsonMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.Arrays; +import java.util.List; + +@RequestMapping("/manager/workflows") +@Controller + +public class ManagerWorkflowController { + + private static final Logger logger = LoggerFactory.getLogger(ManagerWorkflowController.class); + + @ModelAttribute("managerMenu") + public String getAdminMenu() { + return "active"; + } + + @ModelAttribute("activeMenu") + public String getActiveMenu() { + return "workflows"; + } + + @Resource + private UserService userService; + + @Resource + private WorkflowService workflowService; + + @Resource + private WorkflowStepService workflowStepService; + + @GetMapping + public String list(@ModelAttribute("authUserEppn") String authUserEppn, Model model) { + model.addAttribute("workflows", workflowService.getManagerWorkflows(authUserEppn)); + return "managers/workflows/list"; + } + + @GetMapping(value = "/{id}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String show(@PathVariable("id") Long id, Model model, @ModelAttribute("authUserEppn") String authUserEppn) { + model.addAttribute("fromAdmin", true); + model.addAttribute("signTypes", SignType.values()); + Workflow workflow = workflowService.getById(id); + model.addAttribute("workflow", workflow); + return "managers/workflows/show"; + } + + @PostMapping(produces = "text/html") + public String create(@ModelAttribute("authUserEppn") String authUserEppn, @RequestParam(name = "title", required = false) String title, @RequestParam(name = "description") String description, RedirectAttributes redirectAttributes) { + if(title == null) { + title = description; + } + Workflow workflow; + try { + workflow = workflowService.createWorkflow(title, description, userService.getByEppn(authUserEppn)); + } catch (EsupSignatureException e) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", "Un circuit possède déjà ce préfixe")); + return "redirect:/manager/workflows/"; + } + return "redirect:/manager/workflows/" + workflow.getId(); + } + + @GetMapping(value = "/update/{id}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String updateForm(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, Model model) { + Workflow workflow = workflowService.getById(id); + User manager = userService.getByEppn(authUserEppn); + model.addAttribute("workflow", workflow); + model.addAttribute("roles", manager.getManagersRoles()); + model.addAttribute("sourceTypes", DocumentIOType.values()); + model.addAttribute("targetTypes", DocumentIOType.values()); + model.addAttribute("shareTypes", ShareType.values()); + model.addAttribute("signTypes", Arrays.asList(SignType.values())); + return "managers/workflows/update"; + } + + @PostMapping(value = "/update") + @PreAuthorize("@preAuthorizeService.workflowManager(#workflow.id, #authUserEppn)") + public String update(@ModelAttribute("authUserEppn") String authUserEppn, + @Valid Workflow workflow, + @RequestParam(value = "types", required = false) String[] types, + @RequestParam(required = false) List managers, Model model) { + User authUser = (User) model.getAttribute("authUser"); + workflow.setPublicUsage(false); + Workflow updateWorkflow = workflowService.update(workflow, authUser, types, managers); + return "redirect:/manager/workflows/update/" + updateWorkflow.getId(); + } + + @DeleteMapping(value = "/{id}", produces = "text/html") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String delete(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, RedirectAttributes redirectAttributes) { + Workflow workflow = workflowService.getById(id); + try { + workflowService.delete(workflow); + } catch (EsupSignatureException e) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); + } + return "redirect:/manager/workflows"; + } + + @PostMapping(value = "/add-step/{id}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String addStep(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, + @RequestParam("signType") String signType, + @RequestParam(name="description", required = false) String description, + @RequestParam(value = "recipientsEmails", required = false) String[] recipientsEmails, + @RequestParam(name="changeable", required = false) Boolean changeable, + @RequestParam(name="allSignToComplete", required = false) Boolean allSignToComplete) { + workflowStepService.addStep(id, signType, description, recipientsEmails, changeable, allSignToComplete, authUserEppn, false); + return "redirect:/manager/workflows/" + id; + } + + @PostMapping(value = "/update-step/{id}/{step}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String changeStepSignType(@ModelAttribute("authUserEppn") String authUserEppn, + @PathVariable("id") Long id, + @PathVariable("step") Integer step, + @RequestParam(name="signType") SignType signType, + @RequestParam(name="description") String description, + @RequestParam(name="repeatable", required = false) Boolean repeatable, + @RequestParam(name="multiSign", required = false) Boolean multiSign, + @RequestParam(name="changeable", required = false) Boolean changeable, + @RequestParam(name="allSignToComplete", required = false) Boolean allSignToComplete) { + Workflow workflow = workflowService.getById(id); + workflowStepService.updateStep(workflow.getWorkflowSteps().get(step).getId(), signType, description, changeable, repeatable, multiSign, allSignToComplete); + return "redirect:/manager/workflows/" + id; + } + + @DeleteMapping(value = "/remove-step-recipent/{id}/{workflowStepId}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String removeStepRecipient(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, + @PathVariable("workflowStepId") Long workflowStepId, + @RequestParam(value = "userToRemoveEppn") String userToRemoveEppn, RedirectAttributes redirectAttributes) { + WorkflowStep workflowStep = workflowStepService.removeStepRecipient(workflowStepId, userToRemoveEppn); + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Participant supprimé")); + return "redirect:/manager/workflows/" + id + "#" + workflowStep.getId(); + } + + @PostMapping(value = "/add-step-recipents/{id}/{workflowStepId}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String addStepRecipient(@ModelAttribute("authUserEppn") String authUserEppn, + @PathVariable("id") Long id, + @PathVariable("workflowStepId") Long workflowStepId, + @RequestParam String recipientsEmails, RedirectAttributes redirectAttributes) { + WorkflowStep workflowStep = workflowStepService.addStepRecipients(workflowStepId, recipientsEmails); + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Participant ajouté")); + return "redirect:/manager/workflows/" + id + "#" + workflowStep.getId(); + } + + @DeleteMapping(value = "/remove-step/{id}/{stepNumber}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String addStep(@ModelAttribute("authUserEppn") String authUserEppn, + @PathVariable("id") Long id, + @PathVariable("stepNumber") Integer stepNumber) { + Workflow workflow = workflowService.getById(id); + workflowStepService.removeStep(workflow, stepNumber); + return "redirect:/manager/workflows/" + id; + } + + @GetMapping(value = "/get-files-from-source/{id}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String getFileFromSource(@ModelAttribute("authUserEppn") String authUserEppn, @PathVariable("id") Long id, Model model, RedirectAttributes redirectAttributes) { + User authUser = (User) model.getAttribute("authUser"); + int nbImportedFiles = workflowService.importFilesFromSource(id, authUser, authUser); + if(nbImportedFiles == 0) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", "Aucun fichier à importer")); + } else { + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", nbImportedFiles + " ficher(s) importé(s)")); + } + return "redirect:/manager/workflows/" + id; + } + + @PostMapping(value = "/add-target/{id}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String addTarget(@PathVariable("id") Long id, + @RequestParam("targetType") String targetType, + @RequestParam("documentsTargetUri") String documentsTargetUri, + @ModelAttribute("authUserEppn") String authUserEppn, + RedirectAttributes redirectAttributes) { + if(workflowService.addTarget(id, targetType, documentsTargetUri)) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Destination ajoutée")); + } else { + redirectAttributes.addFlashAttribute("message", new JsonMessage("warn", "Une destination mail existe déjà")); + } + return "redirect:/manager/workflows/update/" + id; + } + + @GetMapping(value = "/delete-target/{id}/{targetId}") + @PreAuthorize("@preAuthorizeService.workflowManager(#id, #authUserEppn)") + public String deleteTarget(@PathVariable("id") Long id, + @PathVariable("targetId") Long targetId, + @ModelAttribute("authUserEppn") String authUserEppn, + RedirectAttributes redirectAttributes) { + workflowService.deleteTarget(id, targetId); + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Destination supprimée")); + return "redirect:/manager/workflows/update/" + id; + } + +} diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/user/SignRequestController.java b/src/main/java/org/esupportail/esupsignature/web/controller/user/SignRequestController.java index b2d724230..fd266ba43 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/user/SignRequestController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/user/SignRequestController.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.esig.dss.validation.reports.Reports; import org.apache.commons.io.IOUtils; import org.esupportail.esupsignature.config.GlobalProperties; import org.esupportail.esupsignature.entity.*; @@ -68,6 +69,12 @@ public String getActiveMenu() { @Resource private UserService userService; + @Resource + private CertificatService certificatService; + + @Resource + private ValidationService validationService; + @Resource private PreAuthorizeService preAuthorizeService; @@ -111,6 +118,8 @@ public String list(@ModelAttribute("userEppn") String userEppn, @ModelAttribute( @RequestParam(value = "workflowFilter", required = false) String workflowFilter, @RequestParam(value = "docTitleFilter", required = false) String docTitleFilter, @SortDefault(value = "createDate", direction = Direction.DESC) @PageableDefault(size = 10) Pageable pageable, Model model) { + if(statusFilter == null) statusFilter = "pending"; + if(statusFilter.equals("all")) statusFilter = ""; Page signRequests = signRequestService.getSignRequestsPageGrouped(userEppn, authUserEppn, statusFilter, recipientsFilter, workflowFilter, docTitleFilter, pageable); model.addAttribute("statusFilter", statusFilter); model.addAttribute("signRequests", signRequests); @@ -169,6 +178,7 @@ public String show(@ModelAttribute("userEppn") String userEppn, @ModelAttribute( model.addAttribute("nextSignRequest", signRequestService.getNextSignRequest(signRequest.getId(), userEppn, authUserEppn)); model.addAttribute("prevSignRequest", signRequestService.getPreviousSignRequest(signRequest.getId(), userEppn, authUserEppn)); model.addAttribute("fields", signRequestService.prefillSignRequestFields(id, userEppn)); + model.addAttribute("toUseSignRequestParams", Collections.singletonList(signRequestService.getToUseSignRequestParams(id))); model.addAttribute("uiParams", userService.getUiParams(authUserEppn)); if(!signRequest.getStatus().equals(SignRequestStatus.draft)) { try { @@ -181,6 +191,9 @@ public String show(@ModelAttribute("userEppn") String userEppn, @ModelAttribute( model.addAttribute("message", new JsonMessage("warn", e.getMessage())); } } + Reports reports = validationService.validate(id); + model.addAttribute("signatureIds", reports.getSimpleReport().getSignatureIdList()); + model.addAttribute("certificats", certificatService.getCertificatByUser(userEppn)); model.addAttribute("signable", signRequest.getSignable()); model.addAttribute("isTempUsers", signRequestService.isTempUsers(id)); if(signRequest.getStatus().equals(SignRequestStatus.draft)) { @@ -189,7 +202,7 @@ public String show(@ModelAttribute("userEppn") String userEppn, @ModelAttribute( model.addAttribute("refuseLogs", logService.getRefuseLogs(signRequest.getId())); model.addAttribute("viewRight", signRequestService.checkUserViewRights(signRequest, userEppn, authUserEppn)); model.addAttribute("frameMode", frameMode); - if(signRequest.getData() != null) { + if(signRequest.getData() != null && signRequest.getData().getForm() != null) { model.addAttribute("action", signRequest.getData().getForm().getAction()); } List logs = logService.getBySignRequest(signRequest.getId()); @@ -242,16 +255,19 @@ public ResponseEntity sign(@ModelAttribute("userEppn") String userEppn, @RequestParam(value = "formData", required = false) String formData, @RequestParam(value = "visual", required = false) Boolean visual, @RequestParam(value = "password", required = false) String password, - HttpSession httpSession, HttpServletRequest request) { - CsrfToken token = new HttpSessionCsrfTokenRepository().loadToken(request); + @RequestParam(value = "certType", required = false) String certType, + HttpSession httpSession) { if (visual == null) visual = true; Object userShareString = httpSession.getAttribute("userShareId"); Long userShareId = null; if(userShareString != null) userShareId = Long.valueOf(userShareString.toString()); - if(signRequestService.initSign(id, token.getToken(), signRequestParamsJsonString, comment, formData, visual, password, userShareId, userEppn, authUserEppn)) { + try { + signRequestService.initSign(id, signRequestParamsJsonString, comment, formData, visual, password, certType, userShareId, userEppn, authUserEppn); return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @PreAuthorize("@preAuthorizeService.signRequestOwner(#id, #authUserEppn)") @@ -622,9 +638,9 @@ public ResponseEntity massSign(@ModelAttribute("userEppn") String userEp @ModelAttribute("authUserEppn") String authUserEppn, @RequestParam String ids, @RequestParam(value = "password", required = false) String password, - HttpSession httpSession, HttpServletRequest request) throws JsonProcessingException, InterruptedException { - CsrfToken csrfToken = new HttpSessionCsrfTokenRepository().loadToken(request); - signRequestService.initMassSign(userEppn, authUserEppn, ids, httpSession, csrfToken.getToken(), password); + @RequestParam(value = "certType", required = false) String certType, + HttpSession httpSession) throws InterruptedException, EsupSignatureMailException, EsupSignatureException, IOException { + signRequestService.initMassSign(userEppn, authUserEppn, ids, httpSession, password, certType); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/user/ValidationController.java b/src/main/java/org/esupportail/esupsignature/web/controller/user/ValidationController.java index 7c8958826..fc1c3ad39 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/user/ValidationController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/user/ValidationController.java @@ -1,8 +1,17 @@ package org.esupportail.esupsignature.web.controller.user; +import eu.europa.esig.dss.diagnostic.DiagnosticData; +import eu.europa.esig.dss.diagnostic.DiagnosticDataFacade; +import eu.europa.esig.dss.diagnostic.RevocationWrapper; +import eu.europa.esig.dss.diagnostic.TimestampWrapper; +import eu.europa.esig.dss.diagnostic.jaxb.XmlDiagnosticData; +import eu.europa.esig.dss.enumerations.RevocationType; +import eu.europa.esig.dss.enumerations.TimestampType; import eu.europa.esig.dss.model.MimeType; +import eu.europa.esig.dss.utils.Utils; import eu.europa.esig.dss.validation.executor.ValidationLevel; import eu.europa.esig.dss.validation.reports.Reports; +import eu.europa.esig.validationreport.jaxb.ValidationReportType; import org.esupportail.esupsignature.dss.service.FOPService; import org.esupportail.esupsignature.dss.service.XSLTService; import org.esupportail.esupsignature.exception.EsupSignatureException; @@ -13,6 +22,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @@ -21,13 +34,12 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.Arrays; @Controller -@SessionAttributes({ "simpleReportXml", "detailedReportXml" }) +@SessionAttributes({ "simpleReportXml", "detailedReportXml", "diagnosticDataXml" }) @RequestMapping("/user/validation") @ConditionalOnBean(ValidationService.class) public class ValidationController { @@ -64,46 +76,44 @@ public String showValidationForm() { @PostMapping public String validate(@RequestParam(name = "multipartSignedDoc") MultipartFile multipartSignedDoc, @RequestParam(name = "multipartSignature", required = false) MultipartFile multipartSignature, Model model) throws IOException { - Reports reports = validationService.validate(multipartSignedDoc.getInputStream(), multipartSignature.getInputStream()); + InputStream docInputStream = multipartSignedDoc.getInputStream(); + InputStream sigInputStream = multipartSignature.getInputStream(); + extracted(docInputStream, sigInputStream, model); + return "user/validation/result"; + } + + private void extracted(InputStream docInputStream, InputStream sigInputStream, Model model) throws IOException { + byte[] docBytes = docInputStream.readAllBytes(); + Reports reports = validationService.validate(new ByteArrayInputStream(docBytes), sigInputStream); if(reports != null) { String xmlSimpleReport = reports.getXmlSimpleReport(); model.addAttribute("simpleReport", xsltService.generateSimpleReport(xmlSimpleReport)); + model.addAttribute("simpleReportXml", reports.getXmlSimpleReport()); String xmlDetailedReport = reports.getXmlDetailedReport(); model.addAttribute("detailedReport", xsltService.generateDetailedReport(xmlDetailedReport)); model.addAttribute("detailedReportXml", reports.getXmlDetailedReport()); - model.addAttribute("diagnosticTree", reports.getXmlDiagnosticData()); + ValidationReportType etsiValidationReportJaxb = reports.getEtsiValidationReportJaxb(); + + if (etsiValidationReportJaxb != null) { + model.addAttribute("etsiValidationReport", reports.getXmlValidationReport()); + } + model.addAttribute("diagnosticDataXml", reports.getXmlDiagnosticData()); } else { model.addAttribute("simpleReport", "

    Impossible de valider ce document

    "); model.addAttribute("detailedReport", "

    Impossible de valider ce document

    "); } - if(multipartSignedDoc.getContentType().contains("pdf")) { - try { - model.addAttribute("pdfaReport", pdfService.checkPDFA(multipartSignedDoc.getInputStream(), true)); - } catch (EsupSignatureException e) { - e.printStackTrace(); - } - } else { + try { + model.addAttribute("pdfaReport", pdfService.checkPDFA(new ByteArrayInputStream(docBytes), true)); + } catch (EsupSignatureException e) { model.addAttribute("pdfaReport", Arrays.asList("danger", "Impossible de valider ce document")); + logger.error(e.getMessage()); } - - return "user/validation/result"; } - + @GetMapping(value = "/document/{id}") public String validateDocument(@PathVariable(name="id") long id, Model model) throws IOException { File file = signRequestService.getToValidateFile(id); - Reports reports = validationService.validate(new FileInputStream(file), null); - String xmlSimpleReport = reports.getXmlSimpleReport(); - model.addAttribute("simpleReport", xsltService.generateSimpleReport(xmlSimpleReport)); - String xmlDetailedReport = reports.getXmlDetailedReport(); - model.addAttribute("detailedReport", xsltService.generateDetailedReport(xmlDetailedReport)); - model.addAttribute("detailedReportXml", reports.getXmlDetailedReport()); - model.addAttribute("diagnosticTree", reports.getXmlDiagnosticData()); - try { - model.addAttribute("pdfaReport", pdfService.checkPDFA(file, true)); - } catch (EsupSignatureException e) { - logger.error("enable to check pdf"); - } + extracted(new FileInputStream(file), null, model); return "user/validation/result"; } @@ -111,10 +121,8 @@ public String validateDocument(@PathVariable(name="id") long id, Model model) th public void downloadSimpleReport(HttpSession session, HttpServletResponse response) { try { String simpleReport = (String) session.getAttribute("simpleReportXml"); - response.setContentType(MimeType.PDF.getMimeTypeString()); response.setHeader("Content-Disposition", "attachment; filename=DSS-Simple-report.pdf"); - fopService.generateSimpleReport(simpleReport, response.getOutputStream()); } catch (Exception e) { logger.error("An error occured while generating pdf for simple report : " + e.getMessage(), e); @@ -125,16 +133,133 @@ public void downloadSimpleReport(HttpSession session, HttpServletResponse respon public void downloadDetailedReport(HttpSession session, HttpServletResponse response) { try { String detailedReport = (String) session.getAttribute("detailedReportXml"); - response.setContentType(MimeType.PDF.getMimeTypeString()); response.setHeader("Content-Disposition", "attachment; filename=DSS-Detailed-report.pdf"); - fopService.generateDetailedReport(detailedReport, response.getOutputStream()); } catch (Exception e) { logger.error("An error occured while generating pdf for detailed report : " + e.getMessage(), e); } } + @RequestMapping(value = "/download-diagnostic-data") + public void downloadDiagnosticData(HttpSession session, HttpServletResponse response) { + String report = (String) session.getAttribute("diagnosticDataXml"); + + response.setContentType(MimeType.XML.getMimeTypeString()); + response.setHeader("Content-Disposition", "attachment; filename=DSS-Diagnotic-data.xml"); + try { + Utils.write(report.getBytes(StandardCharsets.UTF_8), response.getOutputStream()); + } catch (IOException e) { + logger.error("An error occurred while downloading diagnostic data : " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/diag-data.svg") + public @ResponseBody + ResponseEntity downloadSVG(HttpSession session, HttpServletResponse response) { + String report = (String) session.getAttribute("diagnosticDataXml"); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf(MimeType.SVG.getMimeTypeString())); + ResponseEntity svgEntity = new ResponseEntity(xsltService.generateSVG(report), headers, + HttpStatus.OK); + return svgEntity; + } + + @RequestMapping(value = "/download-revocation") + public void downloadRevocationData(@RequestParam(value = "id") String id, @RequestParam(value = "format") String format, HttpSession session, + HttpServletResponse response) throws Exception { + DiagnosticData diagnosticData = getDiagnosticData(session); + RevocationWrapper revocationData = diagnosticData.getRevocationById(id); + if (revocationData == null) { + String message = "Revocation data " + id + " not found"; + logger.warn(message); + throw new Exception(message); + } + String filename = revocationData.getId(); + MimeType mimeType; + byte[] binaries; + + if (RevocationType.CRL.equals(revocationData.getRevocationType())) { + mimeType = MimeType.CRL; + filename += ".crl"; + + if (Utils.areStringsEqualIgnoreCase(format, "pem")) { + String pem = "-----BEGIN CRL-----\n"; + pem += Utils.toBase64(revocationData.getBinaries()); + pem += "\n-----END CRL-----"; + binaries = pem.getBytes(); + } else { + binaries = revocationData.getBinaries(); + } + } else { + mimeType = MimeType.BINARY; + filename += ".ocsp"; + binaries = revocationData.getBinaries(); + } + + addTokenToResponse(response, filename, mimeType, binaries); + } + + protected void addTokenToResponse(HttpServletResponse response, String filename, MimeType mimeType, byte[] binaries) { + response.setContentType(MimeType.TST.getMimeTypeString()); + response.setHeader("Content-Disposition", "attachment; filename=" + filename); + try (InputStream is = new ByteArrayInputStream(binaries); OutputStream os = response.getOutputStream()) { + Utils.copy(is, os); + } catch (IOException e) { + logger.error("An error occurred while downloading a file : " + e.getMessage(), e); + } + } + + @RequestMapping(value = "/download-timestamp") + public void downloadTimestamp(@RequestParam(value = "id") String id, @RequestParam(value = "format") String format, HttpSession session, + HttpServletResponse response) throws Exception { + DiagnosticData diagnosticData = getDiagnosticData(session); + TimestampWrapper timestamp = diagnosticData.getTimestampById(id); + if (timestamp == null) { + String message = "Timestamp " + id + " not found"; + logger.warn(message); + throw new Exception(message); + } + TimestampType type = timestamp.getType(); + + byte[] binaries; + if (Utils.areStringsEqualIgnoreCase(format, "pem")) { + String pem = "-----BEGIN TIMESTAMP-----\n"; + pem += Utils.toBase64(timestamp.getBinaries()); + pem += "\n-----END TIMESTAMP-----"; + binaries = pem.getBytes(); + } else { + binaries = timestamp.getBinaries(); + } + + String filename = type.name() + ".tst"; + addTokenToResponse(response, filename, MimeType.TST, binaries); + } + + public DiagnosticData getDiagnosticData(HttpSession session) { + String diagnosticDataXml = (String) session.getAttribute("diagnosticDataXml"); + try { + XmlDiagnosticData xmlDiagData = DiagnosticDataFacade.newFacade().unmarshall(diagnosticDataXml); + return new DiagnosticData(xmlDiagData); + } catch (Exception e) { + logger.error("An error occurred while generating DiagnosticData from XML : " + e.getMessage(), e); + } + return null; + } + + @GetMapping(value = "/short/{id}") + @ResponseBody + public String shortValidateDocument(@PathVariable(name="id") long id) throws IOException { + File file = signRequestService.getToValidateFile(id); + Reports reports = validationService.validate(new FileInputStream(file), null); + if(reports != null) { + String xmlSimpleReport = reports.getXmlSimpleReport(); + return xsltService.generateShortReport(xmlSimpleReport); + } + return null; + } + @ModelAttribute("validationLevels") public ValidationLevel[] getValidationLevels() { return new ValidationLevel[] { ValidationLevel.BASIC_SIGNATURES, ValidationLevel.LONG_TERM_DATA, ValidationLevel.ARCHIVAL_DATA }; diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/user/WizardController.java b/src/main/java/org/esupportail/esupsignature/web/controller/user/WizardController.java index e17769ca3..6dec28106 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/user/WizardController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/user/WizardController.java @@ -132,7 +132,7 @@ public String saveWorkflow(@ModelAttribute("userEppn") String userEppn, @PathVar try { signBookService.saveWorkflow(id, name, name, user); } catch (EsupSignatureException e) { - eventService.publishEvent(new JsonMessage("error", "Un circuit de signature porte déjà ce nom"), "user", eventService.getClientIdByEppn(userEppn)); +// eventService.publishEvent(new JsonMessage("error", "Un circuit de signature porte déjà ce nom"), "user", eventService.getClientIdByEppn(userEppn)); return "user/wizard/wiz-save"; } return "user/wizard/wizend"; @@ -184,7 +184,7 @@ public String wiz5Workflow(@ModelAttribute("userEppn") String userEppn, @PathVar } else { Workflow workflow = workflowService.getById(id); model.addAttribute("workflow", workflow); - eventService.publishEvent(new JsonMessage("error", "Un circuit de signature porte déjà ce nom"), "user", eventService.getClientIdByEppn(userEppn)); +// eventService.publishEvent(new JsonMessage("error", "Un circuit de signature porte déjà ce nom"), "user", eventService.getClientIdByEppn(userEppn)); return "user/wizard/wiz-save-workflow"; } } @@ -233,10 +233,11 @@ public String delete(@ModelAttribute("userEppn") String userEppn, @PathVariable( if (!workflow.getCreateBy().getEppn().equals(userEppn)) { redirectAttributes.addFlashAttribute("message", new JsonMessage("error", "Non autorisé")); } else { - if(workflowService.delete(workflow)) { + try { + workflowService.delete(workflow); redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Circuit supprimé")); - } else { - redirectAttributes.addFlashAttribute("message", new JsonMessage("error", "Impossible de supprimer ce circuit car il contient des demandes")); + } catch (EsupSignatureException e) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); } } return "redirect:/user/"; diff --git a/src/main/java/org/esupportail/esupsignature/web/controller/user/WorkflowController.java b/src/main/java/org/esupportail/esupsignature/web/controller/user/WorkflowController.java index 79b0f746e..32c164065 100644 --- a/src/main/java/org/esupportail/esupsignature/web/controller/user/WorkflowController.java +++ b/src/main/java/org/esupportail/esupsignature/web/controller/user/WorkflowController.java @@ -3,6 +3,7 @@ import org.esupportail.esupsignature.entity.Workflow; import org.esupportail.esupsignature.entity.WorkflowStep; import org.esupportail.esupsignature.entity.enums.SignType; +import org.esupportail.esupsignature.exception.EsupSignatureException; import org.esupportail.esupsignature.service.WorkflowService; import org.esupportail.esupsignature.service.WorkflowStepService; import org.esupportail.esupsignature.web.ws.json.JsonMessage; @@ -99,15 +100,19 @@ public String addStep(@ModelAttribute("userEppn") String userEppn, @PreAuthorize("@preAuthorizeService.workflowOwner(#id, #userEppn)") public String delete(@ModelAttribute("userEppn") String userEppn, @PathVariable("id") Long id, RedirectAttributes redirectAttributes) { Workflow workflow = workflowService.getById(id); - workflowService.delete(workflow); - redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Le circuit à bien été supprimé")); + try { + workflowService.delete(workflow); + redirectAttributes.addFlashAttribute("message", new JsonMessage("info", "Le circuit à bien été supprimé")); + } catch (EsupSignatureException e) { + redirectAttributes.addFlashAttribute("message", new JsonMessage("error", e.getMessage())); + } return "redirect:/"; } @DeleteMapping(value = "/silent-delete/{id}", produces = "text/html") @PreAuthorize("@preAuthorizeService.workflowOwner(#id, #userEppn)") @ResponseBody - public void silentDelete(@ModelAttribute("userEppn") String userEppn, @PathVariable("id") Long id) { + public void silentDelete(@ModelAttribute("userEppn") String userEppn, @PathVariable("id") Long id) throws EsupSignatureException { Workflow workflow = workflowService.getById(id); workflowService.delete(workflow); } diff --git a/src/main/java/org/esupportail/esupsignature/web/otp/WsOtpSignController.java b/src/main/java/org/esupportail/esupsignature/web/otp/WsOtpSignController.java index 58b4b5892..c58903814 100644 --- a/src/main/java/org/esupportail/esupsignature/web/otp/WsOtpSignController.java +++ b/src/main/java/org/esupportail/esupsignature/web/otp/WsOtpSignController.java @@ -4,9 +4,9 @@ import org.esupportail.esupsignature.exception.EsupSignatureException; import org.esupportail.esupsignature.exception.EsupSignatureUserException; import org.esupportail.esupsignature.service.UserService; +import org.esupportail.esupsignature.service.interfaces.sms.SmsService; import org.esupportail.esupsignature.service.security.otp.Otp; import org.esupportail.esupsignature.service.security.otp.OtpService; -import org.esupportail.esupsignature.service.interfaces.sms.SmsService; import org.esupportail.esupsignature.web.ws.json.JsonMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +14,7 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; @@ -25,6 +26,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.util.stream.Collectors; + import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY; @ConditionalOnProperty(value = "sms.enable-sms", havingValue = "true") @@ -60,6 +63,8 @@ public String signin(@PathVariable String urlId, Model model) { logger.error(e.getMessage(), e); } otp.setSmsSended(true); + } else { + return "otp/expired"; } model.addAttribute("urlid", urlId); return "otp/signin"; @@ -80,6 +85,7 @@ public String auth(@RequestParam String urlId, @RequestParam String password, Mo Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken); SecurityContext securityContext = SecurityContextHolder.getContext(); securityContext.setAuthentication(authentication); + userService.updateRoles(user.getEppn(), authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList())); HttpSession httpSession = req.getSession(true); httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext); model.addAttribute("user", user); diff --git a/src/main/java/org/esupportail/esupsignature/web/ws/WorkflowWsController.java b/src/main/java/org/esupportail/esupsignature/web/ws/WorkflowWsController.java index 8c0e240d2..a2802d024 100644 --- a/src/main/java/org/esupportail/esupsignature/web/ws/WorkflowWsController.java +++ b/src/main/java/org/esupportail/esupsignature/web/ws/WorkflowWsController.java @@ -52,9 +52,10 @@ public List getAll() { @DeleteMapping("/{id}") public ResponseEntity delete(@PathVariable Long id) { Workflow workflow = workflowService.getById(id); - if (workflowService.delete(workflow)) { + try { + workflowService.delete(workflow); return new ResponseEntity<>(HttpStatus.OK); - } else { + } catch (EsupSignatureException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/src/main/java/org/esupportail/esupsignature/web/ws/WsExportController.java b/src/main/java/org/esupportail/esupsignature/web/ws/WsExportController.java index a192eef77..f5962d8d4 100644 --- a/src/main/java/org/esupportail/esupsignature/web/ws/WsExportController.java +++ b/src/main/java/org/esupportail/esupsignature/web/ws/WsExportController.java @@ -35,7 +35,7 @@ public class WsExportController { @GetMapping(value = "/form/{name}/datas/csv", produces="text/csv") public ResponseEntity getFormDatasCsv(@PathVariable String name, HttpServletResponse response) { - List forms = formRepository.findFormByName(name); + List forms = formRepository.findFormByNameAndDeletedIsNullOrDeletedIsFalse(name); if (forms.size() > 0) { try { response.setContentType("text/csv; charset=utf-8"); @@ -56,7 +56,7 @@ public ResponseEntity getFormDatasCsv(@PathVariable String name, HttpServl @ResponseBody @GetMapping(value = "/form/{name}/datas/json", produces = MediaType.APPLICATION_JSON_VALUE) public List> getFormDatasJson(@PathVariable String name) { - List forms = formRepository.findFormByName(name); + List forms = formRepository.findFormByNameAndDeletedIsNullOrDeletedIsFalse(name); if (forms.size() > 0) { try { return dataExportService.getDatasToExport(forms); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index abd5ce496..79cf6b209 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -184,6 +184,7 @@ server: remote-ip-header: X-Forwarded-For sign: + aes-key : "0000000000000000" cades-digest-algorithm: SHA256 cades-signature-level: CAdES_BASELINE_T container-type: ASiC_E diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index d0be9a26c..79bf31ad5 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -159,6 +159,9 @@ label.cert.validate = Validate a certificate label.validation.policy = Validation policy constrains label.validation.default.policy.file = Validate signatures and whether they are : AdES, AdES/QC or QES. All certificates and their related chains are validated against the EU MS TSL. label.validation.custom.policy.file = Custom validation constrains file +label.validation.visualrepresentation = Visual Representation +label.signatures.and.timestamps = Signatures and related timestamps +label.certificate.chains = Certificate chains label.report = Report label.validation.results = Validation results label.simple.report = Simple Report @@ -393,3 +396,37 @@ label.nexu.signature.process = NexU signature process label.nexu.download = Download the open source version of NexU label.nexu.more.info = more info label.nexu.ready = NexU ready. Please plug card reader, insert ID card and click on button below. + +label.country.code.EU = European Union +label.country.code.AT = Austria +label.country.code.BE = Belgium +label.country.code.BG = Bulgaria +label.country.code.HR = Croatia +label.country.code.CY = Cyprus +label.country.code.CZ = Czech Republic +label.country.code.DK = Denmark +label.country.code.EE = Estonia +label.country.code.FI = Finland +label.country.code.FR = France +label.country.code.DE = Germany +label.country.code.EL = Greece +label.country.code.HU = Hungary +label.country.code.IS = Iceland +label.country.code.IE = Ireland +label.country.code.IT = Italy +label.country.code.LV = Latvia +label.country.code.LI = Liechtenstein +label.country.code.LT = Lithuania +label.country.code.LU = Luxembourg +label.country.code.MT = Malta +label.country.code.NL = Netherlands +label.country.code.NO = Norway +label.country.code.PL = Poland +label.country.code.PT = Portugal +label.country.code.RO = Romania +label.country.code.SK = Slovakia +label.country.code.SI = Slovenia +label.country.code.ES = Spain +label.country.code.SE = Sweden +label.country.code.UK = United Kingdom +label.country.code.no_country = Unknown diff --git a/src/main/resources/keystore.p12 b/src/main/resources/keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..73516691490991c8a7a71cff1583f7e989aab027 GIT binary patch literal 15906 zcmV+-KHb4Ef<7Js0Ru3CJ=X>aDuzgg_YDCD0ic3C$OM8t#4v(Az%YV6yaov>hDe6@ z4FLxRpn^TEFoHd)0s#Opf<2-J2`Yw2hW8Bt2LUiC1_~;MNQU%3{TkFFs&3g6MHo<%(us3qdXe+ z$CscZdn)KsfIJ?oeDNu#QIKPjzx9=VrB=G=&Y?=ZMTFs~+MG*|FQ zszUNX1TW!$^NS@0xKaJ{xqTq&O(zUugZD+m*bp|JB&}(qv}KcN<={D0RYtzVGkg}# zgqnbkfUM+s2{sT5aA7rxM3a$`S+916C#t)VXpY;5WfmTwU~3C=V0E7<9N>ztHqhmp zAqOpsY|X#FG_)jv4V(~kC8UbROp_oGw@Kz0Sp?a<#aP)*EsZN%6s4E~(IBN>Sl zdS5FV*iPCK&xn@yp?)$n3NzFP(iN*H34-!qIO7H71HaWZOHB<@pbv&zAR6vn6UC(Fv|AC^ z)31z+{K^uNW7XJndDigLa@q)pk&Z_wn?05`!|K8r(edK{3WBi_t6sYZC2IqAJls`M zRvRY{;*7fQjRz)5UM9hXH%^^wZtbqY#Q5_`Pvnj#i802=-eP z`({`TM7On;FOVz==0;d$%8Ls1F`E>bmf=NRyx#9sbuK;JT3dJ|9Jr3-jhgv@QFA>^ zUX;A-4VQ)AP8=LzCSE@WYz{M%Cb8p0lG0J41g|0beC_mRTOJUizk99oQL=(6La{QYz8YLdEWzSxHp0SX*nq~8{Z-j zGHe8M9nMhD(xfi+ozAgg(wj}|ZQej4&(2&1dD@F1Ey!pag zPksz*9dC{ir?88wrJc^9RT`!^{M75B>}{Reo8TJ&HW zyjZa=BhQ`_+C&{E$4_2>(1MMO18d(4j?A@nVOXdT<`Rq)zh)YSockfYZ(xC1^uNZM zG@cdH$Z%r5Gpn(8N-QK1a?@P9Iy51H1UWFn7^~tF%;D;iHc$0K10WMWu|=vb8m7N@z3L~38<$qxm$%NfC%)htw?94Y8-(Q_3J%%Lv% zoLfEOb$#VGMDAS`X;1jFSa4@IorA}*6KvZ0m1;J_0*dfBhQsC1(vq3UPM=sVE*B8X zR@57R?X>j$NQ%Axi^}TlShVJ%STrM>3E$-Z-bv=j+p?X)zTbzZU^;N>EJ$4ZOyza5w)0<8H_^FoG&`g9lGmCnQd zPP#f0&SHpOv&F7fMLhx|q94n^dp%m&GJCY@c1S8l3n>0yy1O>T}q{E+LL;Y|Vb*qK13q7ynhSN5<|bxd#pSKz;6>*r1{EgCSR^tBu` z1U(CJb4S}v_P1{V5`3@1qwM{}#n%Bgdz520(=&xq5+{~YI3@BPKptrN2+gvyXk@Oz zjeFbM724w5N$0WAV0)~Qkd1e6mDQ|i{;R6j{g(;{xo$ec1=3*2Yse<0U)@E?Q;wbT zhp;qb$9rZ4n`%{~1OVGK)x<9Zn1mKh+ThKej!qAAj8QUJhT|c%><5sl)-uKS#1aqx zh|46zy)Ve4G!ayn_%v>~DtMEPI@xJ|IjLhJkyr(&1qK0v!7LZxHypt!;$d&PJ+6J} za2VPxQ=P=KN>WfDRsj2aJ4 zc6-h~mtL8nYn^|f8Gd?i)@TrD)xC42_MYH!j`ib|CNR`xTl1fW^7%cZ$fSFCqrL zv%*-0=P7~@Gv`$#UVf)d5zm7DSf{Gh4mCuNWdGa+_Z_biKm4PDnnJ0}M)mC4lm-TQ8cjaQx z6%-O}L3>kim_>X4QmVd%6yPu5CY9B}f@P~t!ltZxx95hX{-qqW$4=C@pzLv~uIhxA znx}nCeOIUO2~V@g$2+Qnsd9o|EoAb*vXo7LM--_Usq{kvQ->^Gd8{UV_Hh-!T@~?K z@3NBD=_v!X)?c5i*8a`R5l<3P4XONO@);j4t0EbUx*A2L7HyLE848D3ukr|pewCoZ z$HHK5-3#zGv-)WaBB_Jsn#|;ABO|^=SzLsPjI?NNSq3Bc$S!(Bx{?UYeyN&j;u%1D zE9N2&sPbr8t1*Pz2%UHrsQjA1>T0l93KZjrNZ)CvI(B-Jv$qSEP=cjCBMJeC?*>^9 zuGmVTQCCYUn~_HX-2n#+G{U=LQu%%I@{{o9cE*E}sXScYh^@4%^$orYQ(9g^;!Jan zXszWdzaH>ohibG)oN+g-yTPoXQi15SrUo4SUElus8T3v4SVhSyIQZeG;dzi#pVKY@ z0uo67-_3<_7encIC7)yK#+R85@GQjw>3OqQ(E*4{rTljl4ftg+ujI}!ZO>M+R8%1w zXTSyS$Qr>S~2{y+|cLg zC1;?bA{OCum_kIqBtjmgCbK#31l}rG(1Z|KhrdFJc_S=M^r!S;&cRt@)302R%!bFi zciUS@#Y22Lvs6?Fa+S=za(}ES2xO=9(o>)VnTDomj-GrUGE0q8bfIDVii%e)shLmy zuJ&3QV^Fn51r{D->;6q1t0+G0JVdw+;F|q2yRrV1iY&#**dV_Jwv8!&oiHx#>i!m$ zJu)9VA_Ct4d-9atBiMjJT6Vnw>haEbm{w>;&M6T`v{!WA$N2 zwP$N^IX-)Swp^9hEikG*T>iyG5#Mnm6oJ3N?y=tpxP%Q?`J`-AvrLV#3r8FW0tHw` z-tTqSoc`;{=(5JLxaE~R$LIFu)n~aXn*flZse>k#UW}P}6?t-+9lIScA^dsGIRCfT z!Sda+GJn=Ha#hZDt2hoDPNFqbRkWW*k)x1AB~Y%yZ4dQ7^aWH;GEXm=PO_F(t{g#U zXBagecLp52S+1H*vJ`|`TMRw;3_jgFKBY6U+S__#O3bo+PJ>lV7#}^$PV}a#Qoi%k zj4R$6VTj9)=Ol-o1T5#FW|fvyxa$!a^=q?0wKi%A?G!<9N88XRG=gqu0pKG-1dF*T3dCNAr7kV5;(9_3q4eEIkygSVv z0J`;AH`?oE>rGhbRsh$iQl>`rr&ircjpD^w?#~(b`x;C-lr;q}_YXI&byG1Pf*qa4 zJo?Z@=-+vj++OOvPHn(!bPp=Apr0A8E|ce}k41DMr7P04{O*xT<#D%4;fqaWam!XL zAmp;~nG57c*Y1YF&W&%~gP|T+t1SwV2YrNCx;x_uQc10Xs{Fiu|991%@c)RWKQ286p=wee zW1NK~^8?4MMTK#8D~NMib|k9@GnD*~U0=R|l=)|V5e#{gDU4k(rVX=oSUvPE9|Mgj zQy25Dt5HLDsCn5}2)EaU&q|pju%B>DJE>}L?hN!7<8^9` zD3(J0(~V#N+Issc--s30e*D;`08s`Hxvlr+8VHSZZL!yG#sVHtoWd5p_+Ln)3;oh+ zRP>^$+9-!j>mJECCVUB7*fyoCFiQB=vU9mmj;}sor!egQ;QU79jgA;S9u68f zSVSA=%s@Am2XTKjboO^UDzFa0bA3|593;uyGEvH^R!_;D7(mJG+XRUz%4G4KpT^-Q z$chs+C_0_?2$O_(CXs4D;)n)*VzNGVzMRF_vya+`ft~m9i=PMQ*8+rP1A4GwN8xP3 zEwQLG37-V)C{3)>O|1Ux+_^|hgrUUgjr3>0@EYpzU+dlXc1i70&mA)gLOV~g(|F67 z#KBWvSC|fZr@@~klkM?k{<%wY&h{(-6_fjBh*xIC+|QhOP#PV!Dec+8#f2=&^P0>p z253Z7t1{a&7}4bU}YX6k6;Ijyj0!;ozKGKwZ_Jq+Eo& zMc|k>$4C!GM>Hg}yEU&9d(?b!tf)76!Q=yPvLHy4Z6HU%uHd1K=Q0iSAA}7qTK9M3 zAz5X4h9E-!#mBw9(WYteUeAkT5YfcoP7EVm`=UFOJTn%diPHX}s1oFT)?(Kak4h>= zYWSjn0RpVXuPo#n%u4sH#UrD3*IUW*RZSp3@SyS+`7h90dz<3wzoiFEoi7_@0`tKp z3o7f+rQKJjD?m(kT#JfNr!9 zsI;Mm2t4fMtA~ndTb3V|(^saqVJU|r{YU;vs7-Z)!>C;QI&&UO(=6maXS_FS=$sRj zeQgauiJ@DLpKpALEf5iYfc*0M_-d-MxWLS*V-2i=FA2C52K<^9Gh}$`oJ{zI?)t04 zB^^3e>V8dc6rn3mx2Idx$W53ysXUDE3&8Kz)Pc(8JA_dQlJ0qvx&3LGZNirf+l1&e zf9nLJ#;EZQO%T^H&!`GIdTwE9eB7+9&_rqy1J?K|B>91|dC)aJS?z(uR_$3?YV zwb5rMCc5+Z#@9l`Ou2kjAf#A<#{be3Qn2z)4u^2aj(*62;;1dNFL;eC2579_8=~iB zDRJ@;3d;}Rc6f>JCEJZ@8!fD0om#D{u7f*2&iWA@0tB~mphq;{$X4?n(` zMJ?`s_jSW{u_>AiL#d%|X+G@k`6j*G+)ls#(9hd2Me`E17`&z81U`Sik}Utdr%qyb znujxDA9SEStXk!Xf|Fq5BegnYAAQLT3|ei+#%Pzlj*&70dNa#?X1@ z_1|=~Y`m5Vc*MR#m1U;1+|w60vHT+tf48;39dJ&YEV42mX4<5a1vKog#5^+ z(Nz`QZ_z7x>cxJvW79A?QdR4}%WBDvWZWg?VEJ*F(M%5x^{+D zdxC}yT3<}|P$uME`0-!+hG*WThCCpFzjwl~z{2q0=Pw$K_rf2RiLITe40Eq~x{S>HTlPIE^e*L$F<8edHs?V|Li#*`tzy z)rAOFZj9hF>`aRxZq5de74F=Tua{4x$Ln&BdV6#SxkqoTCnyJNDCM*k86uLaK*)*cF3vBk9AiT z_!_a~l!yNBV8M`Hq~ZF5R65|yajy);r42;Te-~$bsdayjpUfTem(KP`kfwKa0)IxC z%dE$1xVhx6HzqOgUM__+E7in>`ec$=5f`CU(QU#=l`M4ZIL>E-j6ujwfgPdp8z4X@ zIM|Ton9^7!vH9O5bEStZVz@DG@?51xmjYo-q)#Y$_>z6e`W3!|*HryOo{+#VvM5GJ zV1nlU?!10(%leJzly{?RvZcDq#)vN@3KuR4Wi`ef|&{W1YgpPr8MZ4ImXqw?-}37aM+=n zO5x;0nXXZJgHUJL-CAtd*w5#!(SnUhR;(fanY@k&seL^8u!)RRcjxt=w>BS;wqP1$ z5e+RTNIA)%f>ZAe!Tb~vuS_;|GN_Mcnm!q1pb84bWeV}3C(vc~xz^$y;Z6P<;$L3> zW)W>e;EIKCzg7s~k!6cCu^n%$K0<%AvihzNx(&6uSH7-&9 z1^)Txgm%JRoF5;h)b_US$L=ZR2P(QpSV>h81}hGr=i7fIOab0@?heD7L& zZNy1Fs!MB#4Zr@d!#2@8BgUzv7ZJ>LhLM=qL zwWLgrkH+r=SDI%VJDVNl;~#oHni-; zUIU>!g65++X!l(DgrORoFj2Vtk*U)Yo?OMJ-d=21A*-QXy8rTg`_jtd8x7nDktTvy z@k=+yi8K!T6g%pG{KiD~yOm$W1l`qO@nY4$vkUl@dOagVmIcaOeRLCG^mCtFDh?>; ze-_aEk55jq?c`Jj-(#o9ZQQkjbi_Na8pV(-;T=g4X_h^yt?(w$Y@&03Cw+P%j?KiF zt#^;ki1sT9iQ)$>(GaTH^*MbL#kr&FB@n$txk~=9Rj@&1Zanr=`Sg|ir4Y4I$Xv}*K`fKB>pqI$-F&CPEB z=hVV}mp=4D30M{4n9im3P+YbYoa24iUY6#erQncEJT8dAaW8PzaXrh(rDNz$%`sAo zV_@^(d#Z6E=S^Xoo!XvxFBS{D&F(dTZy1fWrzOzQAg9P);=Gxb#?ORLlhOQt(-;DK zB-N+8Q(IFhvw$5|-F^po%+>9%*w>MD&W!N=$Zu(pf3FR2U@YAU zJI9xAWpu^YM)Wih#;x5jer&`HMOm*0Ct4lND9j1~W7;DvYF>@0}p zoMJd~%*C!`-}!BV%sg4n^c;TdAS1A`qokmaISIL5Y|m?S0fI;fVC;@s^5z*rW%7zh zcT+eZoA1Cu-)U6=pHYmicbE2rOl}QI20^;uTLV3>gc>UIN;bGjf|_73K+||@=%AUn z>330hce)L3c_(>ektJH{GFt(uu&~y9D~!@pafB#L()9V=ne|X@VG=-VgMp;f0Udti ziY_|ua_7?ny+l;YU#Fs!uIO%+aC)n=6dH9A;4y~Zt+T|8-q$-}09w}X7nE?0#$KBikQpe9?jK%Ay!8Jg(#=bKz? z`(Jw|+9-ije&1oC7^%F*uW{yhn=n@jY>V07S-NcvyCx+9L~n9)f}`Y}GNgmvY6FBa zT)Fg?sl}}}%L5zzuSjjz9pNQ$H5~{X`V8(9%PTmwuU#8UDLXpu8%k)lPxv*&_|k69 z6+}0$z`7WkuJQY-^-0{{#zudIQHuv2q_2Lx$Hk)0!1+T9Jah$F5Eoj?cvW6D*Y`Zv zwx|J9tVxHk65?f8oeE;^hp~1&ON3MPDQqt%=z+G>@d~of@WMy6rxx-#hiQb+y)16H zDVNH3fC$%PJc}qHFwoh|V2nc?Pb$G7HL%aCtN0=!+Na7?%JSxa`C?C6^C46PB4stxH2Krl zGc|TE@f(QNz5TLg&QLIzayhyZtvAO)o&l0HXb(VutyOWjk>UXs7quM**a z=j3N-I$v)O7Zi!_pp07D%kzl%_JR%fpOVfbHWA8^>Ivd!M9r0`Y|RoxdLP>~?CIsq z0mijmHPH7VP7GLOj@v1Xn#Ok;*r=9s{E<%ev&;JHl3UjZ&$u9$mcza zV$LJpPA^DVF4jZQ7xucHLZZiIq0{RI=@SAs2fnSkX zabL40Rg^8OMBeN)pbV2(a{hmt!ohRzmqW+8>nHD&vIqG%W<{*tj&~zXDFMa{IJ){E z1dKuwexG78)W#e%()w_z2muyCSMM|)fe^K#Cf#_O37XS{x0h9VhpxoNEl5&Xe-lq$ zgVa}Vr5i}I^LMw|mk*%JWOR~uHqs^@zwaG0@7EDjW8y(4Wsu7Wdpj3-ZPp24Kk(b| zt+4B=JVh9JZBiwjz*M)c3|lGpe^zijnF%`}a}CGhT-ekAD*EhR>;uD+Z^gk+YJAai*$| z{F}q$JY&q$JGg3IH$#FqQ2@&`49taiPWf@;(vm8|TR&3>y-fT_C@!nq!w8vAnJBnc z=|BMM{s62qCocLMW7_CzF0!Hp{Fo$wn=Va`4OXdxD_#phC$5626IZ=pot&>9U%4=| z>D1;@p{|z)+^ULvuRUOv`wp=5u3&y-*;1=G&5T3(U9hfeq6?7he#eH>bohzty-BcJ)EkU zRe>IJx^+y>1{~2jCF!v-{jvCo_HOI}l2*wGtw_-qP2n4h@_cltmc>) z-|5CX?r3%!Tye5@*3&v*;Y(Dm-h|Xe38LE_;#0B-ni5j&`K|r>MZB;agy#)!iCWm7 z3jkuVZD9FyvN@tu0p`8Elq1@^HEx_1REClOoNe62df*tbwM z;VrM5%n_(Qctr>3QW@bYPr8j?lQ_J-@B_wLPM#6_j&6bI-~!g^ze`Aj#08)7#;zDo zqfn=2{?L%+e)$d|G3c9R{?til`PtNJ=MJwf#uJ4#=+L8Bw*d9Ds%A=KzcHLEd!EbZ zYMNyMil~Y41EjSG%sz%@FZYCtQQEUtuF1p3i!3KtALfaU$H?s;3BWP_m{VOhV8Wx@C3kaQX5-ddv zngR7?U7cHN#B*4X9`{tW9}a2iY~G#mSPJxstp(? z&3NdyXJKib=Sld&W1Uir2xFqU3YCRL}h zA1i{EpI8mowAd$%8|+o`*FC2J)bwKbbB#dv(wUD=wJbeVjWe6$96u$`v4y(IX=yRG zjN-RI^F9ZHlkv2f!_B^e@tanfaTB0knx9cO96I9)G<7lRw5lQhK^};@(vLE7Mv3?w zL6$%dpG1S&7S9MBWq?1#Qx`|kTdo51HRU_ROl{C?Ro2V!?+FsBPsv?fHYohc?W4w- zE5Z8%b)}AiCSQSzr!UV{l3)g6vq~2$I&_vebtsI;B#o^_rg?zGV7V8#OMxA z=WMdXoJhVdD$P=FZt+F~hWWYSKOwPm?nGb6#Eu=tILe>{l$`Malr{pf`R&l;{URo5n#!r2J+|C~)>vATe6F`!?f~D{P1#U+R z!!Am?^ZZFYKx}!Jm(G9eJ`DcNv1ZPHO+ZpUzE*qqzDND9z~(oR69d|p4?B_3Nb%f5xg%Er!HG+#b*znS5ouB z9nx!o>siefvHl-C_T$Gv4t__?fWj=s4rHv2pGRz#3#_eV3xxP$_)^)In5VU#5q zEy6%*I(J&zW&Tubv*Vvdz`z@+}DXrkoTA|F~95iNy<-)5Zt&QEs} zYw^goc+*&LKyBO--bwDhIC<(~Kgwq9a5YMVkC(-u8KVi^MFn%&DpDJEHO`5x6+bF9*e1T8-LguEPIJyE=(PN8E_j}7X-(uZvg=k zbsaO2n3@5a{P_({CFWpXT;=BD<`T}(GzP@PV3e>$5`6T@hxO@oT(4LE17~yKP7 zu)*~JORF#0lP+>Kn4bi5mV-Tb zA4*2d+PAiWIOFpe2PFh338nY$m)Z6USP?lLJQNI47u%S3wB1v+SA9Bi_mv*rIZHx_ zuCc(|_Wjifr?9QGUku5#Y_sL}R6zPMds_UHh%gWoDx2QE-ZT}3s*@2>j9 zdxe;tpCZ>VShGIk?W(iLzy>C5i5cqi< z&SvmT2<}cWxQly@!p()RFN|iPJO7lfLvNF8tU~Pqf(`U49?t$!T`NpVX45qRiog zYNTgV8W?%Sa4Go{jJX!5Z3!H|ozA-xDpGwRI7y4CkFYO~gK(Eoy^`ksgT*Y^#56!^0XXc|r7biw9X=QPy=r zV4ODz1*wf%+IL{yZE&L>9q(qm4jnMg+tJn^2_QrG;I>Xu%h~32og!j*5!H9Aj`x6m zP9R_a=auAP4YXtOpD)EN-=<*QwXg$3EPtZamGBnnpb$iDlbE%36;=gcJ@@80(BhIw z%~u8p8h%(GP3&$pYI^P%?hN+GFi7cjFde~v6CJdioVlxGzGX!9s|YGp6~aozsr5o; z+7RXu$-B35-qBt>yVQ??hs(RUhgN=-y7_S8C~G|T+BaR9F&-G8k!{DUsk|(Fp(9kf zv|KOrPk67WG2eLCN;O>8fb*$h_Y7+sWt?ymHBp&dDaQhfgLO{foqm(|Br)sD5(QE%ydA~E^w#Az7cEBa#M2(-{)8q4bf3;k6HqP|35!Z|(FbYfzk9v^byGLU zG0jfCgQ_W4voNNjWgq9J$$!^!r1jb(t7eUXaJceId13=AIqOMa%L7aTIJ9B@VL#6>=j>NpA8K51uy71V0pF7S}0uk3@YnCrj z$_Gf<+WhdY(*YAF+K{;JhH`zoO{D|tOuGzy)OoN~gI$$0x~$`6P=~LSz=-Js(Z0vq zNzSjnoUQO}C_D{C1MW0aWXajvb{yB)wR|hd4G!A-*TEjc>dHKE@+MJ!ZY9VT@9Sb; zq7k$6gZa1-RkRfjm=fG@=1DXdb80cbJQdsqO?&$fc1(6l>Fs5O|wj;+78SJT97 zajO?jxl!y0%L`u6{@e+Ms4#fW>^m12C;NaKJ1T>$-^crzoVh zw})B^tW(X#KL?s&^bBf_I+v)#2gpTn4A7(OjR}ge9{g%Wc}qZK@Cr?N*;XzMy~p{4 z-8F@1fUJ~e3|d&SnJ`=qIR4-?Zoz^tQgKr~Mvl>|VR)vdOjhj@0Wm%{t{vH(;&C)s zpj0fTE8%>uG+}xDidxp>F^!!wRogD*vc*B7tY$OKxDGqTEvc|1h1eo05x)^XMb~3- zTrgcnE4rDZF&2%iPgFX%`aPCQQEkWq8<@9t&c?%?w9= z!j2^!J^Gmw_rZ7#(pH;oQ`xQ|{SUN)Krg8gaD6GoxqHp;R4d>x@srU7>M#kaBda=O zs6R1Tc1*Y|j+rB$W_G@gIAvPstn9_R*)ZKU8)d^Lmp$hK#ncqa4zlk8pk$8yF={f; ze3`!Z+#wO4aZ#_+T<3;k>Yode^;(v)I1p*0A2Nm>=39Ke8hmI~_cOAK7x~&#gm)+} z1pxx`wKe}8YnzlYQTTCUKlb_rizKLjEu7wLkRE*Ix6ly}1t!()oSbVqky-LPxYIHB zWiM-PgRc(jG7149z^!IG+bRnl(J-TU&?!cx}@^WyV;QZZ(gjRG4$*+84 zVvLKgF3a`7GM&#}H>9!Mxgv17< z%o+V)6m*UQ!_T$`BQK!R2Qs`#x|Uh#=54sUwF3*dulBOkubIdlAb7cn!kEI&j5o0` zu9ytTTXQJiaH_;gXag?2cC|7~9v*tRr|U_KeJ#?13W4H!kY*cNip-%g*3wwO;c@K} z={f+AZWf~_G~HSk3tlRv*>cE5t`luyTc(sShWe5Z((>7yfK1W@>C48S4X!s2TDb<3 ziT_*`?f>(Xi27ILIuw(O90Z6!zx<;PBIzb5a|JIh~V+dBGfl+k}@(M7*VcybmO1{m{v`9Gk=h&M@o3{ zfXtI8w(=7eTGEI8A@!l;K6yWcl6EMVaoB% zWlfJ@ZkZ;2QlRG`a$!=>RhoW3{(mf@eOaNM;^#nhM2VSShx;Akco&1{3ng5))B{(7 z!>s1LtUD3=;cK^0>Rd#y`8jY|#M97LJ8zu0I;@@!H z2-hk+dn7?ryRfR-cqvY+aA%{)$CNkf|B3CJa+bNfn37#KfX(&5ej1HlGPO<;0`~}+ zCuv5!rw1tGT*nH_?gi3&&vD7i28d3RxUiWOBNr65_rRh)?NXjQ9vL?uM+(>UCW08y zL6>&es1A_sWdwQNJ_|)GWjWz5k4MyUIM;t$9FHuB_-j0-cdV>+fzeq6ZQOU8H-iX; z(QmJ`Io?u(L>0%KSZb8vhhCjrIGFay(CjXiiRA`d)tq%Jxs(%b_g!+{86-dpc$R(Y zN*G4a#Zzr5?_9I;3yyHwQMq1=e9m>8e-oZ9lJfO6@jzizy?Lqf9AWJYRvIHU)&MG~ zmKi}u>uc1{&~Cxm>0At@52#O*;w=u4YDXHY8h)DH4BS?3ImaU+<$H?zCqVuLG)$w& zWVI7Ai~|umV_DF(4r%zRYZbqtJ1~x3^*Gzwpy<0^_{k1FzQ$T3r$Hq^)FvnDgBT?> z9qfc}!_$JXy9|86IZ(BPk^(ZvC{!LWi*viRcfUn<8L&^&W=g()m%HX!rqTX16yo=F zV51@+4sG087ZF#K5xzDreyNx{gv^`dnavc3l2h^Gz_{q(C3L&gm?$IDW9Sr|1whE{ zHiGx;*kqVv2D8~S8c67#>}0lO6=6=Ia*=oY03+3DUXKVN_gpmAqU-Y`jNWDVwTk)# z{b5Ei?Zrwp#(N86`gQC+WqkD}2VVMp5CnxwCykZUWK4Jq5!W1V*J?Yv1owQ6Vys`A zWw;l#kL%b@#-=sVinfJI8KvaGlN!-HieeaU{p1_>m=+x~CmG`9pQ&V`?}P=tYjTa1 zg!x_Do-`QYm`Ino1bYfFkq7Nfo=KCW3-Q&-S>2@sfU*U#6)=Nf0n_l#@{5FDdtdlc zhzX42mPTWVl0c>C`_iuU6=G_H3<;(#4_`G0Bftd{IlaX;AVG2qgd(K(RfsnLmDW@a zEqWRO+A!GZO}jlp6|p&V5h6yQG7(?09I0QIn=W>e*<)5#-lG?1bH9IWQy~)e!u3Nl zFJeVN3j-4OWxB@noV0iYncrZ-pH-q%uTVZ9m#ZS7X14l&trZvBB-w8t7WKj{XA^ZK z$n7Hv3Gs`&EmDajLjAt(1pStr@$zI%``}8rU4Yw$5?~nZ8TEY?3h2)n*2)X?6!YI5 zMmGx{%B${oJQ~7MwylW<)G172Ny2mO3x7T6sC>-LwuLr0x&<4dqAUrqbBbZ`G}`}J z$Hd<=Vx2*Y5}%vRT;?U!M(M1g##CZjL0|6vP*OEDp# zw4Z`N(>;LUUFUWgZ>UCMF2+dC%vstSZu6Rv<1sd*&Y@%y2=@xQw~vXMkL7OU-DXAD z$V};leq39oD(Lx`qDWR09z_Zt6oA4_#XELkBBK0}me&G8I}kBIgez{D)-8-nBIn&d zc?at=?wf&89ZwfuZ;T{YKCva4stbM+9<~CF8gi=|W7f)*)LGwK$bbaxDX46a=OvRS zVh3$(Oh07a>NBN|xePKg6gfsb$Tnl7Vv5dgJhSaxkiKBvXfB%P3435ICtOsq)p=>~ zepY}kHaQJRO#|Vcxm2e1Hb9KOmML*-vW2#N0P`?J=l$Utq>vp@&Eh94#^S~9pLMm- zwkhC#lNhGyFgvSQ{%vOp2p9%C+6Y(?WAm)DxL^iS=ps#+= z2c{O6^d3t!EHQ9yBzbt4aQ0wk3P=cR)^T`OP;}?S$Z1K*rdUzWMO7`LZzCXoNT#qJ zGnqaHpa2>0ayyG+mIX*MFy1Y)1(i6~+P6rhdt#L22HIjX!5?JQbkPCi^kfa;@NLqJ z{O0B;)13)V4MW9PWWseb8F%m`71=&p7+P35C?KEb{F#=mGfAZ+LsAy3d2SvApJvwK zyvan${eD^zE6IB%`dbW#M1U_yJZ~BD8uVr9Z9tX#Z;Ve)?XxIX<6+bo?QaMQP>$dRCGjH;~wX|H*rh-&xo{4)-HIWEays&%@4TNPTDKi#u|sV&|L5 zM(?qbQ`A(5Az0>`>5nX^h(8g^ zrp1lLGEkiOy`=0IhbdHx_##0xDP1B2r2(18(;fhq6>bvgpAjD)Xdu0Ajsbz5s0R5u4u`Bqy zCk8#{%btnSa)MmqS4F+3EOiOECA}`cgXlIS$!1C#k5$9Sht*OAzt;bNA1%%CIC#}x zr&M>!RYef>$DB9OrIlmlPWeVGdnEZFA);{9Q7410nk=4lWKoX=T{>^J{g<#j|E@W= zdGJ_dghM&UHpzQ4c=Z~iu_g|#RhGqnI=UM7xLIjyqtQ?O^lIPBD4SuS1CY5ZFud9wt-E9%&#?a<3UqS7>K}A05-Xdy{A} zVxvsoxvaYwH;ZJ`=Pj?|rq1Ch@u2tVsN}P4`?GOG+0PHG_svSVS=(WqCadv_B~TO^ zP$J+H~BUD+7WrOid^`n`N4Ylno%Esia-r#8a=m#pXRzlILii=>=p z!1MP(_^x=Y^gs}j4piV5Fj^O;TmBc7tzdD}rY4ownZA4SV%397sW3h;AutIB1uG5% z0vZJX1QheqEyZfmGELQaAG?V=_Dy9jWO)P>f;YY|%9c3^Y+RcIgkCPcwRayz0s{et EpiB?(i2wiq literal 0 HcmV?d00001 diff --git a/src/main/resources/policy/constraint.xml b/src/main/resources/policy/constraint.xml index c88b99c90..45053731e 100644 --- a/src/main/resources/policy/constraint.xml +++ b/src/main/resources/policy/constraint.xml @@ -23,7 +23,6 @@ - ANY_POLICY NO_POLICY @@ -36,14 +35,8 @@ - - - - - - @@ -75,7 +68,30 @@ - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + @@ -85,9 +101,55 @@ - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + @@ -97,7 +159,6 @@ - @@ -151,7 +211,30 @@ - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + @@ -161,9 +244,55 @@ - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + @@ -171,7 +300,6 @@ - - - - - - - - + - + @@ -247,7 +432,30 @@ - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + @@ -257,85 +465,62 @@ - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + - + + + RSA + DSA + ECDSA + + + DSA + RSA + ECDSA + + + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3-224 + SHA3-256 + SHA3-384 + SHA3-512 + RIPEMD160 + + - - - RSA - DSA - ECDSA - PLAIN-ECDSA - - - - DSA - RSA - ECDSA - PLAIN-ECDSA - - - - MD2 - MD5 - SHA1 - SHA224 - SHA256 - SHA384 - SHA512 - SHA3-224 - SHA3-256 - SHA3-384 - SHA3-512 - RIPEMD160 - WHIRLPOOL - - - - MD2 - MD5 - SHA1 - SHA224 - SHA256 - SHA384 - SHA512 - SHA3-224 - SHA3-256 - SHA3-384 - SHA3-512 - RIPEMD160 - WHIRLPOOL - - - DSA - DSA - DSA - DSA - RSA - RSA - RSA - RSA - ECDSA - ECDSA - ECDSA - ECDSA - ECDSA - ECDSA - PLAIN-ECDSA - PLAIN-ECDSA - PLAIN-ECDSA - PLAIN-ECDSA - PLAIN-ECDSA - PLAIN-ECDSA - - - - - - - + + diff --git a/src/main/resources/policy/custom-constraint.xml b/src/main/resources/policy/custom-constraint.xml index 8abc6ae46..9c24dd1b4 100644 --- a/src/main/resources/policy/custom-constraint.xml +++ b/src/main/resources/policy/custom-constraint.xml @@ -19,6 +19,7 @@ application/vnd.etsi.asic-e+zip + @@ -35,7 +36,7 @@ - + @@ -54,8 +55,8 @@ - - + + nonRepudiation @@ -97,7 +98,7 @@ - + @@ -152,6 +153,8 @@ + + @@ -176,7 +179,7 @@ - + @@ -195,8 +198,8 @@ - - + + nonRepudiation @@ -238,7 +241,7 @@ - + @@ -311,20 +314,21 @@ - + + - + - + @@ -358,7 +362,7 @@ - + @@ -413,7 +417,7 @@ - + @@ -425,7 +429,7 @@ - + @@ -458,7 +462,7 @@ - + @@ -520,9 +524,9 @@ - + - + diff --git a/src/main/resources/self_signed_tsa.p12 b/src/main/resources/self_signed_tsa.p12 new file mode 100644 index 0000000000000000000000000000000000000000..626dc9de3c59d2d23e4b3ad4dee00c1a436e8546 GIT binary patch literal 4268 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 textarea::placeholder { - background-color: #ff8f9e; + background-color: #ffeeba; color: black !important; } @@ -181,10 +183,6 @@ code { margin-top: 10px; } -.nav-item { - margin-right: 5px; -} - .loader { border: 5px solid #f3f3f3; -webkit-animation: spin 1s linear infinite; @@ -550,6 +548,11 @@ input:checked + .slider:before { max-width: 60px; } +.scrollbar-lite { + scrollbar-width: none; + overflow-y:scroll; +} + .scrollbar-lite::-webkit-scrollbar { width: 5px; background-color: transparent; @@ -912,6 +915,17 @@ height: 100%; border-bottom : 1px solid #000000; } + +.nav-item.ws-tab { + margin-bottom: -1px; + margin-right: 5px; + border-bottom : 1px solid #000000; +} + +.nav-item.ws-tab.active { + border-bottom : 1px solid #e2e3e5; +} + .nav-tabs .nav-link { color: #6c757d; border-color: #6c757d !important; @@ -919,7 +933,7 @@ height: 100%; /*background-color: #dee2e6;*/ } -.nav-tabs .nav-link.current { +.nav-link.current { color: black; border-color: #000 #000 #e2e3e5 #000 !important; @@ -1178,7 +1192,7 @@ textarea:placeholder-shown.required-field { .fixed-action-btns { position: fixed; - right: 50px; + right: 1vw; bottom: 80px; margin-bottom: 0; z-index: 6; @@ -1378,6 +1392,10 @@ input:focus, textarea:focus, select:focus { display: none; } +.stampAnnotation:hover { + border: 1px solid green !important; +} + .bg-striped { background-image: linear-gradient(135deg, #8a4848 25%, #474747 25%, #474747 50%, #8a4848 50%, #8a4848 75%, #474747 75%, #474747 100%); background-size: 28.28px 28.28px; @@ -1572,7 +1590,7 @@ input:focus, textarea:focus, select:focus { display: inline !important; } .d-xxl-table-cell { - display: table-cell!important; + display: table-cell !important; } .d-xxl-block { display: block !important; @@ -1585,6 +1603,11 @@ input:focus, textarea:focus, select:focus { } } +@media (min-width: 1600px) { + .d-xxxl-table-cell { + display: table-cell !important; + } +} .ss-single-selected { vertical-align: middle !important; margin: auto !important; diff --git a/src/main/resources/static/css/flags.css b/src/main/resources/static/css/flags.css new file mode 100644 index 000000000..26b2a6490 --- /dev/null +++ b/src/main/resources/static/css/flags.css @@ -0,0 +1,149 @@ + +.small_flag { + display: block; + width: 32px; + height : 24px; + float: left; + border: 1px solid #ececec; + margin-right: 5px; +} + +.flag { + display: block; + width: 64px; + height : 48px; + border: 1px solid #ececec; +} + +.flag_AT { + background: url(../images/flags/at.svg); + background-size: contain; +} +.flag_BG { + background: url(../images/flags/bg.svg); + background-size: contain; +} +.flag_CZ { + background: url(../images/flags/cz.svg); + background-size: contain; +} +.flag_DK { + background: url(../images/flags/dk.svg); + background-size: contain; +} +.flag_EL { + background: url(../images/flags/el.svg); + background-size: contain; +} +.flag_EU { + background: url(../images/flags/eu.svg); + background-size: contain; +} +.flag_FR { + background: url(../images/flags/fr.svg); + background-size: contain; +} +.flag_HU { + background: url(../images/flags/hu.svg); + background-size: contain; +} +.flag_IS { + background: url(../images/flags/is.svg); + background-size: contain; +} +.flag_LI { + background: url(../images/flags/li.svg); + background-size: contain; +} +.flag_LU { + background: url(../images/flags/lu.svg); + background-size: contain; +} +.flag_MT { + background: url(../images/flags/mt.svg); + background-size: contain; +} +.flag_NO { + background: url(../images/flags/no.svg); + background-size: contain; +} +.flag_PT { + background: url(../images/flags/pt.svg); + background-size: contain; +} +.flag_SE { + background: url(../images/flags/se.svg); + background-size: contain; +} +.flag_SK { + background: url(../images/flags/sk.svg); + background-size: contain; +} +.flag_BE { + background: url(../images/flags/be.svg); + background-size: contain; +} +.flag_CY { + background: url(../images/flags/cy.svg); + background-size: contain; +} +.flag_DE { + background: url(../images/flags/de.svg); + background-size: contain; +} +.flag_EE { + background: url(../images/flags/ee.svg); + background-size: contain; +} +.flag_ES { + background: url(../images/flags/es.svg); + background-size: contain; +} +.flag_FI { + background: url(../images/flags/fi.svg); + background-size: contain; +} +.flag_HR { + background: url(../images/flags/hr.svg); + background-size: contain; +} +.flag_IE { + background: url(../images/flags/ie.svg); + background-size: contain; +} +.flag_IT { + background: url(../images/flags/it.svg); + background-size: contain; +} +.flag_LT { + background: url(../images/flags/lt.svg); + background-size: contain; +} +.flag_LV { + background: url(../images/flags/lv.svg); + background-size: contain; +} +.flag_NL { + background: url(../images/flags/nl.svg); + background-size: contain; +} +.flag_PL { + background: url(../images/flags/pl.svg); + background-size: contain; +} +.flag_RO { + background: url(../images/flags/ro.svg); + background-size: contain; +} +.flag_SI { + background: url(../images/flags/si.svg); + background-size: contain; +} +.flag_UK { + background: url(../images/flags/uk.svg); + background-size: contain; +} +.flag_no_country { + background: url(../images/flags/no_country.svg); + background-size: contain; +} \ No newline at end of file diff --git a/src/main/resources/static/images/flags/at.svg b/src/main/resources/static/images/flags/at.svg new file mode 100644 index 000000000..38dd0ecef --- /dev/null +++ b/src/main/resources/static/images/flags/at.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/static/images/flags/be.svg b/src/main/resources/static/images/flags/be.svg new file mode 100644 index 000000000..44a0aed26 --- /dev/null +++ b/src/main/resources/static/images/flags/be.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/bg.svg b/src/main/resources/static/images/flags/bg.svg new file mode 100644 index 000000000..d0501130c --- /dev/null +++ b/src/main/resources/static/images/flags/bg.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/cy.svg b/src/main/resources/static/images/flags/cy.svg new file mode 100644 index 000000000..f21a4477d --- /dev/null +++ b/src/main/resources/static/images/flags/cy.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/static/images/flags/cz.svg b/src/main/resources/static/images/flags/cz.svg new file mode 100644 index 000000000..d27a0d5ba --- /dev/null +++ b/src/main/resources/static/images/flags/cz.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/de.svg b/src/main/resources/static/images/flags/de.svg new file mode 100644 index 000000000..089f99952 --- /dev/null +++ b/src/main/resources/static/images/flags/de.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/dk.svg b/src/main/resources/static/images/flags/dk.svg new file mode 100644 index 000000000..031d2032a --- /dev/null +++ b/src/main/resources/static/images/flags/dk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/static/images/flags/ee.svg b/src/main/resources/static/images/flags/ee.svg new file mode 100644 index 000000000..56724db64 --- /dev/null +++ b/src/main/resources/static/images/flags/ee.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/el.svg b/src/main/resources/static/images/flags/el.svg new file mode 100644 index 000000000..441181301 --- /dev/null +++ b/src/main/resources/static/images/flags/el.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/es.svg b/src/main/resources/static/images/flags/es.svg new file mode 100644 index 000000000..67b456a6d --- /dev/null +++ b/src/main/resources/static/images/flags/es.svg @@ -0,0 +1,581 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/eu.svg b/src/main/resources/static/images/flags/eu.svg new file mode 100644 index 000000000..572a8e698 --- /dev/null +++ b/src/main/resources/static/images/flags/eu.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/fi.svg b/src/main/resources/static/images/flags/fi.svg new file mode 100644 index 000000000..a5518b733 --- /dev/null +++ b/src/main/resources/static/images/flags/fi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/static/images/flags/fr.svg b/src/main/resources/static/images/flags/fr.svg new file mode 100644 index 000000000..cb3a021a7 --- /dev/null +++ b/src/main/resources/static/images/flags/fr.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/hr.svg b/src/main/resources/static/images/flags/hr.svg new file mode 100644 index 000000000..7a8ad0977 --- /dev/null +++ b/src/main/resources/static/images/flags/hr.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/hu.svg b/src/main/resources/static/images/flags/hu.svg new file mode 100644 index 000000000..785e9be8a --- /dev/null +++ b/src/main/resources/static/images/flags/hu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/ie.svg b/src/main/resources/static/images/flags/ie.svg new file mode 100644 index 000000000..bd0b654f9 --- /dev/null +++ b/src/main/resources/static/images/flags/ie.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/is.svg b/src/main/resources/static/images/flags/is.svg new file mode 100644 index 000000000..3adce8853 --- /dev/null +++ b/src/main/resources/static/images/flags/is.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/src/main/resources/static/images/flags/it.svg b/src/main/resources/static/images/flags/it.svg new file mode 100644 index 000000000..060927841 --- /dev/null +++ b/src/main/resources/static/images/flags/it.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/li.svg b/src/main/resources/static/images/flags/li.svg new file mode 100644 index 000000000..1d00f1f50 --- /dev/null +++ b/src/main/resources/static/images/flags/li.svg @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/lt.svg b/src/main/resources/static/images/flags/lt.svg new file mode 100644 index 000000000..75c1c7a6a --- /dev/null +++ b/src/main/resources/static/images/flags/lt.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/lu.svg b/src/main/resources/static/images/flags/lu.svg new file mode 100644 index 000000000..80c2270fc --- /dev/null +++ b/src/main/resources/static/images/flags/lu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/static/images/flags/lv.svg b/src/main/resources/static/images/flags/lv.svg new file mode 100644 index 000000000..65ff5decc --- /dev/null +++ b/src/main/resources/static/images/flags/lv.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/static/images/flags/mt.svg b/src/main/resources/static/images/flags/mt.svg new file mode 100644 index 000000000..6333fa64f --- /dev/null +++ b/src/main/resources/static/images/flags/mt.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/nl.svg b/src/main/resources/static/images/flags/nl.svg new file mode 100644 index 000000000..7dfbd4152 --- /dev/null +++ b/src/main/resources/static/images/flags/nl.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/no.svg b/src/main/resources/static/images/flags/no.svg new file mode 100644 index 000000000..490f80d57 --- /dev/null +++ b/src/main/resources/static/images/flags/no.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/no_country.svg b/src/main/resources/static/images/flags/no_country.svg new file mode 100644 index 000000000..42fbbca84 --- /dev/null +++ b/src/main/resources/static/images/flags/no_country.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/pl.svg b/src/main/resources/static/images/flags/pl.svg new file mode 100644 index 000000000..f32275a30 --- /dev/null +++ b/src/main/resources/static/images/flags/pl.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/static/images/flags/pt.svg b/src/main/resources/static/images/flags/pt.svg new file mode 100644 index 000000000..53a6dcfb6 --- /dev/null +++ b/src/main/resources/static/images/flags/pt.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/ro.svg b/src/main/resources/static/images/flags/ro.svg new file mode 100644 index 000000000..b5d3e8a56 --- /dev/null +++ b/src/main/resources/static/images/flags/ro.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/static/images/flags/se.svg b/src/main/resources/static/images/flags/se.svg new file mode 100644 index 000000000..bff128b27 --- /dev/null +++ b/src/main/resources/static/images/flags/se.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/si.svg b/src/main/resources/static/images/flags/si.svg new file mode 100644 index 000000000..2a84bc194 --- /dev/null +++ b/src/main/resources/static/images/flags/si.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/sk.svg b/src/main/resources/static/images/flags/sk.svg new file mode 100644 index 000000000..f3dd1ab0d --- /dev/null +++ b/src/main/resources/static/images/flags/sk.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/main/resources/static/images/flags/uk.svg b/src/main/resources/static/images/flags/uk.svg new file mode 100644 index 000000000..64d7c8171 --- /dev/null +++ b/src/main/resources/static/images/flags/uk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/js/modules/ui/GlobalUi.js b/src/main/resources/static/js/modules/ui/GlobalUi.js index 651c975cf..fe88dff68 100644 --- a/src/main/resources/static/js/modules/ui/GlobalUi.js +++ b/src/main/resources/static/js/modules/ui/GlobalUi.js @@ -1,5 +1,4 @@ import {default as SelectUser} from "../utils/SelectUser.js"; -import {SseSubscribe} from "../utils/SseSubscribe.js"; import {CsrfToken} from "../../prototypes/CsrfToken.js"; import {WizUi} from "./WizUi.js"; @@ -21,10 +20,6 @@ export class GlobalUi { this.autoHide = $('.auto-hide'); this.markAsReadButtons = $('button[id^="markAsReadButton_"]'); this.markHelpAsReadButtons = $('button[id^="markHelpAsReadButton_"]'); - if(authUserEppn != null) { - sessionStorage.setItem("sseId", this.csrf.token) - this.sseSubscribe = new SseSubscribe(this.csrf.token); - } this.initListeners(); this.initBootBox(); this.initSideBar(); @@ -239,6 +234,7 @@ export class GlobalUi { if(!url.match("/user/users+[\\w\\W]+") && !url.match("/user/users") && !url.match("/admin/+[\\w\\W]+") + && !url.match("/manager/+[\\w\\W]+") && !url.match("^/user/$") && !url.match("^/user/signrequests$") && !url.match("/user/signrequests/+[\\w\\W]+")) { diff --git a/src/main/resources/static/js/modules/ui/forms/FormUi.js b/src/main/resources/static/js/modules/ui/forms/FormUi.js index c5fb1af60..a46b62d1f 100644 --- a/src/main/resources/static/js/modules/ui/forms/FormUi.js +++ b/src/main/resources/static/js/modules/ui/forms/FormUi.js @@ -1,16 +1,17 @@ -import {SseDispatcher} from "../../utils/SseDispatcher.js"; import {Message} from "../../../prototypes/Message.js"; +import Toast from "../Toast.js"; export default class FormUi { - constructor(formId, prefillTypes) { + constructor(domain, formId, prefillTypes) { console.info("Starting Form UI for " + formId); + this.toast = new Toast(); this.formId = formId; this.btnAddField = $('#btn-add-field'); this.btnRemove = $('#btn-remove'); this.btnSaveFields = $('#saveButton'); - this.sseDispatcher = new SseDispatcher(); this.prefillTypes = prefillTypes; + this.domain = domain; this.initListeners(); } @@ -97,7 +98,6 @@ export default class FormUi { message.type = "info"; message.text = "Enregistrement en cours"; message.object = null; - this.sseDispatcher.dispatchEvent("user", message); let self = this; let fieldsUpdates = $('form[name^="field-update"]'); let i = 1; @@ -106,7 +106,7 @@ export default class FormUi { console.log(fd.get("_csrf")); $.ajax({ type: "POST", - url: "/admin/forms/field/" + $(this).attr('id') + "/update?_csrf=" + fd.get("_csrf"), + url: "/" + self.domain + "/forms/field/" + $(this).attr('id') + "/update?_csrf=" + fd.get("_csrf"), data: fd, processData: false, contentType: false, @@ -116,7 +116,7 @@ export default class FormUi { message.type = "success"; message.text = "Modifications enregistrées"; message.object = null; - self.sseDispatcher.dispatchEvent("user", message); + self.toast.launch(message); } i++; }, @@ -125,7 +125,7 @@ export default class FormUi { message.type = "error"; message.text = "Problème lors de l'enregistrement"; message.object = null; - this.sseDispatcher.dispatchEvent("user", message); + self.toast.launch(message); }, }); }); diff --git a/src/main/resources/static/js/modules/ui/signrequests/Nexu.js b/src/main/resources/static/js/modules/ui/signrequests/Nexu.js index 8df679c5a..f4db4c503 100644 --- a/src/main/resources/static/js/modules/ui/signrequests/Nexu.js +++ b/src/main/resources/static/js/modules/ui/signrequests/Nexu.js @@ -1,30 +1,31 @@ export class Nexu { - constructor(nexuUrl, nexuVersion, rootUrl, addExtra, id) { - this.nexuUrl = nexuUrl; - this.nexuVersion = nexuVersion; - Nexu.rootUrl = rootUrl; + constructor(addExtra, id) { + this.globalProperties = JSON.parse(sessionStorage.getItem("globalProperties")); + this.nexuUrl = this.globalProperties.nexuUrl; + this.nexuVersion = this.globalProperties.nexuVersion; + Nexu.rootUrl = this.globalProperties.rootUrl; Nexu.addExtra = addExtra; Nexu.id = id; this.tokenId = null; this.keyId = null; - this.checkNexuClient(); + this.bindingPorts = "9795, 9886, 9887, 9888"; + this.detectedPort = ""; this.successDiv = $("#success"); this.successDiv.hide(); - } - - getCertificates() { - Nexu.updateProgressBar("Chargement des certificats", "25%"); - try { - nexu_get_certificates(Nexu.getDataToSign, Nexu.error); - } - catch(e) { - console.error(e); - const merror = { - errorMessage: "Problème avec l'application NexU" - }; - error(Object.create(merror)); - } + $("#warning-text").html("NexU not detected or not started ! "); + $("#nexu_missing_alert").show(); + $("#signFormConfirm").hide(); + let self = this; + this.checkNexuClient().then(function (){ + console.warn("NexU detected"); + $("#warning-text").html(""); + $("#nexu_missing_alert").hide(); + $("#signFormConfirm").show(); + if(id != null) { + self.loadScript(); + } + }); } static getDataToSign(certificateData) { @@ -105,28 +106,35 @@ export class Nexu { } checkNexuClient() { - console.log("Start checking NexU"); - $.ajax({ - type: "GET", - url: this.nexuUrl + "/nexu-info", - crossDomain: true, - dataType: "json", - context : this, - success: data => this.checkNexu(data) - }).fail(function (error) { - console.warn("NexU not detected or not started ! " + JSON.stringify(error)); - $("#warning-text").html("NexU not detected or not started ! "); - $("#nexu_missing_alert").show(); - $("#signFormConfirm").hide(); + return new Promise((resolve, reject) => { + console.log("Start checking NexU"); + let ports = this.bindingPorts.split(","); + let detectNexu = false; + let self = this; + ports.forEach(function (port){ + let url = "http://localhost:" + port.trim() + "/nexu-info"; + console.info("check " + url); + $.ajax({ + type: "GET", + url: url, + crossDomain: true, + dataType: "json", + context : this, + success: function (data) { + console.info("nexu detected on " + url); + detectNexu = true; + self.detectedPort = port.trim(); + self.checkNexu(data); + resolve("nexu detected"); + } + }); + }); }); - } checkNexu(data) { console.log("Check NexU"); - if(data.version.startsWith(this.nexuVersion) || data.version.startsWith("1.23") || data.version.startsWith("1.22")) { - console.log("Loading script..."); - this.loadScript(); + if(data.version.startsWith(this.nexuVersion) || data.version.startsWith("1.23") || data.version.startsWith("1.22") || data.version.startsWith("1.8")) { $("#nexu_ready_alert").show(); $("#submit-button").prop('disabled', false); } else { @@ -137,14 +145,11 @@ export class Nexu { } loadScript() { - let xhrObj = new XMLHttpRequest(); - xhrObj.open('GET', this.nexuUrl + "/nexu.js"); - xhrObj.send(null); - let se = document.createElement('script'); - se.type = "text/javascript"; - se.text = xhrObj.responseText; - document.getElementsByTagName('head')[0].appendChild(se); - console.log("NexU script loaded"); + let url = "http://localhost:" + this.detectedPort + "/nexu.js"; + console.info("loading nexu script : " + url); + $.getScript(url, function() { + nexu_get_certificates(Nexu.getDataToSign, Nexu.error); + }); } } \ No newline at end of file diff --git a/src/main/resources/static/js/modules/ui/signrequests/SignPosition.js b/src/main/resources/static/js/modules/ui/signrequests/SignPosition.js index d54f3a096..7468c464c 100644 --- a/src/main/resources/static/js/modules/ui/signrequests/SignPosition.js +++ b/src/main/resources/static/js/modules/ui/signrequests/SignPosition.js @@ -29,10 +29,12 @@ export class SignPosition extends EventFactory { signRequestParams.signImageNumber = signImageNumber; } if (this.signImages != null && this.signImages.length > 0) { - if (currentSignRequestParams[i].xPos > -1 && currentSignRequestParams[i].yPos > -1) { - signRequestParams.xPos = currentSignRequestParams[i].xPos; - signRequestParams.yPos = currentSignRequestParams[i].yPos; - signRequestParams.signPageNumber = currentSignRequestParams[i].signPageNumber; + if (currentSignRequestParams[i] != null) { + if (currentSignRequestParams[i].xPos > -1 && currentSignRequestParams[i].yPos > -1) { + signRequestParams.xPos = currentSignRequestParams[i].xPos; + signRequestParams.yPos = currentSignRequestParams[i].yPos; + signRequestParams.signPageNumber = currentSignRequestParams[i].signPageNumber; + } } } this.signRequestParamses.set(i + "", signRequestParams); @@ -71,7 +73,11 @@ export class SignPosition extends EventFactory { this.cross.attr("data-current", "true"); this.cross.css("position", "fixed"); this.cross.css("margin-left", "270px"); - this.cross.css("margin-top", "180px"); + if($("#ws-tabs").length) { + this.cross.css("margin-top", "222px"); + } else { + this.cross.css("margin-top", "180px"); + } this.cross.css("left", "0px"); this.cross.css("top", "0px"); } @@ -90,7 +96,7 @@ export class SignPosition extends EventFactory { this.toggleVisual(); $("#visualButton").remove(); } - if(this.signType === "nexuSign") { + if(this.signType === "nexuSign" || this.signType === "certSign") { $("#visualButton").removeClass("d-none"); } this.initListeners(); @@ -143,7 +149,9 @@ export class SignPosition extends EventFactory { } this.signExtraButton.on('click', e => this.toggleExtraInfos()); this.signExtraOnTopButton.on('click', e => this.toggleExtraPosition()); - this.displayMoreToolsButton.show(); + if(this.signType !== "visa" && this.signType !== "hiddenVisa") { + this.displayMoreToolsButton.show(); + } this.displayMoreToolsButton.on('click', e => this.displayMoreTools()); this.hideMoreToolsButton.on('click', e => this.hideMoreTools()); this.watermarkButton.on('click', e => this.toggleWatermark()); @@ -229,7 +237,6 @@ export class SignPosition extends EventFactory { let signRequestParams; if(this.signRequestParamses.get(currentSign) == null) { signRequestParams = new SignRequestParams(); - signRequestParams.xPos = -1; signRequestParams.yPos = -1; signRequestParams.signPageNumber = this.getCurrentSignParams().signPageNumber; @@ -348,6 +355,11 @@ export class SignPosition extends EventFactory { this.hideMoreTools(); this.initCrossToolsListeners(); this.dragSignature(); + let textExtra = $("#textExtra_" + this.currentSign); + textExtra.on("input", e => this.refreshExtraText(e)); + textExtra.on("click mouseup mousedown", function (e){ + e.stopPropagation(); + }); } lockCurrentSign() { @@ -398,12 +410,12 @@ export class SignPosition extends EventFactory { } changeSignSize(result) { - if(this.signImages[this.getCurrentSignParams().signImageNumber] != null) { + // if(this.signImages[this.getCurrentSignParams().signImageNumber] != null) { this.getCurrentSignParams().signWidth = Math.round((result.w + this.getCurrentSignParams().extraWidth) * this.getCurrentSignParams().signScale * this.fixRatio); this.getCurrentSignParams().signHeight = Math.round((result.h + this.getCurrentSignParams().extraHeight) * this.getCurrentSignParams().signScale * this.fixRatio); this.changeSignColor(Color.rgbToHex(this.getCurrentSignParams().red, this.getCurrentSignParams().green, this.getCurrentSignParams().blue)); this.updateSignSize(); - } + // } } getImageDimensions(file) { @@ -659,7 +671,7 @@ export class SignPosition extends EventFactory { this.borders.css("height", 150); this.getCurrentSignParams().signWidth = 150; this.getCurrentSignParams().signHeight = 75; - $("#displayMoreTools_0").hide(); + this.displayMoreToolsButton.hide(); $("#signUndo_0").hide(); } } @@ -853,7 +865,6 @@ export class SignPosition extends EventFactory { addCheckImage() { this.addSign(); this.changeToFaImage(1); - this.forceRemoveExtra(); } addTimesImage() { diff --git a/src/main/resources/static/js/modules/ui/signrequests/SignUi.js b/src/main/resources/static/js/modules/ui/signrequests/SignUi.js index e79b74286..a58722c14 100644 --- a/src/main/resources/static/js/modules/ui/signrequests/SignUi.js +++ b/src/main/resources/static/js/modules/ui/signrequests/SignUi.js @@ -4,13 +4,13 @@ import {Step} from "../../../prototypes/Step.js"; export class SignUi { - constructor(id, dataId, formId, currentSignRequestParams, signImageNumber, currentSignType, signable, postits, isPdf, currentStepNumber, currentStepId, currentStepMultiSign, workflow, signImages, userName, csrf, fields, stepRepeatable, status, profile, action) { + constructor(id, dataId, formId, currentSignRequestParams, signImageNumber, currentSignType, signable, postits, isPdf, currentStepNumber, currentStepId, currentStepMultiSign, workflow, signImages, userName, csrf, fields, stepRepeatable, status, action, nbSignRequests) { console.info("Starting sign UI"); + this.globalProperties = JSON.parse(sessionStorage.getItem("globalProperties")); this.signRequestId = id; this.percent = 0; this.getProgressTimer = null; this.wait = $('#wait'); - this.passwordError = document.getElementById("passwordError"); this.workspace = null; this.signForm = document.getElementById("signForm"); this.csrf = new CsrfToken(csrf); @@ -22,8 +22,10 @@ export class SignUi { this.stepRepeatable = stepRepeatable; this.currentStepNumber = currentStepNumber; this.gotoNext = false; - this.profile = profile; + this.certTypeSelect = $("#certType"); + this.nbSignRequests = nbSignRequests; this.initListeners(); + this.initReportModal(); } initListeners() { @@ -37,8 +39,44 @@ export class SignUi { $("#launchNoInfiniteSignButton").click(); } }); + if(this.certTypeSelect) { + this.certTypeSelect.on("change", e => this.togglePasswordField()); + } + $("#copyButton").on('click', e => this.copy()); - document.addEventListener("sign", e => this.updateWaitModal(e)); + // document.addEventListener("sign", e => this.updateWaitModal(e)); + } + + initReportModal() { + let self = this; + $.ajax({ + url: "/user/validation/short/" + self.signRequestId, + type: 'GET', + success: function (data, textStatus, xhr) { + let modal = "
    " + + "
    " + + "
    " + + "
    " + + data + + "
    "; + $("body").append(modal); + $('#reportModal').on('hidden.bs.modal', function () { + $("div[id^='report_']").each(function() { + $(this).show(); + }); + }) + $("#reportModalBtn").removeClass("d-none"); + } + }); + } + + togglePasswordField(){ + let value = $("#certType").val(); + if(value === "etab") { + $("#password").hide(); + } else { + $("#password").show(); + } } launchNoInfiniteSign() { @@ -91,7 +129,8 @@ export class SignUi { } if(this.workspace != null) { this.signRequestUrlParams = { - 'password' : document.getElementById("password").value, + 'password' : $("#password").val(), + 'certType' : $("#certType").val(), 'signRequestParams' : JSON.stringify(Array.from(this.workspace.signPosition.signRequestParamses.values())), 'visual' : this.workspace.signPosition.visualActive, 'comment' : this.signComment.val(), @@ -113,59 +152,37 @@ export class SignUi { url: "/user/signrequests/sign/" + this.signRequestId + "/?" + self.csrf.parameterName + "=" + self.csrf.token, type: 'POST', data: signRequestUrlParams, - error: function(e) { - bootbox.alert("La signature s'est terminée, d'une façon inattendue. La page va s'actualiser", function() { - document.location.reload(); - }); - } - }); - } - - updateWaitModal(e) { - console.info("update wait modal"); - let message = e.detail - console.info(message); - this.percent = this.percent + 5; - if(message.type === "sign_system_error" || message.type === "not_authorized") { - console.error("sign error : system error"); - document.getElementById("signError").style.display = "block"; - document.getElementById("signError").innerHTML =" Erreur du système de signature :
    " + message.text; - document.getElementById("closeModal").style.display = "block"; - document.getElementById("bar").classList.remove("progress-bar-animated"); - } else if(message.type === "initNexu") { - console.info("redirect to NexU sign proccess"); - document.location.href="/user/nexu-sign/" + this.signRequestId; - }else if(message.type === "end") { - console.info("sign end"); - document.getElementById("bar-text").innerHTML = ""; - document.getElementById("bar").classList.remove("progress-bar-animated"); - document.getElementById("bar-text").innerHTML = message.text; - document.getElementById("bar").style.width = 100 + "%"; - if (this.gotoNext) { - document.location.href = $("#nextSignRequestButton").attr('href'); - } else { - if(this.profile != null && this.profile === "dev") { - document.location.href = "/user/signrequests/" + this.signRequestId; + success: function(data, textStatus, xhr) { + if(self.gotoNext) { + document.location.href = $("#nextSignRequestButton").attr('href'); } else { - document.location.href = "/user/"; + if(self.nbSignRequests > 1 || !self.globalProperties.returnToHomeAfterSign) { + document.location.href = "/user/signrequests/" + self.signRequestId; + } else { + document.location.href = "/user/"; + } + } + }, + error: function(data, textStatus, xhr) { + if(data.responseText === "initNexu") { + document.location.href="/user/nexu-sign/" + self.signRequestId; + } else { + $("#signSpinner").hide(); + console.error("sign error : " + data.responseText); + document.getElementById("signError").style.display = "block"; + document.getElementById("signError").innerHTML = " Erreur du système de signature :
    " + data.responseText; + document.getElementById("closeModal").style.display = "block"; } } - } else { - console.debug("update bar"); - document.getElementById("bar").style.display = "block"; - document.getElementById("bar").style.width = this.percent + "%"; - document.getElementById("bar-text").innerHTML = message.text; - } + }); } reset() { this.percent = 0; - this.passwordError.style.display = "none"; + $("#signSpinner").show(); document.getElementById("signError").style.display = "none"; document.getElementById("closeModal").style.display = "none"; document.getElementById("validModal").style.display = "none"; - document.getElementById("bar").style.display = "none"; - document.getElementById("bar").classList.add("progress-bar-animated"); } redirect() { diff --git a/src/main/resources/static/js/modules/ui/signrequests/WorkspacePdf.js b/src/main/resources/static/js/modules/ui/signrequests/WorkspacePdf.js index 2aff5a867..996d69512 100644 --- a/src/main/resources/static/js/modules/ui/signrequests/WorkspacePdf.js +++ b/src/main/resources/static/js/modules/ui/signrequests/WorkspacePdf.js @@ -2,7 +2,6 @@ import {PdfViewer} from "../../utils/PdfViewer.js"; import {SignPosition} from "./SignPosition.js"; import {WheelDetector} from "../../utils/WheelDetector.js"; import {Message} from "../../../prototypes/Message.js"; -import {SseDispatcher} from "../../utils/SseDispatcher.js"; export class WorkspacePdf { @@ -32,7 +31,7 @@ export class WorkspacePdf { } } if (this.isPdf) { - this.pdfViewer = new PdfViewer('/user/signrequests/get-last-file/' + id, signable, currentStepNumber, currentStepId, this.forcePageNum, fields); + this.pdfViewer = new PdfViewer('/user/signrequests/get-last-file/' + id, signable, currentStepNumber, currentStepId, this.forcePageNum, fields, false); } this.signPosition = new SignPosition( signType, @@ -42,7 +41,6 @@ export class WorkspacePdf { userName, signable, this.forcePageNum); this.mode = 'sign'; this.wheelDetector = new WheelDetector(); - this.sseDispatcher = new SseDispatcher(); this.signLaunchButton = $("#signLaunchButton"); this.addSpotEnabled = false; this.addCommentEnabled = false; @@ -54,7 +52,12 @@ export class WorkspacePdf { this.initDataFields(fields); if ((formId == null && workflow == null) || (currentStepMultiSign !== null && currentStepMultiSign)) { $("#second-tools").toggleClass("d-none d-flex"); - $("#workspace").css("margin-top", "170px"); + if($("#ws-tabs").length) { + $("#workspace").css("margin-top", "212px"); + } else { + $("#workspace").css("margin-top", "170px"); + } + } } @@ -169,6 +172,9 @@ export class WorkspacePdf { this.pdfViewer.adjustZoom(); this.initLaunchButtons(); this.pdfViewer.removeEventListener('ready'); + if(this.signPosition.signImages.length === 0 && this.signType !== "visa" && this.signType !== "hiddenVisa") { + this.signPosition.toggleVisual(); + } } initLaunchButtons() { @@ -220,7 +226,6 @@ export class WorkspacePdf { message.type = "success"; message.text = "Modifications enregistrées"; message.object = null; - // self.sseDispatcher.dispatchEvent("user", message); dataId.val(response); if (redirect) { location.href = "/user/datas/" + response + "/update"; @@ -260,7 +265,7 @@ export class WorkspacePdf { if ((self.signPosition.cross.css("position") === 'fixed' || self.signPosition.getCurrentSignParams().xPos === -1) && self.signPosition.visualActive) { bootbox.alert("Merci de placer la signature", function () { self.pdfViewer.initSavedValues(); - if (self.currentSignRequestParams != null) { + if (self.currentSignRequestParams != null && self.currentSignRequestParams[0] != null) { self.pdfViewer.renderPage(self.currentSignRequestParams[0].signPageNumber); } self.signPosition.firstDrag = true; @@ -614,7 +619,7 @@ export class WorkspacePdf { this.signPosition.cross.removeClass('d-none'); } this.pdfViewer.rotation = 0; - if (this.currentSignRequestParams != null && this.currentSignRequestParams.length > 0) { + if (this.currentSignRequestParams != null && this.currentSignRequestParams.length > 0 && this.currentSignRequestParams[0] != null) { if (!this.forcePageNum) { this.pdfViewer.renderPage(1); let signPage = this.currentSignRequestParams[0].signPageNumber; diff --git a/src/main/resources/static/js/modules/utils/PdfViewer.js b/src/main/resources/static/js/modules/utils/PdfViewer.js index b9dfc53c4..0e5802779 100644 --- a/src/main/resources/static/js/modules/utils/PdfViewer.js +++ b/src/main/resources/static/js/modules/utils/PdfViewer.js @@ -367,16 +367,18 @@ export class PdfViewer extends EventFactory { let datePickerIndex = 40; console.debug("rending pdfForm items with fields" + items); let signFieldNumber = 0; + let self = this; for (let i = 0; i < items.length; i++) { if(items[i].fieldType === undefined) { if(items[i].title && items[i].title.toLowerCase().includes('sign')) { signFieldNumber = signFieldNumber + 1; $('.popupWrapper').remove(); let signField = $('section[data-annotation-id=' + items[i].id + '] > div'); + signField.addClass("sign-field") signField.append('Champ signature ' + signFieldNumber + '
    '); - signField.addClass("sign-field"); // signField.addClass("d-none"); // signField.parent().remove(); + } continue; } @@ -607,7 +609,7 @@ export class PdfViewer extends EventFactory { break; } } - return (isIncludeCurrentStep || (this.currentStepNumber === 0 && dataField.stepZero)) && this.signable + return (isIncludeCurrentStep || (this.currentStepNumber === 0 && dataField.stepZero));// && this.signable } renderPdfForm(items) { @@ -617,15 +619,27 @@ export class PdfViewer extends EventFactory { console.debug(">>Start compute item"); if(items[i].fieldType === undefined) { console.log(items[i]); - // if(items[i].title && items[i].title.toLowerCase().includes('sign')) { - // signFieldNumber = signFieldNumber + 1; - // $('.popupWrapper').remove(); - // let signField = $('section[data-annotation-id=' + items[i].id + '] > div'); - // signField.append('Champ signature ' + signFieldNumber + '
    '); - // signField.addClass("sign-field"); - // // signField.addClass("d-none"); - // // signField.parent().remove(); - // } + if(items[i].title && items[i].title.toLowerCase().includes('sign')) { + signFieldNumber = signFieldNumber + 1; + $('.popupWrapper').remove(); + let section = $('section[data-annotation-id=' + items[i].id +']'); + let signField = $('section[data-annotation-id=' + items[i].id + '] > div'); + signField.addClass("sign-field"); + signField.unbind(); + section.unbind(); + section.attr("id", signFieldNumber); + section.on('click', function () { + $("#reportModal").modal("show"); + $("div[id^='report_']").each(function() { + $(this).hide(); + }); + $("#report_" + $(this).attr("id")).show(); + }) + // signField.attr("data-toggle", "modal"); + // signField.attr("data-target", "#sign_" + self.signRequestId); + // signField.addClass("d-none"); + // signField.parent().remove(); + } continue; } let inputName = items[i].fieldName.split(/\$|#|!/)[0]; diff --git a/src/main/resources/static/js/modules/utils/SseDispatcher.js b/src/main/resources/static/js/modules/utils/SseDispatcher.js deleted file mode 100644 index 623506c11..000000000 --- a/src/main/resources/static/js/modules/utils/SseDispatcher.js +++ /dev/null @@ -1,13 +0,0 @@ -export class SseDispatcher { - - constructor() { - } - - dispatchEvent(type, message) { - console.info("dispatch event : " + type + " " + message); - let event = new CustomEvent(type); - event.initCustomEvent(type, false, false, message); - document.dispatchEvent(event); - } - -} \ No newline at end of file diff --git a/src/main/resources/static/js/modules/utils/SseSubscribe.js b/src/main/resources/static/js/modules/utils/SseSubscribe.js deleted file mode 100644 index 900eb33cb..000000000 --- a/src/main/resources/static/js/modules/utils/SseSubscribe.js +++ /dev/null @@ -1,53 +0,0 @@ -import {Message} from "../../prototypes/Message.js"; -import {EventFactory} from "./EventFactory.js"; -import {SseDispatcher} from "./SseDispatcher.js"; - -export class SseSubscribe extends EventFactory { - - constructor(sseId) { - console.info("Start SSE"); - super(); - this.sseId = sseId; - this.initSse(); - this.listenToEvent(); - this.sseDispatcher = new SseDispatcher(); - } - - initSse() { - console.info("connect to sse"); - let self = this; - this.eventSource = new EventSource('/sse/' + this.sseId); - this.eventSource.onopen = function(e) { - console.info("refresh sse : " + self.sseId); - } - window.onbeforeunload = function(e) { - console.info("close sse : " + self.sseId); - self.eventSource.close(); - }; - - } - - listenToEvent() { - console.info("subscribe to events"); - this.eventSource.addEventListener("global", response => { - console.info("receive global event"); - let message = new Message(JSON.parse(response.data)); - this.sseDispatcher.dispatchEvent("global", message); - }, false); - this.eventSource.addEventListener("user", response => { - console.info("receive user event"); - let message = new Message(JSON.parse(response.data)); - this.sseDispatcher.dispatchEvent("user", message); - }, false); - this.eventSource.addEventListener("sign", response => { - console.info("receive sign event"); - let message = new Message(JSON.parse(response.data)); - this.sseDispatcher.dispatchEvent("sign", message); - }, false); - this.eventSource.addEventListener("massSign", response => { - console.info("receive mass-sign event"); - let message = new Message(JSON.parse(response.data)); - this.sseDispatcher.dispatchEvent("massSign", message); - }, false); - } -} \ No newline at end of file diff --git a/src/main/resources/templates/admin/certificats/list.html b/src/main/resources/templates/admin/certificats/list.html new file mode 100644 index 000000000..0bce96d46 --- /dev/null +++ b/src/main/resources/templates/admin/certificats/list.html @@ -0,0 +1,93 @@ + + + + + + +
    +
    + + +
    +
    + +
    +
    +
    +
    Certificats
    + + + + + + + + + + + + + + + + + + + +
    NomDate d'expirationRolesActions
    +
    +
    +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/src/main/resources/templates/admin/currentsessions.html b/src/main/resources/templates/admin/currentsessions.html index 5209ff900..7e0d24df0 100644 --- a/src/main/resources/templates/admin/currentsessions.html +++ b/src/main/resources/templates/admin/currentsessions.html @@ -6,7 +6,7 @@
    - +
    +
    +
    + + + diff --git a/src/main/resources/templates/admin/dss/tl-info-country.html b/src/main/resources/templates/admin/dss/tl-info-country.html index c65228b35..5f4bb5861 100644 --- a/src/main/resources/templates/admin/dss/tl-info-country.html +++ b/src/main/resources/templates/admin/dss/tl-info-country.html @@ -1,43 +1,61 @@ - - - - - -
    -

    No data found for this country

    - -
    - -
    - - - - -

    -
    -
    -
    - -
    + + + +
    +
    + + +
    +
    +

    No data found for this country

    + +
    + +
    + + + + + +

    +
    +
    +
    + +
    -
    -
    -
    - -
    -
    -
    - -
    -
    -
    - +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + \ No newline at end of file diff --git a/src/main/resources/templates/admin/dss/tl-summary.html b/src/main/resources/templates/admin/dss/tl-summary.html index 6df5a7a59..17225c527 100644 --- a/src/main/resources/templates/admin/dss/tl-summary.html +++ b/src/main/resources/templates/admin/dss/tl-summary.html @@ -5,147 +5,166 @@
    - +
    - Voir les certificats -

    +
    + +
    +
    + +   + + + +
    +

     

    +

    +

    +   + link +

    +
    +
    +
    - -

    + + +
    - -

    - - - -   - -

    - - -
    - - -
    -

    -   - -

    - - - - - - - - - - - + + +   + + + + +
    + + +
    +

    +   + +

    +
    + + + + + + + + + + - - - - - - - - - - - - - - - - -
    - - - - - - - - - -
    -
    - - -
    -

    - - - - + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    + + +
    +

    + + + + - - - - - + -
    + +
    -
    - -
    -

    - - + +
    +

    +
    + - - - - - - + + +
    +
    +
    +
    + + - -
    -
    - - +
    \ No newline at end of file diff --git a/src/main/resources/templates/admin/export/list.html b/src/main/resources/templates/admin/export/list.html index 47f2cc1fc..035cd5602 100644 --- a/src/main/resources/templates/admin/export/list.html +++ b/src/main/resources/templates/admin/export/list.html @@ -7,7 +7,7 @@
    - +
diff --git a/src/main/resources/templates/fragments/new.html b/src/main/resources/templates/fragments/new.html index 3fedc2c8e..85a5a2775 100644 --- a/src/main/resources/templates/fragments/new.html +++ b/src/main/resources/templates/fragments/new.html @@ -7,7 +7,7 @@
Nouveau
-
+
Outils de signature
+
+

Champs

+
+
+ + + +
+
+ + +
+
+
+ + +
+ + diff --git a/src/main/resources/templates/managers/forms/list.html b/src/main/resources/templates/managers/forms/list.html new file mode 100644 index 000000000..47f68a87d --- /dev/null +++ b/src/main/resources/templates/managers/forms/list.html @@ -0,0 +1,188 @@ + + + + + + + +
+
+
+ + +
+
+ + + +
    +
  • +
  • +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
TitreNomVersionActifRôleAction
+ + + + + + + +
+
+
+ Aucun formulaire pour le moment +
+
+
+
+
+
+ +
+ + diff --git a/src/main/resources/templates/managers/forms/show.html b/src/main/resources/templates/managers/forms/show.html new file mode 100644 index 000000000..a8754e3e3 --- /dev/null +++ b/src/main/resources/templates/managers/forms/show.html @@ -0,0 +1,128 @@ + + + + + + + + +
+
+
+ + +
+
+ +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NomDescriptionTypeFavorisableRequisLecture seulePré-remplissageAuto-completionNom du serviceType de donnéesAttribut retournéÉtapes autorisées
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/managers/forms/update.html b/src/main/resources/templates/managers/forms/update.html new file mode 100644 index 000000000..ea2396370 --- /dev/null +++ b/src/main/resources/templates/managers/forms/update.html @@ -0,0 +1,160 @@ + + + + + + +
+
+
+ + +
+
+ + + +
+ +
+
+
+
+
+ Modele actuel : + +
+
+
+ + +
+
+ +
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+
+

Champs

+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/src/main/resources/templates/managers/managersRoles.html b/src/main/resources/templates/managers/managersRoles.html new file mode 100644 index 000000000..2a51764ae --- /dev/null +++ b/src/main/resources/templates/managers/managersRoles.html @@ -0,0 +1,65 @@ + + + + + +
+
+ + +
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
RolesManagers
+ + +
+ +
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/managers/workflows/cards/stepscard-light.html b/src/main/resources/templates/managers/workflows/cards/stepscard-light.html new file mode 100644 index 000000000..9dbeca147 --- /dev/null +++ b/src/main/resources/templates/managers/workflows/cards/stepscard-light.html @@ -0,0 +1,44 @@ + + + +
+
+
Etapes du circuit
+
+
+ +
+
+ +
+ +
+ +
    + +
  • + +
  • +
    +
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+ \ No newline at end of file diff --git a/src/main/resources/templates/managers/workflows/cards/stepscard.html b/src/main/resources/templates/managers/workflows/cards/stepscard.html new file mode 100644 index 000000000..aed1150fa --- /dev/null +++ b/src/main/resources/templates/managers/workflows/cards/stepscard.html @@ -0,0 +1,159 @@ + + + + +
+
+
+ +
+ +
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/managers/workflows/list.html b/src/main/resources/templates/managers/workflows/list.html new file mode 100644 index 000000000..86a46e1d5 --- /dev/null +++ b/src/main/resources/templates/managers/workflows/list.html @@ -0,0 +1,161 @@ + + + + + +
+
+
+ + +
+
+ + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
NomCréé parDestinatairesSuperviseur(s) du circuitActions
+
    + +
  • + + + + +
    +
    +
  • +
    +
+
+ Aucun + + + + +
+
+
+ + + + +
+
+
+
+
+
+ +
+ + diff --git a/src/main/resources/templates/managers/workflows/show-class.html b/src/main/resources/templates/managers/workflows/show-class.html new file mode 100644 index 000000000..95969551a --- /dev/null +++ b/src/main/resources/templates/managers/workflows/show-class.html @@ -0,0 +1,72 @@ + + + + + +
+
+
+ + +
+
+
Circuit :
+
+
+
Description
+
+
+
+
Circuit de signatures
+
+
+
+
Superviseur(s) du circuit
+
+
+ + +
+
+
+
+
+
+
Protocole pour la source des documents
+
+
+
+
+
Lien pour la source des documents
+
+
+
+
+
Protocole pour la destination des documents +
+
+
+
+
+
Lien pour la destination des documents
+
+
+
+
+
+ +
+
+ +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/managers/workflows/show.html b/src/main/resources/templates/managers/workflows/show.html new file mode 100644 index 000000000..abbb28d5d --- /dev/null +++ b/src/main/resources/templates/managers/workflows/show.html @@ -0,0 +1,161 @@ + + + + + + +
+
+
+ + +
+ + +
+
+
+
Circuit de signatures
+
+
+ Vous pouvez ajouter des étapes +
+
+
+ + +
+
+ +
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/managers/workflows/update.html b/src/main/resources/templates/managers/workflows/update.html new file mode 100644 index 000000000..ed65ae82a --- /dev/null +++ b/src/main/resources/templates/managers/workflows/update.html @@ -0,0 +1,199 @@ + + + + + + + + + +
+
+ + +
+
+ + + +
+ +
+
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + +
TypeURISupprimer + + +
+ + + + + +
+
+
+
+
+
+
+
+ + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/user/home/index.html b/src/main/resources/templates/user/home/index.html index 386b9d1a4..08a3b023c 100644 --- a/src/main/resources/templates/user/home/index.html +++ b/src/main/resources/templates/user/home/index.html @@ -16,7 +16,7 @@
- +