2
0
mirror of https://github.com/offen/website.git synced 2024-11-23 01:20:29 +01:00

further define cookie specifics, break auditorium into testable pieces

This commit is contained in:
Frederik Ring 2019-07-08 15:11:06 +02:00
parent 7f77d693ed
commit 7a2f47cbcc
4 changed files with 78 additions and 12 deletions

View File

@ -44,6 +44,7 @@ def post_login():
expires=expiry, expires=expiry,
path="/", path="/",
domain=environ.get("COOKIE_DOMAIN"), domain=environ.get("COOKIE_DOMAIN"),
samesite="strict"
) )
return resp return resp

View File

@ -33,6 +33,10 @@ custom:
production: accounts.offen.dev production: accounts.offen.dev
staging: accounts-staging.offen.dev staging: accounts-staging.offen.dev
alpha: accounts-alpha.offen.dev alpha: accounts-alpha.offen.dev
cookieDomain:
production: .offen.dev
staging: .offen.dev
alpha: .offen.dev
customDomain: customDomain:
basePath: '' basePath: ''
certificateName: '*.offen.dev' certificateName: '*.offen.dev'
@ -64,6 +68,7 @@ functions:
environment: environment:
USER: offen USER: offen
CORS_ORIGIN: https://${self:custom.origin.${self:custom.stage}} CORS_ORIGIN: https://${self:custom.origin.${self:custom.stage}}
COOKIE_DOMAIN: ${self:custom.origin.${self:custom.stage}}
JWT_PRIVATE_KEY: '${ssm:/aws/reference/secretsmanager/${self:custom.stage}/accounts/jwtPrivateKey~true}' JWT_PRIVATE_KEY: '${ssm:/aws/reference/secretsmanager/${self:custom.stage}/accounts/jwtPrivateKey~true}'
JWT_PUBLIC_KEY: '${ssm:/aws/reference/secretsmanager/${self:custom.stage}/accounts/jwtPublicKey~true}' JWT_PUBLIC_KEY: '${ssm:/aws/reference/secretsmanager/${self:custom.stage}/accounts/jwtPublicKey~true}'
HASHED_PASSWORD: ${ssm:/aws/reference/secretsmanager/${self:custom.stage}/accounts/hashedPassword~true} HASHED_PASSWORD: ${ssm:/aws/reference/secretsmanager/${self:custom.stage}/accounts/hashedPassword~true}

View File

@ -14,10 +14,6 @@ import (
"github.com/lestrrat-go/jwx/jwt" "github.com/lestrrat-go/jwx/jwt"
) )
type keyResponse struct {
Key string `json:"key"`
}
type contextKey string type contextKey string
// ClaimsContextKey will be used to attach a JWT claim to a request context // ClaimsContextKey will be used to attach a JWT claim to a request context
@ -34,27 +30,25 @@ func JWTProtect(keyURL, cookieName string) func(http.Handler) http.Handler {
RespondWithJSONError(w, err, http.StatusForbidden) RespondWithJSONError(w, err, http.StatusForbidden)
return return
} }
keyRes, keyErr := http.Get(keyURL)
keyRes, keyErr := fetchKey(keyURL)
if keyErr != nil { if keyErr != nil {
RespondWithJSONError(w, keyErr, http.StatusInternalServerError) RespondWithJSONError(w, keyErr, http.StatusInternalServerError)
return return
} }
defer keyRes.Body.Close()
payload := keyResponse{} keyBytes, _ := pem.Decode([]byte(keyRes))
if err := json.NewDecoder(keyRes.Body).Decode(&payload); err != nil {
RespondWithJSONError(w, keyErr, http.StatusBadGateway)
return
}
keyBytes, _ := pem.Decode([]byte(payload.Key))
if keyBytes == nil { if keyBytes == nil {
RespondWithJSONError(w, errors.New("no pem block found"), http.StatusInternalServerError) RespondWithJSONError(w, errors.New("no pem block found"), http.StatusInternalServerError)
return return
} }
parseResult, parseErr := x509.ParsePKIXPublicKey(keyBytes.Bytes) parseResult, parseErr := x509.ParsePKIXPublicKey(keyBytes.Bytes)
if parseErr != nil { if parseErr != nil {
RespondWithJSONError(w, parseErr, http.StatusBadGateway) RespondWithJSONError(w, parseErr, http.StatusBadGateway)
return return
} }
pubKey, pubKeyOk := parseResult.(*rsa.PublicKey) pubKey, pubKeyOk := parseResult.(*rsa.PublicKey)
if !pubKeyOk { if !pubKeyOk {
RespondWithJSONError(w, errors.New("unable to use given key"), http.StatusInternalServerError) RespondWithJSONError(w, errors.New("unable to use given key"), http.StatusInternalServerError)
@ -79,3 +73,20 @@ func JWTProtect(keyURL, cookieName string) func(http.Handler) http.Handler {
}) })
} }
} }
type keyResponse struct {
Key string `json:"key"`
}
func fetchKey(keyURL string) ([]byte, error) {
fetchRes, fetchErr := http.Get(keyURL)
if fetchErr != nil {
return nil, fetchErr
}
defer fetchRes.Body.Close()
payload := keyResponse{}
if err := json.NewDecoder(fetchRes.Body).Decode(&payload); err != nil {
return nil, err
}
return []byte(payload.Key), nil
}

49
shared/http/jwt_test.go Normal file
View File

@ -0,0 +1,49 @@
package http
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestJWTProtect(t *testing.T) {
tests := []struct {
name string
cookie *http.Cookie
keyURL string
expectedStatusCode int
}{
{
"no cookie",
nil,
"http://localhost:9999",
http.StatusForbidden,
},
{
"bad url",
&http.Cookie{
Name: "auth",
Value: "irrelevantgibberish",
},
"http://localhost:9999",
http.StatusInternalServerError,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
wrappedHandler := JWTProtect("http://localhost:9999", "auth")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}))
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/", nil)
if test.cookie != nil {
r.AddCookie(test.cookie)
}
wrappedHandler.ServeHTTP(w, r)
if w.Code != test.expectedStatusCode {
t.Errorf("Unexpected status code %v", w.Code)
}
})
}
}