Files
Bridge-and-Join-s/internal/lkgateway/advanced_gate.go
T
zuevav e043e7c371 Мастер, шаг 3 DEPO: подключение → трансферные таблицы → обработка (job); по умолчанию джобу SQL Agent создаёт bj разовым логином администратора
- редакция SQL Server определяется (Express/PostgreSQL — обработку запускает bj);
- создание джобы и таблиц — логином администратора, который не сохраняется;
- настройки обмена с DEPO открыты без пароля перспективных модулей, если QUIK МНП в лицензии.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-09-24 22:11:42 +03:00

99 lines
4.3 KiB
Go

package lkgateway
import (
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"net/http"
"os"
)
// advanced_gate.go — пароль на «перспективные модули» (QUIK МНП, Обмен Fancy).
// Эти модули относятся к будущим профилям («автомат/полуавтомат» со своим ЛК) и
// пока не в проде. Чтобы оператор их случайно не трогал, они скрыты и
// открываются паролем. Простая cookie-схема: значение cookie = токен пароля,
// подделать без пароля нельзя. Пароль берётся из BJ_ADVANCED_PASSWORD, иначе —
// значение по умолчанию.
const defaultAdvancedPassword = "bj-modules-2026"
const advancedCookie = "bj_adv"
func effectiveAdvancedPassword() string {
if p := os.Getenv("BJ_ADVANCED_PASSWORD"); p != "" {
return p
}
return defaultAdvancedPassword
}
// advancedToken — неподделываемый токен для cookie: hex(sha256(пароль)).
func advancedToken() string {
sum := sha256.Sum256([]byte("bj-adv:" + effectiveAdvancedPassword()))
return hex.EncodeToString(sum[:])
}
// advancedUnlocked — открыт ли доступ к перспективным модулям в этом запросе.
func advancedUnlocked(r *http.Request) bool {
// Модуль QUIK МНП (обмен Fancy) есть в лицензии — разделы открыты без пароля: пароль нужен
// только там, где модули не куплены. Иначе кнопки разделов видны, а настройки пустые.
if globalRC != nil && moduleAllowed(globalRC, "quik-mnp") {
return true
}
if !devMode(globalRC) { // пароль на модули — инструмент разработчиков
return false
}
c, err := r.Cookie(advancedCookie)
if err != nil {
return false
}
want := advancedToken()
return subtle.ConstantTimeCompare([]byte(c.Value), []byte(want)) == 1
}
// requireAdvanced — защита ручек сохранения перспективных модулей: если доступ
// не открыт, отвечает 403. true — можно продолжать.
// requireDepo — настройки обмена с DEPO: открыты, если модуль «QUIK: неторговые поручения»
// входит в лицензию (мастер настройки, шаг 3), иначе — как перспективный модуль, по паролю.
func requireDepo(w http.ResponseWriter, r *http.Request) bool {
if globalRC != nil && moduleAllowed(globalRC, "quik-mnp") {
return true
}
return requireAdvanced(w, r)
}
func requireAdvanced(w http.ResponseWriter, r *http.Request) bool {
if advancedUnlocked(r) {
return true
}
http.Error(w, "Раздел «Перспективные модули» закрыт. Откройте его паролем в Настройке.", http.StatusForbidden)
return false
}
// unlockAdvanced — POST /admin/setup/advanced-unlock: проверяет пароль и ставит
// cookie доступа.
func (h *setupHandlers) unlockAdvanced(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method", http.StatusMethodNotAllowed)
return
}
if r.FormValue("password") != effectiveAdvancedPassword() {
setupFlash(w, r, "Перспективные модули: неверный пароль")
return
}
http.SetCookie(w, &http.Cookie{
Name: advancedCookie, Value: advancedToken(), Path: "/admin",
HttpOnly: true, SameSite: http.SameSiteLaxMode, MaxAge: 30 * 24 * 3600,
})
setupFlash(w, r, "Перспективные модули открыты (QUIK, Обмен Fancy). Разделы появились в меню слева.")
}
// lockAdvanced — POST /admin/setup/advanced-lock: снова прячет модули.
func (h *setupHandlers) lockAdvanced(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method", http.StatusMethodNotAllowed)
return
}
http.SetCookie(w, &http.Cookie{
Name: advancedCookie, Value: "", Path: "/admin", HttpOnly: true, MaxAge: -1,
})
setupFlash(w, r, "Перспективные модули снова скрыты")
}