From 9ee27f14cfa6ad9679f7920e26a39dc19a923290 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 31 Jan 2025 10:26:30 -0600 Subject: [PATCH] implement and test forget and reset nkode --- api/nkode_api.go | 2 +- handler/handler.go | 6 ++- handler/handler_test.go | 105 +++++++++++++++++++++++++++++++++--- models/models.go | 6 +-- security/jwt_claims.go | 4 +- security/jwt_claims_test.go | 2 +- sqlc/models.go | 6 +++ 7 files changed, 114 insertions(+), 17 deletions(-) diff --git a/api/nkode_api.go b/api/nkode_api.go index fd538fc..114bf9e 100644 --- a/api/nkode_api.go +++ b/api/nkode_api.go @@ -258,7 +258,7 @@ func (n *NKodeAPI) ForgotNKode(userEmail entities.UserEmail, customerId entities return nil } - nkodeResetJwt, err := security.ResetNKodeToken(string(userEmail), uuid.UUID(customerId)) + nkodeResetJwt, err := security.ResetNKodeToken(string(userEmail), uuid.UUID(customerId).String()) if err != nil { return err } diff --git a/handler/handler.go b/handler/handler.go index 3826fd0..fc8bb53 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -39,11 +39,13 @@ func (h *NkodeHandler) RegisterRoutes(r *gin.Engine) { r.POST("/refresh-token", h.RefreshTokenHandler) r.POST("/forgot-nkode", h.ForgotNKodeHandler) r.POST("/signout", h.SignoutHandler) - r.POST("/reset", h.ResetHandler) + r.POST("/reset-nkode", h.ResetHandler) } } func (h *NkodeHandler) CreateNewCustomerHandler(c *gin.Context) { + h.Logger.Println("create new customer") + var newCustomerPost entities.NKodePolicy if err := c.ShouldBind(&newCustomerPost); err != nil { handleError(c, err) @@ -217,7 +219,7 @@ func (h *NkodeHandler) RenewAttributesHandler(c *gin.Context) { return } - if err := h.API.RenewAttributes(entities.CustomerId(customerId)); err != nil { + if err = h.API.RenewAttributes(entities.CustomerId(customerId)); err != nil { handleError(c, err) return } diff --git a/handler/handler_test.go b/handler/handler_test.go index 60b20fe..9a7a25c 100644 --- a/handler/handler_test.go +++ b/handler/handler_test.go @@ -20,7 +20,7 @@ import ( "testing" ) -func TestNkodeAPI(t *testing.T) { +func TestNKodeAPI(t *testing.T) { tr := NewTestRouter() tr.Start() defer func(tr *TestRouter) { @@ -34,10 +34,9 @@ func TestNkodeAPI(t *testing.T) { attrPerKey := 9 numKeys := 6 userEmail := "test_username" + security.GenerateRandomString(12) + "@example.com" - reset := false // *** Signup *** - resp, status, err := tr.Signup(customerID, attrPerKey, numKeys, userEmail, reset) + resp, status, err := tr.Signup(customerID, attrPerKey, numKeys, userEmail) assert.NoError(t, err) assert.Equal(t, 200, status) @@ -82,7 +81,54 @@ func TestNkodeAPI(t *testing.T) { 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 { @@ -102,8 +148,8 @@ func NewTestRouter() *TestRouter { if err != nil { log.Fatal(err) } - sesClient := email.NewSESClient() - emailQueue := email.NewEmailQueue(email.EmailQueueBufferSize, email.MaxEmailsPerSecond, &sesClient) + emailClient := email.TestEmailClient{} + emailQueue := email.NewEmailQueue(email.EmailQueueBufferSize, email.MaxEmailsPerSecond, &emailClient) nkodeAPI := api.NewNKodeAPI(repo, emailQueue) h := NkodeHandler{ API: nkodeAPI, @@ -157,15 +203,13 @@ func (r *TestRouter) Signup( attrsPerKey int, numberOfKeys int, userEmail string, - reset bool, ) (*entities.SignupResetInterface, int, error) { body := bytes.NewBufferString(fmt.Sprintf( - "customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s&reset=%t", + "customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s", customerID, attrsPerKey, numberOfKeys, userEmail, - reset, )) req := httptest.NewRequest(http.MethodPost, "/signup", body) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") @@ -290,3 +334,48 @@ func (r *TestRouter) RenewAttributes( 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 +} diff --git a/models/models.go b/models/models.go index 63865c3..1c14191 100644 --- a/models/models.go +++ b/models/models.go @@ -40,9 +40,9 @@ type LoginInterfacePost struct { } type LoginPost struct { - CustomerId string `json:"customer_id" binding:"required"` - UserEmail string `json:"email" binding:"required"` - KeySelection entities.KeySelection `json:"key_selection" binding:"required"` + CustomerId string `form:"customer_id" binding:"required"` + UserEmail string `form:"email" binding:"required"` + KeySelection entities.KeySelection `form:"key_selection" binding:"required"` } type RenewAttributesPost struct { diff --git a/security/jwt_claims.go b/security/jwt_claims.go index 3c7f0c8..c000eb5 100644 --- a/security/jwt_claims.go +++ b/security/jwt_claims.go @@ -115,12 +115,12 @@ func ClaimExpired(claims jwt.RegisteredClaims) error { return config.ErrClaimExpOrNil } -func ResetNKodeToken(userEmail string, customerId uuid.UUID) (string, error) { +func ResetNKodeToken(userEmail string, customerId string) (string, error) { resetClaims := ResetNKodeClaims{ true, jwt.RegisteredClaims{ Subject: userEmail, - Issuer: customerId.String(), + Issuer: customerId, ExpiresAt: jwt.NewNumericDate(time.Now().Add(resetNKodeTokenExp)), }, } diff --git a/security/jwt_claims_test.go b/security/jwt_claims_test.go index e2c1b50..692c253 100644 --- a/security/jwt_claims_test.go +++ b/security/jwt_claims_test.go @@ -19,7 +19,7 @@ func TestJwtClaims(t *testing.T) { assert.NoError(t, err) assert.Equal(t, refreshToken.Subject, email) assert.NoError(t, ClaimExpired(*refreshToken)) - resetNKode, err := ResetNKodeToken(email, customerId) + resetNKode, err := ResetNKodeToken(email, customerId.String()) assert.NoError(t, err) resetToken, err := ParseRestNKodeToken(resetNKode) assert.NoError(t, err) diff --git a/sqlc/models.go b/sqlc/models.go index d8f68cf..43575da 100644 --- a/sqlc/models.go +++ b/sqlc/models.go @@ -48,3 +48,9 @@ type User struct { LastLogin interface{} CreatedAt sql.NullString } + +type UserPermission struct { + ID int64 + UserID string + Permission string +}