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 5f48577e2d8fa3fe451372780d5224a3d06ede7f
parent 70f5bc4a2b9a0b5e52375a580feeda5a84d7bd0e
Author: Ryan Wolf <rwolf@borderstylo.com>
Date:   Thu, 12 Apr 2012 21:26:38 -0700

Fixed 500s on some well-formed auth.

Moved basic auth to its own module.
Added unit tests for basic auth.

Diffstat:
MREADME.md | 8+++++++-
Abasicauth/basicauth.go | 29+++++++++++++++++++++++++++++
Abasicauth/basicauth_test.go | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnovelty/novelty.go | 15+++++----------
4 files changed, 101 insertions(+), 11 deletions(-)

diff --git a/README.md b/README.md @@ -1,7 +1,7 @@ Setup === -1. 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. +1. Get your [dev environment](https://developers.google.com/appengine/docs/go/gettingstarted/devenvironment) setup for GAE. 2. Clone this repository, cd into the directory. 3. ```cp app.yaml.example app.yaml``` 4. Edit app.yaml with the app name you plan to use. @@ -19,6 +19,12 @@ password for changing the answer is "bees". To change the answer to "no", simply visit larry:bees@localhost:8080/no +Running tests +=== + +1. Install go. +2. ```$ cd basicauth/ && go test``` + Running on appspot === diff --git a/basicauth/basicauth.go b/basicauth/basicauth.go @@ -0,0 +1,29 @@ +package basicauth + +import ( + "encoding/base64" + "errors" + "strings" +) + +func Decode(h string) (u string, p string, e error) { + prefix := "Basic " + if !strings.HasPrefix(h, prefix) { + e = errors.New("Bad Request") + return + } + h = h[6:] + auth, err := base64.StdEncoding.DecodeString(h) + if err != nil { + e = errors.New("Bad Request") + return + } + fields := strings.Split(string(auth), ":") + if len(fields) != 2 { + e = errors.New("Bad Request") + return + } + u = fields[0] + p = fields[1] + return +} diff --git a/basicauth/basicauth_test.go b/basicauth/basicauth_test.go @@ -0,0 +1,60 @@ +package basicauth + +import ( + "encoding/base64" + "testing" +) + +func TestDecodeEmpty(t *testing.T) { + if _, _, err := Decode(""); err == nil { + t.Error("Should fail on empty") + } +} + +func TestDecodeMissingPrefix(t *testing.T) { + if _, _, err := Decode("Bees"); err == nil { + t.Error("Should fail on missing prefix") + } +} + +func TestDecodeEmptySuffix(t *testing.T) { + if _, _, err := Decode("Basic "); err == nil { + t.Error("Should fail on empty suffix") + } +} + +func TestDecodeInvalidSuffix(t *testing.T) { + if _, _, err := Decode("Basic !*@&#@"); err == nil { + t.Error("Should fail on invalid suffix") + } +} + +func TestDecodeNoDelimiter(t *testing.T) { + a := base64.StdEncoding.EncodeToString([]byte("bees")) + if _, _, err := Decode(a); err == nil { + t.Error("Should fail on no delmiter") + } +} + +func TestDecodeTooManyDelimiters(t *testing.T) { + a := base64.StdEncoding.EncodeToString([]byte("b:e:s")) + if _, _, err := Decode(a); err == nil { + t.Error("Should fail on too many delimiters") + } +} + +func TestDecodeValidAuth(t *testing.T) { + u := "ryan" + p := "clowns" + a := base64.StdEncoding.EncodeToString([]byte(u + ":" + p)) + username, password, err := Decode("Basic " + a) + if err != nil { + t.Error("Error should be nil") + } + if username != u { + t.Errorf("Wrong username. Expected '%s' got '%s'", u, username) + } + if password != p { + t.Errorf("Wrong password. Expected '%s' got '%s'", p, password) + } +} diff --git a/novelty/novelty.go b/novelty/novelty.go @@ -3,10 +3,9 @@ package novelty import ( "appengine" "appengine/datastore" - "encoding/base64" + "basicauth" "html/template" "net/http" - "strings" ) type Answer struct { @@ -39,12 +38,8 @@ 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 { + _, password, err := basicauth.Decode(h) + if err != nil { return false } c := appengine.NewContext(r) @@ -54,14 +49,14 @@ func authorized(r *http.Request) bool { // 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], + Value: password, } if _, err := datastore.Put(c, k, &dp); err != nil { return false } return true } - return p.Value == fs[1] + return p.Value == password } func setAnswer(answer string) func(w http.ResponseWriter, r *http.Request) {