package admin import ( "crypto/hmac" "crypto/sha256" "errors" "fmt" "html/template" "io/ioutil" "log" "net/http" "os" "path/filepath" "github.com/gorilla/sessions" ) type AuthCookie struct { Templates *template.Template Store *sessions.CookieStore DataDir *os.File PasswordSecret string } func (app *AuthCookie) LoginPage(res http.ResponseWriter, req *http.Request) { formErr := make(map[string]error) switch req.Method { case "POST": username := req.FormValue("username") formErr["username"] = app.VerifyUsername(username) formErr["password"] = app.VerifyPassword(username, req.FormValue("password"), res, req) if formErr["username"] == nil && formErr["password"] == nil { app.SaveUsername(username, res, req) RedirectHome(res, req) return } fallthrough case "GET": app.Templates.ExecuteTemplate(res, "login.html", formErr) } } func (app *AuthCookie) LogoutPage(res http.ResponseWriter, req *http.Request) { app.SaveUsername("", res, req) RedirectHome(res, req) } func (app *AuthCookie) VerifyUsername(username string) error { if username == "" { return errors.New("Empty username") } return nil } func (app *AuthCookie) VerifyPassword(username, password string, res http.ResponseWriter, req *http.Request) error { if password == "" { return errors.New("Empty password") } passfile, err := os.Open(filepath.Join(app.DataDir.Name(), username, ".password")) if err != nil { log.Println("Cannot open password file", err) return errors.New("Authentification failed") } defer passfile.Close() if username == "" { return nil } expected, err := ioutil.ReadAll(passfile) if err != nil { log.Println("Cannot read password file", err) return errors.New("Authentification failed") } expectedStr := string(expected) var expectedMAC []byte fmt.Sscanf(expectedStr, "%x", &expectedMAC) mac := hmac.New(sha256.New, []byte(app.PasswordSecret)) mac.Write([]byte(password)) passwordMAC := mac.Sum(nil) if !hmac.Equal(passwordMAC, expectedMAC) { log.Printf("Unmatched password for %s: %x - %x\n", username, passwordMAC, expectedMAC) return errors.New("Authentification failed") } log.Printf("Authentification successful") return nil } func (app *AuthCookie) CurrentSession(res http.ResponseWriter, req *http.Request) *sessions.Session { session, _ := app.Store.Get(req, "session") return session } func (app *AuthCookie) SaveUsername(username string, res http.ResponseWriter, req *http.Request) { session := app.CurrentSession(res, req) session.Values["username"] = username session.Save(req, res) } func (app *AuthCookie) IsLoggedIn(res http.ResponseWriter, req *http.Request) bool { session := app.CurrentSession(res, req) return session != nil && session.Values["username"] != "" } func (app *AuthCookie) Username(res http.ResponseWriter, req *http.Request) string { session := app.CurrentSession(res, req) if session == nil { return "" } return session.Values["username"].(string) } func RedirectHome(res http.ResponseWriter, req *http.Request) { http.Redirect(res, req, "/", http.StatusSeeOther) }