This repository has been archived on 2023-10-10. You can view files and clone it, but cannot push or open issues or pull requests.
ciscoadressbook/main.go

176 lines
4.9 KiB
Go

package main
import (
"crypto/tls"
"encoding/xml"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/go-ldap/ldap"
"github.com/golang/glog"
)
type DirectoryEntry struct {
Name string
Telephone string
}
type CiscoIPPhoneDirectory struct {
Title string
Prompt string
DirectoryEntry []DirectoryEntry
}
var (
ldapAddress string
ldapUsername string
ldapPasswordFile string
ldapSearchBase string
ldapPhoneAttribute string
ldapNameAttribute string
bindAddress string
directoryTitle string
directoryPrompt string
regenerationPeriodString string
ldapPassword string
regenerationPeriod time.Duration
directory *CiscoIPPhoneDirectory
mutex sync.RWMutex
)
func main() {
flag.StringVar(&ldapAddress, "ldap_address", "ldap.hackerspace.pl", "Address of LDAP endpoint")
flag.StringVar(&ldapUsername, "ldap_username", "cn=ciscoaddressbook,ou=Services,dc=hackerspace,dc=pl", "DN to bind as to LDAP")
flag.StringVar(&ldapPasswordFile, "ldap_password_file", "/var/ciscoaddressbook/password", "File with password to bind with to LDAP.")
flag.StringVar(&ldapSearchBase, "ldap_search_base", "ou=People,dc=hackerspace,dc=pl", "LDAP search base.")
flag.StringVar(&ldapNameAttribute, "ldap_name_attribute", "uid", "LDAP attribute containing display name.")
flag.StringVar(&ldapPhoneAttribute, "ldap_phone_attribute", "mobile", "LDAP attribute containing phone number.")
flag.StringVar(&bindAddress, "bind_address", "127.0.0.1:8080", "Address to bind HTTP server to.")
flag.StringVar(&directoryTitle, "directory_title", "Warsaw Hackerspace Members", "Name of Phone Directory to display.")
flag.StringVar(&directoryPrompt, "directory_prompt", "Choose the Hacker", "Prompt of Phone Directory to display.")
flag.StringVar(&regenerationPeriodString, "regeneration_rate", "1h", "How often to update the directory.")
flag.Parse()
file, err := os.Open(ldapPasswordFile)
if err != nil {
glog.Exit(err)
}
bytes, err := ioutil.ReadAll(file)
if err != nil {
glog.Exit(err)
}
ldapPassword = strings.TrimSpace(string(bytes))
period, err := time.ParseDuration(regenerationPeriodString)
if err != nil {
glog.Exit(err)
}
regenerationPeriod = period
if err = regenerateDirectory(); err != nil {
glog.Exit(err)
}
go func() {
for {
time.Sleep(regenerationPeriod)
if err := regenerateDirectory(); err != nil {
glog.Error("When regenerating directory: %v", err)
}
}
}()
http.HandleFunc("/api/1/directory.xml", handleXMLRequest)
http.HandleFunc("/api/1/regenerate", handleRegenerateRequest)
glog.Exit(http.ListenAndServe(bindAddress, nil))
}
func handleXMLRequest(w http.ResponseWriter, r *http.Request) {
mutex.RLock()
defer mutex.RUnlock()
data, err := xml.Marshal(directory)
if err != nil {
glog.Error("When rendering directory: %v", err)
w.WriteHeader(500)
w.Write([]byte("An internal error occured."))
glog.Infof("%v: %v %v 500 Internal Error", r.RemoteAddr, r.Method, r.URL.RequestURI())
return
}
glog.Infof("%v: %v %v 200 OK", r.RemoteAddr, r.Method, r.URL.RequestURI())
w.Write(data)
}
func handleRegenerateRequest(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Regeneration request queued."))
go func() {
err := regenerateDirectory()
if err != nil {
glog.Error("When regenerating directory: %v", err)
}
}()
}
func regenerateDirectory() error {
glog.Info("Regenerating directory...")
address := fmt.Sprintf("%s:389", ldapAddress)
l, err := ldap.Dial("tcp", address)
if err != nil {
return fmt.Errorf("in ldap.Dial(%q): %v", address, err)
}
config := &tls.Config{
ServerName: ldapAddress,
}
err = l.StartTLS(config)
if err != nil {
return fmt.Errorf("in StartTLS: %v", err)
}
err = l.Bind(ldapUsername, ldapPassword)
if err != nil {
return fmt.Errorf("in Bind: %v", err)
}
request := ldap.NewSearchRequest(
ldapSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(cn=*)",
[]string{"dn", ldapNameAttribute, ldapPhoneAttribute},
nil,
)
res, err := l.Search(request)
if err != nil {
glog.Exit(err)
}
mutex.Lock()
defer mutex.Unlock()
directory = &CiscoIPPhoneDirectory{
Title: directoryTitle,
Prompt: directoryPrompt,
DirectoryEntry: []DirectoryEntry{},
}
for _, entry := range res.Entries {
directoryEntry := DirectoryEntry{}
for _, attribute := range entry.Attributes {
if attribute.Name != ldapPhoneAttribute || len(attribute.Values) == 0 {
directoryEntry.Name = attribute.Values[0]
}
if attribute.Name != ldapNameAttribute || len(attribute.Values) == 0 {
directoryEntry.Telephone = attribute.Values[0]
}
}
if directoryEntry.Name == "" || directoryEntry.Telephone == "" {
continue
}
directory.DirectoryEntry = append(directory.DirectoryEntry, directoryEntry)
}
glog.Info("Directory regenerated.")
return nil
}