Style, auth with file
This commit is contained in:
parent
ee2bfd95cb
commit
8ec59e4826
@ -1,31 +1,35 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthCookie struct {
|
type AuthCookie struct {
|
||||||
Templates *template.Template
|
Templates *template.Template
|
||||||
Store *sessions.CookieStore
|
Store *sessions.CookieStore
|
||||||
|
DataDir *os.File
|
||||||
|
PasswordSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *AuthCookie) LoginPage(res http.ResponseWriter, req *http.Request) {
|
func (app *AuthCookie) LoginPage(res http.ResponseWriter, req *http.Request) {
|
||||||
formErr := make(map[string]string)
|
formErr := make(map[string]error)
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case "POST":
|
case "POST":
|
||||||
username := req.FormValue("username")
|
username := req.FormValue("username")
|
||||||
if username == "" {
|
formErr["username"] = app.VerifyUsername(username)
|
||||||
formErr["username"] = "Empty username"
|
formErr["password"] = app.VerifyPassword(username, req.FormValue("password"), res, req)
|
||||||
}
|
if formErr["username"] == nil && formErr["password"] == nil {
|
||||||
password := req.FormValue("password")
|
|
||||||
if password == "" {
|
|
||||||
formErr["password"] = "Empty password"
|
|
||||||
}
|
|
||||||
// FIXME verifiy password with file in data dir
|
|
||||||
if len(formErr) == 0 {
|
|
||||||
app.SaveUsername(username, res, req)
|
app.SaveUsername(username, res, req)
|
||||||
RedirectHome(res, req)
|
RedirectHome(res, req)
|
||||||
return
|
return
|
||||||
@ -39,6 +43,44 @@ func (app *AuthCookie) LogoutPage(res http.ResponseWriter, req *http.Request) {
|
|||||||
app.SaveUsername("", res, req)
|
app.SaveUsername("", res, req)
|
||||||
RedirectHome(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 {
|
func (app *AuthCookie) CurrentSession(res http.ResponseWriter, req *http.Request) *sessions.Session {
|
||||||
session, _ := app.Store.Get(req, "session")
|
session, _ := app.Store.Get(req, "session")
|
||||||
return session
|
return session
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -29,20 +28,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer data.Close()
|
defer data.Close()
|
||||||
|
|
||||||
admin := admin.AuthCookie{
|
|
||||||
Templates: tpl,
|
|
||||||
Store: sessions.NewCookieStore([]byte("flQ6QzM/c3Jtdl9ycDx6OXRIfFgK")),
|
|
||||||
}
|
|
||||||
app := photo.PhotoBlog{
|
app := photo.PhotoBlog{
|
||||||
AuthCookie: &admin,
|
admin.AuthCookie{Templates: tpl,
|
||||||
Templates: tpl,
|
Store: sessions.NewCookieStore([]byte("flQ6QzM/c3Jtdl9ycDx6OXRIfFgK")),
|
||||||
DataDir: data,
|
DataDir: data,
|
||||||
|
PasswordSecret: "d2xnNSwoREQhfSxBVDQ0bF0yb2AK",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fileServer := http.FileServer(http.Dir(data.Name()))
|
fileServer := http.FileServer(http.Dir(data.Name()))
|
||||||
|
|
||||||
http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
|
http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
|
||||||
fmt.Println(req.RequestURI)
|
|
||||||
if strings.HasPrefix(req.RequestURI, "/data") {
|
if strings.HasPrefix(req.RequestURI, "/data") {
|
||||||
http.StripPrefix("/data/", fileServer).ServeHTTP(res, req)
|
http.StripPrefix("/data/", fileServer).ServeHTTP(res, req)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,6 @@ package photo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -25,8 +24,8 @@ var (
|
|||||||
type AppData struct {
|
type AppData struct {
|
||||||
Username string
|
Username string
|
||||||
Photos []string
|
Photos []string
|
||||||
errors []error
|
Err error
|
||||||
messages []string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimedFile struct {
|
type TimedFile struct {
|
||||||
@ -47,9 +46,7 @@ func (m timeMap) Swap(i, j int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PhotoBlog struct {
|
type PhotoBlog struct {
|
||||||
*admin.AuthCookie
|
admin.AuthCookie
|
||||||
Templates *template.Template
|
|
||||||
DataDir *os.File
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RedirectHome(res http.ResponseWriter, req *http.Request) {
|
func RedirectHome(res http.ResponseWriter, req *http.Request) {
|
||||||
@ -99,14 +96,16 @@ func (app *PhotoBlog) UploadPage(res http.ResponseWriter, req *http.Request) {
|
|||||||
RedirectHome(res, req)
|
RedirectHome(res, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := make(map[string]string)
|
data := AppData{
|
||||||
|
Username: app.Username(res, req),
|
||||||
|
}
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case "POST":
|
case "POST":
|
||||||
message, err := app.NewPhoto(res, req)
|
message, err := app.NewPhoto(res, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
data["error"] = err.Error()
|
data.Err = err
|
||||||
}
|
}
|
||||||
data["message"] = message
|
data.Message = message
|
||||||
fallthrough
|
fallthrough
|
||||||
case "GET":
|
case "GET":
|
||||||
app.Templates.ExecuteTemplate(res, "upload.html", data)
|
app.Templates.ExecuteTemplate(res, "upload.html", data)
|
||||||
|
@ -1,2 +1,87 @@
|
|||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
<style>
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
ul.photos {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin:0;
|
||||||
|
font-family: Verdana,sans-serif;
|
||||||
|
}
|
||||||
|
header ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
header li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
header p {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 14px 16px;
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
header a {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 14px 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
div.form-container {
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
input[type=submit] {
|
||||||
|
width: 50%;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 8px 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input[type=submit]:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
input[type=text], input[type=password] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 20px;
|
||||||
|
margin: 8px 0;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
a.mainlink {
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
.form-container a {
|
||||||
|
color: #4CAF50;
|
||||||
|
padding-left: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</html>
|
</html>
|
||||||
|
@ -2,3 +2,16 @@
|
|||||||
<html>
|
<html>
|
||||||
<head><title>Photo blog</title></head>
|
<head><title>Photo blog</title></head>
|
||||||
<body>
|
<body>
|
||||||
|
<header>
|
||||||
|
<ul>
|
||||||
|
<li><p>Home</p></li>
|
||||||
|
{{ if .Username }}
|
||||||
|
<li><p>Hello {{ .Username }}</p></li>
|
||||||
|
<li style="float: right"><a href="/logout">Logout</a></li>
|
||||||
|
{{ else }}
|
||||||
|
<li><p>You are not logged in</p></li>
|
||||||
|
<li style="float: right"><a href="/login">Login</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
@ -1,24 +1,13 @@
|
|||||||
{{ template "header.html" }}
|
{{ template "header.html" . }}
|
||||||
<header>
|
|
||||||
{{ if .Username }}
|
|
||||||
<p>Hello {{ .Username }}</p>
|
|
||||||
<a href="/logout">Logout</a>
|
|
||||||
{{ else }}
|
|
||||||
<p>You are not logged in<p>
|
|
||||||
<a href="/login">Login</a>
|
|
||||||
{{ end }}
|
|
||||||
</header>
|
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
<h1>Photo blog</h1>
|
<h1>Photo blog</h1>
|
||||||
{{ if .Username }}
|
{{ if .Username }}
|
||||||
<a href="upload">Add photo</a>
|
<a class="mainlink" href="upload">Add photo</a>
|
||||||
{{ end }}
|
|
||||||
<ul>
|
|
||||||
{{ range .Photos }}
|
|
||||||
<li><img src="{{ . }}" style="width:320px;height:200px;"></li>
|
|
||||||
{{ else }}
|
|
||||||
<li>No photos</li>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
<ul class="photos">
|
||||||
|
{{ range .Photos }}
|
||||||
|
<li><a href="{{ . }}"><img src="{{ . }}"></a></li>
|
||||||
|
{{ else }}
|
||||||
|
<li>No photos</li>
|
||||||
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ template "footer.html" }}
|
{{ template "footer.html" }}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
{{ template "header.html" }}
|
{{ template "header.html" . }}
|
||||||
<h1>Authentification</h1>
|
<h1>Authentification</h1>
|
||||||
<form action="/login" method="POST">
|
<div class="form-container">
|
||||||
<label>
|
<form action="/login" method="POST">
|
||||||
Username: {{ if .username }}<b>{{ .username }}</b>{{ end }}
|
<label>
|
||||||
<input type="text" name="username">
|
Username: {{ if .username }}<b class="error">{{ .username }}</b>{{ end }}
|
||||||
</label>
|
<input type="text" name="username" placeholder="login...">
|
||||||
<br>
|
</label>
|
||||||
<label>
|
<br>
|
||||||
Password: {{ if .password }}<b>{{ .password }}</b>{{ end }}
|
<label>
|
||||||
<input type="password" name="password">
|
Password: {{ if .password }}<b class="error">{{ .password }}</b>{{ end }}
|
||||||
</label>
|
<input type="password" name="password" placeholder="password...">
|
||||||
<br>
|
</label>
|
||||||
<input type="submit">
|
<br>
|
||||||
</form>
|
<input type="submit">
|
||||||
|
<a href="/">Back</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{{ template "footer.html" }}
|
{{ template "footer.html" }}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
{{ template "header.html" }}
|
{{ template "header.html" . }}
|
||||||
<h1>Send photo</h1>
|
<h1>Send photo</h1>
|
||||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
<div class="form-container">
|
||||||
{{ if .message }}<p class="message">{{ .message }}</p>{{ end }}
|
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||||
{{ if .error }}<p class="errormsg">{{ .error }}</p>{{ end }}
|
{{ if .Message }}<p class="message">{{ .Message }}</p>{{ end }}
|
||||||
<label>
|
{{ if .Err }}<p class="error">{{ .Err }}</p>{{ end }}
|
||||||
Select photo: {{ if .file }}<b>{{ .file }}</b>{{ end }}
|
<label>
|
||||||
<input type="file" name="file">
|
Select photo:
|
||||||
</label>
|
<input type="file" name="file">
|
||||||
<br>
|
</label>
|
||||||
<input type="submit">
|
<br>
|
||||||
</form>
|
<input type="submit" value="Send">
|
||||||
<a href="/">Back</a>
|
<a href="/">Back</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{{ template "footer.html" }}
|
{{ template "footer.html" }}
|
||||||
|
Loading…
Reference in New Issue
Block a user