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:
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) {