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) } }