Merge pull request 'Sqlc' (#5) from Sqlc into main
Reviewed-on: https://git.infra.nkode.tech/dkelly/go-nkode/pulls/5
This commit is contained in:
1
.env.test.example
Normal file
1
.env.test.example
Normal file
@@ -0,0 +1 @@
|
||||
TEST_DB=/path/to/test.db
|
||||
@@ -43,15 +43,18 @@ func main() {
|
||||
if dbPath == "" {
|
||||
log.Fatalf("SQLITE_DB=/path/to/nkode.db not set")
|
||||
}
|
||||
db := db.NewSqliteDB(dbPath)
|
||||
defer db.CloseDb()
|
||||
sqlitedb, err := db.NewSqliteDB(dbPath)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
defer sqlitedb.Close()
|
||||
|
||||
sesClient := email.NewSESClient()
|
||||
emailQueue := email.NewEmailQueue(emailQueueBufferSize, maxEmailsPerSecond, &sesClient)
|
||||
emailQueue.Start()
|
||||
defer emailQueue.Stop()
|
||||
|
||||
nkodeApi := api.NewNKodeAPI(db, emailQueue)
|
||||
nkodeApi := api.NewNKodeAPI(sqlitedb, emailQueue)
|
||||
AddDefaultCustomer(nkodeApi)
|
||||
handler := api.NKodeHandler{Api: nkodeApi}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/internal/api"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"io"
|
||||
@@ -19,7 +20,7 @@ func TestApi(t *testing.T) {
|
||||
newCustomerBody := models.NewCustomerPost{
|
||||
NKodePolicy: models.NewDefaultNKodePolicy(),
|
||||
}
|
||||
kp := models.KeypadDimension{
|
||||
kp := entities.KeypadDimension{
|
||||
AttrsPerKey: 14,
|
||||
NumbOfKeys: 10,
|
||||
}
|
||||
@@ -40,8 +41,8 @@ func TestApi(t *testing.T) {
|
||||
passcodeLen := 4
|
||||
setInterface := signupInterfaceResp.UserIdxInterface
|
||||
userPasscode := setInterface[:passcodeLen]
|
||||
kpSet := models.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys}
|
||||
setKeySelection, err := models.SelectKeyByAttrIdx(setInterface, userPasscode, kpSet)
|
||||
kpSet := entities.KeypadDimension{NumbOfKeys: kp.NumbOfKeys, AttrsPerKey: kp.NumbOfKeys}
|
||||
setKeySelection, err := entities.SelectKeyByAttrIdx(setInterface, userPasscode, kpSet)
|
||||
assert.NoError(t, err)
|
||||
setNKodeBody := models.SetNKodePost{
|
||||
CustomerId: customerResp.CustomerId,
|
||||
@@ -51,7 +52,7 @@ func TestApi(t *testing.T) {
|
||||
var setNKodeResp models.SetNKodeResp
|
||||
testApiPost(t, base+api.SetNKode, setNKodeBody, &setNKodeResp)
|
||||
confirmInterface := setNKodeResp.UserInterface
|
||||
confirmKeySelection, err := models.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet)
|
||||
confirmKeySelection, err := entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet)
|
||||
assert.NoError(t, err)
|
||||
confirmNKodeBody := models.ConfirmNKodePost{
|
||||
CustomerId: customerResp.CustomerId,
|
||||
@@ -69,7 +70,7 @@ func TestApi(t *testing.T) {
|
||||
testApiPost(t, base+api.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp)
|
||||
assert.Equal(t, loginInterfaceResp.AttrsPerKey, kp.AttrsPerKey)
|
||||
assert.Equal(t, loginInterfaceResp.NumbOfKeys, kp.NumbOfKeys)
|
||||
loginKeySelection, err := models.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp)
|
||||
loginKeySelection, err := entities.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp)
|
||||
assert.NoError(t, err)
|
||||
loginBody := models.LoginPost{
|
||||
CustomerId: customerResp.CustomerId,
|
||||
@@ -86,7 +87,7 @@ func TestApi(t *testing.T) {
|
||||
renewBody := models.RenewAttributesPost{CustomerId: customerResp.CustomerId}
|
||||
testApiPost(t, base+api.RenewAttributes, renewBody, nil)
|
||||
|
||||
loginKeySelection, err = models.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp)
|
||||
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterfaceResp.UserIdxInterface, userPasscode, kp)
|
||||
assert.NoError(t, err)
|
||||
loginBody = models.LoginPost{
|
||||
CustomerId: customerResp.CustomerId,
|
||||
@@ -98,7 +99,7 @@ func TestApi(t *testing.T) {
|
||||
|
||||
var randomSvgInterfaceResp models.RandomSvgInterfaceResp
|
||||
testApiGet(t, base+api.RandomSvgInterface, &randomSvgInterfaceResp, "")
|
||||
assert.Equal(t, models.KeypadMax.TotalAttrs(), len(randomSvgInterfaceResp.Svgs))
|
||||
assert.Equal(t, entities.KeypadMax.TotalAttrs(), len(randomSvgInterfaceResp.Svgs))
|
||||
|
||||
var refreshTokenResp models.RefreshTokenResp
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"go-nkode/internal/models"
|
||||
)
|
||||
|
||||
type DbAccessor interface {
|
||||
GetCustomer(models.CustomerId) (*models.Customer, error)
|
||||
GetUser(models.UserEmail, models.CustomerId) (*models.User, error)
|
||||
WriteNewCustomer(models.Customer) error
|
||||
WriteNewUser(models.User) error
|
||||
UpdateUserNKode(models.User) error
|
||||
UpdateUserInterface(models.UserId, models.UserInterface) error
|
||||
UpdateUserRefreshToken(models.UserId, string) error
|
||||
Renew(models.CustomerId) error
|
||||
RefreshUserPasscode(models.User, []int, models.CustomerAttributes) error
|
||||
RandomSvgInterface(models.KeypadDimension) ([]string, error)
|
||||
RandomSvgIdxInterface(models.KeypadDimension) (models.SvgIdInterface, error)
|
||||
GetSvgStringInterface(models.SvgIdInterface) ([]string, error)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"log"
|
||||
@@ -108,7 +109,7 @@ func (h *NKodeHandler) GenerateSignupResetInterfaceHandler(w http.ResponseWriter
|
||||
return
|
||||
}
|
||||
|
||||
kp := models.KeypadDimension{
|
||||
kp := entities.KeypadDimension{
|
||||
AttrsPerKey: signupResetPost.AttrsPerKey,
|
||||
NumbOfKeys: signupResetPost.NumbOfKeys,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/db"
|
||||
"go-nkode/internal/email"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"log"
|
||||
@@ -19,12 +21,12 @@ const (
|
||||
)
|
||||
|
||||
type NKodeAPI struct {
|
||||
Db DbAccessor
|
||||
Db db.CustomerUserRepository
|
||||
SignupSessionCache *cache.Cache
|
||||
EmailQueue *email.EmailQueue
|
||||
EmailQueue *email.Queue
|
||||
}
|
||||
|
||||
func NewNKodeAPI(db DbAccessor, queue *email.EmailQueue) NKodeAPI {
|
||||
func NewNKodeAPI(db db.CustomerUserRepository, queue *email.Queue) NKodeAPI {
|
||||
return NKodeAPI{
|
||||
Db: db,
|
||||
EmailQueue: queue,
|
||||
@@ -33,14 +35,14 @@ func NewNKodeAPI(db DbAccessor, queue *email.EmailQueue) NKodeAPI {
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy models.NKodePolicy, id *models.CustomerId) (*models.CustomerId, error) {
|
||||
newCustomer, err := models.NewCustomer(nkodePolicy)
|
||||
newCustomer, err := entities.NewCustomer(nkodePolicy)
|
||||
if id != nil {
|
||||
newCustomer.Id = *id
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = n.Db.WriteNewCustomer(*newCustomer)
|
||||
err = n.Db.CreateCustomer(*newCustomer)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -48,7 +50,7 @@ func (n *NKodeAPI) CreateNewCustomer(nkodePolicy models.NKodePolicy, id *models.
|
||||
return &newCustomer.Id, nil
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail models.UserEmail, customerId models.CustomerId, kp models.KeypadDimension, reset bool) (*models.GenerateSignupResetInterfaceResp, error) {
|
||||
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail models.UserEmail, customerId models.CustomerId, kp entities.KeypadDimension, reset bool) (*models.GenerateSignupResetInterfaceResp, error) {
|
||||
user, err := n.Db.GetUser(userEmail, customerId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -61,7 +63,7 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail models.UserEmail, cust
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signupSession, err := models.NewSignupResetSession(userEmail, kp, customerId, svgIdxInterface, reset)
|
||||
signupSession, err := entities.NewSignupResetSession(userEmail, kp, customerId, svgIdxInterface, reset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -94,7 +96,7 @@ func (n *NKodeAPI) SetNKode(customerId models.CustomerId, sessionId models.Sessi
|
||||
log.Printf("session id does not exist %s", sessionId)
|
||||
return nil, config.ErrSignupSessionDNE
|
||||
}
|
||||
userSession, ok := session.(models.UserSignSession)
|
||||
userSession, ok := session.(entities.UserSignSession)
|
||||
if !ok {
|
||||
// handle the case where the type assertion fails
|
||||
return nil, config.ErrSignupSessionDNE
|
||||
@@ -113,7 +115,7 @@ func (n *NKodeAPI) ConfirmNKode(customerId models.CustomerId, sessionId models.S
|
||||
log.Printf("session id does not exist %s", sessionId)
|
||||
return config.ErrSignupSessionDNE
|
||||
}
|
||||
userSession, ok := session.(models.UserSignSession)
|
||||
userSession, ok := session.(entities.UserSignSession)
|
||||
if !ok {
|
||||
// handle the case where the type assertion fails
|
||||
return config.ErrSignupSessionDNE
|
||||
@@ -129,7 +131,7 @@ func (n *NKodeAPI) ConfirmNKode(customerId models.CustomerId, sessionId models.S
|
||||
if err = customer.IsValidNKode(userSession.Kp, passcode); err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := models.NewUser(*customer, string(userSession.UserEmail), passcode, userSession.LoginUserInterface, userSession.Kp)
|
||||
user, err := entities.NewUser(*customer, string(userSession.UserEmail), passcode, userSession.LoginUserInterface, userSession.Kp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -186,7 +188,7 @@ func (n *NKodeAPI) Login(customerId models.CustomerId, userEmail models.UserEmai
|
||||
log.Printf("user %s for customer %s dne", userEmail, customerId)
|
||||
return nil, config.ErrUserForCustomerDNE
|
||||
}
|
||||
passcode, err := models.ValidKeyEntry(*user, *customer, keySelection)
|
||||
passcode, err := entities.ValidKeyEntry(*user, *customer, keySelection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -213,7 +215,7 @@ func (n *NKodeAPI) RenewAttributes(customerId models.CustomerId) error {
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) RandomSvgInterface() ([]string, error) {
|
||||
return n.Db.RandomSvgInterface(models.KeypadMax)
|
||||
return n.Db.RandomSvgInterface(entities.KeypadMax)
|
||||
}
|
||||
|
||||
func (n *NKodeAPI) RefreshToken(userEmail models.UserEmail, customerId models.CustomerId, refreshToken string) (string, error) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/internal/db"
|
||||
"go-nkode/internal/email"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"os"
|
||||
@@ -16,8 +17,9 @@ func TestNKodeAPI(t *testing.T) {
|
||||
|
||||
dbFile := os.Getenv("TEST_DB")
|
||||
|
||||
db2 := db.NewSqliteDB(dbFile)
|
||||
defer db2.CloseDb()
|
||||
db2, err := db.NewSqliteDB(dbFile)
|
||||
assert.NoError(t, err)
|
||||
defer db2.Close()
|
||||
testNKodeAPI(t, db2)
|
||||
|
||||
//if _, err := os.Stat(dbFile); err == nil {
|
||||
@@ -28,7 +30,7 @@ func TestNKodeAPI(t *testing.T) {
|
||||
//}
|
||||
}
|
||||
|
||||
func testNKodeAPI(t *testing.T, db DbAccessor) {
|
||||
func testNKodeAPI(t *testing.T, db db.CustomerUserRepository) {
|
||||
bufferSize := 100
|
||||
emailsPerSec := 14
|
||||
testClient := email.TestEmailClient{}
|
||||
@@ -41,7 +43,7 @@ func testNKodeAPI(t *testing.T, db DbAccessor) {
|
||||
userEmail := models.UserEmail("test_username" + security.GenerateRandomString(12) + "@example.com")
|
||||
passcodeLen := 4
|
||||
nkodePolicy := models.NewDefaultNKodePolicy()
|
||||
keypadSize := models.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
keypadSize := entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
nkodeApi := NewNKodeAPI(db, queue)
|
||||
customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy, nil)
|
||||
assert.NoError(t, err)
|
||||
@@ -51,20 +53,20 @@ func testNKodeAPI(t *testing.T, db DbAccessor) {
|
||||
sessionIdStr := signupResponse.SessionId
|
||||
sessionId, err := models.SessionIdFromString(sessionIdStr)
|
||||
assert.NoError(t, err)
|
||||
keypadSize = models.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
|
||||
keypadSize = entities.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
|
||||
userPasscode := setInterface[:passcodeLen]
|
||||
setKeySelect, err := models.SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
|
||||
setKeySelect, err := entities.SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
confirmInterface, err := nkodeApi.SetNKode(*customerId, sessionId, setKeySelect)
|
||||
assert.NoError(t, err)
|
||||
confirmKeySelect, err := models.SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
|
||||
confirmKeySelect, err := entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
|
||||
err = nkodeApi.ConfirmNKode(*customerId, sessionId, confirmKeySelect)
|
||||
assert.NoError(t, err)
|
||||
|
||||
keypadSize = models.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
keypadSize = entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
loginInterface, err := nkodeApi.GetLoginInterface(userEmail, *customerId)
|
||||
assert.NoError(t, err)
|
||||
loginKeySelection, err := models.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize)
|
||||
loginKeySelection, err := entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
_, err = nkodeApi.Login(*customerId, userEmail, loginKeySelection)
|
||||
assert.NoError(t, err)
|
||||
@@ -74,34 +76,34 @@ func testNKodeAPI(t *testing.T, db DbAccessor) {
|
||||
|
||||
loginInterface, err = nkodeApi.GetLoginInterface(userEmail, *customerId)
|
||||
assert.NoError(t, err)
|
||||
loginKeySelection, err = models.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize)
|
||||
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
_, err = nkodeApi.Login(*customerId, userEmail, loginKeySelection)
|
||||
assert.NoError(t, err)
|
||||
|
||||
/// Reset nKode
|
||||
attrsPerKey = 6
|
||||
keypadSize = models.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
keypadSize = entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
resetResponse, err := nkodeApi.GenerateSignupResetInterface(userEmail, *customerId, keypadSize, true)
|
||||
assert.NoError(t, err)
|
||||
setInterface = resetResponse.UserIdxInterface
|
||||
sessionIdStr = resetResponse.SessionId
|
||||
sessionId, err = models.SessionIdFromString(sessionIdStr)
|
||||
assert.NoError(t, err)
|
||||
keypadSize = models.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
|
||||
keypadSize = entities.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
|
||||
userPasscode = setInterface[:passcodeLen]
|
||||
setKeySelect, err = models.SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
|
||||
setKeySelect, err = entities.SelectKeyByAttrIdx(setInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
confirmInterface, err = nkodeApi.SetNKode(*customerId, sessionId, setKeySelect)
|
||||
assert.NoError(t, err)
|
||||
confirmKeySelect, err = models.SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
|
||||
confirmKeySelect, err = entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, keypadSize)
|
||||
err = nkodeApi.ConfirmNKode(*customerId, sessionId, confirmKeySelect)
|
||||
assert.NoError(t, err)
|
||||
|
||||
keypadSize = models.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
keypadSize = entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||
loginInterface2, err := nkodeApi.GetLoginInterface(userEmail, *customerId)
|
||||
assert.NoError(t, err)
|
||||
loginKeySelection, err = models.SelectKeyByAttrIdx(loginInterface2.UserIdxInterface, userPasscode, keypadSize)
|
||||
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterface2.UserIdxInterface, userPasscode, keypadSize)
|
||||
assert.NoError(t, err)
|
||||
_, err = nkodeApi.Login(*customerId, userEmail, loginKeySelection)
|
||||
assert.NoError(t, err)
|
||||
|
||||
21
internal/db/customer_user_repository.go
Normal file
21
internal/db/customer_user_repository.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
)
|
||||
|
||||
type CustomerUserRepository interface {
|
||||
GetCustomer(models.CustomerId) (*entities.Customer, error)
|
||||
GetUser(models.UserEmail, models.CustomerId) (*entities.User, error)
|
||||
CreateCustomer(entities.Customer) error
|
||||
WriteNewUser(entities.User) error
|
||||
UpdateUserNKode(entities.User) error
|
||||
UpdateUserInterface(models.UserId, entities.UserInterface) error
|
||||
UpdateUserRefreshToken(models.UserId, string) error
|
||||
Renew(models.CustomerId) error
|
||||
RefreshUserPasscode(entities.User, []int, entities.CustomerAttributes) error
|
||||
RandomSvgInterface(entities.KeypadDimension) ([]string, error)
|
||||
RandomSvgIdxInterface(entities.KeypadDimension) (models.SvgIdInterface, error)
|
||||
GetSvgStringInterface(models.SvgIdInterface) ([]string, error)
|
||||
}
|
||||
@@ -3,24 +3,25 @@ package db
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
)
|
||||
|
||||
type InMemoryDb struct {
|
||||
Customers map[models.CustomerId]models.Customer
|
||||
Users map[models.UserId]models.User
|
||||
Customers map[models.CustomerId]entities.Customer
|
||||
Users map[models.UserId]entities.User
|
||||
userIdMap map[string]models.UserId
|
||||
}
|
||||
|
||||
func NewInMemoryDb() InMemoryDb {
|
||||
return InMemoryDb{
|
||||
Customers: make(map[models.CustomerId]models.Customer),
|
||||
Users: make(map[models.UserId]models.User),
|
||||
Customers: make(map[models.CustomerId]entities.Customer),
|
||||
Users: make(map[models.UserId]entities.User),
|
||||
userIdMap: make(map[string]models.UserId),
|
||||
}
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) GetCustomer(id models.CustomerId) (*models.Customer, error) {
|
||||
func (db *InMemoryDb) GetCustomer(id models.CustomerId) (*entities.Customer, error) {
|
||||
customer, exists := db.Customers[id]
|
||||
if !exists {
|
||||
return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id))
|
||||
@@ -28,7 +29,7 @@ func (db *InMemoryDb) GetCustomer(id models.CustomerId) (*models.Customer, error
|
||||
return &customer, nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) GetUser(username models.UserEmail, customerId models.CustomerId) (*models.User, error) {
|
||||
func (db *InMemoryDb) GetUser(username models.UserEmail, customerId models.CustomerId) (*entities.User, error) {
|
||||
key := userIdKey(customerId, username)
|
||||
userId, exists := db.userIdMap[key]
|
||||
if !exists {
|
||||
@@ -41,7 +42,7 @@ func (db *InMemoryDb) GetUser(username models.UserEmail, customerId models.Custo
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) WriteNewCustomer(customer models.Customer) error {
|
||||
func (db *InMemoryDb) CreateCustomer(customer entities.Customer) error {
|
||||
_, exists := db.Customers[customer.Id]
|
||||
|
||||
if exists {
|
||||
@@ -51,7 +52,7 @@ func (db *InMemoryDb) WriteNewCustomer(customer models.Customer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) WriteNewUser(user models.User) error {
|
||||
func (db *InMemoryDb) WriteNewUser(user entities.User) error {
|
||||
_, exists := db.Customers[user.CustomerId]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("can't add user %s to customer %s: customer dne", user.Email, user.CustomerId))
|
||||
@@ -67,11 +68,11 @@ func (db *InMemoryDb) WriteNewUser(user models.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) UpdateUserNKode(user models.User) error {
|
||||
func (db *InMemoryDb) UpdateUserNKode(user entities.User) error {
|
||||
return errors.ErrUnsupported
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) UpdateUserInterface(userId models.UserId, ui models.UserInterface) error {
|
||||
func (db *InMemoryDb) UpdateUserInterface(userId models.UserId, ui entities.UserInterface) error {
|
||||
user, exists := db.Users[userId]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("can't update user %s, dne", user.Id))
|
||||
@@ -107,7 +108,7 @@ func (db *InMemoryDb) Renew(id models.CustomerId) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) RefreshUserPasscode(user models.User, passocode []int, customerAttr models.CustomerAttributes) error {
|
||||
func (db *InMemoryDb) RefreshUserPasscode(user entities.User, passocode []int, customerAttr entities.CustomerAttributes) error {
|
||||
err := user.RefreshPasscode(passocode, customerAttr)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -116,11 +117,11 @@ func (db *InMemoryDb) RefreshUserPasscode(user models.User, passocode []int, cus
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) RandomSvgInterface(kp models.KeypadDimension) ([]string, error) {
|
||||
func (db *InMemoryDb) RandomSvgInterface(kp entities.KeypadDimension) ([]string, error) {
|
||||
return make([]string, kp.TotalAttrs()), nil
|
||||
}
|
||||
|
||||
func (db *InMemoryDb) RandomSvgIdxInterface(kp models.KeypadDimension) (models.SvgIdInterface, error) {
|
||||
func (db *InMemoryDb) RandomSvgIdxInterface(kp entities.KeypadDimension) (models.SvgIdInterface, error) {
|
||||
svgs := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
for idx := range svgs {
|
||||
svgs[idx] = idx
|
||||
|
||||
@@ -1,444 +1,402 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
_ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"go-nkode/internal/sqlc"
|
||||
"go-nkode/internal/utils"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SqliteDB struct {
|
||||
db *sql.DB
|
||||
stop bool
|
||||
writeQueue chan WriteTx
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
const writeBufferSize = 100
|
||||
|
||||
type sqlcGeneric func(*sqlc.Queries, context.Context, any) error
|
||||
|
||||
// WriteTx represents a write transaction
|
||||
type WriteTx struct {
|
||||
ErrChan chan error
|
||||
Query string
|
||||
Args []any
|
||||
Query sqlcGeneric
|
||||
Args interface{}
|
||||
}
|
||||
|
||||
const (
|
||||
writeBuffer = 1000
|
||||
)
|
||||
// SqliteDB represents the SQLite database connection and write queue
|
||||
type SqliteDB struct {
|
||||
queries *sqlc.Queries
|
||||
db *sql.DB
|
||||
writeQueue chan WriteTx
|
||||
wg sync.WaitGroup
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// NewSqliteDB initializes a new SqliteDB instance
|
||||
func NewSqliteDB(path string) (*SqliteDB, error) {
|
||||
if path == "" {
|
||||
return nil, errors.New("database path is required")
|
||||
}
|
||||
|
||||
func NewSqliteDB(path string) *SqliteDB {
|
||||
db, err := sql.Open("sqlite3", path)
|
||||
if err != nil {
|
||||
log.Fatal("database didn't open ", err)
|
||||
return nil, fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
sqldb := SqliteDB{
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to database: %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
sqldb := &SqliteDB{
|
||||
queries: sqlc.New(db),
|
||||
db: db,
|
||||
stop: false,
|
||||
writeQueue: make(chan WriteTx, writeBuffer),
|
||||
writeQueue: make(chan WriteTx, writeBufferSize),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for writeTx := range sqldb.writeQueue {
|
||||
writeTx.ErrChan <- sqldb.writeToDb(writeTx.Query, writeTx.Args)
|
||||
sqldb.wg.Done()
|
||||
}
|
||||
}()
|
||||
sqldb.wg.Add(1)
|
||||
go sqldb.processWriteQueue()
|
||||
|
||||
return &sqldb
|
||||
return sqldb, nil
|
||||
}
|
||||
|
||||
func (d *SqliteDB) CloseDb() {
|
||||
d.stop = true
|
||||
// processWriteQueue handles write transactions from the queue
|
||||
func (d *SqliteDB) processWriteQueue() {
|
||||
defer d.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-d.ctx.Done():
|
||||
return
|
||||
case writeTx := <-d.writeQueue:
|
||||
err := writeTx.Query(d.queries, d.ctx, writeTx.Args)
|
||||
writeTx.ErrChan <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SqliteDB) Close() error {
|
||||
d.cancel()
|
||||
d.wg.Wait()
|
||||
if err := d.db.Close(); err != nil {
|
||||
// If db.Close() returns an error, panic
|
||||
panic(fmt.Sprintf("Failed to close the database: %v", err))
|
||||
}
|
||||
close(d.writeQueue)
|
||||
return d.db.Close()
|
||||
}
|
||||
|
||||
func (d *SqliteDB) WriteNewCustomer(c models.Customer) error {
|
||||
query := `
|
||||
INSERT INTO customer (
|
||||
id
|
||||
,max_nkode_len
|
||||
,min_nkode_len
|
||||
,distinct_sets
|
||||
,distinct_attributes
|
||||
,lock_out
|
||||
,expiration
|
||||
,attribute_values
|
||||
,set_values
|
||||
,last_renew
|
||||
,created_at
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
`
|
||||
args := []any{
|
||||
uuid.UUID(c.Id), c.NKodePolicy.MaxNkodeLen, c.NKodePolicy.MinNkodeLen, c.NKodePolicy.DistinctSets,
|
||||
c.NKodePolicy.DistinctAttributes, c.NKodePolicy.LockOut, c.NKodePolicy.Expiration,
|
||||
c.Attributes.AttrBytes(), c.Attributes.SetBytes(), timeStamp(), timeStamp(),
|
||||
func (d *SqliteDB) CreateCustomer(c entities.Customer) error {
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.CreateCustomerParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected CreateCustomerParams")
|
||||
}
|
||||
return d.addWriteTx(query, args)
|
||||
return q.CreateCustomer(ctx, params)
|
||||
}
|
||||
|
||||
func (d *SqliteDB) WriteNewUser(u models.User) error {
|
||||
query := `
|
||||
INSERT INTO user (
|
||||
id
|
||||
,email
|
||||
,renew
|
||||
,refresh_token
|
||||
,customer_id
|
||||
,code
|
||||
,mask
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
,alpha_key
|
||||
,set_key
|
||||
,pass_key
|
||||
,mask_key
|
||||
,salt
|
||||
,max_nkode_len
|
||||
,idx_interface
|
||||
,svg_id_interface
|
||||
,created_at
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
`
|
||||
var renew int
|
||||
return d.enqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
|
||||
}
|
||||
|
||||
func (d *SqliteDB) WriteNewUser(u entities.User) error {
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.CreateUserParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected CreateUserParams")
|
||||
}
|
||||
return q.CreateUser(ctx, params)
|
||||
}
|
||||
// Use the wrapped function in enqueueWriteTx
|
||||
|
||||
renew := 0
|
||||
if u.Renew {
|
||||
renew = 1
|
||||
} else {
|
||||
renew = 0
|
||||
}
|
||||
// Map entities.User to CreateUserParams
|
||||
params := sqlc.CreateUserParams{
|
||||
ID: uuid.UUID(u.Id).String(),
|
||||
Email: string(u.Email),
|
||||
Renew: int64(renew),
|
||||
RefreshToken: sql.NullString{String: u.RefreshToken, Valid: u.RefreshToken != ""},
|
||||
CustomerID: uuid.UUID(u.CustomerId).String(),
|
||||
Code: u.EncipheredPasscode.Code,
|
||||
Mask: u.EncipheredPasscode.Mask,
|
||||
AttributesPerKey: int64(u.Kp.AttrsPerKey),
|
||||
NumberOfKeys: int64(u.Kp.NumbOfKeys),
|
||||
AlphaKey: security.Uint64ArrToByteArr(u.CipherKeys.AlphaKey),
|
||||
SetKey: security.Uint64ArrToByteArr(u.CipherKeys.SetKey),
|
||||
PassKey: security.Uint64ArrToByteArr(u.CipherKeys.PassKey),
|
||||
MaskKey: security.Uint64ArrToByteArr(u.CipherKeys.MaskKey),
|
||||
Salt: u.CipherKeys.Salt,
|
||||
MaxNkodeLen: int64(u.CipherKeys.MaxNKodeLen),
|
||||
IdxInterface: security.IntArrToByteArr(u.Interface.IdxInterface),
|
||||
SvgIDInterface: security.IntArrToByteArr(u.Interface.SvgId),
|
||||
CreatedAt: sql.NullString{String: utils.TimeStamp(), Valid: true},
|
||||
}
|
||||
return d.enqueueWriteTx(queryFunc, params)
|
||||
}
|
||||
|
||||
args := []any{
|
||||
uuid.UUID(u.Id), u.Email, renew, u.RefreshToken, uuid.UUID(u.CustomerId),
|
||||
u.EncipheredPasscode.Code, u.EncipheredPasscode.Mask, u.Kp.AttrsPerKey, u.Kp.NumbOfKeys,
|
||||
security.Uint64ArrToByteArr(u.CipherKeys.AlphaKey), security.Uint64ArrToByteArr(u.CipherKeys.SetKey),
|
||||
security.Uint64ArrToByteArr(u.CipherKeys.PassKey), security.Uint64ArrToByteArr(u.CipherKeys.MaskKey),
|
||||
u.CipherKeys.Salt, u.CipherKeys.MaxNKodeLen, security.IntArrToByteArr(u.Interface.IdxInterface),
|
||||
security.IntArrToByteArr(u.Interface.SvgId), timeStamp(),
|
||||
func (d *SqliteDB) UpdateUserNKode(u entities.User) error {
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.UpdateUserParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected UpdateUserParams")
|
||||
}
|
||||
|
||||
return d.addWriteTx(query, args)
|
||||
return q.UpdateUser(ctx, params)
|
||||
}
|
||||
|
||||
func (d *SqliteDB) UpdateUserNKode(u models.User) error {
|
||||
query := `
|
||||
UPDATE user
|
||||
SET renew = ?
|
||||
,refresh_token = ?
|
||||
,code = ?
|
||||
,mask = ?
|
||||
,attributes_per_key = ?
|
||||
,number_of_keys = ?
|
||||
,alpha_key = ?
|
||||
,set_key = ?
|
||||
,pass_key = ?
|
||||
,mask_key = ?
|
||||
,salt = ?
|
||||
,max_nkode_len = ?
|
||||
,idx_interface = ?
|
||||
,svg_id_interface = ?
|
||||
WHERE email = ? AND customer_id = ?
|
||||
`
|
||||
var renew int
|
||||
// Use the wrapped function in enqueueWriteTx
|
||||
renew := 0
|
||||
if u.Renew {
|
||||
renew = 1
|
||||
} else {
|
||||
renew = 0
|
||||
}
|
||||
args := []any{renew, u.RefreshToken, u.EncipheredPasscode.Code, u.EncipheredPasscode.Mask, u.Kp.AttrsPerKey, u.Kp.NumbOfKeys, security.Uint64ArrToByteArr(u.CipherKeys.AlphaKey), security.Uint64ArrToByteArr(u.CipherKeys.SetKey), security.Uint64ArrToByteArr(u.CipherKeys.PassKey), security.Uint64ArrToByteArr(u.CipherKeys.MaskKey), u.CipherKeys.Salt, u.CipherKeys.MaxNKodeLen, security.IntArrToByteArr(u.Interface.IdxInterface), security.IntArrToByteArr(u.Interface.SvgId), string(u.Email), uuid.UUID(u.CustomerId)}
|
||||
|
||||
return d.addWriteTx(query, args)
|
||||
params := sqlc.UpdateUserParams{
|
||||
Email: string(u.Email),
|
||||
Renew: int64(renew),
|
||||
RefreshToken: sql.NullString{String: u.RefreshToken, Valid: u.RefreshToken != ""},
|
||||
CustomerID: uuid.UUID(u.CustomerId).String(),
|
||||
Code: u.EncipheredPasscode.Code,
|
||||
Mask: u.EncipheredPasscode.Mask,
|
||||
AttributesPerKey: int64(u.Kp.AttrsPerKey),
|
||||
NumberOfKeys: int64(u.Kp.NumbOfKeys),
|
||||
AlphaKey: security.Uint64ArrToByteArr(u.CipherKeys.AlphaKey),
|
||||
SetKey: security.Uint64ArrToByteArr(u.CipherKeys.SetKey),
|
||||
PassKey: security.Uint64ArrToByteArr(u.CipherKeys.PassKey),
|
||||
MaskKey: security.Uint64ArrToByteArr(u.CipherKeys.MaskKey),
|
||||
Salt: u.CipherKeys.Salt,
|
||||
MaxNkodeLen: int64(u.CipherKeys.MaxNKodeLen),
|
||||
IdxInterface: security.IntArrToByteArr(u.Interface.IdxInterface),
|
||||
SvgIDInterface: security.IntArrToByteArr(u.Interface.SvgId),
|
||||
}
|
||||
return d.enqueueWriteTx(queryFunc, params)
|
||||
}
|
||||
|
||||
func (d *SqliteDB) UpdateUserInterface(id models.UserId, ui models.UserInterface) error {
|
||||
query := `
|
||||
UPDATE user SET idx_interface = ?, last_login = ? WHERE id = ?
|
||||
`
|
||||
args := []any{security.IntArrToByteArr(ui.IdxInterface), timeStamp(), uuid.UUID(id).String()}
|
||||
func (d *SqliteDB) UpdateUserInterface(id models.UserId, ui entities.UserInterface) error {
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.UpdateUserInterfaceParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected UpdateUserInterfaceParams")
|
||||
}
|
||||
return q.UpdateUserInterface(ctx, params)
|
||||
}
|
||||
params := sqlc.UpdateUserInterfaceParams{
|
||||
IdxInterface: security.IntArrToByteArr(ui.IdxInterface),
|
||||
LastLogin: utils.TimeStamp(),
|
||||
ID: uuid.UUID(id).String(),
|
||||
}
|
||||
|
||||
return d.addWriteTx(query, args)
|
||||
return d.enqueueWriteTx(queryFunc, params)
|
||||
}
|
||||
|
||||
func (d *SqliteDB) UpdateUserRefreshToken(id models.UserId, refreshToken string) error {
|
||||
query := `
|
||||
UPDATE user SET refresh_token = ? WHERE id = ?
|
||||
`
|
||||
args := []any{refreshToken, uuid.UUID(id).String()}
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.UpdateUserRefreshTokenParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected UpdateUserRefreshToken")
|
||||
}
|
||||
return q.UpdateUserRefreshToken(ctx, params)
|
||||
}
|
||||
params := sqlc.UpdateUserRefreshTokenParams{
|
||||
RefreshToken: sql.NullString{
|
||||
String: refreshToken,
|
||||
Valid: true,
|
||||
},
|
||||
ID: uuid.UUID(id).String(),
|
||||
}
|
||||
return d.enqueueWriteTx(queryFunc, params)
|
||||
}
|
||||
|
||||
return d.addWriteTx(query, args)
|
||||
func (d *SqliteDB) RenewCustomer(renewParams sqlc.RenewCustomerParams) error {
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.RenewCustomerParams)
|
||||
if !ok {
|
||||
|
||||
}
|
||||
return q.RenewCustomer(ctx, params)
|
||||
}
|
||||
return d.enqueueWriteTx(queryFunc, renewParams)
|
||||
}
|
||||
|
||||
func (d *SqliteDB) Renew(id models.CustomerId) error {
|
||||
// TODO: How long does a renew take?
|
||||
customer, err := d.GetCustomer(id)
|
||||
setXor, attrXor, err := d.renewCustomer(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setXor, attrXor, err := customer.RenewKeys()
|
||||
customerId := models.CustomerIdToString(id)
|
||||
userRenewRows, err := d.queries.GetUserRenew(d.ctx, customerId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
renewArgs := []any{security.Uint64ArrToByteArr(customer.Attributes.AttrVals), security.Uint64ArrToByteArr(customer.Attributes.SetVals), uuid.UUID(customer.Id).String()}
|
||||
// TODO: replace with tx
|
||||
renewQuery := `
|
||||
UPDATE customer
|
||||
SET attribute_values = ?, set_values = ?
|
||||
WHERE id = ?;
|
||||
`
|
||||
|
||||
userQuery := `
|
||||
SELECT
|
||||
id
|
||||
,alpha_key
|
||||
,set_key
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
FROM user
|
||||
WHERE customer_id = ?
|
||||
`
|
||||
tx, err := d.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.RenewUserParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected RenewUserParams")
|
||||
}
|
||||
rows, err := tx.Query(userQuery, uuid.UUID(id).String())
|
||||
for rows.Next() {
|
||||
var userId string
|
||||
var alphaBytes []byte
|
||||
var setBytes []byte
|
||||
var attrsPerKey int
|
||||
var numbOfKeys int
|
||||
err = rows.Scan(&userId, &alphaBytes, &setBytes, &attrsPerKey, &numbOfKeys)
|
||||
if err != nil {
|
||||
return err
|
||||
return q.RenewUser(ctx, params)
|
||||
}
|
||||
user := models.User{
|
||||
Id: models.UserId{},
|
||||
|
||||
for _, row := range userRenewRows {
|
||||
user := entities.User{
|
||||
Id: models.UserIdFromString(row.ID),
|
||||
CustomerId: models.CustomerId{},
|
||||
Email: "",
|
||||
EncipheredPasscode: models.EncipheredNKode{},
|
||||
Kp: models.KeypadDimension{
|
||||
AttrsPerKey: attrsPerKey,
|
||||
NumbOfKeys: numbOfKeys,
|
||||
Kp: entities.KeypadDimension{
|
||||
AttrsPerKey: int(row.AttributesPerKey),
|
||||
NumbOfKeys: int(row.NumberOfKeys),
|
||||
},
|
||||
CipherKeys: models.UserCipherKeys{
|
||||
AlphaKey: security.ByteArrToUint64Arr(alphaBytes),
|
||||
SetKey: security.ByteArrToUint64Arr(setBytes),
|
||||
CipherKeys: entities.UserCipherKeys{
|
||||
AlphaKey: security.ByteArrToUint64Arr(row.AlphaKey),
|
||||
SetKey: security.ByteArrToUint64Arr(row.SetKey),
|
||||
},
|
||||
Interface: models.UserInterface{},
|
||||
Interface: entities.UserInterface{},
|
||||
Renew: false,
|
||||
}
|
||||
err = user.RenewKeys(setXor, attrXor)
|
||||
if err != nil {
|
||||
|
||||
if err = user.RenewKeys(setXor, attrXor); err != nil {
|
||||
return err
|
||||
}
|
||||
renewQuery += `
|
||||
UPDATE user
|
||||
SET alpha_key = ?, set_key = ?, renew = ?
|
||||
WHERE id = ?;
|
||||
`
|
||||
renewArgs = append(renewArgs, security.Uint64ArrToByteArr(user.CipherKeys.AlphaKey), security.Uint64ArrToByteArr(user.CipherKeys.SetKey), 1, userId)
|
||||
params := sqlc.RenewUserParams{
|
||||
AlphaKey: security.Uint64ArrToByteArr(user.CipherKeys.AlphaKey),
|
||||
SetKey: security.Uint64ArrToByteArr(user.CipherKeys.SetKey),
|
||||
Renew: 1,
|
||||
ID: uuid.UUID(user.Id).String(),
|
||||
}
|
||||
renewQuery += `
|
||||
`
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
if err = d.enqueueWriteTx(queryFunc, params); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.addWriteTx(renewQuery, renewArgs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SqliteDB) RefreshUserPasscode(user models.User, passcodeIdx []int, customerAttr models.CustomerAttributes) error {
|
||||
err := user.RefreshPasscode(passcodeIdx, customerAttr)
|
||||
func (d *SqliteDB) renewCustomer(id models.CustomerId) ([]uint64, []uint64, error) {
|
||||
customer, err := d.GetCustomer(id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
setXor, attrXor, err := customer.RenewKeys()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.RenewCustomerParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected RenewCustomerParams")
|
||||
}
|
||||
return q.RenewCustomer(ctx, params)
|
||||
}
|
||||
params := sqlc.RenewCustomerParams{
|
||||
AttributeValues: security.Uint64ArrToByteArr(customer.Attributes.AttrVals),
|
||||
SetValues: security.Uint64ArrToByteArr(customer.Attributes.SetVals),
|
||||
ID: uuid.UUID(customer.Id).String(),
|
||||
}
|
||||
|
||||
if err = d.enqueueWriteTx(queryFunc, params); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return setXor, attrXor, nil
|
||||
}
|
||||
|
||||
func (d *SqliteDB) RefreshUserPasscode(user entities.User, passcodeIdx []int, customerAttr entities.CustomerAttributes) error {
|
||||
if err := user.RefreshPasscode(passcodeIdx, customerAttr); err != nil {
|
||||
return err
|
||||
}
|
||||
query := `
|
||||
UPDATE user
|
||||
SET
|
||||
renew = ?
|
||||
,code = ?
|
||||
,mask = ?
|
||||
,alpha_key = ?
|
||||
,set_key = ?
|
||||
,pass_key = ?
|
||||
,mask_key = ?
|
||||
,salt = ?
|
||||
WHERE id = ?;
|
||||
`
|
||||
args := []any{user.RefreshToken, 0, user.EncipheredPasscode.Code, user.EncipheredPasscode.Mask, security.Uint64ArrToByteArr(user.CipherKeys.AlphaKey), security.Uint64ArrToByteArr(user.CipherKeys.SetKey), security.Uint64ArrToByteArr(user.CipherKeys.PassKey), security.Uint64ArrToByteArr(user.CipherKeys.MaskKey), user.CipherKeys.Salt, uuid.UUID(user.Id).String()}
|
||||
return d.addWriteTx(query, args)
|
||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||
params, ok := args.(sqlc.RefreshUserPasscodeParams)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument type: expected RefreshUserPasscodeParams")
|
||||
}
|
||||
func (d *SqliteDB) GetCustomer(id models.CustomerId) (*models.Customer, error) {
|
||||
tx, err := d.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return q.RefreshUserPasscode(ctx, params)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = tx.Rollback()
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Write new user won't roll back %+v", err))
|
||||
params := sqlc.RefreshUserPasscodeParams{
|
||||
Renew: 0,
|
||||
Code: user.EncipheredPasscode.Code,
|
||||
Mask: user.EncipheredPasscode.Mask,
|
||||
AlphaKey: security.Uint64ArrToByteArr(user.CipherKeys.AlphaKey),
|
||||
SetKey: security.Uint64ArrToByteArr(user.CipherKeys.SetKey),
|
||||
PassKey: security.Uint64ArrToByteArr(user.CipherKeys.PassKey),
|
||||
MaskKey: security.Uint64ArrToByteArr(user.CipherKeys.MaskKey),
|
||||
Salt: user.CipherKeys.Salt,
|
||||
ID: uuid.UUID(user.Id).String(),
|
||||
}
|
||||
return d.enqueueWriteTx(queryFunc, params)
|
||||
}
|
||||
}()
|
||||
selectCustomer := `
|
||||
SELECT
|
||||
max_nkode_len
|
||||
,min_nkode_len
|
||||
,distinct_sets
|
||||
,distinct_attributes
|
||||
,lock_out
|
||||
,expiration
|
||||
,attribute_values
|
||||
,set_values
|
||||
FROM customer
|
||||
WHERE id = ?
|
||||
`
|
||||
rows, err := tx.Query(selectCustomer, uuid.UUID(id))
|
||||
|
||||
func (d *SqliteDB) GetCustomer(id models.CustomerId) (*entities.Customer, error) {
|
||||
customer, err := d.queries.GetCustomer(d.ctx, uuid.UUID(id).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !rows.Next() {
|
||||
log.Printf("no new row for customer %s with err %s", id, rows.Err())
|
||||
return nil, config.ErrCustomerDne
|
||||
}
|
||||
|
||||
var maxNKodeLen int
|
||||
var minNKodeLen int
|
||||
var distinctSets int
|
||||
var distinctAttributes int
|
||||
var lockOut int
|
||||
var expiration int
|
||||
var attributeValues []byte
|
||||
var setValues []byte
|
||||
err = rows.Scan(&maxNKodeLen, &minNKodeLen, &distinctSets, &distinctAttributes, &lockOut, &expiration, &attributeValues, &setValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
customer := models.Customer{
|
||||
return &entities.Customer{
|
||||
Id: id,
|
||||
NKodePolicy: models.NKodePolicy{
|
||||
MaxNkodeLen: maxNKodeLen,
|
||||
MinNkodeLen: minNKodeLen,
|
||||
DistinctSets: distinctSets,
|
||||
DistinctAttributes: distinctAttributes,
|
||||
LockOut: lockOut,
|
||||
Expiration: expiration,
|
||||
MaxNkodeLen: int(customer.MaxNkodeLen),
|
||||
MinNkodeLen: int(customer.MinNkodeLen),
|
||||
DistinctSets: int(customer.DistinctSets),
|
||||
DistinctAttributes: int(customer.DistinctAttributes),
|
||||
LockOut: int(customer.LockOut),
|
||||
Expiration: int(customer.Expiration),
|
||||
},
|
||||
Attributes: models.NewCustomerAttributesFromBytes(attributeValues, setValues),
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &customer, nil
|
||||
Attributes: entities.NewCustomerAttributesFromBytes(customer.AttributeValues, customer.SetValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SqliteDB) GetUser(email models.UserEmail, customerId models.CustomerId) (*models.User, error) {
|
||||
tx, err := d.db.Begin()
|
||||
func (d *SqliteDB) GetUser(email models.UserEmail, customerId models.CustomerId) (*entities.User, error) {
|
||||
userRow, err := d.queries.GetUser(d.ctx, sqlc.GetUserParams{
|
||||
Email: string(email),
|
||||
CustomerID: uuid.UUID(customerId).String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSelect := `
|
||||
SELECT
|
||||
id
|
||||
,renew
|
||||
,refresh_token
|
||||
,code
|
||||
,mask
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
,alpha_key
|
||||
,set_key
|
||||
,pass_key
|
||||
,mask_key
|
||||
,salt
|
||||
,max_nkode_len
|
||||
,idx_interface
|
||||
,svg_id_interface
|
||||
FROM user
|
||||
WHERE user.email = ? AND user.customer_id = ?
|
||||
`
|
||||
rows, err := tx.Query(userSelect, string(email), uuid.UUID(customerId).String())
|
||||
if !rows.Next() {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
var (
|
||||
id string
|
||||
renewVal int
|
||||
refreshToken string
|
||||
code string
|
||||
mask string
|
||||
attrsPerKey int
|
||||
numbOfKeys int
|
||||
alphaKey []byte
|
||||
setKey []byte
|
||||
passKey []byte
|
||||
maskKey []byte
|
||||
salt []byte
|
||||
maxNKodeLen int
|
||||
idxInterface []byte
|
||||
svgIdInterface []byte
|
||||
)
|
||||
err = rows.Scan(&id, &renewVal, &refreshToken, &code, &mask, &attrsPerKey, &numbOfKeys, &alphaKey, &setKey, &passKey, &maskKey, &salt, &maxNKodeLen, &idxInterface, &svgIdInterface)
|
||||
|
||||
userId, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to get user: %w", err)
|
||||
}
|
||||
var renew bool
|
||||
if renewVal == 0 {
|
||||
renew = false
|
||||
} else {
|
||||
|
||||
kp := entities.KeypadDimension{
|
||||
AttrsPerKey: int(userRow.AttributesPerKey),
|
||||
NumbOfKeys: int(userRow.NumberOfKeys),
|
||||
}
|
||||
|
||||
renew := false
|
||||
if userRow.Renew == 1 {
|
||||
renew = true
|
||||
}
|
||||
|
||||
user := models.User{
|
||||
Id: models.UserId(userId),
|
||||
user := entities.User{
|
||||
Id: models.UserIdFromString(userRow.ID),
|
||||
CustomerId: customerId,
|
||||
Email: email,
|
||||
EncipheredPasscode: models.EncipheredNKode{
|
||||
Code: code,
|
||||
Mask: mask,
|
||||
Code: userRow.Code,
|
||||
Mask: userRow.Mask,
|
||||
},
|
||||
Kp: models.KeypadDimension{
|
||||
AttrsPerKey: attrsPerKey,
|
||||
NumbOfKeys: numbOfKeys,
|
||||
Kp: kp,
|
||||
CipherKeys: entities.UserCipherKeys{
|
||||
AlphaKey: security.ByteArrToUint64Arr(userRow.AlphaKey),
|
||||
SetKey: security.ByteArrToUint64Arr(userRow.SetKey),
|
||||
PassKey: security.ByteArrToUint64Arr(userRow.PassKey),
|
||||
MaskKey: security.ByteArrToUint64Arr(userRow.MaskKey),
|
||||
Salt: userRow.Salt,
|
||||
MaxNKodeLen: int(userRow.MaxNkodeLen),
|
||||
Kp: &kp,
|
||||
},
|
||||
CipherKeys: models.UserCipherKeys{
|
||||
AlphaKey: security.ByteArrToUint64Arr(alphaKey),
|
||||
SetKey: security.ByteArrToUint64Arr(setKey),
|
||||
PassKey: security.ByteArrToUint64Arr(passKey),
|
||||
MaskKey: security.ByteArrToUint64Arr(maskKey),
|
||||
Salt: salt,
|
||||
MaxNKodeLen: maxNKodeLen,
|
||||
Kp: nil,
|
||||
},
|
||||
Interface: models.UserInterface{
|
||||
IdxInterface: security.ByteArrToIntArr(idxInterface),
|
||||
SvgId: security.ByteArrToIntArr(svgIdInterface),
|
||||
Kp: nil,
|
||||
Interface: entities.UserInterface{
|
||||
IdxInterface: security.ByteArrToIntArr(userRow.IdxInterface),
|
||||
SvgId: security.ByteArrToIntArr(userRow.SvgIDInterface),
|
||||
Kp: &kp,
|
||||
},
|
||||
Renew: renew,
|
||||
RefreshToken: refreshToken,
|
||||
}
|
||||
user.Interface.Kp = &user.Kp
|
||||
user.CipherKeys.Kp = &user.Kp
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
RefreshToken: userRow.RefreshToken.String,
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (d *SqliteDB) RandomSvgInterface(kp models.KeypadDimension) ([]string, error) {
|
||||
func (d *SqliteDB) RandomSvgInterface(kp entities.KeypadDimension) ([]string, error) {
|
||||
ids, err := d.getRandomIds(kp.TotalAttrs())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -446,7 +404,7 @@ func (d *SqliteDB) RandomSvgInterface(kp models.KeypadDimension) ([]string, erro
|
||||
return d.getSvgsById(ids)
|
||||
}
|
||||
|
||||
func (d *SqliteDB) RandomSvgIdxInterface(kp models.KeypadDimension) (models.SvgIdInterface, error) {
|
||||
func (d *SqliteDB) RandomSvgIdxInterface(kp entities.KeypadDimension) (models.SvgIdInterface, error) {
|
||||
return d.getRandomIds(kp.TotalAttrs())
|
||||
}
|
||||
|
||||
@@ -455,68 +413,30 @@ func (d *SqliteDB) GetSvgStringInterface(idxs models.SvgIdInterface) ([]string,
|
||||
}
|
||||
|
||||
func (d *SqliteDB) getSvgsById(ids []int) ([]string, error) {
|
||||
tx, err := d.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selectId := `
|
||||
SELECT svg
|
||||
FROM svg_icon
|
||||
WHERE id = ?
|
||||
`
|
||||
svgs := make([]string, len(ids))
|
||||
for idx, id := range ids {
|
||||
rows, err := tx.Query(selectId, id)
|
||||
svg, err := d.queries.GetSvgId(d.ctx, int64(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !rows.Next() {
|
||||
log.Printf("id not found: %d", id)
|
||||
return nil, config.ErrSvgDne
|
||||
}
|
||||
if err = rows.Scan(&svgs[idx]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
svgs[idx] = svg
|
||||
}
|
||||
return svgs, nil
|
||||
}
|
||||
|
||||
func (d *SqliteDB) writeToDb(query string, args []any) error {
|
||||
tx, err := d.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = tx.Rollback()
|
||||
if err != nil {
|
||||
log.Fatalf("fatal error: write won't roll back %+v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if _, err = tx.Exec(query, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (d *SqliteDB) enqueueWriteTx(queryFunc sqlcGeneric, args any) error {
|
||||
select {
|
||||
case <-d.ctx.Done():
|
||||
return errors.New("database is shutting down")
|
||||
default:
|
||||
}
|
||||
|
||||
func (d *SqliteDB) addWriteTx(query string, args []any) error {
|
||||
if d.stop {
|
||||
return config.ErrStoppingDatabase
|
||||
}
|
||||
errChan := make(chan error)
|
||||
errChan := make(chan error, 1)
|
||||
writeTx := WriteTx{
|
||||
Query: query,
|
||||
Query: queryFunc,
|
||||
Args: args,
|
||||
ErrChan: errChan,
|
||||
}
|
||||
d.wg.Add(1)
|
||||
d.writeQueue <- writeTx
|
||||
return <-errChan
|
||||
}
|
||||
@@ -558,7 +478,3 @@ func (d *SqliteDB) getRandomIds(count int) ([]int, error) {
|
||||
|
||||
return perm[:count], nil
|
||||
}
|
||||
|
||||
func timeStamp() string {
|
||||
return time.Now().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package db
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/internal/api"
|
||||
"go-nkode/internal/entities"
|
||||
"go-nkode/internal/models"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
func TestNewSqliteDB(t *testing.T) {
|
||||
dbFile := os.Getenv("TEST_DB")
|
||||
// sql_driver.MakeTables(dbFile)
|
||||
db := NewSqliteDB(dbFile)
|
||||
defer db.CloseDb()
|
||||
db, err := NewSqliteDB(dbFile)
|
||||
assert.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
testSignupLoginRenew(t, db)
|
||||
testSqliteDBRandomSvgInterface(t, db)
|
||||
@@ -24,22 +25,22 @@ func TestNewSqliteDB(t *testing.T) {
|
||||
// }
|
||||
}
|
||||
|
||||
func testSignupLoginRenew(t *testing.T, db api.DbAccessor) {
|
||||
func testSignupLoginRenew(t *testing.T, db CustomerUserRepository) {
|
||||
nkodePolicy := models.NewDefaultNKodePolicy()
|
||||
customerOrig, err := models.NewCustomer(nkodePolicy)
|
||||
customerOrig, err := entities.NewCustomer(nkodePolicy)
|
||||
assert.NoError(t, err)
|
||||
err = db.WriteNewCustomer(*customerOrig)
|
||||
err = db.CreateCustomer(*customerOrig)
|
||||
assert.NoError(t, err)
|
||||
customer, err := db.GetCustomer(customerOrig.Id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, customerOrig, customer)
|
||||
username := "test_user@example.com"
|
||||
kp := models.KeypadDefault
|
||||
kp := entities.KeypadDefault
|
||||
passcodeIdx := []int{0, 1, 2, 3}
|
||||
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
ui, err := models.NewUserInterface(&kp, mockSvgInterface)
|
||||
ui, err := entities.NewUserInterface(&kp, mockSvgInterface)
|
||||
assert.NoError(t, err)
|
||||
userOrig, err := models.NewUser(*customer, username, passcodeIdx, *ui, kp)
|
||||
userOrig, err := entities.NewUser(*customer, username, passcodeIdx, *ui, kp)
|
||||
assert.NoError(t, err)
|
||||
err = db.WriteNewUser(*userOrig)
|
||||
assert.NoError(t, err)
|
||||
@@ -52,8 +53,8 @@ func testSignupLoginRenew(t *testing.T, db api.DbAccessor) {
|
||||
|
||||
}
|
||||
|
||||
func testSqliteDBRandomSvgInterface(t *testing.T, db api.DbAccessor) {
|
||||
kp := models.KeypadMax
|
||||
func testSqliteDBRandomSvgInterface(t *testing.T, db CustomerUserRepository) {
|
||||
kp := entities.KeypadMax
|
||||
svgs, err := db.RandomSvgInterface(kp)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, svgs, kp.TotalAttrs())
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type EmailClient interface {
|
||||
type Client interface {
|
||||
SendEmail(Email) error
|
||||
}
|
||||
|
||||
@@ -103,22 +103,22 @@ func (s *SESClient) SendEmail(email Email) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EmailQueue represents the email queue with rate limiting
|
||||
type EmailQueue struct {
|
||||
// Queue represents the email queue with rate limiting
|
||||
type Queue struct {
|
||||
stop bool
|
||||
emailQueue chan Email // Email queue
|
||||
rateLimit <-chan time.Time // Rate limiter
|
||||
client EmailClient // SES client to send emails
|
||||
client Client // SES client to send emails
|
||||
wg sync.WaitGroup // To wait for all emails to be processed
|
||||
FailedSendCount int
|
||||
}
|
||||
|
||||
// NewEmailQueue creates a new rate-limited email queue
|
||||
func NewEmailQueue(bufferSize int, emailsPerSecond int, client EmailClient) *EmailQueue {
|
||||
func NewEmailQueue(bufferSize int, emailsPerSecond int, client Client) *Queue {
|
||||
// Create a ticker that ticks every second to limit the rate of sending emails
|
||||
rateLimit := time.Tick(time.Second / time.Duration(emailsPerSecond))
|
||||
|
||||
return &EmailQueue{
|
||||
return &Queue{
|
||||
stop: false,
|
||||
emailQueue: make(chan Email, bufferSize),
|
||||
rateLimit: rateLimit,
|
||||
@@ -128,7 +128,7 @@ func NewEmailQueue(bufferSize int, emailsPerSecond int, client EmailClient) *Ema
|
||||
}
|
||||
|
||||
// AddEmail queues a new email to be sent
|
||||
func (q *EmailQueue) AddEmail(email Email) {
|
||||
func (q *Queue) AddEmail(email Email) {
|
||||
if q.stop {
|
||||
log.Printf("email %s with subject %s not add. Stopping queue", email.Recipient, email.Subject)
|
||||
return
|
||||
@@ -138,7 +138,7 @@ func (q *EmailQueue) AddEmail(email Email) {
|
||||
}
|
||||
|
||||
// Start begins processing the email queue with rate limiting
|
||||
func (q *EmailQueue) Start() {
|
||||
func (q *Queue) Start() {
|
||||
q.stop = false
|
||||
// Worker goroutine that processes emails from the queue
|
||||
go func() {
|
||||
@@ -151,7 +151,7 @@ func (q *EmailQueue) Start() {
|
||||
}
|
||||
|
||||
// sendEmail sends an email using the SES client
|
||||
func (q *EmailQueue) sendEmail(email Email) {
|
||||
func (q *Queue) sendEmail(email Email) {
|
||||
if err := q.client.SendEmail(email); err != nil {
|
||||
q.FailedSendCount += 1
|
||||
log.Printf("Failed to send email to %s: %v\n", email.Recipient, err)
|
||||
@@ -159,7 +159,7 @@ func (q *EmailQueue) sendEmail(email Email) {
|
||||
}
|
||||
|
||||
// Stop stops the queue after all emails have been processed
|
||||
func (q *EmailQueue) Stop() {
|
||||
func (q *Queue) Stop() {
|
||||
q.stop = true
|
||||
// Wait for all emails to be processed
|
||||
q.wg.Wait()
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestEmailQueue(t *testing.T) {
|
||||
}
|
||||
queue.AddEmail(email)
|
||||
}
|
||||
// CloseDb the queue after all emails are processed
|
||||
// Close the queue after all emails are processed
|
||||
queue.Stop()
|
||||
|
||||
assert.Equal(t, queue.FailedSendCount, 0)
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"go-nkode/internal/sqlc"
|
||||
"go-nkode/internal/utils"
|
||||
)
|
||||
|
||||
type Customer struct {
|
||||
Id CustomerId
|
||||
NKodePolicy NKodePolicy
|
||||
Id models.CustomerId
|
||||
NKodePolicy models.NKodePolicy
|
||||
Attributes CustomerAttributes
|
||||
}
|
||||
|
||||
func NewCustomer(nkodePolicy NKodePolicy) (*Customer, error) {
|
||||
func NewCustomer(nkodePolicy models.NKodePolicy) (*Customer, error) {
|
||||
customerAttrs, err := NewCustomerAttributes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
customer := Customer{
|
||||
Id: CustomerId(uuid.New()),
|
||||
Id: models.CustomerId(uuid.New()),
|
||||
NKodePolicy: nkodePolicy,
|
||||
Attributes: *customerAttrs,
|
||||
}
|
||||
@@ -82,3 +84,19 @@ func (c *Customer) RenewKeys() ([]uint64, []uint64, error) {
|
||||
}
|
||||
return setXor, attrsXor, nil
|
||||
}
|
||||
|
||||
func (c *Customer) ToSqlcCreateCustomerParams() sqlc.CreateCustomerParams {
|
||||
return sqlc.CreateCustomerParams{
|
||||
ID: uuid.UUID(c.Id).String(),
|
||||
MaxNkodeLen: int64(c.NKodePolicy.MaxNkodeLen),
|
||||
MinNkodeLen: int64(c.NKodePolicy.MinNkodeLen),
|
||||
DistinctSets: int64(c.NKodePolicy.DistinctSets),
|
||||
DistinctAttributes: int64(c.NKodePolicy.DistinctAttributes),
|
||||
LockOut: int64(c.NKodePolicy.LockOut),
|
||||
Expiration: int64(c.NKodePolicy.Expiration),
|
||||
AttributeValues: c.Attributes.AttrBytes(),
|
||||
SetValues: c.Attributes.SetBytes(),
|
||||
LastRenew: utils.TimeStamp(),
|
||||
CreatedAt: utils.TimeStamp(),
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"go-nkode/internal/security"
|
||||
@@ -1,7 +1,8 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/internal/models"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -18,10 +19,10 @@ func testNewCustomerAttributes(t *testing.T) {
|
||||
|
||||
func testCustomerValidKeyEntry(t *testing.T) {
|
||||
kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 9}
|
||||
nkodePolicy := NewDefaultNKodePolicy()
|
||||
nkodePolicy := models.NewDefaultNKodePolicy()
|
||||
customer, err := NewCustomer(nkodePolicy)
|
||||
assert.NoError(t, err)
|
||||
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
|
||||
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
|
||||
assert.NoError(t, err)
|
||||
userEmail := "testing@example.com"
|
||||
@@ -42,10 +43,10 @@ func testCustomerValidKeyEntry(t *testing.T) {
|
||||
|
||||
func testCustomerIsValidNKode(t *testing.T) {
|
||||
kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 7}
|
||||
nkodePolicy := NewDefaultNKodePolicy()
|
||||
nkodePolicy := models.NewDefaultNKodePolicy()
|
||||
customer, err := NewCustomer(nkodePolicy)
|
||||
assert.NoError(t, err)
|
||||
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
|
||||
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
|
||||
assert.NoError(t, err)
|
||||
userEmail := "testing123@example.com"
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"go-nkode/config"
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -1,17 +1,18 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"log"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id UserId
|
||||
CustomerId CustomerId
|
||||
Email UserEmail
|
||||
EncipheredPasscode EncipheredNKode
|
||||
Id models.UserId
|
||||
CustomerId models.CustomerId
|
||||
Email models.UserEmail
|
||||
EncipheredPasscode models.EncipheredNKode
|
||||
Kp KeypadDimension
|
||||
CipherKeys UserCipherKeys
|
||||
Interface UserInterface
|
||||
@@ -36,11 +37,13 @@ func (u *User) RenewKeys(setXor []uint64, attrXor []uint64) error {
|
||||
|
||||
func (u *User) RefreshPasscode(passcodeAttrIdx []int, customerAttributes CustomerAttributes) error {
|
||||
setVals, err := customerAttributes.SetValsForKp(u.Kp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newKeys, err := NewUserCipherKeys(&u.Kp, setVals, u.CipherKeys.MaxNKodeLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encipheredPasscode, err := newKeys.EncipherNKode(passcodeAttrIdx, customerAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -116,7 +119,7 @@ func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, err
|
||||
}
|
||||
|
||||
func NewUser(customer Customer, userEmail string, passcodeIdx []int, ui UserInterface, kp KeypadDimension) (*User, error) {
|
||||
_, err := ParseEmail(userEmail)
|
||||
_, err := models.ParseEmail(userEmail)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,8 +136,8 @@ func NewUser(customer Customer, userEmail string, passcodeIdx []int, ui UserInte
|
||||
return nil, err
|
||||
}
|
||||
newUser := User{
|
||||
Id: UserId(uuid.New()),
|
||||
Email: UserEmail(userEmail),
|
||||
Id: models.UserId(uuid.New()),
|
||||
Email: models.UserEmail(userEmail),
|
||||
EncipheredPasscode: *encipheredNKode,
|
||||
CipherKeys: *newKeys,
|
||||
Interface: ui,
|
||||
@@ -1,9 +1,10 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@@ -166,7 +167,7 @@ func (u *UserCipherKeys) DecipherMask(mask string, setVals []uint64, passcodeLen
|
||||
return passcodeSet, nil
|
||||
}
|
||||
|
||||
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*EncipheredNKode, error) {
|
||||
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*models.EncipheredNKode, error) {
|
||||
attrVals, err := customerAttrs.AttrValsForKp(*u.Kp)
|
||||
code, err := u.EncipherSaltHashCode(passcodeAttrIdx, attrVals)
|
||||
if err != nil {
|
||||
@@ -185,7 +186,7 @@ func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs Cust
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encipheredCode := EncipheredNKode{
|
||||
encipheredCode := models.EncipheredNKode{
|
||||
Code: code,
|
||||
Mask: mask,
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
"go-nkode/internal/utils"
|
||||
"log"
|
||||
)
|
||||
|
||||
type UserInterface struct {
|
||||
IdxInterface IdxInterface
|
||||
SvgId SvgIdInterface
|
||||
IdxInterface models.IdxInterface
|
||||
SvgId models.SvgIdInterface
|
||||
Kp *KeypadDimension
|
||||
}
|
||||
|
||||
func NewUserInterface(kp *KeypadDimension, svgId SvgIdInterface) (*UserInterface, error) {
|
||||
func NewUserInterface(kp *KeypadDimension, svgId models.SvgIdInterface) (*UserInterface, error) {
|
||||
idxInterface := security.IdentityArray(kp.TotalAttrs())
|
||||
userInterface := UserInterface{
|
||||
IdxInterface: idxInterface,
|
||||
@@ -1,8 +1,9 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"go-nkode/config"
|
||||
"go-nkode/internal/models"
|
||||
"go-nkode/internal/security"
|
||||
py "go-nkode/internal/utils"
|
||||
"log"
|
||||
@@ -10,20 +11,20 @@ import (
|
||||
)
|
||||
|
||||
type UserSignSession struct {
|
||||
Id SessionId
|
||||
CustomerId CustomerId
|
||||
Id models.SessionId
|
||||
CustomerId models.CustomerId
|
||||
LoginUserInterface UserInterface
|
||||
Kp KeypadDimension
|
||||
SetIdxInterface IdxInterface
|
||||
ConfirmIdxInterface IdxInterface
|
||||
SetKeySelection KeySelection
|
||||
UserEmail UserEmail
|
||||
SetIdxInterface models.IdxInterface
|
||||
ConfirmIdxInterface models.IdxInterface
|
||||
SetKeySelection models.KeySelection
|
||||
UserEmail models.UserEmail
|
||||
Reset bool
|
||||
Expire int
|
||||
Colors []RGBColor
|
||||
Colors []models.RGBColor
|
||||
}
|
||||
|
||||
func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId CustomerId, svgInterface SvgIdInterface, reset bool) (*UserSignSession, error) {
|
||||
func NewSignupResetSession(userEmail models.UserEmail, kp KeypadDimension, customerId models.CustomerId, svgInterface models.SvgIdInterface, reset bool) (*UserSignSession, error) {
|
||||
loginInterface, err := NewUserInterface(&kp, svgInterface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -33,7 +34,7 @@ func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId C
|
||||
return nil, err
|
||||
}
|
||||
session := UserSignSession{
|
||||
Id: SessionId(uuid.New()),
|
||||
Id: models.SessionId(uuid.New()),
|
||||
CustomerId: customerId,
|
||||
LoginUserInterface: *loginInterface,
|
||||
SetIdxInterface: signupInterface.IdxInterface,
|
||||
@@ -48,7 +49,7 @@ func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId C
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, error) {
|
||||
func (s *UserSignSession) DeducePasscode(confirmKeyEntry models.KeySelection) ([]int, error) {
|
||||
validEntry := py.All[int](confirmKeyEntry, func(i int) bool {
|
||||
return 0 <= i && i < s.Kp.NumbOfKeys
|
||||
})
|
||||
@@ -109,7 +110,7 @@ func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, e
|
||||
return passcode, nil
|
||||
}
|
||||
|
||||
func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface, error) {
|
||||
func (s *UserSignSession) SetUserNKode(keySelection models.KeySelection) (models.IdxInterface, error) {
|
||||
validKeySelection := py.All[int](keySelection, func(i int) bool {
|
||||
return 0 <= i && i < s.Kp.NumbOfKeys
|
||||
})
|
||||
@@ -129,7 +130,7 @@ func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface,
|
||||
return s.ConfirmIdxInterface, nil
|
||||
}
|
||||
|
||||
func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInterface []int) ([][]int, error) {
|
||||
func (s *UserSignSession) getSelectedKeyVals(keySelections models.KeySelection, userInterface []int) ([][]int, error) {
|
||||
signupKp := s.SignupKeypad()
|
||||
keypadInterface, err := security.ListToMatrix(userInterface, signupKp.AttrsPerKey)
|
||||
if err != nil {
|
||||
@@ -143,7 +144,7 @@ func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInt
|
||||
return keyVals, nil
|
||||
}
|
||||
|
||||
func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []RGBColor, error) {
|
||||
func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []models.RGBColor, error) {
|
||||
// This method randomly drops sets from the base user interface so it is a square and dispersable matrix
|
||||
if kp.IsDispersable() {
|
||||
return nil, nil, config.ErrKeypadIsNotDispersible
|
||||
@@ -170,11 +171,11 @@ func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*User
|
||||
setIdxs = setIdxs[:kp.NumbOfKeys]
|
||||
sort.Ints(setIdxs)
|
||||
selectedSets := make([][]int, kp.NumbOfKeys)
|
||||
selectedColors := make([]RGBColor, kp.NumbOfKeys)
|
||||
selectedColors := make([]models.RGBColor, kp.NumbOfKeys)
|
||||
|
||||
for idx, setIdx := range setIdxs {
|
||||
selectedSets[idx] = attrSetView[setIdx]
|
||||
selectedColors[idx] = SetColors[setIdx]
|
||||
selectedColors[idx] = models.SetColors[setIdx]
|
||||
}
|
||||
// convert set view back into key view
|
||||
selectedSets, err = security.MatrixTranspose(selectedSets)
|
||||
@@ -1,7 +1,8 @@
|
||||
package models
|
||||
package entities
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go-nkode/internal/models"
|
||||
py "go-nkode/internal/utils"
|
||||
"testing"
|
||||
)
|
||||
@@ -64,7 +65,7 @@ func TestUserInterface_RandomShuffle(t *testing.T) {
|
||||
AttrsPerKey: 10,
|
||||
NumbOfKeys: 8,
|
||||
}
|
||||
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
|
||||
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
|
||||
assert.NoError(t, err)
|
||||
userInterfaceCopy := make([]int, len(userInterface.IdxInterface))
|
||||
@@ -87,7 +88,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
|
||||
|
||||
for idx := 0; idx < 10000; idx++ {
|
||||
kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
|
||||
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
|
||||
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
|
||||
assert.NoError(t, err)
|
||||
preDispersion, err := userInterface.AttributeAdjacencyGraph()
|
||||
@@ -106,7 +107,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
|
||||
|
||||
func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
|
||||
kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
|
||||
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
|
||||
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
|
||||
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
|
||||
assert.NoError(t, err)
|
||||
preShuffle := userInterface.IdxInterface
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"net/mail"
|
||||
"strings"
|
||||
@@ -99,10 +100,17 @@ func CustomerIdToString(customerId CustomerId) string {
|
||||
type SessionId uuid.UUID
|
||||
type UserId uuid.UUID
|
||||
|
||||
func UserIdFromString(userId string) UserId {
|
||||
id, err := uuid.Parse(userId)
|
||||
if err != nil {
|
||||
fmt.Errorf("unable to parse user id %+v", err)
|
||||
}
|
||||
return UserId(id)
|
||||
}
|
||||
|
||||
func (s *SessionId) String() string {
|
||||
id := uuid.UUID(*s)
|
||||
return id.String()
|
||||
|
||||
}
|
||||
|
||||
type UserEmail string
|
||||
|
||||
31
internal/sqlc/db.go
Normal file
31
internal/sqlc/db.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
50
internal/sqlc/models.go
Normal file
50
internal/sqlc/models.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Customer struct {
|
||||
ID string
|
||||
MaxNkodeLen int64
|
||||
MinNkodeLen int64
|
||||
DistinctSets int64
|
||||
DistinctAttributes int64
|
||||
LockOut int64
|
||||
Expiration int64
|
||||
AttributeValues []byte
|
||||
SetValues []byte
|
||||
LastRenew string
|
||||
CreatedAt string
|
||||
}
|
||||
|
||||
type SvgIcon struct {
|
||||
ID int64
|
||||
Svg string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string
|
||||
Email string
|
||||
Renew int64
|
||||
RefreshToken sql.NullString
|
||||
CustomerID string
|
||||
Code string
|
||||
Mask string
|
||||
AttributesPerKey int64
|
||||
NumberOfKeys int64
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
PassKey []byte
|
||||
MaskKey []byte
|
||||
Salt []byte
|
||||
MaxNkodeLen int64
|
||||
IdxInterface []byte
|
||||
SvgIDInterface []byte
|
||||
LastLogin interface{}
|
||||
CreatedAt sql.NullString
|
||||
}
|
||||
478
internal/sqlc/query.sql.go
Normal file
478
internal/sqlc/query.sql.go
Normal file
@@ -0,0 +1,478 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: query.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createCustomer = `-- name: CreateCustomer :exec
|
||||
INSERT INTO customer (
|
||||
id
|
||||
,max_nkode_len
|
||||
,min_nkode_len
|
||||
,distinct_sets
|
||||
,distinct_attributes
|
||||
,lock_out
|
||||
,expiration
|
||||
,attribute_values
|
||||
,set_values
|
||||
,last_renew
|
||||
,created_at
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
`
|
||||
|
||||
type CreateCustomerParams struct {
|
||||
ID string
|
||||
MaxNkodeLen int64
|
||||
MinNkodeLen int64
|
||||
DistinctSets int64
|
||||
DistinctAttributes int64
|
||||
LockOut int64
|
||||
Expiration int64
|
||||
AttributeValues []byte
|
||||
SetValues []byte
|
||||
LastRenew string
|
||||
CreatedAt string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCustomer(ctx context.Context, arg CreateCustomerParams) error {
|
||||
_, err := q.db.ExecContext(ctx, createCustomer,
|
||||
arg.ID,
|
||||
arg.MaxNkodeLen,
|
||||
arg.MinNkodeLen,
|
||||
arg.DistinctSets,
|
||||
arg.DistinctAttributes,
|
||||
arg.LockOut,
|
||||
arg.Expiration,
|
||||
arg.AttributeValues,
|
||||
arg.SetValues,
|
||||
arg.LastRenew,
|
||||
arg.CreatedAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const createUser = `-- name: CreateUser :exec
|
||||
INSERT INTO user (
|
||||
id
|
||||
,email
|
||||
,renew
|
||||
,refresh_token
|
||||
,customer_id
|
||||
,code
|
||||
,mask
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
,alpha_key
|
||||
,set_key
|
||||
,pass_key
|
||||
,mask_key
|
||||
,salt
|
||||
,max_nkode_len
|
||||
,idx_interface
|
||||
,svg_id_interface
|
||||
,created_at
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
ID string
|
||||
Email string
|
||||
Renew int64
|
||||
RefreshToken sql.NullString
|
||||
CustomerID string
|
||||
Code string
|
||||
Mask string
|
||||
AttributesPerKey int64
|
||||
NumberOfKeys int64
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
PassKey []byte
|
||||
MaskKey []byte
|
||||
Salt []byte
|
||||
MaxNkodeLen int64
|
||||
IdxInterface []byte
|
||||
SvgIDInterface []byte
|
||||
CreatedAt sql.NullString
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, createUser,
|
||||
arg.ID,
|
||||
arg.Email,
|
||||
arg.Renew,
|
||||
arg.RefreshToken,
|
||||
arg.CustomerID,
|
||||
arg.Code,
|
||||
arg.Mask,
|
||||
arg.AttributesPerKey,
|
||||
arg.NumberOfKeys,
|
||||
arg.AlphaKey,
|
||||
arg.SetKey,
|
||||
arg.PassKey,
|
||||
arg.MaskKey,
|
||||
arg.Salt,
|
||||
arg.MaxNkodeLen,
|
||||
arg.IdxInterface,
|
||||
arg.SvgIDInterface,
|
||||
arg.CreatedAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCustomer = `-- name: GetCustomer :one
|
||||
SELECT
|
||||
max_nkode_len
|
||||
,min_nkode_len
|
||||
,distinct_sets
|
||||
,distinct_attributes
|
||||
,lock_out
|
||||
,expiration
|
||||
,attribute_values
|
||||
,set_values
|
||||
FROM customer
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
type GetCustomerRow struct {
|
||||
MaxNkodeLen int64
|
||||
MinNkodeLen int64
|
||||
DistinctSets int64
|
||||
DistinctAttributes int64
|
||||
LockOut int64
|
||||
Expiration int64
|
||||
AttributeValues []byte
|
||||
SetValues []byte
|
||||
}
|
||||
|
||||
func (q *Queries) GetCustomer(ctx context.Context, id string) (GetCustomerRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCustomer, id)
|
||||
var i GetCustomerRow
|
||||
err := row.Scan(
|
||||
&i.MaxNkodeLen,
|
||||
&i.MinNkodeLen,
|
||||
&i.DistinctSets,
|
||||
&i.DistinctAttributes,
|
||||
&i.LockOut,
|
||||
&i.Expiration,
|
||||
&i.AttributeValues,
|
||||
&i.SetValues,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getSvgCount = `-- name: GetSvgCount :one
|
||||
SELECT COUNT(*) as count FROM svg_icon
|
||||
`
|
||||
|
||||
func (q *Queries) GetSvgCount(ctx context.Context) (int64, error) {
|
||||
row := q.db.QueryRowContext(ctx, getSvgCount)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
const getSvgId = `-- name: GetSvgId :one
|
||||
SELECT svg
|
||||
FROM svg_icon
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetSvgId(ctx context.Context, id int64) (string, error) {
|
||||
row := q.db.QueryRowContext(ctx, getSvgId, id)
|
||||
var svg string
|
||||
err := row.Scan(&svg)
|
||||
return svg, err
|
||||
}
|
||||
|
||||
const getUser = `-- name: GetUser :one
|
||||
SELECT
|
||||
id
|
||||
,renew
|
||||
,refresh_token
|
||||
,code
|
||||
,mask
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
,alpha_key
|
||||
,set_key
|
||||
,pass_key
|
||||
,mask_key
|
||||
,salt
|
||||
,max_nkode_len
|
||||
,idx_interface
|
||||
,svg_id_interface
|
||||
FROM user
|
||||
WHERE user.email = ? AND user.customer_id = ?
|
||||
`
|
||||
|
||||
type GetUserParams struct {
|
||||
Email string
|
||||
CustomerID string
|
||||
}
|
||||
|
||||
type GetUserRow struct {
|
||||
ID string
|
||||
Renew int64
|
||||
RefreshToken sql.NullString
|
||||
Code string
|
||||
Mask string
|
||||
AttributesPerKey int64
|
||||
NumberOfKeys int64
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
PassKey []byte
|
||||
MaskKey []byte
|
||||
Salt []byte
|
||||
MaxNkodeLen int64
|
||||
IdxInterface []byte
|
||||
SvgIDInterface []byte
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, arg GetUserParams) (GetUserRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUser, arg.Email, arg.CustomerID)
|
||||
var i GetUserRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Renew,
|
||||
&i.RefreshToken,
|
||||
&i.Code,
|
||||
&i.Mask,
|
||||
&i.AttributesPerKey,
|
||||
&i.NumberOfKeys,
|
||||
&i.AlphaKey,
|
||||
&i.SetKey,
|
||||
&i.PassKey,
|
||||
&i.MaskKey,
|
||||
&i.Salt,
|
||||
&i.MaxNkodeLen,
|
||||
&i.IdxInterface,
|
||||
&i.SvgIDInterface,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserRenew = `-- name: GetUserRenew :many
|
||||
SELECT
|
||||
id
|
||||
,alpha_key
|
||||
,set_key
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
FROM user
|
||||
WHERE customer_id = ?
|
||||
`
|
||||
|
||||
type GetUserRenewRow struct {
|
||||
ID string
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
AttributesPerKey int64
|
||||
NumberOfKeys int64
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserRenew(ctx context.Context, customerID string) ([]GetUserRenewRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getUserRenew, customerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetUserRenewRow
|
||||
for rows.Next() {
|
||||
var i GetUserRenewRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.AlphaKey,
|
||||
&i.SetKey,
|
||||
&i.AttributesPerKey,
|
||||
&i.NumberOfKeys,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const refreshUserPasscode = `-- name: RefreshUserPasscode :exec
|
||||
UPDATE user
|
||||
SET
|
||||
renew = ?
|
||||
,code = ?
|
||||
,mask = ?
|
||||
,alpha_key = ?
|
||||
,set_key = ?
|
||||
,pass_key = ?
|
||||
,mask_key = ?
|
||||
,salt = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
type RefreshUserPasscodeParams struct {
|
||||
Renew int64
|
||||
Code string
|
||||
Mask string
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
PassKey []byte
|
||||
MaskKey []byte
|
||||
Salt []byte
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) RefreshUserPasscode(ctx context.Context, arg RefreshUserPasscodeParams) error {
|
||||
_, err := q.db.ExecContext(ctx, refreshUserPasscode,
|
||||
arg.Renew,
|
||||
arg.Code,
|
||||
arg.Mask,
|
||||
arg.AlphaKey,
|
||||
arg.SetKey,
|
||||
arg.PassKey,
|
||||
arg.MaskKey,
|
||||
arg.Salt,
|
||||
arg.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const renewCustomer = `-- name: RenewCustomer :exec
|
||||
UPDATE customer
|
||||
SET attribute_values = ?, set_values = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
type RenewCustomerParams struct {
|
||||
AttributeValues []byte
|
||||
SetValues []byte
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) RenewCustomer(ctx context.Context, arg RenewCustomerParams) error {
|
||||
_, err := q.db.ExecContext(ctx, renewCustomer, arg.AttributeValues, arg.SetValues, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const renewUser = `-- name: RenewUser :exec
|
||||
UPDATE user
|
||||
SET alpha_key = ?, set_key = ?, renew = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
type RenewUserParams struct {
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
Renew int64
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) RenewUser(ctx context.Context, arg RenewUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, renewUser,
|
||||
arg.AlphaKey,
|
||||
arg.SetKey,
|
||||
arg.Renew,
|
||||
arg.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUser = `-- name: UpdateUser :exec
|
||||
UPDATE user
|
||||
SET renew = ?
|
||||
,refresh_token = ?
|
||||
,code = ?
|
||||
,mask = ?
|
||||
,attributes_per_key = ?
|
||||
,number_of_keys = ?
|
||||
,alpha_key = ?
|
||||
,set_key = ?
|
||||
,pass_key = ?
|
||||
,mask_key = ?
|
||||
,salt = ?
|
||||
,max_nkode_len = ?
|
||||
,idx_interface = ?
|
||||
,svg_id_interface = ?
|
||||
WHERE email = ? AND customer_id = ?
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
Renew int64
|
||||
RefreshToken sql.NullString
|
||||
Code string
|
||||
Mask string
|
||||
AttributesPerKey int64
|
||||
NumberOfKeys int64
|
||||
AlphaKey []byte
|
||||
SetKey []byte
|
||||
PassKey []byte
|
||||
MaskKey []byte
|
||||
Salt []byte
|
||||
MaxNkodeLen int64
|
||||
IdxInterface []byte
|
||||
SvgIDInterface []byte
|
||||
Email string
|
||||
CustomerID string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateUser,
|
||||
arg.Renew,
|
||||
arg.RefreshToken,
|
||||
arg.Code,
|
||||
arg.Mask,
|
||||
arg.AttributesPerKey,
|
||||
arg.NumberOfKeys,
|
||||
arg.AlphaKey,
|
||||
arg.SetKey,
|
||||
arg.PassKey,
|
||||
arg.MaskKey,
|
||||
arg.Salt,
|
||||
arg.MaxNkodeLen,
|
||||
arg.IdxInterface,
|
||||
arg.SvgIDInterface,
|
||||
arg.Email,
|
||||
arg.CustomerID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUserInterface = `-- name: UpdateUserInterface :exec
|
||||
UPDATE user SET idx_interface = ?, last_login = ? WHERE id = ?
|
||||
`
|
||||
|
||||
type UpdateUserInterfaceParams struct {
|
||||
IdxInterface []byte
|
||||
LastLogin interface{}
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserInterface(ctx context.Context, arg UpdateUserInterfaceParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateUserInterface, arg.IdxInterface, arg.LastLogin, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUserRefreshToken = `-- name: UpdateUserRefreshToken :exec
|
||||
UPDATE user SET refresh_token = ? WHERE id = ?
|
||||
`
|
||||
|
||||
type UpdateUserRefreshTokenParams struct {
|
||||
RefreshToken sql.NullString
|
||||
ID string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserRefreshToken(ctx context.Context, arg UpdateUserRefreshTokenParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateUserRefreshToken, arg.RefreshToken, arg.ID)
|
||||
return err
|
||||
}
|
||||
7
internal/utils/timestamp.go
Normal file
7
internal/utils/timestamp.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package utils
|
||||
|
||||
import "time"
|
||||
|
||||
func TimeStamp() string {
|
||||
return time.Now().Format(time.RFC3339)
|
||||
}
|
||||
9
sqlc.yaml
Normal file
9
sqlc.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: "2"
|
||||
sql:
|
||||
- engine: "sqlite"
|
||||
queries: "./sqlite/query.sql"
|
||||
schema: "./sqlite/schema.sql"
|
||||
gen:
|
||||
go:
|
||||
package: "sqlc"
|
||||
out: "./internal/sqlc"
|
||||
136
sqlite/query.sql
Normal file
136
sqlite/query.sql
Normal file
@@ -0,0 +1,136 @@
|
||||
-- name: CreateCustomer :exec
|
||||
INSERT INTO customer (
|
||||
id
|
||||
,max_nkode_len
|
||||
,min_nkode_len
|
||||
,distinct_sets
|
||||
,distinct_attributes
|
||||
,lock_out
|
||||
,expiration
|
||||
,attribute_values
|
||||
,set_values
|
||||
,last_renew
|
||||
,created_at
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?);
|
||||
|
||||
-- name: CreateUser :exec
|
||||
INSERT INTO user (
|
||||
id
|
||||
,email
|
||||
,renew
|
||||
,refresh_token
|
||||
,customer_id
|
||||
,code
|
||||
,mask
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
,alpha_key
|
||||
,set_key
|
||||
,pass_key
|
||||
,mask_key
|
||||
,salt
|
||||
,max_nkode_len
|
||||
,idx_interface
|
||||
,svg_id_interface
|
||||
,created_at
|
||||
)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);
|
||||
|
||||
-- name: UpdateUser :exec
|
||||
UPDATE user
|
||||
SET renew = ?
|
||||
,refresh_token = ?
|
||||
,code = ?
|
||||
,mask = ?
|
||||
,attributes_per_key = ?
|
||||
,number_of_keys = ?
|
||||
,alpha_key = ?
|
||||
,set_key = ?
|
||||
,pass_key = ?
|
||||
,mask_key = ?
|
||||
,salt = ?
|
||||
,max_nkode_len = ?
|
||||
,idx_interface = ?
|
||||
,svg_id_interface = ?
|
||||
WHERE email = ? AND customer_id = ?;
|
||||
|
||||
-- name: UpdateUserInterface :exec
|
||||
UPDATE user SET idx_interface = ?, last_login = ? WHERE id = ?;
|
||||
|
||||
-- name: UpdateUserRefreshToken :exec
|
||||
UPDATE user SET refresh_token = ? WHERE id = ?;
|
||||
|
||||
-- name: RenewCustomer :exec
|
||||
UPDATE customer
|
||||
SET attribute_values = ?, set_values = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: RenewUser :exec
|
||||
UPDATE user
|
||||
SET alpha_key = ?, set_key = ?, renew = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: RefreshUserPasscode :exec
|
||||
UPDATE user
|
||||
SET
|
||||
renew = ?
|
||||
,code = ?
|
||||
,mask = ?
|
||||
,alpha_key = ?
|
||||
,set_key = ?
|
||||
,pass_key = ?
|
||||
,mask_key = ?
|
||||
,salt = ?
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: GetUserRenew :many
|
||||
SELECT
|
||||
id
|
||||
,alpha_key
|
||||
,set_key
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
FROM user
|
||||
WHERE customer_id = ?;
|
||||
|
||||
-- name: GetCustomer :one
|
||||
SELECT
|
||||
max_nkode_len
|
||||
,min_nkode_len
|
||||
,distinct_sets
|
||||
,distinct_attributes
|
||||
,lock_out
|
||||
,expiration
|
||||
,attribute_values
|
||||
,set_values
|
||||
FROM customer
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: GetUser :one
|
||||
SELECT
|
||||
id
|
||||
,renew
|
||||
,refresh_token
|
||||
,code
|
||||
,mask
|
||||
,attributes_per_key
|
||||
,number_of_keys
|
||||
,alpha_key
|
||||
,set_key
|
||||
,pass_key
|
||||
,mask_key
|
||||
,salt
|
||||
,max_nkode_len
|
||||
,idx_interface
|
||||
,svg_id_interface
|
||||
FROM user
|
||||
WHERE user.email = ? AND user.customer_id = ?;
|
||||
|
||||
-- name: GetSvgId :one
|
||||
SELECT svg
|
||||
FROM svg_icon
|
||||
WHERE id = ?;
|
||||
|
||||
-- name: GetSvgCount :one
|
||||
SELECT COUNT(*) as count FROM svg_icon;
|
||||
57
sqlite/schema.sql
Normal file
57
sqlite/schema.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
PRAGMA journal_mode=WAL;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS customer (
|
||||
id TEXT NOT NULL PRIMARY KEY
|
||||
,max_nkode_len INTEGER NOT NULL
|
||||
,min_nkode_len INTEGER NOT NULL
|
||||
,distinct_sets INTEGER NOT NULL
|
||||
,distinct_attributes INTEGER NOT NULL
|
||||
,lock_out INTEGER NOT NULL
|
||||
,expiration INTEGER NOT NULL
|
||||
,attribute_values BLOB NOT NULL
|
||||
,set_values BLOB NOT NULL
|
||||
,last_renew TEXT NOT NULL
|
||||
,created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user (
|
||||
id TEXT NOT NULL PRIMARY KEY
|
||||
,email TEXT NOT NULL
|
||||
-- first_name TEXT NOT NULL
|
||||
-- last_name TEXT NOT NULL
|
||||
,renew INT NOT NULL
|
||||
,refresh_token TEXT
|
||||
,customer_id TEXT NOT NULL
|
||||
|
||||
-- Enciphered Passcode
|
||||
,code TEXT NOT NULL
|
||||
,mask TEXT NOT NULL
|
||||
|
||||
-- Keypad Dimensions
|
||||
,attributes_per_key INT NOT NULL
|
||||
,number_of_keys INT NOT NULL
|
||||
|
||||
-- User Keys
|
||||
,alpha_key BLOB NOT NULL
|
||||
,set_key BLOB NOT NULL
|
||||
,pass_key BLOB NOT NULL
|
||||
,mask_key BLOB NOT NULL
|
||||
,salt BLOB NOT NULL
|
||||
,max_nkode_len INT NOT NULL
|
||||
|
||||
-- User Interface
|
||||
,idx_interface BLOB NOT NULL
|
||||
,svg_id_interface BLOB NOT NULL
|
||||
|
||||
,last_login TEXT NULL
|
||||
,created_at TEXT
|
||||
|
||||
,FOREIGN KEY (customer_id) REFERENCES customer(id)
|
||||
,UNIQUE(customer_id, email)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS svg_icon (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,svg TEXT NOT NULL
|
||||
);
|
||||
Reference in New Issue
Block a user