package main import ( "fmt" "html/template" "net/http" "regexp" "strings" "github.com/go-ldap/ldap/v3" ) func checkInviterLogin(w http.ResponseWriter, r *http.Request) *LoginStatus { login := checkLogin(w, r) if login == nil { return nil } if !login.CanInvite { http.Error(w, "Not authorized to invite new users.", http.StatusUnauthorized) return nil } return login } type NewAccountData struct { Username string DisplayName string GivenName string Surname string ErrorUsernameTaken bool ErrorInvalidUsername bool ErrorPasswordTooShort bool ErrorPasswordMismatch bool ErrorMessage string WarningMessage string Success bool } func handleInviteNewAccount(w http.ResponseWriter, r *http.Request) { templateInviteNewAccount := template.Must(template.ParseFiles("templates/layout.html", "templates/invite_new_account.html")) login := checkInviterLogin(w, r) if login == nil { return } data := &NewAccountData{} if r.Method == "POST" { r.ParseForm() data.Username = strings.TrimSpace(strings.Join(r.Form["username"], "")) data.DisplayName = strings.TrimSpace(strings.Join(r.Form["displayname"], "")) data.GivenName = strings.TrimSpace(strings.Join(r.Form["givenname"], "")) data.Surname = strings.TrimSpace(strings.Join(r.Form["surname"], "")) password1 := strings.Join(r.Form["password"], "") password2 := strings.Join(r.Form["password2"], "") tryCreateAccount(login.conn, data, password1, password2) } templateInviteNewAccount.Execute(w, data) } func tryCreateAccount(l *ldap.Conn, data *NewAccountData, pass1 string, pass2 string) { // Check if username is correct if match, err := regexp.MatchString("^[a-zA-Z0-9._-]+$", data.Username); !(err == nil && match) { data.ErrorInvalidUsername = true } // Check if user exists userDn := config.UserNameAttr + "=" + data.Username + "," + config.UserBaseDN searchRq := ldap.NewSearchRequest( userDn, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectclass=*)", []string{"dn"}, nil) sr, err := l.Search(searchRq) if err != nil { data.ErrorMessage = err.Error() return } if len(sr.Entries) > 0 { data.ErrorUsernameTaken = true return } // Check that password is long enough if len(pass1) < 8 { data.ErrorPasswordTooShort = true return } if pass1 != pass2 { data.ErrorPasswordMismatch = true return } // Actually create user req := ldap.NewAddRequest(userDn, nil) req.Attribute("objectclass", []string{"inetOrgPerson", "organizationalPerson", "person", "top"}) req.Attribute("structuralobjectclass", []string{"inetOrgPerson"}) req.Attribute("userpassword", []string{SSHAEncode([]byte(pass1))}) if len(data.DisplayName) > 0 { req.Attribute("displayname", []string{data.DisplayName}) } if len(data.GivenName) > 0 { req.Attribute("givenname", []string{data.GivenName}) } if len(data.Surname) > 0 { req.Attribute("sn", []string{data.Surname}) } if len(config.InvitedMailFormat) > 0 { email := strings.ReplaceAll(config.InvitedMailFormat, "{}", data.Username) req.Attribute("mail", []string{email}) } err = l.Add(req) if err != nil { data.ErrorMessage = err.Error() return } for _, group := range config.InvitedAutoGroups { req := ldap.NewModifyRequest(group, nil) req.Add("member", []string{userDn}) err = l.Modify(req) if err != nil { data.WarningMessage += fmt.Sprintf("Cannot add to %s: %s\n", group, err.Error()) } } data.Success = true } func handleInviteSendCode(w http.ResponseWriter, r *http.Request) { }