From 6617e42343ea39603b90e18b887fed2a88bdd2b8 Mon Sep 17 00:00:00 2001 From: Zaripov Emil Date: Thu, 23 Jan 2025 10:44:05 +0300 Subject: [PATCH 01/41] set version 1.9.8-SNAPSHOT --- backend/pom.xml | 2 +- distribution/pom.xml | 2 +- frontend/pom.xml | 2 +- pom.xml | 2 +- resources/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index 7bb53446..86251230 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -5,7 +5,7 @@ ru.micord.ervu.lkrp ul - 1.9.7 + 1.9.8-SNAPSHOT ru.micord.ervu.lkrp.ul backend diff --git a/distribution/pom.xml b/distribution/pom.xml index 7c7a0cfa..d316cc1a 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -4,7 +4,7 @@ ru.micord.ervu.lkrp ul - 1.9.7 + 1.9.8-SNAPSHOT ru.micord.ervu.lkrp.ul diff --git a/frontend/pom.xml b/frontend/pom.xml index 3fc63c6d..a52f9155 100644 --- a/frontend/pom.xml +++ b/frontend/pom.xml @@ -4,7 +4,7 @@ ru.micord.ervu.lkrp ul - 1.9.7 + 1.9.8-SNAPSHOT ru.micord.ervu.lkrp.ul diff --git a/pom.xml b/pom.xml index b1f682c0..52bed811 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ru.micord.ervu.lkrp ul - 1.9.7 + 1.9.8-SNAPSHOT pom backend diff --git a/resources/pom.xml b/resources/pom.xml index e0b2f38a..4dd0d0e3 100644 --- a/resources/pom.xml +++ b/resources/pom.xml @@ -4,7 +4,7 @@ ru.micord.ervu.lkrp ul - 1.9.7 + 1.9.8-SNAPSHOT ru.micord.ervu.lkrp.ul From ecec2d595de2c83135325add310b4ea0b350df51 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 23 Jan 2025 11:19:24 +0300 Subject: [PATCH 02/41] SUPPORT-8783: add landing --- .../template/app/component/app_header.html | 8 +- .../template/app/component/home_landing.html | 82 ++++++++++ .../src/resources/template/webbpm/webbpm.html | 13 +- .../src/ts/modules/app/app-routing.module.ts | 8 +- frontend/src/ts/modules/app/app.module.ts | 4 +- .../app/component/app-header.component.ts | 11 +- .../app/component/home-landing.component.ts | 49 ++++++ .../ts/modules/security/guard/auth.guard.ts | 154 +++++++++++------- .../webbpm/component/webbpm.component.ts | 9 +- 9 files changed, 268 insertions(+), 70 deletions(-) create mode 100644 frontend/src/resources/template/app/component/home_landing.html create mode 100644 frontend/src/ts/modules/app/component/home-landing.component.ts diff --git a/frontend/src/resources/template/app/component/app_header.html b/frontend/src/resources/template/app/component/app_header.html index 010761f7..31d44586 100644 --- a/frontend/src/resources/template/app/component/app_header.html +++ b/frontend/src/resources/template/app/component/app_header.html @@ -1,10 +1,14 @@ diff --git a/frontend/src/resources/template/app/component/home_landing.html b/frontend/src/resources/template/app/component/home_landing.html new file mode 100644 index 00000000..4f02f274 --- /dev/null +++ b/frontend/src/resources/template/app/component/home_landing.html @@ -0,0 +1,82 @@ +
+
+
Личный кабинет для ведения воинского учета в организациях
+
+
+
Кому доступен личный кабинет?
+
Организациям, за ĸоторыми заĸреплена обязанность по осуществлению воинсĸого учета в соответствии с ФЗ от 28.03.1998 No 53-ФЗ
+
+
+
Для чего нужен личный кабинет?
+
Для своевременной и оперативной передачи сведений в военĸоматы в элеĸтронном виде
+
+
+
+
+
+
+
+
Как получить доступ к Личному кабинету?
+
+
+
+
Необходимо авторизоваться
+
Потребуется подтвержденная учетная запись организации
+
Доступ предоставляется тольĸо сотрудниĸу, наделенному соответствующими полномочиями (ролью) на ведения воинсĸого учета внутри организации
+
+ +
+
+
+
+
Какие виды сведений доступны для отправки?
+
+
+
+
+ Сведения о приеме на работу (увольнении), зачислении в образовательную организацию (отчислении) +
Срок передачи сведений: до 5 дней
+
+
+ Изменения сведений сотрудников, необходимых для ведения воинского учета +
Срок передачи сведений: до 5 дней
+
+
+ Сообщение о гражданах, не состоящих, но обязанных состоять на воинском учете +
Срок передачи сведений: до 3 дней
+
+
+
+
+
+
+ Ежегодное предоставление списка граждан мужского пола, подлежащих первоначальной постановке на воинский учет в год достижения ими возраста 17 лет +
Срок передачи сведений: ежегодно, в срок до 1 ноября
+
+
+ Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету +
Срок передачи сведений: ежегодно, по согласованию с военкоматом
+
+
+
+
+
+
+
Как передавать сведения?
+
+
Войдите в личный ĸабинет организации через Госуслуги
+
Подготовьте файл-csv с данными в соответствии с форматом в личном кабинете организации
+
Убедитесь, что все данные в файле введены ĸорреĸтно
+
Выберите необходимый вид сведений и загрузите файл
+
Следите за статусом приема
+
+
+
+
+ Если в файле будут ошибĸи, данные не будут приняты Реестром и выгрузĸу сведений придется повторить +
+
+
+
\ No newline at end of file diff --git a/frontend/src/resources/template/webbpm/webbpm.html b/frontend/src/resources/template/webbpm/webbpm.html index f3f9cb3c..0ad0ac4e 100644 --- a/frontend/src/resources/template/webbpm/webbpm.html +++ b/frontend/src/resources/template/webbpm/webbpm.html @@ -1,8 +1,19 @@
-
+
+
+
+

