go-examples/photoblog/admin/admin.go

169 lines
4.4 KiB
Go
Raw Permalink Normal View History

package admin
import (
2017-07-29 14:51:12 +00:00
"crypto/hmac"
"crypto/sha256"
"errors"
"fmt"
"html/template"
2017-07-29 14:51:12 +00:00
"io/ioutil"
"log"
"net/http"
2017-07-29 14:51:12 +00:00
"os"
"path/filepath"
2017-07-30 08:01:23 +00:00
"time"
2017-07-30 09:05:20 +00:00
"github.com/gorilla/csrf"
"github.com/gorilla/sessions"
)
2017-07-29 17:22:11 +00:00
const (
USER = "username"
PWD = "password"
SESS_USER = "username"
SESSION = "photoblog.session"
)
// Application
type AuthCookie struct {
2017-07-29 14:51:12 +00:00
Templates *template.Template
Store *sessions.CookieStore
DataDir *os.File
PasswordSecret string
2017-07-30 09:05:20 +00:00
CsrfSecret string
}
2017-07-30 08:01:23 +00:00
// Constructor AuthCookie
2017-07-30 09:05:20 +00:00
func NewAuthCookie(tpl *template.Template, sessionSecret, passwordSecret, csrfSecret string, data *os.File) *AuthCookie {
2017-07-30 08:01:23 +00:00
app := &AuthCookie{
Templates: tpl,
Store: sessions.NewCookieStore([]byte(sessionSecret)),
DataDir: data,
PasswordSecret: passwordSecret,
2017-07-30 09:05:20 +00:00
CsrfSecret: csrfSecret,
2017-07-30 08:01:23 +00:00
}
app.Store.Options = &sessions.Options{
Secure: true,
HttpOnly: true,
MaxAge: int((24 * time.Hour) / time.Second),
}
return app
}
2017-07-30 09:05:20 +00:00
type LoginModel struct {
Username string
UsernameError error
PasswordError error
CsrfToken string
}
// Csrf handler wrapper
func (app *AuthCookie) Protect() func(http.Handler) http.Handler {
return csrf.Protect([]byte(app.CsrfSecret), csrf.Secure(true), csrf.HttpOnly(true))
}
2017-07-29 17:22:11 +00:00
// Verify Username
2017-07-29 14:51:12 +00:00
func (app *AuthCookie) VerifyUsername(username string) error {
if username == "" {
return errors.New("Empty username")
}
return nil
}
2017-07-29 17:22:11 +00:00
// Verify password for user, check with file
2017-07-29 14:51:12 +00:00
func (app *AuthCookie) VerifyPassword(username, password string, res http.ResponseWriter, req *http.Request) error {
if password == "" {
return errors.New("Empty password")
}
2017-07-29 17:22:11 +00:00
if username == "" {
return nil
}
// open password file
2017-07-29 14:51:12 +00:00
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()
2017-07-29 17:22:11 +00:00
// read password file
2017-07-29 14:51:12 +00:00
expected, err := ioutil.ReadAll(passfile)
if err != nil {
log.Println("Cannot read password file", err)
return errors.New("Authentification failed")
}
2017-07-29 17:22:11 +00:00
// hash password and compare
2017-07-29 14:51:12 +00:00
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")
}
2017-07-29 17:22:11 +00:00
log.Printf("Authentification successful (" + username + ")")
2017-07-29 14:51:12 +00:00
return nil
}
2017-07-29 17:22:11 +00:00
// Save username in session
func (app *AuthCookie) SaveUsername(username string, res http.ResponseWriter, req *http.Request) {
session := app.CurrentSession(res, req)
2017-07-29 17:22:11 +00:00
session.Values[SESS_USER] = username
session.Save(req, res)
}
2017-07-29 17:22:11 +00:00
// Test if a user is logged in
2017-07-29 11:54:23 +00:00
func (app *AuthCookie) IsLoggedIn(res http.ResponseWriter, req *http.Request) bool {
session := app.CurrentSession(res, req)
2017-07-29 17:22:11 +00:00
return session != nil && session.Values[SESS_USER] != ""
2017-07-29 11:54:23 +00:00
}
2017-07-29 17:22:11 +00:00
// Current username (from session cookie)
2017-07-29 11:54:23 +00:00
func (app *AuthCookie) Username(res http.ResponseWriter, req *http.Request) string {
session := app.CurrentSession(res, req)
if session == nil {
return ""
}
2017-07-29 17:30:32 +00:00
val, _ := session.Values[SESS_USER].(string)
return val
2017-07-29 11:54:23 +00:00
}
2017-07-29 17:22:11 +00:00
// Current session
func (app *AuthCookie) CurrentSession(res http.ResponseWriter, req *http.Request) *sessions.Session {
session, _ := app.Store.Get(req, SESSION)
return session
}
// Redirect to home page
func (app *AuthCookie) RedirectHome(res http.ResponseWriter, req *http.Request) {
http.Redirect(res, req, "/", http.StatusSeeOther)
}
2017-07-29 17:22:11 +00:00
// ROUTES //
// login: form
func (app *AuthCookie) LoginPage(res http.ResponseWriter, req *http.Request) {
2017-07-30 09:05:20 +00:00
model := LoginModel{
CsrfToken: csrf.Token(req),
}
2017-07-29 17:22:11 +00:00
if req.Method == http.MethodPost {
2017-07-30 09:05:20 +00:00
model.Username = req.FormValue(USER)
model.UsernameError = app.VerifyUsername(model.Username)
model.PasswordError = app.VerifyPassword(model.Username, req.FormValue(PWD), res, req)
if model.UsernameError == nil && model.PasswordError == nil {
app.SaveUsername(model.Username, res, req)
2017-07-29 17:22:11 +00:00
app.RedirectHome(res, req)
return
}
}
2017-07-30 09:05:20 +00:00
app.Templates.ExecuteTemplate(res, "login.html", model)
2017-07-29 17:22:11 +00:00
}
// logout: delete session and redirect to home
func (app *AuthCookie) LogoutPage(res http.ResponseWriter, req *http.Request) {
app.SaveUsername("", res, req)
app.RedirectHome(res, req)
}