Add github.com/codegangsta/martini-contrib/auth dep

This commit is contained in:
Jakob Borg 2014-04-09 10:17:19 +02:00
parent c2f0c2225a
commit 07eb4020bd
6 changed files with 135 additions and 0 deletions

5
Godeps/Godeps.json generated
View File

@ -27,6 +27,11 @@
"Comment": "v0.1-142-g8659df7",
"Rev": "8659df7a51aebe6c6120268cd5a8b4c34fa8441a"
},
{
"ImportPath": "github.com/codegangsta/martini-contrib/auth",
"Comment": "v0.1-159-g8ce6181",
"Rev": "8ce6181c2609699e4c7cd30994b76a850a9cdadc"
},
{
"ImportPath": "github.com/golang/groupcache/lru",
"Rev": "d781998583680cda80cf61e0b37dd0cd8da2eb52"

View File

@ -0,0 +1,25 @@
# auth
Martini middleware/handler for http basic authentication.
[API Reference](http://godoc.org/github.com/codegangsta/martini-contrib/auth)
## Usage
~~~ go
import (
"github.com/codegangsta/martini"
"github.com/codegangsta/martini-contrib/auth"
)
func main() {
m := martini.Classic()
// authenticate every request
m.Use(auth.Basic("username", "secretpassword"))
m.Run()
}
~~~
## Authors
* [Jeremy Saenz](http://github.com/codegangsta)
* [Brendon Murphy](http://github.com/bemurphy)

View File

@ -0,0 +1,19 @@
package auth
import (
"encoding/base64"
"net/http"
)
// Basic returns a Handler that authenticates via Basic Auth. Writes a http.StatusUnauthorized
// if authentication fails
func Basic(username string, password string) http.HandlerFunc {
var siteAuth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
return func(res http.ResponseWriter, req *http.Request) {
auth := req.Header.Get("Authorization")
if !SecureCompare(auth, "Basic "+siteAuth) {
res.Header().Set("WWW-Authenticate", "Basic realm=\"Authorization Required\"")
http.Error(res, "Not Authorized", http.StatusUnauthorized)
}
}
}

View File

@ -0,0 +1,45 @@
package auth
import (
"encoding/base64"
"github.com/codegangsta/martini"
"net/http"
"net/http/httptest"
"testing"
)
func Test_BasicAuth(t *testing.T) {
recorder := httptest.NewRecorder()
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar"))
m := martini.New()
m.Use(Basic("foo", "bar"))
m.Use(func(res http.ResponseWriter, req *http.Request) {
res.Write([]byte("hello"))
})
r, _ := http.NewRequest("GET", "foo", nil)
m.ServeHTTP(recorder, r)
if recorder.Code != 401 {
t.Error("Response not 401")
}
if recorder.Body.String() == "hello" {
t.Error("Auth block failed")
}
recorder = httptest.NewRecorder()
r.Header.Set("Authorization", auth)
m.ServeHTTP(recorder, r)
if recorder.Code == 401 {
t.Error("Response is 401")
}
if recorder.Body.String() != "hello" {
t.Error("Auth failed, got: ", recorder.Body.String())
}
}

View File

@ -0,0 +1,15 @@
package auth
import (
"crypto/subtle"
)
// SecureCompare performs a constant time compare of two strings to limit timing attacks.
func SecureCompare(given string, actual string) bool {
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
} else {
/* Securely compare actual to itself to keep constant time, but always return false */
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
}
}

View File

@ -0,0 +1,26 @@
package auth
import (
"testing"
)
var comparetests = []struct {
a string
b string
val bool
}{
{"foo", "foo", true},
{"bar", "bar", true},
{"password", "password", true},
{"Foo", "foo", false},
{"foo", "foobar", false},
{"password", "pass", false},
}
func Test_SecureCompare(t *testing.T) {
for _, tt := range comparetests {
if SecureCompare(tt.a, tt.b) != tt.val {
t.Errorf("Expected SecureCompare(%v, %v) to return %v but did not", tt.a, tt.b, tt.val)
}
}
}