Andrea Fazzi 5 лет назад
Родитель
Сommit
bbdc0bd981

+ 3 - 3
Godeps/Godeps.json

@@ -67,7 +67,7 @@
 		},
 		{
 			"ImportPath": "github.com/remogatto/slicediff",
-			"Rev": "5db6595c4621647a2de9e82b7b1eccdb13b7aff3"
+			"Rev": "d19e1ebfb964929670d755944d08cbabbe3f9c2a"
 		},
 		{
 			"ImportPath": "github.com/robfig/cron",
@@ -81,11 +81,11 @@
 		},
 		{
 			"ImportPath": "gogs.carducci-dante.gov.it/karmen/client",
-			"Rev": "18e148968439edd35c33d117263e938cf03fb2a1"
+			"Rev": "ccac10c3d0409ebc87486006c94226f58d5b43a7"
 		},
 		{
 			"ImportPath": "gogs.carducci-dante.gov.it/karmen/ldap",
-			"Rev": "135235df9074fbeff395a0e0dc3e8656160e2c6a"
+			"Rev": "6b648ede6a663dc4df7c87f5bd1668f45c6dcb67"
 		},
 		{
 			"ImportPath": "gogs.carducci-dante.gov.it/karmen/util/template",

+ 1 - 0
config/config.go

@@ -78,6 +78,7 @@ type ConfigT struct {
 	Sync struct {
 		Schedule string
 		SafeRun  bool `yaml:"safe_run"`
+		Verbose  bool
 	}
 }
 

+ 89 - 88
cron/sync/sync.go

@@ -23,13 +23,10 @@ import (
 	tpl_util "gogs.carducci-dante.gov.it/karmen/util/template"
 )
 
-type TeachersDsts []*ldap.Entry
-type TeachersSrcs []*orm.Teacher
+type entriesToDN []*ldap.Entry
 
-type TeachersMLSrcs []*orm.Teacher
-
-func (d TeachersDsts) Strings() (result []string) {
-	for _, entry := range d {
+func (entries entriesToDN) Convert() (result []string) {
+	for _, entry := range entries {
 		result = append(
 			result,
 			entry.DN,
@@ -38,43 +35,34 @@ func (d TeachersDsts) Strings() (result []string) {
 	return
 }
 
-func (s TeachersSrcs) Strings() (result []string) {
-	for _, teacher := range s {
+type usersToDNConverter []orm.User
+
+func (users usersToDNConverter) Convert() (result []string) {
+	for _, user := range users {
 		result = append(
 			result,
-			fmt.Sprintf("cn=%s %s,%s", teacher.Name, teacher.Surname, "ou=Docenti,ou=Persone,dc=carducci-dante,dc=gov,dc=it"),
+			user.DN(),
 		)
 	}
 	return
 }
 
-type TeachersGroupDsts []string
-type TeachersGroupSrcs []*orm.Teacher
+type usersToUsernamesConverter []orm.User
 
-func (d TeachersGroupDsts) Strings() (result []string) {
-	return d
-}
-
-func (s TeachersGroupSrcs) Strings() (result []string) {
-	for _, teacher := range s {
+func (users usersToUsernamesConverter) Convert() (result []string) {
+	for _, user := range users {
 		result = append(
 			result,
-			teacher.Username(),
+			user.Username(),
 		)
 	}
 	return
 }
 
-type TeachersMLDsts []*orm.Teacher
+type SliceToStringSlicer []string
 
-func (s TeachersMLDsts) Strings() (result []string) {
-	for _, teacher := range s {
-		result = append(
-			result,
-			fmt.Sprintf("cn=%s %s,%s", teacher.Name, teacher.Surname, "ou=Docenti,ou=Persone,dc=carducci-dante,dc=gov,dc=it"),
-		)
-	}
-	return
+func (slice SliceToStringSlicer) Convert() (result []string) {
+	return slice
 }
 
 type SyncJob struct {
@@ -85,21 +73,27 @@ func NewSyncJob(conf *config.ConfigT) *SyncJob {
 	return &SyncJob{conf}
 }
 
-func (syncJob *SyncJob) sendMail(teacher *orm.Teacher, tpl *template.Template) error {
+func logf(fmt string, args ...interface{}) {
+	if config.Config.Sync.Verbose {
+		log.Printf(fmt, args)
+	}
+}
+
+func (syncJob *SyncJob) sendMail(user orm.User, tpl *template.Template) error {
 	var buf bytes.Buffer
 
-	err := tpl.Execute(&buf, teacher)
+	err := tpl.Execute(&buf, user)
 	if err != nil {
 		return err
 	}
 
 	if !syncJob.conf.Sync.SafeRun {
-		log.Printf("SEND user credential to %s...", teacher.AltEmail)
+		logf("SEND user credential to %s...", user.GetAltEmail())
 		m := gomail.NewMessage()
 		m.SetHeader("From", syncJob.conf.Smtp.From)
-		m.SetHeader("To", teacher.AltEmail)
+		m.SetHeader("To", user.GetAltEmail())
 		m.SetHeader("Cc", syncJob.conf.Smtp.Cc)
-		m.SetHeader("Subject", fmt.Sprintf("Attivazione dell'utenza %s per l'accesso ai servizi informatici del Liceo \"Carducci-Dante\" di Trieste", teacher.CompleteName()))
+		m.SetHeader("Subject", fmt.Sprintf("Attivazione dell'utenza %s per l'accesso ai servizi informatici del Liceo \"Carducci-Dante\" di Trieste", user.CompleteName()))
 		m.SetBody("text/plain", buf.String())
 
 		d := gomail.NewDialer(syncJob.conf.Smtp.Host, syncJob.conf.Smtp.Port, syncJob.conf.Smtp.Username, syncJob.conf.Smtp.Password)
@@ -109,24 +103,23 @@ func (syncJob *SyncJob) sendMail(teacher *orm.Teacher, tpl *template.Template) e
 			return err
 		}
 	} else {
-		log.Printf("Credentials would be sent to %s, new password is %s", teacher.AltEmail, teacher.PlainPassword)
+		logf("Credentials would be sent to %s, new password is %s", user.GetAltEmail(), user.GetPlainPassword())
 	}
 
 	return nil
 }
 
-func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClient *karmen_client.Client, entries []*ldap.Entry, teachers []*orm.Teacher) error {
-
+func (syncJob *SyncJob) SyncUsers(ldapClient *karmen_ldap.Client, karmenClient *karmen_client.Client, entries []*ldap.Entry, users []orm.User) error {
 	mailTpl, err := tpl_util.LoadTextTemplate("cron/sync/mail.tpl")
 	if err != nil {
 		log.Println(err)
 	}
 
-	actions := slicediff.Diff(TeachersDsts(entries), TeachersSrcs(teachers))
+	actions := slicediff.Diff(entriesToDN(entries).Convert, usersToDNConverter(users).Convert)
 
-	toBeAdded := make([]*orm.Teacher, 0)
+	toBeAdded := make([]orm.User, 0)
 	toBeRemoved := make([]string, 0)
-	toBeUpdated := make([]*orm.Teacher, 0)
+	toBeUpdated := make([]orm.User, 0)
 
 	for _, a := range actions {
 		switch a.Type {
@@ -134,15 +127,15 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 			toBeRemoved = append(toBeRemoved, entries[a.Id].DN)
 
 		case slicediff.Add:
-			teacher := teachers[a.Id]
-			if !teacher.Exclude {
-				toBeAdded = append(toBeAdded, teacher)
+			user := users[a.Id]
+			if !user.GetExclude() {
+				toBeAdded = append(toBeAdded, user)
 			}
 
 		case slicediff.Update:
-			teacher := teachers[a.Id]
-			if !teacher.Exclude && teacher.Regenerate {
-				toBeUpdated = append(toBeUpdated, teacher)
+			user := users[a.Id]
+			if !user.GetExclude() && user.GetRegenerate() {
+				toBeUpdated = append(toBeUpdated, user)
 			}
 
 		}
@@ -151,31 +144,31 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 	// Add
 
 	if !syncJob.conf.Sync.SafeRun {
-		log.Printf("ADDING %d users", len(toBeAdded))
-		for _, teacher := range toBeAdded {
+		logf("ADDING %d users", len(toBeAdded))
+		for _, user := range toBeAdded {
 			password, err := password.Generate(8, 2, 0, false, true)
 			if err != nil {
 				return err
 			}
 
-			teacher.PlainPassword = password
+			user.SetPlainPassword(password)
 
-			err = ldapClient.AddTeacher(teacher)
+			err = ldapClient.AddUser(user)
 			if err != nil {
 				return err
 			}
 
-			log.Printf("ADD teacher %s", teacher.CompleteName())
+			logf("ADD teacher %s", user.CompleteName())
 
-			if teacher.Regenerate {
-				err = syncJob.sendMail(teacher, mailTpl)
+			if user.GetRegenerate() {
+				err = syncJob.sendMail(user, mailTpl)
 				if err != nil {
 					return err
 				}
 
-				teacher.Regenerate = false
+				user.SetRegenerate(false)
 
-				err = karmenClient.UpdateTeacher(teacher)
+				err = karmenClient.UpdateUser(user.(*orm.Credential), "teachers")
 				if err != nil {
 					return err
 				}
@@ -187,16 +180,16 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 		if len(toBeAdded) == 0 {
 			log.Println("No users WOULD BE added.")
 		} else {
-			log.Printf("%d users WOULD BE added...", len(toBeAdded))
+			logf("%d users WOULD BE added...", len(toBeAdded))
 			for _, teacher := range toBeAdded {
 				log.Println(teacher.CompleteName())
-				if teacher.Regenerate {
+				if teacher.GetRegenerate() {
 					password, err := password.Generate(8, 2, 0, false, true)
 					if err != nil {
 						return err
 					}
 
-					teacher.PlainPassword = password
+					teacher.SetPlainPassword(password)
 					err = syncJob.sendMail(teacher, mailTpl)
 					if err != nil {
 						return err
@@ -211,18 +204,18 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 
 	if !syncJob.conf.Sync.SafeRun {
 		if len(toBeRemoved) > 0 {
-			log.Printf("%d will be REMOVED...", len(toBeRemoved))
+			logf("%d will be REMOVED...", len(toBeRemoved))
 		}
 		for _, entry := range toBeRemoved {
 			err := ldapClient.DeleteByDN(entry)
 			if err != nil {
 				return err
 			}
-			log.Printf("REMOVE %s", entry)
+			logf("REMOVE %s", entry)
 		}
 	} else {
 		if len(toBeRemoved) > 0 {
-			log.Printf("%d users WOULD BE removed", len(toBeRemoved))
+			logf("%d users WOULD BE removed", len(toBeRemoved))
 		}
 		for _, entry := range toBeRemoved {
 			log.Println(entry)
@@ -233,28 +226,28 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 
 	if !syncJob.conf.Sync.SafeRun {
 		if len(toBeUpdated) > 0 {
-			log.Printf("UPDATING %d users", len(toBeUpdated))
+			logf("UPDATING %d users", len(toBeUpdated))
 		}
 		for _, teacher := range toBeUpdated {
-			if teacher.Regenerate {
+			if teacher.GetRegenerate() {
 				password, err := password.Generate(8, 2, 0, false, true)
 				if err != nil {
 					return err
 				}
 
-				teacher.PlainPassword = password
+				teacher.SetPlainPassword(password)
 				err = syncJob.sendMail(teacher, mailTpl)
 				if err != nil {
 					return err
 				}
 
-				teacher.Regenerate = false
+				teacher.SetRegenerate(false)
 
-				err = karmenClient.UpdateTeacher(teacher)
+				err = karmenClient.UpdateUser(teacher.(*orm.Credential), "teachers")
 				if err != nil {
 					return err
 				}
-				err = ldapClient.UpdateTeacherPassword(teacher, teacher.PlainPassword)
+				err = ldapClient.UpdateUserPassword(teacher.(*orm.Credential), teacher.GetPlainPassword())
 				if err != nil {
 					return err
 
@@ -264,7 +257,7 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 
 	} else {
 		if len(toBeRemoved) > 0 {
-			log.Printf("%d users WOULD BE updated...", len(toBeUpdated))
+			logf("%d users WOULD BE updated...", len(toBeUpdated))
 		}
 		for _, teacher := range toBeUpdated {
 			log.Println(teacher.CompleteName())
@@ -274,7 +267,7 @@ func (syncJob *SyncJob) syncTeachers(ldapClient *karmen_ldap.Client, karmenClien
 	return nil
 }
 
-func (syncJob *SyncJob) syncGroup(ldapClient *karmen_ldap.Client, groupDN string, teachers []*orm.Teacher) error {
+func (syncJob *SyncJob) syncGroup(ldapClient *karmen_ldap.Client, groupDN string, users []orm.User) error {
 	actions := make(map[string]*slicediff.Action)
 	entries, err := ldapClient.GroupMembers(groupDN)
 	if err != nil {
@@ -284,12 +277,12 @@ func (syncJob *SyncJob) syncGroup(ldapClient *karmen_ldap.Client, groupDN string
 	values := entries[0].Attributes[0].Values
 
 	if strings.Contains(groupDN, "Mailing Lists") {
-		actions = slicediff.Diff(TeachersGroupDsts(values), TeachersMLDsts(teachers))
+		actions = slicediff.Diff(SliceToStringSlicer(values).Convert, usersToDNConverter(users).Convert)
 	} else {
-		actions = slicediff.Diff(TeachersGroupDsts(values), TeachersGroupSrcs(teachers))
+		actions = slicediff.Diff(SliceToStringSlicer(values).Convert, usersToUsernamesConverter(users).Convert)
 	}
 
-	toBeAdded := make([]*orm.Teacher, 0)
+	toBeAdded := make([]orm.User, 0)
 	toBeRemoved := make([]string, 0)
 
 	for _, a := range actions {
@@ -298,9 +291,9 @@ func (syncJob *SyncJob) syncGroup(ldapClient *karmen_ldap.Client, groupDN string
 			toBeRemoved = append(toBeRemoved, values[a.Id])
 
 		case slicediff.Add:
-			teacher := teachers[a.Id]
-			if !teacher.Exclude {
-				toBeAdded = append(toBeAdded, teacher)
+			user := users[a.Id]
+			if !user.GetExclude() {
+				toBeAdded = append(toBeAdded, user)
 			}
 
 		}
@@ -308,18 +301,18 @@ func (syncJob *SyncJob) syncGroup(ldapClient *karmen_ldap.Client, groupDN string
 
 	if !syncJob.conf.Sync.SafeRun {
 		if len(toBeAdded) > 0 {
-			log.Printf("ADDING %d teachers to %s group", len(toBeAdded), groupDN)
+			logf("ADDING %d teachers to %s group", len(toBeAdded), groupDN)
 		}
 		for _, teacher := range toBeAdded {
 			log.Println(teacher.CompleteName())
-			err = ldapClient.AddTeacherToGroup(teacher, groupDN)
+			err = ldapClient.AddUserToGroup(teacher.(*orm.Credential), groupDN)
 			if err != nil {
 				return err
 			}
 		}
 	} else {
 		if len(toBeAdded) > 0 {
-			log.Printf("%d users WOULD BE ADDED to %s group", len(toBeAdded), groupDN)
+			logf("%d users WOULD BE ADDED to %s group", len(toBeAdded), groupDN)
 		}
 		for _, teacher := range toBeAdded {
 			log.Println(teacher.CompleteName())
@@ -328,15 +321,15 @@ func (syncJob *SyncJob) syncGroup(ldapClient *karmen_ldap.Client, groupDN string
 
 	if !syncJob.conf.Sync.SafeRun {
 		if len(toBeRemoved) > 0 {
-			log.Printf("REMOVING %d users from %s group...", len(toBeRemoved), groupDN)
+			logf("REMOVING %d users from %s group...", len(toBeRemoved), groupDN)
 		}
 		for _, value := range toBeRemoved {
-			log.Printf("REMOVE %s from %s group.", value, groupDN)
-			ldapClient.RemoveTeacherFromGroupByMemberValue(value, groupDN)
+			logf("REMOVE %s from %s group.", value, groupDN)
+			ldapClient.RemoveUserFromGroupByMemberValue(value, groupDN)
 		}
 	} else {
 		if len(toBeRemoved) > 0 {
-			log.Printf("%d users WOULD BE REMOVED FROM %s group", len(toBeRemoved), groupDN)
+			logf("%d users WOULD BE REMOVED FROM %s group", len(toBeRemoved), groupDN)
 		}
 		for _, entry := range toBeRemoved {
 			log.Println(entry)
@@ -351,7 +344,7 @@ func (syncJob *SyncJob) Run() {
 		log.Println("Running in SAFE MODE...")
 	}
 
-	log.Printf("Connecting to karmen at %s...", syncJob.conf.Url)
+	logf("Connecting to karmen at %s...", syncJob.conf.Url)
 	karmenClient, err := karmen_client.Dial(syncJob.conf.Url, syncJob.conf.Admin.Username, syncJob.conf.Admin.Password)
 	if err != nil {
 		log.Println(err)
@@ -362,14 +355,14 @@ func (syncJob *SyncJob) Run() {
 		log.Println(err)
 	}
 
-	log.Printf("Connecting to LDAP at %s...", syncJob.conf.Ldap.Host)
+	logf("Connecting to LDAP at %s...", syncJob.conf.Ldap.Host)
 	ldapClient, err := karmen_ldap.NewClient(syncJob.conf.Ldap.Host, syncJob.conf)
 	if err != nil {
 		log.Println(err)
 	}
 
 	log.Println("Retrieving teachers from LDAP...")
-	entries, err := ldapClient.Teachers()
+	entries, err := ldapClient.Users("ou=Docenti,ou=Persone,dc=carducci-dante,dc=gov,dc=it")
 	if err != nil {
 		log.Println(err)
 	}
@@ -380,30 +373,38 @@ func (syncJob *SyncJob) Run() {
 		log.Println(err)
 	}
 
+	users := make([]orm.User, 0)
+	for _, teacher := range teachers {
+		users = append(users, teacher)
+	}
 	log.Println("Sync teachers...")
-	if err := syncJob.syncTeachers(ldapClient, karmenClient, entries, teachers); err != nil {
+	if err := syncJob.SyncUsers(ldapClient, karmenClient, entries, users); err != nil {
 		panic(err)
 	}
 
 	log.Println("Sync 'Docenti' group...")
-	if err := syncJob.syncGroup(ldapClient, "cn=Docenti", teachers); err != nil {
+	if err := syncJob.syncGroup(ldapClient, "cn=Docenti", users); err != nil {
 		panic(err)
 	}
 
 	log.Println("Sync 'Tutti i docenti' ML...")
-	if err := syncJob.syncGroup(ldapClient, "cn=Tutti i docenti,ou=Mailing Lists", teachers); err != nil {
+	if err := syncJob.syncGroup(ldapClient, "cn=Tutti i docenti,ou=Mailing Lists", users); err != nil {
 		panic(err)
 	}
 
 	log.Println("Sync Departments and MLs...")
 	for _, department := range departments {
+		users := make([]orm.User, 0)
+		for _, teacher := range department.Teachers {
+			users = append(users, teacher)
+		}
 		group := fmt.Sprintf("cn=%s,ou=Mailing Lists", department.Name)
-		err = syncJob.syncGroup(ldapClient, group, department.Teachers)
+		err = syncJob.syncGroup(ldapClient, group, users)
 		if err != nil {
 			panic(err)
 		}
 		group = fmt.Sprintf("cn=%s,ou=Dipartimenti", department.Name)
-		err = syncJob.syncGroup(ldapClient, group, department.Teachers)
+		err = syncJob.syncGroup(ldapClient, group, users)
 		if err != nil {
 			panic(err)
 		}

+ 8 - 0
cron/sync/test/Makefile

@@ -0,0 +1,8 @@
+PHONY: all
+
+test:
+	docker-compose -f "karmen_test_sync/docker-compose.yml" down
+	docker-compose -f "karmen_test_sync/docker-compose.yml" up -d --build
+	go test
+
+all: test

+ 22 - 0
cron/sync/test/config.yaml

@@ -0,0 +1,22 @@
+domain: "foo.org"
+url: "http://localhost:3000"
+
+admin:
+  username: "admin"
+  password: "admin"
+
+ldap:
+  host: "localhost:389"
+  admin_cn: "admin"
+  admin_password: "admin"
+  people_dn: "ou=People"
+  teachers_dn: "ou=People"
+  groups_dn: "ou=Groups"
+  maildir_base_parh: "/var/mail/foo.org"
+  mail_quota: "10240"
+  first_uid_number: "5000"
+  mail_gid_number: "5000"
+
+sync:
+  safe_run: false
+  verbose: false

+ 10 - 0
cron/sync/test/karmen_test_sync/db.env

@@ -0,0 +1,10 @@
+MYSQL_ROOT_PASSWORD=karmen
+MYSQL_PASSWORD=karmen
+MYSQL_DATABASE=karmen_dev
+MYSQL_USER=karmen
+
+
+
+
+
+

+ 48 - 0
cron/sync/test/karmen_test_sync/docker-compose.yml

@@ -0,0 +1,48 @@
+version: "3"
+
+services:
+
+  karmen_server:
+    build: ../../../../
+    ports:
+      - 3000:3000
+    environment:
+      - DB_HOST=db
+      - DB_PORT=3306
+    ports:
+      - 3000:3000
+      
+  db:
+    image: mariadb
+    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
+    restart: always
+    volumes:
+      - db:/var/lib/mysql
+      - ./sql:/docker-entrypoint-initdb.d
+    env_file:
+      - db.env
+
+  ldap_server:
+    restart: always
+    build: ./image
+    command: --loglevel debug
+    domainname: foo.org
+    hostname: foo.org
+    ports:
+      - 389:389
+    environment:
+      - LDAP_ORGANISATION=Foo Organization
+      - LDAP_DOMAIN=foo.org
+      - LDAP_ADMIN_PASSWORD=admin
+
+  ldap_web_client:
+    restart: always
+    image: osixia/phpldapadmin
+    environment:
+      - PHPLDAPADMIN_LDAP_HOSTS=ldap_server
+      - PHPLDAPADMIN_HTTPS=false
+    ports:
+      - 3001:80
+      
+volumes:
+  db:

+ 9 - 0
cron/sync/test/karmen_test_sync/image/Dockerfile

@@ -0,0 +1,9 @@
+FROM osixia/openldap:1.2.1
+MAINTAINER Andrea fazzi <andrea.fazzi@carducci-dante.gov.it>
+
+ADD bootstrap /container/service/slapd/assets/config/bootstrap
+RUN rm /container/service/slapd/assets/config/bootstrap/schema/mmc/mail.schema
+
+# ADD certs /container/service/slapd/assets/certs
+#ADD environment /container/environment/01-custom
+

+ 52 - 0
cron/sync/test/karmen_test_sync/image/bootstrap/ldif/foo.org.ldif

@@ -0,0 +1,52 @@
+version: 1
+
+dn: ou=Groups,dc=foo,dc=org
+changetype: add
+objectclass: organizationalUnit
+objectclass: top
+ou: Groups
+
+dn: ou=Mailing Lists,ou=Groups,dc=foo,dc=org
+changetype: add
+objectclass: organizationalUnit
+objectclass: top
+ou: Mailing Lists
+
+dn: cn=Teachers,ou=Mailing Lists,ou=Groups,dc=foo,dc=org
+changetype: add
+cn: Teachers
+member: cn=John DOE,ou=People,dc=foo,dc=org
+objectclass: groupOfNames
+objectclass: top
+
+dn: ou=People,dc=foo,dc=org
+changetype: add
+objectclass: organizationalUnit
+objectclass: top
+ou: People
+
+dn: cn=John DOE,ou=People,dc=foo,dc=org
+changetype: add
+cn: John DOE
+gidnumber: 5043
+givenname: John Doe
+homedirectory: /home/users/john.doe
+loginshell: /bin/sh
+mail: john.doe@foo.org
+mailenabled: TRUE
+mailgidnumber: 5000
+mailhomedirectory: /var/mail/foo.org/john.doe/
+mailquota: 10240
+mailstoragedirectory: maildir:/var/mail/foo.org/john.doe/
+mailuidnumber: 5043
+objectclass: inetOrgPerson
+objectclass: posixAccount
+objectclass: top
+objectclass: PostfixBookMailAccount
+objectclass: organizationalPerson
+objectclass: extensibleObject
+sn: DOE
+uid: john.doe
+uidnumber: 5043
+uniqueidentifier: john.doe
+userpassword: {SSHA}hMdtrV6Ai0AUBfnN08x++n12ARvqSsxy

+ 70 - 0
cron/sync/test/karmen_test_sync/image/bootstrap/schema/postfix-book.schema

@@ -0,0 +1,70 @@
+# $Id$
+#
+# State of Mind
+# Private Enterprise Number: 29426
+#
+# OID prefix: 1.3.6.1.4.1.29426
+#
+# Attributes: 1.3.6.1.4.1.29426.1.10.x
+#
+
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.1 NAME 'mailHomeDirectory'
+	DESC 'The absolute path to the mail user home directory'
+        EQUALITY caseExactIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.2 NAME 'mailAlias'
+        DESC 'RFC822 Mailbox - mail alias'
+        EQUALITY caseIgnoreIA5Match
+        SUBSTR caseIgnoreIA5SubstringsMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.3 NAME 'mailUidNumber'
+        DESC 'UID required to access the mailbox'
+        EQUALITY integerMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.4 NAME 'mailGidNumber'
+        DESC 'GID required to access the mailbox'
+        EQUALITY integerMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.5 NAME 'mailEnabled'
+	DESC 'TRUE to enable, FALSE to disable account'
+        EQUALITY booleanMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.6 NAME 'mailGroupMember'
+	DESC 'Name of a mail distribution list'
+        EQUALITY caseExactIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.7 NAME 'mailQuota'
+	DESC 'Mail quota limit in kilobytes'
+        EQUALITY caseExactIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.4.1.29426.1.10.8 NAME 'mailStorageDirectory'
+	DESC 'The absolute path to the mail users mailbox'
+        EQUALITY caseExactIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+
+#
+# Objects: 1.3.6.1.4.1.29426.1.2.2.x
+#
+
+objectclass ( 1.3.6.1.4.1.29426.1.2.2.1 NAME 'PostfixBookMailAccount'
+        SUP top AUXILIARY
+	DESC 'Mail account used in Postfix Book'
+	MUST ( mail )
+        MAY ( mailHomeDirectory $ mailAlias $ mailGroupMember 
+		$ mailUidNumber $ mailGidNumber $ mailEnabled
+		$ mailQuota $mailStorageDirectory ) )
+
+objectclass ( 1.3.6.1.4.1.29426.1.2.2.2 NAME 'PostfixBookMailForward'
+        SUP top AUXILIARY
+	DESC 'Mail forward used in Postfix Book'
+	MUST ( mail $ mailAlias ))
+

+ 10 - 0
cron/sync/test/karmen_test_sync/image/environment/my-env.yaml

@@ -0,0 +1,10 @@
+# This is the default image configuration file
+# These values will persists in container environment.
+
+# All environment variables used after the container first start
+# must be defined here.
+# more information : https://github.com/osixia/docker-light-baseimage
+
+# General container configuration
+# see table 5.1 in http://www.openldap.org/doc/admin24/slapdconf2.html for the available log levels.
+LDAP_LOG_LEVEL: 256

+ 464 - 0
cron/sync/test/karmen_test_sync/sql/karmen_test.sql

@@ -0,0 +1,464 @@
+-- MySQL dump 10.16  Distrib 10.3.9-MariaDB, for debian-linux-gnu (x86_64)
+--
+-- Host: localhost    Database: karmen_dev
+-- ------------------------------------------------------
+-- Server version	10.3.9-MariaDB-1:10.3.9+maria~bionic
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `activities`
+--
+
+DROP TABLE IF EXISTS `activities`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `activities` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `class_id` int(10) unsigned DEFAULT NULL,
+  `teacher_id` int(10) unsigned DEFAULT NULL,
+  `subject_id` int(10) unsigned DEFAULT NULL,
+  `hours` int(11) DEFAULT NULL,
+  `student_id` int(10) unsigned DEFAULT NULL,
+  `supply_teacher_id` int(10) unsigned DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_activities_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `activities`
+--
+
+LOCK TABLES `activities` WRITE;
+/*!40000 ALTER TABLE `activities` DISABLE KEYS */;
+INSERT INTO `activities` VALUES (1,'2018-08-24 17:17:22','2018-08-24 17:17:22',NULL,'1 A MATEMATICA 2h',1,1,1,2,NULL,NULL),(2,'2018-08-24 17:18:01','2018-08-24 17:18:01',NULL,'1 A INGLESE 2h',1,2,3,2,NULL,NULL),(3,'2018-08-24 17:18:26','2018-08-24 17:18:26',NULL,'1 A ITALIANO 5h',1,3,2,5,NULL,NULL),(4,'2018-08-24 17:23:58','2018-08-24 17:23:58',NULL,'1 A STORIA DELL\'ARTE 2h',1,5,4,2,NULL,NULL),(5,'2018-08-24 17:24:20','2018-08-24 17:24:20',NULL,'1 A TECNOLOGIA 2h',1,4,9,2,NULL,NULL),(6,'2018-08-24 17:24:41','2018-08-24 17:24:41',NULL,'1 A FRANCESE 2h',1,7,7,2,NULL,NULL),(7,'2018-08-24 17:25:13','2018-08-24 17:25:13',NULL,'1 A RELIGIONE 2h',1,9,6,2,NULL,NULL),(8,'2018-08-24 17:25:44','2018-08-24 17:25:44',NULL,'1 A STORIA 2h',1,6,8,2,NULL,NULL),(9,'2018-08-24 17:26:15','2018-08-24 17:26:15',NULL,'1 A SCIENZE 2h',1,8,5,2,NULL,NULL),(10,'2018-09-18 09:21:28','2018-09-18 09:21:28',NULL,NULL,1,10,10,5,1,NULL),(11,'2018-09-18 09:27:33','2018-09-18 09:27:33',NULL,NULL,1,11,10,5,1,NULL),(12,'2018-09-18 09:29:43','2018-09-18 09:29:43',NULL,NULL,1,11,10,3,2,NULL),(13,'2018-10-12 11:20:39','2018-10-12 11:20:39',NULL,NULL,3,1,5,2,0,0);
+/*!40000 ALTER TABLE `activities` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `administratives`
+--
+
+DROP TABLE IF EXISTS `administratives`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `administratives` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `surname` varchar(255) DEFAULT NULL,
+  `username` varchar(255) DEFAULT NULL,
+  `password` varchar(255) DEFAULT NULL,
+  `email` varchar(255) DEFAULT NULL,
+  `telephone_number` varchar(255) DEFAULT NULL,
+  `role` int(11) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_administratives_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `administratives`
+--
+
+LOCK TABLES `administratives` WRITE;
+/*!40000 ALTER TABLE `administratives` DISABLE KEYS */;
+/*!40000 ALTER TABLE `administratives` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `classes`
+--
+
+DROP TABLE IF EXISTS `classes`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `classes` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `coordinator_id` int(10) unsigned DEFAULT NULL,
+  `minuter_id` int(10) unsigned DEFAULT NULL,
+  `alias` varchar(255) DEFAULT NULL,
+  `year` int(11) DEFAULT NULL,
+  `section` varchar(255) DEFAULT NULL,
+  `field` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_classes_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `classes`
+--
+
+LOCK TABLES `classes` WRITE;
+/*!40000 ALTER TABLE `classes` DISABLE KEYS */;
+INSERT INTO `classes` VALUES (1,'2018-08-24 17:11:29','2018-08-24 17:26:35',NULL,'1 A',3,2,NULL,NULL,NULL,NULL),(2,'2018-08-24 17:12:43','2018-08-24 17:12:43','2018-08-24 17:17:01','1 A',1,2,NULL,NULL,NULL,NULL),(3,'2018-10-12 11:20:21','2018-10-12 11:20:21',NULL,'1 B',0,0,'',1,'B','SCIENZE UMANE');
+/*!40000 ALTER TABLE `classes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `department_teachers`
+--
+
+DROP TABLE IF EXISTS `department_teachers`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `department_teachers` (
+  `department_id` int(10) unsigned NOT NULL,
+  `teacher_id` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`department_id`,`teacher_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `department_teachers`
+--
+
+LOCK TABLES `department_teachers` WRITE;
+/*!40000 ALTER TABLE `department_teachers` DISABLE KEYS */;
+/*!40000 ALTER TABLE `department_teachers` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `departments`
+--
+
+DROP TABLE IF EXISTS `departments`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `departments` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `coordinator_id` int(10) unsigned DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_departments_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `departments`
+--
+
+LOCK TABLES `departments` WRITE;
+/*!40000 ALTER TABLE `departments` DISABLE KEYS */;
+INSERT INTO `departments` VALUES (1,'2018-09-13 16:21:31','2018-09-13 16:30:10',NULL,'LINGUE STRANIERE',NULL),(4,'2018-09-18 09:20:14','2018-09-18 09:20:14',NULL,'SOSTEGNO',0);
+/*!40000 ALTER TABLE `departments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `directors`
+--
+
+DROP TABLE IF EXISTS `directors`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `directors` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `surname` varchar(255) DEFAULT NULL,
+  `username` varchar(255) DEFAULT NULL,
+  `password` varchar(255) DEFAULT NULL,
+  `email` varchar(255) DEFAULT NULL,
+  `telephone_number` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_directors_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `directors`
+--
+
+LOCK TABLES `directors` WRITE;
+/*!40000 ALTER TABLE `directors` DISABLE KEYS */;
+/*!40000 ALTER TABLE `directors` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `issues`
+--
+
+DROP TABLE IF EXISTS `issues`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `issues` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `description` varchar(255) DEFAULT NULL,
+  `type` int(10) unsigned DEFAULT NULL,
+  `reported` tinyint(1) DEFAULT NULL,
+  `teacher_id` int(10) unsigned DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_issues_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `issues`
+--
+
+LOCK TABLES `issues` WRITE;
+/*!40000 ALTER TABLE `issues` DISABLE KEYS */;
+/*!40000 ALTER TABLE `issues` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `schools`
+--
+
+DROP TABLE IF EXISTS `schools`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `schools` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `address` varchar(255) DEFAULT NULL,
+  `domain` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_schools_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `schools`
+--
+
+LOCK TABLES `schools` WRITE;
+/*!40000 ALTER TABLE `schools` DISABLE KEYS */;
+INSERT INTO `schools` VALUES (1,'2018-08-24 17:19:56','2018-08-24 17:19:56',NULL,'Scuola media inferiore \"Leonardo da Vinci\"','via Castroneria 4, Perlizzi (TN)','scuolamediadavinci.gov.it');
+/*!40000 ALTER TABLE `schools` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `students`
+--
+
+DROP TABLE IF EXISTS `students`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `students` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `surname` varchar(255) DEFAULT NULL,
+  `username` varchar(255) DEFAULT NULL,
+  `password` varchar(255) DEFAULT NULL,
+  `email` varchar(255) DEFAULT NULL,
+  `telephone_number` varchar(255) DEFAULT NULL,
+  `plain_password` varchar(255) DEFAULT NULL,
+  `class_id` int(10) unsigned DEFAULT NULL,
+  `alt_email` varchar(255) DEFAULT NULL,
+  `date_from` timestamp NULL DEFAULT NULL,
+  `date_to` timestamp NULL DEFAULT NULL,
+  `regenerate` tinyint(1) DEFAULT 0,
+  `handicap` tinyint(1) DEFAULT NULL,
+  `dsa` tinyint(1) DEFAULT NULL,
+  `bes` tinyint(1) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_students_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `students`
+--
+
+LOCK TABLES `students` WRITE;
+/*!40000 ALTER TABLE `students` DISABLE KEYS */;
+INSERT INTO `students` VALUES (1,'2018-09-18 09:12:14','2018-09-18 09:41:15',NULL,'Mario','ROSSI','','','','','',1,NULL,NULL,NULL,0,NULL,NULL,NULL),(2,'2018-09-18 09:29:07','2018-09-18 09:40:41',NULL,'Francesco','CONTINO','','','','','',1,NULL,NULL,NULL,0,NULL,NULL,NULL);
+/*!40000 ALTER TABLE `students` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `subjects`
+--
+
+DROP TABLE IF EXISTS `subjects`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `subjects` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `department_id` int(10) unsigned DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_subjects_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `subjects`
+--
+
+LOCK TABLES `subjects` WRITE;
+/*!40000 ALTER TABLE `subjects` DISABLE KEYS */;
+INSERT INTO `subjects` VALUES (1,'2018-08-24 17:09:21','2018-08-24 17:09:21',NULL,'MATEMATICA',0),(2,'2018-08-24 17:09:35','2018-08-24 17:09:35',NULL,'ITALIANO',0),(3,'2018-08-24 17:09:44','2018-08-24 17:09:44',NULL,'INGLESE',0),(4,'2018-08-24 17:21:29','2018-08-24 17:21:29',NULL,'STORIA DELL\'ARTE',0),(5,'2018-08-24 17:21:44','2018-08-24 17:21:44',NULL,'SCIENZE',0),(6,'2018-08-24 17:21:59','2018-08-24 17:21:59',NULL,'RELIGIONE',0),(7,'2018-08-24 17:22:31','2018-09-13 16:52:35',NULL,'FRANCESE',1),(8,'2018-08-24 17:23:19','2018-08-24 17:23:19',NULL,'STORIA',0),(9,'2018-08-24 17:23:33','2018-08-24 17:23:33',NULL,'TECNOLOGIA',0),(10,'2018-09-18 09:20:52','2018-09-18 09:20:52',NULL,'SOSTEGNO',4);
+/*!40000 ALTER TABLE `subjects` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `teacher_classes`
+--
+
+DROP TABLE IF EXISTS `teacher_classes`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `teacher_classes` (
+  `teacher_id` int(10) unsigned NOT NULL,
+  `class_id` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`teacher_id`,`class_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `teacher_classes`
+--
+
+LOCK TABLES `teacher_classes` WRITE;
+/*!40000 ALTER TABLE `teacher_classes` DISABLE KEYS */;
+INSERT INTO `teacher_classes` VALUES (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1);
+/*!40000 ALTER TABLE `teacher_classes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `teacher_subjects`
+--
+
+DROP TABLE IF EXISTS `teacher_subjects`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `teacher_subjects` (
+  `subject_id` int(10) unsigned NOT NULL,
+  `teacher_id` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`subject_id`,`teacher_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `teacher_subjects`
+--
+
+LOCK TABLES `teacher_subjects` WRITE;
+/*!40000 ALTER TABLE `teacher_subjects` DISABLE KEYS */;
+INSERT INTO `teacher_subjects` VALUES (1,1),(2,3),(3,2),(4,5),(5,8),(6,9),(7,7),(8,6),(9,4);
+/*!40000 ALTER TABLE `teacher_subjects` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `teachers`
+--
+
+DROP TABLE IF EXISTS `teachers`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `teachers` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `surname` varchar(255) DEFAULT NULL,
+  `username` varchar(255) DEFAULT NULL,
+  `password` varchar(255) DEFAULT NULL,
+  `email` varchar(255) DEFAULT NULL,
+  `telephone_number` varchar(255) DEFAULT NULL,
+  `hours` int(11) DEFAULT NULL,
+  `curr_hours` int(11) DEFAULT NULL,
+  `the_boss_id` int(10) unsigned DEFAULT NULL,
+  `plain_password` varchar(255) DEFAULT NULL,
+  `alt_email` varchar(255) DEFAULT NULL,
+  `date_from` timestamp NULL DEFAULT NULL,
+  `date_to` timestamp NULL DEFAULT NULL,
+  `regenerate` tinyint(1) DEFAULT 0,
+  PRIMARY KEY (`id`),
+  KEY `idx_teachers_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `teachers`
+--
+
+LOCK TABLES `teachers` WRITE;
+/*!40000 ALTER TABLE `teachers` DISABLE KEYS */;
+INSERT INTO `teachers` VALUES (1,'2018-08-24 17:06:38','2018-08-24 17:17:22',NULL,'Amilcare','AGOSTINO','amilcare.agostino','TpbbDZmSdjzOzEcm5797h2TXY/hzYWx0','amilcare.agostino@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(2,'2018-08-24 17:06:58','2018-08-24 17:18:01',NULL,'Assuntina','DONADONI','assuntina.donadoni','Fi28Y8Q76pqz3ggoAuXvMNliew9zYWx0','assuntina.donadoni@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(3,'2018-08-24 17:07:14','2018-08-24 17:18:26',NULL,'Costanza','LUNGA','costanza.lunga','//XInIkvIeMYj0+x9AsabCyA8zVzYWx0','costanza.lunga@foo.org','',18,5,0,NULL,NULL,NULL,NULL,0),(4,'2018-08-24 17:07:28','2018-08-24 17:24:20',NULL,'Giacomo','DEL BEN','giacomo.delben','TTdp/vYL+NjgJZTErlceSVwCa1lzYWx0','giacomo.delben@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(5,'2018-08-24 17:07:51','2018-08-24 17:23:58',NULL,'Giorgio','ARISTI','giorgio.aristi','bbtj5tK+TSBKd38G/zHK6TUo0JZzYWx0','giorgio.aristi@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(6,'2018-08-24 17:08:12','2018-08-24 17:25:44',NULL,'Vittoria','GIRONI','vittoria.gironi','jS5M30Y9d5JUsyXSfxU1Vz4Tmc1zYWx0','vittoria.gironi@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(7,'2018-08-24 17:08:33','2018-08-24 17:24:41',NULL,'Francesco','DELLE ROSE','francesco.dellerose','9HakZF2Oh1vZy+8J/doqR1Jpr/lzYWx0','francesco.dellerose@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(8,'2018-08-24 17:08:51','2018-08-24 17:26:15',NULL,'Andrea','PETRUZZELLI','andrea.petruzzelli','enbmvOYnUs/2IkttlR90h3oJC9dzYWx0','andrea.petruzzelli@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(9,'2018-08-24 17:09:07','2018-08-24 17:25:13',NULL,'Piero','FRANCESCHINI','piero.franceschini','eGAGAG9etZ+rehwW0A/KRWt/7CFzYWx0','piero.franceschini@foo.org','',18,2,0,NULL,NULL,NULL,NULL,0),(10,'2018-09-18 09:20:32','2018-09-18 09:20:32',NULL,'Pierpaolo','ULCINO','','','','',18,0,NULL,'',NULL,NULL,NULL,0),(11,'2018-09-18 09:27:15','2018-09-18 09:27:15',NULL,'Nicoletta','MARTINO','','','','',18,0,NULL,'',NULL,NULL,NULL,0);
+/*!40000 ALTER TABLE `teachers` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `the_bosses`
+--
+
+DROP TABLE IF EXISTS `the_bosses`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `the_bosses` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `created_at` timestamp NULL DEFAULT NULL,
+  `updated_at` timestamp NULL DEFAULT NULL,
+  `deleted_at` timestamp NULL DEFAULT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `surname` varchar(255) DEFAULT NULL,
+  `username` varchar(255) DEFAULT NULL,
+  `password` varchar(255) DEFAULT NULL,
+  `email` varchar(255) DEFAULT NULL,
+  `telephone_number` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `idx_the_bosses_deleted_at` (`deleted_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `the_bosses`
+--
+
+LOCK TABLES `the_bosses` WRITE;
+/*!40000 ALTER TABLE `the_bosses` DISABLE KEYS */;
+/*!40000 ALTER TABLE `the_bosses` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2018-10-12 11:21:21

+ 77 - 0
cron/sync/test/sync_test.go

@@ -0,0 +1,77 @@
+package sync_test
+
+import (
+	"log"
+	"testing"
+	"time"
+
+	"github.com/remogatto/prettytest"
+	karmen_client "gogs.carducci-dante.gov.it/karmen/client"
+	"gogs.carducci-dante.gov.it/karmen/core/config"
+	"gogs.carducci-dante.gov.it/karmen/core/cron/sync"
+	"gogs.carducci-dante.gov.it/karmen/core/orm"
+	karmen_ldap "gogs.carducci-dante.gov.it/karmen/ldap"
+)
+
+type testSuite struct {
+	prettytest.Suite
+}
+
+var (
+	ldap   *karmen_ldap.Client
+	karmen *karmen_client.Client
+	conf   *config.ConfigT
+)
+
+func TestRunner(t *testing.T) {
+	prettytest.Run(
+		t,
+		new(testSuite),
+	)
+}
+
+func (t *testSuite) BeforeAll() {
+	var err error
+
+	time.Sleep(time.Second * 10) // Wait for the server to go up...
+
+	conf = new(config.ConfigT)
+
+	err = config.ReadFile("config.yaml", conf)
+	if err != nil {
+		panic(err)
+	}
+
+	ldap, err = karmen_ldap.NewClient("localhost:389", conf)
+	if err != nil {
+		panic(err)
+	}
+
+	karmen, err = karmen_client.Dial(conf.Url, conf.Admin.Username, conf.Admin.Password)
+	if err != nil {
+		log.Println(err)
+	}
+
+}
+
+func (t *testSuite) AfterAll() {
+	ldap.Close()
+}
+
+func (t *testSuite) TestSyncUsers() {
+	syncJob := sync.NewSyncJob(conf)
+
+	teachers, err := karmen.GetTeachers()
+	t.Nil(err)
+
+	entries, err := ldap.Users("ou=People,dc=foo,dc=org")
+	t.Nil(err)
+
+	users := make([]orm.User, 0)
+	for _, teacher := range teachers {
+		users = append(users, teacher)
+	}
+
+	err = syncJob.SyncUsers(ldap, karmen, entries, users)
+	t.Nil(err)
+}

+ 0 - 2
orm/administrative.go

@@ -4,12 +4,10 @@ import (
 	"fmt"
 	"net/http"
 
-	"github.com/jinzhu/gorm"
 	"gogs.carducci-dante.gov.it/karmen/core/renderer"
 )
 
 type Administrative struct {
-	gorm.Model
 	Credential
 
 	Office *Office

+ 52 - 7
orm/credential.go

@@ -7,12 +7,15 @@ import (
 	"strings"
 	"time"
 
+	"github.com/jinzhu/gorm"
 	"github.com/sethvargo/go-password/password"
 )
 
 type Checkbox bool
 
 type Credential struct {
+	gorm.Model
+
 	Name          string `csv:"firstname"`
 	Surname       string `csv:"lastname"`
 	Password      string
@@ -27,6 +30,8 @@ type Credential struct {
 
 	Regenerate bool `schema:"Regenerate" sql:"default: false"`
 	Exclude    bool
+
+	LdapDNFmt string
 }
 
 func (c *Credential) sanitize(s string) string {
@@ -39,12 +44,6 @@ func (c *Credential) Username() string {
 	return strings.Join([]string{c.sanitize(c.Name), c.sanitize(c.Surname)}, ".")
 }
 
-func (c *Credential) SaltPassword(password string) string {
-	bs := sha1.Sum([]byte(password + "salt"))
-	str := base64.StdEncoding.EncodeToString(append(bs[:], []byte("salt")...))
-	return fmt.Sprintf("%s", str)
-}
-
 func (c *Credential) GeneratePassword() (string, error) {
 	password, err := password.Generate(8, 2, 0, false, true)
 	if err != nil {
@@ -55,9 +54,55 @@ func (c *Credential) GeneratePassword() (string, error) {
 }
 
 func (c *Credential) CompleteName() string {
-	return fmt.Sprintf("%s, %s", c.Name, c.Surname)
+	return fmt.Sprintf("%s %s", c.Name, c.Surname)
 }
 
 func (c *Credential) MailAddress(domain string) string {
 	return fmt.Sprintf("%s@%s", c.Username(), domain)
 }
+
+func (c *Credential) DN() string {
+	return fmt.Sprintf(c.LdapDNFmt, c.Name, c.Surname)
+}
+
+func (c *Credential) GetName() string {
+	return c.Name
+}
+
+func (c *Credential) GetSurname() string {
+	return c.Surname
+}
+
+func (c *Credential) GetExclude() bool {
+	return c.Exclude
+}
+
+func (c *Credential) GetRegenerate() bool {
+	return c.Regenerate
+}
+
+func (c *Credential) SetRegenerate(value bool) {
+	c.Regenerate = value
+}
+
+func (c *Credential) GetPlainPassword() string {
+	return c.PlainPassword
+}
+
+func (c *Credential) SetPlainPassword(password string) {
+	c.PlainPassword = password
+}
+
+func (c *Credential) GetEmail() string {
+	return c.Email
+}
+
+func (c *Credential) GetAltEmail() string {
+	return c.AltEmail
+}
+
+func SaltPassword(password string) string {
+	bs := sha1.Sum([]byte(password + "salt"))
+	str := base64.StdEncoding.EncodeToString(append(bs[:], []byte("salt")...))
+	return fmt.Sprintf("%s", str)
+}

+ 0 - 2
orm/student.go

@@ -4,12 +4,10 @@ import (
 	"fmt"
 	"net/http"
 
-	"github.com/jinzhu/gorm"
 	"gogs.carducci-dante.gov.it/karmen/core/renderer"
 )
 
 type Student struct {
-	gorm.Model
 	Credential
 
 	Handicap bool `schema:"Handicap" sql:"default: false"`

+ 0 - 2
orm/teacher.go

@@ -6,12 +6,10 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/jinzhu/gorm"
 	"gogs.carducci-dante.gov.it/karmen/core/renderer"
 )
 
 type Teacher struct {
-	gorm.Model
 	Credential
 
 	IsCoordinator bool `gorm:"-"`

+ 22 - 0
orm/user.go

@@ -0,0 +1,22 @@
+package orm
+
+type User interface {
+	GetRegenerate() bool
+	SetRegenerate(value bool)
+
+	GetExclude() bool
+
+	SetPlainPassword(password string)
+	GetPlainPassword() string
+
+	CompleteName() string
+	Username() string
+
+	GetName() string
+	GetSurname() string
+
+	GetEmail() string
+	GetAltEmail() string
+
+	DN() string
+}

+ 5 - 0
templates/teachers_add_update.html.tpl

@@ -76,6 +76,11 @@
       </div>
     </div>
 
+    <div class="form-group">
+      <label class="control-label has-feedback" for="teacher_ldapdnfmt">Stringa DN per LDAP</label>
+      <input type="text" name="LdapDNFmt" class="form-control" id="teacher_ldapdnfmt" placeholder="cn=%s %s,dc=foo,dc=org" {{if .Options.Get "update"}}value="{{.Data.LdapDNFmt}}"{{end}}>
+    </div>
+
     <div class="form-group">
       <button type="submit" class="btn btn-primary">Salva</button>
       {{if .Options.Get "update"}}

+ 3 - 7
vendor/github.com/remogatto/slicediff/slicediff.go

@@ -11,21 +11,17 @@ type Action struct {
 	Type int
 }
 
-type StringSlicer interface {
-	Strings() []string
-}
-
 var ActionString = [3]string{"REMOVE", "ADD", "UPDATE"}
 
-func Diff(dst StringSlicer, src StringSlicer) map[string]*Action {
+func Diff(dst func() []string, src func() []string) map[string]*Action {
 	actions := make(map[string]*Action)
 	m := make(map[string]bool)
 
-	for id, s := range dst.Strings() {
+	for id, s := range dst() {
 		actions[s] = &Action{id, Remove}
 	}
 
-	for id, s := range src.Strings() {
+	for id, s := range src() {
 		if _, ok := m[s]; !ok {
 			m[s] = true
 			_, ok := actions[s]

+ 20 - 0
vendor/gogs.carducci-dante.gov.it/karmen/client/client.go

@@ -454,6 +454,26 @@ func (c *Client) UpdateTeacher(teacher *orm.Teacher) error {
 	return nil
 }
 
+func (c *Client) UpdateUser(user *orm.Credential, model string) error {
+	var response renderer.JsonResponse
+
+	data, err := json.Marshal(user)
+	if err != nil {
+		return err
+	}
+	resp, err := c.SendRequest("POST", fmt.Sprintf("/api/%s/%d/update?format=json", model, user.ID), data)
+	if err != nil {
+		return err
+	}
+	if err := json.Unmarshal(resp, &response); err != nil {
+		return err
+	}
+	if string(response.Error) != "" {
+		return errors.New(string(response.Error))
+	}
+	return nil
+}
+
 func (c *Client) AddTeacher(teacher *orm.Teacher) error {
 	var response renderer.JsonResponse
 

+ 34 - 42
vendor/gogs.carducci-dante.gov.it/karmen/ldap/ldap.go

@@ -42,13 +42,13 @@ func NewClient(host string, config *config.ConfigT) (*Client, error) {
 	return client, nil
 }
 
-func (c *Client) AddTeacher(teacher *orm.Teacher) error {
-	mailDir := fmt.Sprintf("%s/%s/", c.Config.Ldap.MailDirBasePath, teacher.Username())
+func (c *Client) AddUser(user orm.User) error {
+	mailDir := fmt.Sprintf("%s/%s/", c.Config.Ldap.MailDirBasePath, user.Username())
 	uidNumber, err := c.NextMailUIDNumber()
 	if err != nil {
 		return err
 	}
-	addRequest := ldap.NewAddRequest(c.TeacherDN(teacher))
+	addRequest := ldap.NewAddRequest(user.DN())
 	addRequest.Attribute("objectClass", []string{
 		"inetOrgPerson",
 		"posixAccount",
@@ -56,21 +56,21 @@ func (c *Client) AddTeacher(teacher *orm.Teacher) error {
 		"organizationalPerson",
 		"extensibleObject",
 	})
-	addRequest.Attribute("sn", []string{teacher.Surname})
-	addRequest.Attribute("uid", []string{teacher.Username()})
-	addRequest.Attribute("homeDirectory", []string{fmt.Sprintf("/home/users/%s", teacher.Username())})
-	addRequest.Attribute("givenName", []string{teacher.Surname})
-	addRequest.Attribute("mail", []string{fmt.Sprintf("%s@%s", teacher.Username(), c.Config.Domain)})
+	addRequest.Attribute("sn", []string{user.GetSurname()})
+	addRequest.Attribute("uid", []string{user.Username()})
+	addRequest.Attribute("homeDirectory", []string{fmt.Sprintf("/home/users/%s", user.Username())})
+	addRequest.Attribute("givenName", []string{user.GetSurname()})
+	addRequest.Attribute("mail", []string{fmt.Sprintf("%s@%s", user.Username(), c.Config.Domain)})
 	addRequest.Attribute("mailEnabled", []string{"TRUE"})
 	addRequest.Attribute("mailGidNumber", []string{c.Config.Ldap.MailGIDNumber})
 	addRequest.Attribute("mailUidNumber", []string{uidNumber})
 	addRequest.Attribute("uidNumber", []string{uidNumber})
 	addRequest.Attribute("gidNumber", []string{uidNumber})
-	addRequest.Attribute("uniqueIdentifier", []string{teacher.Username()})
+	addRequest.Attribute("uniqueIdentifier", []string{user.Username()})
 	addRequest.Attribute("mailHomeDirectory", []string{mailDir})
 	addRequest.Attribute("mailStorageDirectory", []string{"maildir:" + mailDir})
 	addRequest.Attribute("mailQuota", []string{c.Config.Ldap.MailQuota})
-	addRequest.Attribute("userPassword", []string{fmt.Sprintf("{SSHA}%s", teacher.SaltPassword(teacher.PlainPassword))})
+	addRequest.Attribute("userPassword", []string{fmt.Sprintf("{SSHA}%s", orm.SaltPassword(user.GetPlainPassword()))})
 
 	err = c.Conn.Add(addRequest)
 	if err != nil {
@@ -80,10 +80,10 @@ func (c *Client) AddTeacher(teacher *orm.Teacher) error {
 	return nil
 }
 
-func (c *Client) UpdateTeacher(teacher *orm.Teacher) error {
-	mailDir := fmt.Sprintf("%s/%s/", c.Config.Ldap.MailDirBasePath, teacher.Username())
-	modRequest := ldap.NewModifyRequest(c.TeacherDN(teacher))
-	modRequest.Replace("mail", []string{fmt.Sprintf("%s@%s", teacher.Username(), c.Config.Domain)})
+func (c *Client) UpdateUser(user *orm.Credential) error {
+	mailDir := fmt.Sprintf("%s/%s/", c.Config.Ldap.MailDirBasePath, user.Username())
+	modRequest := ldap.NewModifyRequest(user.DN())
+	modRequest.Replace("mail", []string{fmt.Sprintf("%s@%s", user.Username(), c.Config.Domain)})
 	modRequest.Replace("mailHomeDirectory", []string{mailDir})
 	modRequest.Replace("mailStorageDirectory", []string{"maildir:" + mailDir})
 	modRequest.Replace("mailEnabled", []string{"TRUE"})
@@ -98,9 +98,9 @@ func (c *Client) UpdateTeacher(teacher *orm.Teacher) error {
 	return nil
 }
 
-func (c *Client) UpdateTeacherPassword(teacher *orm.Teacher, password string) error {
-	modRequest := ldap.NewModifyRequest(c.TeacherDN(teacher))
-	modRequest.Replace("userPassword", []string{fmt.Sprintf("{SSHA}%s", teacher.SaltPassword(password))})
+func (c *Client) UpdateUserPassword(user *orm.Credential, password string) error {
+	modRequest := ldap.NewModifyRequest(user.DN())
+	modRequest.Replace("userPassword", []string{fmt.Sprintf("{SSHA}%s", orm.SaltPassword(password))})
 	err := c.Conn.Modify(modRequest)
 	if err != nil {
 		return err
@@ -108,8 +108,8 @@ func (c *Client) UpdateTeacherPassword(teacher *orm.Teacher, password string) er
 	return nil
 }
 
-func (c *Client) DeleteTeacher(teacher *orm.Teacher) error {
-	delRequest := ldap.NewDelRequest(c.TeacherDN(teacher), nil)
+func (c *Client) DeleteUser(user *orm.Credential) error {
+	delRequest := ldap.NewDelRequest(user.DN(), nil)
 	err := c.Conn.Del(delRequest)
 	if err != nil {
 		return err
@@ -128,7 +128,7 @@ func (c *Client) DeleteByDN(dn string) error {
 	return nil
 }
 
-func (c *Client) AddTeacherToGroup(teacher *orm.Teacher, groupDN string) error {
+func (c *Client) AddUserToGroup(user *orm.Credential, groupDN string) error {
 	memberAttr, err := c.memberAttribute(groupDN)
 	if err != nil {
 		return err
@@ -136,9 +136,9 @@ func (c *Client) AddTeacherToGroup(teacher *orm.Teacher, groupDN string) error {
 	modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
 	switch memberAttr {
 	case "member":
-		modRequest.Add(memberAttr, []string{c.personDN(teacher, c.TeachersDN())})
+		modRequest.Add(memberAttr, []string{user.DN()})
 	case "memberuid":
-		modRequest.Add(memberAttr, []string{teacher.Username()})
+		modRequest.Add(memberAttr, []string{user.Username()})
 	default:
 		return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
 	}
@@ -150,7 +150,7 @@ func (c *Client) AddTeacherToGroup(teacher *orm.Teacher, groupDN string) error {
 	return nil
 }
 
-func (c *Client) RemoveTeacherFromGroup(teacher *orm.Teacher, groupDN string) error {
+func (c *Client) RemoveUserFromGroup(user *orm.Credential, groupDN string) error {
 	memberAttr, err := c.memberAttribute(groupDN)
 	if err != nil {
 		return err
@@ -158,9 +158,9 @@ func (c *Client) RemoveTeacherFromGroup(teacher *orm.Teacher, groupDN string) er
 	modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
 	switch memberAttr {
 	case "member":
-		modRequest.Delete(memberAttr, []string{c.personDN(teacher, c.TeachersDN())})
+		modRequest.Delete(memberAttr, []string{user.DN()})
 	case "memberuid":
-		modRequest.Delete(memberAttr, []string{teacher.Username()})
+		modRequest.Delete(memberAttr, []string{user.Username()})
 	default:
 		return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
 	}
@@ -172,7 +172,7 @@ func (c *Client) RemoveTeacherFromGroup(teacher *orm.Teacher, groupDN string) er
 	return nil
 }
 
-func (c *Client) RemoveTeacherFromGroupByMemberValue(memberValue string, groupDN string) error {
+func (c *Client) RemoveUserFromGroupByMemberValue(memberValue string, groupDN string) error {
 	memberAttr, err := c.memberAttribute(groupDN)
 	if err != nil {
 		return err
@@ -194,7 +194,7 @@ func (c *Client) RemoveTeacherFromGroupByMemberValue(memberValue string, groupDN
 	return nil
 }
 
-func (c *Client) IsTeacherInGroup(teacher *orm.Teacher, groupDN string) (bool, error) {
+func (c *Client) IsUserInGroup(user *orm.Credential, groupDN string) (bool, error) {
 	memberAttr, err := c.memberAttribute(groupDN)
 	if err != nil {
 		return false, err
@@ -202,9 +202,9 @@ func (c *Client) IsTeacherInGroup(teacher *orm.Teacher, groupDN string) (bool, e
 	var memberValue string
 	switch memberAttr {
 	case "member":
-		memberValue = c.personDN(teacher, c.TeachersDN())
+		memberValue = user.DN()
 	case "memberuid":
-		memberValue = teacher.Username()
+		memberValue = user.Username()
 	default:
 		return false, errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
 	}
@@ -241,9 +241,9 @@ func (c *Client) GroupMembers(groupDN string) ([]*ldap.Entry, error) {
 	return sr.Entries, nil
 }
 
-func (c *Client) Teachers() ([]*ldap.Entry, error) {
+func (c *Client) Users(searchDN string) ([]*ldap.Entry, error) {
 	result, err := c.Search(
-		c.TeachersDN(),
+		searchDN,
 		"(&(objectClass=organizationalPerson))",
 		[]string{"dn", "cn"},
 	)
@@ -271,11 +271,11 @@ func (c *Client) Search(base string, filter string, attributes []string) (*ldap.
 	return sr, nil
 }
 
-func (c *Client) TeacherExists(teacher *orm.Teacher) (bool, error) {
+func (c *Client) UserExists(user *orm.Credential, searchDN string) (bool, error) {
 	searchRequest := ldap.NewSearchRequest(
-		c.TeachersDN(),
+		searchDN,
 		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
-		fmt.Sprintf("(&(objectClass=organizationalPerson)(cn=%s))", teacher.CompleteName()),
+		fmt.Sprintf("(&(objectClass=organizationalPerson)(cn=%s))", user.CompleteName()),
 
 		[]string{"dn", "cn"},
 		nil,
@@ -289,18 +289,10 @@ func (c *Client) TeacherExists(teacher *orm.Teacher) (bool, error) {
 	return len(sr.Entries) > 0, nil
 }
 
-func (c *Client) TeachersDN() string {
-	return fmt.Sprintf("%s,%s", c.Config.Ldap.TeachersDN, c.DomainDN())
-}
-
 func (c *Client) DomainDN() string {
 	return c.Config.DomainDN()
 }
 
-func (c *Client) TeacherDN(teacher *orm.Teacher) string {
-	return fmt.Sprintf("cn=%s %s,%s", teacher.Name, teacher.Surname, c.TeachersDN())
-}
-
 func (c *Client) GroupsDN() string {
 	return fmt.Sprintf("%s,%s", c.Config.Ldap.GroupsDN, c.DomainDN())
 }