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", "ЕСИА Финанс (платформа личного кабинета)"}, } 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 }