split sign and reset
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"git.infra.nkode.tech/dkelly/nkode-core/config"
|
"git.infra.nkode.tech/dkelly/nkode-core/config"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/email"
|
"git.infra.nkode.tech/dkelly/nkode-core/email"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/memCache"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/repository"
|
"git.infra.nkode.tech/dkelly/nkode-core/repository"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/security"
|
"git.infra.nkode.tech/dkelly/nkode-core/security"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -23,6 +24,7 @@ type NKodeAPI struct {
|
|||||||
repo repository.CustomerUserRepository
|
repo repository.CustomerUserRepository
|
||||||
signupSessionCache *cache.Cache
|
signupSessionCache *cache.Cache
|
||||||
emailQueue *email.Queue
|
emailQueue *email.Queue
|
||||||
|
forgotNkodeCache memCache.ForgotNKodeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNKodeAPI(repo repository.CustomerUserRepository, queue *email.Queue) NKodeAPI {
|
func NewNKodeAPI(repo repository.CustomerUserRepository, queue *email.Queue) NKodeAPI {
|
||||||
@@ -30,6 +32,7 @@ func NewNKodeAPI(repo repository.CustomerUserRepository, queue *email.Queue) NKo
|
|||||||
repo: repo,
|
repo: repo,
|
||||||
emailQueue: queue,
|
emailQueue: queue,
|
||||||
signupSessionCache: cache.New(sessionExpiration, sessionCleanupInterval),
|
signupSessionCache: cache.New(sessionExpiration, sessionCleanupInterval),
|
||||||
|
forgotNkodeCache: memCache.NewForgotNKodeCache(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +82,6 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, cu
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
svgInterface, err := n.repo.GetSvgStringInterface(signupSession.LoginUserInterface.SvgId)
|
svgInterface, err := n.repo.GetSvgStringInterface(signupSession.LoginUserInterface.SvgId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -246,7 +248,7 @@ func (n *NKodeAPI) RefreshToken(userEmail entities.UserEmail, customerId entitie
|
|||||||
return security.EncodeAndSignClaims(newAccessClaims)
|
return security.EncodeAndSignClaims(newAccessClaims)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NKodeAPI) ResetNKode(userEmail entities.UserEmail, customerId entities.CustomerId) error {
|
func (n *NKodeAPI) ForgotNKode(userEmail entities.UserEmail, customerId entities.CustomerId) error {
|
||||||
user, err := n.repo.GetUser(userEmail, customerId)
|
user, err := n.repo.GetUser(userEmail, customerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting user in rest nkode %v", err)
|
return fmt.Errorf("error getting user in rest nkode %v", err)
|
||||||
@@ -272,6 +274,7 @@ func (n *NKodeAPI) ResetNKode(userEmail entities.UserEmail, customerId entities.
|
|||||||
Content: htmlBody,
|
Content: htmlBody,
|
||||||
}
|
}
|
||||||
n.emailQueue.AddEmail(email)
|
n.emailQueue.AddEmail(email)
|
||||||
|
n.forgotNkodeCache.Set(userEmail, customerId)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EmailRetryExpiration = 5 * time.Minute
|
||||||
|
SesCleanupInterval = 10 * time.Minute
|
||||||
|
EmailQueueBufferSize = 100
|
||||||
|
MaxEmailsPerSecond = 13
|
||||||
|
)
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
SendEmail(Email) error
|
SendEmail(Email) error
|
||||||
}
|
}
|
||||||
@@ -36,14 +43,9 @@ type SESClient struct {
|
|||||||
ResetCache *cache.Cache
|
ResetCache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
emailRetryExpiration = 5 * time.Minute
|
|
||||||
sesCleanupInterval = 10 * time.Minute
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewSESClient() SESClient {
|
func NewSESClient() SESClient {
|
||||||
return SESClient{
|
return SESClient{
|
||||||
ResetCache: cache.New(emailRetryExpiration, sesCleanupInterval),
|
ResetCache: cache.New(EmailRetryExpiration, SesCleanupInterval),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ func (s *SESClient) SendEmail(email Email) error {
|
|||||||
Source: aws.String(email.Sender),
|
Source: aws.String(email.Sender),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = s.ResetCache.Add(email.Recipient, nil, emailRetryExpiration); err != nil {
|
if err = s.ResetCache.Add(email.Recipient, nil, EmailRetryExpiration); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package entities
|
|||||||
import "git.infra.nkode.tech/dkelly/nkode-core/config"
|
import "git.infra.nkode.tech/dkelly/nkode-core/config"
|
||||||
|
|
||||||
type NKodePolicy struct {
|
type NKodePolicy struct {
|
||||||
MaxNkodeLen int `json:"max_nkode_len" form:"max_nkode_len" binding:"required"`
|
MaxNkodeLen int `form:"max_nkode_len"`
|
||||||
MinNkodeLen int `json:"min_nkode_len" form:"min_nkode_len" binding:"required"`
|
MinNkodeLen int `form:"min_nkode_len"`
|
||||||
DistinctSets int `json:"distinct_sets" form:"distinct_sets" binding:"required"`
|
DistinctSets int `form:"distinct_sets"`
|
||||||
DistinctAttributes int `json:"distinct_attributes" form:"distinct_attributes" binding:"required"`
|
DistinctAttributes int `form:"distinct_attributes"`
|
||||||
LockOut int `json:"lock_out" form:"lock_out" binding:"required"`
|
LockOut int `form:"lock_out"`
|
||||||
Expiration int `json:"expiration" form:"expiration" binding:"required"` // seconds, -1 no expiration
|
Expiration int `form:"expiration"` // seconds, -1 no expiration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultNKodePolicy() NKodePolicy {
|
func NewDefaultNKodePolicy() NKodePolicy {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func (h *NkodeHandler) RegisterRoutes(r *gin.Engine) {
|
|||||||
r.Group("/v1/nkode")
|
r.Group("/v1/nkode")
|
||||||
{
|
{
|
||||||
r.POST("/create-new-customer", h.CreateNewCustomerHandler)
|
r.POST("/create-new-customer", h.CreateNewCustomerHandler)
|
||||||
r.POST("/generate-signup-reset-interface", h.SignupResetHandler)
|
r.POST("/signup", h.SignupHandler)
|
||||||
r.POST("/set-nkode", h.SetNKodeHandler)
|
r.POST("/set-nkode", h.SetNKodeHandler)
|
||||||
r.POST("/confirm-nkode", h.ConfirmNKodeHandler)
|
r.POST("/confirm-nkode", h.ConfirmNKodeHandler)
|
||||||
r.POST("/get-login-interface", h.GetLoginInterfaceHandler)
|
r.POST("/get-login-interface", h.GetLoginInterfaceHandler)
|
||||||
@@ -37,8 +37,9 @@ func (h *NkodeHandler) RegisterRoutes(r *gin.Engine) {
|
|||||||
r.POST("/renew-attributes", h.RenewAttributesHandler)
|
r.POST("/renew-attributes", h.RenewAttributesHandler)
|
||||||
r.POST("/random-svg-interface", h.RandomSvgInterfaceHandler)
|
r.POST("/random-svg-interface", h.RandomSvgInterfaceHandler)
|
||||||
r.POST("/refresh-token", h.RefreshTokenHandler)
|
r.POST("/refresh-token", h.RefreshTokenHandler)
|
||||||
r.POST("/reset-nkode", h.ResetNKodeHandler)
|
r.POST("/forgot-nkode", h.ForgotNKodeHandler)
|
||||||
r.POST("/signout", h.SignoutHandler)
|
r.POST("/signout", h.SignoutHandler)
|
||||||
|
r.POST("/reset", h.ResetHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,12 +56,12 @@ func (h *NkodeHandler) CreateNewCustomerHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.Logger.Println("create new customer")
|
h.Logger.Println("create new customer")
|
||||||
c.JSON(200, gin.H{"customer_id": customerId})
|
c.JSON(200, gin.H{"customer_id": entities.CustomerIdToString(*customerId)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *NkodeHandler) SignupResetHandler(c *gin.Context) {
|
func (h *NkodeHandler) SignupHandler(c *gin.Context) {
|
||||||
h.Logger.Println("generate signup reset interface")
|
h.Logger.Println("generate signup reset interface")
|
||||||
var postBody models.SignupRestPostBody
|
var postBody models.SignupPostBody
|
||||||
if err := c.ShouldBind(&postBody); err != nil {
|
if err := c.ShouldBind(&postBody); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
@@ -86,7 +87,8 @@ func (h *NkodeHandler) SignupResetHandler(c *gin.Context) {
|
|||||||
c.String(400, malformedUserEmail)
|
c.String(400, malformedUserEmail)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := h.API.GenerateSignupResetInterface(userEmail, entities.CustomerId(customerId), kp, postBody.Reset)
|
|
||||||
|
resp, err := h.API.GenerateSignupResetInterface(userEmail, entities.CustomerId(customerId), kp, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
@@ -98,7 +100,7 @@ func (h *NkodeHandler) SignupResetHandler(c *gin.Context) {
|
|||||||
func (h *NkodeHandler) SetNKodeHandler(c *gin.Context) {
|
func (h *NkodeHandler) SetNKodeHandler(c *gin.Context) {
|
||||||
h.Logger.Println("set nkode")
|
h.Logger.Println("set nkode")
|
||||||
var postBody models.SetNKodePost
|
var postBody models.SetNKodePost
|
||||||
if err := c.ShouldBind(&postBody); err != nil {
|
if err := c.ShouldBindJSON(&postBody); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -125,7 +127,7 @@ func (h *NkodeHandler) SetNKodeHandler(c *gin.Context) {
|
|||||||
func (h *NkodeHandler) ConfirmNKodeHandler(c *gin.Context) {
|
func (h *NkodeHandler) ConfirmNKodeHandler(c *gin.Context) {
|
||||||
h.Logger.Println("confirm nkode")
|
h.Logger.Println("confirm nkode")
|
||||||
var postBody models.ConfirmNKodePost
|
var postBody models.ConfirmNKodePost
|
||||||
if err := c.ShouldBind(&postBody); err != nil {
|
if err := c.ShouldBindJSON(&postBody); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -177,7 +179,7 @@ func (h *NkodeHandler) LoginHandler(c *gin.Context) {
|
|||||||
h.Logger.Println("login")
|
h.Logger.Println("login")
|
||||||
|
|
||||||
var loginPost models.LoginPost
|
var loginPost models.LoginPost
|
||||||
if err := c.ShouldBind(&loginPost); err != nil {
|
if err := c.ShouldBindJSON(&loginPost); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -259,25 +261,25 @@ func (h *NkodeHandler) RefreshTokenHandler(c *gin.Context) {
|
|||||||
c.JSON(200, gin.H{"access_token": accessToken})
|
c.JSON(200, gin.H{"access_token": accessToken})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *NkodeHandler) ResetNKodeHandler(c *gin.Context) {
|
func (h *NkodeHandler) ForgotNKodeHandler(c *gin.Context) {
|
||||||
h.Logger.Println("reset nkode")
|
h.Logger.Println("forgot nkode")
|
||||||
var resetNKodePost models.ResetNKodePost
|
var forgotNKodePost models.ForgotNKodePost
|
||||||
if err := c.ShouldBind(&resetNKodePost); err != nil {
|
if err := c.ShouldBind(&forgotNKodePost); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
customerId, err := uuid.Parse(resetNKodePost.CustomerId)
|
customerId, err := uuid.Parse(forgotNKodePost.CustomerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(400, malformedCustomerId)
|
c.String(400, malformedCustomerId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userEmail, err := entities.ParseEmail(resetNKodePost.UserEmail)
|
userEmail, err := entities.ParseEmail(forgotNKodePost.UserEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(400, malformedUserEmail)
|
c.String(400, malformedUserEmail)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.API.ResetNKode(userEmail, entities.CustomerId(customerId)); err != nil {
|
if err := h.API.ForgotNKode(userEmail, entities.CustomerId(customerId)); err != nil {
|
||||||
handleError(c, err)
|
handleError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -313,6 +315,59 @@ func (h *NkodeHandler) SignoutHandler(c *gin.Context) {
|
|||||||
c.Status(200)
|
c.Status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) ResetHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("reset")
|
||||||
|
|
||||||
|
token, err := getBearerToken(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(403, "forbidden")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resetClaims, err := security.ParseRestNKodeToken(token)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var postBody models.SignupPostBody
|
||||||
|
if err = c.ShouldBind(&postBody); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(postBody.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userEmail, err := entities.ParseEmail(postBody.UserEmail)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedUserEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if postBody.UserEmail != resetClaims.Subject ||
|
||||||
|
postBody.CustomerId != resetClaims.Issuer {
|
||||||
|
c.String(403, "forbidden")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kp := entities.KeypadDimension{
|
||||||
|
AttrsPerKey: postBody.AttrsPerKey,
|
||||||
|
NumbOfKeys: postBody.NumbOfKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := kp.IsValidKeypadDimension(); err != nil {
|
||||||
|
c.String(400, invalidKeypadDimensions)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := h.API.GenerateSignupResetInterface(userEmail, entities.CustomerId(customerId), kp, true)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, resp)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func handleError(c *gin.Context, err error) {
|
func handleError(c *gin.Context, err error) {
|
||||||
log.Print("handling error: ", err)
|
log.Print("handling error: ", err)
|
||||||
statusCode, _ := config.HttpErrMap[err]
|
statusCode, _ := config.HttpErrMap[err]
|
||||||
|
|||||||
292
handler/handler_test.go
Normal file
292
handler/handler_test.go
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
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"
|
||||||
|
reset := false
|
||||||
|
|
||||||
|
// *** Signup ***
|
||||||
|
resp, status, err := tr.Signup(customerID, attrPerKey, numKeys, userEmail, reset)
|
||||||
|
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 ***
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
sesClient := email.NewSESClient()
|
||||||
|
emailQueue := email.NewEmailQueue(email.EmailQueueBufferSize, email.MaxEmailsPerSecond, &sesClient)
|
||||||
|
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,
|
||||||
|
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",
|
||||||
|
customerID,
|
||||||
|
attrsPerKey,
|
||||||
|
numberOfKeys,
|
||||||
|
userEmail,
|
||||||
|
reset,
|
||||||
|
))
|
||||||
|
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
|
||||||
|
}
|
||||||
38
memCache/forgot_nkode.go
Normal file
38
memCache/forgot_nkode.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package memCache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
forgotExpiration = 5 * time.Minute
|
||||||
|
forgotCleanupInterval = 10 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type ForgotNKodeCache struct {
|
||||||
|
innerCache *cache.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewForgotNKodeCache() ForgotNKodeCache {
|
||||||
|
forgotCache := cache.New(forgotExpiration, forgotCleanupInterval)
|
||||||
|
return ForgotNKodeCache{forgotCache}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ForgotNKodeCache) Set(userEmail entities.UserEmail, customerId entities.CustomerId) {
|
||||||
|
f.innerCache.Set(key(userEmail, customerId), true, forgotExpiration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ForgotNKodeCache) Get(userEmail entities.UserEmail, customerId entities.CustomerId) bool {
|
||||||
|
_, found := f.innerCache.Get(key(userEmail, customerId))
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ForgotNKodeCache) Delete(userEmail entities.UserEmail, customerId entities.CustomerId) {
|
||||||
|
f.innerCache.Delete(key(userEmail, customerId))
|
||||||
|
}
|
||||||
|
|
||||||
|
func key(email entities.UserEmail, id entities.CustomerId) string {
|
||||||
|
return string(email) + entities.CustomerIdToString(id)
|
||||||
|
}
|
||||||
@@ -15,24 +15,23 @@ type RefreshTokenResp struct {
|
|||||||
AccessToken string `form:"access_token" binding:"required"`
|
AccessToken string `form:"access_token" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignupRestPostBody struct {
|
type SignupPostBody struct {
|
||||||
CustomerId string `form:"customer_id" binding:"required"`
|
CustomerId string `form:"customer_id"`
|
||||||
AttrsPerKey int `form:"attrs_per_key" binding:"required"`
|
AttrsPerKey int `form:"attrs_per_key"`
|
||||||
NumbOfKeys int `form:"numb_of_keys" binding:"required"`
|
NumbOfKeys int `form:"numb_of_keys"`
|
||||||
UserEmail string `form:"email" binding:"required"`
|
UserEmail string `form:"email"`
|
||||||
Reset bool `form:"reset" binding:"required"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetNKodePost struct {
|
type SetNKodePost struct {
|
||||||
CustomerId string `form:"customer_id" binding:"required"`
|
CustomerId string `json:"customer_id" binding:"required"`
|
||||||
KeySelection []int `form:"key_selection" binding:"required"`
|
KeySelection []int `json:"key_selection" binding:"required"`
|
||||||
SessionId string `form:"session_id" binding:"required"`
|
SessionId string `json:"session_id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfirmNKodePost struct {
|
type ConfirmNKodePost struct {
|
||||||
CustomerId string `form:"customer_id" binding:"required"`
|
CustomerId string `json:"customer_id" binding:"required"`
|
||||||
KeySelection entities.KeySelection `form:"key_selection" binding:"required"`
|
KeySelection []int `json:"key_selection" binding:"required"`
|
||||||
SessionId string `form:"session_id" binding:"required"`
|
SessionId string `json:"session_id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginInterfacePost struct {
|
type LoginInterfacePost struct {
|
||||||
@@ -41,16 +40,16 @@ type LoginInterfacePost struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoginPost struct {
|
type LoginPost struct {
|
||||||
CustomerId string `form:"customer_id" binding:"required"`
|
CustomerId string `json:"customer_id" binding:"required"`
|
||||||
UserEmail string `form:"email" binding:"required"`
|
UserEmail string `json:"email" binding:"required"`
|
||||||
KeySelection entities.KeySelection `form:"key_selection" binding:"required"`
|
KeySelection entities.KeySelection `json:"key_selection" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RenewAttributesPost struct {
|
type RenewAttributesPost struct {
|
||||||
CustomerId string `form:"customer_id" binding:"required"`
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetNKodePost struct {
|
type ForgotNKodePost struct {
|
||||||
UserEmail string `form:"email" binding:"required"`
|
UserEmail string `form:"email" binding:"required"`
|
||||||
CustomerId string `form:"customer_id" binding:"required"`
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func NewAuthenticationTokens(username string, customerId uuid.UUID) (Authenticat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewAccessClaim(username string, customerId uuid.UUID) jwt.RegisteredClaims {
|
func NewAccessClaim(username string, customerId uuid.UUID) jwt.RegisteredClaims {
|
||||||
|
// TODO: CHANGE ISSUER TO BE URL
|
||||||
return jwt.RegisteredClaims{
|
return jwt.RegisteredClaims{
|
||||||
Subject: username,
|
Subject: username,
|
||||||
Issuer: customerId.String(),
|
Issuer: customerId.String(),
|
||||||
@@ -85,6 +86,10 @@ func ParseRestNKodeToken(resetNKodeToken string) (*ResetNKodeClaims, error) {
|
|||||||
return parseJwt[*ResetNKodeClaims](resetNKodeToken, &ResetNKodeClaims{})
|
return parseJwt[*ResetNKodeClaims](resetNKodeToken, &ResetNKodeClaims{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseResetNKodeClaim(token string) (*ResetNKodeClaims, error) {
|
||||||
|
return parseJwt[*ResetNKodeClaims](token, &ResetNKodeClaims{})
|
||||||
|
}
|
||||||
|
|
||||||
func parseJwt[T *ResetNKodeClaims | *jwt.RegisteredClaims](tokenStr string, claim jwt.Claims) (T, error) {
|
func parseJwt[T *ResetNKodeClaims | *jwt.RegisteredClaims](tokenStr string, claim jwt.Claims) (T, error) {
|
||||||
token, err := jwt.ParseWithClaims(tokenStr, claim, func(token *jwt.Token) (interface{}, error) {
|
token, err := jwt.ParseWithClaims(tokenStr, claim, func(token *jwt.Token) (interface{}, error) {
|
||||||
return secret, nil
|
return secret, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user