ldap.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. package ldap
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. "strconv"
  7. "gogs.carduccidante.edu.it/karmen/core/config"
  8. "gogs.carduccidante.edu.it/karmen/core/orm"
  9. ldap "gopkg.in/ldap.v2"
  10. )
  11. type DNer interface {
  12. DN() string
  13. }
  14. type CompleteNameI interface {
  15. CompleteName() string
  16. }
  17. type Client struct {
  18. Conn *ldap.Conn
  19. Config *config.ConfigT
  20. }
  21. func (c *Client) Close() {
  22. c.Conn.Close()
  23. }
  24. func NewClient(host string, config *config.ConfigT) (*Client, error) {
  25. var err error
  26. client := new(Client)
  27. client.Config = config
  28. client.Conn, err = ldap.Dial("tcp", host)
  29. if err != nil {
  30. return nil, err
  31. }
  32. err = client.Conn.Bind(config.AdminCN(), config.Ldap.AdminPassword)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return client, nil
  37. }
  38. func (c *Client) AddUser(user orm.User) error {
  39. mailDir := fmt.Sprintf("%s/%s/", c.Config.Ldap.MailDirBasePath, user.Username())
  40. uidNumber, err := c.NextMailUIDNumber()
  41. if err != nil {
  42. return err
  43. }
  44. addRequest := ldap.NewAddRequest(user.DN())
  45. addRequest.Attribute("objectClass", []string{
  46. "inetOrgPerson",
  47. "posixAccount",
  48. "PostfixBookMailAccount",
  49. "organizationalPerson",
  50. "extensibleObject",
  51. })
  52. addRequest.Attribute("sn", []string{user.GetSurname()})
  53. addRequest.Attribute("uid", []string{user.Username()})
  54. addRequest.Attribute("homeDirectory", []string{fmt.Sprintf("/home/users/%s", user.Username())})
  55. addRequest.Attribute("givenName", []string{user.GetSurname()})
  56. addRequest.Attribute("mail", []string{fmt.Sprintf("%s@%s", user.Username(), c.Config.Domain)})
  57. addRequest.Attribute("mailEnabled", []string{"TRUE"})
  58. addRequest.Attribute("mailGidNumber", []string{c.Config.Ldap.MailGIDNumber})
  59. addRequest.Attribute("mailUidNumber", []string{uidNumber})
  60. addRequest.Attribute("uidNumber", []string{uidNumber})
  61. addRequest.Attribute("gidNumber", []string{uidNumber})
  62. addRequest.Attribute("uniqueIdentifier", []string{user.Username()})
  63. addRequest.Attribute("mailHomeDirectory", []string{mailDir})
  64. addRequest.Attribute("mailStorageDirectory", []string{"maildir:" + mailDir})
  65. addRequest.Attribute("mailQuota", []string{c.Config.Ldap.MailQuota})
  66. addRequest.Attribute("userPassword", []string{fmt.Sprintf("{SSHA}%s", orm.SaltPassword(user.GetPlainPassword()))})
  67. err = c.Conn.Add(addRequest)
  68. if err != nil {
  69. return err
  70. }
  71. return nil
  72. }
  73. func (c *Client) UpdateUser(user orm.User) error {
  74. mailDir := fmt.Sprintf("%s/%s/", c.Config.Ldap.MailDirBasePath, user.Username())
  75. modRequest := ldap.NewModifyRequest(user.DN())
  76. modRequest.Replace("mail", []string{fmt.Sprintf("%s@%s", user.Username(), c.Config.Domain)})
  77. modRequest.Replace("mailHomeDirectory", []string{mailDir})
  78. modRequest.Replace("mailStorageDirectory", []string{"maildir:" + mailDir})
  79. modRequest.Replace("mailEnabled", []string{"TRUE"})
  80. modRequest.Replace("mailGidNumber", []string{c.Config.Ldap.MailGIDNumber})
  81. modRequest.Replace("mailQuota", []string{c.Config.Ldap.MailQuota})
  82. err := c.Conn.Modify(modRequest)
  83. if err != nil {
  84. return err
  85. }
  86. return nil
  87. }
  88. func (c *Client) UpdateUserPassword(user orm.User) error {
  89. modRequest := ldap.NewModifyRequest(user.DN())
  90. modRequest.Replace("userPassword", []string{fmt.Sprintf("{SSHA}%s", orm.SaltPassword(user.GetPlainPassword()))})
  91. err := c.Conn.Modify(modRequest)
  92. if err != nil {
  93. return err
  94. }
  95. return nil
  96. }
  97. func (c *Client) VerifyUserLogin(username, password string) error {
  98. // Search for the given username
  99. searchRequest := ldap.NewSearchRequest(
  100. c.DomainDN(),
  101. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  102. fmt.Sprintf("(&(objectClass=organizationalPerson)(uid=%s))", username),
  103. []string{"dn"},
  104. nil,
  105. )
  106. sr, err := c.Conn.Search(searchRequest)
  107. if err != nil {
  108. return err
  109. }
  110. if len(sr.Entries) != 1 {
  111. return errors.New("User does not exist or too many entries returned")
  112. }
  113. userdn := sr.Entries[0].DN
  114. // Bind as the user to verify their password
  115. err = c.Conn.Bind(userdn, password)
  116. if err != nil {
  117. return err
  118. }
  119. return nil
  120. }
  121. func (c *Client) DeleteUser(user orm.User) error {
  122. delRequest := ldap.NewDelRequest(user.DN(), nil)
  123. err := c.Conn.Del(delRequest)
  124. if err != nil {
  125. return err
  126. }
  127. return nil
  128. }
  129. func (c *Client) DeleteByDN(dn string) error {
  130. delRequest := ldap.NewDelRequest(dn, nil)
  131. err := c.Conn.Del(delRequest)
  132. if err != nil {
  133. return err
  134. }
  135. return nil
  136. }
  137. func (c *Client) AddUserToGroup(user orm.User, groupDN string) error {
  138. memberAttr, err := c.memberAttribute(groupDN)
  139. if err != nil {
  140. return err
  141. }
  142. modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
  143. switch memberAttr {
  144. case "member":
  145. modRequest.Add(memberAttr, []string{user.DN()})
  146. case "memberuid":
  147. modRequest.Add(memberAttr, []string{user.Username()})
  148. default:
  149. return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
  150. }
  151. err = c.Conn.Modify(modRequest)
  152. if err != nil {
  153. return err
  154. }
  155. return nil
  156. }
  157. func (c *Client) RemoveUserFromGroup(user *orm.Credential, groupDN string) error {
  158. memberAttr, err := c.memberAttribute(groupDN)
  159. if err != nil {
  160. return err
  161. }
  162. modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
  163. switch memberAttr {
  164. case "member":
  165. modRequest.Delete(memberAttr, []string{user.DN()})
  166. case "memberuid":
  167. modRequest.Delete(memberAttr, []string{user.Username()})
  168. default:
  169. return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
  170. }
  171. err = c.Conn.Modify(modRequest)
  172. if err != nil {
  173. return err
  174. }
  175. return nil
  176. }
  177. func (c *Client) RemoveUserFromGroupByMemberValue(memberValue string, groupDN string) error {
  178. memberAttr, err := c.memberAttribute(groupDN)
  179. if err != nil {
  180. return err
  181. }
  182. modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
  183. switch memberAttr {
  184. case "member":
  185. modRequest.Delete(memberAttr, []string{memberValue})
  186. case "memberuid":
  187. modRequest.Delete(memberAttr, []string{memberValue})
  188. default:
  189. return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
  190. }
  191. err = c.Conn.Modify(modRequest)
  192. if err != nil {
  193. return err
  194. }
  195. return nil
  196. }
  197. func (c *Client) IsUserInGroup(user *orm.Credential, groupDN string) (bool, error) {
  198. memberAttr, err := c.memberAttribute(groupDN)
  199. if err != nil {
  200. return false, err
  201. }
  202. var memberValue string
  203. switch memberAttr {
  204. case "member":
  205. memberValue = user.DN()
  206. case "memberuid":
  207. memberValue = user.Username()
  208. default:
  209. return false, errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
  210. }
  211. searchRequest := ldap.NewSearchRequest(
  212. fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()),
  213. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  214. fmt.Sprintf("(&(%s=%s))", memberAttr, memberValue),
  215. []string{memberAttr},
  216. nil,
  217. )
  218. sr, err := c.Conn.Search(searchRequest)
  219. if err != nil {
  220. return false, err
  221. }
  222. return len(sr.Entries) > 0, nil
  223. }
  224. func (c *Client) GroupMembers(groupDN string) ([]*ldap.Entry, error) {
  225. searchRequest := ldap.NewSearchRequest(
  226. fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()),
  227. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  228. "(|(member=*)(memberuid=*))",
  229. []string{"member", "memberuid"},
  230. nil,
  231. )
  232. sr, err := c.Conn.Search(searchRequest)
  233. if err != nil {
  234. return nil, err
  235. }
  236. return sr.Entries, nil
  237. }
  238. func (c *Client) Users(searchDN string) ([]*ldap.Entry, error) {
  239. result, err := c.Search(
  240. searchDN,
  241. "(&(objectClass=organizationalPerson))",
  242. []string{"dn", "cn"},
  243. )
  244. if err != nil {
  245. return nil, err
  246. }
  247. return result.Entries, nil
  248. }
  249. func (c *Client) Search(base string, filter string, attributes []string) (*ldap.SearchResult, error) {
  250. searchRequest := ldap.NewSearchRequest(
  251. base,
  252. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  253. filter,
  254. attributes,
  255. nil,
  256. )
  257. sr, err := c.Conn.Search(searchRequest)
  258. if err != nil {
  259. return nil, err
  260. }
  261. return sr, nil
  262. }
  263. func (c *Client) UserExists(user *orm.Credential, searchDN string) (bool, error) {
  264. searchRequest := ldap.NewSearchRequest(
  265. searchDN,
  266. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  267. fmt.Sprintf("(&(objectClass=organizationalPerson)(cn=%s))", user.CompleteName()),
  268. []string{"dn", "cn"},
  269. nil,
  270. )
  271. sr, err := c.Conn.Search(searchRequest)
  272. if err != nil {
  273. return false, err
  274. }
  275. return len(sr.Entries) > 0, nil
  276. }
  277. func (c *Client) DomainDN() string {
  278. return c.Config.DomainDN()
  279. }
  280. func (c *Client) GroupsDN() string {
  281. return fmt.Sprintf("%s,%s", c.Config.Ldap.GroupsDN, c.DomainDN())
  282. }
  283. func (c *Client) PeopleDN() string {
  284. return fmt.Sprintf("%s,%s", c.Config.Ldap.PeopleDN, c.DomainDN())
  285. }
  286. func (c *Client) NextMailUIDNumber() (string, error) {
  287. searchRequest := ldap.NewSearchRequest(
  288. c.PeopleDN(),
  289. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  290. "(mailUidNumber=*)",
  291. []string{"mailUidNumber"},
  292. nil,
  293. )
  294. sr, err := c.Conn.Search(searchRequest)
  295. if err != nil {
  296. return "0", err
  297. }
  298. if len(sr.Entries) == 0 {
  299. return c.Config.Ldap.FirstUIDNumber, nil
  300. }
  301. var uids []int
  302. for _, e := range sr.Entries {
  303. n, err := strconv.Atoi(e.Attributes[0].Values[0])
  304. uids = append(uids, n)
  305. if err != nil {
  306. return "0", err
  307. }
  308. }
  309. sort.Ints(uids)
  310. nextUid := uids[len(uids)-1] + 1
  311. return strconv.Itoa(nextUid), nil
  312. }
  313. func (c *Client) memberAttribute(groupDN string) (string, error) {
  314. searchRequest := ldap.NewSearchRequest(
  315. fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()),
  316. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  317. "(member=*)",
  318. []string{"member"},
  319. nil,
  320. )
  321. sr, err := c.Conn.Search(searchRequest)
  322. if err != nil {
  323. return "", err
  324. }
  325. if len(sr.Entries) > 0 {
  326. return "member", nil
  327. }
  328. return "memberuid", nil
  329. }
  330. func (c *Client) personDN(person CompleteNameI, baseDN string) string {
  331. dn := fmt.Sprintf("cn=%s,%s", person.CompleteName(), baseDN)
  332. return dn
  333. }