Files
Bridge-and-Join-s/internal/lkgateway/licensecheck.go
T
zuevav 73b246831c Пароль домена не попадает в страницу; домен в мастере — открыт, крупно, группы; справочник DEPO без переводов; 1С — бэк-офис
- Безопасность: в мастере подпись к паролю выводила сам сохранённый пароль служебной записи домена — теперь только «задан/введён» (тест: пароль не попадает в HTML). Список сессий в настройках больше не содержит токены (по ним можно было войти под чужим именем) — короткий ID.
- Домен: после подключения и «Доверять» блок остаётся открытым, страница возвращается к нему; карточка «Домен подключён» и «Найден домен» — крупно; поиск сотрудников и групп; группе — доступ с ролью (ADGroups), список групп с «убрать».
- «Обмен с DEPO»: справочник FTV_M2M_HANDBOOK считается и когда переводов ещё нет (на свежей установке писало «не создана»).
- Модуль 1С: onec-back «1С: база бэк-офиса (тест)» вместо мидл-офиса (в лицензиях не выдавался).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-10-07 01:19:30 +03:00

180 lines
6.5 KiB
Go

package lkgateway
import (
"strings"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/license"
)
// DefaultLicensePublicKey — открытый ключ проверки лицензий: пара к ключу подписи сервера
// лицензий lic.zetit.ru. Зашит в bj и в настройках не меняется, поэтому ключ, подписанный
// чем-то другим, не принимается. var — только для подмены в тестах.
var DefaultLicensePublicKey = license.DefaultPublicKey
// LicenseStatus — сводка состояния лицензии для UI и гейтов.
type LicenseStatus struct {
Present bool // ключ введён
Valid bool // подпись верна, срок не истёк, ключ этой установки
Online bool // выдан сервером лицензий под эту установку и продлевается автоматически
LicenseID string
Tenant string
Plan string
ExpiresAt time.Time
ContractUntil time.Time
DaysLeft int
AllowsUpdates bool
Features []string // признаки (модули), разрешённые ключом
DevUntil time.Time // режим разработки до (выдаёт сервер лицензий; нулевое — выключен)
Message string
}
// LicenseFeatureView — признак лицензии для отображения.
type LicenseFeatureView struct {
Key, Title string
On bool
}
// knownFeatures — известные признаки (модули) и их названия.
var knownFeatures = []struct{ Key, Title string }{
{"updates", "Обновления через артефакторию"},
{"web-cabinet", "Личный кабинет клиента (bj-lk)"},
{"quik-mnp", "QUIK: модуль неторговых поручений (цикл M2M)"},
{"quik-export", "QUIK: экспорт биржевой информации"},
{"npr", "Риск-монитор НПР / УДС"},
{"esia-finance", "ЕСИА Финанс (платформа личного кабинета)"},
{"onec-back", "1С: база бэк-офиса (тест)"},
}
func licenseFeatureViews(rc *RuntimeConfig) []LicenseFeatureView {
out := make([]LicenseFeatureView, 0, len(knownFeatures))
for _, f := range knownFeatures {
out = append(out, LicenseFeatureView{Key: f.Key, Title: f.Title, On: moduleAllowed(rc, f.Key)})
}
st := licenseStatus(rc)
for _, f := range st.Features {
known := false
for _, k := range knownFeatures {
if k.Key == f {
known = true
}
}
if !known {
out = append(out, LicenseFeatureView{Key: f, Title: f, On: st.Valid})
}
}
return out
}
func modulesAllowed(rc *RuntimeConfig) map[string]bool {
m := map[string]bool{}
for _, f := range knownFeatures {
m[f.Key] = moduleAllowed(rc, f.Key)
}
return m
}
// devModeForced — режим разработки в тестах пакета.
var devModeForced bool
// devMode — режим разработки: сервер лицензий временно открыл разработчикам bj тестовые разделы и
// служебные инструменты на этой установке. Без него у клиента их нет (адреса отвечают 404).
func devMode(rc *RuntimeConfig) bool {
if devModeForced {
return true
}
if rc == nil {
return false
}
st := licenseStatus(rc)
return st.Valid && time.Now().Before(st.DevUntil)
}
// moduleAllowed — разрешён ли модуль (лицензионный признак). План enterprise — все модули.
func moduleAllowed(rc *RuntimeConfig, feature string) bool {
st := licenseStatus(rc)
if !st.Valid {
return false
}
if st.Plan == string(license.PlanEnterprise) {
return true
}
for _, f := range st.Features {
if f == feature {
return true
}
}
return false
}
// licensingEnabled — лицензирование действует всегда: ключ проверки зашит в bj.
func licensingEnabled(_ *RuntimeConfig) bool { return DefaultLicensePublicKey != "" }
// verifyLicenseToken — подпись ключа зашитым открытым ключом (срок не проверяется).
func verifyLicenseToken(key string) (*license.License, error) {
pub, err := license.ParsePublicKey(DefaultLicensePublicKey)
if err != nil {
return nil, err
}
tok, err := license.DecodeToken(key)
if err != nil {
return nil, err
}
return license.Verify(tok, pub)
}
// licenseStatus разбирает и проверяет лицензию из настроек.
func licenseStatus(rc *RuntimeConfig) LicenseStatus {
s := rc.Snapshot().License
st := LicenseStatus{}
if strings.TrimSpace(s.Key) == "" {
st.Message = "лицензия не активирована"
return st
}
st.Present = true
lic, err := verifyLicenseToken(s.Key)
if err != nil {
st.Message = "ключ недействителен: подпись не сходится или ключ повреждён"
return st
}
now := time.Now().UTC()
st.LicenseID = lic.ID
st.Tenant = lic.Tenant
st.Plan = string(lic.Plan)
st.ExpiresAt = lic.ExpiresAt
st.ContractUntil = lic.ContractUntil
st.DaysLeft = lic.DaysLeft(now)
st.AllowsUpdates = lic.AllowsUpdates()
st.Features = lic.Features
if lic.DevUntil != nil {
st.DevUntil = *lic.DevUntil
}
st.Online = lic.InstallID != ""
if lic.Product != "" && lic.Product != "bj-server" {
st.Message = "ключ выпущен для другого продукта (" + lic.Product + ")"
return st
}
if st.Online && !strings.EqualFold(lic.InstallID, s.InstallID) {
st.Message = "ключ выдан другой установке: активируйте эту установку кодом"
return st
}
if err := lic.Valid(now); err != nil {
st.Message = err.Error()
return st
}
st.Valid = true
if st.Online {
st.Message = "активна до " + lic.ExpiresAt.Format("02.01.2006") + ", продлевается автоматически"
if !lic.ContractUntil.IsZero() {
if time.Now().After(lic.ContractUntil) {
st.Message += "; поддержка закончилась " + lic.ContractUntil.Format("02.01.2006") + " — работает без новых версий"
} else {
st.Message += "; поддержка (обновления) до " + lic.ContractUntil.Format("02.01.2006")
}
}
} else {
st.Message = "активна до " + lic.ExpiresAt.Format("02.01.2006") + " (офлайн-ключ)"
}
return st
}