mirror of
				https://github.com/h44z/wg-portal.git
				synced 2025-11-03 23:56:18 +00:00 
			
		
		
		
	cleanup recursive ldap group sync
This commit is contained in:
		@@ -8,11 +8,11 @@ import (
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ObjectType int64
 | 
			
		||||
type ObjectType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Users  ObjectType = 1
 | 
			
		||||
	Groups ObjectType = 2
 | 
			
		||||
	Users ObjectType = iota
 | 
			
		||||
	Groups
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RawLdapData struct {
 | 
			
		||||
@@ -86,7 +86,8 @@ func FindAllObjects(cfg *Config, objType ObjectType) ([]RawLdapData, error) {
 | 
			
		||||
	var searchRequest *ldap.SearchRequest
 | 
			
		||||
	var attrs []string
 | 
			
		||||
 | 
			
		||||
	if objType == Users {
 | 
			
		||||
	switch objType {
 | 
			
		||||
	case Users:
 | 
			
		||||
		// Search all users
 | 
			
		||||
		attrs = []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
 | 
			
		||||
			cfg.PhoneAttribute, cfg.GroupMemberAttribute}
 | 
			
		||||
@@ -95,7 +96,7 @@ func FindAllObjects(cfg *Config, objType ObjectType) ([]RawLdapData, error) {
 | 
			
		||||
			ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 | 
			
		||||
			cfg.SyncFilter, attrs, nil,
 | 
			
		||||
		)
 | 
			
		||||
	} else if objType == Groups {
 | 
			
		||||
	case Groups:
 | 
			
		||||
		// Search all groups
 | 
			
		||||
		attrs = []string{"dn", cfg.GroupMemberAttribute}
 | 
			
		||||
		searchRequest = ldap.NewSearchRequest(
 | 
			
		||||
@@ -103,6 +104,8 @@ func FindAllObjects(cfg *Config, objType ObjectType) ([]RawLdapData, error) {
 | 
			
		||||
			ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 | 
			
		||||
			cfg.SyncGroupFilter, attrs, nil,
 | 
			
		||||
		)
 | 
			
		||||
	default:
 | 
			
		||||
		panic("invalid object type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sr, err := client.Search(searchRequest)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,9 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	gldap "github.com/go-ldap/ldap/v3"
 | 
			
		||||
	"github.com/h44z/wg-portal/internal/wireguard"
 | 
			
		||||
 | 
			
		||||
	"github.com/h44z/wg-portal/internal/ldap"
 | 
			
		||||
	"github.com/h44z/wg-portal/internal/users"
 | 
			
		||||
	"github.com/h44z/wg-portal/internal/wireguard"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
@@ -21,12 +20,17 @@ func (s *Server) SyncLdapWithUserDatabase() {
 | 
			
		||||
		// Main work here
 | 
			
		||||
		logrus.Trace("syncing ldap users to database...")
 | 
			
		||||
		ldapUsers, err := ldap.FindAllObjects(&s.config.LDAP, ldap.Users)
 | 
			
		||||
		ldapGroups, errGroups := ldap.FindAllObjects(&s.config.LDAP, ldap.Groups)
 | 
			
		||||
		if err != nil && errGroups != nil {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Errorf("failed to fetch users from ldap: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		logrus.Tracef("found %d users in ldap", len(ldapUsers))
 | 
			
		||||
		ldapGroups, err := ldap.FindAllObjects(&s.config.LDAP, ldap.Groups)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Errorf("failed to fetch groups from ldap: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logrus.Tracef("found %d users and %d groups in ldap", len(ldapUsers), len(ldapGroups))
 | 
			
		||||
 | 
			
		||||
		// Update existing LDAP users
 | 
			
		||||
		s.updateLdapUsers(ldapUsers, ldapGroups)
 | 
			
		||||
@@ -34,6 +38,8 @@ func (s *Server) SyncLdapWithUserDatabase() {
 | 
			
		||||
		// Disable missing LDAP users
 | 
			
		||||
		s.disableMissingLdapUsers(ldapUsers)
 | 
			
		||||
 | 
			
		||||
		logrus.Trace("synchronized ldap users to database")
 | 
			
		||||
 | 
			
		||||
		// Select blocks until one of the cases happens
 | 
			
		||||
		select {
 | 
			
		||||
		case <-time.After(1 * time.Minute):
 | 
			
		||||
@@ -47,35 +53,62 @@ func (s *Server) SyncLdapWithUserDatabase() {
 | 
			
		||||
	logrus.Info("ldap user synchronization stopped")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData, ldapGroupData []ldap.RawLdapData, layer int) bool {
 | 
			
		||||
func (s Server) userIsInAdminGroup(userData *ldap.RawLdapData, groupTreeData []ldap.RawLdapData) bool {
 | 
			
		||||
	if s.config.LDAP.EveryoneAdmin {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if s.config.LDAP.AdminLdapGroup_ == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	//fmt.Printf("%+v\n", ldapData.Attributes)
 | 
			
		||||
	var prefix string
 | 
			
		||||
	for i := 0; i < layer; i++ {
 | 
			
		||||
		prefix += "+"
 | 
			
		||||
	}
 | 
			
		||||
	logrus.Tracef("%s Group layer: %d\n", prefix, layer)
 | 
			
		||||
	for _, group := range ldapData.RawAttributes[s.config.LDAP.GroupMemberAttribute] {
 | 
			
		||||
		logrus.Tracef("%s%s\n", prefix, string(group))
 | 
			
		||||
		var dn, _ = gldap.ParseDN(string(group))
 | 
			
		||||
		if s.config.LDAP.AdminLdapGroup_.Equal(dn) {
 | 
			
		||||
			logrus.Tracef("%sFOUND: %s\n", prefix, string(group))
 | 
			
		||||
 | 
			
		||||
	for _, userGroup := range userData.RawAttributes[s.config.LDAP.GroupMemberAttribute] {
 | 
			
		||||
		var userGroupDn, _ = gldap.ParseDN(string(userGroup))
 | 
			
		||||
		if s.dnIsAdminGroup(userGroupDn, groupTreeData) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		for _, group2 := range ldapGroupData {
 | 
			
		||||
			if group2.DN == string(group) {
 | 
			
		||||
				logrus.Tracef("%sChecking nested: %s\n", prefix, group2.DN)
 | 
			
		||||
				isAdmin := s.userIsInAdminGroup(&group2, ldapGroupData, layer+1)
 | 
			
		||||
				if isAdmin {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dnIsAdminGroup checks if the given DN is equal to the admin group, or if it is included in a groupTree that has the
 | 
			
		||||
// admin group as parent/root.
 | 
			
		||||
//
 | 
			
		||||
// WGPortal-Admin          (L0)
 | 
			
		||||
//
 | 
			
		||||
//	\_ IT-Admin            (L1)
 | 
			
		||||
//	  |_ Alice             (L2)
 | 
			
		||||
//	  |_ Bob               (L2)
 | 
			
		||||
//	  \_ Eve               (L2)
 | 
			
		||||
//	\_ External-Company    (L1)
 | 
			
		||||
//	  |_ External-Admin    (L2)
 | 
			
		||||
//	    |_ Sam             (L3)
 | 
			
		||||
//	    \_ Steve           (L3)
 | 
			
		||||
//
 | 
			
		||||
// All DNs in the example above are member of the admin group.
 | 
			
		||||
func (s Server) dnIsAdminGroup(dn *gldap.DN, groupTreeData []ldap.RawLdapData) bool {
 | 
			
		||||
	if s.config.LDAP.AdminLdapGroup_ == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if s.config.LDAP.AdminLdapGroup_.Equal(dn) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Recursively check the whole group tree
 | 
			
		||||
	for _, group := range groupTreeData {
 | 
			
		||||
		var groupDn, _ = gldap.ParseDN(group.DN)
 | 
			
		||||
		if !dn.Equal(groupDn) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, parentGroupDn := range group.RawAttributes[s.config.LDAP.GroupMemberAttribute] {
 | 
			
		||||
			var parentDn, _ = gldap.ParseDN(string(parentGroupDn))
 | 
			
		||||
			if s.dnIsAdminGroup(parentDn, groupTreeData) {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
@@ -101,7 +134,7 @@ func (s Server) userChangedInLdap(user *users.User, ldapData *ldap.RawLdapData,
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user.IsAdmin != s.userIsInAdminGroup(ldapData, ldapGroupData, 0) {
 | 
			
		||||
	if user.IsAdmin != s.userIsInAdminGroup(ldapData, ldapGroupData) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -176,7 +209,7 @@ func (s *Server) updateLdapUsers(ldapUsers []ldap.RawLdapData, ldapGroups []ldap
 | 
			
		||||
			user.Lastname = ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute]
 | 
			
		||||
			user.Email = ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute]
 | 
			
		||||
			user.Phone = ldapUsers[i].Attributes[s.config.LDAP.PhoneAttribute]
 | 
			
		||||
			user.IsAdmin = s.userIsInAdminGroup(&ldapUsers[i], ldapGroups, 0)
 | 
			
		||||
			user.IsAdmin = s.userIsInAdminGroup(&ldapUsers[i], ldapGroups)
 | 
			
		||||
			user.Source = users.UserSourceLdap
 | 
			
		||||
			user.DeletedAt = gorm.DeletedAt{} // Not deleted
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user