Скрипт создания логина DEPO: пароль из поля мастера подставляется в скрипт (передаётся только в теле POST, не сохраняется, кавычки экранируются)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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("пустой пароль — заглушка")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,26 +119,40 @@ a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
|
||||
<li><b>чтение</b> учётных таблиц DEPO (лица, счета, остатки, бумаги) — для сверки переводов;</li>
|
||||
<li><b>запись только в таблицы обмена</b> <code>FTV_M2M*</code> — их права выдаются на следующем экране, вместе с созданием таблиц.</li>
|
||||
</ul>
|
||||
<p style="margin:0 0 6px">Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём уже подставлены тип СУБД, база и имя логина из полей выше, нужно только заменить пароль.</p>
|
||||
<a id="wiz-login-dl" href="/admin/setup/fancy/depo-login/script.sql">Скачать скрипт создания логина</a>
|
||||
<p style="margin:0 0 6px">Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём подставлены тип СУБД, база, имя логина и пароль из полей выше (если пароль не введён — впишите его в скрипт сами). Скрипт с паролем не храните.</p>
|
||||
<a id="wiz-login-dl" href="#">Скачать скрипт создания логина</a> <span class="muted" id="wiz-login-pwnote" style="font-size:12px"></span>
|
||||
<pre class="copyable" id="wiz-login-pre" style="max-height:260px;margin-top:6px;font-size:12px">…</pre>
|
||||
<p class="muted" style="margin:4px 0 0">После выполнения скрипта впишите логин и пароль в поля выше и нажмите «Проверить и далее».</p>
|
||||
</div>
|
||||
</details>
|
||||
<script>
|
||||
(function(){
|
||||
var f=document.currentScript.closest('form'), d=document.getElementById('wiz-login-help'), pre=document.getElementById('wiz-login-pre'), a=document.getElementById('wiz-login-dl');
|
||||
function q(){
|
||||
var t=f.querySelector('[name=db_type]'), db=f.querySelector('[name=depo_database]'), u=f.querySelector('[name=user]');
|
||||
return 'type='+encodeURIComponent(t?t.value:'mssql')+'&db='+encodeURIComponent(db&&db.value.trim()?db.value.trim():(db?db.placeholder:''))+'&login='+encodeURIComponent(u&&u.value.trim()?u.value.trim():'bj_user');
|
||||
var f=document.currentScript.closest('form'), d=document.getElementById('wiz-login-help'), pre=document.getElementById('wiz-login-pre'), a=document.getElementById('wiz-login-dl'), note=document.getElementById('wiz-login-pwnote');
|
||||
function val(n){ var e=f.querySelector('[name='+n+']'); return e?e.value:''; }
|
||||
function params(){
|
||||
var db=f.querySelector('[name=depo_database]');
|
||||
var p=new URLSearchParams();
|
||||
p.set('type', val('db_type')||'mssql');
|
||||
p.set('db', db&&db.value.trim()?db.value.trim():(db?db.placeholder:''));
|
||||
p.set('login', val('user').trim()||'bj_user');
|
||||
p.set('password', val('password')); // только в теле POST — не в адресе и не в журналах
|
||||
return p;
|
||||
}
|
||||
function upd(){
|
||||
a.href='/admin/setup/fancy/depo-login/script.sql?'+q();
|
||||
note.textContent = val('password') ? '— пароль из поля подставлен в скрипт' : '— пароль не введён: в скрипте заглушка, замените её';
|
||||
if(!d.open) return;
|
||||
fetch('/admin/setup/fancy/depo-login/script.sql?download=0&'+q(),{cache:'no-store'}).then(function(r){return r.text()}).then(function(t){pre.textContent=t}).catch(function(){});
|
||||
var p=params(); p.set('download','0');
|
||||
fetch('/admin/setup/fancy/depo-login/script.sql',{method:'POST',body:p,cache:'no-store'}).then(function(r){return r.text()}).then(function(t){pre.textContent=t}).catch(function(){});
|
||||
}
|
||||
a.addEventListener('click', function(e){
|
||||
e.preventDefault();
|
||||
var p=params(), fm=document.createElement('form');
|
||||
fm.method='POST'; fm.action='/admin/setup/fancy/depo-login/script.sql'; fm.style.display='none';
|
||||
p.forEach(function(v,k){ var i=document.createElement('input'); i.type='hidden'; i.name=k; i.value=v; fm.appendChild(i); });
|
||||
document.body.appendChild(fm); fm.submit(); fm.remove();
|
||||
});
|
||||
d.addEventListener('toggle',upd);
|
||||
['db_type','depo_database','user'].forEach(function(n){var e=f.querySelector('[name='+n+']'); if(e) e.addEventListener('change',upd);});
|
||||
['db_type','depo_database','user','password'].forEach(function(n){var e=f.querySelector('[name='+n+']'); if(e){ e.addEventListener('change',upd); e.addEventListener('input',function(){ clearTimeout(e._t); e._t=setTimeout(upd,400); }); }});
|
||||
upd();
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user