From 13c038effd66a295172ffd70468ff8042c7c32b2 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 21 Jan 2022 12:18:07 +0530 Subject: [PATCH] fix: env + session to new db format --- server/db/db.go | 26 +++++ server/db/models/env.go | 11 ++ server/db/models/model.go | 21 ++++ server/db/models/session.go | 13 +++ server/db/models/user.go | 24 +++++ server/db/models/verification_requests.go | 13 +++ server/db/providers/arangodb/arangodb.go | 123 ++++++++++++++++++++++ server/db/providers/arangodb/env.go | 73 +++++++++++++ server/db/providers/arangodb/session.go | 42 ++++++++ server/db/providers/mongodb/env.go | 66 ++++++++++++ server/db/providers/mongodb/mongodb.go | 88 ++++++++++++++++ server/db/providers/mongodb/session.go | 40 +++++++ server/db/providers/providers.go | 41 ++++++++ server/db/providers/sql/env.go | 49 +++++++++ server/db/providers/sql/session.go | 38 +++++++ server/db/providers/sql/sql.go | 53 ++++++++++ server/utils/common.go | 14 ++- 17 files changed, 732 insertions(+), 3 deletions(-) create mode 100644 server/db/models/env.go create mode 100644 server/db/models/model.go create mode 100644 server/db/models/session.go create mode 100644 server/db/models/user.go create mode 100644 server/db/models/verification_requests.go create mode 100644 server/db/providers/arangodb/arangodb.go create mode 100644 server/db/providers/arangodb/env.go create mode 100644 server/db/providers/arangodb/session.go create mode 100644 server/db/providers/mongodb/env.go create mode 100644 server/db/providers/mongodb/mongodb.go create mode 100644 server/db/providers/mongodb/session.go create mode 100644 server/db/providers/providers.go create mode 100644 server/db/providers/sql/env.go create mode 100644 server/db/providers/sql/session.go create mode 100644 server/db/providers/sql/sql.go diff --git a/server/db/db.go b/server/db/db.go index 25d1ddc..61a36eb 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -5,6 +5,10 @@ import ( arangoDriver "github.com/arangodb/go-driver" "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db/providers" + "github.com/authorizerdev/authorizer/server/db/providers/arangodb" + "github.com/authorizerdev/authorizer/server/db/providers/mongodb" + "github.com/authorizerdev/authorizer/server/db/providers/sql" "github.com/authorizerdev/authorizer/server/envstore" "go.mongodb.org/mongo-driver/mongo" "gorm.io/driver/mysql" @@ -53,6 +57,7 @@ var ( IsArangoDB bool IsMongoDB bool Mgr Manager + Provider providers.Provider Prefix = "authorizer_" Collections = CollectionList{ User: Prefix + "users", @@ -77,6 +82,27 @@ func InitDB() { }, } + if IsORMSupported { + Provider, err = sql.NewProvider() + if err != nil { + log.Println("=> error setting sql provider:", err) + } + } + + if IsArangoDB { + Provider, err = arangodb.NewProvider() + if err != nil { + log.Println("=> error setting arangodb provider:", err) + } + } + + if IsMongoDB { + Provider, err = mongodb.NewProvider() + if err != nil { + log.Println("=> error setting arangodb provider:", err) + } + } + log.Println("db type:", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType)) switch envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) { diff --git a/server/db/models/env.go b/server/db/models/env.go new file mode 100644 index 0000000..24c6353 --- /dev/null +++ b/server/db/models/env.go @@ -0,0 +1,11 @@ +package models + +// Env model for db +type Env struct { + Key string `json:"_key,omitempty" bson:"_key"` // for arangodb + ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"` + EnvData []byte `gorm:"type:text" json:"env" bson:"env"` + Hash string `gorm:"type:hash" json:"hash" bson:"hash"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"` +} diff --git a/server/db/models/model.go b/server/db/models/model.go new file mode 100644 index 0000000..c9eb0b8 --- /dev/null +++ b/server/db/models/model.go @@ -0,0 +1,21 @@ +package models + +// Collections / Tables available for authorizer in the database +type CollectionList struct { + User string + VerificationRequest string + Session string + Env string +} + +var ( + // Prefix for table name / collection names + Prefix = "authorizer_" + // Collections / Tables available for authorizer in the database (used for dbs other than gorm) + Collections = CollectionList{ + User: Prefix + "users", + VerificationRequest: Prefix + "verification_requests", + Session: Prefix + "sessions", + Env: Prefix + "env", + } +) diff --git a/server/db/models/session.go b/server/db/models/session.go new file mode 100644 index 0000000..c989e8b --- /dev/null +++ b/server/db/models/session.go @@ -0,0 +1,13 @@ +package models + +// Session model for db +type Session struct { + Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb + ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"` + UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id"` + User User `json:"-" bson:"-"` + UserAgent string `json:"user_agent" bson:"user_agent"` + IP string `json:"ip" bson:"ip"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"` +} diff --git a/server/db/models/user.go b/server/db/models/user.go new file mode 100644 index 0000000..5d45dfe --- /dev/null +++ b/server/db/models/user.go @@ -0,0 +1,24 @@ +package models + +// User model for db +type User struct { + Key string `json:"_key,omitempty" bson:"_key"` // for arangodb + ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"` + + Email string `gorm:"unique" json:"email" bson:"email"` + EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at"` + Password *string `gorm:"type:text" json:"password" bson:"password"` + SignupMethods string `json:"signup_methods" bson:"signup_methods"` + GivenName *string `json:"given_name" bson:"given_name"` + FamilyName *string `json:"family_name" bson:"family_name"` + MiddleName *string `json:"middle_name" bson:"middle_name"` + Nickname *string `json:"nickname" bson:"nickname"` + Gender *string `json:"gender" bson:"gender"` + Birthdate *string `json:"birthdate" bson:"birthdate"` + PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number"` + PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at"` + Picture *string `gorm:"type:text" json:"picture" bson:"picture"` + Roles string `json:"roles" bson:"roles"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"` +} diff --git a/server/db/models/verification_requests.go b/server/db/models/verification_requests.go new file mode 100644 index 0000000..931887f --- /dev/null +++ b/server/db/models/verification_requests.go @@ -0,0 +1,13 @@ +package models + +// VerificationRequest model for db +type VerificationRequest struct { + Key string `json:"_key,omitempty" bson:"_key"` // for arangodb + ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"` + Token string `gorm:"type:text" json:"token" bson:"token"` + Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"` + ExpiresAt int64 `json:"expires_at" bson:"expires_at"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"` + Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"` +} diff --git a/server/db/providers/arangodb/arangodb.go b/server/db/providers/arangodb/arangodb.go new file mode 100644 index 0000000..9c9bff8 --- /dev/null +++ b/server/db/providers/arangodb/arangodb.go @@ -0,0 +1,123 @@ +package arangodb + +import ( + "context" + "log" + + "github.com/arangodb/go-driver" + arangoDriver "github.com/arangodb/go-driver" + "github.com/arangodb/go-driver/http" + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/authorizerdev/authorizer/server/envstore" +) + +type provider struct { + db arangoDriver.Database +} + +// for this we need arangodb instance up and running +// for local testing we can use dockerized version of it +// docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=root arangodb/arangodb:3.8.4 + +// NewProvider to initialize arangodb connection +func NewProvider() (*provider, error) { + ctx := context.Background() + conn, err := http.NewConnection(http.ConnectionConfig{ + Endpoints: []string{envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)}, + }) + if err != nil { + return nil, err + } + + arangoClient, err := arangoDriver.NewClient(arangoDriver.ClientConfig{ + Connection: conn, + }) + if err != nil { + return nil, err + } + + var arangodb driver.Database + + arangodb_exists, err := arangoClient.DatabaseExists(nil, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName)) + + if arangodb_exists { + log.Println(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) + " db exists already") + arangodb, err = arangoClient.Database(nil, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName)) + if err != nil { + return nil, err + } + } else { + arangodb, err = arangoClient.CreateDatabase(nil, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), nil) + if err != nil { + return nil, err + } + } + + userCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.User) + if userCollectionExists { + log.Println(models.Collections.User + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, models.Collections.User, nil) + if err != nil { + log.Println("error creating collection("+models.Collections.User+"):", err) + } + } + userCollection, _ := arangodb.Collection(nil, models.Collections.User) + userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + userCollection.EnsureHashIndex(ctx, []string{"phone_number"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + + verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.VerificationRequest) + if verificationRequestCollectionExists { + log.Println(models.Collections.VerificationRequest + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, models.Collections.VerificationRequest, nil) + if err != nil { + log.Println("error creating collection("+models.Collections.VerificationRequest+"):", err) + } + } + + verificationRequestCollection, _ := arangodb.Collection(nil, models.Collections.VerificationRequest) + verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + verificationRequestCollection.EnsureHashIndex(ctx, []string{"token"}, &arangoDriver.EnsureHashIndexOptions{ + Sparse: true, + }) + + sessionCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Session) + if sessionCollectionExists { + log.Println(models.Collections.Session + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, models.Collections.Session, nil) + if err != nil { + log.Println("error creating collection("+models.Collections.Session+"):", err) + } + } + + sessionCollection, _ := arangodb.Collection(nil, models.Collections.Session) + sessionCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{ + Sparse: true, + }) + + configCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env) + if configCollectionExists { + log.Println(models.Collections.Env + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, models.Collections.Env, nil) + if err != nil { + log.Println("error creating collection("+models.Collections.Env+"):", err) + } + } + + return &provider{ + db: arangodb, + }, err +} diff --git a/server/db/providers/arangodb/env.go b/server/db/providers/arangodb/env.go new file mode 100644 index 0000000..c2d866f --- /dev/null +++ b/server/db/providers/arangodb/env.go @@ -0,0 +1,73 @@ +package arangodb + +import ( + "fmt" + "log" + "time" + + arangoDriver "github.com/arangodb/go-driver" + "github.com/google/uuid" + + "github.com/authorizerdev/authorizer/server/db/models" +) + +// AddEnv to save environment information in database +func (p *provider) AddEnv(env models.Env) (models.Env, error) { + if env.ID == "" { + env.ID = uuid.New().String() + } + + env.CreatedAt = time.Now().Unix() + env.UpdatedAt = time.Now().Unix() + configCollection, _ := p.db.Collection(nil, models.Collections.Env) + meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(nil), env) + if err != nil { + log.Println("error adding config:", err) + return env, err + } + env.Key = meta.Key + env.ID = meta.ID.String() + return env, nil +} + +// UpdateEnv to update environment information in database +func (p *provider) UpdateEnv(env models.Env) (models.Env, error) { + env.UpdatedAt = time.Now().Unix() + collection, _ := p.db.Collection(nil, models.Collections.Env) + meta, err := collection.UpdateDocument(nil, env.Key, env) + if err != nil { + log.Println("error updating config:", err) + return env, err + } + + env.Key = meta.Key + env.ID = meta.ID.String() + return env, nil +} + +// GetEnv to get environment information from database +func (p *provider) GetEnv() (models.Env, error) { + var env models.Env + query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.Env) + + cursor, err := p.db.Query(nil, query, nil) + if err != nil { + return env, err + } + defer cursor.Close() + + for { + if !cursor.HasMore() { + if env.Key == "" { + return env, fmt.Errorf("config not found") + } + break + } + _, err := cursor.ReadDocument(nil, &env) + if err != nil { + return env, err + } + } + + return env, nil +} diff --git a/server/db/providers/arangodb/session.go b/server/db/providers/arangodb/session.go new file mode 100644 index 0000000..9cfc8be --- /dev/null +++ b/server/db/providers/arangodb/session.go @@ -0,0 +1,42 @@ +package arangodb + +import ( + "fmt" + "log" + "time" + + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/google/uuid" +) + +// AddSession to save session information in database +func (p *provider) AddSession(session models.Session) error { + if session.ID == "" { + session.ID = uuid.New().String() + } + + session.CreatedAt = time.Now().Unix() + session.UpdatedAt = time.Now().Unix() + sessionCollection, _ := p.db.Collection(nil, models.Collections.Session) + _, err := sessionCollection.CreateDocument(nil, session) + if err != nil { + log.Println(`error saving session`, err) + return err + } + return nil +} + +// DeleteSession to delete session information from database +func (p *provider) DeleteSession(userId string) error { + query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @userId REMOVE { _key: d._key } IN %s`, models.Collections.Session, models.Collections.Session) + bindVars := map[string]interface{}{ + "userId": userId, + } + cursor, err := p.db.Query(nil, query, bindVars) + if err != nil { + log.Println("=> error deleting arangodb session:", err) + return err + } + defer cursor.Close() + return nil +} diff --git a/server/db/providers/mongodb/env.go b/server/db/providers/mongodb/env.go new file mode 100644 index 0000000..0d144e6 --- /dev/null +++ b/server/db/providers/mongodb/env.go @@ -0,0 +1,66 @@ +package mongodb + +import ( + "fmt" + "log" + "time" + + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// AddEnv to save environment information in database +func (p *provider) AddEnv(env models.Env) (models.Env, error) { + if env.ID == "" { + env.ID = uuid.New().String() + } + + env.CreatedAt = time.Now().Unix() + env.UpdatedAt = time.Now().Unix() + env.Key = env.ID + configCollection := p.db.Collection(models.Collections.Env, options.Collection()) + _, err := configCollection.InsertOne(nil, env) + if err != nil { + log.Println("error adding config:", err) + return env, err + } + return env, nil +} + +// UpdateEnv to update environment information in database +func (p *provider) UpdateEnv(env models.Env) (models.Env, error) { + env.UpdatedAt = time.Now().Unix() + configCollection := p.db.Collection(models.Collections.Env, options.Collection()) + _, err := configCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": env.ID}}, bson.M{"$set": env}, options.MergeUpdateOptions()) + if err != nil { + log.Println("error updating config:", err) + return env, err + } + return env, nil +} + +// GetEnv to get environment information from database +func (p *provider) GetEnv() (models.Env, error) { + var env models.Env + configCollection := p.db.Collection(models.Collections.Env, options.Collection()) + cursor, err := configCollection.Find(nil, bson.M{}, options.Find()) + if err != nil { + return env, err + } + defer cursor.Close(nil) + + for cursor.Next(nil) { + err := cursor.Decode(&env) + if err != nil { + return env, err + } + } + + if env.ID == "" { + return env, fmt.Errorf("config not found") + } + + return env, nil +} diff --git a/server/db/providers/mongodb/mongodb.go b/server/db/providers/mongodb/mongodb.go new file mode 100644 index 0000000..b53b276 --- /dev/null +++ b/server/db/providers/mongodb/mongodb.go @@ -0,0 +1,88 @@ +package mongodb + +import ( + "context" + "time" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/authorizerdev/authorizer/server/envstore" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +type provider struct { + db *mongo.Database +} + +// NewProvider to initialize mongodb connection +func NewProvider() (*provider, error) { + mongodbOptions := options.Client().ApplyURI(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)) + maxWait := time.Duration(5 * time.Second) + mongodbOptions.ConnectTimeout = &maxWait + mongoClient, err := mongo.NewClient(mongodbOptions) + if err != nil { + return nil, err + } + ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) + err = mongoClient.Connect(ctx) + if err != nil { + return nil, err + } + + err = mongoClient.Ping(ctx, readpref.Primary()) + if err != nil { + return nil, err + } + + mongodb := mongoClient.Database(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName), options.Database()) + + mongodb.CreateCollection(ctx, models.Collections.User, options.CreateCollection()) + userCollection := mongodb.Collection(models.Collections.User, options.Collection()) + userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{ + mongo.IndexModel{ + Keys: bson.M{"email": 1}, + Options: options.Index().SetUnique(true).SetSparse(true), + }, + }, options.CreateIndexes()) + userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{ + mongo.IndexModel{ + Keys: bson.M{"phone_number": 1}, + Options: options.Index().SetUnique(true).SetSparse(true).SetPartialFilterExpression(map[string]interface{}{ + "phone_number": map[string]string{"$type": "string"}, + }), + }, + }, options.CreateIndexes()) + + mongodb.CreateCollection(ctx, models.Collections.VerificationRequest, options.CreateCollection()) + verificationRequestCollection := mongodb.Collection(models.Collections.VerificationRequest, options.Collection()) + verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{ + mongo.IndexModel{ + Keys: bson.M{"email": 1, "identifier": 1}, + Options: options.Index().SetUnique(true).SetSparse(true), + }, + }, options.CreateIndexes()) + verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{ + mongo.IndexModel{ + Keys: bson.M{"token": 1}, + Options: options.Index().SetSparse(true), + }, + }, options.CreateIndexes()) + + mongodb.CreateCollection(ctx, models.Collections.Session, options.CreateCollection()) + sessionCollection := mongodb.Collection(models.Collections.Session, options.Collection()) + sessionCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{ + mongo.IndexModel{ + Keys: bson.M{"user_id": 1}, + Options: options.Index().SetSparse(true), + }, + }, options.CreateIndexes()) + + mongodb.CreateCollection(ctx, models.Collections.Env, options.CreateCollection()) + + return &provider{ + db: mongodb, + }, nil +} diff --git a/server/db/providers/mongodb/session.go b/server/db/providers/mongodb/session.go new file mode 100644 index 0000000..88c492f --- /dev/null +++ b/server/db/providers/mongodb/session.go @@ -0,0 +1,40 @@ +package mongodb + +import ( + "log" + "time" + + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// AddSession to save session information in database +func (p *provider) AddSession(session models.Session) error { + if session.ID == "" { + session.ID = uuid.New().String() + } + + session.Key = session.ID + session.CreatedAt = time.Now().Unix() + session.UpdatedAt = time.Now().Unix() + sessionCollection := p.db.Collection(models.Collections.Session, options.Collection()) + _, err := sessionCollection.InsertOne(nil, session) + if err != nil { + log.Println(`error saving session`, err) + return err + } + return nil +} + +// DeleteSession to delete session information from database +func (p *provider) DeleteSession(userId string) error { + sessionCollection := p.db.Collection(models.Collections.Session, options.Collection()) + _, err := sessionCollection.DeleteMany(nil, bson.M{"user_id": userId}, options.Delete()) + if err != nil { + log.Println("error deleting session:", err) + return err + } + return nil +} diff --git a/server/db/providers/providers.go b/server/db/providers/providers.go new file mode 100644 index 0000000..80a9395 --- /dev/null +++ b/server/db/providers/providers.go @@ -0,0 +1,41 @@ +package providers + +import "github.com/authorizerdev/authorizer/server/db/models" + +type Provider interface { + // AddUser to save user information in database + AddUser(user models.User) (models.User, error) + // UpdateUser to update user information in database + UpdateUser(user models.User) (models.User, error) + // DeleteUser to delete user information from database + DeleteUser(user models.User) error + // ListUsers to get list of users from database + ListUsers() ([]models.User, error) + // GetUserByEmail to get user information from database using email address + GetUserByEmail(email string) (models.User, error) + // GetUserByID to get user information from database using user ID + GetUserByID(id string) (models.User, error) + + // AddVerification to save verification request in database + AddVerificationRequest(verification models.VerificationRequest) (models.VerificationRequest, error) + // GetVerificationRequestByToken to get verification request from database using token + GetVerificationRequestByToken(token string) (models.VerificationRequest, error) + // GetVerificationRequestByEmail to get verification request by email from database + GetVerificationRequestByEmail(email string, identifier string) (models.VerificationRequest, error) + // ListVerificationRequests to get list of verification requests from database + ListVerificationRequests() ([]models.VerificationRequest, error) + // DeleteVerificationRequest to delete verification request from database + DeleteVerificationRequest(verificationRequest models.VerificationRequest) error + + // AddSession to save session information in database + AddSession(session models.Session) error + // DeleteSession to delete session information from database + DeleteSession(userId string) error + + // AddEnv to save environment information in database + AddEnv(env models.Env) (models.Env, error) + // UpdateEnv to update environment information in database + UpdateEnv(env models.Env) (models.Env, error) + // GetEnv to get environment information from database + GetEnv() (models.Env, error) +} diff --git a/server/db/providers/sql/env.go b/server/db/providers/sql/env.go new file mode 100644 index 0000000..489fa8d --- /dev/null +++ b/server/db/providers/sql/env.go @@ -0,0 +1,49 @@ +package sql + +import ( + "log" + "time" + + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/google/uuid" +) + +// AddEnv to save environment information in database +func (p *provider) AddEnv(env models.Env) (models.Env, error) { + if env.ID == "" { + env.ID = uuid.New().String() + } + + env.Key = env.ID + result := p.db.Create(&env) + + if result.Error != nil { + log.Println("error adding config:", result.Error) + return env, result.Error + } + return env, nil +} + +// UpdateEnv to update environment information in database +func (p *provider) UpdateEnv(env models.Env) (models.Env, error) { + env.UpdatedAt = time.Now().Unix() + result := p.db.Save(&env) + + if result.Error != nil { + log.Println("error updating config:", result.Error) + return env, result.Error + } + return env, nil +} + +// GetEnv to get environment information from database +func (p *provider) GetEnv() (models.Env, error) { + var env models.Env + result := p.db.First(&env) + + if result.Error != nil { + return env, result.Error + } + + return env, nil +} diff --git a/server/db/providers/sql/session.go b/server/db/providers/sql/session.go new file mode 100644 index 0000000..28230b5 --- /dev/null +++ b/server/db/providers/sql/session.go @@ -0,0 +1,38 @@ +package sql + +import ( + "log" + + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/google/uuid" + "gorm.io/gorm/clause" +) + +// AddSession to save session information in database +func (p *provider) AddSession(session models.Session) error { + if session.ID == "" { + session.ID = uuid.New().String() + } + + session.Key = session.ID + res := p.db.Clauses( + clause.OnConflict{ + DoNothing: true, + }).Create(&session) + if res.Error != nil { + log.Println(`error saving session`, res.Error) + return res.Error + } + return nil +} + +// DeleteSession to delete session information from database +func (p *provider) DeleteSession(userId string) error { + result := p.db.Where("user_id = ?", userId).Delete(&models.Session{}) + + if result.Error != nil { + log.Println(`error deleting session:`, result.Error) + return result.Error + } + return nil +} diff --git a/server/db/providers/sql/sql.go b/server/db/providers/sql/sql.go new file mode 100644 index 0000000..ccc1c2c --- /dev/null +++ b/server/db/providers/sql/sql.go @@ -0,0 +1,53 @@ +package sql + +import ( + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db/models" + "github.com/authorizerdev/authorizer/server/envstore" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" + "gorm.io/driver/sqlserver" + "gorm.io/gorm" + "gorm.io/gorm/schema" +) + +type provider struct { + db *gorm.DB +} + +// NewProvider returns a new SQL provider +func NewProvider() (*provider, error) { + var sqlDB *gorm.DB + var err error + + ormConfig := &gorm.Config{ + NamingStrategy: schema.NamingStrategy{ + TablePrefix: models.Prefix, + }, + } + + switch envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) { + case constants.DbTypePostgres: + sqlDB, err = gorm.Open(postgres.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig) + break + case constants.DbTypeSqlite: + sqlDB, err = gorm.Open(sqlite.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig) + break + case constants.DbTypeMysql: + sqlDB, err = gorm.Open(mysql.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig) + break + case constants.DbTypeSqlserver: + sqlDB, err = gorm.Open(sqlserver.Open(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)), ormConfig) + break + } + + if err != nil { + return nil, err + } + + sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}) + return &provider{ + db: sqlDB, + }, nil +} diff --git a/server/utils/common.go b/server/utils/common.go index 52348bf..1feadf8 100644 --- a/server/utils/common.go +++ b/server/utils/common.go @@ -1,7 +1,10 @@ package utils import ( + "log" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/db/models" "github.com/gin-gonic/gin" ) @@ -16,13 +19,18 @@ func StringSliceContains(s []string, e string) bool { } // SaveSessionInDB saves sessions generated for a given user with meta information -// Not store token here as that could be security breach +// Do not store token here as that could be security breach func SaveSessionInDB(userId string, c *gin.Context) { - sessionData := db.Session{ + sessionData := models.Session{ UserID: userId, UserAgent: GetUserAgent(c.Request), IP: GetIP(c.Request), } - db.Mgr.AddSession(sessionData) + err := db.Provider.AddSession(&sessionData) + if err != nil { + log.Println("=> error saving session in db:", err) + } else { + log.Println("=> session saved in db:", sessionData) + } }