Reputation: 65
I try to build web application using Active Directory authentication. I also need to get email address of a user. I have a function that can get email address. Where and how should I use the function to get email in mainHandler()?
main.go
func main() {
http.HandleFunc("/", auth.BasicAuth(mainHandler))
http.ListenAndServe(":8080", nil)
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("templates/main.html")
if err == nil {
tmpl.Execute(w, nil)
}
}
auth.go
type Handler func(w http.ResponseWriter, r *http.Request)
// BasicAuth - handler wrapper for authentication
func BasicAuth(pass Handler) Handler {
return func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
err := ValidateAD(username, password)
if err != nil || !ok {
realm := "Please enter your corporate key and password"
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
// w.WriteHeader(401)
http.Error(w, "authorization failed", http.StatusUnauthorized)
return
}
pass(w, r)
}
}
var ErrEmptyUserOrPass = errors.New("Username or password cannot be empty")
var conn *ldap.Conn
// ValidateAD validation based on Active Directory
func ValidateAD(user, passwd string) error {
if user == "" || passwd == "" {
return ErrEmptyUserOrPass
}
tlsConfig := &tls.Config{InsecureSkipVerify: true}
var err error
conn, err = ldap.DialTLS("tcp", "ad.something.com:636", tlsConfig)
if err != nil {
return err
}
defer conn.Close()
domainPrefix := "ad\\"
err = conn.Bind(domainPrefix+user, passwd)
if err != nil {
return err
}
return nil
}
// GetLDAPEmail returns email address for given username and password
func GetLDAPEmail(user, password string) (string, error) {
if err := ValidateAD(user, password); err != nil {
return "", err
}
searchBase := "CN=" + user + ",OU=OU1,OU=OU2,OU=OU3,DC=ad,DC=something,DC=com"
searchFilter := "(&(samAccountName=" + user + "))"
searchRequest := ldap.NewSearchRequest(
searchBase, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
searchFilter, // The filter to apply
[]string{"mail"}, // A list attributes to retrieve
nil,
)
sr, err := conn.Search(searchRequest)
if err != nil {
return "", err
}
email := sr.Entries[0].GetAttributeValue("mail")
return strings.ToLower(email), nil
}
Upvotes: 1
Views: 2335
Reputation: 46
The way your functions and handlers are wired, I do not see a whole lot of "clean" options to pass state back from BasicAuth()
to the mainHandler()
per request.
If you are open to the idea of changing the way you setup your handlers, here is a skeleton structure which you can expand to fit your needs:
package main
import (
"fmt"
"log"
"net/http"
)
type User struct {
Name string
Password string
}
func main() {
mux := http.NewServeMux()
mux.Handle("/", &User{})
s := &http.Server{Addr: "localhost:8080", Handler: mux}
log.Fatal(s.ListenAndServe())
}
func (u *User) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//Pull out the username and password
u.BasicAuth()
fmt.Println(u.Name)
fmt.Println(u.Password)
//The rest of your main handler
}
func (u *User) BasicAuth() {
//This is to demonstrate the ability to pass state
//Edit this to fit your needs
u.Name = "user"
u.Password = "pass"
}
The User
struct implements the ServeHTTP function, effectively implementing the http.Handler
interface which opens up the option of adding it to a multiplexer which in turn helps maintain the username and password per request. Although the methods receive a pointer type you could change it to better suit your needs.
Upvotes: 1