Browse Source
add support for auth keys
add support for auth keys
Add a middleware that requires authorization for all POST, PUT, and DELETE requests. This is done using the Authorization header and the provided auth key is then checked against a file containing scrypted auth keys. These keys are salted the constant string `linx-server`.pull/56/head
mutantmonkey
9 years ago
4 changed files with 150 additions and 2 deletions
-
1README.md
-
111auth.go
-
16server.go
-
20server_test.go
@ -0,0 +1,111 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"encoding/base64" |
||||
|
"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 uploadBasicAuth struct { |
||||
|
successHandler http.Handler |
||||
|
failureHandler http.Handler |
||||
|
o AuthOptions |
||||
|
} |
||||
|
|
||||
|
func checkAuth(authFile string, decodedAuth []byte) (result bool, err error) { |
||||
|
f, err := os.Open(authFile) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
checkKey, err := scrypt.Key([]byte(decodedAuth), []byte(scryptSalt), scryptN, scryptr, scryptp, scryptKeyLen) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
encodedKey := base64.StdEncoding.EncodeToString(checkKey) |
||||
|
|
||||
|
scanner := bufio.NewScanner(bufio.NewReader(f)) |
||||
|
for scanner.Scan() { |
||||
|
if encodedKey == scanner.Text() { |
||||
|
result = true |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result = false |
||||
|
err = scanner.Err() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (a uploadBasicAuth) 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.o.AuthFile, 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 uploadBasicAuth{ |
||||
|
successHandler: h, |
||||
|
failureHandler: http.HandlerFunc(badAuthorizationHandler), |
||||
|
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 |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue