AD: участники групп с доступом в bj
У каждой группы домена в «Пользователи и вход» — «участники: N»: ФИО, логин, почта, отметки «отключён в AD» и «входил в bj»; с вложенными группами (LDAP_MATCHING_RULE_IN_CHAIN). GET /admin/setup/users/ad-group-members — только для групп из настроек bj. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -29,6 +31,7 @@ func (h *setupHandlers) registerADWizard(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/admin/setup/users/ad-trust", h.adTrust)
|
||||
mux.HandleFunc("/admin/setup/users/ad-discover", h.adDiscoverHTTP)
|
||||
mux.HandleFunc("/admin/setup/users/ad-group", h.adGroupSave)
|
||||
mux.HandleFunc("/admin/setup/users/ad-group-members", h.adGroupMembersHTTP)
|
||||
}
|
||||
|
||||
// adConnect — POST: учётная запись домена и пароль; домен и контроллер — из расширенных настроек или bj находит
|
||||
@@ -546,3 +549,78 @@ func (h *setupHandlers) adGroupSave(w http.ResponseWriter, r *http.Request) {
|
||||
igateShareSyncSoon(h.rc) // папки шлюза «через домен» — тем же группам
|
||||
h.usersFlash(w, r, msg)
|
||||
}
|
||||
|
||||
// adMember — участник группы домена (для контроля, кто получает доступ в bj через группу).
|
||||
type adMember struct {
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Disabled bool `json:"disabled,omitempty"` // учётная запись отключена в AD — войти не сможет
|
||||
InBJ bool `json:"in_bj,omitempty"` // уже входил в bj (есть пользователь bj)
|
||||
}
|
||||
|
||||
// adGroupMembersHTTP — GET /admin/setup/users/ad-group-members?dn=…: участники группы с доступом в bj,
|
||||
// включая вложенные группы (LDAP_MATCHING_RULE_IN_CHAIN). Только группы из настроек bj.
|
||||
func (h *setupHandlers) adGroupMembersHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
a := h.rc.Snapshot().Auth
|
||||
dn := strings.TrimSpace(r.URL.Query().Get("dn"))
|
||||
if !a.ADEnabled || a.ADBindUser == "" {
|
||||
jsonOut(w, map[string]any{"ok": false, "error": "домен не подключён"})
|
||||
return
|
||||
}
|
||||
known := false
|
||||
for _, g := range a.ADGroups {
|
||||
known = known || strings.EqualFold(g.Group, dn)
|
||||
}
|
||||
if !known {
|
||||
jsonOut(w, map[string]any{"ok": false, "error": "группы нет в настройках bj"})
|
||||
return
|
||||
}
|
||||
list, err := adGroupMembers(a, dn, 1000)
|
||||
if err != nil {
|
||||
jsonOut(w, map[string]any{"ok": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
jsonOut(w, map[string]any{"ok": true, "members": list})
|
||||
}
|
||||
|
||||
func adGroupMembers(a AuthSettings, groupDN string, limit int) ([]adMember, error) {
|
||||
conn, err := adDial(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
conn.SetTimeout(15 * time.Second)
|
||||
if err := adBindService(conn, a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base := a.ADBaseDN
|
||||
if base == "" {
|
||||
base = adDefaultBase(conn)
|
||||
}
|
||||
filter := "(&(objectCategory=person)(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=" + ldap.EscapeFilter(groupDN) + "))"
|
||||
sr, err := conn.Search(ldap.NewSearchRequest(base, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, limit, 15, false,
|
||||
filter, []string{"sAMAccountName", "displayName", "mail", "userAccountControl"}, nil))
|
||||
if err != nil && (sr == nil || len(sr.Entries) == 0) {
|
||||
return nil, fmt.Errorf("участники группы: %v", err)
|
||||
}
|
||||
out := []adMember{}
|
||||
for _, en := range sr.Entries {
|
||||
m := adMember{Login: en.GetAttributeValue("sAMAccountName"), Name: en.GetAttributeValue("displayName"), Email: en.GetAttributeValue("mail")}
|
||||
if m.Login == "" {
|
||||
continue
|
||||
}
|
||||
if uac, err := strconv.Atoi(en.GetAttributeValue("userAccountControl")); err == nil && uac&2 != 0 {
|
||||
m.Disabled = true
|
||||
}
|
||||
m.InBJ = globalUsers.Get(m.Login) != nil
|
||||
out = append(out, m)
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].Disabled != out[j].Disabled {
|
||||
return !out[i].Disabled
|
||||
}
|
||||
return strings.ToLower(pickStr(out[i].Name, out[i].Login)) < strings.ToLower(pickStr(out[j].Name, out[j].Login))
|
||||
})
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user