From 2999eaf189309e5aa624d9d12798eea997186225 Mon Sep 17 00:00:00 2001 From: zuevav Date: Mon, 5 Oct 2026 13:21:51 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BA=D1=80=D0=B8=D0=BF=D1=82=20=D1=81?= =?UTF-8?q?=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BB=D0=BE=D0=B3?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0=20DEPO:=20=D0=BF=D0=B0=D1=80=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=20=D0=B8=D0=B7=20=D0=BF=D0=BE=D0=BB=D1=8F=20=D0=BC=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D1=80=D0=B0=20=D0=BF=D0=BE=D0=B4=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=20(=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B0=D1=91=D1=82=D1=81=D1=8F=20=D1=82=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=BE=20=D0=B2=20=D1=82=D0=B5=D0=BB=D0=B5=20POST,?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=D1=81=D1=8F,=20=D0=BA=D0=B0=D0=B2=D1=8B=D1=87?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=D0=B8=D1=80=D1=83?= =?UTF-8?q?=D1=8E=D1=82=D1=81=D1=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi --- internal/lkgateway/depologin.go | 32 +++++++++++++------ internal/lkgateway/depologin_test.go | 18 +++++++++-- .../lkgateway/web/templates/admin_wizard.html | 32 +++++++++++++------ 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/internal/lkgateway/depologin.go b/internal/lkgateway/depologin.go index 5541eb8..a9c766c 100644 --- a/internal/lkgateway/depologin.go +++ b/internal/lkgateway/depologin.go @@ -10,26 +10,32 @@ import ( "strings" ) -// depoLoginScript — скрипт создания логина bj. pg — PostgreSQL; db — база DEPO; login — имя логина. -func depoLoginScript(pg bool, db, login string) string { +// depoLoginScript — скрипт создания логина bj. pg — PostgreSQL; db — база DEPO; login — имя логина; +// pass — пароль из поля мастера (пусто — заглушка, администратор впишет сам). Пароль только вставляется +// в текст скрипта, нигде не сохраняется. +func depoLoginScript(pg bool, db, login, pass string) string { if !validDBName(db) { db = depoDefaultDatabase } if !depoLoginRe.MatchString(login) { login = "bj_user" } + pw := "ЗАМЕНИТЕ_НА_ПАРОЛЬ" + if pass != "" { + pw = strings.ReplaceAll(pass, "'", "''") // строковый литерал SQL: одинарная кавычка удваивается + } var b strings.Builder if pg { fmt.Fprintf(&b, `-- Bridge&Join: роль (логин) для bj в базе DEPO. PostgreSQL 10 и новее. -- Выполнить суперпользователем или владельцем базы, например: -- psql -h <сервер> -U postgres -f bj-depo-login-postgres.sql --- Пароль замените на свой и передайте его тому, кто настраивает bj (в мастере, шаг «Подключение»). +-- Пароль — из поля мастера; если там пусто, замените ЗАМЕНИТЕ_НА_ПАРОЛЬ на свой. Скрипт с паролем не храните. -- Права: только подключение и чтение учётных таблиц DEPO. Запись — только в таблицы обмена FTV_M2M*, -- её выдаёт скрипт таблиц на следующем шаге мастера. DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '%[2]s') THEN - CREATE ROLE "%[2]s" LOGIN PASSWORD 'ЗАМЕНИТЕ_НА_ПАРОЛЬ'; + CREATE ROLE "%[2]s" LOGIN PASSWORD '%[3]s'; END IF; END $$; @@ -39,12 +45,12 @@ GRANT CONNECT ON DATABASE "%[1]s" TO "%[2]s"; GRANT USAGE ON SCHEMA public TO "%[2]s"; GRANT SELECT ON ALL TABLES IN SCHEMA public TO "%[2]s"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "%[2]s"; -`, db, login) +`, db, login, pw) return b.String() } fmt.Fprintf(&b, `-- Bridge&Join: логин для bj в базе DEPO. Microsoft SQL Server. -- Выполнить в SSMS или sqlcmd под логином с правами sysadmin (или securityadmin + db_owner в базе %[1]s). --- Пароль замените на свой и передайте его тому, кто настраивает bj (в мастере, шаг «Подключение»). +-- Пароль — из поля мастера; если там пусто, замените ЗАМЕНИТЕ_НА_ПАРОЛЬ на свой. Скрипт с паролем не храните. -- Права: только чтение учётных таблиц DEPO (db_datareader). Запись — только в таблицы обмена FTV_M2M*, -- её выдаёт скрипт таблиц на следующем шаге мастера. Скрипт можно запускать повторно. @@ -52,7 +58,7 @@ USE [master]; GO IF SUSER_ID(N'%[2]s') IS NULL - CREATE LOGIN [%[2]s] WITH PASSWORD = N'ЗАМЕНИТЕ_НА_ПАРОЛЬ', CHECK_POLICY = ON, DEFAULT_DATABASE = [%[1]s]; + CREATE LOGIN [%[2]s] WITH PASSWORD = N'%[3]s', CHECK_POLICY = ON, DEFAULT_DATABASE = [%[1]s]; GO USE [%[1]s]; @@ -69,7 +75,7 @@ GO IF IS_ROLEMEMBER(N'db_datareader', N'%[2]s') = 0 ALTER ROLE db_datareader ADD MEMBER [%[2]s]; GO -`, db, login) +`, db, login, pw) return b.String() } @@ -77,7 +83,8 @@ GO // Без параметров — по сохранённым настройкам; download=0 — показать текстом (предпросмотр в мастере). func (h *setupHandlers) fancyDepoLoginScript(w http.ResponseWriter, r *http.Request) { s := h.rc.Snapshot().Fancy - q := r.URL.Query() + _ = r.ParseForm() // пароль приходит только в теле POST, не в адресе + q := r.Form pg := s.IsPostgres() if t := q.Get("type"); t != "" { pg = t == "postgres" @@ -90,7 +97,12 @@ func (h *setupHandlers) fancyDepoLoginScript(w http.ResponseWriter, r *http.Requ if login == "" { login = pickStr(strings.TrimSpace(s.User), "bj_user") } - body := depoLoginScript(pg, db, login) + pass := "" + if r.Method == http.MethodPost { + pass = r.PostForm.Get("password") + } + w.Header().Set("Cache-Control", "no-store") + body := depoLoginScript(pg, db, login, pass) name := "bj-depo-login-mssql.sql" if pg { name = "bj-depo-login-postgres.sql" diff --git a/internal/lkgateway/depologin_test.go b/internal/lkgateway/depologin_test.go index ff6fba3..a426980 100644 --- a/internal/lkgateway/depologin_test.go +++ b/internal/lkgateway/depologin_test.go @@ -6,19 +6,31 @@ import ( ) func TestDepoLoginScript(t *testing.T) { - ms := depoLoginScript(false, "CLIENT_DEPO", "bj_svc") + ms := depoLoginScript(false, "CLIENT_DEPO", "bj_svc", "") for _, w := range []string{"CREATE LOGIN [bj_svc]", "USE [CLIENT_DEPO]", "CREATE USER [bj_svc] FOR LOGIN [bj_svc]", "db_datareader", "ALTER USER [bj_svc] WITH LOGIN = [bj_svc]"} { if !strings.Contains(ms, w) { t.Fatalf("MS SQL: нет %q", w) } } - pg := depoLoginScript(true, "depo", "bj_svc") + pg := depoLoginScript(true, "depo", "bj_svc", "") for _, w := range []string{`CREATE ROLE "bj_svc" LOGIN`, `GRANT CONNECT ON DATABASE "depo"`, `GRANT SELECT ON ALL TABLES`} { if !strings.Contains(pg, w) { t.Fatalf("PostgreSQL: нет %q", w) } } - if s := depoLoginScript(false, "bad;db", "x'; DROP"); !strings.Contains(s, "[BJ_DEPO_DATA]") || !strings.Contains(s, "[bj_user]") { + if s := depoLoginScript(false, "bad;db", "x'; DROP", ""); !strings.Contains(s, "[BJ_DEPO_DATA]") || !strings.Contains(s, "[bj_user]") { t.Fatal("недопустимые имена — подставляются значения по умолчанию") } } + +func TestDepoLoginScriptPassword(t *testing.T) { + if s := depoLoginScript(false, "D", "u", "Pa'ss"); !strings.Contains(s, "PASSWORD = N'Pa''ss'") || strings.Contains(s, "N'ЗАМЕНИТЕ_НА_ПАРОЛЬ'") { + t.Fatal("MS SQL: пароль из поля с экранированием кавычки") + } + if s := depoLoginScript(true, "d", "u", "p'w%x"); !strings.Contains(s, "PASSWORD 'p''w%x'") { + t.Fatal("PostgreSQL: пароль из поля") + } + if s := depoLoginScript(false, "D", "u", ""); !strings.Contains(s, "N'ЗАМЕНИТЕ_НА_ПАРОЛЬ'") { + t.Fatal("пустой пароль — заглушка") + } +} diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index b438a29..74da3ea 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -119,26 +119,40 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
  • чтение учётных таблиц DEPO (лица, счета, остатки, бумаги) — для сверки переводов;
  • запись только в таблицы обмена FTV_M2M* — их права выдаются на следующем экране, вместе с созданием таблиц.
  • -

    Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём уже подставлены тип СУБД, база и имя логина из полей выше, нужно только заменить пароль.

    - Скачать скрипт создания логина +

    Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём подставлены тип СУБД, база, имя логина и пароль из полей выше (если пароль не введён — впишите его в скрипт сами). Скрипт с паролем не храните.

    + Скачать скрипт создания логина
    …

    После выполнения скрипта впишите логин и пароль в поля выше и нажмите «Проверить и далее».