mirror of
				https://github.com/h44z/wg-portal.git
				synced 2025-11-03 23:56:18 +00:00 
			
		
		
		
	Add nested group admin state resolution
This commit is contained in:
		@@ -27,6 +27,7 @@ type Config struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	LoginFilter     string    `yaml:"loginFilter" envconfig:"LDAP_LOGIN_FILTER"` // {{login_identifier}} gets replaced with the login email address
 | 
						LoginFilter     string    `yaml:"loginFilter" envconfig:"LDAP_LOGIN_FILTER"` // {{login_identifier}} gets replaced with the login email address
 | 
				
			||||||
	SyncFilter      string    `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"`
 | 
						SyncFilter      string    `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"`
 | 
				
			||||||
 | 
						SyncGroupFilter string    `yaml:"syncGroupFilter" envconfig:"LDAP_SYNC_GROUP_FILTER"`
 | 
				
			||||||
	AdminLdapGroup  string    `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal
 | 
						AdminLdapGroup  string    `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal
 | 
				
			||||||
	AdminLdapGroup_ *gldap.DN `yaml:"-"`
 | 
						AdminLdapGroup_ *gldap.DN `yaml:"-"`
 | 
				
			||||||
	EveryoneAdmin   bool      `yaml:"everyoneAdmin" envconfig:"LDAP_EVERYONE_ADMIN"`
 | 
						EveryoneAdmin   bool      `yaml:"everyoneAdmin" envconfig:"LDAP_EVERYONE_ADMIN"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,13 @@ import (
 | 
				
			|||||||
	"github.com/pkg/errors"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ObjectType int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						Users  ObjectType = 1
 | 
				
			||||||
 | 
						Groups ObjectType = 2
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RawLdapData struct {
 | 
					type RawLdapData struct {
 | 
				
			||||||
	DN            string
 | 
						DN            string
 | 
				
			||||||
	Attributes    map[string]string
 | 
						Attributes    map[string]string
 | 
				
			||||||
@@ -69,21 +76,34 @@ func Close(conn *ldap.Conn) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func FindAllUsers(cfg *Config) ([]RawLdapData, error) {
 | 
					func FindAllObjects(cfg *Config, objType ObjectType) ([]RawLdapData, error) {
 | 
				
			||||||
	client, err := Open(cfg)
 | 
						client, err := Open(cfg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, errors.WithMessage(err, "failed to open ldap connection")
 | 
							return nil, errors.WithMessage(err, "failed to open ldap connection")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer Close(client)
 | 
						defer Close(client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Search all users
 | 
						var searchRequest *ldap.SearchRequest
 | 
				
			||||||
	attrs := []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
 | 
						var attrs []string
 | 
				
			||||||
		cfg.PhoneAttribute, cfg.GroupMemberAttribute}
 | 
					
 | 
				
			||||||
	searchRequest := ldap.NewSearchRequest(
 | 
						if objType == Users {
 | 
				
			||||||
		cfg.BaseDN,
 | 
							// Search all users
 | 
				
			||||||
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 | 
							attrs = []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
 | 
				
			||||||
		cfg.SyncFilter, attrs, nil,
 | 
								cfg.PhoneAttribute, cfg.GroupMemberAttribute}
 | 
				
			||||||
	)
 | 
							searchRequest = ldap.NewSearchRequest(
 | 
				
			||||||
 | 
								cfg.BaseDN,
 | 
				
			||||||
 | 
								ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 | 
				
			||||||
 | 
								cfg.SyncFilter, attrs, nil,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						} else if objType == Groups {
 | 
				
			||||||
 | 
							// Search all groups
 | 
				
			||||||
 | 
							attrs = []string{"dn", cfg.GroupMemberAttribute}
 | 
				
			||||||
 | 
							searchRequest = ldap.NewSearchRequest(
 | 
				
			||||||
 | 
								cfg.BaseDN,
 | 
				
			||||||
 | 
								ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 | 
				
			||||||
 | 
								cfg.SyncGroupFilter, attrs, nil,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sr, err := client.Search(searchRequest)
 | 
						sr, err := client.Search(searchRequest)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,6 +114,7 @@ func NewConfig() *Config {
 | 
				
			|||||||
	cfg.LDAP.AdminLdapGroup = "CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL"
 | 
						cfg.LDAP.AdminLdapGroup = "CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL"
 | 
				
			||||||
	cfg.LDAP.LoginFilter = "(&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))"
 | 
						cfg.LDAP.LoginFilter = "(&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2))"
 | 
				
			||||||
	cfg.LDAP.SyncFilter = "(&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))"
 | 
						cfg.LDAP.SyncFilter = "(&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))"
 | 
				
			||||||
 | 
						cfg.LDAP.SyncGroupFilter = "(&(objectCategory=group))"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg.WG.DeviceNames = []string{"wg0"}
 | 
						cfg.WG.DeviceNames = []string{"wg0"}
 | 
				
			||||||
	cfg.WG.DefaultDeviceName = "wg0"
 | 
						cfg.WG.DefaultDeviceName = "wg0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,8 +16,25 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (s *Server) SyncLdapWithUserDatabase() {
 | 
					func (s *Server) SyncLdapWithUserDatabase() {
 | 
				
			||||||
	logrus.Info("starting ldap user synchronization...")
 | 
						logrus.Info("starting ldap user synchronization...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	running := true
 | 
						running := true
 | 
				
			||||||
	for running {
 | 
						for running {
 | 
				
			||||||
 | 
							// 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 {
 | 
				
			||||||
 | 
								logrus.Errorf("failed to fetch users from ldap: %v", err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							logrus.Tracef("found %d users in ldap", len(ldapUsers))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update existing LDAP users
 | 
				
			||||||
 | 
							s.updateLdapUsers(ldapUsers, ldapGroups)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Disable missing LDAP users
 | 
				
			||||||
 | 
							s.disableMissingLdapUsers(ldapUsers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Select blocks until one of the cases happens
 | 
							// Select blocks until one of the cases happens
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case <-time.After(1 * time.Minute):
 | 
							case <-time.After(1 * time.Minute):
 | 
				
			||||||
@@ -27,26 +44,11 @@ func (s *Server) SyncLdapWithUserDatabase() {
 | 
				
			|||||||
			running = false
 | 
								running = false
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Main work here
 | 
					 | 
				
			||||||
		logrus.Trace("syncing ldap users to database...")
 | 
					 | 
				
			||||||
		ldapUsers, err := ldap.FindAllUsers(&s.config.LDAP)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			logrus.Errorf("failed to fetch users from ldap: %v", err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		logrus.Tracef("found %d users in ldap", len(ldapUsers))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Update existing LDAP users
 | 
					 | 
				
			||||||
		s.updateLdapUsers(ldapUsers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Disable missing LDAP users
 | 
					 | 
				
			||||||
		s.disableMissingLdapUsers(ldapUsers)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	logrus.Info("ldap user synchronization stopped")
 | 
						logrus.Info("ldap user synchronization stopped")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData) bool {
 | 
					func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData, ldapGroupData []ldap.RawLdapData) bool {
 | 
				
			||||||
	if s.config.LDAP.EveryoneAdmin {
 | 
						if s.config.LDAP.EveryoneAdmin {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -58,11 +60,16 @@ func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData) bool {
 | 
				
			|||||||
		if s.config.LDAP.AdminLdapGroup_.Equal(dn) {
 | 
							if s.config.LDAP.AdminLdapGroup_.Equal(dn) {
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							for _, group2 := range ldapGroupData {
 | 
				
			||||||
 | 
								if group2.DN == string(group) {
 | 
				
			||||||
 | 
									return s.userIsInAdminGroup(&group2, ldapGroupData)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s Server) userChangedInLdap(user *users.User, ldapData *ldap.RawLdapData) bool {
 | 
					func (s Server) userChangedInLdap(user *users.User, ldapData *ldap.RawLdapData, ldapGroupData []ldap.RawLdapData) bool {
 | 
				
			||||||
	if user.Firstname != ldapData.Attributes[s.config.LDAP.FirstNameAttribute] {
 | 
						if user.Firstname != ldapData.Attributes[s.config.LDAP.FirstNameAttribute] {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -83,7 +90,7 @@ func (s Server) userChangedInLdap(user *users.User, ldapData *ldap.RawLdapData)
 | 
				
			|||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if user.IsAdmin != s.userIsInAdminGroup(ldapData) {
 | 
						if user.IsAdmin != s.userIsInAdminGroup(ldapData, ldapGroupData) {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,7 +133,7 @@ func (s *Server) disableMissingLdapUsers(ldapUsers []ldap.RawLdapData) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *Server) updateLdapUsers(ldapUsers []ldap.RawLdapData) {
 | 
					func (s *Server) updateLdapUsers(ldapUsers []ldap.RawLdapData, ldapGroups []ldap.RawLdapData) {
 | 
				
			||||||
	for i := range ldapUsers {
 | 
						for i := range ldapUsers {
 | 
				
			||||||
		if ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute] == "" {
 | 
							if ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute] == "" {
 | 
				
			||||||
			logrus.Tracef("skipping sync of %s, empty email attribute", ldapUsers[i].DN)
 | 
								logrus.Tracef("skipping sync of %s, empty email attribute", ldapUsers[i].DN)
 | 
				
			||||||
@@ -152,13 +159,13 @@ func (s *Server) updateLdapUsers(ldapUsers []ldap.RawLdapData) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Sync attributes from ldap
 | 
							// Sync attributes from ldap
 | 
				
			||||||
		if s.userChangedInLdap(user, &ldapUsers[i]) {
 | 
							if s.userChangedInLdap(user, &ldapUsers[i], ldapGroups) {
 | 
				
			||||||
			logrus.Debugf("updating ldap user %s", user.Email)
 | 
								logrus.Debugf("updating ldap user %s", user.Email)
 | 
				
			||||||
			user.Firstname = ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute]
 | 
								user.Firstname = ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute]
 | 
				
			||||||
			user.Lastname = ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute]
 | 
								user.Lastname = ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute]
 | 
				
			||||||
			user.Email = ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute]
 | 
								user.Email = ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute]
 | 
				
			||||||
			user.Phone = ldapUsers[i].Attributes[s.config.LDAP.PhoneAttribute]
 | 
								user.Phone = ldapUsers[i].Attributes[s.config.LDAP.PhoneAttribute]
 | 
				
			||||||
			user.IsAdmin = s.userIsInAdminGroup(&ldapUsers[i])
 | 
								user.IsAdmin = s.userIsInAdminGroup(&ldapUsers[i], ldapGroups)
 | 
				
			||||||
			user.Source = users.UserSourceLdap
 | 
								user.Source = users.UserSourceLdap
 | 
				
			||||||
			user.DeletedAt = gorm.DeletedAt{} // Not deleted
 | 
								user.DeletedAt = gorm.DeletedAt{} // Not deleted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user