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); }
FTV_M2M* — их права выдаются на следующем экране, вместе с созданием таблиц.Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём уже подставлены тип СУБД, база и имя логина из полей выше, нужно только заменить пароль.
- Скачать скрипт создания логина +Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём подставлены тип СУБД, база, имя логина и пароль из полей выше (если пароль не введён — впишите его в скрипт сами). Скрипт с паролем не храните.
+ Скачать скрипт создания логина…
После выполнения скрипта впишите логин и пароль в поля выше и нажмите «Проверить и далее».