+ Доступ к личному кабинету для юридических лиц осуществляется исключительно через браузеры Яндекс или Chromium gost. +

+

+ Пожалуйста, попробуйте снова, выбрав один из этих браузеров. +

+
+
+
diff --git a/frontend/src/ts/modules/app/app-routing.module.ts b/frontend/src/ts/modules/app/app-routing.module.ts index d31e3ac5..a98f835f 100644 --- a/frontend/src/ts/modules/app/app-routing.module.ts +++ b/frontend/src/ts/modules/app/app-routing.module.ts @@ -3,6 +3,7 @@ import {RouterModule, Routes} from "@angular/router"; import {AccessDeniedComponent} from "./component/access-denied.component"; import {AuthGuard} from "../security/guard/auth.guard"; import {ConfirmExitGuard} from "@webbpm/base-package"; +import {HomeLandingComponent} from "./component/home-landing.component"; const appRoutes: Routes = [ { @@ -19,7 +20,12 @@ const appRoutes: Routes = [ path: 'filesentlog', loadChildren: 'generated-sources/page-filesentlog.module#PagefilesentlogModule', canActivate: [AuthGuard], - } + }, + { + path: 'home-landing', + component: HomeLandingComponent, + canActivate: [ConfirmExitGuard], + }, ]; diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts index 4f1ac1b3..f6c56163 100644 --- a/frontend/src/ts/modules/app/app.module.ts +++ b/frontend/src/ts/modules/app/app.module.ts @@ -26,6 +26,7 @@ import {ErvuFileUpload} from "../../ervu/component/fileupload/ErvuFileUpload"; import {InMemoryStaticGrid} from "../../ervu/component/grid/InMemoryStaticGrid"; import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFileButton"; import {AuthenticationService} from "../security/authentication.service"; +import {HomeLandingComponent} from "./component/home-landing.component"; registerLocaleData(localeRu); export const DIRECTIVES = [ @@ -37,7 +38,8 @@ export const DIRECTIVES = [ forwardRef(() => AppProgressIndicationComponent), forwardRef(() => ErvuFileUpload), forwardRef(() => ErvuDownloadFileButton), - forwardRef(() => InMemoryStaticGrid) + forwardRef(() => InMemoryStaticGrid), + forwardRef(() => HomeLandingComponent), ]; export function checkAuthentication(authService: AuthenticationService): () => Promise { diff --git a/frontend/src/ts/modules/app/component/app-header.component.ts b/frontend/src/ts/modules/app/component/app-header.component.ts index de4f18ef..a459d06f 100644 --- a/frontend/src/ts/modules/app/component/app-header.component.ts +++ b/frontend/src/ts/modules/app/component/app-header.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, Component} from "@angular/core"; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from "@angular/core"; import {Router} from "@angular/router"; @Component({ @@ -8,7 +8,14 @@ import {Router} from "@angular/router"; changeDetection: ChangeDetectionStrategy.OnPush }) export class AppHeaderComponent { + public isLanding: boolean = false; - constructor(protected router: Router) { + constructor(protected router: Router, + private cd: ChangeDetectorRef) { + this.isLanding = window.location.href.endsWith('home-landing'); + router.events.subscribe((event) => { + this.isLanding = window.location.href.endsWith('home-landing'); + this.cd.markForCheck(); + }) } } diff --git a/frontend/src/ts/modules/app/component/home-landing.component.ts b/frontend/src/ts/modules/app/component/home-landing.component.ts new file mode 100644 index 00000000..6d8348b3 --- /dev/null +++ b/frontend/src/ts/modules/app/component/home-landing.component.ts @@ -0,0 +1,49 @@ +import {ChangeDetectorRef, Component, OnInit} from "@angular/core"; +import {CadesHelper} from "@webbpm/base-package"; + +@Component({ + moduleId: module.id, + selector: "home-landing", + templateUrl: "../../../../../src/resources/template/app/component/home_landing.html", + }) +export class HomeLandingComponent implements OnInit { + + constructor(private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.checkCsp() + .then((cspCheckPassed) => { + if (this.checkBrowser() && cspCheckPassed) { + document.getElementById("browser-check-info").hidden = true; + } + }); + this.cd.markForCheck(); + } + + + private checkBrowser(): boolean { + const userAgent = navigator.userAgent; + return userAgent.indexOf("Chromium GOST") > -1 || userAgent.indexOf("YaBrowser") > -1; + } + + /** + * Проверка работы CSP через CadesPlugin (асинхронно) + */ + private async checkCsp(): Promise { + try { + if (!CadesHelper.plugin()) { + console.error("Ошибка: cadesplugin не найден!"); + return false; + } + await CadesHelper.plugin(); + const oAbout = await CadesHelper.plugin().CreateObjectAsync("CAdESCOM.About"); + const cspVersion = await oAbout.CSPVersion(); + let cspVersionStr = (await cspVersion.MajorVersion) + "." + (await cspVersion.MinorVersion) + "." + (await cspVersion.BuildVersion); + return !!cspVersionStr; + } catch (error) { + console.error("Ошибка при получении версии CSP:", error); + return false; + } + } +} diff --git a/frontend/src/ts/modules/security/guard/auth.guard.ts b/frontend/src/ts/modules/security/guard/auth.guard.ts index 88c5df10..35c5d48f 100644 --- a/frontend/src/ts/modules/security/guard/auth.guard.ts +++ b/frontend/src/ts/modules/security/guard/auth.guard.ts @@ -5,6 +5,7 @@ import {HttpClient, HttpParams} from "@angular/common/http"; import {MessagesService} from "@webbpm/base-package"; import {AuthenticationService} from "../authentication.service"; import {EsiaErrorDetail} from "../EsiaErrorDetail"; +import {CadesHelper} from "@webbpm/base-package"; @Injectable({providedIn:'root'}) export abstract class AuthGuard implements CanActivate { @@ -21,68 +22,79 @@ export abstract class AuthGuard implements CanActivate { state: RouterStateSnapshot): Observable | Promise | boolean { let url = new URL(window.location.href); if (!this.checkBrowser()) { - window.open(url.origin + url.pathname + "home.html", "_self"); + this.router.navigateByUrl('/home-landing'); return false; } - let hasAccess: Promise | boolean = this.checkAccess(); - - return Promise.resolve(hasAccess).then((isAccess) => { - let params = new URLSearchParams(url.search); - let code = params.get('code'); - let error = params.get('error'); - let errorDescription = params.get('error_description'); - if (isAccess) { - return true; - } - if (code || error) { - const params = new HttpParams().set('code', code).set('error', error); - this.httpClient.get("esia/auth", - { - params: params, responseType: 'text', observe: 'response', headers: { - "Error-intercept-skip": "true" - } - }) - .toPromise() - .then( - (response) => { - window.open(url.origin + url.pathname, "_self"); - }) - .catch((reason) => { - let errorMessage = reason.error.messages != null - ? reason.error.messages - : reason.error.replaceAll('\\', ''); - if (error) { - errorMessage = 'Произошла неизвестная ошибка. Обратитесь к системному администратору'; - let errorCode = this.extractCode(errorDescription); - if (errorCode) { - errorMessage = EsiaErrorDetail.getDescription(errorCode); - } - let consoleError = error + ', error description = ' + errorDescription; - this.messageService.error(errorMessage); - console.error(consoleError); - } - else { - this.messageService.error(errorMessage); - console.error(reason); - } - }); - return false; - } - else { - return this.httpClient.get("esia/url") - .toPromise() - .then(url => { - window.open(url, "_self"); - return true; - }).catch((reason)=> { - console.error(reason); - return false; - }); - } - }).catch((reason) => { - console.error(reason); - return false - }); + return this.checkCsp() + .then((cspCheckPassed) => { + if (!cspCheckPassed) { + this.router.navigateByUrl('/home-landing'); + return Promise.reject("Navigation on /home-landing") + } + return Promise.resolve(this.checkAccess()); + }).then((isAccess) => { + let params = new URLSearchParams(url.search); + let code = params.get('code'); + let error = params.get('error'); + let errorDescription = params.get('error_description'); + if (isAccess) { + return true; + } + if (code || error) { + const params = new HttpParams().set('code', code).set('error', error); + this.httpClient.get("esia/auth", + { + params: params, + responseType: 'text', + observe: 'response', + headers: { + "Error-intercept-skip": "true" + } + }) + .toPromise() + .then( + (response) => { + window.open(url.origin + url.pathname, "_self"); + }) + .catch((reason) => { + let errorMessage = reason.error.messages != null + ? reason.error.messages + : reason.error.replaceAll('\\', ''); + if (error) { + errorMessage = + 'Произошла неизвестная ошибка. Обратитесь к системному администратору'; + let errorCode = this.extractCode(errorDescription); + if (errorCode) { + errorMessage = EsiaErrorDetail.getDescription(errorCode); + } + let consoleError = error + ', error description = ' + errorDescription; + this.messageService.error(errorMessage); + console.error(consoleError); + } + else { + this.messageService.error(errorMessage); + console.error(reason); + } + }); + return false; + } + else { + return this.httpClient.get("esia/url") + .toPromise() + .then(url => { + window.open(url, "_self"); + return true; + }).catch((reason) => { + console.error(reason); + return false; + }); + } + }).catch((reason) => { + if (reason !== 'Navigation on /home-landing') { + console.error(reason); + } + return false + }); } private checkAccess(): boolean { @@ -99,4 +111,24 @@ export abstract class AuthGuard implements CanActivate { const userAgent = navigator.userAgent; return userAgent.indexOf("Chromium GOST") > -1 || userAgent.indexOf("YaBrowser") > -1; } -} + + /** + * Проверка работы CSP через CadesPlugin (асинхронно) + */ + private async checkCsp(): Promise { + try { + if (!CadesHelper.plugin()) { + console.error("Ошибка: cadesplugin не найден!"); + return false; + } + await CadesHelper.plugin(); + const oAbout = await CadesHelper.plugin().CreateObjectAsync("CAdESCOM.About"); + const cspVersion = await oAbout.CSPVersion(); + let cspVersionStr = (await cspVersion.MajorVersion) + "." + (await cspVersion.MinorVersion) + "." + (await cspVersion.BuildVersion); + return !!cspVersionStr; + } catch (error) { + console.error("Ошибка при получении версии CSP:", error); + return false; + } + } +} \ No newline at end of file diff --git a/frontend/src/ts/modules/webbpm/component/webbpm.component.ts b/frontend/src/ts/modules/webbpm/component/webbpm.component.ts index df6a67a0..87a9be43 100644 --- a/frontend/src/ts/modules/webbpm/component/webbpm.component.ts +++ b/frontend/src/ts/modules/webbpm/component/webbpm.component.ts @@ -1,4 +1,4 @@ -import {Component} from "@angular/core"; +import {ChangeDetectorRef, Component} from "@angular/core"; import { Event, NavigationCancel, @@ -17,10 +17,14 @@ import {ProgressIndicationService} from "@webbpm/base-package"; export class WebbpmComponent { public headerVisible: boolean = true; public footerVisible: boolean = true; + public isLanding: boolean = false; constructor(private router: Router, - private progressIndicationService: ProgressIndicationService) { + private progressIndicationService: ProgressIndicationService, + private cd: ChangeDetectorRef) { + this.isLanding = window.location.href.endsWith('home-landing'); router.events.subscribe((event: Event) => { + this.isLanding = window.location.href.endsWith('home-landing'); if (event instanceof NavigationStart) { progressIndicationService.showProgressBar(); } @@ -29,6 +33,7 @@ export class WebbpmComponent { || event instanceof NavigationCancel) { progressIndicationService.hideProgressBar(); } + this.cd.markForCheck(); }) } } From e9684ab64e7460a92a37f0438edba731a5c21c6c Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 23 Jan 2025 11:44:40 +0300 Subject: [PATCH 03/41] SUPPORT-8783: Fix --- frontend/src/resources/template/webbpm/webbpm.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/resources/template/webbpm/webbpm.html b/frontend/src/resources/template/webbpm/webbpm.html index 0ad0ac4e..c2799ed9 100644 --- a/frontend/src/resources/template/webbpm/webbpm.html +++ b/frontend/src/resources/template/webbpm/webbpm.html @@ -6,10 +6,10 @@

- Доступ к личному кабинету для юридических лиц осуществляется исключительно через браузеры Яндекс или Chromium gost. + Доступ к личному кабинету для юридических лиц осуществляется исключительно через браузеры Яндекс или Chromium gost, а также необходимо наличие установленного приложения КриптоПро CSP.

- Пожалуйста, попробуйте снова, выбрав один из этих браузеров. + Пожалуйста, попробуйте снова, выбрав один из этих браузеров и установив КриптоПро CSP.

From bc749d462d720a9c533014de7e3aec0d5a767cb7 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 23 Jan 2025 13:07:18 +0300 Subject: [PATCH 04/41] SUPPORT-8783: Fix (cherry picked from commit bbffa1b9043334984f7067f90bbc10c582763b87) --- frontend/src/resources/template/app/component/app_header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/resources/template/app/component/app_header.html b/frontend/src/resources/template/app/component/app_header.html index 31d44586..c7f633b0 100644 --- a/frontend/src/resources/template/app/component/app_header.html +++ b/frontend/src/resources/template/app/component/app_header.html @@ -1,5 +1,5 @@