diff --git a/internal/lkgateway/keywizard.go b/internal/lkgateway/keywizard.go index 383f889..972bed2 100644 --- a/internal/lkgateway/keywizard.go +++ b/internal/lkgateway/keywizard.go @@ -105,6 +105,39 @@ type keyWizardData struct { Drives []flashDrive SuggestContour string // контур по сертификату в архиве: test3 (издатель «Тестовый УЦ») | prod | "" (не понятно) SuggestIssuer string + WantContour string // контур, для которого открыли установку (кнопка «Установить ключ» у канала) + InWizard bool // блок — на шаге мастера: формы возвращают туда же +} + +// keyWizView — данные блока установки ключа (want — контур, у которого нажали «Установить ключ»). +func (h *setupHandlers) keyWizView(want string, inWizard bool) *keyWizardData { + if h.keyWiz == nil { // обработчики без мастера ключа (тесты) + h.keyWiz = newKeyWizardState() + } + h.keyWiz.mu.Lock() + st := h.keyWiz + h.keyWiz.mu.Unlock() + d := &keyWizardData{State: st, InWizard: inWizard} + if want == "test3" || want == "prod" { + d.WantContour = want + } + if st.StagingID != "" && !st.Done { // список флешек — на шаге выбора носителя (архив уже загружен) + d.Drives = listFlashDrives() + d.SuggestContour, d.SuggestIssuer = keyArchiveContour(filepath.Join("/var/lib/bj/media/iso", st.StagingID)) + } + return d +} + +// keyWizBack — куда вернуться после действия: на шаг мастера (ret=wizard) или на отдельную страницу. +func keyWizBack(r *http.Request) string { + u := "/admin/setup/keywizard?" + if r.FormValue("ret") == "wizard" { + u = "/admin/wizard?step=8&" + } + if c := r.FormValue("want"); c == "test3" || c == "prod" { + u += "contour=" + c + "&" + } + return u } // keyArchiveContour — контур ключа по сертификатам в распакованном архиве (не самоподписанным — это УЦ): @@ -229,25 +262,17 @@ func (h *setupHandlers) renderKeyWizard(w http.ResponseWriter, r *http.Request) } h.keyWiz.mu.Lock() h.keyWiz.Flash = r.URL.Query().Get("flash") - st := h.keyWiz h.keyWiz.mu.Unlock() - // Список флешек нужен на шаге выбора носителя (когда архив уже загружен). - var drives []flashDrive - if st.StagingID != "" && !st.Done { - drives = listFlashDrives() - } - d := keyWizardData{page: nowPage("Установка ключа", "setup"), State: st, Drives: drives} - if st.StagingID != "" && !st.Done { - d.SuggestContour, d.SuggestIssuer = keyArchiveContour(filepath.Join("/var/lib/bj/media/iso", st.StagingID)) - } - render(w, h.tpl.a.keyWizard, d) + d := h.keyWizView(r.URL.Query().Get("contour"), false) + d.page = nowPage("Установка ключа", "setup") + render(w, h.tpl.a.keyWizard, *d) } func (h *setupHandlers) keyWizardReset(w http.ResponseWriter, r *http.Request) { h.keyWiz.mu.Lock() h.keyWiz.reset() h.keyWiz.mu.Unlock() - http.Redirect(w, r, "/admin/setup/keywizard", http.StatusSeeOther) + http.Redirect(w, r, strings.TrimRight(keyWizBack(r), "?&"), http.StatusSeeOther) } // keyWizardUpload — шаг 1: приём .7z + пароль, распаковка, инспекция. @@ -320,7 +345,7 @@ func (h *setupHandlers) keyWizardUpload(w http.ResponseWriter, r *http.Request) h.keyWiz.set(1, "active", "") h.keyWiz.mu.Unlock() - http.Redirect(w, r, "/admin/setup/keywizard", http.StatusSeeOther) + http.Redirect(w, r, strings.TrimRight(keyWizBack(r), "?&"), http.StatusSeeOther) } // keyWizardInstall — шаги 2-5: запись на флешку, справочник, проверка, готово. @@ -427,7 +452,7 @@ func (h *setupHandlers) keyWizardInstall(w http.ResponseWriter, r *http.Request) if dev { final += " Финальная проверка — тестовым документом роботу." } - http.Redirect(w, r, "/admin/setup/keywizard?flash="+urlQ(final), http.StatusSeeOther) + http.Redirect(w, r, keyWizBack(r)+"flash="+urlQ(final), http.StatusSeeOther) } // restartAndVerifyISH перезапускает Интеграционный шлюз и проверяет, что он @@ -635,7 +660,7 @@ func runKeymediaOp(ctx context.Context, action string, fields map[string]string) } func (h *setupHandlers) keyWizFlash(w http.ResponseWriter, r *http.Request, msg string) { - http.Redirect(w, r, "/admin/setup/keywizard?flash="+urlQ(msg), http.StatusSeeOther) + http.Redirect(w, r, keyWizBack(r)+"flash="+urlQ(msg), http.StatusSeeOther) } func fallback(s, def string) string { diff --git a/internal/lkgateway/onboarding_test.go b/internal/lkgateway/onboarding_test.go index f9108b5..c4c0e5d 100644 --- a/internal/lkgateway/onboarding_test.go +++ b/internal/lkgateway/onboarding_test.go @@ -758,3 +758,19 @@ func TestIgateInstallStatus(t *testing.T) { t.Fatalf("готово: %+v", st) } } + +// Установка ключа из мастера возвращает на шаг «Ключ подписи» (с выбранным контуром), со страницы — на страницу. +func TestKeyWizBack(t *testing.T) { + for form, want := range map[string]string{ + "ret=wizard&want=prod": "/admin/wizard?step=8&contour=prod&", + "ret=wizard": "/admin/wizard?step=8&", + "": "/admin/setup/keywizard?", + "want=evil": "/admin/setup/keywizard?", + } { + r := httptest.NewRequest(http.MethodPost, "/admin/setup/keywizard/reset", strings.NewReader(form)) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if got := keyWizBack(r); got != want { + t.Errorf("%q: %q, ожидалось %q", form, got, want) + } + } +} diff --git a/internal/lkgateway/setup.go b/internal/lkgateway/setup.go index 7a828ca..eab2644 100644 --- a/internal/lkgateway/setup.go +++ b/internal/lkgateway/setup.go @@ -185,6 +185,8 @@ type WizardData struct { AutoDone []string // шаги до текущего, уже выполненные без участия пользователя, — с пояснением Certs []cryptocli.Certificate Flash string + KeyWiz *keyWizardData // шаг «Ключ подписи»: установка ключа прямо в мастере + IgateOK bool // ИШ на сервере установлен (ключ ставится в его профиль) // главы мастера (wizchapters.go): путь этой установки, соседние шаги, шапка Path []int Chapters []WizChapter @@ -322,6 +324,9 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) { fd.InWizard = true d.Fancy = &fd } + if step == 8 { + d.KeyWiz, d.IgateOK = h.keyWizView(r.URL.Query().Get("contour"), true), igateInstalled() + } if step == 3 { d.Sub = depoWizardSub(r.URL.Query().Get("sub")) d.DepoUser, d.DepoPass = depoSetupLogin(s.Fancy), s.Fancy.Password diff --git a/internal/lkgateway/web/templates/admin_keywizard.html b/internal/lkgateway/web/templates/admin_keywizard.html index 8c00cc9..f0c5312 100644 --- a/internal/lkgateway/web/templates/admin_keywizard.html +++ b/internal/lkgateway/web/templates/admin_keywizard.html @@ -6,109 +6,7 @@ Загрузите архив НРД → запись на носитель → справочник сертификатов → проверка → готово -{{$s := .State}} - -{{/* ===== Лента шагов ===== */}} -
-
    - {{range $i, $step := $s.Steps}} -
  1. - - {{if eq $step.Status "ok"}}✓{{else if eq $step.Status "error"}}{{else}}{{add $i 1}}{{end}} - -
    -
    {{$step.Title}}
    - {{if $step.Detail}}
    {{$step.Detail}}
    {{end}} -
    -
  2. - {{end}} -
-
- -{{/* ===== Действие в зависимости от состояния ===== */}} -{{if $s.Done}} -
-

✓ Готово

-

Ключ установлен на флешку, справочник сертификатов сформирован, Валидата проверена.

- {{if $s.Backup}}

Бэкап прежнего носителя: {{$s.Backup}}

{{end}} -
- {{if devOn}}
- - -
{{else}}→ Вернуться в мастер настройки Проверить подключения{{end}} -
- -
-
-
-{{else if $s.StagingID}} - {{/* Архив загружен — выбор флешки + запись */}} -
-

Шаг 2 — выбор флешки и запись

-

Архив распакован. Ключ: {{fallbackTpl $s.VDK "—"}}. - Выберите носитель — запись сделает бэкап, запишет ключ и справочник - сертификатов, дотянет CRL и перезапустит ИШ.

-
- -
- - {{if .Drives}} - {{range $i, $d := .Drives}} - - {{end}} - {{else}} -

Съёмные носители не обнаружены — будет использован текущий ключевой носитель ИШ по умолчанию.

- {{end}} -
- -
- - {{$c := .SuggestContour}}{{if not $c}}{{$c = currentContour}}{{end}} - {{if .SuggestIssuer}}

В архиве сертификат от «{{.SuggestIssuer}}» — это {{if eq .SuggestContour "test3"}}тестовый{{else}}боевой{{end}} ключ.

{{end}} - - - У тестового и боевого контуров разные ключи: ключ подключается только к каналу ИШ своего контура. -
- -
- - - Пусто = имя берётся из архива автоматически. -
- - -
-
- -
-
-{{else}} - {{/* Начало — форма загрузки */}} -
-

Шаг 1 — загрузка архива

-

Выберите .7z-архив с ключом от НРД и введите пароль архива.

-
- - - -
-
-{{end}} +{{template "keyWizBody" .}}

← Назад к настройкам

diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index bfde931..5cb048e 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -86,6 +86,13 @@ .dq-jobname { margin-top:14px; max-width:520px; } .dq-jobname label { display:block; margin:0; font-size:13.5px; font-weight:500; color:var(--text-2); } .dq-jobname input { display:block; width:100%; box-sizing:border-box; margin-top:5px; } +/* установка ключа (общий блок part_keywiz) — в виде мастера */ +.keywiz ol { gap:4px !important; } +.keywiz ol li > div > div:first-child { font-weight:500 !important; font-size:14.5px; } +.keywiz ol li > span:first-child { flex-basis:24px !important; height:24px !important; font-size:12px !important; } +.keywiz .card h2 { font-size:16px; margin:0 0 6px; } +.keywiz form .btn-ok, .keywiz form .btn-secondary { justify-self:start; border-radius:999px; padding:10px 22px; font-weight:600; } +.keywiz form .btn-ok { background:var(--accent); border-color:var(--accent); } .dq-box { border:1px solid var(--border); border-radius:12px; padding:14px 16px 16px; } .dq-box-bj { background:color-mix(in srgb, var(--accent-weak) 45%, transparent); } .dq-box-h { font-weight:600; font-size:14.5px; margin:0 0 10px; } @@ -556,8 +563,8 @@

Ключ и сертификаты

Ключ электронной подписи организации для обмена с НРД. Его выдаёт Удостоверяющий центр Московской биржи: вам передадут архив ключа и пароль.

- {{if not .Done.NSD}}
Ключ ставится в шлюз НРД — сначала установите шлюз.
{{end}} -

{{if .Certs}}Установить другой ключ{{else}}Установить ключ{{end}} архив → носитель → справочник → проверка подписи

+ {{if not .IgateOK}}
Ключ ставится в шлюз НРД — сначала установите шлюз.
+ {{else if .KeyWiz}}
{{template "keyWizBody" .KeyWiz}}
{{end}}
Сертификаты НРД diff --git a/internal/lkgateway/web/templates/part_igate.html b/internal/lkgateway/web/templates/part_igate.html index e0abaa9..18b4993 100644 --- a/internal/lkgateway/web/templates/part_igate.html +++ b/internal/lkgateway/web/templates/part_igate.html @@ -93,7 +93,7 @@ s.push(ch.profile?ok('ключ: профиль '+ch.profile):bad('ключ этого контура не установлен')); s.push(ch.enabled?ok('канал включён'):'канал выключен до установки ключа'); a.push(''); - if(!ch.profile) a.push('Установить ключ'); + if(!ch.profile) a.push('Установить ключ'); if(ch.enabled && !act) a.push(x[0]==='prod'?'':''); } return ''+x[1]+''+(act?'
● активный':'')+''+s.join('
')+(a.length?'
'+a.join('')+'
':'')+''; diff --git a/internal/lkgateway/web/templates/part_keywiz.html b/internal/lkgateway/web/templates/part_keywiz.html new file mode 100644 index 0000000..4d8eec7 --- /dev/null +++ b/internal/lkgateway/web/templates/part_keywiz.html @@ -0,0 +1,111 @@ +{{/* Установка ключа Валидаты: загрузка архива НРД → носитель → справочник → проверка ИШ. Один и тот же блок — + на шаге «Ключ подписи» мастера и на отдельной странице /admin/setup/keywizard (данные — keyWizardData). */}} +{{define "keyWizRet"}}{{if .InWizard}}{{end}}{{if .WantContour}}{{end}}{{end}} + +{{define "keyWizBody"}} +{{$s := .State}} + +{{/* ===== Лента шагов ===== */}} +
+
    + {{range $i, $step := $s.Steps}} +
  1. + + {{if eq $step.Status "ok"}}✓{{else if eq $step.Status "error"}}{{else}}{{add $i 1}}{{end}} + +
    +
    {{$step.Title}}
    + {{if $step.Detail}}
    {{$step.Detail}}
    {{end}} +
    +
  2. + {{end}} +
+
+ +{{/* ===== Действие в зависимости от состояния ===== */}} +{{if $s.Done}} +
+

✓ Готово

+

Ключ установлен на флешку, справочник сертификатов сформирован, Валидата проверена.

+ {{if $s.Backup}}

Бэкап прежнего носителя: {{$s.Backup}}

{{end}} +
+ {{if devOn}}
+ + +
{{else if not .InWizard}}→ Вернуться в мастер настройки Проверить подключения{{end}} +
{{template "keyWizRet" .}} + +
+
+
+{{else if $s.StagingID}} + {{/* Архив загружен — выбор флешки + запись */}} +
+

Шаг 2 — выбор флешки и запись

+

Архив распакован. Ключ: {{fallbackTpl $s.VDK "—"}}. + Выберите носитель — запись сделает бэкап, запишет ключ и справочник + сертификатов, дотянет CRL и перезапустит ИШ.

+
+ {{template "keyWizRet" .}} + +
+ + {{if .Drives}} + {{range $i, $d := .Drives}} + + {{end}} + {{else}} +

Съёмные носители не обнаружены — будет использован текущий ключевой носитель ИШ по умолчанию.

+ {{end}} +
+ +
+ + {{$c := .SuggestContour}}{{if not $c}}{{$c = .WantContour}}{{end}}{{if not $c}}{{$c = currentContour}}{{end}} + {{if .SuggestIssuer}}

В архиве сертификат от «{{.SuggestIssuer}}» — это {{if eq .SuggestContour "test3"}}тестовый{{else}}боевой{{end}} ключ.

{{end}} + + + У тестового и боевого контуров разные ключи: ключ подключается только к каналу ИШ своего контура. +
+ +
+ + + Пусто = имя берётся из архива автоматически. +
+ + +
+
{{template "keyWizRet" .}} + +
+
+{{else}} + {{/* Начало — форма загрузки */}} +
+

Шаг 1 — загрузка архива

+

Выберите .7z-архив с ключом от НРД и введите пароль архива.

+
+ {{template "keyWizRet" .}} + + + +
+
+{{end}} +{{end}}