From abe809ca68ef0f730573d1c9ce0c717ee24bd3b6 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Wed, 12 Jul 2023 11:24:13 +0530 Subject: [PATCH] [draft] Move sms verificaiton to otp models --- server/db/models/otp.go | 22 +++++++++++------ server/db/models/sms_verification_requests.go | 3 ++- server/db/providers/arangodb/provider.go | 19 ++++++++++++++- server/db/providers/couchbase/shared.go | 5 ---- server/db/providers/mongodb/otp.go | 13 ++++++++++ server/db/providers/mongodb/provider.go | 6 +++++ .../mongodb/sms_verification_requests.go | 7 ++---- server/db/providers/providers.go | 2 ++ server/db/providers/sql/otp.go | 24 +++++++++++++++---- 9 files changed, 78 insertions(+), 23 deletions(-) diff --git a/server/db/models/otp.go b/server/db/models/otp.go index ac9732b..b0e02ee 100644 --- a/server/db/models/otp.go +++ b/server/db/models/otp.go @@ -1,14 +1,22 @@ package models +const ( + // FieldName email is the field name for email + FieldNameEmail = "email" + // FieldNamePhoneNumber is the field name for phone number + FieldNamePhoneNumber = "phone_number" +) + // OTP model for database type OTP struct { - Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb - ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"` - Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"` - Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"` - ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"` - CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` - UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` + Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb + ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"` + Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"` + PhoneNumber string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number" index:"phone_number,hash"` + Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"` + ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"` + CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` + UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` } type Paging struct { diff --git a/server/db/models/sms_verification_requests.go b/server/db/models/sms_verification_requests.go index a938298..e14da4c 100644 --- a/server/db/models/sms_verification_requests.go +++ b/server/db/models/sms_verification_requests.go @@ -1,7 +1,8 @@ package models -// SMS verification requests model for database +// SMSVerificationRequest is SMS verification requests model for database type SMSVerificationRequest struct { + Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"` PhoneNumber string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number" index:"phone_number,hash"` Code string `json:"code" bson:"code" cql:"code" dynamo:"code"` diff --git a/server/db/providers/arangodb/provider.go b/server/db/providers/arangodb/provider.go index 5488428..88fc6e4 100644 --- a/server/db/providers/arangodb/provider.go +++ b/server/db/providers/arangodb/provider.go @@ -225,7 +225,6 @@ func NewProvider() (*provider, error) { return nil, err } } - otpCollection, err := arangodb.Collection(ctx, models.Collections.OTP) if err != nil { return nil, err @@ -235,6 +234,24 @@ func NewProvider() (*provider, error) { Sparse: true, }) + smsVerificationCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.SMSVerificationRequest) + if err != nil { + return nil, err + } + if !smsVerificationCollectionExists { + _, err = arangodb.CreateCollection(ctx, models.Collections.SMSVerificationRequest, nil) + if err != nil { + return nil, err + } + } + smsVerificationCollection, err := arangodb.Collection(ctx, models.Collections.SMSVerificationRequest) + if err != nil { + return nil, err + } + smsVerificationCollection.EnsureHashIndex(ctx, []string{"phone_number"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) return &provider{ db: arangodb, }, err diff --git a/server/db/providers/couchbase/shared.go b/server/db/providers/couchbase/shared.go index 104ad02..d640f68 100644 --- a/server/db/providers/couchbase/shared.go +++ b/server/db/providers/couchbase/shared.go @@ -11,24 +11,19 @@ import ( func GetSetFields(webhookMap map[string]interface{}) (string, map[string]interface{}) { params := make(map[string]interface{}, 1) - updateFields := "" - for key, value := range webhookMap { if key == "_id" { continue } - if key == "_key" { continue } - if value == nil { updateFields += fmt.Sprintf("%s=$%s,", key, key) params[key] = "null" continue } - valueType := reflect.TypeOf(value) if valueType.Name() == "string" { updateFields += fmt.Sprintf("%s = $%s, ", key, key) diff --git a/server/db/providers/mongodb/otp.go b/server/db/providers/mongodb/otp.go index d6ff2df..8726f5a 100644 --- a/server/db/providers/mongodb/otp.go +++ b/server/db/providers/mongodb/otp.go @@ -2,6 +2,7 @@ package mongodb import ( "context" + "errors" "time" "github.com/authorizerdev/authorizer/server/db/models" @@ -12,6 +13,18 @@ import ( // UpsertOTP to add or update otp func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) { + // check if email or phone number is present + if otpParam.Email == "" && otpParam.PhoneNumber == "" { + return nil, errors.New("email or phone_number is required") + } + // check if email or phone number is present + if otpParam.Email == "" && otpParam.PhoneNumber == "" { + return nil, errors.New("email or phone_number is required") + } + uniqueField := models.FieldNameEmail + if otp.Email == "" && otp.PhoneNumber != "" { + uniqueField = models.FieldNamePhoneNumber + } otp, _ := p.GetOTPByEmail(ctx, otpParam.Email) shouldCreate := false if otp == nil { diff --git a/server/db/providers/mongodb/provider.go b/server/db/providers/mongodb/provider.go index fd4a0b2..1922b0e 100644 --- a/server/db/providers/mongodb/provider.go +++ b/server/db/providers/mongodb/provider.go @@ -118,6 +118,12 @@ func NewProvider() (*provider, error) { Options: options.Index().SetUnique(true).SetSparse(true), }, }, options.CreateIndexes()) + otpCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{ + { + Keys: bson.M{"phone_number": 1}, + Options: options.Index().SetUnique(true).SetSparse(true), + }, + }, options.CreateIndexes()) mongodb.CreateCollection(ctx, models.Collections.SMSVerificationRequest, options.CreateCollection()) smsCollection := mongodb.Collection(models.Collections.SMSVerificationRequest, options.Collection()) diff --git a/server/db/providers/mongodb/sms_verification_requests.go b/server/db/providers/mongodb/sms_verification_requests.go index 1183df1..0ee0e6c 100644 --- a/server/db/providers/mongodb/sms_verification_requests.go +++ b/server/db/providers/mongodb/sms_verification_requests.go @@ -12,10 +12,7 @@ import ( // UpsertSMSRequest adds/updates SMS verification request func (p *provider) UpsertSMSRequest(ctx context.Context, smsRequest *models.SMSVerificationRequest) (*models.SMSVerificationRequest, error) { - smsVerificationRequest, err := p.GetCodeByPhone(ctx, smsRequest.PhoneNumber) - if err != nil { - return nil, err - } + smsVerificationRequest, _ := p.GetCodeByPhone(ctx, smsRequest.PhoneNumber) // Boolean to check if we should create a new record or update the existing one shouldCreate := false if smsVerificationRequest == nil { @@ -29,7 +26,7 @@ func (p *provider) UpsertSMSRequest(ctx context.Context, smsRequest *models.SMSV } shouldCreate = true } - + var err error smsVerificationRequest.UpdatedAt = time.Now().Unix() smsRequestCollection := p.db.Collection(models.Collections.SMSVerificationRequest, options.Collection()) if shouldCreate { diff --git a/server/db/providers/providers.go b/server/db/providers/providers.go index 31c021c..c6065c1 100644 --- a/server/db/providers/providers.go +++ b/server/db/providers/providers.go @@ -82,6 +82,8 @@ type Provider interface { UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP, error) // GetOTPByEmail to get otp for a given email address GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) + // GetOTPByPhoneNumber to get otp for a given phone number + GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) // DeleteOTP to delete otp DeleteOTP(ctx context.Context, otp *models.OTP) error diff --git a/server/db/providers/sql/otp.go b/server/db/providers/sql/otp.go index 9aabcab..5503a7d 100644 --- a/server/db/providers/sql/otp.go +++ b/server/db/providers/sql/otp.go @@ -2,6 +2,7 @@ package sql import ( "context" + "errors" "time" "github.com/authorizerdev/authorizer/server/db/models" @@ -14,13 +15,19 @@ func (p *provider) UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP, if otp.ID == "" { otp.ID = uuid.New().String() } - + // check if email or phone number is present + if otp.Email == "" && otp.PhoneNumber == "" { + return nil, errors.New("email or phone_number is required") + } + uniqueField := models.FieldNameEmail + if otp.Email == "" && otp.PhoneNumber != "" { + uniqueField = models.FieldNamePhoneNumber + } otp.Key = otp.ID otp.CreatedAt = time.Now().Unix() otp.UpdatedAt = time.Now().Unix() - res := p.db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "email"}}, + Columns: []clause.Column{{Name: uniqueField}}, DoUpdates: clause.AssignmentColumns([]string{"otp", "expires_at", "updated_at"}), }).Create(&otp) if res.Error != nil { @@ -33,7 +40,6 @@ func (p *provider) UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP, // GetOTPByEmail to get otp for a given email address func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) { var otp models.OTP - result := p.db.Where("email = ?", emailAddress).First(&otp) if result.Error != nil { return nil, result.Error @@ -41,6 +47,16 @@ func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*mod return &otp, nil } +// GetOTPByPhoneNumber to get otp for a given phone number +func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) { + var otp models.OTP + result := p.db.Where("phone_number = ?", phoneNumber).First(&otp) + if result.Error != nil { + return nil, result.Error + } + return &otp, nil +} + // DeleteOTP to delete otp func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error { result := p.db.Delete(&models.OTP{