Индикатор «Шлюз НРД», проверка ключа ИШ, папки шлюза по сети, сотрудники из домена, переключатель QUIK в настройках
- журнал ИШ: номер процесса выравнивается пробелами — опросы НРД не распознавались (серый индикатор) - «Проверить СКЗИ и ключ»: Валидата, датчик случайных чисел, подпись профилем ИШ (а не старый bj-crypto); индикатор «Криптографии» — по готовности ключа; карточка автозагрузки УЦ — только для разработки - шаг «Шлюз НРД»: папки обмена по сети (Samba): логин/пароль, ручная настройка не трогается - «Учётные записи»: необязательно — подключить домен, проверить связь, найти сотрудника и добавить с ролью; роль из bj действует, пока группы домена с ролями не сопоставлены - настройка QUIK МНП: «Только чтение / Чтение и запись», тонкая настройка цикла свёрнута - индикатор «Обновления» — только когда есть что обновить; «Мастер настройки» в меню и в настройках Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
package lkgateway
|
||||
|
||||
// Сотрудники из домена в шаге «Учётные записи» (07.10.2026). Основной путь — создать учётную запись bj сразу
|
||||
// (логин и пароль); второй, необязательный, — подключить домен Active Directory прямо здесь, проверить связь и
|
||||
// выбрать сотрудника поиском: он входит своим доменным логином и паролем, bj проверяет пароль в домене.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
func (h *setupHandlers) registerADWizard(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/admin/setup/users/ad-connect", h.adConnect)
|
||||
mux.HandleFunc("/admin/setup/users/ad-search", h.adSearch)
|
||||
}
|
||||
|
||||
// adConnect — POST: адрес контроллера, домен, сервисная учётная запись (для поиска сотрудников); остальные
|
||||
// настройки входа не трогает. Сразу проверяет связь: контроллер отвечает и сервисная запись входит.
|
||||
func (h *setupHandlers) adConnect(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
a := h.rc.Snapshot().Auth
|
||||
a.ADServer = strings.TrimSpace(r.FormValue("ad_server"))
|
||||
a.ADDomain = strings.TrimSpace(strings.TrimPrefix(r.FormValue("ad_domain"), "@"))
|
||||
a.ADBindUser = strings.TrimSpace(r.FormValue("ad_bind_user"))
|
||||
if pw := r.FormValue("ad_bind_password"); pw != "" {
|
||||
a.ADBindPassword = pw
|
||||
}
|
||||
if a.ADServer != "" && !strings.Contains(a.ADServer, "://") {
|
||||
a.ADServer = "ldaps://" + a.ADServer // как в проводнике вводят имя — по умолчанию защищённое подключение
|
||||
}
|
||||
var miss []string
|
||||
for _, f := range []struct{ v, n string }{{a.ADServer, "контроллер домена"}, {a.ADDomain, "домен"}, {a.ADBindUser, "сервисная учётная запись"}, {a.ADBindPassword, "её пароль"}} {
|
||||
if f.v == "" {
|
||||
miss = append(miss, f.n)
|
||||
}
|
||||
}
|
||||
if len(miss) > 0 {
|
||||
h.usersFlash(w, r, "Домен: не заполнено: "+strings.Join(miss, ", "))
|
||||
return
|
||||
}
|
||||
probe, err := adProbe(a)
|
||||
if err != nil {
|
||||
h.usersFlash(w, r, "Домен: контроллер не отвечает — "+err.Error()+". Настройки не сохранены.")
|
||||
return
|
||||
}
|
||||
conn, err := adDial(a)
|
||||
if err == nil {
|
||||
err = adBindService(conn, a)
|
||||
conn.Close()
|
||||
}
|
||||
if err != nil {
|
||||
h.usersFlash(w, r, "Домен: "+err.Error()+". Настройки не сохранены.")
|
||||
return
|
||||
}
|
||||
a.ADEnabled = true
|
||||
if err := h.rc.UpdateAuth(a); err != nil {
|
||||
h.usersFlash(w, r, "Домен: настройки не сохранены — "+err.Error())
|
||||
return
|
||||
}
|
||||
h.usersFlash(w, r, "Домен подключён ("+probe+"): можно добавлять сотрудников из домена — они входят своим доменным логином и паролем.")
|
||||
}
|
||||
|
||||
// adUserView — сотрудник домена в результатах поиска.
|
||||
type adUserView struct {
|
||||
Login string `json:"login"` // sAMAccountName
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Have bool `json:"have,omitempty"` // уже есть в bj
|
||||
}
|
||||
|
||||
// adSearch — GET ?q=: сотрудники домена по фамилии, логину или почте (до 20, только включённые учётные записи).
|
||||
func (h *setupHandlers) adSearch(w http.ResponseWriter, r *http.Request) {
|
||||
a := h.rc.Snapshot().Auth
|
||||
q := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||
if !a.ADEnabled || a.ADBindUser == "" {
|
||||
jsonOut(w, map[string]any{"ok": false, "error": "домен не подключён"})
|
||||
return
|
||||
}
|
||||
if len([]rune(q)) < 2 {
|
||||
jsonOut(w, map[string]any{"ok": true, "users": []adUserView{}})
|
||||
return
|
||||
}
|
||||
users, err := adSearchUsers(a, q, 20)
|
||||
if err != nil {
|
||||
jsonOut(w, map[string]any{"ok": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
jsonOut(w, map[string]any{"ok": true, "users": users})
|
||||
}
|
||||
|
||||
func adSearchUsers(a AuthSettings, q string, limit int) ([]adUserView, error) {
|
||||
conn, err := adDial(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
conn.SetTimeout(10 * time.Second)
|
||||
if err := adBindService(conn, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base := a.ADBaseDN
|
||||
if base == "" {
|
||||
base = adDefaultBase(conn)
|
||||
}
|
||||
e := ldap.EscapeFilter(q)
|
||||
filter := fmt.Sprintf("(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(sAMAccountName=*%s*)(displayName=*%s*)(sn=*%s*)(mail=*%s*)))", e, e, e, e)
|
||||
sr, err := conn.Search(ldap.NewSearchRequest(base, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, limit, 10, false,
|
||||
filter, []string{"sAMAccountName", "displayName", "mail"}, nil))
|
||||
if err != nil && (sr == nil || len(sr.Entries) == 0) { // превышение лимита — не ошибка, берём найденное
|
||||
return nil, fmt.Errorf("поиск в домене: %v", err)
|
||||
}
|
||||
var out []adUserView
|
||||
for _, en := range sr.Entries {
|
||||
u := adUserView{Login: en.GetAttributeValue("sAMAccountName"), Name: en.GetAttributeValue("displayName"), Email: en.GetAttributeValue("mail")}
|
||||
if u.Login == "" {
|
||||
continue
|
||||
}
|
||||
u.Have = globalUsers.Get(u.Login) != nil
|
||||
out = append(out, u)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user