Compare commits
12 Commits
v1.1.1
...
OIDCSqlite
| Author | SHA1 | Date | |
|---|---|---|---|
| 28bfbb84ad | |||
| 4e61d7714e | |||
| 75d8250f72 | |||
| f853b22b43 | |||
| f421249943 | |||
| 6289faf1ba | |||
| d58c69cb08 | |||
| 72414bc8fb | |||
| 9ee27f14cf | |||
| 6dd84e4ca3 | |||
| 597532bf26 | |||
| 44bede14e4 |
@@ -1,6 +1,14 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
test_db: "~/databases/test.db"
|
||||||
|
schema_db: "./sqlite/schema.sql"
|
||||||
tasks:
|
tasks:
|
||||||
sql:
|
sqlc:
|
||||||
cmds:
|
cmds:
|
||||||
- sqlc generate
|
- sqlc generate
|
||||||
|
|
||||||
|
rebuild_test_db:
|
||||||
|
cmds:
|
||||||
|
- rm {{.test_db}}
|
||||||
|
- sqlite3 {{.test_db}} < {{.schema_db}}
|
||||||
|
|||||||
@@ -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,14 +32,12 @@ 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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy entities.NKodePolicy, id *entities.CustomerId) (*entities.CustomerId, error) {
|
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy entities.NKodePolicy) (*entities.CustomerId, error) {
|
||||||
newCustomer, err := entities.NewCustomer(nkodePolicy)
|
newCustomer, err := entities.NewCustomer(nkodePolicy)
|
||||||
if id != nil {
|
|
||||||
newCustomer.Id = *id
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,18 @@ func (n *NKodeAPI) CreateNewCustomer(nkodePolicy entities.NKodePolicy, id *entit
|
|||||||
return &newCustomer.Id, nil
|
return &newCustomer.Id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NKodeAPI) CreateCustomerWithID(id entities.CustomerId, nkodePolicy entities.NKodePolicy) error {
|
||||||
|
newCustomer, err := entities.NewCustomer(nkodePolicy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newCustomer.Id = id
|
||||||
|
if err = n.repo.CreateCustomer(*newCustomer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, customerId entities.CustomerId, kp entities.KeypadDimension, reset bool) (*entities.SignupResetInterface, error) {
|
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, customerId entities.CustomerId, kp entities.KeypadDimension, reset bool) (*entities.SignupResetInterface, error) {
|
||||||
user, err := n.repo.GetUser(userEmail, customerId)
|
user, err := n.repo.GetUser(userEmail, customerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,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
|
||||||
}
|
}
|
||||||
@@ -237,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)
|
||||||
@@ -247,7 +258,7 @@ func (n *NKodeAPI) ResetNKode(userEmail entities.UserEmail, customerId entities.
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
nkodeResetJwt, err := security.ResetNKodeToken(string(userEmail), uuid.UUID(customerId))
|
nkodeResetJwt, err := security.ResetNKodeToken(string(userEmail), uuid.UUID(customerId).String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -263,5 +274,21 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NKodeAPI) Signout(userEmail entities.UserEmail, customerId entities.CustomerId) error {
|
||||||
|
user, err := n.repo.GetUser(userEmail, customerId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
log.Printf("user %s for customer %s dne", userEmail, customerId)
|
||||||
|
return config.ErrUserForCustomerDNE
|
||||||
|
}
|
||||||
|
if err = n.repo.UpdateUserRefreshToken(user.Id, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ func TestNKodeAPI(t *testing.T) {
|
|||||||
|
|
||||||
dbPath := os.Getenv("TEST_DB")
|
dbPath := os.Getenv("TEST_DB")
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
sqlitedb, err := repository.NewSqliteRepository(dbPath, ctx)
|
sqlitedb, err := repository.NewSqliteNKodeRepo(ctx, dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
sqlitedb.Start()
|
sqlitedb.Start()
|
||||||
defer func(sqldb *repository.SqliteRepository) {
|
defer func(sqldb *repository.SqliteNKodeRepo) {
|
||||||
if err := sqldb.Stop(); err != nil {
|
if err := sqldb.Stop(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,12 @@ func testNKodeAPI(t *testing.T, db repository.CustomerUserRepository) {
|
|||||||
attrsPerKey := 5
|
attrsPerKey := 5
|
||||||
numbOfKeys := 4
|
numbOfKeys := 4
|
||||||
for idx := 0; idx < 1; idx++ {
|
for idx := 0; idx < 1; idx++ {
|
||||||
userEmail := entities.UserEmail("test_username" + security.GenerateRandomString(12) + "@example.com")
|
userEmail := entities.UserEmail("test_username" + security.GenerateNonSecureRandomString(12) + "@example.com")
|
||||||
passcodeLen := 4
|
passcodeLen := 4
|
||||||
nkodePolicy := entities.NewDefaultNKodePolicy()
|
nkodePolicy := entities.NewDefaultNKodePolicy()
|
||||||
keypadSize := entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
keypadSize := entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
|
||||||
nkodeApi := NewNKodeAPI(db, queue)
|
nkodeApi := NewNKodeAPI(db, queue)
|
||||||
customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy, nil)
|
customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
signupResponse, err := nkodeApi.GenerateSignupResetInterface(userEmail, *customerId, keypadSize, false)
|
signupResponse, err := nkodeApi.GenerateSignupResetInterface(userEmail, *customerId, keypadSize, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
sqliteRepo, err := repository.NewSqliteRepository(*dbPath, ctx)
|
sqliteRepo, err := repository.NewSqliteNKodeRepo(ctx, *dbPath)
|
||||||
sqliteRepo.Start()
|
sqliteRepo.Start()
|
||||||
defer func(sqliteRepo *repository.SqliteRepository) {
|
defer func(sqliteRepo *repository.SqliteNKodeRepo) {
|
||||||
if err := sqliteRepo.Stop(); err != nil {
|
if err := sqliteRepo.Stop(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ func main() {
|
|||||||
log.Println(fmt.Sprintf("Successfully added all SVGs in %s to the database at %s\n", *svgPath, *dbPath))
|
log.Println(fmt.Sprintf("Successfully added all SVGs in %s to the database at %s\n", *svgPath, *dbPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FlaticonToSqlite(repo *repository.SqliteRepository, svgDir string) {
|
func FlaticonToSqlite(repo *repository.SqliteNKodeRepo, svgDir string) {
|
||||||
files, err := os.ReadDir(svgDir)
|
files, err := os.ReadDir(svgDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,19 @@ type KeySelection []int
|
|||||||
|
|
||||||
type CustomerId uuid.UUID
|
type CustomerId uuid.UUID
|
||||||
|
|
||||||
func CustomerIdToString(customerId CustomerId) string {
|
func (c *CustomerId) String() string {
|
||||||
customerUuid := uuid.UUID(customerId)
|
id := uuid.UUID(*c)
|
||||||
return customerUuid.String()
|
return id.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionId uuid.UUID
|
type SessionId uuid.UUID
|
||||||
type UserId uuid.UUID
|
type UserId uuid.UUID
|
||||||
|
|
||||||
|
func (u *UserId) String() string {
|
||||||
|
id := uuid.UUID(*u)
|
||||||
|
return id.String()
|
||||||
|
}
|
||||||
|
|
||||||
func UserIdFromString(userId string) UserId {
|
func UserIdFromString(userId string) UserId {
|
||||||
id, err := uuid.Parse(userId)
|
id, err := uuid.Parse(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -99,3 +104,14 @@ type LoginInterface struct {
|
|||||||
NumbOfKeys int `json:"numb_of_keys"`
|
NumbOfKeys int `json:"numb_of_keys"`
|
||||||
Colors []RGBColor `json:"colors"`
|
Colors []RGBColor `json:"colors"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserPermission int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Default UserPermission = iota
|
||||||
|
Admin
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p UserPermission) String() string {
|
||||||
|
return [...]string{"Default", "Admin"}[p]
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"`
|
MaxNkodeLen int `form:"max_nkode_len"`
|
||||||
MinNkodeLen int `json:"min_nkode_len"`
|
MinNkodeLen int `form:"min_nkode_len"`
|
||||||
DistinctSets int `json:"distinct_sets"`
|
DistinctSets int `form:"distinct_sets"`
|
||||||
DistinctAttributes int `json:"distinct_attributes"`
|
DistinctAttributes int `form:"distinct_attributes"`
|
||||||
LockOut int `json:"lock_out"`
|
LockOut int `form:"lock_out"`
|
||||||
Expiration int `json:"expiration"` // seconds, -1 no expiration
|
Expiration int `form:"expiration"` // seconds, -1 no expiration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultNKodePolicy() NKodePolicy {
|
func NewDefaultNKodePolicy() NKodePolicy {
|
||||||
|
|||||||
@@ -123,11 +123,4 @@ func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
|
|||||||
return n == true
|
return n == true
|
||||||
})
|
})
|
||||||
assert.False(t, allTrue)
|
assert.False(t, allTrue)
|
||||||
|
|
||||||
allFalse := all.All[bool](shuffleCompare, func(n bool) bool {
|
|
||||||
return n == false
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.False(t, allFalse)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
go.mod
25
go.mod
@@ -27,8 +27,33 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect
|
||||||
github.com/aws/smithy-go v1.22.1 // indirect
|
github.com/aws/smithy-go v1.22.1 // indirect
|
||||||
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.10.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
|
golang.org/x/net v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
70
go.sum
70
go.sum
@@ -28,31 +28,101 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 h1:BRVDbewN6VZcwr+FBOszDKvYeXY1
|
|||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.9/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.9/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw=
|
||||||
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
|
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
|
||||||
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|||||||
401
handler/handler.go
Normal file
401
handler/handler.go
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/api"
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/config"
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/models"
|
||||||
|
"git.infra.nkode.tech/dkelly/nkode-core/security"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NkodeHandler struct {
|
||||||
|
API api.NKodeAPI
|
||||||
|
Logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
malformedCustomerId = "malformed customer id"
|
||||||
|
malformedUserEmail = "malformed user email"
|
||||||
|
malformedSessionId = "malformed session id"
|
||||||
|
invalidKeypadDimensions = "invalid keypad dimensions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *NkodeHandler) RegisterRoutes(r *gin.Engine) {
|
||||||
|
r.Group("/v1/nkode")
|
||||||
|
{
|
||||||
|
// r.POST("/create-new-customer", h.CreateNewCustomerHandler)
|
||||||
|
r.POST("/signup", h.SignupHandler)
|
||||||
|
r.POST("/set-nkode", h.SetNKodeHandler)
|
||||||
|
r.POST("/confirm-nkode", h.ConfirmNKodeHandler)
|
||||||
|
r.POST("/get-login-interface", h.GetLoginInterfaceHandler)
|
||||||
|
r.POST("/login", h.LoginHandler)
|
||||||
|
r.POST("/renew-attributes", h.RenewAttributesHandler)
|
||||||
|
r.POST("/random-svg-interface", h.RandomSvgInterfaceHandler)
|
||||||
|
r.POST("/refresh-token", h.RefreshTokenHandler)
|
||||||
|
r.POST("/forgot-nkode", h.ForgotNKodeHandler)
|
||||||
|
r.POST("/signout", h.SignoutHandler)
|
||||||
|
r.POST("/reset-nkode", h.ResetHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) CreateNewCustomerHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("create new customer")
|
||||||
|
|
||||||
|
var newCustomerPost entities.NKodePolicy
|
||||||
|
if err := c.ShouldBind(&newCustomerPost); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customerId, err := h.API.CreateNewCustomer(newCustomerPost)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, "Internal Server Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.Logger.Println("create new customer")
|
||||||
|
c.JSON(200, gin.H{"customer_id": customerId.String()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) SignupHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("generate signup reset interface")
|
||||||
|
var postBody models.SignupPostBody
|
||||||
|
if err := c.ShouldBind(&postBody); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
kp := entities.KeypadDimension{
|
||||||
|
AttrsPerKey: postBody.AttrsPerKey,
|
||||||
|
NumbOfKeys: postBody.NumbOfKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := kp.IsValidKeypadDimension(); err != nil {
|
||||||
|
c.String(400, invalidKeypadDimensions)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.API.GenerateSignupResetInterface(userEmail, entities.CustomerId(customerId), kp, false)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) SetNKodeHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("set nkode")
|
||||||
|
var postBody models.SetNKodePost
|
||||||
|
if err := c.ShouldBindJSON(&postBody); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(postBody.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessionId, err := uuid.Parse(postBody.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedSessionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmInterface, err := h.API.SetNKode(entities.CustomerId(customerId), entities.SessionId(sessionId), postBody.KeySelection)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := models.SetNKodeResp{UserInterface: confirmInterface}
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) ConfirmNKodeHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("confirm nkode")
|
||||||
|
var postBody models.ConfirmNKodePost
|
||||||
|
if err := c.ShouldBindJSON(&postBody); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(postBody.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessionId, err := uuid.Parse(postBody.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedSessionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := h.API.ConfirmNKode(entities.CustomerId(customerId), entities.SessionId(sessionId), postBody.KeySelection); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) GetLoginInterfaceHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("get login interface")
|
||||||
|
var loginInterfacePost models.LoginInterfacePost
|
||||||
|
if err := c.ShouldBind(&loginInterfacePost); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customerId, err := uuid.Parse(loginInterfacePost.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userEmail, err := entities.ParseEmail(loginInterfacePost.UserEmail)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedUserEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
postBody, err := h.API.GetLoginInterface(userEmail, entities.CustomerId(customerId))
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, postBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) LoginHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("login")
|
||||||
|
|
||||||
|
var loginPost models.LoginPost
|
||||||
|
if err := c.ShouldBindJSON(&loginPost); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(loginPost.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userEmail, err := entities.ParseEmail(loginPost.UserEmail)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedUserEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jwtToken, err := h.API.Login(entities.CustomerId(customerId), userEmail, loginPost.KeySelection)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, jwtToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) RenewAttributesHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("renew attributes")
|
||||||
|
|
||||||
|
var renewAttributesPost models.RenewAttributesPost
|
||||||
|
if err := c.ShouldBind(&renewAttributesPost); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customerId, err := uuid.Parse(renewAttributesPost.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.API.RenewAttributes(entities.CustomerId(customerId)); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) RandomSvgInterfaceHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("random svg interface")
|
||||||
|
svgs, err := h.API.RandomSvgInterface()
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := models.RandomSvgInterfaceResp{Svgs: svgs}
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) RefreshTokenHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("refresh token")
|
||||||
|
refreshToken, err := getBearerToken(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(403, "forbidden")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
refreshClaims, err := security.ParseRegisteredClaimToken(refreshToken)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, "Internal Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(refreshClaims.Issuer)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userEmail, err := entities.ParseEmail(refreshClaims.Subject)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedUserEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
accessToken, err := h.API.RefreshToken(userEmail, entities.CustomerId(customerId), refreshToken)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, gin.H{"access_token": accessToken})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) ForgotNKodeHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("forgot nkode")
|
||||||
|
var forgotNKodePost models.ForgotNKodePost
|
||||||
|
if err := c.ShouldBind(&forgotNKodePost); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(forgotNKodePost.CustomerId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userEmail, err := entities.ParseEmail(forgotNKodePost.UserEmail)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedUserEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.API.ForgotNKode(userEmail, entities.CustomerId(customerId)); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Status(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NkodeHandler) SignoutHandler(c *gin.Context) {
|
||||||
|
h.Logger.Println("signout")
|
||||||
|
token, err := getBearerToken(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(403, "forbidden")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
accessClaims, err := security.ParseRegisteredClaimToken(token)
|
||||||
|
if err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customerId, err := uuid.Parse(accessClaims.Issuer)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedCustomerId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userEmail, err := entities.ParseEmail(accessClaims.Subject)
|
||||||
|
if err != nil {
|
||||||
|
c.String(400, malformedUserEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = h.API.Signout(userEmail, entities.CustomerId(customerId)); err != nil {
|
||||||
|
handleError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
log.Print("handling error: ", err)
|
||||||
|
statusCode, _ := config.HttpErrMap[err]
|
||||||
|
switch statusCode {
|
||||||
|
case 400:
|
||||||
|
c.String(400, err.Error())
|
||||||
|
case 403:
|
||||||
|
c.String(403, err.Error())
|
||||||
|
case 500:
|
||||||
|
c.String(500, "Internal Server Error")
|
||||||
|
default:
|
||||||
|
log.Print("unknown error: ", err)
|
||||||
|
c.String(500, "Internal Server Error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBearerToken(c *gin.Context) (string, error) {
|
||||||
|
authHeader := c.GetHeader("Authorization")
|
||||||
|
// Check if the Authorization header is present and starts with "Bearer "
|
||||||
|
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
|
||||||
|
return "", errors.New("forbidden")
|
||||||
|
}
|
||||||
|
token := strings.TrimPrefix(authHeader, "Bearer ")
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
364
handler/handler_test.go
Normal file
364
handler/handler_test.go
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
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 with invalid access token ***
|
||||||
|
customerID, err := tr.CreateNewCustomerDefaultPolicy()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
attrPerKey := 9
|
||||||
|
numKeys := 6
|
||||||
|
userEmail := "test_username" + security.GenerateNonSecureRandomString(12) + "@example.com"
|
||||||
|
|
||||||
|
// *** Signup ***
|
||||||
|
resp, status, err := tr.Signup(customerID, attrPerKey, numKeys, userEmail)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
|
||||||
|
passcodeLen := 4
|
||||||
|
userPasscode := resp.UserIdxInterface[:passcodeLen]
|
||||||
|
kpSet := entities.KeypadDimension{
|
||||||
|
AttrsPerKey: numKeys,
|
||||||
|
NumbOfKeys: numKeys,
|
||||||
|
}
|
||||||
|
setKeySelection, err := entities.SelectKeyByAttrIdx(resp.UserIdxInterface, userPasscode, kpSet)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// *** Set nKode ***
|
||||||
|
confirmInterface, status, err := tr.SetNKode(customerID, setKeySelection, resp.SessionId)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
|
||||||
|
confirmKeySelection, err := entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// *** Confirm nKode ***
|
||||||
|
status, err = tr.ConfirmNKode(customerID, confirmKeySelection, resp.SessionId)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
|
||||||
|
// *** Get Login Interface ***
|
||||||
|
loginInterface, status, err := tr.GetLoginInterface(userEmail, customerID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
kp := entities.KeypadDimension{
|
||||||
|
AttrsPerKey: attrPerKey,
|
||||||
|
NumbOfKeys: numKeys,
|
||||||
|
}
|
||||||
|
loginKeySelection, err := entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// *** Login ***
|
||||||
|
tokens, status, err := tr.Login(customerID, userEmail, loginKeySelection)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
assert.NotEmpty(t, tokens.AccessToken)
|
||||||
|
assert.NotEmpty(t, tokens.RefreshToken)
|
||||||
|
|
||||||
|
// *** Renew Attributes ***
|
||||||
|
status, err = tr.RenewAttributes(customerID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
loginInterface, status, err = tr.GetLoginInterface(userEmail, customerID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
tokens, status, err = tr.Login(customerID, userEmail, loginKeySelection)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// *** Test Forgot nKode ***
|
||||||
|
status, err = tr.ForgotNKode(customerID, userEmail)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
|
||||||
|
// *** Test Reset nKode ***
|
||||||
|
nkodeResetJwt, err := security.ResetNKodeToken(userEmail, customerID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
resetResp, status, err := tr.Reset(customerID, attrPerKey, numKeys, userEmail, nkodeResetJwt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
assert.NotEmpty(t, resetResp.SessionId)
|
||||||
|
userPasscode = resetResp.UserIdxInterface[:passcodeLen]
|
||||||
|
setKeySelection, err = entities.SelectKeyByAttrIdx(resetResp.UserIdxInterface, userPasscode, kpSet)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
confirmInterface, status, err = tr.SetNKode(customerID, setKeySelection, resetResp.SessionId)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
confirmKeySelection, err = entities.SelectKeyByAttrIdx(confirmInterface, userPasscode, kpSet)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
status, err = tr.ConfirmNKode(customerID, confirmKeySelection, resetResp.SessionId)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
loginInterface, status, err = tr.GetLoginInterface(userEmail, customerID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
loginKeySelection, err = entities.SelectKeyByAttrIdx(loginInterface.UserIdxInterface, userPasscode, kp)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
tokens, status, err = tr.Login(customerID, userEmail, loginKeySelection)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, status)
|
||||||
|
assert.NotEmpty(t, tokens.AccessToken)
|
||||||
|
assert.NotEmpty(t, tokens.RefreshToken)
|
||||||
|
|
||||||
|
// *** Test Reset nKode with invalid token ***
|
||||||
|
_, status, err = tr.Reset(customerID, attrPerKey, numKeys, userEmail, "invalid token")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 403, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestRouter struct {
|
||||||
|
router *gin.Engine
|
||||||
|
emailQueue *email.Queue
|
||||||
|
repo *repository.SqliteNKodeRepo
|
||||||
|
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.NewSqliteNKodeRepo(ctx, dbPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
emailClient := email.TestEmailClient{}
|
||||||
|
emailQueue := email.NewEmailQueue(email.EmailQueueBufferSize, email.MaxEmailsPerSecond, &emailClient)
|
||||||
|
nkodeAPI := api.NewNKodeAPI(repo, emailQueue)
|
||||||
|
h := NkodeHandler{
|
||||||
|
API: nkodeAPI,
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
|
h.RegisterRoutes(router)
|
||||||
|
return &TestRouter{
|
||||||
|
handler: &h,
|
||||||
|
router: router,
|
||||||
|
emailQueue: emailQueue,
|
||||||
|
repo: repo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) Start() {
|
||||||
|
r.repo.Start()
|
||||||
|
r.emailQueue.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) Stop() error {
|
||||||
|
r.emailQueue.Stop()
|
||||||
|
return r.repo.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) CreateNewCustomerDefaultPolicy() (string, error) {
|
||||||
|
customerId, err := r.handler.API.CreateNewCustomer(entities.NewDefaultNKodePolicy())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return customerId.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) Signup(
|
||||||
|
customerID string,
|
||||||
|
attrsPerKey int,
|
||||||
|
numberOfKeys int,
|
||||||
|
userEmail string,
|
||||||
|
) (*entities.SignupResetInterface, int, error) {
|
||||||
|
body := bytes.NewBufferString(fmt.Sprintf(
|
||||||
|
"customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s",
|
||||||
|
customerID,
|
||||||
|
attrsPerKey,
|
||||||
|
numberOfKeys,
|
||||||
|
userEmail,
|
||||||
|
))
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/signup", body)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
var resp entities.SignupResetInterface
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
return nil, rec.Code, err
|
||||||
|
}
|
||||||
|
return &resp, rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) SetNKode(
|
||||||
|
customerID string,
|
||||||
|
selection []int,
|
||||||
|
sessionID string,
|
||||||
|
) ([]int, int, error) {
|
||||||
|
data := models.SetNKodePost{
|
||||||
|
CustomerId: customerID,
|
||||||
|
KeySelection: selection,
|
||||||
|
SessionId: sessionID,
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/set-nkode", bytes.NewBuffer(body))
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
var resp struct {
|
||||||
|
UserInterface []int `json:"user_interface"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
return nil, rec.Code, err
|
||||||
|
}
|
||||||
|
return resp.UserInterface, rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) ConfirmNKode(
|
||||||
|
customerID string,
|
||||||
|
selection entities.KeySelection,
|
||||||
|
sessionID string,
|
||||||
|
) (int, error) {
|
||||||
|
data := models.ConfirmNKodePost{
|
||||||
|
CustomerId: customerID,
|
||||||
|
KeySelection: selection,
|
||||||
|
SessionId: sessionID,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/confirm-nkode", bytes.NewBuffer(body))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
return rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) GetLoginInterface(
|
||||||
|
userEmail string,
|
||||||
|
customerID string,
|
||||||
|
) (entities.LoginInterface, int, error) {
|
||||||
|
body := bytes.NewBufferString(fmt.Sprintf(
|
||||||
|
"email=%s&customer_id=%s",
|
||||||
|
userEmail,
|
||||||
|
customerID,
|
||||||
|
))
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/get-login-interface", body)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
var resp entities.LoginInterface
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
return entities.LoginInterface{}, rec.Code, err
|
||||||
|
}
|
||||||
|
return resp, rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) Login(
|
||||||
|
customerID string,
|
||||||
|
userEmail string,
|
||||||
|
selection []int,
|
||||||
|
) (security.AuthenticationTokens, int, error) {
|
||||||
|
data := models.LoginPost{
|
||||||
|
CustomerId: customerID,
|
||||||
|
UserEmail: userEmail,
|
||||||
|
KeySelection: selection,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return security.AuthenticationTokens{}, 0, err
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/login", bytes.NewBuffer(body))
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
var resp security.AuthenticationTokens
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
return security.AuthenticationTokens{}, rec.Code, err
|
||||||
|
}
|
||||||
|
return resp, rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) RenewAttributes(
|
||||||
|
customerID string,
|
||||||
|
) (int, error) {
|
||||||
|
data := models.RenewAttributesPost{
|
||||||
|
CustomerId: customerID,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/renew-attributes", bytes.NewBuffer(body))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
return rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) ForgotNKode(
|
||||||
|
customerID string,
|
||||||
|
userEmail string,
|
||||||
|
) (int, error) {
|
||||||
|
data := models.ForgotNKodePost{
|
||||||
|
CustomerId: customerID,
|
||||||
|
UserEmail: userEmail,
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/forgot-nkode", bytes.NewBuffer(body))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
return rec.Code, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TestRouter) Reset(
|
||||||
|
customerID string,
|
||||||
|
attrsPerKey int,
|
||||||
|
numberOfKeys int,
|
||||||
|
userEmail string,
|
||||||
|
resetAuthToken string,
|
||||||
|
) (*entities.SignupResetInterface, int, error) {
|
||||||
|
body := bytes.NewBufferString(fmt.Sprintf(
|
||||||
|
"customer_id=%s&attrs_per_key=%d&numb_of_keys=%d&email=%s",
|
||||||
|
customerID,
|
||||||
|
attrsPerKey,
|
||||||
|
numberOfKeys,
|
||||||
|
userEmail,
|
||||||
|
))
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/reset-nkode", body)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+resetAuthToken)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
r.router.ServeHTTP(rec, req)
|
||||||
|
var resp entities.SignupResetInterface
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||||
|
return nil, rec.Code, err
|
||||||
|
}
|
||||||
|
return &resp, rec.Code, nil
|
||||||
|
}
|
||||||
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) + id.String()
|
||||||
|
}
|
||||||
59
models/models.go
Normal file
59
models/models.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "git.infra.nkode.tech/dkelly/nkode-core/entities"
|
||||||
|
|
||||||
|
type SetNKodeResp struct {
|
||||||
|
UserInterface []int `json:"user_interface"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RandomSvgInterfaceResp struct {
|
||||||
|
Svgs []string `form:"svgs" binding:"required"`
|
||||||
|
Colors []entities.RGBColor `form:"colors" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RefreshTokenResp struct {
|
||||||
|
AccessToken string `form:"access_token" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignupPostBody struct {
|
||||||
|
CustomerId string `form:"customer_id"`
|
||||||
|
AttrsPerKey int `form:"attrs_per_key"`
|
||||||
|
NumbOfKeys int `form:"numb_of_keys"`
|
||||||
|
UserEmail string `form:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetNKodePost struct {
|
||||||
|
CustomerId string `json:"customer_id" binding:"required"`
|
||||||
|
KeySelection []int `json:"key_selection" binding:"required"`
|
||||||
|
SessionId string `json:"session_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfirmNKodePost struct {
|
||||||
|
CustomerId string `json:"customer_id" binding:"required"`
|
||||||
|
KeySelection []int `json:"key_selection" binding:"required"`
|
||||||
|
SessionId string `json:"session_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginInterfacePost struct {
|
||||||
|
UserEmail string `form:"email" binding:"required"`
|
||||||
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginPost struct {
|
||||||
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
|
UserEmail string `form:"email" binding:"required"`
|
||||||
|
KeySelection entities.KeySelection `form:"key_selection" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenewAttributesPost struct {
|
||||||
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ForgotNKodePost struct {
|
||||||
|
UserEmail string `form:"email" binding:"required"`
|
||||||
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateNewCustomerResp struct {
|
||||||
|
CustomerId string `form:"customer_id" binding:"required"`
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/config"
|
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
"git.infra.nkode.tech/dkelly/nkode-core/entities"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/security"
|
"git.infra.nkode.tech/dkelly/nkode-core/security"
|
||||||
"git.infra.nkode.tech/dkelly/nkode-core/sqlc"
|
"git.infra.nkode.tech/dkelly/nkode-core/sqlc"
|
||||||
@@ -15,12 +14,12 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SqliteRepository struct {
|
type SqliteNKodeRepo struct {
|
||||||
Queue *sqlc.Queue
|
Queue *sqlc.Queue
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqliteRepository(dbPath string, ctx context.Context) (*SqliteRepository, error) {
|
func NewSqliteNKodeRepo(ctx context.Context, dbPath string) (*SqliteNKodeRepo, error) {
|
||||||
sqliteDb, err := sqlc.OpenSqliteDb(dbPath)
|
sqliteDb, err := sqlc.OpenSqliteDb(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -29,21 +28,21 @@ func NewSqliteRepository(dbPath string, ctx context.Context) (*SqliteRepository,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &SqliteRepository{
|
return &SqliteNKodeRepo{
|
||||||
Queue: queue,
|
Queue: queue,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) Start() {
|
func (d *SqliteNKodeRepo) Start() {
|
||||||
d.Queue.Start()
|
d.Queue.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) Stop() error {
|
func (d *SqliteNKodeRepo) Stop() error {
|
||||||
return d.Queue.Stop()
|
return d.Queue.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) CreateCustomer(c entities.Customer) error {
|
func (d *SqliteNKodeRepo) CreateCustomer(c entities.Customer) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(sqlc.CreateCustomerParams)
|
params, ok := args.(sqlc.CreateCustomerParams)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -55,7 +54,7 @@ func (d *SqliteRepository) CreateCustomer(c entities.Customer) error {
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
|
return d.Queue.EnqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) WriteNewUser(u entities.User) error {
|
func (d *SqliteNKodeRepo) WriteNewUser(u entities.User) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(sqlc.CreateUserParams)
|
params, ok := args.(sqlc.CreateUserParams)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -93,7 +92,7 @@ func (d *SqliteRepository) WriteNewUser(u entities.User) error {
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) UpdateUserNKode(u entities.User) error {
|
func (d *SqliteNKodeRepo) UpdateUserNKode(u entities.User) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(sqlc.UpdateUserParams)
|
params, ok := args.(sqlc.UpdateUserParams)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -127,7 +126,7 @@ func (d *SqliteRepository) UpdateUserNKode(u entities.User) error {
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) UpdateUserInterface(id entities.UserId, ui entities.UserInterface) error {
|
func (d *SqliteNKodeRepo) UpdateUserInterface(id entities.UserId, ui entities.UserInterface) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(sqlc.UpdateUserInterfaceParams)
|
params, ok := args.(sqlc.UpdateUserInterfaceParams)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -144,7 +143,7 @@ func (d *SqliteRepository) UpdateUserInterface(id entities.UserId, ui entities.U
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) UpdateUserRefreshToken(id entities.UserId, refreshToken string) error {
|
func (d *SqliteNKodeRepo) UpdateUserRefreshToken(id entities.UserId, refreshToken string) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(sqlc.UpdateUserRefreshTokenParams)
|
params, ok := args.(sqlc.UpdateUserRefreshTokenParams)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -162,7 +161,7 @@ func (d *SqliteRepository) UpdateUserRefreshToken(id entities.UserId, refreshTok
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) RenewCustomer(renewParams sqlc.RenewCustomerParams) error {
|
func (d *SqliteNKodeRepo) RenewCustomer(renewParams sqlc.RenewCustomerParams) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(sqlc.RenewCustomerParams)
|
params, ok := args.(sqlc.RenewCustomerParams)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -173,13 +172,12 @@ func (d *SqliteRepository) RenewCustomer(renewParams sqlc.RenewCustomerParams) e
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, renewParams)
|
return d.Queue.EnqueueWriteTx(queryFunc, renewParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) Renew(id entities.CustomerId) error {
|
func (d *SqliteNKodeRepo) Renew(id entities.CustomerId) error {
|
||||||
setXor, attrXor, err := d.renewCustomer(id)
|
setXor, attrXor, err := d.renewCustomer(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
customerId := entities.CustomerIdToString(id)
|
userRenewRows, err := d.Queue.Queries.GetUserRenew(d.ctx, id.String())
|
||||||
userRenewRows, err := d.Queue.Queries.GetUserRenew(d.ctx, customerId)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -226,7 +224,7 @@ func (d *SqliteRepository) Renew(id entities.CustomerId) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) renewCustomer(id entities.CustomerId) ([]uint64, []uint64, error) {
|
func (d *SqliteNKodeRepo) renewCustomer(id entities.CustomerId) ([]uint64, []uint64, error) {
|
||||||
customer, err := d.GetCustomer(id)
|
customer, err := d.GetCustomer(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -255,7 +253,7 @@ func (d *SqliteRepository) renewCustomer(id entities.CustomerId) ([]uint64, []ui
|
|||||||
return setXor, attrXor, nil
|
return setXor, attrXor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx []int, customerAttr entities.CustomerAttributes) error {
|
func (d *SqliteNKodeRepo) RefreshUserPasscode(user entities.User, passcodeIdx []int, customerAttr entities.CustomerAttributes) error {
|
||||||
if err := user.RefreshPasscode(passcodeIdx, customerAttr); err != nil {
|
if err := user.RefreshPasscode(passcodeIdx, customerAttr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -280,7 +278,7 @@ func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx [
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) AddSvg(svg string) error {
|
func (d *SqliteNKodeRepo) AddSvg(svg string) error {
|
||||||
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
params, ok := args.(string)
|
params, ok := args.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -291,7 +289,7 @@ func (d *SqliteRepository) AddSvg(svg string) error {
|
|||||||
return d.Queue.EnqueueWriteTx(queryFunc, svg)
|
return d.Queue.EnqueueWriteTx(queryFunc, svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) GetCustomer(id entities.CustomerId) (*entities.Customer, error) {
|
func (d *SqliteNKodeRepo) GetCustomer(id entities.CustomerId) (*entities.Customer, error) {
|
||||||
customer, err := d.Queue.Queries.GetCustomer(d.ctx, uuid.UUID(id).String())
|
customer, err := d.Queue.Queries.GetCustomer(d.ctx, uuid.UUID(id).String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -311,7 +309,7 @@ func (d *SqliteRepository) GetCustomer(id entities.CustomerId) (*entities.Custom
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) GetUser(email entities.UserEmail, customerId entities.CustomerId) (*entities.User, error) {
|
func (d *SqliteNKodeRepo) GetUser(email entities.UserEmail, customerId entities.CustomerId) (*entities.User, error) {
|
||||||
userRow, err := d.Queue.Queries.GetUser(d.ctx, sqlc.GetUserParams{
|
userRow, err := d.Queue.Queries.GetUser(d.ctx, sqlc.GetUserParams{
|
||||||
Email: string(email),
|
Email: string(email),
|
||||||
CustomerID: uuid.UUID(customerId).String(),
|
CustomerID: uuid.UUID(customerId).String(),
|
||||||
@@ -361,7 +359,7 @@ func (d *SqliteRepository) GetUser(email entities.UserEmail, customerId entities
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) RandomSvgInterface(kp entities.KeypadDimension) ([]string, error) {
|
func (d *SqliteNKodeRepo) RandomSvgInterface(kp entities.KeypadDimension) ([]string, error) {
|
||||||
ids, err := d.getRandomIds(kp.TotalAttrs())
|
ids, err := d.getRandomIds(kp.TotalAttrs())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -369,15 +367,35 @@ func (d *SqliteRepository) RandomSvgInterface(kp entities.KeypadDimension) ([]st
|
|||||||
return d.getSvgsById(ids)
|
return d.getSvgsById(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) RandomSvgIdxInterface(kp entities.KeypadDimension) (entities.SvgIdInterface, error) {
|
func (d *SqliteNKodeRepo) RandomSvgIdxInterface(kp entities.KeypadDimension) (entities.SvgIdInterface, error) {
|
||||||
return d.getRandomIds(kp.TotalAttrs())
|
return d.getRandomIds(kp.TotalAttrs())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) GetSvgStringInterface(idxs entities.SvgIdInterface) ([]string, error) {
|
func (d *SqliteNKodeRepo) GetSvgStringInterface(idxs entities.SvgIdInterface) ([]string, error) {
|
||||||
return d.getSvgsById(idxs)
|
return d.getSvgsById(idxs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) getSvgsById(ids []int) ([]string, error) {
|
// Is this even useful?
|
||||||
|
func (d *SqliteNKodeRepo) AddUserPermission(userEmail entities.UserEmail, customerId entities.CustomerId, permission entities.UserPermission) error {
|
||||||
|
user, err := d.GetUser(userEmail, customerId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
|
||||||
|
params, ok := args.(sqlc.AddUserPermissionParams)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid argument type: expected AddUserPermissionParams")
|
||||||
|
}
|
||||||
|
return q.AddUserPermission(ctx, params)
|
||||||
|
}
|
||||||
|
params := sqlc.AddUserPermissionParams{
|
||||||
|
UserID: user.Id.String(),
|
||||||
|
Permission: permission.String(),
|
||||||
|
}
|
||||||
|
return d.Queue.EnqueueWriteTx(queryFunc, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SqliteNKodeRepo) getSvgsById(ids []int) ([]string, error) {
|
||||||
svgs := make([]string, len(ids))
|
svgs := make([]string, len(ids))
|
||||||
for idx, id := range ids {
|
for idx, id := range ids {
|
||||||
svg, err := d.Queue.Queries.GetSvgId(d.ctx, int64(id))
|
svg, err := d.Queue.Queries.GetSvgId(d.ctx, int64(id))
|
||||||
@@ -389,40 +407,13 @@ func (d *SqliteRepository) getSvgsById(ids []int) ([]string, error) {
|
|||||||
return svgs, nil
|
return svgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqliteRepository) getRandomIds(count int) ([]int, error) {
|
func (d *SqliteNKodeRepo) getRandomIds(count int) ([]int, error) {
|
||||||
tx, err := d.Queue.Db.Begin()
|
totalRows, err := d.Queue.Queries.GetSvgCount(d.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
return nil, config.ErrSqliteTx
|
|
||||||
}
|
|
||||||
rows, err := tx.Query("SELECT COUNT(*) as count FROM svg_icon;")
|
|
||||||
if err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
return nil, config.ErrSqliteTx
|
|
||||||
}
|
|
||||||
var tableLen int
|
|
||||||
if !rows.Next() {
|
|
||||||
return nil, config.ErrEmptySvgTable
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = rows.Scan(&tableLen); err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
return nil, config.ErrSqliteTx
|
|
||||||
}
|
|
||||||
perm, err := security.RandomPermutation(tableLen)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
perm, err := security.RandomPermutation(int(totalRows))
|
||||||
for idx := range perm {
|
|
||||||
perm[idx] += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
return nil, config.ErrSqliteTx
|
|
||||||
}
|
|
||||||
|
|
||||||
return perm[:count], nil
|
return perm[:count], nil
|
||||||
}
|
}
|
||||||
@@ -11,10 +11,10 @@ import (
|
|||||||
func TestNewSqliteDB(t *testing.T) {
|
func TestNewSqliteDB(t *testing.T) {
|
||||||
dbPath := os.Getenv("TEST_DB")
|
dbPath := os.Getenv("TEST_DB")
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
sqliteDb, err := NewSqliteRepository(dbPath, ctx)
|
sqliteDb, err := NewSqliteNKodeRepo(ctx, dbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
sqliteDb.Start()
|
sqliteDb.Start()
|
||||||
defer func(t *testing.T, sqliteDb *SqliteRepository) {
|
defer func(t *testing.T, sqliteDb *SqliteNKodeRepo) {
|
||||||
err := sqliteDb.Stop()
|
err := sqliteDb.Stop()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}(t, sqliteDb)
|
}(t, sqliteDb)
|
||||||
@@ -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
|
||||||
@@ -110,12 +115,12 @@ func ClaimExpired(claims jwt.RegisteredClaims) error {
|
|||||||
return config.ErrClaimExpOrNil
|
return config.ErrClaimExpOrNil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResetNKodeToken(userEmail string, customerId uuid.UUID) (string, error) {
|
func ResetNKodeToken(userEmail string, customerId string) (string, error) {
|
||||||
resetClaims := ResetNKodeClaims{
|
resetClaims := ResetNKodeClaims{
|
||||||
true,
|
true,
|
||||||
jwt.RegisteredClaims{
|
jwt.RegisteredClaims{
|
||||||
Subject: userEmail,
|
Subject: userEmail,
|
||||||
Issuer: customerId.String(),
|
Issuer: customerId,
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(resetNKodeTokenExp)),
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(resetNKodeTokenExp)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestJwtClaims(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, refreshToken.Subject, email)
|
assert.Equal(t, refreshToken.Subject, email)
|
||||||
assert.NoError(t, ClaimExpired(*refreshToken))
|
assert.NoError(t, ClaimExpired(*refreshToken))
|
||||||
resetNKode, err := ResetNKodeToken(email, customerId)
|
resetNKode, err := ResetNKodeToken(email, customerId.String())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
resetToken, err := ParseRestNKodeToken(resetNKode)
|
resetToken, err := ParseRestNKodeToken(resetNKode)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -268,8 +268,7 @@ func Choice[T any](items []T) T {
|
|||||||
return items[r.Intn(len(items))]
|
return items[r.Intn(len(items))]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRandomString creates a random string of a specified length.
|
func GenerateNonSecureRandomString(length int) string {
|
||||||
func GenerateRandomString(length int) string {
|
|
||||||
charset := []rune("abcdefghijklmnopqrstuvwxyz0123456789")
|
charset := []rune("abcdefghijklmnopqrstuvwxyz0123456789")
|
||||||
b := make([]rune, length)
|
b := make([]rune, length)
|
||||||
for i := range b {
|
for i := range b {
|
||||||
|
|||||||
@@ -6,8 +6,43 @@ package sqlc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AuthorizationCode struct {
|
||||||
|
ID int64
|
||||||
|
Code string
|
||||||
|
CodeChallenge string
|
||||||
|
CodeChallengeMethod string
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
Scope sql.NullString
|
||||||
|
RedirectUri string
|
||||||
|
CreatedAt sql.NullTime
|
||||||
|
ExpiresAt time.Time
|
||||||
|
UsedAt sql.NullTime
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Owner string
|
||||||
|
CreatedAt sql.NullTime
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientApproval struct {
|
||||||
|
ID int64
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientRedirect struct {
|
||||||
|
ID int64
|
||||||
|
Uri string
|
||||||
|
ClientID string
|
||||||
|
CreatedAt sql.NullTime
|
||||||
|
}
|
||||||
|
|
||||||
type Customer struct {
|
type Customer struct {
|
||||||
ID string
|
ID string
|
||||||
MaxNkodeLen int64
|
MaxNkodeLen int64
|
||||||
@@ -27,6 +62,17 @@ type SvgIcon struct {
|
|||||||
Svg string
|
Svg string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
ID int64
|
||||||
|
TokenType string
|
||||||
|
TokenValue string
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
Scope sql.NullString
|
||||||
|
CreatedAt sql.NullTime
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string
|
ID string
|
||||||
Email string
|
Email string
|
||||||
@@ -48,3 +94,9 @@ type User struct {
|
|||||||
LastLogin interface{}
|
LastLogin interface{}
|
||||||
CreatedAt sql.NullString
|
CreatedAt sql.NullString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserPermission struct {
|
||||||
|
ID int64
|
||||||
|
UserID string
|
||||||
|
Permission string
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package sqlc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const addSvg = `-- name: AddSvg :exec
|
const addSvg = `-- name: AddSvg :exec
|
||||||
@@ -19,6 +20,83 @@ func (q *Queries) AddSvg(ctx context.Context, svg string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addUserPermission = `-- name: AddUserPermission :exec
|
||||||
|
INSERT INTO user_permission (user_id, permission) VALUES (?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type AddUserPermissionParams struct {
|
||||||
|
UserID string
|
||||||
|
Permission string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) AddUserPermission(ctx context.Context, arg AddUserPermissionParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, addUserPermission, arg.UserID, arg.Permission)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const approveClient = `-- name: ApproveClient :exec
|
||||||
|
INSERT INTO client_approvals (user_id, client_id)
|
||||||
|
VALUES (?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type ApproveClientParams struct {
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ApproveClient(ctx context.Context, arg ApproveClientParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, approveClient, arg.UserID, arg.ClientID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientApproved = `-- name: ClientApproved :one
|
||||||
|
SELECT id, user_id, client_id
|
||||||
|
FROM client_approvals
|
||||||
|
WHERE user_id = ? AND client_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
type ClientApprovedParams struct {
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ClientApproved(ctx context.Context, arg ClientApprovedParams) (ClientApproval, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, clientApproved, arg.UserID, arg.ClientID)
|
||||||
|
var i ClientApproval
|
||||||
|
err := row.Scan(&i.ID, &i.UserID, &i.ClientID)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createAuthorizationCode = `-- name: CreateAuthorizationCode :exec
|
||||||
|
INSERT INTO authorization_codes (code, code_challenge, code_challenge_method, user_id, client_id, scope, redirect_uri, expires_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateAuthorizationCodeParams struct {
|
||||||
|
Code string
|
||||||
|
CodeChallenge string
|
||||||
|
CodeChallengeMethod string
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
Scope sql.NullString
|
||||||
|
RedirectUri string
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateAuthorizationCode(ctx context.Context, arg CreateAuthorizationCodeParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, createAuthorizationCode,
|
||||||
|
arg.Code,
|
||||||
|
arg.CodeChallenge,
|
||||||
|
arg.CodeChallengeMethod,
|
||||||
|
arg.UserID,
|
||||||
|
arg.ClientID,
|
||||||
|
arg.Scope,
|
||||||
|
arg.RedirectUri,
|
||||||
|
arg.ExpiresAt,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const createCustomer = `-- name: CreateCustomer :exec
|
const createCustomer = `-- name: CreateCustomer :exec
|
||||||
INSERT INTO customer (
|
INSERT INTO customer (
|
||||||
id
|
id
|
||||||
@@ -67,6 +145,63 @@ func (q *Queries) CreateCustomer(ctx context.Context, arg CreateCustomerParams)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createOIDCClient = `-- name: CreateOIDCClient :exec
|
||||||
|
INSERT INTO clients (id, name, owner)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateOIDCClientParams struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Owner string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateOIDCClient(ctx context.Context, arg CreateOIDCClientParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, createOIDCClient, arg.ID, arg.Name, arg.Owner)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createRedirectURI = `-- name: CreateRedirectURI :exec
|
||||||
|
INSERT INTO client_redirects (uri, client_id)
|
||||||
|
VALUES (?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateRedirectURIParams struct {
|
||||||
|
Uri string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateRedirectURI(ctx context.Context, arg CreateRedirectURIParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, createRedirectURI, arg.Uri, arg.ClientID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createToken = `-- name: CreateToken :exec
|
||||||
|
INSERT INTO tokens (token_type, token_value, user_id, client_id, scope, expires_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateTokenParams struct {
|
||||||
|
TokenType string
|
||||||
|
TokenValue string
|
||||||
|
UserID string
|
||||||
|
ClientID string
|
||||||
|
Scope sql.NullString
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateToken(ctx context.Context, arg CreateTokenParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, createToken,
|
||||||
|
arg.TokenType,
|
||||||
|
arg.TokenValue,
|
||||||
|
arg.UserID,
|
||||||
|
arg.ClientID,
|
||||||
|
arg.Scope,
|
||||||
|
arg.ExpiresAt,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const createUser = `-- name: CreateUser :exec
|
const createUser = `-- name: CreateUser :exec
|
||||||
INSERT INTO user (
|
INSERT INTO user (
|
||||||
id
|
id
|
||||||
@@ -136,6 +271,110 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteAuthCode = `-- name: DeleteAuthCode :exec
|
||||||
|
DELETE FROM authorization_codes
|
||||||
|
WHERE code = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteAuthCode(ctx context.Context, code string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteAuthCode, code)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOldAuthCodes = `-- name: DeleteOldAuthCodes :exec
|
||||||
|
DELETE FROM authorization_codes
|
||||||
|
WHERE expires_at < CURRENT_TIMESTAMP
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOldAuthCodes(ctx context.Context) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOldAuthCodes)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOldTokens = `-- name: DeleteOldTokens :exec
|
||||||
|
DELETE FROM tokens
|
||||||
|
WHERE expires_at < CURRENT_TIMESTAMP
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOldTokens(ctx context.Context) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOldTokens)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRedirectURI = `-- name: DeleteRedirectURI :exec
|
||||||
|
DELETE FROM client_redirects
|
||||||
|
WHERE uri = ? AND client_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
type DeleteRedirectURIParams struct {
|
||||||
|
Uri string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) DeleteRedirectURI(ctx context.Context, arg DeleteRedirectURIParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteRedirectURI, arg.Uri, arg.ClientID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAuthorizationCode = `-- name: GetAuthorizationCode :one
|
||||||
|
SELECT id, code, code_challenge, code_challenge_method, user_id, client_id, scope, redirect_uri, created_at, expires_at, used_at
|
||||||
|
FROM authorization_codes
|
||||||
|
WHERE code = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetAuthorizationCode(ctx context.Context, code string) (AuthorizationCode, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getAuthorizationCode, code)
|
||||||
|
var i AuthorizationCode
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Code,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
&i.CodeChallengeMethod,
|
||||||
|
&i.UserID,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectUri,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.UsedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getClientRedirectURIs = `-- name: GetClientRedirectURIs :many
|
||||||
|
SELECT id, uri, client_id, created_at
|
||||||
|
FROM client_redirects
|
||||||
|
WHERE client_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetClientRedirectURIs(ctx context.Context, clientID string) ([]ClientRedirect, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getClientRedirectURIs, clientID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ClientRedirect
|
||||||
|
for rows.Next() {
|
||||||
|
var i ClientRedirect
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Uri,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
); 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 getCustomer = `-- name: GetCustomer :one
|
const getCustomer = `-- name: GetCustomer :one
|
||||||
SELECT
|
SELECT
|
||||||
max_nkode_len
|
max_nkode_len
|
||||||
@@ -177,6 +416,24 @@ func (q *Queries) GetCustomer(ctx context.Context, id string) (GetCustomerRow, e
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOIDCClientByID = `-- name: GetOIDCClientByID :one
|
||||||
|
SELECT id, name, owner, created_at
|
||||||
|
FROM clients
|
||||||
|
WHERE id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOIDCClientByID(ctx context.Context, id string) (Client, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOIDCClientByID, id)
|
||||||
|
var i Client
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Owner,
|
||||||
|
&i.CreatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getSvgCount = `-- name: GetSvgCount :one
|
const getSvgCount = `-- name: GetSvgCount :one
|
||||||
SELECT COUNT(*) as count FROM svg_icon
|
SELECT COUNT(*) as count FROM svg_icon
|
||||||
`
|
`
|
||||||
@@ -201,6 +458,28 @@ func (q *Queries) GetSvgId(ctx context.Context, id int64) (string, error) {
|
|||||||
return svg, err
|
return svg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getTokenByValue = `-- name: GetTokenByValue :one
|
||||||
|
SELECT id, token_type, token_value, user_id, client_id, scope, created_at, expires_at
|
||||||
|
FROM tokens
|
||||||
|
WHERE token_value = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetTokenByValue(ctx context.Context, tokenValue string) (Token, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getTokenByValue, tokenValue)
|
||||||
|
var i Token
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.TokenType,
|
||||||
|
&i.TokenValue,
|
||||||
|
&i.UserID,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.Scope,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getUser = `-- name: GetUser :one
|
const getUser = `-- name: GetUser :one
|
||||||
SELECT
|
SELECT
|
||||||
id
|
id
|
||||||
@@ -268,6 +547,69 @@ func (q *Queries) GetUser(ctx context.Context, arg GetUserParams) (GetUserRow, e
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getUserClients = `-- name: GetUserClients :many
|
||||||
|
|
||||||
|
SELECT id, name, owner, created_at
|
||||||
|
FROM clients
|
||||||
|
WHERE owner = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
// -------- go-oidc
|
||||||
|
func (q *Queries) GetUserClients(ctx context.Context, owner string) ([]Client, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getUserClients, owner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Client
|
||||||
|
for rows.Next() {
|
||||||
|
var i Client
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Owner,
|
||||||
|
&i.CreatedAt,
|
||||||
|
); 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 getUserPermissions = `-- name: GetUserPermissions :many
|
||||||
|
SELECT permission FROM user_permission WHERE user_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetUserPermissions(ctx context.Context, userID string) ([]string, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getUserPermissions, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []string
|
||||||
|
for rows.Next() {
|
||||||
|
var permission string
|
||||||
|
if err := rows.Scan(&permission); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, permission)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getUserRenew = `-- name: GetUserRenew :many
|
const getUserRenew = `-- name: GetUserRenew :many
|
||||||
SELECT
|
SELECT
|
||||||
id
|
id
|
||||||
|
|||||||
@@ -137,3 +137,78 @@ WHERE id = ?;
|
|||||||
|
|
||||||
-- name: GetSvgCount :one
|
-- name: GetSvgCount :one
|
||||||
SELECT COUNT(*) as count FROM svg_icon;
|
SELECT COUNT(*) as count FROM svg_icon;
|
||||||
|
|
||||||
|
-- name: GetUserPermissions :many
|
||||||
|
SELECT permission FROM user_permission WHERE user_id = ?;
|
||||||
|
|
||||||
|
-- name: AddUserPermission :exec
|
||||||
|
INSERT INTO user_permission (user_id, permission) VALUES (?, ?);
|
||||||
|
|
||||||
|
|
||||||
|
---------- go-oidc
|
||||||
|
|
||||||
|
-- name: GetUserClients :many
|
||||||
|
SELECT *
|
||||||
|
FROM clients
|
||||||
|
WHERE owner = ?;
|
||||||
|
|
||||||
|
-- name: GetOIDCClientByID :one
|
||||||
|
SELECT *
|
||||||
|
FROM clients
|
||||||
|
WHERE id = ?;
|
||||||
|
|
||||||
|
-- name: CreateOIDCClient :exec
|
||||||
|
INSERT INTO clients (id, name, owner)
|
||||||
|
VALUES (?, ?, ?);
|
||||||
|
|
||||||
|
-- name: CreateRedirectURI :exec
|
||||||
|
INSERT INTO client_redirects (uri, client_id)
|
||||||
|
VALUES (?, ?);
|
||||||
|
|
||||||
|
-- name: DeleteRedirectURI :exec
|
||||||
|
DELETE FROM client_redirects
|
||||||
|
WHERE uri = ? AND client_id = ?;
|
||||||
|
|
||||||
|
-- name: GetClientRedirectURIs :many
|
||||||
|
SELECT *
|
||||||
|
FROM client_redirects
|
||||||
|
WHERE client_id = ?;
|
||||||
|
|
||||||
|
-- name: GetAuthorizationCode :one
|
||||||
|
SELECT *
|
||||||
|
FROM authorization_codes
|
||||||
|
WHERE code = ?;
|
||||||
|
|
||||||
|
-- name: CreateAuthorizationCode :exec
|
||||||
|
INSERT INTO authorization_codes (code, code_challenge, code_challenge_method, user_id, client_id, scope, redirect_uri, expires_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
||||||
|
|
||||||
|
-- name: DeleteOldAuthCodes :exec
|
||||||
|
DELETE FROM authorization_codes
|
||||||
|
WHERE expires_at < CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- name: DeleteOldTokens :exec
|
||||||
|
DELETE FROM tokens
|
||||||
|
WHERE expires_at < CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- name: GetTokenByValue :one
|
||||||
|
SELECT *
|
||||||
|
FROM tokens
|
||||||
|
WHERE token_value = ?;
|
||||||
|
|
||||||
|
-- name: CreateToken :exec
|
||||||
|
INSERT INTO tokens (token_type, token_value, user_id, client_id, scope, expires_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?);
|
||||||
|
|
||||||
|
-- name: ApproveClient :exec
|
||||||
|
INSERT INTO client_approvals (user_id, client_id)
|
||||||
|
VALUES (?, ?);
|
||||||
|
|
||||||
|
-- name: ClientApproved :one
|
||||||
|
SELECT *
|
||||||
|
FROM client_approvals
|
||||||
|
WHERE user_id = ? AND client_id = ?;
|
||||||
|
|
||||||
|
-- name: DeleteAuthCode :exec
|
||||||
|
DELETE FROM authorization_codes
|
||||||
|
WHERE code = ?;
|
||||||
|
|||||||
@@ -55,3 +55,71 @@ CREATE TABLE IF NOT EXISTS svg_icon (
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
,svg TEXT NOT NULL
|
,svg TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS user_permission (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
,user_id TEXT NOT NULL
|
||||||
|
,permission TEXT NOT NULL
|
||||||
|
,FOREIGN KEY (user_id) REFERENCES user(id)
|
||||||
|
,UNIQUE(user_id, permission)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
---- go-oidc
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS clients (
|
||||||
|
id TEXT PRIMARY KEY
|
||||||
|
,name TEXT NOT NULL
|
||||||
|
,owner TEXT NOT NULL
|
||||||
|
,created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
,FOREIGN KEY (owner) REFERENCES user (id)
|
||||||
|
,UNIQUE(name, owner)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS client_redirects (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
,uri TEXT NOT NULL
|
||||||
|
,client_id TEXT NOT NULL
|
||||||
|
,created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
,FOREIGN KEY (client_id) REFERENCES clients (id)
|
||||||
|
,UNIQUE(uri, client_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS authorization_codes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
,code TEXT NOT NULL UNIQUE
|
||||||
|
,code_challenge TEXT NOT NULL UNIQUE
|
||||||
|
,code_challenge_method TEXT NOT NULL CHECK (code_challenge_method IN ('S256', 'plain'))
|
||||||
|
,user_id TEXT NOT NULL
|
||||||
|
,client_id TEXT NOT NULL
|
||||||
|
,scope TEXT
|
||||||
|
,redirect_uri TEXT NOT NULL
|
||||||
|
,created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
,expires_at DATETIME NOT NULL
|
||||||
|
,used_at DATETIME
|
||||||
|
,FOREIGN KEY (user_id) REFERENCES user (id)
|
||||||
|
,FOREIGN KEY (client_id) REFERENCES client (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS tokens (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
,token_type TEXT NOT NULL CHECK (token_type IN ('access', 'refresh'))
|
||||||
|
,token_value TEXT NOT NULL UNIQUE
|
||||||
|
,user_id TEXT NOT NULL
|
||||||
|
,client_id TEXT NOT NULL
|
||||||
|
,scope TEXT
|
||||||
|
,created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
,expires_at DATETIME NOT NULL
|
||||||
|
,FOREIGN KEY (user_id) REFERENCES user (id)
|
||||||
|
,FOREIGN KEY (client_id) REFERENCES clients (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS client_approvals (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
,user_id TEXT NOT NULL
|
||||||
|
,client_id TEXT NOT NULL
|
||||||
|
,UNIQUE(user_id, client_id)
|
||||||
|
,FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
|
,FOREIGN KEY (client_id) REFERENCES clients (id)
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user