From f1102553104d4e544fc00be7e30fc5b535ff3da0 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 17 Dec 2021 21:25:07 +0530 Subject: [PATCH] feat/add arangodb support (#80) *feat: add arangodb init * fix: dao for sql + arangodb * fix: update error logs * fix: use db name dynamically --- Dockerfile | 2 +- server/constants/constants.go | 1 + server/constants/oauthInfoUrls.go | 1 + server/db/arangodb.go | 113 ++++++++++++ server/db/db.go | 93 +++++++--- server/db/roles.go | 34 ---- server/db/session.go | 67 ++++--- server/db/user.go | 243 +++++++++++++++++++------- server/db/verificationRequests.go | 199 ++++++++++++++++----- server/enum/dbType.go | 2 + server/env.go | 9 +- server/go.mod | 3 +- server/go.sum | 11 ++ server/handlers/oauthCallback.go | 10 +- server/handlers/verifyEmail.go | 9 +- server/main.go | 3 +- server/resolvers/adminUpdateUser.go | 4 +- server/resolvers/deleteUser.go | 4 +- server/resolvers/forgotPassword.go | 2 +- server/resolvers/login.go | 4 +- server/resolvers/logout.go | 2 +- server/resolvers/magicLogin.go | 10 +- server/resolvers/resendVerifyEmail.go | 2 +- server/resolvers/resetPassword.go | 4 +- server/resolvers/signup.go | 8 +- server/resolvers/token.go | 4 +- server/resolvers/updateProfile.go | 4 +- server/resolvers/verifyEmail.go | 9 +- server/session/inMemoryStore.go | 5 +- server/session/redisStore.go | 6 +- server/session/session.go | 10 +- server/utils/authToken.go | 4 +- server/utils/initServer.go | 24 --- 33 files changed, 640 insertions(+), 266 deletions(-) create mode 100644 server/db/arangodb.go delete mode 100644 server/db/roles.go diff --git a/Dockerfile b/Dockerfile index 8cf9c40..1dbe626 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16-alpine as builder +FROM golang:1.17-alpine as builder WORKDIR /app COPY server server COPY Makefile . diff --git a/server/constants/constants.go b/server/constants/constants.go index b61db88..e5ad0aa 100644 --- a/server/constants/constants.go +++ b/server/constants/constants.go @@ -6,6 +6,7 @@ var ( VERSION = "" DATABASE_TYPE = "" DATABASE_URL = "" + DATABASE_NAME = "" SMTP_HOST = "" SMTP_PORT = "" SENDER_EMAIL = "" diff --git a/server/constants/oauthInfoUrls.go b/server/constants/oauthInfoUrls.go index 34756ff..220fb95 100644 --- a/server/constants/oauthInfoUrls.go +++ b/server/constants/oauthInfoUrls.go @@ -2,6 +2,7 @@ package constants var ( // Ref: https://github.com/qor/auth/blob/master/providers/google/google.go + // deprecated and not used. instead we follow open id approach for google login GoogleUserInfoURL = "https://www.googleapis.com/oauth2/v3/userinfo" // Ref: https://github.com/qor/auth/blob/master/providers/facebook/facebook.go#L18 FacebookUserInfoURL = "https://graph.facebook.com/me?fields=id,first_name,last_name,name,email,picture&access_token=" diff --git a/server/db/arangodb.go b/server/db/arangodb.go new file mode 100644 index 0000000..e8eb1b1 --- /dev/null +++ b/server/db/arangodb.go @@ -0,0 +1,113 @@ +package db + +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" +) + +// 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 + +func initArangodb() (arangoDriver.Database, error) { + ctx := context.Background() + conn, err := http.NewConnection(http.ConnectionConfig{ + Endpoints: []string{constants.DATABASE_URL}, + }) + if err != nil { + return nil, err + } + + client, err := arangoDriver.NewClient(arangoDriver.ClientConfig{ + Connection: conn, + }) + if err != nil { + return nil, err + } + + var arangodb driver.Database + + arangodb_exists, err := client.DatabaseExists(nil, constants.DATABASE_NAME) + + if arangodb_exists { + log.Println(constants.DATABASE_NAME + " db exists already") + + arangodb, err = client.Database(nil, constants.DATABASE_NAME) + + if err != nil { + return nil, err + } + + } else { + arangodb, err = client.CreateDatabase(nil, constants.DATABASE_NAME, nil) + + if err != nil { + return nil, err + } + } + + userCollectionExists, err := arangodb.CollectionExists(ctx, Collections.User) + if userCollectionExists { + log.Println(Collections.User + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, Collections.User, nil) + if err != nil { + log.Println("error creating collection("+Collections.User+"):", err) + } + } + userCollection, _ := arangodb.Collection(nil, Collections.User) + userCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + userCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + + verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, Collections.VerificationRequest) + if verificationRequestCollectionExists { + log.Println(Collections.VerificationRequest + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, Collections.VerificationRequest, nil) + if err != nil { + log.Println("error creating collection("+Collections.VerificationRequest+"):", err) + } + } + verificationRequestCollection, _ := arangodb.Collection(nil, Collections.VerificationRequest) + verificationRequestCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + verificationRequestCollection.EnsureHashIndex(ctx, []string{"email", "identifier"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + verificationRequestCollection.EnsureHashIndex(ctx, []string{"token"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + + sessionCollectionExists, err := arangodb.CollectionExists(ctx, Collections.Session) + if sessionCollectionExists { + log.Println(Collections.Session + " collection exists already") + } else { + _, err = arangodb.CreateCollection(ctx, Collections.Session, nil) + if err != nil { + log.Println("error creating collection("+Collections.Session+"):", err) + } + } + + sessionCollection, _ := arangodb.Collection(nil, Collections.Session) + sessionCollection.EnsureHashIndex(ctx, []string{"id"}, &arangoDriver.EnsureHashIndexOptions{ + Unique: true, + Sparse: true, + }) + + return arangodb, err +} diff --git a/server/db/db.go b/server/db/db.go index e856d1d..acdf688 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -3,9 +3,9 @@ package db import ( "log" + arangoDriver "github.com/arangodb/go-driver" "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/enum" - "github.com/google/uuid" "gorm.io/driver/mysql" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -14,51 +14,94 @@ import ( ) type Manager interface { - SaveUser(user User) (User, error) + AddUser(user User) (User, error) UpdateUser(user User) (User, error) + DeleteUser(user User) error GetUsers() ([]User, error) GetUserByEmail(email string) (User, error) GetUserByID(email string) (User, error) - UpdateVerificationTime(verifiedAt int64, id uuid.UUID) error AddVerification(verification VerificationRequest) (VerificationRequest, error) GetVerificationByToken(token string) (VerificationRequest, error) - DeleteToken(email string) error + DeleteVerificationRequest(verificationRequest VerificationRequest) error GetVerificationRequests() ([]VerificationRequest, error) GetVerificationByEmail(email string) (VerificationRequest, error) - DeleteUser(email string) error - SaveRoles(roles []Role) error - SaveSession(session Session) error + AddSession(session Session) error } type manager struct { - db *gorm.DB + sqlDB *gorm.DB + arangodb arangoDriver.Database } -var Mgr Manager +// mainly used by nosql dbs +type CollectionList struct { + User string + VerificationRequest string + Session string +} + +var ( + IsSQL bool + IsArangoDB bool + Mgr Manager + Prefix = "authorizer_" + Collections = CollectionList{ + User: Prefix + "users", + VerificationRequest: Prefix + "verification_requests", + Session: Prefix + "sessions", + } +) func InitDB() { - var db *gorm.DB + var sqlDB *gorm.DB var err error + + IsSQL = constants.DATABASE_TYPE != enum.Arangodb.String() + IsArangoDB = constants.DATABASE_TYPE == enum.Arangodb.String() + + // sql db orm config ormConfig := &gorm.Config{ NamingStrategy: schema.NamingStrategy{ - TablePrefix: "authorizer_", + TablePrefix: Prefix, }, } - if constants.DATABASE_TYPE == enum.Postgres.String() { - db, err = gorm.Open(postgres.Open(constants.DATABASE_URL), ormConfig) - } - if constants.DATABASE_TYPE == enum.Mysql.String() { - db, err = gorm.Open(mysql.Open(constants.DATABASE_URL), ormConfig) - } - if constants.DATABASE_TYPE == enum.Sqlite.String() { - db, err = gorm.Open(sqlite.Open(constants.DATABASE_URL), ormConfig) + + log.Println("db type:", constants.DATABASE_TYPE) + + switch constants.DATABASE_TYPE { + case enum.Postgres.String(): + sqlDB, err = gorm.Open(postgres.Open(constants.DATABASE_URL), ormConfig) + break + case enum.Sqlite.String(): + sqlDB, err = gorm.Open(sqlite.Open(constants.DATABASE_URL), ormConfig) + break + case enum.Mysql.String(): + sqlDB, err = gorm.Open(mysql.Open(constants.DATABASE_URL), ormConfig) + break + case enum.Arangodb.String(): + arangodb, err := initArangodb() + if err != nil { + log.Fatal("error initing arangodb:", err) + } + + Mgr = &manager{ + sqlDB: nil, + arangodb: arangodb, + } + + break } - if err != nil { - log.Fatal("Failed to init db:", err) - } else { - db.AutoMigrate(&User{}, &VerificationRequest{}, &Role{}, &Session{}) + // common for all sql dbs that are configured via gorm + if IsSQL { + if err != nil { + log.Fatal("Failed to init sqlDB:", err) + } else { + sqlDB.AutoMigrate(&User{}, &VerificationRequest{}, &Session{}) + } + Mgr = &manager{ + sqlDB: sqlDB, + arangodb: nil, + } } - - Mgr = &manager{db: db} } diff --git a/server/db/roles.go b/server/db/roles.go deleted file mode 100644 index 2b35c5a..0000000 --- a/server/db/roles.go +++ /dev/null @@ -1,34 +0,0 @@ -package db - -import ( - "log" - - "github.com/google/uuid" - "gorm.io/gorm" - "gorm.io/gorm/clause" -) - -type Role struct { - ID uuid.UUID `gorm:"primaryKey;type:char(36)"` - Role string `gorm:"unique"` -} - -func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { - r.ID = uuid.New() - - return -} - -// SaveRoles function to save roles -func (mgr *manager) SaveRoles(roles []Role) error { - res := mgr.db.Clauses( - clause.OnConflict{ - DoNothing: true, - }).Create(&roles) - if res.Error != nil { - log.Println(`Error saving roles`) - return res.Error - } - - return nil -} diff --git a/server/db/session.go b/server/db/session.go index 0912457..07baca5 100644 --- a/server/db/session.go +++ b/server/db/session.go @@ -2,37 +2,62 @@ package db import ( "log" + "time" "github.com/google/uuid" - "gorm.io/gorm" "gorm.io/gorm/clause" ) type Session struct { - ID uuid.UUID `gorm:"primaryKey;type:char(36)"` - UserID uuid.UUID `gorm:"type:char(36)"` - User User - UserAgent string - IP string - CreatedAt int64 `gorm:"autoCreateTime"` - UpdatedAt int64 `gorm:"autoUpdateTime"` + Key string `json:"_key,omitempty"` // for arangodb + ObjectID string `json:"_id,omitempty"` // for arangodb & mongodb + ID string `gorm:"primaryKey;type:char(36)" json:"id"` + UserID string `gorm:"type:char(36)" json:"user_id"` + User User `json:"-"` + UserAgent string `json:"user_agent"` + IP string `json:"ip"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at"` } -func (r *Session) BeforeCreate(tx *gorm.DB) (err error) { - r.ID = uuid.New() +// AddSession function to save user sessiosn +func (mgr *manager) AddSession(session Session) error { + if session.ID == "" { + session.ID = uuid.New().String() + } - return -} + if session.CreatedAt == 0 { + session.CreatedAt = time.Now().Unix() + } -// SaveSession function to save user sessiosn -func (mgr *manager) SaveSession(session Session) error { - res := mgr.db.Clauses( - clause.OnConflict{ - DoNothing: true, - }).Create(&session) - if res.Error != nil { - log.Println(`Error saving session`, res.Error) - return res.Error + if session.UpdatedAt == 0 { + session.CreatedAt = time.Now().Unix() + } + + if IsSQL { + // copy id as value for fields required for mongodb & arangodb + session.Key = session.ID + session.ObjectID = session.ID + res := mgr.sqlDB.Clauses( + clause.OnConflict{ + DoNothing: true, + }).Create(&session) + if res.Error != nil { + log.Println(`error saving session`, res.Error) + return res.Error + } + } + + if IsArangoDB { + + session.CreatedAt = time.Now().Unix() + session.UpdatedAt = time.Now().Unix() + sessionCollection, _ := mgr.arangodb.Collection(nil, Collections.Session) + _, err := sessionCollection.CreateDocument(nil, session) + if err != nil { + log.Println(`error saving session`, err) + return err + } } return nil diff --git a/server/db/user.go b/server/db/user.go index b359850..549091c 100644 --- a/server/db/user.go +++ b/server/db/user.go @@ -1,45 +1,68 @@ package db import ( + "context" + "errors" + "fmt" "log" "time" + "github.com/arangodb/go-driver" + arangoDriver "github.com/arangodb/go-driver" "github.com/google/uuid" - "gorm.io/gorm" "gorm.io/gorm/clause" ) type User struct { - ID uuid.UUID `gorm:"primaryKey;type:char(36)"` - FirstName string - LastName string - Email string `gorm:"unique"` - Password string `gorm:"type:text"` - SignupMethod string - EmailVerifiedAt int64 - CreatedAt int64 `gorm:"autoCreateTime"` - UpdatedAt int64 `gorm:"autoUpdateTime"` - Image string `gorm:"type:text"` - Roles string + Key string `json:"_key,omitempty"` // for arangodb + ObjectID string `json:"_id,omitempty"` // for arangodb & mongodb + ID string `gorm:"primaryKey;type:char(36)" json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Email string `gorm:"unique" json:"email"` + Password string `gorm:"type:text" json:"password"` + SignupMethod string `json:"signup_method"` + EmailVerifiedAt int64 `json:"email_verified_at"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at"` + Image string `gorm:"type:text" json:"image"` + Roles string `json:"roles"` } -func (u *User) BeforeCreate(tx *gorm.DB) (err error) { - u.ID = uuid.New() +// AddUser function to add user even with email conflict +func (mgr *manager) AddUser(user User) (User, error) { + if user.ID == "" { + user.ID = uuid.New().String() + } - return -} + if IsSQL { + // copy id as value for fields required for mongodb & arangodb + user.Key = user.ID + user.ObjectID = user.ID + result := mgr.sqlDB.Clauses( + clause.OnConflict{ + UpdateAll: true, + Columns: []clause.Column{{Name: "email"}}, + }).Create(&user) -// SaveUser function to add user even with email conflict -func (mgr *manager) SaveUser(user User) (User, error) { - result := mgr.db.Clauses( - clause.OnConflict{ - UpdateAll: true, - Columns: []clause.Column{{Name: "email"}}, - }).Create(&user) + if result.Error != nil { + log.Println("error adding user:", result.Error) + return user, result.Error + } + } - if result.Error != nil { - log.Println(result.Error) - return user, result.Error + if IsArangoDB { + user.CreatedAt = time.Now().Unix() + user.UpdatedAt = time.Now().Unix() + ctx := context.Background() + userCollection, _ := mgr.arangodb.Collection(nil, Collections.User) + meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), user) + if err != nil { + log.Println("error adding user:", err) + return user, err + } + user.Key = meta.Key + user.ObjectID = meta.ID.String() } return user, nil } @@ -47,15 +70,26 @@ func (mgr *manager) SaveUser(user User) (User, error) { // UpdateUser function to update user with ID conflict func (mgr *manager) UpdateUser(user User) (User, error) { user.UpdatedAt = time.Now().Unix() - result := mgr.db.Clauses( - clause.OnConflict{ - UpdateAll: true, - Columns: []clause.Column{{Name: "email"}}, - }).Create(&user) - if result.Error != nil { - log.Println(result.Error) - return user, result.Error + if IsSQL { + result := mgr.sqlDB.Save(&user) + + if result.Error != nil { + log.Println("error updating user:", result.Error) + return user, result.Error + } + } + + if IsArangoDB { + collection, _ := mgr.arangodb.Collection(nil, Collections.User) + meta, err := collection.UpdateDocument(nil, user.Key, user) + if err != nil { + log.Println("error updating user:", err) + return user, err + } + + user.Key = meta.Key + user.ObjectID = meta.ID.String() } return user, nil } @@ -63,20 +97,76 @@ func (mgr *manager) UpdateUser(user User) (User, error) { // GetUsers function to get all users func (mgr *manager) GetUsers() ([]User, error) { var users []User - result := mgr.db.Find(&users) - if result.Error != nil { - log.Println(result.Error) - return users, result.Error + + if IsSQL { + result := mgr.sqlDB.Find(&users) + if result.Error != nil { + log.Println("error getting users:", result.Error) + return users, result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf("FOR d in %s RETURN d", Collections.User) + + cursor, err := mgr.arangodb.Query(nil, query, nil) + if err != nil { + return users, err + } + defer cursor.Close() + + for { + var user User + meta, err := cursor.ReadDocument(nil, &user) + + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return users, err + } + + if meta.Key != "" { + user.Key = meta.Key + user.ObjectID = meta.ID.String() + users = append(users, user) + } + + } } return users, nil } func (mgr *manager) GetUserByEmail(email string) (User, error) { var user User - result := mgr.db.Where("email = ?", email).First(&user) - if result.Error != nil { - return user, result.Error + if IsSQL { + result := mgr.sqlDB.Where("email = ?", email).First(&user) + + if result.Error != nil { + return user, result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf("FOR d in %s FILTER d.email == @email LIMIT 1 RETURN d", Collections.User) + bindVars := map[string]interface{}{ + "email": email, + } + + cursor, err := mgr.arangodb.Query(nil, query, bindVars) + if err != nil { + return user, err + } + defer cursor.Close() + + for { + _, err := cursor.ReadDocument(nil, &user) + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return user, err + } + } } return user, nil @@ -84,35 +174,62 @@ func (mgr *manager) GetUserByEmail(email string) (User, error) { func (mgr *manager) GetUserByID(id string) (User, error) { var user User - result := mgr.db.Where("id = ?", id).First(&user) - if result.Error != nil { - return user, result.Error + if IsSQL { + result := mgr.sqlDB.Where("id = ?", id).First(&user) + + if result.Error != nil { + return user, result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf("FOR d in %s FILTER d.id == @id LIMIT 1 RETURN d", Collections.User) + bindVars := map[string]interface{}{ + "id": id, + } + + cursor, err := mgr.arangodb.Query(nil, query, bindVars) + if err != nil { + return user, err + } + defer cursor.Close() + + count := cursor.Count() + if count == 0 { + return user, errors.New("user not found") + } + + for { + _, err := cursor.ReadDocument(nil, &user) + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return user, err + } + } } return user, nil } -func (mgr *manager) UpdateVerificationTime(verifiedAt int64, id uuid.UUID) error { - user := &User{ - ID: id, - } - result := mgr.db.Model(&user).Where("id = ?", id).Update("email_verified_at", verifiedAt) +func (mgr *manager) DeleteUser(user User) error { + if IsSQL { + result := mgr.sqlDB.Delete(&user) - if result.Error != nil { - return result.Error - } - - return nil -} - -func (mgr *manager) DeleteUser(email string) error { - var user User - result := mgr.db.Where("email = ?", email).Delete(&user) - - if result.Error != nil { - log.Println(`Error deleting user:`, result.Error) - return result.Error + if result.Error != nil { + log.Println(`error deleting user:`, result.Error) + return result.Error + } + } + + if IsArangoDB { + collection, _ := mgr.arangodb.Collection(nil, Collections.User) + _, err := collection.RemoveDocument(nil, user.Key) + if err != nil { + log.Println(`error deleting user:`, err) + return err + } } return nil diff --git a/server/db/verificationRequests.go b/server/db/verificationRequests.go index eb23171..38a11b6 100644 --- a/server/db/verificationRequests.go +++ b/server/db/verificationRequests.go @@ -1,50 +1,132 @@ package db import ( + "fmt" "log" + "github.com/arangodb/go-driver" "github.com/google/uuid" - "gorm.io/gorm" "gorm.io/gorm/clause" ) type VerificationRequest struct { - ID uuid.UUID `gorm:"primaryKey;type:char(36)"` - Token string `gorm:"type:text"` - Identifier string - ExpiresAt int64 - CreatedAt int64 `gorm:"autoCreateTime"` - UpdatedAt int64 `gorm:"autoUpdateTime"` - Email string `gorm:"unique"` -} - -func (v *VerificationRequest) BeforeCreate(tx *gorm.DB) (err error) { - v.ID = uuid.New() - - return + Key string `json:"_key,omitempty"` // for arangodb + ObjectID string `json:"_id,omitempty"` // for arangodb & mongodb + ID string `gorm:"primaryKey;type:char(36)" json:"id"` + Token string `gorm:"type:text" json:"token"` + Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier"` + ExpiresAt int64 `json:"expires_at"` + CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at"` + Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email"` } // AddVerification function to add verification record func (mgr *manager) AddVerification(verification VerificationRequest) (VerificationRequest, error) { - result := mgr.db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "email"}}, - DoUpdates: clause.AssignmentColumns([]string{"token", "identifier", "expires_at"}), - }).Create(&verification) + if verification.ID == "" { + verification.ID = uuid.New().String() + } + if IsSQL { + // copy id as value for fields required for mongodb & arangodb + verification.Key = verification.ID + verification.ObjectID = verification.ID + result := mgr.sqlDB.Clauses(clause.OnConflict{ + DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}), + }).Create(&verification) - if result.Error != nil { - log.Println(`Error saving verification record`, result.Error) - return verification, result.Error + if result.Error != nil { + log.Println(`error saving verification record`, result.Error) + return verification, result.Error + } + } + + if IsArangoDB { + verificationRequestCollection, _ := mgr.arangodb.Collection(nil, Collections.VerificationRequest) + meta, err := verificationRequestCollection.CreateDocument(nil, verification) + if err != nil { + return verification, err + } + verification.Key = meta.Key + verification.ObjectID = meta.ID.String() } return verification, nil } +// GetVerificationRequests function to get all verification requests +func (mgr *manager) GetVerificationRequests() ([]VerificationRequest, error) { + var verificationRequests []VerificationRequest + + if IsSQL { + result := mgr.sqlDB.Find(&verificationRequests) + if result.Error != nil { + log.Println("error getting verification requests:", result.Error) + return verificationRequests, result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf("FOR d in %s RETURN d", Collections.VerificationRequest) + + cursor, err := mgr.arangodb.Query(nil, query, nil) + if err != nil { + return verificationRequests, err + } + defer cursor.Close() + + for { + var verificationRequest VerificationRequest + meta, err := cursor.ReadDocument(nil, &verificationRequest) + + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return verificationRequests, err + } + + if meta.Key != "" { + verificationRequest.Key = meta.Key + verificationRequest.ObjectID = meta.ID.String() + verificationRequests = append(verificationRequests, verificationRequest) + } + + } + } + return verificationRequests, nil +} + func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, error) { var verification VerificationRequest - result := mgr.db.Where("token = ?", token).First(&verification) - if result.Error != nil { - log.Println(`Error getting verification token:`, result.Error) - return verification, result.Error + if IsSQL { + result := mgr.sqlDB.Where("token = ?", token).First(&verification) + + if result.Error != nil { + log.Println(`error getting verification request:`, result.Error) + return verification, result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf("FOR d in %s FILTER d.token == @token LIMIT 1 RETURN d", Collections.VerificationRequest) + bindVars := map[string]interface{}{ + "token": token, + } + + cursor, err := mgr.arangodb.Query(nil, query, bindVars) + if err != nil { + return verification, err + } + defer cursor.Close() + + for { + _, err := cursor.ReadDocument(nil, &verification) + + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return verification, err + } + } } return verification, nil @@ -52,35 +134,60 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) { var verification VerificationRequest - result := mgr.db.Where("email = ?", email).First(&verification) + if IsSQL { + result := mgr.sqlDB.Where("email = ?", email).First(&verification) - if result.Error != nil { - log.Println(`Error getting verification token:`, result.Error) - return verification, result.Error + if result.Error != nil { + log.Println(`error getting verification token:`, result.Error) + return verification, result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf("FOR d in %s FILTER d.email == @email LIMIT 1 RETURN d", Collections.VerificationRequest) + bindVars := map[string]interface{}{ + "email": email, + } + + cursor, err := mgr.arangodb.Query(nil, query, bindVars) + if err != nil { + return verification, err + } + defer cursor.Close() + + for { + _, err := cursor.ReadDocument(nil, &verification) + + if driver.IsNoMoreDocuments(err) { + break + } else if err != nil { + return verification, err + } + + } } return verification, nil } -func (mgr *manager) DeleteToken(email string) error { - var verification VerificationRequest - result := mgr.db.Where("email = ?", email).Delete(&verification) +func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRequest) error { + if IsSQL { + result := mgr.sqlDB.Delete(&verificationRequest) - if result.Error != nil { - log.Println(`Error deleting token:`, result.Error) - return result.Error + if result.Error != nil { + log.Println(`error deleting verification request:`, result.Error) + return result.Error + } + } + + if IsArangoDB { + collection, _ := mgr.arangodb.Collection(nil, Collections.VerificationRequest) + _, err := collection.RemoveDocument(nil, verificationRequest.Key) + if err != nil { + log.Println(`error deleting verification request:`, err) + return err + } } return nil } - -// GetUsers function to get all users -func (mgr *manager) GetVerificationRequests() ([]VerificationRequest, error) { - var verificationRequests []VerificationRequest - result := mgr.db.Find(&verificationRequests) - if result.Error != nil { - log.Println(result.Error) - return verificationRequests, result.Error - } - return verificationRequests, nil -} diff --git a/server/enum/dbType.go b/server/enum/dbType.go index 3a7d331..bd93507 100644 --- a/server/enum/dbType.go +++ b/server/enum/dbType.go @@ -6,6 +6,7 @@ const ( Postgres DbType = iota Sqlite Mysql + Arangodb ) func (d DbType) String() string { @@ -13,5 +14,6 @@ func (d DbType) String() string { "postgres", "sqlite", "mysql", + "arangodb", }[d] } diff --git a/server/env.go b/server/env.go index f650713..2a99e42 100644 --- a/server/env.go +++ b/server/env.go @@ -35,7 +35,7 @@ func InitEnv() { err := godotenv.Load(envPath) if err != nil { - log.Println("Error loading .env file") + log.Println("error loading .env file") } constants.VERSION = Version @@ -43,6 +43,7 @@ func InitEnv() { constants.ENV = os.Getenv("ENV") constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE") constants.DATABASE_URL = os.Getenv("DATABASE_URL") + constants.DATABASE_NAME = os.Getenv("DATABASE_NAME") constants.SMTP_HOST = os.Getenv("SMTP_HOST") constants.SMTP_PORT = os.Getenv("SMTP_PORT") constants.SENDER_EMAIL = os.Getenv("SENDER_EMAIL") @@ -115,6 +116,10 @@ func InitEnv() { panic("Database type is required") } + if constants.DATABASE_NAME == "" { + constants.DATABASE_NAME = "authorizer" + } + if constants.JWT_TYPE == "" { constants.JWT_TYPE = "HS256" } @@ -137,7 +142,7 @@ func InitEnv() { constants.DISABLE_EMAIL_VERIFICATION = "false" } - log.Println("=> disable email verification:", constants.DISABLE_EMAIL_VERIFICATION) + log.Println("email verification disabled:", constants.DISABLE_EMAIL_VERIFICATION) rolesSplit := strings.Split(os.Getenv("ROLES"), ",") roles := []string{} diff --git a/server/go.mod b/server/go.mod index d2d7063..fbba17d 100644 --- a/server/go.mod +++ b/server/go.mod @@ -4,12 +4,13 @@ go 1.16 require ( github.com/99designs/gqlgen v0.13.0 + github.com/arangodb/go-driver v1.2.1 // indirect github.com/coreos/go-oidc/v3 v3.1.0 // indirect github.com/gin-contrib/location v0.0.2 // indirect github.com/gin-gonic/gin v1.7.2 github.com/go-playground/validator/v10 v10.8.0 // indirect github.com/go-redis/redis/v8 v8.11.0 - github.com/golang-jwt/jwt v3.2.1+incompatible + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 github.com/jackc/pgproto3/v2 v2.1.0 // indirect diff --git a/server/go.sum b/server/go.sum index de89b22..eee02dc 100644 --- a/server/go.sum +++ b/server/go.sum @@ -74,6 +74,10 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/arangodb/go-driver v1.2.1 h1:HREDHhDmzdIWxHmfkfTESbYUnRjESjPh4WUuXq7FZa8= +github.com/arangodb/go-driver v1.2.1/go.mod h1:zdDkJJnCj8DAkfbtIjIXnsTrWIiy6VhP3Vy14p+uQeY= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2LcQBbxd0ZFdbGSyRKTYMZCfBbw/pMJFOk1g= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -109,6 +113,7 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/go-iptables v0.4.3/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -123,6 +128,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= @@ -278,6 +284,7 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -535,6 +542,8 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -569,6 +578,7 @@ github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -896,6 +906,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/server/handlers/oauthCallback.go b/server/handlers/oauthCallback.go index c8f7a7a..7632d30 100644 --- a/server/handlers/oauthCallback.go +++ b/server/handlers/oauthCallback.go @@ -129,7 +129,7 @@ func processFacebookUserInfo(code string) (db.User, error) { response, err := client.Do(req) if err != nil { - log.Println("err:", err) + log.Println("error processing facebook user info:", err) return user, err } @@ -217,6 +217,7 @@ func OAuthCallbackHandler() gin.HandlerFunc { } user.Roles = strings.Join(inputRoles, ",") + user, _ = db.Mgr.AddUser(user) } else { // user exists in db, check if method was google // if not append google to existing signup method and save it @@ -260,9 +261,12 @@ func OAuthCallbackHandler() gin.HandlerFunc { } else { user.Roles = existingUser.Roles } + user.Key = existingUser.Key + user.ObjectID = existingUser.ObjectID + user.ID = existingUser.ID + user, err = db.Mgr.UpdateUser(user) } - user, _ = db.Mgr.SaveUser(user) user, _ = db.Mgr.GetUserByEmail(user.Email) userIdStr := fmt.Sprintf("%v", user.ID) refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, inputRoles) @@ -277,7 +281,7 @@ func OAuthCallbackHandler() gin.HandlerFunc { IP: utils.GetIP(c.Request), } - db.Mgr.SaveSession(sessionData) + db.Mgr.AddSession(sessionData) }() c.Redirect(http.StatusTemporaryRedirect, redirectURL) diff --git a/server/handlers/verifyEmail.go b/server/handlers/verifyEmail.go index fbb080d..fecdbe3 100644 --- a/server/handlers/verifyEmail.go +++ b/server/handlers/verifyEmail.go @@ -24,7 +24,7 @@ func VerifyEmailHandler() gin.HandlerFunc { return } - _, err := db.Mgr.GetVerificationByToken(token) + verificationRequest, err := db.Mgr.GetVerificationByToken(token) if err != nil { c.JSON(400, errorRes) return @@ -47,10 +47,11 @@ func VerifyEmailHandler() gin.HandlerFunc { // update email_verified_at in users table if user.EmailVerifiedAt <= 0 { - db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID) + user.EmailVerifiedAt = time.Now().Unix() + db.Mgr.UpdateUser(user) } // delete from verification table - db.Mgr.DeleteToken(claim.Email) + db.Mgr.DeleteVerificationRequest(verificationRequest) userIdStr := fmt.Sprintf("%v", user.ID) roles := strings.Split(user.Roles, ",") @@ -66,7 +67,7 @@ func VerifyEmailHandler() gin.HandlerFunc { IP: utils.GetIP(c.Request), } - db.Mgr.SaveSession(sessionData) + db.Mgr.AddSession(sessionData) }() utils.SetCookie(c, accessToken) c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL) diff --git a/server/main.go b/server/main.go index afee5d1..41874f0 100644 --- a/server/main.go +++ b/server/main.go @@ -19,7 +19,7 @@ func GinContextToContextMiddleware() gin.HandlerFunc { if constants.AUTHORIZER_URL == "" { url := location.Get(c) constants.AUTHORIZER_URL = url.Scheme + "://" + c.Request.Host - log.Println("=> setting url:", constants.AUTHORIZER_URL) + log.Println("=> authorizer url:", constants.AUTHORIZER_URL) } ctx := context.WithValue(c.Request.Context(), "GinContextKey", c) c.Request = c.Request.WithContext(ctx) @@ -33,7 +33,6 @@ func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { origin := c.Request.Header.Get("Origin") constants.APP_URL = origin - log.Println("=> APP_URL:", constants.APP_URL) c.Writer.Header().Set("Access-Control-Allow-Origin", origin) c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") diff --git a/server/resolvers/adminUpdateUser.go b/server/resolvers/adminUpdateUser.go index 45d9733..602b7da 100644 --- a/server/resolvers/adminUpdateUser.go +++ b/server/resolvers/adminUpdateUser.go @@ -69,7 +69,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m verificationType := enum.UpdateEmail.String() token, err := utils.CreateVerificationToken(newEmail, verificationType) if err != nil { - log.Println(`Error generating token`, err) + log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, @@ -110,7 +110,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m user, err = db.Mgr.UpdateUser(user) if err != nil { - log.Println("Error updating user:", err) + log.Println("error updating user:", err) return res, err } diff --git a/server/resolvers/deleteUser.go b/server/resolvers/deleteUser.go index 31bb6cd..6037c75 100644 --- a/server/resolvers/deleteUser.go +++ b/server/resolvers/deleteUser.go @@ -29,9 +29,9 @@ func DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Respo session.DeleteUserSession(fmt.Sprintf("%x", user.ID)) - err = db.Mgr.DeleteUser(params.Email) + err = db.Mgr.DeleteUser(user) if err != nil { - log.Println("Err:", err) + log.Println("error deleting user:", err) return res, err } diff --git a/server/resolvers/forgotPassword.go b/server/resolvers/forgotPassword.go index c04d9eb..7485f10 100644 --- a/server/resolvers/forgotPassword.go +++ b/server/resolvers/forgotPassword.go @@ -37,7 +37,7 @@ func ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*mod token, err := utils.CreateVerificationToken(params.Email, enum.ForgotPassword.String()) if err != nil { - log.Println(`Error generating token`, err) + log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, diff --git a/server/resolvers/login.go b/server/resolvers/login.go index 5fb4f39..da32ef8 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -43,7 +43,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(params.Password)) if err != nil { - log.Println("Compare password error:", err) + log.Println("compare password error:", err) return res, fmt.Errorf(`invalid password`) } roles := constants.DEFAULT_ROLES @@ -68,7 +68,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e IP: utils.GetIP(gc.Request), } - db.Mgr.SaveSession(sessionData) + db.Mgr.AddSession(sessionData) }() res = &model.AuthResponse{ diff --git a/server/resolvers/logout.go b/server/resolvers/logout.go index 2a1633a..5e79300 100644 --- a/server/resolvers/logout.go +++ b/server/resolvers/logout.go @@ -27,7 +27,7 @@ func Logout(ctx context.Context) (*model.Response, error) { } userId := fmt.Sprintf("%v", claim["id"]) - session.DeleteToken(userId, token) + session.DeleteVerificationRequest(userId, token) res = &model.Response{ Message: "Logged out successfully", } diff --git a/server/resolvers/magicLogin.go b/server/resolvers/magicLogin.go index 0cd1b52..09e6ea5 100644 --- a/server/resolvers/magicLogin.go +++ b/server/resolvers/magicLogin.go @@ -35,6 +35,7 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo // find user with email existingUser, err := db.Mgr.GetUserByEmail(params.Email) + if err != nil { user.SignupMethod = enum.MagicLink.String() // define roles for new user @@ -50,6 +51,7 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo } user.Roles = strings.Join(inputRoles, ",") + user, _ = db.Mgr.AddUser(user) } else { user = existingUser // There multiple scenarios with roles here in magic link login @@ -90,16 +92,18 @@ func MagicLogin(ctx context.Context, params model.MagicLoginInput) (*model.Respo } user.SignupMethod = signupMethod + user, _ = db.Mgr.UpdateUser(user) + if err != nil { + log.Println("error updating user:", err) + } } - user, _ = db.Mgr.SaveUser(user) - if constants.DISABLE_EMAIL_VERIFICATION != "true" { // insert verification request verificationType := enum.MagicLink.String() token, err := utils.CreateVerificationToken(params.Email, verificationType) if err != nil { - log.Println(`Error generating token`, err) + log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, diff --git a/server/resolvers/resendVerifyEmail.go b/server/resolvers/resendVerifyEmail.go index e37837f..5b97aad 100644 --- a/server/resolvers/resendVerifyEmail.go +++ b/server/resolvers/resendVerifyEmail.go @@ -27,7 +27,7 @@ func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier) if err != nil { - log.Println(`Error generating token`, err) + log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, diff --git a/server/resolvers/resetPassword.go b/server/resolvers/resetPassword.go index 17f3ea5..c4cb08c 100644 --- a/server/resolvers/resetPassword.go +++ b/server/resolvers/resetPassword.go @@ -18,7 +18,7 @@ func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model return res, fmt.Errorf(`basic authentication is disabled for this instance`) } - _, err := db.Mgr.GetVerificationByToken(params.Token) + verificationRequest, err := db.Mgr.GetVerificationByToken(params.Token) if err != nil { return res, fmt.Errorf(`invalid token`) } @@ -48,7 +48,7 @@ func ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model user.SignupMethod = signupMethod // delete from verification table - db.Mgr.DeleteToken(claim.Email) + db.Mgr.DeleteVerificationRequest(verificationRequest) db.Mgr.UpdateUser(user) res = &model.Response{ diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index 6ee7393..cc70bae 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -51,7 +51,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, // find user with email existingUser, err := db.Mgr.GetUserByEmail(params.Email) if err != nil { - log.Println("User with email " + params.Email + " not found") + log.Println("user with email " + params.Email + " not found") } if existingUser.EmailVerifiedAt > 0 { @@ -79,7 +79,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, if constants.DISABLE_EMAIL_VERIFICATION == "true" { user.EmailVerifiedAt = time.Now().Unix() } - user, err = db.Mgr.SaveUser(user) + user, err = db.Mgr.AddUser(user) if err != nil { return res, err } @@ -103,7 +103,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, verificationType := enum.BasicAuthSignup.String() token, err := utils.CreateVerificationToken(params.Email, verificationType) if err != nil { - log.Println(`Error generating token`, err) + log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, @@ -135,7 +135,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, IP: utils.GetIP(gc.Request), } - db.Mgr.SaveSession(sessionData) + db.Mgr.AddSession(sessionData) }() res = &model.AuthResponse{ Message: `Signed up successfully.`, diff --git a/server/resolvers/token.go b/server/resolvers/token.go index 6087a1e..39b9367 100644 --- a/server/resolvers/token.go +++ b/server/resolvers/token.go @@ -64,7 +64,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) { // if access token has expired and refresh/session token is valid // generate new accessToken currentRefreshToken := session.GetToken(userIdStr, token) - session.DeleteToken(userIdStr, token) + session.DeleteVerificationRequest(userIdStr, token) token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles) session.SetToken(userIdStr, token, currentRefreshToken) go func() { @@ -74,7 +74,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) { IP: utils.GetIP(gc.Request), } - db.Mgr.SaveSession(sessionData) + db.Mgr.AddSession(sessionData) }() } diff --git a/server/resolvers/updateProfile.go b/server/resolvers/updateProfile.go index 5816910..2335d16 100644 --- a/server/resolvers/updateProfile.go +++ b/server/resolvers/updateProfile.go @@ -109,7 +109,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model verificationType := enum.UpdateEmail.String() token, err := utils.CreateVerificationToken(newEmail, verificationType) if err != nil { - log.Println(`Error generating token`, err) + log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, @@ -126,7 +126,7 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model _, err = db.Mgr.UpdateUser(user) if err != nil { - log.Println("Error updating user:", err) + log.Println("error updating user:", err) return res, err } message := `Profile details updated successfully.` diff --git a/server/resolvers/verifyEmail.go b/server/resolvers/verifyEmail.go index a1f90e3..b1febe0 100644 --- a/server/resolvers/verifyEmail.go +++ b/server/resolvers/verifyEmail.go @@ -20,7 +20,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut return res, err } - _, err = db.Mgr.GetVerificationByToken(params.Token) + verificationRequest, err := db.Mgr.GetVerificationByToken(params.Token) if err != nil { return res, fmt.Errorf(`invalid token`) } @@ -37,9 +37,10 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut } // update email_verified_at in users table - db.Mgr.UpdateVerificationTime(time.Now().Unix(), user.ID) + user.EmailVerifiedAt = time.Now().Unix() + db.Mgr.UpdateUser(user) // delete from verification table - db.Mgr.DeleteToken(claim.Email) + db.Mgr.DeleteVerificationRequest(verificationRequest) userIdStr := fmt.Sprintf("%v", user.ID) roles := strings.Split(user.Roles, ",") @@ -55,7 +56,7 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut IP: utils.GetIP(gc.Request), } - db.Mgr.SaveSession(sessionData) + db.Mgr.AddSession(sessionData) }() res = &model.AuthResponse{ diff --git a/server/session/inMemoryStore.go b/server/session/inMemoryStore.go index 164b7d0..afd5dda 100644 --- a/server/session/inMemoryStore.go +++ b/server/session/inMemoryStore.go @@ -1,7 +1,6 @@ package session import ( - "log" "sync" ) @@ -30,8 +29,6 @@ func (c *InMemoryStore) AddToken(userId, accessToken, refreshToken string) { c.store[userId] = tempMap } - log.Println(c.store) - c.mu.Unlock() } @@ -41,7 +38,7 @@ func (c *InMemoryStore) DeleteUserSession(userId string) { c.mu.Unlock() } -func (c *InMemoryStore) DeleteToken(userId, accessToken string) { +func (c *InMemoryStore) DeleteVerificationRequest(userId, accessToken string) { c.mu.Lock() delete(c.store[userId], accessToken) c.mu.Unlock() diff --git a/server/session/redisStore.go b/server/session/redisStore.go index 91cb5ed..c9d2841 100644 --- a/server/session/redisStore.go +++ b/server/session/redisStore.go @@ -29,7 +29,7 @@ func (c *RedisStore) DeleteUserSession(userId string) { } } -func (c *RedisStore) DeleteToken(userId, accessToken string) { +func (c *RedisStore) DeleteVerificationRequest(userId, accessToken string) { err := c.store.HDel(c.ctx, "authorizer_"+userId, accessToken).Err() if err != nil { log.Fatalln("Error deleting redis token:", err) @@ -47,7 +47,7 @@ func (c *RedisStore) GetToken(userId, accessToken string) string { token := "" res, err := c.store.HMGet(c.ctx, "authorizer_"+userId, accessToken).Result() if err != nil { - log.Println("Error getting token from redis store:", err) + log.Println("error getting token from redis store:", err) } if len(res) > 0 && res[0] != nil { token = fmt.Sprintf("%v", res[0]) @@ -66,7 +66,7 @@ func (c *RedisStore) GetSocialLoginState(key string) string { state := "" state, err := c.store.Get(c.ctx, key).Result() if err != nil { - log.Println("Error getting token from redis store:", err) + log.Println("error getting token from redis store:", err) } return state diff --git a/server/session/session.go b/server/session/session.go index 15d4015..08138b0 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -27,12 +27,12 @@ func SetToken(userId, accessToken, refreshToken string) { } } -func DeleteToken(userId, accessToken string) { +func DeleteVerificationRequest(userId, accessToken string) { if SessionStoreObj.RedisMemoryStoreObj != nil { - SessionStoreObj.RedisMemoryStoreObj.DeleteToken(userId, accessToken) + SessionStoreObj.RedisMemoryStoreObj.DeleteVerificationRequest(userId, accessToken) } if SessionStoreObj.InMemoryStoreObj != nil { - SessionStoreObj.InMemoryStoreObj.DeleteToken(userId, accessToken) + SessionStoreObj.InMemoryStoreObj.DeleteVerificationRequest(userId, accessToken) } } @@ -96,7 +96,7 @@ func RemoveSocialLoginState(key string) { func InitSession() { if constants.REDIS_URL != "" { - log.Println("Using redis store to save sessions") + log.Println("using redis store to save sessions") opt, err := redis.ParseURL(constants.REDIS_URL) if err != nil { log.Fatalln("Error parsing redis url:", err) @@ -114,7 +114,7 @@ func InitSession() { } } else { - log.Println("Using in memory store to save sessions") + log.Println("using in memory store to save sessions") SessionStoreObj.InMemoryStoreObj = &InMemoryStore{ store: map[string]map[string]string{}, socialLoginState: map[string]string{}, diff --git a/server/utils/authToken.go b/server/utils/authToken.go index 02e5165..5a9baba 100644 --- a/server/utils/authToken.go +++ b/server/utils/authToken.go @@ -64,12 +64,12 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st val, err := vm.Get("functionRes") if err != nil { - log.Println("=> err custom access token script:", err) + log.Println("error getting custom access token script:", err) } else { extraPayload := make(map[string]interface{}) err = json.Unmarshal([]byte(fmt.Sprintf("%s", val)), &extraPayload) if err != nil { - log.Println("Error converting accessTokenScript response to map:", err) + log.Println("error converting accessTokenScript response to map:", err) } else { for k, v := range extraPayload { customClaims[k] = v diff --git a/server/utils/initServer.go b/server/utils/initServer.go index 4634c4a..9315314 100644 --- a/server/utils/initServer.go +++ b/server/utils/initServer.go @@ -1,30 +1,6 @@ package utils -import ( - "log" - - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/db" -) - // any jobs that we want to run at start of server can be executed here -// 1. create roles table and add the roles list from env to table - func InitServer() { - roles := []db.Role{} - for _, val := range constants.ROLES { - roles = append(roles, db.Role{ - Role: val, - }) - } - for _, val := range constants.PROTECTED_ROLES { - roles = append(roles, db.Role{ - Role: val, - }) - } - err := db.Mgr.SaveRoles(roles) - if err != nil { - log.Println(`Error saving roles`, err) - } }