382 lines
11 KiB
Go
382 lines
11 KiB
Go
package handler
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"git.infra.nkode.tech/dkelly/nkode-core/api"
|
|
"git.infra.nkode.tech/dkelly/nkode-core/email"
|
|
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
|
"git.infra.nkode.tech/dkelly/nkode-core/models"
|
|
"git.infra.nkode.tech/dkelly/nkode-core/repository"
|
|
"git.infra.nkode.tech/dkelly/nkode-core/security"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
func TestNKodeAPI(t *testing.T) {
|
|
tr := NewTestRouter()
|
|
tr.Start()
|
|
defer func(tr *TestRouter) {
|
|
err := tr.Stop()
|
|
assert.NoError(t, err)
|
|
}(tr)
|
|
// *** Create New Customer ***
|
|
customerID, status, err := tr.CreateNewCustomerDefaultPolicy()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
attrPerKey := 9
|
|
numKeys := 6
|
|
userEmail := "test_username" + security.GenerateRandomString(12) + "@example.com"
|
|
|
|
// *** Signup ***
|
|
resp, status, err := tr.Signup(customerID, attrPerKey, numKeys, userEmail)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
|
|
passcodeLen := 4
|
|
userPasscode := resp.UserIdxInterface[:passcodeLen]
|
|
kpSet := entities.KeypadDimension{
|
|
AttrsPerKey: numKeys,
|
|
NumbOfKeys: numKeys,
|
|
}
|
|
setKeySelection, err := entities.SelectKeyByAttrIdx(resp.UserIdxInterface, userPasscode, kpSet)
|
|
assert.NoError(t, err)
|
|
|
|
// *** Set nKode ***
|
|
confirmInterface, status, err := tr.SetNKode(customerID, setKeySelection, resp.SessionId)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
|
|
confirmKeySelection, err := entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet)
|
|
assert.NoError(t, err)
|
|
|
|
// *** Confirm nKode ***
|
|
status, err = tr.ConfirmNKode(customerID, confirmKeySelection, resp.SessionId)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
|
|
// *** Get Login Interface ***
|
|
loginInterface, status, err := tr.GetLoginInterface(userEmail, customerID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
kp := entities.KeypadDimension{
|
|
AttrsPerKey: attrPerKey,
|
|
NumbOfKeys: numKeys,
|
|
}
|
|
loginKeySelection, err := entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp)
|
|
assert.NoError(t, err)
|
|
|
|
// *** Login ***
|
|
tokens, status, err := tr.Login(customerID, userEmail, loginKeySelection)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
assert.NotEmpty(t, tokens.AccessToken)
|
|
assert.NotEmpty(t, tokens.RefreshToken)
|
|
|
|
// *** Renew Attributes ***
|
|
status, err = tr.RenewAttributes(customerID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
loginInterface, status, err = tr.GetLoginInterface(userEmail, customerID)
|
|
assert.NoError(t, err)
|
|
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp)
|
|
assert.NoError(t, err)
|
|
tokens, status, err = tr.Login(customerID, userEmail, loginKeySelection)
|
|
assert.NoError(t, err)
|
|
|
|
// *** Test Forgot nKode ***
|
|
status, err = tr.ForgotNKode(customerID, userEmail)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
|
|
// *** Test Reset nKode ***
|
|
nkodeResetJwt, err := security.ResetNKodeToken(userEmail, customerID)
|
|
assert.NoError(t, err)
|
|
resetResp, status, err := tr.Reset(customerID, attrPerKey, numKeys, userEmail, nkodeResetJwt)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
assert.NotEmpty(t, resetResp.SessionId)
|
|
userPasscode = resetResp.UserIdxInterface[:passcodeLen]
|
|
setKeySelection, err = entities.SelectKeyByAttrIdx(resetResp.UserIdxInterface, userPasscode, kpSet)
|
|
assert.NoError(t, err)
|
|
confirmInterface, status, err = tr.SetNKode(customerID, setKeySelection, resetResp.SessionId)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
confirmKeySelection, err = entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet)
|
|
assert.NoError(t, err)
|
|
status, err = tr.ConfirmNKode(customerID, confirmKeySelection, resetResp.SessionId)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
loginInterface, status, err = tr.GetLoginInterface(userEmail, customerID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp)
|
|
assert.NoError(t, err)
|
|
tokens, status, err = tr.Login(customerID, userEmail, loginKeySelection)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 200, status)
|
|
assert.NotEmpty(t, tokens.AccessToken)
|
|
assert.NotEmpty(t, tokens.RefreshToken)
|
|
|
|
// *** Test Reset nKode with invalid token ***
|
|
_, status, err = tr.Reset(customerID, attrPerKey, numKeys, userEmail, "invalid token")
|
|
assert.Error(t, err)
|
|
assert.Equal(t, 403, status)
|
|
}
|
|
|
|
type TestRouter struct {
|
|
Router *gin.Engine
|
|
EmailQueue *email.Queue
|
|
Repo *repository.SqliteRepository
|
|
Handler *NkodeHandler
|
|
}
|
|
|
|
func NewTestRouter() *TestRouter {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.Default()
|
|
logger := log.Default()
|
|
ctx := context.Background()
|
|
dbPath := os.Getenv("TEST_DB")
|
|
repo, err := repository.NewSqliteRepository(ctx, dbPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
emailClient := email.TestEmailClient{}
|
|
emailQueue := email.NewEmailQueue(email.EmailQueueBufferSize, email.MaxEmailsPerSecond, &emailClient)
|
|
nkodeAPI := api.NewNKodeAPI(repo, emailQueue)
|
|
h := NkodeHandler{
|
|
API: nkodeAPI,
|
|
Logger: logger,
|
|
}
|
|
h.RegisterRoutes(router)
|
|
return &TestRouter{
|
|
Handler: &h,
|
|
Router: router,
|
|
EmailQueue: emailQueue,
|
|
Repo: repo,
|
|
}
|
|
}
|
|
|
|
func (r *TestRouter) Start() {
|
|
r.Repo.Start()
|
|
r.EmailQueue.Start()
|
|
}
|
|
|
|
func (r *TestRouter) Stop() error {
|
|
r.EmailQueue.Stop()
|
|
return r.Repo.Stop()
|
|
}
|
|
|
|
func (r *TestRouter) CreateNewCustomerDefaultPolicy() (string, int, error) {
|
|
p := entities.NewDefaultNKodePolicy()
|
|
body := bytes.NewBufferString(fmt.Sprintf(
|
|
"max_nkode_len=%d&min_nkode_len=%d&distinct_sets=%d&distinct_attributes=%d&lock_out=%d&expiration=%d",
|
|
p.MaxNkodeLen,
|
|
p.MinNkodeLen,
|
|
p.DistinctSets,
|
|
p.DistinctAttributes,
|
|
p.LockOut,
|
|
p.Expiration,
|
|
))
|
|
req := httptest.NewRequest(http.MethodPost, "/create-new-customer", body)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
var resp struct {
|
|
CustomerID string `json:"customer_id"`
|
|
}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
return "", rec.Code, err
|
|
}
|
|
return resp.CustomerID, rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) Signup(
|
|
customerID string,
|
|
attrsPerKey int,
|
|
numberOfKeys int,
|
|
userEmail string,
|
|
) (*entities.SignupResetInterface, int, error) {
|
|
body := bytes.NewBufferString(fmt.Sprintf(
|
|
"customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s",
|
|
customerID,
|
|
attrsPerKey,
|
|
numberOfKeys,
|
|
userEmail,
|
|
))
|
|
req := httptest.NewRequest(http.MethodPost, "/signup", body)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
var resp entities.SignupResetInterface
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
return nil, rec.Code, err
|
|
}
|
|
return &resp, rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) SetNKode(
|
|
customerID string,
|
|
selection []int,
|
|
sessionID string,
|
|
) ([]int, int, error) {
|
|
data := models.SetNKodePost{
|
|
CustomerId: customerID,
|
|
KeySelection: selection,
|
|
SessionId: sessionID,
|
|
}
|
|
|
|
body, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
req := httptest.NewRequest(http.MethodPost, "/set-nkode", bytes.NewBuffer(body))
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
var resp struct {
|
|
UserInterface []int `json:"user_interface"`
|
|
}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
return nil, rec.Code, err
|
|
}
|
|
return resp.UserInterface, rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) ConfirmNKode(
|
|
customerID string,
|
|
selection entities.KeySelection,
|
|
sessionID string,
|
|
) (int, error) {
|
|
data := models.ConfirmNKodePost{
|
|
CustomerId: customerID,
|
|
KeySelection: selection,
|
|
SessionId: sessionID,
|
|
}
|
|
body, err := json.Marshal(data)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
req := httptest.NewRequest(http.MethodPost, "/confirm-nkode", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
return rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) GetLoginInterface(
|
|
userEmail string,
|
|
customerID string,
|
|
) (entities.LoginInterface, int, error) {
|
|
body := bytes.NewBufferString(fmt.Sprintf(
|
|
"email=%s&customer_id=%s",
|
|
userEmail,
|
|
customerID,
|
|
))
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/get-login-interface", body)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
var resp entities.LoginInterface
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
return entities.LoginInterface{}, rec.Code, err
|
|
}
|
|
return resp, rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) Login(
|
|
customerID string,
|
|
userEmail string,
|
|
selection []int,
|
|
) (security.AuthenticationTokens, int, error) {
|
|
data := models.LoginPost{
|
|
CustomerId: customerID,
|
|
UserEmail: userEmail,
|
|
KeySelection: selection,
|
|
}
|
|
body, err := json.Marshal(data)
|
|
if err != nil {
|
|
return security.AuthenticationTokens{}, 0, err
|
|
}
|
|
req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
var resp security.AuthenticationTokens
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
return security.AuthenticationTokens{}, rec.Code, err
|
|
}
|
|
return resp, rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) RenewAttributes(
|
|
customerID string,
|
|
) (int, error) {
|
|
data := models.RenewAttributesPost{
|
|
CustomerId: customerID,
|
|
}
|
|
body, err := json.Marshal(data)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
req := httptest.NewRequest(http.MethodPost, "/renew-attributes", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
return rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) ForgotNKode(
|
|
customerID string,
|
|
userEmail string,
|
|
) (int, error) {
|
|
data := models.ForgotNKodePost{
|
|
CustomerId: customerID,
|
|
UserEmail: userEmail,
|
|
}
|
|
body, err := json.Marshal(data)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
req := httptest.NewRequest(http.MethodPost, "/forgot-nkode", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
return rec.Code, nil
|
|
}
|
|
|
|
func (r *TestRouter) Reset(
|
|
customerID string,
|
|
attrsPerKey int,
|
|
numberOfKeys int,
|
|
userEmail string,
|
|
resetAuthToken string,
|
|
) (*entities.SignupResetInterface, int, error) {
|
|
body := bytes.NewBufferString(fmt.Sprintf(
|
|
"customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s",
|
|
customerID,
|
|
attrsPerKey,
|
|
numberOfKeys,
|
|
userEmail,
|
|
))
|
|
req := httptest.NewRequest(http.MethodPost, "/reset-nkode", body)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.Header.Set("Authorization", "Bearer "+resetAuthToken)
|
|
rec := httptest.NewRecorder()
|
|
r.Router.ServeHTTP(rec, req)
|
|
var resp entities.SignupResetInterface
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
return nil, rec.Code, err
|
|
}
|
|
return &resp, rec.Code, nil
|
|
}
|