From 52ed6d8d2c893d0c3566c2581e1bb2df1646e9f3 Mon Sep 17 00:00:00 2001 From: Daniel Krol Date: Thu, 23 Dec 2021 22:29:02 -0500 Subject: [PATCH] Implement NewFullToken for Auth. Add a couple test stubs for this and wallet. --- auth.go | 41 ++++++++++++++----------- auth_test.go | 23 ++++++++++++++ integration_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++-- server.go | 2 +- server_test.go | 18 ++++++++++- store_test.go | 4 +++ 6 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 auth_test.go diff --git a/auth.go b/auth.go index 1ffc925..668a8d1 100644 --- a/auth.go +++ b/auth.go @@ -1,6 +1,11 @@ package main // TODO - make it its own `auth` package later -import "time" +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "time" +) // TODO - Learn how to use https://github.com/golang/oauth2 instead // TODO - Look into jwt, etc. @@ -10,24 +15,24 @@ type AuthTokenString string type PublicKey string type AuthInterface interface { - NewToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) + NewFullToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) IsValidSignature(pubKey PublicKey, payload string, signature string) bool // for future request: - // IsDownloadKeyValid(DownloadKey) bool // IsValidToken(AuthTokenString) bool } type Auth struct{} func (a *Auth) IsValidSignature(pubKey PublicKey, payload string, signature string) bool { - // TODO - return false + // TODO - a real check + return signature == "Good Signature" } type AuthToken struct { Token AuthTokenString `json:"token"` DeviceID string `json:"deviceId"` + Scope string `json:"scope"` PubKey PublicKey `json:"publicKey"` Expiration *time.Time `json:"expiration"` } @@ -42,17 +47,19 @@ func (s *Server) validateTokenRequest(tokenRequest *TokenRequest) bool { return true } -func (a *Auth) NewToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) { - /* - TODO +const tokenLength = 32 - authToken := auth.AuthToken( - token: random(), - deviceID: tokenRequest.deviceID, - scope: "*", // "download" for a downloadToken - expiration= now() + 2 weeks, - pubkey? - ) - */ - return &AuthToken{}, nil +func (a *Auth) NewFullToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) { + b := make([]byte, tokenLength) + if _, err := rand.Read(b); err != nil { + return nil, fmt.Errorf("Error generating token: %+v", err) + } + + return &AuthToken{ + Token: AuthTokenString(hex.EncodeToString(b)), + DeviceID: tokenRequest.DeviceID, + Scope: "*", + PubKey: pubKey, + // TODO add Expiration here instead of putting it in store.go. and thus redo store.go. d'oh. + }, nil } diff --git a/auth_test.go b/auth_test.go new file mode 100644 index 0000000..b9806ce --- /dev/null +++ b/auth_test.go @@ -0,0 +1,23 @@ +package main + +import ( + "testing" +) + +// Test stubs for now + +func TestAuthSignaturePass(t *testing.T) { + t.Fatalf("Valid siganture passes") +} + +func TestAuthSignatureFail(t *testing.T) { + t.Fatalf("Valid siganture fails") +} + +func TestAuthNewFullTokenSuccess(t *testing.T) { + t.Fatalf("New token passes") +} + +func TestAuthNewFullTokenFail(t *testing.T) { + t.Fatalf("New token fails (error generating random string? others?)") +} diff --git a/integration_test.go b/integration_test.go index 925d6af..2275976 100644 --- a/integration_test.go +++ b/integration_test.go @@ -1,5 +1,73 @@ package main -// TODO some of the same things tested as server_test, except without any of the mocking -// probably should do the httptest.NewServer thing -// https://medium.com/what-i-talk-about-when-i-talk-about-technology/go-code-examples-httptest-newserver-f965fb349884 +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" +) + +// TODO - test some unhappy paths? Don't want to retest all the unit tests though. + +func checkStatusCode(t *testing.T, statusCode int) { + if want, got := http.StatusOK, statusCode; want != got { + t.Errorf("StatusCode: expected %s (%d), got %s (%d)", http.StatusText(want), want, http.StatusText(got), got) + } +} + +func request(t *testing.T, method string, handler func(http.ResponseWriter, *http.Request), path string, jsonResult interface{}, requestBody string) ([]byte, int) { + req := httptest.NewRequest( + method, + path, + bytes.NewBuffer([]byte(requestBody)), + ) + w := httptest.NewRecorder() + + handler(w, req) + responseBody, _ := ioutil.ReadAll(w.Body) + + err := json.Unmarshal(responseBody, &jsonResult) + if err != nil { + t.Errorf("Error unmarshalling response body err: %+v body: %s", err, responseBody) + } + + return responseBody, w.Result().StatusCode +} + +func TestIntegrationFlow(t *testing.T) { + store, tmpFile := storeTestInit(t) + defer storeTestCleanup(tmpFile) + + s := Server{ + &Auth{}, + &store, + } + + var authToken AuthToken + responseBody, statusCode := request( + t, + http.MethodPost, + s.getAuthToken, + PathGetAuthToken, + &authToken, + `{ + "tokenRequestJSON": "{\"deviceID\": \"devID\"}", + "publickey": "testPubKey", + "signature": "Good Signature" + }`, + ) + + checkStatusCode(t, statusCode) + + // result.Token is in hex, tokenLength is bytes in the original + expectedTokenLength := tokenLength * 2 + if len(authToken.Token) != expectedTokenLength { + t.Errorf("Expected auth response to contain token length 32: result: %+v", string(responseBody)) + } + if authToken.DeviceID != "devID" { + t.Errorf("Unexpected auth response DeviceID. want: %+v got: %+v", "devID", authToken.DeviceID) + } + +} diff --git a/server.go b/server.go index 6d1176d..9ef5ab0 100644 --- a/server.go +++ b/server.go @@ -147,7 +147,7 @@ func (s *Server) getAuthToken(w http.ResponseWriter, req *http.Request) { // TODO } - authToken, err := s.auth.NewToken(authRequest.PubKey, &tokenRequest) + authToken, err := s.auth.NewFullToken(authRequest.PubKey, &tokenRequest) if err != nil { errorJSON(w, http.StatusInternalServerError, "Error generating auth token") diff --git a/server_test.go b/server_test.go index c612766..30843ad 100644 --- a/server_test.go +++ b/server_test.go @@ -26,7 +26,7 @@ type TestAuth struct { FailGenToken bool } -func (a *TestAuth) NewToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) { +func (a *TestAuth) NewFullToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) { if a.FailGenToken { return nil, fmt.Errorf("Test error: fail to generate token") } @@ -204,3 +204,19 @@ func TestServerValidateTokenRequest(t *testing.T) { // also add a basic test case for this in TestAuthHandlerErrors to make sure it's called at all t.Fatalf("Implement and test validateTokenRequest") } + +func TestServerGetWalletSuccess(t *testing.T) { + t.Fatalf("GetWallet succeeds") +} + +func TestServerGetWalletErrors(t *testing.T) { + t.Fatalf("GetWallet fails for various reasons (malformed, auth, db fail)") +} + +func TestServerPutWalletSuccess(t *testing.T) { + t.Fatalf("GetWallet succeeds") +} + +func TestServerPutWalletErrors(t *testing.T) { + t.Fatalf("GetWallet fails for various reasons (malformed, auth, db fail)") +} diff --git a/store_test.go b/store_test.go index 40761bd..b0bb29d 100644 --- a/store_test.go +++ b/store_test.go @@ -43,6 +43,7 @@ func TestStoreInsertToken(t *testing.T) { authToken1 := AuthToken{ Token: "seekrit-1", DeviceID: "dID", + Scope: "*", PubKey: "pubKey", } @@ -99,6 +100,7 @@ func TestStoreUpdateToken(t *testing.T) { authToken1 := AuthToken{ Token: "seekrit-1", DeviceID: "dID", + Scope: "*", PubKey: "pubKey", } authToken2 := authToken1 @@ -160,6 +162,7 @@ func TestStoreSaveToken(t *testing.T) { authToken_d1_1 := AuthToken{ Token: "seekrit-d1-1", DeviceID: "dID-1", + Scope: "*", PubKey: "pubKey", } @@ -268,6 +271,7 @@ func TestStoreGetToken(t *testing.T) { authToken := AuthToken{ Token: "seekrit-d1", DeviceID: "dID", + Scope: "*", PubKey: "pubKey", }