Browse Source
Merge pull request #56 from mutantmonkey/auth
Merge pull request #56 from mutantmonkey/auth
Add support for auth keys (and remote auth keys)pull/58/head
Andrei Marcu
9 years ago
6 changed files with 234 additions and 2 deletions
-
2README.md
-
128auth.go
-
24auth_test.go
-
28server.go
-
41server_test.go
-
13upload.go
@ -0,0 +1,128 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"encoding/base64" |
|||
"log" |
|||
"net/http" |
|||
"os" |
|||
"strings" |
|||
|
|||
"golang.org/x/crypto/scrypt" |
|||
) |
|||
|
|||
const ( |
|||
authPrefix = "Linx " |
|||
scryptSalt = "linx-server" |
|||
scryptN = 16384 |
|||
scryptr = 8 |
|||
scryptp = 1 |
|||
scryptKeyLen = 32 |
|||
) |
|||
|
|||
type AuthOptions struct { |
|||
AuthFile string |
|||
UnauthMethods []string |
|||
} |
|||
|
|||
type auth struct { |
|||
successHandler http.Handler |
|||
failureHandler http.Handler |
|||
authKeys []string |
|||
o AuthOptions |
|||
} |
|||
|
|||
func readAuthKeys(authFile string) []string { |
|||
var authKeys []string |
|||
|
|||
f, err := os.Open(authFile) |
|||
if err != nil { |
|||
log.Fatal("Failed to open authfile: ", err) |
|||
} |
|||
defer f.Close() |
|||
|
|||
scanner := bufio.NewScanner(f) |
|||
for scanner.Scan() { |
|||
authKeys = append(authKeys, scanner.Text()) |
|||
} |
|||
|
|||
err = scanner.Err() |
|||
if err != nil { |
|||
log.Fatal("Scanner error while reading authfile: ", err) |
|||
} |
|||
|
|||
return authKeys |
|||
} |
|||
|
|||
func checkAuth(authKeys []string, decodedAuth []byte) (result bool, err error) { |
|||
checkKey, err := scrypt.Key([]byte(decodedAuth), []byte(scryptSalt), scryptN, scryptr, scryptp, scryptKeyLen) |
|||
if err != nil { |
|||
return |
|||
} |
|||
|
|||
encodedKey := base64.StdEncoding.EncodeToString(checkKey) |
|||
for _, v := range authKeys { |
|||
if encodedKey == v { |
|||
result = true |
|||
return |
|||
} |
|||
} |
|||
|
|||
result = false |
|||
return |
|||
} |
|||
|
|||
func (a auth) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
|||
if sliceContains(a.o.UnauthMethods, r.Method) { |
|||
// allow unauthenticated methods
|
|||
a.successHandler.ServeHTTP(w, r) |
|||
return |
|||
} |
|||
|
|||
authHeader := r.Header.Get("Authorization") |
|||
if !strings.HasPrefix(authHeader, authPrefix) { |
|||
a.failureHandler.ServeHTTP(w, r) |
|||
return |
|||
} |
|||
|
|||
decodedAuth, err := base64.StdEncoding.DecodeString(authHeader[len(authPrefix):]) |
|||
if err != nil { |
|||
a.failureHandler.ServeHTTP(w, r) |
|||
return |
|||
} |
|||
|
|||
result, err := checkAuth(a.authKeys, decodedAuth) |
|||
if err != nil || !result { |
|||
a.failureHandler.ServeHTTP(w, r) |
|||
return |
|||
} |
|||
|
|||
a.successHandler.ServeHTTP(w, r) |
|||
} |
|||
|
|||
func UploadAuth(o AuthOptions) func(http.Handler) http.Handler { |
|||
fn := func(h http.Handler) http.Handler { |
|||
return auth{ |
|||
successHandler: h, |
|||
failureHandler: http.HandlerFunc(badAuthorizationHandler), |
|||
authKeys: readAuthKeys(o.AuthFile), |
|||
o: o, |
|||
} |
|||
} |
|||
return fn |
|||
} |
|||
|
|||
func badAuthorizationHandler(w http.ResponseWriter, r *http.Request) { |
|||
w.WriteHeader(http.StatusUnauthorized) |
|||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) |
|||
} |
|||
|
|||
func sliceContains(slice []string, s string) bool { |
|||
for _, v := range slice { |
|||
if s == v { |
|||
return true |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
@ -0,0 +1,24 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"testing" |
|||
) |
|||
|
|||
func TestCheckAuth(t *testing.T) { |
|||
authKeys := []string{ |
|||
"vhvZ/PT1jeTbTAJ8JdoxddqFtebSxdVb0vwPlYO+4HM=", |
|||
"vFpNprT9wbHgwAubpvRxYCCpA2FQMAK6hFqPvAGrdZo=", |
|||
} |
|||
|
|||
if r, err := checkAuth(authKeys, []byte("")); err != nil && r { |
|||
t.Fatal("Authorization passed for empty key") |
|||
} |
|||
|
|||
if r, err := checkAuth(authKeys, []byte("thisisnotvalid")); err != nil && r { |
|||
t.Fatal("Authorization passed for invalid key") |
|||
} |
|||
|
|||
if r, err := checkAuth(authKeys, []byte("haPVipRnGJ0QovA9nyqK")); err != nil && !r { |
|||
t.Fatal("Authorization failed for valid key") |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue