Files
Bridge-and-Join-s/internal/lkgateway/advanced_gate.go
T
zuevav da12a9ec1c feat(bj): модуль ЛК на PostgreSQL, обмен с DEPO, боевой ЛК с имитатором НРД, QUIK, НПР
- bj_exchange и «кран» Fancy убраны: данные модуля ЛК в PostgreSQL (схема lk)
- трансферные таблицы FTV_M2M: создание при установке, события, справочник участников
- сверка с DEPO: счёт, ФИО, дата рождения, ИИС по договору, квал-инвестор из опции DEPO
- боевой ЛК ЕСИА Финанс при тестовом ИШ: журнал, отказ через DEPO, статусы в ЛК
- онлайн-лицензирование в bj, план тестирования M2M для контрагента (интерактивный HTML)
- модули QUIK МНП, экспорт QUIK, НПР/УДС; документы и тестовые материалы

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 19:41:12 +03:00

87 lines
3.7 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
}
c, err := r.Cookie(advancedCookie)
if err != nil {
return false
}
want := advancedToken()
return subtle.ConstantTimeCompare([]byte(c.Value), []byte(want)) == 1
}
// requireAdvanced — защита ручек сохранения перспективных модулей: если доступ
// не открыт, отвечает 403. true — можно продолжать.
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, "Перспективные модули снова скрыты")
}