123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- package ldap
- import (
- "errors"
- "fmt"
- "sort"
- "strconv"
- "gogs.carducci-dante.gov.it/karmen/core/config"
- "gogs.carducci-dante.gov.it/karmen/core/orm"
- ldap "gopkg.in/ldap.v2"
- )
- type DNer interface {
- DN() string
- }
- type CompleteNameI interface {
- CompleteName() string
- }
- type Client struct {
- Conn *ldap.Conn
- Config *config.ConfigT
- }
- func (c *Client) Close() {
- c.Conn.Close()
- }
- func NewClient(host string, config *config.ConfigT) (*Client, error) {
- var err error
- client := new(Client)
- client.Config = config
- client.Conn, err = ldap.Dial("tcp", host)
- if err != nil {
- return nil, err
- }
- err = client.Conn.Bind(config.AdminCN(), config.Ldap.AdminPassword)
- if err != nil {
- return nil, err
- }
- return client, nil
- }
- 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(user.DN())
- addRequest.Attribute("objectClass", []string{
- "inetOrgPerson",
- "posixAccount",
- "PostfixBookMailAccount",
- "organizationalPerson",
- "extensibleObject",
- })
- 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{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", orm.SaltPassword(user.GetPlainPassword()))})
- err = c.Conn.Add(addRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) UpdateUser(user orm.User) 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"})
- modRequest.Replace("mailGidNumber", []string{c.Config.Ldap.MailGIDNumber})
- modRequest.Replace("mailQuota", []string{c.Config.Ldap.MailQuota})
- err := c.Conn.Modify(modRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) UpdateUserPassword(user orm.User) error {
- modRequest := ldap.NewModifyRequest(user.DN())
- modRequest.Replace("userPassword", []string{fmt.Sprintf("{SSHA}%s", orm.SaltPassword(user.GetPlainPassword()))})
- err := c.Conn.Modify(modRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) VerifyUserLogin(username, password string) error {
- // Search for the given username
- searchRequest := ldap.NewSearchRequest(
- c.DomainDN(),
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- fmt.Sprintf("(&(objectClass=organizationalPerson)(uid=%s))", username),
- []string{"dn"},
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return err
- }
- if len(sr.Entries) != 1 {
- return errors.New("User does not exist or too many entries returned")
- }
- userdn := sr.Entries[0].DN
- // Bind as the user to verify their password
- err = c.Conn.Bind(userdn, password)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) DeleteUser(user orm.User) error {
- delRequest := ldap.NewDelRequest(user.DN(), nil)
- err := c.Conn.Del(delRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) DeleteByDN(dn string) error {
- delRequest := ldap.NewDelRequest(dn, nil)
- err := c.Conn.Del(delRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) AddUserToGroup(user orm.User, groupDN string) error {
- memberAttr, err := c.memberAttribute(groupDN)
- if err != nil {
- return err
- }
- modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
- switch memberAttr {
- case "member":
- modRequest.Add(memberAttr, []string{user.DN()})
- case "memberuid":
- modRequest.Add(memberAttr, []string{user.Username()})
- default:
- return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
- }
- err = c.Conn.Modify(modRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) RemoveUserFromGroup(user *orm.Credential, groupDN string) error {
- memberAttr, err := c.memberAttribute(groupDN)
- if err != nil {
- return err
- }
- modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
- switch memberAttr {
- case "member":
- modRequest.Delete(memberAttr, []string{user.DN()})
- case "memberuid":
- modRequest.Delete(memberAttr, []string{user.Username()})
- default:
- return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
- }
- err = c.Conn.Modify(modRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) RemoveUserFromGroupByMemberValue(memberValue string, groupDN string) error {
- memberAttr, err := c.memberAttribute(groupDN)
- if err != nil {
- return err
- }
- modRequest := ldap.NewModifyRequest(fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()))
- switch memberAttr {
- case "member":
- modRequest.Delete(memberAttr, []string{memberValue})
- case "memberuid":
- modRequest.Delete(memberAttr, []string{memberValue})
- default:
- return errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
- }
- err = c.Conn.Modify(modRequest)
- if err != nil {
- return err
- }
- return nil
- }
- func (c *Client) IsUserInGroup(user *orm.Credential, groupDN string) (bool, error) {
- memberAttr, err := c.memberAttribute(groupDN)
- if err != nil {
- return false, err
- }
- var memberValue string
- switch memberAttr {
- case "member":
- memberValue = user.DN()
- case "memberuid":
- memberValue = user.Username()
- default:
- return false, errors.New(fmt.Sprintf("Attribute %s is not supported!", memberAttr))
- }
- searchRequest := ldap.NewSearchRequest(
- fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()),
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- fmt.Sprintf("(&(%s=%s))", memberAttr, memberValue),
- []string{memberAttr},
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return false, err
- }
- return len(sr.Entries) > 0, nil
- }
- func (c *Client) GroupMembers(groupDN string) ([]*ldap.Entry, error) {
- searchRequest := ldap.NewSearchRequest(
- fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()),
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- "(|(member=*)(memberuid=*))",
- []string{"member", "memberuid"},
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return nil, err
- }
- return sr.Entries, nil
- }
- func (c *Client) Users(searchDN string) ([]*ldap.Entry, error) {
- result, err := c.Search(
- searchDN,
- "(&(objectClass=organizationalPerson))",
- []string{"dn", "cn"},
- )
- if err != nil {
- return nil, err
- }
- return result.Entries, nil
- }
- func (c *Client) Search(base string, filter string, attributes []string) (*ldap.SearchResult, error) {
- searchRequest := ldap.NewSearchRequest(
- base,
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- filter,
- attributes,
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return nil, err
- }
- return sr, nil
- }
- func (c *Client) UserExists(user *orm.Credential, searchDN string) (bool, error) {
- searchRequest := ldap.NewSearchRequest(
- searchDN,
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- fmt.Sprintf("(&(objectClass=organizationalPerson)(cn=%s))", user.CompleteName()),
- []string{"dn", "cn"},
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return false, err
- }
- return len(sr.Entries) > 0, nil
- }
- func (c *Client) DomainDN() string {
- return c.Config.DomainDN()
- }
- func (c *Client) GroupsDN() string {
- return fmt.Sprintf("%s,%s", c.Config.Ldap.GroupsDN, c.DomainDN())
- }
- func (c *Client) PeopleDN() string {
- return fmt.Sprintf("%s,%s", c.Config.Ldap.PeopleDN, c.DomainDN())
- }
- func (c *Client) NextMailUIDNumber() (string, error) {
- searchRequest := ldap.NewSearchRequest(
- c.PeopleDN(),
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- "(mailUidNumber=*)",
- []string{"mailUidNumber"},
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return "0", err
- }
- if len(sr.Entries) == 0 {
- return c.Config.Ldap.FirstUIDNumber, nil
- }
- var uids []int
- for _, e := range sr.Entries {
- n, err := strconv.Atoi(e.Attributes[0].Values[0])
- uids = append(uids, n)
- if err != nil {
- return "0", err
- }
- }
- sort.Ints(uids)
- nextUid := uids[len(uids)-1] + 1
- return strconv.Itoa(nextUid), nil
- }
- func (c *Client) memberAttribute(groupDN string) (string, error) {
- searchRequest := ldap.NewSearchRequest(
- fmt.Sprintf("%s,%s", groupDN, c.GroupsDN()),
- ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
- "(member=*)",
- []string{"member"},
- nil,
- )
- sr, err := c.Conn.Search(searchRequest)
- if err != nil {
- return "", err
- }
- if len(sr.Entries) > 0 {
- return "member", nil
- }
- return "memberuid", nil
- }
- func (c *Client) personDN(person CompleteNameI, baseDN string) string {
- dn := fmt.Sprintf("cn=%s,%s", person.CompleteName(), baseDN)
- return dn
- }
|