2017-07-29 09:22:34 +00:00
|
|
|
package admin
|
|
|
|
|
|
|
|
import (
|
2017-07-29 14:51:12 +00:00
|
|
|
"crypto/hmac"
|
|
|
|
"crypto/sha256"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2017-07-29 09:22:34 +00:00
|
|
|
"html/template"
|
2017-07-29 14:51:12 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
2017-07-29 09:22:34 +00:00
|
|
|
"net/http"
|
2017-07-29 14:51:12 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-07-29 09:22:34 +00:00
|
|
|
|
|
|
|
"github.com/gorilla/sessions"
|
|
|
|
)
|
|
|
|
|
2017-07-29 17:22:11 +00:00
|
|
|
const (
|
|
|
|
USER = "username"
|
|
|
|
PWD = "password"
|
|
|
|
SESS_USER = "username"
|
|
|
|
SESSION = "photoblog.session"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Application
|
2017-07-29 09:22:34 +00:00
|
|
|
type AuthCookie struct {
|
2017-07-29 14:51:12 +00:00
|
|
|
Templates *template.Template
|
|
|
|
Store *sessions.CookieStore
|
|
|
|
DataDir *os.File
|
|
|
|
PasswordSecret string
|
2017-07-29 09:22:34 +00:00
|
|
|
}
|
|
|
|
|
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
|
2017-07-29 09:22:34 +00:00
|
|
|
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
|
2017-07-29 09:22:34 +00:00
|
|
|
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:22:11 +00:00
|
|
|
val := session.Values[SESS_USER]
|
|
|
|
if val == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return val.(string)
|
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) {
|
2017-07-29 09:22:34 +00:00
|
|
|
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) {
|
|
|
|
formErr := make(map[string]error)
|
|
|
|
if req.Method == http.MethodPost {
|
|
|
|
username := req.FormValue(USER)
|
|
|
|
formErr[USER] = app.VerifyUsername(username)
|
|
|
|
formErr[PWD] = app.VerifyPassword(username, req.FormValue(PWD), res, req)
|
|
|
|
if formErr[USER] == nil && formErr[PWD] == nil {
|
|
|
|
app.SaveUsername(username, res, req)
|
|
|
|
app.RedirectHome(res, req)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
app.Templates.ExecuteTemplate(res, "login.html", formErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|