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
-
24server.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