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
 | 
			
		||||
	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_ *gldap.DN `yaml:"-"`
 | 
			
		||||
	EveryoneAdmin   bool      `yaml:"everyoneAdmin" envconfig:"LDAP_EVERYONE_ADMIN"`
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,13 @@ import (
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ObjectType int64
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Users  ObjectType = 1
 | 
			
		||||
	Groups ObjectType = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RawLdapData struct {
 | 
			
		||||
	DN            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)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.WithMessage(err, "failed to open ldap connection")
 | 
			
		||||
	}
 | 
			
		||||
	defer Close(client)
 | 
			
		||||
 | 
			
		||||
	// Search all users
 | 
			
		||||
	attrs := []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
 | 
			
		||||
		cfg.PhoneAttribute, cfg.GroupMemberAttribute}
 | 
			
		||||
	searchRequest := ldap.NewSearchRequest(
 | 
			
		||||
		cfg.BaseDN,
 | 
			
		||||
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
 | 
			
		||||
		cfg.SyncFilter, attrs, nil,
 | 
			
		||||
	)
 | 
			
		||||
	var searchRequest *ldap.SearchRequest
 | 
			
		||||
	var attrs []string
 | 
			
		||||
 | 
			
		||||
	if objType == Users {
 | 
			
		||||
		// Search all users
 | 
			
		||||
		attrs = []string{"dn", cfg.EmailAttribute, cfg.EmailAttribute, cfg.FirstNameAttribute, cfg.LastNameAttribute,
 | 
			
		||||
			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)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -114,6 +114,7 @@ func NewConfig() *Config {
 | 
			
		||||
	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.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.DefaultDeviceName = "wg0"
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,25 @@ import (
 | 
			
		||||
 | 
			
		||||
func (s *Server) SyncLdapWithUserDatabase() {
 | 
			
		||||
	logrus.Info("starting ldap user synchronization...")
 | 
			
		||||
 | 
			
		||||
	running := true
 | 
			
		||||
	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 {
 | 
			
		||||
		case <-time.After(1 * time.Minute):
 | 
			
		||||
@@ -27,26 +44,11 @@ func (s *Server) SyncLdapWithUserDatabase() {
 | 
			
		||||
			running = false
 | 
			
		||||
			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")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData) bool {
 | 
			
		||||
func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData, ldapGroupData []ldap.RawLdapData) bool {
 | 
			
		||||
	if s.config.LDAP.EveryoneAdmin {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
@@ -58,11 +60,16 @@ func (s Server) userIsInAdminGroup(ldapData *ldap.RawLdapData) bool {
 | 
			
		||||
		if s.config.LDAP.AdminLdapGroup_.Equal(dn) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		for _, group2 := range ldapGroupData {
 | 
			
		||||
			if group2.DN == string(group) {
 | 
			
		||||
				return s.userIsInAdminGroup(&group2, ldapGroupData)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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] {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
@@ -83,7 +90,7 @@ func (s Server) userChangedInLdap(user *users.User, ldapData *ldap.RawLdapData)
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user.IsAdmin != s.userIsInAdminGroup(ldapData) {
 | 
			
		||||
	if user.IsAdmin != s.userIsInAdminGroup(ldapData, ldapGroupData) {
 | 
			
		||||
		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 {
 | 
			
		||||
		if ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute] == "" {
 | 
			
		||||
			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
 | 
			
		||||
		if s.userChangedInLdap(user, &ldapUsers[i]) {
 | 
			
		||||
		if s.userChangedInLdap(user, &ldapUsers[i], ldapGroups) {
 | 
			
		||||
			logrus.Debugf("updating ldap user %s", user.Email)
 | 
			
		||||
			user.Firstname = ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute]
 | 
			
		||||
			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])
 | 
			
		||||
			user.IsAdmin = s.userIsInAdminGroup(&ldapUsers[i], ldapGroups)
 | 
			
		||||
			user.Source = users.UserSourceLdap
 | 
			
		||||
			user.DeletedAt = gorm.DeletedAt{} // Not deleted
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user