Skip to content

Instantly share code, notes, and snippets.

@cellofellow
Created May 7, 2015 07:06
Show Gist options
  • Select an option

  • Save cellofellow/918bed2eafe2d313aefa to your computer and use it in GitHub Desktop.

Select an option

Save cellofellow/918bed2eafe2d313aefa to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"database/sql" // For sqlite
"encoding/base64"
"fmt"
"log"
"net/http"
"strings"
"github.com/gocraft/web"
// Anonymous import of sqlite3 driver for database/sql.
_ "github.com/mattn/go-sqlite3"
)
var (
db *sql.DB
)
func init() {
var err error
db, err = sql.Open("sqlite3", "db.sqlite3")
if err != nil {
log.Fatal(err)
}
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(`
CREATE TABLE IF NOT EXISTS users (
username text PRIMARY KEY,
password text
)
`)
if err != nil {
log.Fatal(err)
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
}
type RootContext struct{}
func (c *RootContext) Register(w web.ResponseWriter, r *web.Request) {
if err := r.ParseForm(); err != nil {
panic(err) // gocraft/web catches panics and sends a 500
}
username := r.Form.Get("username")
password := r.Form.Get("password")
if username == "" || password == "" {
w.WriteHeader(http.StatusBadRequest) // Error 400
fmt.Fprintln(w, "Cannot have empty username or password.")
}
_, err := db.Exec(
`INSERT INTO users (username, password) VALUES (?, ?)`,
username,
password,
)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "User %s created\n", username)
}
type UserContext struct {
*RootContext
Username string
}
func (c *UserContext) AuthenticationMiddleware(
w web.ResponseWriter,
r *web.Request,
next web.NextMiddlewareFunc) {
// Declare all variables ahead so goto works.
var (
authHeader string
decodedHeader []byte
split [][]byte
err error
exists bool
)
authHeader = r.Header.Get("Authorization")
if strings.Index(authHeader, "Basic ") != 0 {
goto unauthorized
}
decodedHeader, err = base64.StdEncoding.DecodeString(authHeader[6:])
if err != nil {
goto unauthorized
}
split = bytes.SplitN(decodedHeader, []byte{':'}, 2)
db.QueryRow(`SELECT EXISTS(
SELECT 1
FROM users
WHERE username = ? AND password = ?
LIMIT 1
)`, string(split[0]), string(split[1])).Scan(&exists)
if exists {
c.Username = string(split[0])
next(w, r)
return
}
unauthorized:
// Tell client we expect Basic auth.
w.Header().Set(
"WWW-Authenticate",
"Basic realm=go server",
)
// Set a 401 return code.
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Not authorized\n"))
// We did not call next(w, r).
// No more middleware or handlers will be called.
return
}
func (c *UserContext) MakeSomething(w web.ResponseWriter, r *web.Request) {
if err := r.ParseForm(); err != nil {
panic(err)
}
something := r.Form.Get("what")
if strings.ToLower(something) != "tea" {
w.WriteHeader(http.StatusTeapot)
fmt.Fprintf(w, "I can't make %s, I'm a teapot!\n", something)
return
}
fmt.Fprintln(w, "Making a nice pot of tea now.")
}
func main() {
router := web.New(RootContext{})
router.Post("/register", (*RootContext).Register)
authrouter := router.Subrouter(UserContext{}, "/")
authrouter.Middleware((*UserContext).AuthenticationMiddleware)
authrouter.Post("/make", (*UserContext).MakeSomething)
log.Fatal(http.ListenAndServe(":3000", router))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment