DEPO: процедуры с EXECUTE AS — проверка пользователя SYSDBA, понятная ошибка
Процедуры FTV_M2M_PROCESS_* содержат EXECUTE AS USER=SYSDBA; SQL Server требует этого пользователя при CREATE (FTV_M2M_JOB без EXECUTE AS создаётся, а 4 PROCESS_* — нет). - «Проверить возможности» проверяет наличие пользователя EXECUTE AS (из текста процедур) и блокирует установку с понятным сообщением, если его нет. - Ошибка установки сохраняется в статус (не мелькает флешем) с подсказкой про SYSDBA. - Подсказка добавлена и в мастере при «Подготовить». Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
@@ -156,13 +156,15 @@ func depoProcsPreflight(ctx context.Context, f FancyMSSQLSettings, adminUser, ad
|
|||||||
}
|
}
|
||||||
missing := depoMissing(ctx, db, depoBj2depoProcs, "P")
|
missing := depoMissing(ctx, db, depoBj2depoProcs, "P")
|
||||||
login := depoSetupLogin(f)
|
login := depoSetupLogin(f)
|
||||||
var canCreate, sysadmin, bjUser int
|
execAs := depoProcExecAsUser(f) // пользователь для EXECUTE AS в теле процедур (обычно SYSDBA)
|
||||||
|
var canCreate, sysadmin, bjUser, execAsOK int
|
||||||
_ = db.QueryRowContext(ctx,
|
_ = db.QueryRowContext(ctx,
|
||||||
`SELECT CAST(ISNULL(HAS_PERMS_BY_NAME(NULL,'DATABASE','CREATE PROCEDURE'),0) AS int),
|
`SELECT CAST(ISNULL(HAS_PERMS_BY_NAME(NULL,'DATABASE','CREATE PROCEDURE'),0) AS int),
|
||||||
CAST(ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) AS int),
|
CAST(ISNULL(IS_SRVROLEMEMBER('sysadmin'),0) AS int),
|
||||||
CASE WHEN DATABASE_PRINCIPAL_ID(@u) IS NULL THEN 0 ELSE 1 END`,
|
CASE WHEN DATABASE_PRINCIPAL_ID(@u) IS NULL THEN 0 ELSE 1 END,
|
||||||
sql.Named("u", login)).Scan(&canCreate, &sysadmin, &bjUser)
|
CASE WHEN DATABASE_PRINCIPAL_ID(@e) IS NULL THEN 0 ELSE 1 END`,
|
||||||
canApply = canCreate == 1 || sysadmin == 1
|
sql.Named("u", login), sql.Named("e", execAs)).Scan(&canCreate, &sysadmin, &bjUser, &execAsOK)
|
||||||
|
canApply = (canCreate == 1 || sysadmin == 1) && execAsOK == 1
|
||||||
|
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
fmt.Fprintf(&b, "Сервер %s, база %s, логин %s.\n", f.Server, f.DepoDB(), conn.User)
|
fmt.Fprintf(&b, "Сервер %s, база %s, логин %s.\n", f.Server, f.DepoDB(), conn.User)
|
||||||
@@ -181,10 +183,37 @@ func depoProcsPreflight(ctx context.Context, f FancyMSSQLSettings, adminUser, ad
|
|||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&b, "Логин %s в базе: нет — сначала подготовьте базу (логин/таблицы), GRANT будет пропущен.\n", login)
|
fmt.Fprintf(&b, "Логин %s в базе: нет — сначала подготовьте базу (логин/таблицы), GRANT будет пропущен.\n", login)
|
||||||
}
|
}
|
||||||
|
if execAsOK == 1 {
|
||||||
|
fmt.Fprintf(&b, "Пользователь %s (для EXECUTE AS в процедурах): есть.\n", execAs)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&b, "Пользователь %s (для EXECUTE AS в процедурах): НЕТ — процедуры FTV_M2M_PROCESS_* используют EXECUTE AS USER='%s' и не создадутся без него. Создайте этого пользователя в базе (как в рабочей базе Fancy) или замените имя в «Тексты процедур».\n", execAs, execAs)
|
||||||
|
}
|
||||||
b.WriteString("Будет выполнено: CREATE OR ALTER 5 процедур (WIRING→OPERATION→EVENTS→CREATE→JOB) + GRANT EXECUTE на FTV_M2M_JOB.")
|
b.WriteString("Будет выполнено: CREATE OR ALTER 5 процедур (WIRING→OPERATION→EVENTS→CREATE→JOB) + GRANT EXECUTE на FTV_M2M_JOB.")
|
||||||
return b.String(), canApply, nil
|
return b.String(), canApply, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// depoProcExecAsUser — пользователь, под которым процедуры выполняют EXECUTE AS (из их текста,
|
||||||
|
// строка «execute as user = '...'»). По умолчанию SYSDBA.
|
||||||
|
func depoProcExecAsUser(f FancyMSSQLSettings) string {
|
||||||
|
body := depoProcEffective(f, "FTV_M2M_PROCESS_WIRING")
|
||||||
|
if m := execAsUserRe.FindStringSubmatch(body); len(m) == 2 {
|
||||||
|
return m[1]
|
||||||
|
}
|
||||||
|
return "SYSDBA"
|
||||||
|
}
|
||||||
|
|
||||||
|
var execAsUserRe = regexp.MustCompile(`(?i)execute\s+as\s+user\s*=\s*'([^']+)'`)
|
||||||
|
|
||||||
|
// depoProcErrHint — подсказка по типичной причине отказа CREATE процедур.
|
||||||
|
func depoProcErrHint(msg string) string {
|
||||||
|
l := strings.ToLower(msg)
|
||||||
|
if strings.Contains(l, "sysdba") || strings.Contains(l, "principal") || strings.Contains(l, "15517") ||
|
||||||
|
strings.Contains(l, "execute as") || strings.Contains(l, "не удается олицетворить") || strings.Contains(l, "олицетвор") {
|
||||||
|
return " Процедуры FTV_M2M_PROCESS_* используют EXECUTE AS USER='SYSDBA' — в базе DEPO должен быть пользователь SYSDBA (как в рабочей базе Fancy). Создайте его или замените имя пользователя в «Тексты процедур» и повторите."
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// depoProcsInstall — установить процедуры логином администратора. Сначала preflight; при отсутствии
|
// depoProcsInstall — установить процедуры логином администратора. Сначала preflight; при отсутствии
|
||||||
// прав — отказ без изменений. Возвращает итоговое сообщение.
|
// прав — отказ без изменений. Возвращает итоговое сообщение.
|
||||||
func depoProcsInstall(ctx context.Context, f FancyMSSQLSettings, adminUser, adminPass string) (string, error) {
|
func depoProcsInstall(ctx context.Context, f FancyMSSQLSettings, adminUser, adminPass string) (string, error) {
|
||||||
@@ -257,7 +286,11 @@ func (h *setupHandlers) installFancyDepoProcs(w http.ResponseWriter, r *http.Req
|
|||||||
msg, err := depoProcsInstall(ctx, s, r.FormValue("admin_user"), r.FormValue("admin_password"))
|
msg, err := depoProcsInstall(ctx, s, r.FormValue("admin_user"), r.FormValue("admin_password"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("depo-procs: установка не удалась: %v", err)
|
log.Printf("depo-procs: установка не удалась: %v", err)
|
||||||
setupFlash(w, r, "Процедуры DEPO: не установлены — "+err.Error())
|
txt := "установка не удалась: " + err.Error() + depoProcErrHint(err.Error())
|
||||||
|
s2 := h.rc.Snapshot().Fancy // сохранить ошибку, чтобы не мелькала во флеше
|
||||||
|
s2.DepoProcCheck, s2.DepoProcCheckOK, s2.DepoProcCheckAt = txt, false, time.Now()
|
||||||
|
_ = h.rc.UpdateFancy(s2)
|
||||||
|
setupFlash(w, r, "Процедуры DEPO: "+txt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pmsg, pok := depoProcCheck(ctx, h.rc)
|
pmsg, pok := depoProcCheck(ctx, h.rc)
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ func (h *setupHandlers) depoSetupDo(ctx context.Context, r *http.Request, step d
|
|||||||
adm := s
|
adm := s
|
||||||
adm.User, adm.Password = au, r.FormValue("admin_password")
|
adm.User, adm.Password = au, r.FormValue("admin_password")
|
||||||
if err := depoProcsInstallInto(ctx, adm, s, db, s.User); err != nil {
|
if err := depoProcsInstallInto(ctx, adm, s, db, s.User); err != nil {
|
||||||
return fail("процедуры обработки не установлены — " + err.Error())
|
return fail("процедуры обработки не установлены — " + err.Error() + depoProcErrHint(err.Error()))
|
||||||
}
|
}
|
||||||
step("procs", "ok", "")
|
step("procs", "ok", "")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user