go-examples/photoblog/admin/admin.go
2017-07-29 16:51:12 +02:00

107 lines
3.0 KiB
Go

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)
}