Newer
Older
wg-portal / internal / server / ldapsync.go
package server

import (
	"time"

	"github.com/h44z/wg-portal/internal/ldap"
	"github.com/h44z/wg-portal/internal/users"
	"github.com/sirupsen/logrus"
	"gorm.io/gorm"
)

func (s *Server) SyncLdapWithUserDatabase() {
	logrus.Info("starting ldap user synchronization...")
	running := true
	for running {
		// Select blocks until one of the cases happens
		select {
		case <-time.After(1 * time.Minute):
			// Sleep for 1 minute
		case <-s.ctx.Done():
			logrus.Trace("ldap-sync shutting down (context ended)...")
			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
		}

		for i := range ldapUsers {
			// prefilter
			if ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute] == "" ||
				ldapUsers[i].Attributes[s.config.LDAP.FirstNameAttribute] == "" ||
				ldapUsers[i].Attributes[s.config.LDAP.LastNameAttribute] == "" {
				continue
			}

			user, err := s.users.GetOrCreateUserUnscoped(ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute])
			if err != nil {
				logrus.Errorf("failed to get/create user %s in database: %v", ldapUsers[i].Attributes[s.config.LDAP.EmailAttribute], err)
			}

			// check if user should be deactivated
			ldapDeactivated := false
			switch s.config.LDAP.Type {
			case ldap.TypeActiveDirectory:
				ldapDeactivated = ldap.IsActiveDirectoryUserDisabled(ldapUsers[i].Attributes[s.config.LDAP.DisabledAttribute])
			case ldap.TypeOpenLDAP:
				ldapDeactivated = ldap.IsOpenLdapUserDisabled(ldapUsers[i].Attributes[s.config.LDAP.DisabledAttribute])
			}

			// check if user has been disabled in ldap, update peers accordingly
			if ldapDeactivated != user.DeletedAt.Valid {
				if ldapDeactivated {
					// disable all peers for the given user
					for _, peer := range s.peers.GetPeersByMail(user.Email) {
						now := time.Now()
						peer.DeactivatedAt = &now
						if err = s.UpdatePeer(peer, now); err != nil {
							logrus.Errorf("failed to update deactivated peer %s: %v", peer.PublicKey, err)
						}
					}
				} else {
					// enable all peers for the given user
					for _, peer := range s.peers.GetPeersByMail(user.Email) {
						now := time.Now()
						peer.DeactivatedAt = nil
						if err = s.UpdatePeer(peer, now); err != nil {
							logrus.Errorf("failed to update activated peer %s: %v", peer.PublicKey, err)
						}
					}
				}
			}

			// Sync attributes from ldap
			if s.UserChangedInLdap(user, &ldapUsers[i]) {
				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 = false
				user.Source = users.UserSourceLdap
				user.DeletedAt = gorm.DeletedAt{} // Not deleted

				for _, group := range ldapUsers[i].RawAttributes[s.config.LDAP.GroupMemberAttribute] {
					if string(group) == s.config.LDAP.AdminLdapGroup {
						user.IsAdmin = true
						break
					}
				}

				if err = s.users.UpdateUser(user); err != nil {
					logrus.Errorf("failed to update ldap user %s in database: %v", user.Email, err)
					continue
				}

				if ldapDeactivated {
					if err = s.users.DeleteUser(user); err != nil {
						logrus.Errorf("failed to delete deactivated user %s in database: %v", user.Email, err)
						continue
					}
				}
			}
		}
	}
	logrus.Info("ldap user synchronization stopped")
}

func (s Server) UserChangedInLdap(user *users.User, ldapData *ldap.RawLdapData) bool {
	if user.Firstname != ldapData.Attributes[s.config.LDAP.FirstNameAttribute] {
		return true
	}
	if user.Lastname != ldapData.Attributes[s.config.LDAP.LastNameAttribute] {
		return true
	}
	if user.Email != ldapData.Attributes[s.config.LDAP.EmailAttribute] {
		return true
	}
	if user.Phone != ldapData.Attributes[s.config.LDAP.PhoneAttribute] {
		return true
	}

	ldapDeactivated := false
	switch s.config.LDAP.Type {
	case ldap.TypeActiveDirectory:
		ldapDeactivated = ldap.IsActiveDirectoryUserDisabled(ldapData.Attributes[s.config.LDAP.DisabledAttribute])
	case ldap.TypeOpenLDAP:
		ldapDeactivated = ldap.IsOpenLdapUserDisabled(ldapData.Attributes[s.config.LDAP.DisabledAttribute])
	}
	if ldapDeactivated != user.DeletedAt.Valid {
		return true
	}

	ldapAdmin := false
	for _, group := range ldapData.RawAttributes[s.config.LDAP.GroupMemberAttribute] {
		if string(group) == s.config.LDAP.AdminLdapGroup {
			ldapAdmin = true
			break
		}
	}
	if user.IsAdmin != ldapAdmin {
		return true
	}

	return false
}