|
@@ -0,0 +1,325 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "bufio"
|
|
|
+ "bytes"
|
|
|
+ "crypto/tls"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+
|
|
|
+ "github.com/sethvargo/go-password/password"
|
|
|
+
|
|
|
+ gomail "gopkg.in/gomail.v2"
|
|
|
+ ldap "gopkg.in/ldap.v2"
|
|
|
+
|
|
|
+ "text/template"
|
|
|
+
|
|
|
+ tpl_util "gogs.carducci-dante.gov.it/andrea.fazzi/karmen/util/template"
|
|
|
+ karmen_client "gogs.carducci-dante.gov.it/karmen/client"
|
|
|
+ "gogs.carducci-dante.gov.it/karmen/core/orm"
|
|
|
+ karmen_ldap "gogs.carducci-dante.gov.it/karmen/ldap"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ Remove = iota + 1
|
|
|
+ Add
|
|
|
+ Update
|
|
|
+)
|
|
|
+
|
|
|
+type Action struct {
|
|
|
+ Id int
|
|
|
+ Type int
|
|
|
+}
|
|
|
+
|
|
|
+var actDict = map[int]string{
|
|
|
+ 1: "REMOVE",
|
|
|
+ 2: "ADD",
|
|
|
+ 3: "UPDATE",
|
|
|
+}
|
|
|
+
|
|
|
+func diff(dst []string, src []string, excludes []string) map[string]*Action {
|
|
|
+ actions := make(map[string]*Action)
|
|
|
+
|
|
|
+ for id, el := range dst {
|
|
|
+ if !excluded(excludes, el) {
|
|
|
+ actions[el] = &Action{id, Remove}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for id, el := range src {
|
|
|
+ _, ok := actions[el]
|
|
|
+ if !ok {
|
|
|
+ actions[el] = &Action{id, Add}
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ actions[el] = &Action{id, Update}
|
|
|
+ }
|
|
|
+
|
|
|
+ return actions
|
|
|
+}
|
|
|
+
|
|
|
+func entriesToSliceFunc(elements []*ldap.Entry, fn func(*ldap.Entry) string) (result []string) {
|
|
|
+ for _, el := range elements {
|
|
|
+ result = append(result, fn(el))
|
|
|
+ }
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+func teachersToSliceFunc(elements []*orm.Teacher, fn func(*orm.Teacher) string) (result []string) {
|
|
|
+ for _, el := range elements {
|
|
|
+ result = append(result, fn(el))
|
|
|
+ }
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+func excluded(excludes []string, name string) bool {
|
|
|
+ for _, excluded := range excludes {
|
|
|
+ if name == excluded {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func filterTeachers(teachers []*orm.Teacher, excludes []string) []*orm.Teacher {
|
|
|
+ fTeachers := teachers[:0]
|
|
|
+ for _, t := range teachers {
|
|
|
+ if t.Name != "Senza nome" && !excluded(excludes, t.CompleteName()) {
|
|
|
+ fTeachers = append(fTeachers, t)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return fTeachers
|
|
|
+}
|
|
|
+
|
|
|
+func sendMail(teacher *orm.Teacher, tpl *template.Template, dryRun bool) error {
|
|
|
+ var buf bytes.Buffer
|
|
|
+
|
|
|
+ err := tpl.Execute(&buf, teacher)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ log.Printf("Send mail to %s", teacher.AltEmail)
|
|
|
+
|
|
|
+ if !dryRun {
|
|
|
+ m := gomail.NewMessage()
|
|
|
+ m.SetHeader("From", "karmen@carducci-dante.gov.it")
|
|
|
+ m.SetHeader("To", teacher.AltEmail)
|
|
|
+ //m.SetHeader("To", "segreteria.automatica@carducci-dante.gov.it")
|
|
|
+ m.SetHeader("Subject", "Credenziali servizi informatici del Liceo \"Carducci-Dante\" di Trieste")
|
|
|
+ m.SetBody("text/plain", buf.String())
|
|
|
+
|
|
|
+ d := gomail.NewDialer("mail.carducci-dante.gov.it", 25, "karmen", `Kq&&mZ3s5Bdg>"*V~"X+`)
|
|
|
+ d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
|
|
+
|
|
|
+ if err := d.DialAndSend(m); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func syncTeachers(ldapClient *karmen_ldap.Client, karmenClient *karmen_client.Client, ldapTeachers []*ldap.Entry, teachers []*orm.Teacher, excludes []string, mailTpl *template.Template) error {
|
|
|
+ actions := diff(
|
|
|
+ entriesToSliceFunc(ldapTeachers, func(entry *ldap.Entry) string { return entry.DN }),
|
|
|
+ teachersToSliceFunc(teachers, func(t *orm.Teacher) string { return ldapClient.TeacherDN(t) }),
|
|
|
+ excludes,
|
|
|
+ )
|
|
|
+
|
|
|
+ for el, act := range actions {
|
|
|
+ switch act.Type {
|
|
|
+ case Remove:
|
|
|
+ err := ldapClient.DeleteByDN(el)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ case Add:
|
|
|
+ teacher := teachers[act.Id]
|
|
|
+ log.Printf("ADD teacher %s", teacher.CompleteName())
|
|
|
+ password, err := password.Generate(8, 2, 0, false, true)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ teacher.PlainPassword = password
|
|
|
+ err = ldapClient.AddTeacher(teacher)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if !teacher.MailSent {
|
|
|
+ log.Printf("Sending credentials to %s, pw is %s", teacher.AltEmail, teacher.PlainPassword)
|
|
|
+ err = sendMail(teacher, mailTpl, false)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ teacher.MailSent = true
|
|
|
+ err = karmenClient.UpdateTeacher(teacher)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // case Update:
|
|
|
+ // // teacher := teachers[act.Id]
|
|
|
+ // // log.Printf("UPDATE teacher %s", teacher.CompleteName())
|
|
|
+ // // err = ldapClient.UpdateTeacher(teacher)
|
|
|
+ // // if err != nil {
|
|
|
+ // // panic(err)
|
|
|
+ // // }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func syncMailingList(ldapClient *karmen_ldap.Client, teachers []*orm.Teacher, excludes []string, groupDN string) error {
|
|
|
+ entries, err := ldapClient.GroupMembers(groupDN)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ actions := diff(
|
|
|
+ entries[0].Attributes[0].Values,
|
|
|
+ teachersToSliceFunc(teachers, func(t *orm.Teacher) string { return ldapClient.TeacherDN(t) }),
|
|
|
+ excludes,
|
|
|
+ )
|
|
|
+
|
|
|
+ for el, act := range actions {
|
|
|
+ switch act.Type {
|
|
|
+ case Remove:
|
|
|
+ log.Printf("REMOVE teacher %s from %s", el, groupDN)
|
|
|
+ ldapClient.RemoveTeacherFromGroupByMemberValue(el, groupDN)
|
|
|
+ case Add:
|
|
|
+ log.Printf("ADD teacher %s to %s", el, groupDN)
|
|
|
+ teacher := teachers[act.Id]
|
|
|
+ err = ldapClient.AddTeacherToGroup(teacher, groupDN)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func syncTeachersGroup(ldapClient *karmen_ldap.Client, teachers []*orm.Teacher, excludes []string, groupDN string) error {
|
|
|
+ entries, err := ldapClient.GroupMembers(groupDN)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ actions := diff(
|
|
|
+ entries[0].Attributes[0].Values,
|
|
|
+ teachersToSliceFunc(teachers, func(t *orm.Teacher) string { return t.GenerateUsername() }),
|
|
|
+ excludes,
|
|
|
+ )
|
|
|
+
|
|
|
+ for el, act := range actions {
|
|
|
+ switch act.Type {
|
|
|
+ case Remove:
|
|
|
+ log.Printf("REMOVE teacher %s from %s", el, groupDN)
|
|
|
+ ldapClient.RemoveTeacherFromGroupByMemberValue(el, groupDN)
|
|
|
+ case Add:
|
|
|
+ log.Printf("ADD teacher %s to %s", el, groupDN)
|
|
|
+ teacher := teachers[act.Id]
|
|
|
+ err = ldapClient.AddTeacherToGroup(teacher, groupDN)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ log.Printf("Connecting to karmen at %s...", "https://karmen.andreafazzi.eu")
|
|
|
+ karmenClient, err := karmen_client.Dial("https://karmen.andreafazzi.eu", "admin", "aolieVooju")
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ teachers, err := karmenClient.GetTeachers()
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ departments, err := karmenClient.GetDepartments()
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ conf := &karmen_ldap.Config{
|
|
|
+ Domain: "carducci-dante.gov.it",
|
|
|
+ AdminCN: "admin",
|
|
|
+ AdminPassword: "CDlinux2016",
|
|
|
+ TeachersDN: "ou=Docenti,ou=Persone",
|
|
|
+ GroupsDN: "ou=Gruppi",
|
|
|
+ PeopleDN: "ou=Persone",
|
|
|
+ MailDirPath: "/var/mail/carducci-dante.gov.it",
|
|
|
+ MailQuota: "10240",
|
|
|
+ FirstUIDNumber: "5000",
|
|
|
+ MailGIDNumber: "5000",
|
|
|
+ }
|
|
|
+
|
|
|
+ log.Printf("Connecting to LDAP at %s...", "ldap.carducci-dante.gov.it:389")
|
|
|
+ ldapClient, err := karmen_ldap.NewClient("ldap.carducci-dante.gov.it:389", conf)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ log.Println("Getting LDAP registered teachers...")
|
|
|
+ ldapTeachers, err := ldapClient.Teachers()
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ excludes := make([]string, 0)
|
|
|
+ log.Print("Read excludes...")
|
|
|
+ f, err := os.Open("./excludes.txt")
|
|
|
+ defer f.Close()
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ } else {
|
|
|
+ scanner := bufio.NewScanner(f)
|
|
|
+ for scanner.Scan() {
|
|
|
+ excludes = append(excludes, scanner.Text())
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := scanner.Err(); err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ tpl, err := tpl_util.LoadTextTemplate("mail.tpl")
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ fTeachers := filterTeachers(teachers, excludes)
|
|
|
+
|
|
|
+ err = syncTeachers(ldapClient, karmenClient, ldapTeachers, fTeachers, excludes, tpl)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, department := range departments {
|
|
|
+ group := fmt.Sprintf("cn=%s,ou=Mailing Lists", department.Name)
|
|
|
+ err = syncMailingList(ldapClient, filterTeachers(department.Teachers, excludes), excludes, group)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ group = fmt.Sprintf("cn=%s,ou=Dipartimenti", department.Name)
|
|
|
+ err = syncTeachersGroup(ldapClient, filterTeachers(department.Teachers, excludes), excludes, group)
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ err = syncTeachersGroup(ldapClient, fTeachers, excludes, "cn=Docenti")
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+
|
|
|
+}
|