Nuestro burocrata preferido: Sam Lowry https://lowry.sindominio.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lowry/ldap/user.go

226 lines
5.5 KiB

package ldap
import (
"errors"
"fmt"
"log"
5 years ago
"strconv"
"github.com/go-ldap/ldap"
)
5 years ago
//User has the ldap data of the user
type User struct {
DN string
Name string
Shell string
Home string
Mail string
5 years ago
UID int
GID int
}
// ValidateUser in the ldap
func (l Ldap) ValidateUser(user string, pass string) error {
conn, err := l.login(user, pass)
if err == nil {
conn.Close()
}
return err
}
// ChangePass changes logged in user's password
func (l Ldap) ChangePass(user string, oldpass string, newpass string) error {
if oldpass == "" {
return errors.New("Old password can not be empty")
}
conn, err := l.login(user, oldpass)
if err != nil {
return err
}
defer conn.Close()
return l.changePass(conn, user, oldpass, newpass)
}
// ChangePassAdmin changes user's password as admin
// (without knowing the old password)
func (l Ldap) ChangePassAdmin(user string, pass string) error {
conn, err := l.connect()
if err != nil {
return err
}
defer conn.Close()
return l.changePass(conn, user, "", pass)
}
func (l Ldap) changePass(conn *ldap.Conn, user, oldpass, newpass string) error {
if l.RO {
log.Println("Changing password in read only mode")
return nil
}
passwordModifyRequest := ldap.NewPasswordModifyRequest(l.userDN(user), oldpass, newpass)
_, err := conn.PasswordModify(passwordModifyRequest)
5 years ago
return err
}
//GetUser returns the user data
func (l Ldap) GetUser(name string) (User, error) {
conn, err := l.connect()
if err != nil {
return User{}, err
}
defer conn.Close()
entry, err := l.searchUser(name, conn)
if err != nil {
return User{}, err
}
return newUser(entry), nil
}
//ListUsers returns a list of all users in the ldap
func (l Ldap) ListUsers() ([]User, error) {
5 years ago
conn, err := l.connect()
if err != nil {
return nil, err
}
defer conn.Close()
searchRequest := ldap.NewSearchRequest(
"ou=people,"+l.DC,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=posixAccount))",
[]string{"dn", "uid", "uidNumber", "gidNumber", "loginShell", "homeDirectory", "mail"},
5 years ago
nil,
)
sr, err := conn.Search(searchRequest)
if err != nil {
return nil, err
}
users := []User{}
for _, entry := range sr.Entries {
users = append(users, newUser(entry))
5 years ago
}
return users, nil
}
// AddUser to the ldap
func (l *Ldap) AddUser(user string, pass string, gid int) error {
conn, err := l.connect()
if err != nil {
return err
}
defer conn.Close()
entry, err := l.searchUser(user, conn)
if entry != nil {
return errors.New("User name already exist: " + user)
}
uid, err := l.getLastID("uidNumber")
if err != nil {
return err
}
uid++
dn := l.userDN(user)
addRequest := ldap.NewAddRequest(dn)
addRequest.Attribute("uid", []string{ldap.EscapeFilter(user)})
addRequest.Attribute("cn", []string{ldap.EscapeFilter(user)})
addRequest.Attribute("sn", []string{ldap.EscapeFilter(user)})
addRequest.Attribute("objectClass", []string{"inetOrgPerson", "posixAccount", "shadowAccount", "inetLocalMailRecipient", "top"})
addRequest.Attribute("uidNumber", []string{strconv.Itoa(uid)})
addRequest.Attribute("gidNumber", []string{strconv.Itoa(gid)})
addRequest.Attribute("loginShell", []string{"/bin/false"})
addRequest.Attribute("homeDirectory", []string{l.HomePath + user})
addRequest.Attribute("mail", []string{user + "@" + l.MailDomain})
addRequest.Attribute("mailHost", []string{"mail." + l.MailDomain})
addRequest.Attribute("mailRoutingAddress", []string{user + "@mail." + l.MailDomain})
err = conn.Add(addRequest)
if err != nil {
return err
}
passwordModifyRequest := ldap.NewPasswordModifyRequest(dn, "", pass)
_, err = conn.PasswordModify(passwordModifyRequest)
return err
}
// DelUser removes the user from ldap
func (l Ldap) DelUser(user string) error {
return l.del(l.userDN(user))
}
// ChangeShell for the user
func (l Ldap) ChangeShell(user, shell string) error {
conn, err := l.connect()
if err != nil {
return err
}
defer conn.Close()
modifyRequest := ldap.NewModifyRequest(l.userDN(user))
modifyRequest.Replace("loginShell", []string{shell})
return conn.Modify(modifyRequest)
}
func (l Ldap) userDN(user string) string {
userStr := ldap.EscapeFilter(user)
return fmt.Sprintf("uid=%s,ou=People,%s", userStr, l.DC)
}
func (l Ldap) login(user string, password string) (*ldap.Conn, error) {
conn, err := l.connect()
if err != nil {
return nil, err
}
entry, err := l.searchUser(user, conn)
if err != nil {
conn.Close()
return nil, err
}
userdn := entry.DN
return conn, conn.Bind(userdn, password)
}
func (l Ldap) searchUser(user string, conn *ldap.Conn) (entry *ldap.Entry, err error) {
searchRequest := ldap.NewSearchRequest(
"ou=people,"+l.DC,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=posixAccount)(uid=%s))", ldap.EscapeFilter(user)),
[]string{"dn", "uid", "uidNumber", "gidNumber", "loginShell", "homeDirectory", "mail"},
nil,
)
sr, err := conn.Search(searchRequest)
if err != nil {
return entry, err
}
switch len(sr.Entries) {
case 1:
entry = sr.Entries[0]
return entry, nil
case 0:
return entry, errors.New("No user found")
default:
return entry, errors.New("More than one user found!!!")
}
}
func newUser(entry *ldap.Entry) User {
uid, _ := strconv.Atoi(entry.GetAttributeValue("uidNumber"))
gid, _ := strconv.Atoi(entry.GetAttributeValue("gidNumber"))
return User{
DN: entry.DN,
Name: entry.GetAttributeValue("uid"),
Shell: entry.GetAttributeValue("loginShell"),
Home: entry.GetAttributeValue("homeDirectory"),
Mail: entry.GetAttributeValue("mail"),
UID: uid,
GID: gid,
}
}