main.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/tls"
  6. "fmt"
  7. "log"
  8. "os"
  9. "github.com/sethvargo/go-password/password"
  10. gomail "gopkg.in/gomail.v2"
  11. ldap "gopkg.in/ldap.v2"
  12. "text/template"
  13. tpl_util "gogs.carducci-dante.gov.it/andrea.fazzi/karmen/util/template"
  14. karmen_client "gogs.carducci-dante.gov.it/karmen/client"
  15. "gogs.carducci-dante.gov.it/karmen/core/orm"
  16. karmen_ldap "gogs.carducci-dante.gov.it/karmen/ldap"
  17. )
  18. const (
  19. Remove = iota + 1
  20. Add
  21. Update
  22. )
  23. type Action struct {
  24. Id int
  25. Type int
  26. }
  27. var actDict = map[int]string{
  28. 1: "REMOVE",
  29. 2: "ADD",
  30. 3: "UPDATE",
  31. }
  32. func diff(dst []string, src []string, excludes []string) map[string]*Action {
  33. actions := make(map[string]*Action)
  34. for id, el := range dst {
  35. if !excluded(excludes, el) {
  36. actions[el] = &Action{id, Remove}
  37. }
  38. }
  39. for id, el := range src {
  40. _, ok := actions[el]
  41. if !ok {
  42. actions[el] = &Action{id, Add}
  43. continue
  44. }
  45. actions[el] = &Action{id, Update}
  46. }
  47. return actions
  48. }
  49. func entriesToSliceFunc(elements []*ldap.Entry, fn func(*ldap.Entry) string) (result []string) {
  50. for _, el := range elements {
  51. result = append(result, fn(el))
  52. }
  53. return result
  54. }
  55. func teachersToSliceFunc(elements []*orm.Teacher, fn func(*orm.Teacher) string) (result []string) {
  56. for _, el := range elements {
  57. result = append(result, fn(el))
  58. }
  59. return result
  60. }
  61. func excluded(excludes []string, name string) bool {
  62. for _, excluded := range excludes {
  63. if name == excluded {
  64. return true
  65. }
  66. }
  67. return false
  68. }
  69. func filterTeachers(teachers []*orm.Teacher, excludes []string) []*orm.Teacher {
  70. fTeachers := teachers[:0]
  71. for _, t := range teachers {
  72. if t.Name != "Senza nome" && !excluded(excludes, t.CompleteName()) {
  73. fTeachers = append(fTeachers, t)
  74. }
  75. }
  76. return fTeachers
  77. }
  78. func sendMail(teacher *orm.Teacher, tpl *template.Template, dryRun bool) error {
  79. var buf bytes.Buffer
  80. err := tpl.Execute(&buf, teacher)
  81. if err != nil {
  82. return err
  83. }
  84. log.Printf("Send mail to %s", teacher.AltEmail)
  85. if !dryRun {
  86. m := gomail.NewMessage()
  87. m.SetHeader("From", "karmen@carducci-dante.gov.it")
  88. m.SetHeader("To", teacher.AltEmail)
  89. //m.SetHeader("To", "segreteria.automatica@carducci-dante.gov.it")
  90. m.SetHeader("Subject", "Credenziali servizi informatici del Liceo \"Carducci-Dante\" di Trieste")
  91. m.SetBody("text/plain", buf.String())
  92. d := gomail.NewDialer("mail.carducci-dante.gov.it", 25, "karmen", `Kq&&mZ3s5Bdg>"*V~"X+`)
  93. d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
  94. if err := d.DialAndSend(m); err != nil {
  95. return err
  96. }
  97. }
  98. return nil
  99. }
  100. func syncTeachers(ldapClient *karmen_ldap.Client, karmenClient *karmen_client.Client, ldapTeachers []*ldap.Entry, teachers []*orm.Teacher, excludes []string, mailTpl *template.Template) error {
  101. actions := diff(
  102. entriesToSliceFunc(ldapTeachers, func(entry *ldap.Entry) string { return entry.DN }),
  103. teachersToSliceFunc(teachers, func(t *orm.Teacher) string { return ldapClient.TeacherDN(t) }),
  104. excludes,
  105. )
  106. for el, act := range actions {
  107. switch act.Type {
  108. case Remove:
  109. err := ldapClient.DeleteByDN(el)
  110. if err != nil {
  111. return err
  112. }
  113. case Add:
  114. teacher := teachers[act.Id]
  115. log.Printf("ADD teacher %s", teacher.CompleteName())
  116. password, err := password.Generate(8, 2, 0, false, true)
  117. if err != nil {
  118. return err
  119. }
  120. teacher.PlainPassword = password
  121. err = ldapClient.AddTeacher(teacher)
  122. if err != nil {
  123. panic(err)
  124. }
  125. if !teacher.MailSent {
  126. log.Printf("Sending credentials to %s, pw is %s", teacher.AltEmail, teacher.PlainPassword)
  127. err = sendMail(teacher, mailTpl, false)
  128. if err != nil {
  129. return err
  130. }
  131. teacher.MailSent = true
  132. err = karmenClient.UpdateTeacher(teacher)
  133. if err != nil {
  134. return err
  135. }
  136. }
  137. // case Update:
  138. // // teacher := teachers[act.Id]
  139. // // log.Printf("UPDATE teacher %s", teacher.CompleteName())
  140. // // err = ldapClient.UpdateTeacher(teacher)
  141. // // if err != nil {
  142. // // panic(err)
  143. // // }
  144. }
  145. }
  146. return nil
  147. }
  148. func syncMailingList(ldapClient *karmen_ldap.Client, teachers []*orm.Teacher, excludes []string, groupDN string) error {
  149. entries, err := ldapClient.GroupMembers(groupDN)
  150. if err != nil {
  151. return err
  152. }
  153. actions := diff(
  154. entries[0].Attributes[0].Values,
  155. teachersToSliceFunc(teachers, func(t *orm.Teacher) string { return ldapClient.TeacherDN(t) }),
  156. excludes,
  157. )
  158. for el, act := range actions {
  159. switch act.Type {
  160. case Remove:
  161. log.Printf("REMOVE teacher %s from %s", el, groupDN)
  162. ldapClient.RemoveTeacherFromGroupByMemberValue(el, groupDN)
  163. case Add:
  164. log.Printf("ADD teacher %s to %s", el, groupDN)
  165. teacher := teachers[act.Id]
  166. err = ldapClient.AddTeacherToGroup(teacher, groupDN)
  167. if err != nil {
  168. panic(err)
  169. }
  170. }
  171. }
  172. return nil
  173. }
  174. func syncTeachersGroup(ldapClient *karmen_ldap.Client, teachers []*orm.Teacher, excludes []string, groupDN string) error {
  175. entries, err := ldapClient.GroupMembers(groupDN)
  176. if err != nil {
  177. return err
  178. }
  179. actions := diff(
  180. entries[0].Attributes[0].Values,
  181. teachersToSliceFunc(teachers, func(t *orm.Teacher) string { return t.GenerateUsername() }),
  182. excludes,
  183. )
  184. for el, act := range actions {
  185. switch act.Type {
  186. case Remove:
  187. log.Printf("REMOVE teacher %s from %s", el, groupDN)
  188. ldapClient.RemoveTeacherFromGroupByMemberValue(el, groupDN)
  189. case Add:
  190. log.Printf("ADD teacher %s to %s", el, groupDN)
  191. teacher := teachers[act.Id]
  192. err = ldapClient.AddTeacherToGroup(teacher, groupDN)
  193. if err != nil {
  194. panic(err)
  195. }
  196. }
  197. }
  198. return nil
  199. }
  200. func main() {
  201. log.Printf("Connecting to karmen at %s...", "https://karmen.andreafazzi.eu")
  202. karmenClient, err := karmen_client.Dial("https://karmen.andreafazzi.eu", "admin", "aolieVooju")
  203. if err != nil {
  204. panic(err)
  205. }
  206. teachers, err := karmenClient.GetTeachers()
  207. if err != nil {
  208. panic(err)
  209. }
  210. departments, err := karmenClient.GetDepartments()
  211. if err != nil {
  212. panic(err)
  213. }
  214. conf := &karmen_ldap.Config{
  215. Domain: "carducci-dante.gov.it",
  216. AdminCN: "admin",
  217. AdminPassword: "CDlinux2016",
  218. TeachersDN: "ou=Docenti,ou=Persone",
  219. GroupsDN: "ou=Gruppi",
  220. PeopleDN: "ou=Persone",
  221. MailDirPath: "/var/mail/carducci-dante.gov.it",
  222. MailQuota: "10240",
  223. FirstUIDNumber: "5000",
  224. MailGIDNumber: "5000",
  225. }
  226. log.Printf("Connecting to LDAP at %s...", "ldap.carducci-dante.gov.it:389")
  227. ldapClient, err := karmen_ldap.NewClient("ldap.carducci-dante.gov.it:389", conf)
  228. if err != nil {
  229. panic(err)
  230. }
  231. log.Println("Getting LDAP registered teachers...")
  232. ldapTeachers, err := ldapClient.Teachers()
  233. if err != nil {
  234. panic(err)
  235. }
  236. excludes := make([]string, 0)
  237. log.Print("Read excludes...")
  238. f, err := os.Open("./excludes.txt")
  239. defer f.Close()
  240. if err != nil {
  241. log.Println(err)
  242. } else {
  243. scanner := bufio.NewScanner(f)
  244. for scanner.Scan() {
  245. excludes = append(excludes, scanner.Text())
  246. }
  247. if err := scanner.Err(); err != nil {
  248. panic(err)
  249. }
  250. }
  251. tpl, err := tpl_util.LoadTextTemplate("mail.tpl")
  252. if err != nil {
  253. panic(err)
  254. }
  255. fTeachers := filterTeachers(teachers, excludes)
  256. err = syncTeachers(ldapClient, karmenClient, ldapTeachers, fTeachers, excludes, tpl)
  257. if err != nil {
  258. panic(err)
  259. }
  260. for _, department := range departments {
  261. group := fmt.Sprintf("cn=%s,ou=Mailing Lists", department.Name)
  262. err = syncMailingList(ldapClient, filterTeachers(department.Teachers, excludes), excludes, group)
  263. if err != nil {
  264. panic(err)
  265. }
  266. group = fmt.Sprintf("cn=%s,ou=Dipartimenti", department.Name)
  267. err = syncTeachersGroup(ldapClient, filterTeachers(department.Teachers, excludes), excludes, group)
  268. if err != nil {
  269. panic(err)
  270. }
  271. }
  272. err = syncTeachersGroup(ldapClient, fTeachers, excludes, "cn=Docenti")
  273. if err != nil {
  274. panic(err)
  275. }
  276. }