novelty.go

Single word (yes/no) site for app engine in go
git clone https://wehaveforgeathome.hates.computer/novelty.go.git
Log | Files | Refs | LICENSE

commit 28c429bcefdb36b06dbe7f7f0c1d90fc292c09e6
parent 3859231e17c68dbe8084054fd9bd1d6bc808acbd
Author: Ryan Wolf <rwolf@borderstylo.com>
Date:   Tue, 10 Apr 2012 23:12:03 -0700

Added basic auth, todo.

Diffstat:
AREADME | 29+++++++++++++++++++++++++++++
MTODO | 7+------
Mnovelty/novelty.go | 38++++++++++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/README b/README @@ -0,0 +1,29 @@ +Setup +=== + +# Get your [dev environment](https://developers.google.com/appengine/docs/go/gettingstarted/devenvironment) setup for GAE. It comes with a version of Go, so don't worry about compiling/installing that. +# Clone this repository, cd into the directory. +# $ cp app.yaml.example app.yaml +# Edit app.yaml with the app name you plan to use. + +Running locally +=== + +# Start the dev server: $ dev_appserver.py . +# Seed the server with starting data: $ curl whatever:bees@localhost:8080/yes +# Visit localhost:8080 in your browser. + +Your novelty server is ready to go. The answer is current set to "yes", and the +password for changing the answer is "bees". + +To change the answer to "no", simply visit larry:bees@localhost:8080/no + +Running on appspot +=== + +# Follow the [registration instuctions](https://developers.google.com/appengine/docs/go/gettingstarted/uploading) for GAE. +# Make sure that the app id in app.yaml matches you new app id. +# Push the app: $ appcfg.py . +# Seed the server with starting data. I'd suggest a different password than +"bees": $ curl moe:$PASSWORD@$APPID.appspot.com/yes +# Visit $APPID.appspot.com in your browser to behold your new novelty server. diff --git a/TODO b/TODO @@ -1,8 +1,3 @@ -* readme -* use post instead of get for editing -* basic auth for editing -* backend replication to survive instance restarts -* styling for index.template * make sure we're sending right content types * html error pages -* env variables for question, basic auth header +* salt password? diff --git a/novelty/novelty.go b/novelty/novelty.go @@ -3,14 +3,20 @@ package novelty import ( "appengine" "appengine/datastore" + "encoding/base64" "html/template" "net/http" + "strings" ) type Answer struct { Value string } +type Password struct { + Value string +} + func init() { http.HandleFunc("/", getAnswer) http.HandleFunc("/yes", setAnswer("yes")) @@ -31,8 +37,40 @@ func getAnswer(w http.ResponseWriter, r *http.Request) { } } +func authorized(r *http.Request) bool { + h := r.Header.Get("Authorization") + if !strings.HasPrefix(h, "Basic ") { + return false + } + a, _ := base64.StdEncoding.DecodeString(strings.TrimLeft(h, "Basic ")) + fs := strings.Split(string(a), ":") + if len(fs) != 2 { + return false + } + c := appengine.NewContext(r) + k := datastore.NewKey(c, "Password", "password", 0, nil) + p := new(Password) + if err := datastore.Get(c, k, p); err != nil { + // If password is not set, seed with whatever password was passed in. + // See: http://golang.org/misc/dashboard/app/build/key.go + dp := Password{ + Value: fs[1], + } + if _, err := datastore.Put(c, k, &dp); err != nil { + return false + } + return true + } + return p.Value == fs[1] +} + func setAnswer(answer string) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { + if !authorized(r) { + w.Header().Set("WWW-Authenticate", "Basic realm=\"novelty.go\"") + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } c := appengine.NewContext(r) k := datastore.NewKey(c, "Answer", "answer", 0, nil) a := Answer{