Implement NewFullToken for Auth.
Add a couple test stubs for this and wallet.
This commit is contained in:
parent
5d297b04bb
commit
52ed6d8d2c
6 changed files with 140 additions and 22 deletions
41
auth.go
41
auth.go
|
@ -1,6 +1,11 @@
|
||||||
package main // TODO - make it its own `auth` package later
|
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 - Learn how to use https://github.com/golang/oauth2 instead
|
||||||
// TODO - Look into jwt, etc.
|
// TODO - Look into jwt, etc.
|
||||||
|
@ -10,24 +15,24 @@ type AuthTokenString string
|
||||||
type PublicKey string
|
type PublicKey string
|
||||||
|
|
||||||
type AuthInterface interface {
|
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
|
IsValidSignature(pubKey PublicKey, payload string, signature string) bool
|
||||||
|
|
||||||
// for future request:
|
// for future request:
|
||||||
// IsDownloadKeyValid(DownloadKey) bool
|
|
||||||
// IsValidToken(AuthTokenString) bool
|
// IsValidToken(AuthTokenString) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Auth struct{}
|
type Auth struct{}
|
||||||
|
|
||||||
func (a *Auth) IsValidSignature(pubKey PublicKey, payload string, signature string) bool {
|
func (a *Auth) IsValidSignature(pubKey PublicKey, payload string, signature string) bool {
|
||||||
// TODO
|
// TODO - a real check
|
||||||
return false
|
return signature == "Good Signature"
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthToken struct {
|
type AuthToken struct {
|
||||||
Token AuthTokenString `json:"token"`
|
Token AuthTokenString `json:"token"`
|
||||||
DeviceID string `json:"deviceId"`
|
DeviceID string `json:"deviceId"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
PubKey PublicKey `json:"publicKey"`
|
PubKey PublicKey `json:"publicKey"`
|
||||||
Expiration *time.Time `json:"expiration"`
|
Expiration *time.Time `json:"expiration"`
|
||||||
}
|
}
|
||||||
|
@ -42,17 +47,19 @@ func (s *Server) validateTokenRequest(tokenRequest *TokenRequest) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Auth) NewToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) {
|
const tokenLength = 32
|
||||||
/*
|
|
||||||
TODO
|
|
||||||
|
|
||||||
authToken := auth.AuthToken(
|
func (a *Auth) NewFullToken(pubKey PublicKey, tokenRequest *TokenRequest) (*AuthToken, error) {
|
||||||
token: random(),
|
b := make([]byte, tokenLength)
|
||||||
deviceID: tokenRequest.deviceID,
|
if _, err := rand.Read(b); err != nil {
|
||||||
scope: "*", // "download" for a downloadToken
|
return nil, fmt.Errorf("Error generating token: %+v", err)
|
||||||
expiration= now() + 2 weeks,
|
}
|
||||||
pubkey?
|
|
||||||
)
|
return &AuthToken{
|
||||||
*/
|
Token: AuthTokenString(hex.EncodeToString(b)),
|
||||||
return &AuthToken{}, nil
|
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
|
||||||
}
|
}
|
||||||
|
|
23
auth_test.go
Normal file
23
auth_test.go
Normal file
|
@ -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?)")
|
||||||
|
}
|
|
@ -1,5 +1,73 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
// TODO some of the same things tested as server_test, except without any of the mocking
|
import (
|
||||||
// probably should do the httptest.NewServer thing
|
"bytes"
|
||||||
// https://medium.com/what-i-talk-about-when-i-talk-about-technology/go-code-examples-httptest-newserver-f965fb349884
|
"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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -147,7 +147,7 @@ func (s *Server) getAuthToken(w http.ResponseWriter, req *http.Request) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
authToken, err := s.auth.NewToken(authRequest.PubKey, &tokenRequest)
|
authToken, err := s.auth.NewFullToken(authRequest.PubKey, &tokenRequest)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(w, http.StatusInternalServerError, "Error generating auth token")
|
errorJSON(w, http.StatusInternalServerError, "Error generating auth token")
|
||||||
|
|
|
@ -26,7 +26,7 @@ type TestAuth struct {
|
||||||
FailGenToken bool
|
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 {
|
if a.FailGenToken {
|
||||||
return nil, fmt.Errorf("Test error: fail to generate token")
|
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
|
// also add a basic test case for this in TestAuthHandlerErrors to make sure it's called at all
|
||||||
t.Fatalf("Implement and test validateTokenRequest")
|
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)")
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ func TestStoreInsertToken(t *testing.T) {
|
||||||
authToken1 := AuthToken{
|
authToken1 := AuthToken{
|
||||||
Token: "seekrit-1",
|
Token: "seekrit-1",
|
||||||
DeviceID: "dID",
|
DeviceID: "dID",
|
||||||
|
Scope: "*",
|
||||||
PubKey: "pubKey",
|
PubKey: "pubKey",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +100,7 @@ func TestStoreUpdateToken(t *testing.T) {
|
||||||
authToken1 := AuthToken{
|
authToken1 := AuthToken{
|
||||||
Token: "seekrit-1",
|
Token: "seekrit-1",
|
||||||
DeviceID: "dID",
|
DeviceID: "dID",
|
||||||
|
Scope: "*",
|
||||||
PubKey: "pubKey",
|
PubKey: "pubKey",
|
||||||
}
|
}
|
||||||
authToken2 := authToken1
|
authToken2 := authToken1
|
||||||
|
@ -160,6 +162,7 @@ func TestStoreSaveToken(t *testing.T) {
|
||||||
authToken_d1_1 := AuthToken{
|
authToken_d1_1 := AuthToken{
|
||||||
Token: "seekrit-d1-1",
|
Token: "seekrit-d1-1",
|
||||||
DeviceID: "dID-1",
|
DeviceID: "dID-1",
|
||||||
|
Scope: "*",
|
||||||
PubKey: "pubKey",
|
PubKey: "pubKey",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +271,7 @@ func TestStoreGetToken(t *testing.T) {
|
||||||
authToken := AuthToken{
|
authToken := AuthToken{
|
||||||
Token: "seekrit-d1",
|
Token: "seekrit-d1",
|
||||||
DeviceID: "dID",
|
DeviceID: "dID",
|
||||||
|
Scope: "*",
|
||||||
PubKey: "pubKey",
|
PubKey: "pubKey",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue