commit 28c429bcefdb36b06dbe7f7f0c1d90fc292c09e6
parent 3859231e17c68dbe8084054fd9bd1d6bc808acbd
Author: Ryan Wolf <rwolf@borderstylo.com>
Date: Tue, 10 Apr 2012 23:12:03 -0700
Added basic auth, todo.
Diffstat:
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{