diff --git a/.env.sample b/.env.sample index 1970573..18e8c2b 100644 --- a/.env.sample +++ b/.env.sample @@ -2,8 +2,11 @@ ENV=production DATABASE_URL=data.db DATABASE_TYPE=sqlite ADMIN_SECRET=admin -DISABLE_EMAIL_VERIFICATION=true JWT_SECRET=random_string +SENDER_EMAIL=username +SENDER_PASSWORD=password +SMTP_HOST=smtp.mailtrap.io +SMTP_PORT=2525 JWT_TYPE=HS256 ROLES=user DEFAULT_ROLES=user diff --git a/server/db/arangodb.go b/server/db/arangodb.go index 4b501e7..1d624ed 100644 --- a/server/db/arangodb.go +++ b/server/db/arangodb.go @@ -95,5 +95,10 @@ func initArangodb() (arangoDriver.Database, error) { } } + sessionCollection, _ := arangodb.Collection(nil, Collections.Session) + sessionCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{ + Sparse: true, + }) + return arangodb, err } diff --git a/server/db/db.go b/server/db/db.go index 3031c3f..f8e7256 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -26,8 +26,9 @@ type Manager interface { GetVerificationByToken(token string) (VerificationRequest, error) DeleteVerificationRequest(verificationRequest VerificationRequest) error GetVerificationRequests() ([]VerificationRequest, error) - GetVerificationByEmail(email string) (VerificationRequest, error) + GetVerificationByEmail(email string, identifier string) (VerificationRequest, error) AddSession(session Session) error + DeleteUserSession(userId string) error } type manager struct { @@ -94,8 +95,8 @@ func InitDB() { Mgr = &manager{ sqlDB: nil, - mongodb: nil, arangodb: arangodb, + mongodb: nil, } break diff --git a/server/db/mongodb.go b/server/db/mongodb.go index 8bb715a..c69f5bc 100644 --- a/server/db/mongodb.go +++ b/server/db/mongodb.go @@ -65,6 +65,13 @@ func initMongodb() (*mongo.Database, error) { }, options.CreateIndexes()) mongodb.CreateCollection(ctx, Collections.Session, options.CreateCollection()) + sessionCollection := mongodb.Collection(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()) return mongodb, nil } diff --git a/server/db/session.go b/server/db/session.go index 8be4124..47b31aa 100644 --- a/server/db/session.go +++ b/server/db/session.go @@ -1,10 +1,12 @@ package db import ( + "fmt" "log" "time" "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" "gorm.io/gorm/clause" ) @@ -12,7 +14,7 @@ import ( 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)" json:"user_id" bson:"user_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"` @@ -63,3 +65,38 @@ func (mgr *manager) AddSession(session Session) error { return nil } + +func (mgr *manager) DeleteUserSession(userId string) error { + if IsORMSupported { + result := mgr.sqlDB.Where("user_id = ?", userId).Delete(&Session{}) + + if result.Error != nil { + log.Println(`error deleting session:`, result.Error) + return result.Error + } + } + + if IsArangoDB { + query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @userId REMOVE { _key: d._key } IN %s`, Collections.Session, Collections.Session) + bindVars := map[string]interface{}{ + "userId": userId, + } + cursor, err := mgr.arangodb.Query(nil, query, bindVars) + if err != nil { + log.Println("=> error deleting arangodb session:", err) + return err + } + defer cursor.Close() + } + + if IsMongoDB { + sessionCollection := mgr.mongodb.Collection(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/user.go b/server/db/user.go index 48c03f8..60254fa 100644 --- a/server/db/user.go +++ b/server/db/user.go @@ -7,6 +7,7 @@ import ( "github.com/arangodb/go-driver" arangoDriver "github.com/arangodb/go-driver" + "github.com/authorizerdev/authorizer/server/constants" "github.com/google/uuid" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" @@ -41,6 +42,10 @@ func (mgr *manager) AddUser(user User) (User, error) { user.ID = uuid.New().String() } + if user.Roles == "" { + user.Roles = constants.DEFAULT_ROLES[0] + } + if IsORMSupported { // copy id as value for fields required for mongodb & arangodb user.Key = user.ID @@ -111,7 +116,7 @@ func (mgr *manager) UpdateUser(user User) (User, error) { if IsMongoDB { userCollection := mgr.mongodb.Collection(Collections.User, options.Collection()) - _, err := userCollection.UpdateOne(nil, bson.M{"id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions()) + _, err := userCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions()) if err != nil { log.Println("error updating user:", err) return user, err @@ -240,7 +245,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) { } if IsArangoDB { - query := fmt.Sprintf("FOR d in %s FILTER d.id == @id LIMIT 1 RETURN d", Collections.User) + query := fmt.Sprintf("FOR d in %s FILTER d._id == @id LIMIT 1 RETURN d", Collections.User) bindVars := map[string]interface{}{ "id": id, } @@ -267,7 +272,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) { if IsMongoDB { userCollection := mgr.mongodb.Collection(Collections.User, options.Collection()) - err := userCollection.FindOne(nil, bson.M{"id": id}).Decode(&user) + err := userCollection.FindOne(nil, bson.M{"_id": id}).Decode(&user) if err != nil { return user, err } @@ -297,7 +302,7 @@ func (mgr *manager) DeleteUser(user User) error { if IsMongoDB { userCollection := mgr.mongodb.Collection(Collections.User, options.Collection()) - _, err := userCollection.DeleteOne(nil, bson.M{"id": user.ID}, options.Delete()) + _, err := userCollection.DeleteOne(nil, bson.M{"_id": user.ID}, options.Delete()) if err != nil { log.Println("error deleting user:", err) return err diff --git a/server/db/verification_requests.go b/server/db/verification_requests.go index d468910..45b5de0 100644 --- a/server/db/verification_requests.go +++ b/server/db/verification_requests.go @@ -179,10 +179,10 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e return verification, nil } -func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) { +func (mgr *manager) GetVerificationByEmail(email string, identifier string) (VerificationRequest, error) { var verification VerificationRequest if IsORMSupported { - result := mgr.sqlDB.Where("email = ?", email).First(&verification) + result := mgr.sqlDB.Where("email = ? AND identifier = ?", email, identifier).First(&verification) if result.Error != nil { log.Println(`error getting verification token:`, result.Error) @@ -191,9 +191,10 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e } if IsArangoDB { - query := fmt.Sprintf("FOR d in %s FILTER d.email == @email LIMIT 1 RETURN d", Collections.VerificationRequest) + query := fmt.Sprintf("FOR d in %s FILTER d.email == @email FILTER d.identifier == @identifier LIMIT 1 RETURN d", Collections.VerificationRequest) bindVars := map[string]interface{}{ - "email": email, + "email": email, + "identifier": identifier, } cursor, err := mgr.arangodb.Query(nil, query, bindVars) @@ -218,7 +219,7 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e if IsMongoDB { verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection()) - err := verificationRequestCollection.FindOne(nil, bson.M{"email": email}).Decode(&verification) + err := verificationRequestCollection.FindOne(nil, bson.M{"email": email, "identifier": identifier}).Decode(&verification) if err != nil { return verification, err } @@ -248,7 +249,7 @@ func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRe if IsMongoDB { verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection()) - _, err := verificationRequestCollection.DeleteOne(nil, bson.M{"id": verificationRequest.ID}, options.Delete()) + _, err := verificationRequestCollection.DeleteOne(nil, bson.M{"_id": verificationRequest.ID}, options.Delete()) if err != nil { log.Println("error deleting verification request::", err) return err diff --git a/server/env/env.go b/server/env/env.go index f103d56..32283da 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -1,7 +1,6 @@ package env import ( - "flag" "log" "os" "strings" @@ -25,13 +24,8 @@ func InitEnv() { if constants.ENV_PATH == "" { constants.ENV_PATH = `.env` } - ARG_DB_URL = flag.String("database_url", "", "Database connection string") - ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite") - ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com") - ARG_ENV_FILE = flag.String("env_file", "", "Env file path") - flag.Parse() - if *ARG_ENV_FILE != "" { + if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" { constants.ENV_PATH = *ARG_ENV_FILE } @@ -65,8 +59,9 @@ func InitEnv() { if constants.DATABASE_TYPE == "" { constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE") + log.Println(constants.DATABASE_TYPE) - if *ARG_DB_TYPE != "" { + if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" { constants.DATABASE_TYPE = *ARG_DB_TYPE } @@ -78,7 +73,7 @@ func InitEnv() { if constants.DATABASE_URL == "" { constants.DATABASE_URL = os.Getenv("DATABASE_URL") - if *ARG_DB_URL != "" { + if ARG_DB_URL != nil && *ARG_DB_URL != "" { constants.DATABASE_URL = *ARG_DB_URL } @@ -129,7 +124,7 @@ func InitEnv() { if constants.AUTHORIZER_URL == "" { constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/") - if *ARG_AUTHORIZER_URL != "" { + if ARG_AUTHORIZER_URL != nil && *ARG_AUTHORIZER_URL != "" { constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL } } diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index fbc197d..300307a 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -82,7 +82,7 @@ type ComplexityRoot struct { Query struct { Meta func(childComplexity int) int Profile func(childComplexity int) int - Token func(childComplexity int, roles []string) int + Session func(childComplexity int, roles []string) int Users func(childComplexity int) int VerificationRequests func(childComplexity int) int } @@ -137,7 +137,7 @@ type MutationResolver interface { } type QueryResolver interface { Meta(ctx context.Context) (*model.Meta, error) - Token(ctx context.Context, roles []string) (*model.AuthResponse, error) + Session(ctx context.Context, roles []string) (*model.AuthResponse, error) Profile(ctx context.Context) (*model.User, error) Users(ctx context.Context) ([]*model.User, error) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) @@ -390,17 +390,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Profile(childComplexity), true - case "Query.token": - if e.complexity.Query.Token == nil { + case "Query.session": + if e.complexity.Query.Session == nil { break } - args, err := ec.field_Query_token_args(context.TODO(), rawArgs) + args, err := ec.field_Query_session_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Query.Token(childComplexity, args["roles"].([]string)), true + return e.complexity.Query.Session(childComplexity, args["roles"].([]string)), true case "Query._users": if e.complexity.Query.Users == nil { @@ -746,6 +746,7 @@ input VerifyEmailInput { input ResendVerifyEmailInput { email: String! + identifier: String! } input UpdateProfileInput { @@ -813,7 +814,7 @@ type Mutation { type Query { meta: Meta! - token(roles: [String!]): AuthResponse + session(roles: [String!]): AuthResponse profile: User! # admin only apis _users: [User!]! @@ -992,7 +993,7 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } -func (ec *executionContext) field_Query_token_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_session_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 []string @@ -1981,7 +1982,7 @@ func (ec *executionContext) _Query_meta(ctx context.Context, field graphql.Colle return ec.marshalNMeta2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMeta(ctx, field.Selections, res) } -func (ec *executionContext) _Query_token(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Query_session(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -1998,7 +1999,7 @@ func (ec *executionContext) _Query_token(ctx context.Context, field graphql.Coll ctx = graphql.WithFieldContext(ctx, fc) rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Query_token_args(ctx, rawArgs) + args, err := ec.field_Query_session_args(ctx, rawArgs) if err != nil { ec.Error(ctx, err) return graphql.Null @@ -2006,7 +2007,7 @@ func (ec *executionContext) _Query_token(ctx context.Context, field graphql.Coll fc.Args = args resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().Token(rctx, args["roles"].([]string)) + return ec.resolvers.Query().Session(rctx, args["roles"].([]string)) }) if err != nil { ec.Error(ctx, err) @@ -4272,6 +4273,14 @@ func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Con if err != nil { return it, err } + case "identifier": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("identifier")) + it.Identifier, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } } } @@ -4905,7 +4914,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } return res }) - case "token": + case "session": field := field out.Concurrently(i, func() (res graphql.Marshaler) { defer func() { @@ -4913,7 +4922,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_token(ctx, field) + res = ec._Query_session(ctx, field) return res }) case "profile": diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go index 513ef7d..ffd76ea 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -44,7 +44,8 @@ type Meta struct { } type ResendVerifyEmailInput struct { - Email string `json:"email"` + Email string `json:"email"` + Identifier string `json:"identifier"` } type ResetPasswordInput struct { diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index 937ecd5..4353073 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -89,6 +89,7 @@ input VerifyEmailInput { input ResendVerifyEmailInput { email: String! + identifier: String! } input UpdateProfileInput { @@ -156,7 +157,7 @@ type Mutation { type Query { meta: Meta! - token(roles: [String!]): AuthResponse + session(roles: [String!]): AuthResponse profile: User! # admin only apis _users: [User!]! diff --git a/server/graph/schema.resolvers.go b/server/graph/schema.resolvers.go index 21785d2..e963623 100644 --- a/server/graph/schema.resolvers.go +++ b/server/graph/schema.resolvers.go @@ -59,7 +59,7 @@ func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) { return resolvers.Meta(ctx) } -func (r *queryResolver) Token(ctx context.Context, roles []string) (*model.AuthResponse, error) { +func (r *queryResolver) Session(ctx context.Context, roles []string) (*model.AuthResponse, error) { return resolvers.Token(ctx, roles) } diff --git a/server/handlers/oauth_callback.go b/server/handlers/oauth_callback.go index e7cddc3..4fd0364 100644 --- a/server/handlers/oauth_callback.go +++ b/server/handlers/oauth_callback.go @@ -264,15 +264,7 @@ func OAuthCallbackHandler() gin.HandlerFunc { accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles) utils.SetCookie(c, accessToken) session.SetToken(userIdStr, accessToken, refreshToken) - go func() { - sessionData := db.Session{ - UserID: user.ID, - UserAgent: utils.GetUserAgent(c.Request), - IP: utils.GetIP(c.Request), - } - - db.Mgr.AddSession(sessionData) - }() + utils.CreateSession(user.ID, c) c.Redirect(http.StatusTemporaryRedirect, redirectURL) } diff --git a/server/handlers/verify_email.go b/server/handlers/verify_email.go index bdba17f..b8eabf5 100644 --- a/server/handlers/verify_email.go +++ b/server/handlers/verify_email.go @@ -1,7 +1,6 @@ package handlers import ( - "fmt" "net/http" "strings" "time" @@ -54,22 +53,13 @@ func VerifyEmailHandler() gin.HandlerFunc { // delete from verification table db.Mgr.DeleteVerificationRequest(verificationRequest) - userIdStr := fmt.Sprintf("%v", user.ID) roles := strings.Split(user.Roles, ",") refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles) accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) - session.SetToken(userIdStr, accessToken, refreshToken) - go func() { - sessionData := db.Session{ - UserID: user.ID, - UserAgent: utils.GetUserAgent(c.Request), - IP: utils.GetIP(c.Request), - } - - db.Mgr.AddSession(sessionData) - }() + session.SetToken(user.ID, accessToken, refreshToken) + utils.CreateSession(user.ID, c) utils.SetCookie(c, accessToken) c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL) } diff --git a/server/main.go b/server/main.go index c3f5e01..594553f 100644 --- a/server/main.go +++ b/server/main.go @@ -1,6 +1,8 @@ package main import ( + "flag" + "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/env" @@ -12,6 +14,12 @@ import ( ) func main() { + env.ARG_DB_URL = flag.String("database_url", "", "Database connection string") + env.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite") + env.ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com") + env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path") + flag.Parse() + env.InitEnv() db.InitDB() session.InitSession() @@ -20,7 +28,8 @@ func main() { router := router.InitRouter() - // login wall app related routes + // login wall app related routes. + // if we put them in router file then tests would fail as templates or build path will be different router.LoadHTMLGlob("templates/*") app := router.Group("/app") { diff --git a/server/resolvers/login.go b/server/resolvers/login.go index d01bcb5..8f7e0cb 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -60,15 +60,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) session.SetToken(user.ID, accessToken, refreshToken) - go func() { - sessionData := db.Session{ - UserID: user.ID, - UserAgent: utils.GetUserAgent(gc.Request), - IP: utils.GetIP(gc.Request), - } - - db.Mgr.AddSession(sessionData) - }() + utils.CreateSession(user.ID, gc) res = &model.AuthResponse{ Message: `Logged in successfully`, diff --git a/server/resolvers/resend_verify_email.go b/server/resolvers/resend_verify_email.go index 5b97aad..0838a40 100644 --- a/server/resolvers/resend_verify_email.go +++ b/server/resolvers/resend_verify_email.go @@ -20,18 +20,28 @@ func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) return res, fmt.Errorf("invalid email") } - verificationRequest, err := db.Mgr.GetVerificationByEmail(params.Email) + if !utils.IsValidVerificationIdentifier(params.Identifier) { + return res, fmt.Errorf("invalid identifier") + } + + verificationRequest, err := db.Mgr.GetVerificationByEmail(params.Email, params.Identifier) if err != nil { return res, fmt.Errorf(`verification request not found`) } - token, err := utils.CreateVerificationToken(params.Email, verificationRequest.Identifier) + // delete current verification and create new one + err = db.Mgr.DeleteVerificationRequest(verificationRequest) + if err != nil { + log.Println("error deleting verification request:", err) + } + + token, err := utils.CreateVerificationToken(params.Email, params.Identifier) if err != nil { log.Println(`error generating token`, err) } db.Mgr.AddVerification(db.VerificationRequest{ Token: token, - Identifier: verificationRequest.Identifier, + Identifier: params.Identifier, ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), Email: params.Email, }) diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index 436f997..a29833a 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -145,15 +145,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) session.SetToken(userIdStr, accessToken, refreshToken) - go func() { - sessionData := db.Session{ - UserID: user.ID, - UserAgent: utils.GetUserAgent(gc.Request), - IP: utils.GetIP(gc.Request), - } - - db.Mgr.AddSession(sessionData) - }() + utils.CreateSession(user.ID, gc) res = &model.AuthResponse{ Message: `Signed up successfully.`, AccessToken: &accessToken, diff --git a/server/resolvers/token.go b/server/resolvers/token.go index 12b4d39..ba38f2f 100644 --- a/server/resolvers/token.go +++ b/server/resolvers/token.go @@ -66,15 +66,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) { session.DeleteVerificationRequest(userIdStr, token) token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles) session.SetToken(userIdStr, token, currentRefreshToken) - go func() { - sessionData := db.Session{ - UserID: user.ID, - UserAgent: utils.GetUserAgent(gc.Request), - IP: utils.GetIP(gc.Request), - } - - db.Mgr.AddSession(sessionData) - }() + utils.CreateSession(user.ID, gc) } utils.SetCookie(gc, token) diff --git a/server/resolvers/verify_email.go b/server/resolvers/verify_email.go index 60bf4e1..cc41902 100644 --- a/server/resolvers/verify_email.go +++ b/server/resolvers/verify_email.go @@ -43,22 +43,13 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut // delete from verification table db.Mgr.DeleteVerificationRequest(verificationRequest) - userIdStr := fmt.Sprintf("%v", user.ID) roles := strings.Split(user.Roles, ",") refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles) accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) - session.SetToken(userIdStr, accessToken, refreshToken) - go func() { - sessionData := db.Session{ - UserID: user.ID, - UserAgent: utils.GetUserAgent(gc.Request), - IP: utils.GetIP(gc.Request), - } - - db.Mgr.AddSession(sessionData) - }() + session.SetToken(user.ID, accessToken, refreshToken) + utils.CreateSession(user.ID, gc) res = &model.AuthResponse{ Message: `Email verified successfully.`, diff --git a/server/test/cors_test.go b/server/test/cors_test.go index 850b4ee..dbe47b5 100644 --- a/server/test/cors_test.go +++ b/server/test/cors_test.go @@ -2,44 +2,25 @@ package test import ( "net/http" - "net/http/httptest" "testing" - "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/db" - "github.com/authorizerdev/authorizer/server/env" - "github.com/authorizerdev/authorizer/server/router" - "github.com/authorizerdev/authorizer/server/session" "github.com/stretchr/testify/assert" ) func TestCors(t *testing.T) { - constants.ENV_PATH = "../../.env.sample" - constants.DATABASE_URL = "../../data.db" - env.InitEnv() - db.InitDB() - session.InitSession() - router := router.InitRouter() - allowedOrigin := "http://localhost:8080" // The allowed origin that you want to check notAllowedOrigin := "http://myapp.com" - server := httptest.NewServer(router) - defer server.Close() - + s := testSetup() + defer s.Server.Close() client := &http.Client{} - req, _ := http.NewRequest( - "GET", - "http://"+server.Listener.Addr().String()+"/graphql", - nil, - ) - req.Header.Add("Origin", allowedOrigin) - get, _ := client.Do(req) + s.Req.Header.Add("Origin", allowedOrigin) + res, _ := client.Do(s.Req) // You should get your origin (or a * depending on your config) if the // passed origin is allowed. - o := get.Header.Get("Access-Control-Allow-Origin") + o := res.Header.Get("Access-Control-Allow-Origin") assert.NotEqual(t, o, notAllowedOrigin, "Origins should not match") - assert.Equal(t, o, allowedOrigin, "Origins don't match") + assert.Equal(t, o, allowedOrigin, "Origins do match") } diff --git a/server/test/env_test.go b/server/test/env_test.go index 1f4380f..690ad51 100644 --- a/server/test/env_test.go +++ b/server/test/env_test.go @@ -4,20 +4,16 @@ import ( "testing" "github.com/authorizerdev/authorizer/server/constants" - "github.com/authorizerdev/authorizer/server/enum" "github.com/stretchr/testify/assert" ) func TestEnvs(t *testing.T) { constants.ENV_PATH = "../../.env.sample" - // env.InitEnv() assert.Equal(t, constants.ADMIN_SECRET, "admin") assert.Equal(t, constants.ENV, "production") - assert.Equal(t, constants.DATABASE_URL, "../../data.db") - assert.Equal(t, constants.DATABASE_TYPE, enum.Sqlite.String()) - assert.True(t, constants.DISABLE_EMAIL_VERIFICATION) - assert.True(t, constants.DISABLE_MAGIC_LINK_LOGIN) + assert.False(t, constants.DISABLE_EMAIL_VERIFICATION) + assert.False(t, constants.DISABLE_MAGIC_LINK_LOGIN) assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION) assert.Equal(t, constants.JWT_TYPE, "HS256") assert.Equal(t, constants.JWT_SECRET, "random_string") diff --git a/server/test/forgot_password_test.go b/server/test/forgot_password_test.go new file mode 100644 index 0000000..bcecfc3 --- /dev/null +++ b/server/test/forgot_password_test.go @@ -0,0 +1,65 @@ +package test + +import ( + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func commonForgotPasswordTest(s TestSetup, t *testing.T) { + email := "forgot_password." + s.TestInfo.Email + _, err := resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + _, err = resolvers.ForgotPassword(s.Ctx, model.ForgotPasswordInput{ + Email: email, + }) + assert.Nil(t, err, "no errors for forgot password") + + verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String()) + assert.Nil(t, err) + + assert.Equal(t, verificationRequest.Identifier, enum.ForgotPassword.String()) + + cleanData(email) +} + +func TestForgotPassword(t *testing.T) { + s := testSetup() + defer s.Server.Close() + + if s.TestInfo.ShouldExecuteForSQL { + t.Run("forgot password for sql dbs should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.SQL + constants.DATABASE_TYPE = enum.Sqlite.String() + db.InitDB() + commonForgotPasswordTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForArango { + t.Run("forgot password for arangodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.ArangoDB + constants.DATABASE_TYPE = enum.Arangodb.String() + db.InitDB() + commonForgotPasswordTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForMongo { + t.Run("forgot password for mongodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.MongoDB + constants.DATABASE_TYPE = enum.Mongodb.String() + db.InitDB() + commonForgotPasswordTest(s, t) + }) + } +} diff --git a/server/test/login_test.go b/server/test/login_test.go new file mode 100644 index 0000000..f9c359e --- /dev/null +++ b/server/test/login_test.go @@ -0,0 +1,90 @@ +package test + +import ( + "log" + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func commonLoginTest(s TestSetup, t *testing.T) { + email := "login." + s.TestInfo.Email + _, err := resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + _, err = resolvers.Login(s.Ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + }) + + assert.NotNil(t, err, "should fail because email is not verified") + + verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String()) + resolvers.VerifyEmail(s.Ctx, model.VerifyEmailInput{ + Token: verificationRequest.Token, + }) + + _, err = resolvers.Login(s.Ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + Roles: []string{"test"}, + }) + assert.NotNil(t, err, "invalid roles") + + _, err = resolvers.Login(s.Ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password + "s", + }) + assert.NotNil(t, err, "invalid password") + + loginRes, err := resolvers.Login(s.Ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + }) + + log.Println("=> access token:", loginRes.AccessToken) + assert.Nil(t, err, "login successful") + assert.NotNil(t, loginRes.AccessToken, "access token should not be empty") + + cleanData(email) +} + +func TestLogin(t *testing.T) { + s := testSetup() + defer s.Server.Close() + + if s.TestInfo.ShouldExecuteForSQL { + t.Run("login for sql dbs should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.SQL + constants.DATABASE_TYPE = enum.Sqlite.String() + db.InitDB() + commonLoginTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForArango { + t.Run("login for arangodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.ArangoDB + constants.DATABASE_TYPE = enum.Arangodb.String() + db.InitDB() + commonLoginTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForMongo { + t.Run("login for mongodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.MongoDB + constants.DATABASE_TYPE = enum.Mongodb.String() + db.InitDB() + commonLoginTest(s, t) + }) + } +} diff --git a/server/test/resend_verify_email_test.go b/server/test/resend_verify_email_test.go new file mode 100644 index 0000000..50e1b97 --- /dev/null +++ b/server/test/resend_verify_email_test.go @@ -0,0 +1,62 @@ +package test + +import ( + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func commonResendVerifyEmailTest(s TestSetup, t *testing.T) { + email := "resend_verify_email." + s.TestInfo.Email + _, err := resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + _, err = resolvers.ResendVerifyEmail(s.Ctx, model.ResendVerifyEmailInput{ + Email: email, + Identifier: enum.BasicAuthSignup.String(), + }) + + assert.Nil(t, err) + + cleanData(email) +} + +func TestResendVerifyEmail(t *testing.T) { + s := testSetup() + defer s.Server.Close() + + if s.TestInfo.ShouldExecuteForSQL { + t.Run("resend verify email for sql dbs should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.SQL + constants.DATABASE_TYPE = enum.Sqlite.String() + db.InitDB() + commonResendVerifyEmailTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForArango { + t.Run("resend verify email for arangodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.ArangoDB + constants.DATABASE_TYPE = enum.Arangodb.String() + db.InitDB() + commonResendVerifyEmailTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForMongo { + t.Run("resend verify email for mongodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.MongoDB + constants.DATABASE_TYPE = enum.Mongodb.String() + db.InitDB() + commonResendVerifyEmailTest(s, t) + }) + } +} diff --git a/server/test/reset_password_test.go b/server/test/reset_password_test.go new file mode 100644 index 0000000..2dcb8c3 --- /dev/null +++ b/server/test/reset_password_test.go @@ -0,0 +1,79 @@ +package test + +import ( + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func commonResetPasswordTest(s TestSetup, t *testing.T) { + email := "reset_password." + s.TestInfo.Email + _, err := resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + _, err = resolvers.ForgotPassword(s.Ctx, model.ForgotPasswordInput{ + Email: email, + }) + assert.Nil(t, err, "no errors for forgot password") + + verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String()) + assert.Nil(t, err, "should get forgot password request") + + _, err = resolvers.ResetPassword(s.Ctx, model.ResetPasswordInput{ + Token: verificationRequest.Token, + Password: "test1", + ConfirmPassword: "test", + }) + + assert.NotNil(t, err, "passowrds don't match") + + _, err = resolvers.ResetPassword(s.Ctx, model.ResetPasswordInput{ + Token: verificationRequest.Token, + Password: "test1", + ConfirmPassword: "test1", + }) + + assert.Nil(t, err, "password changed successfully") + + cleanData(email) +} + +func TestResetPassword(t *testing.T) { + s := testSetup() + defer s.Server.Close() + + if s.TestInfo.ShouldExecuteForSQL { + t.Run("reset password for sql dbs should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.SQL + constants.DATABASE_TYPE = enum.Sqlite.String() + db.InitDB() + commonResetPasswordTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForArango { + t.Run("reset password for arangodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.ArangoDB + constants.DATABASE_TYPE = enum.Arangodb.String() + db.InitDB() + commonResetPasswordTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForMongo { + t.Run("reset password for mongodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.MongoDB + constants.DATABASE_TYPE = enum.Mongodb.String() + db.InitDB() + commonResetPasswordTest(s, t) + }) + } +} diff --git a/server/test/signup_test.go b/server/test/signup_test.go index 93d861a..9d934b0 100644 --- a/server/test/signup_test.go +++ b/server/test/signup_test.go @@ -1,29 +1,77 @@ package test import ( - "context" - "log" - "net/http/httptest" "testing" + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/resolvers" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) -func TestSQLSignUp(t *testing.T) { - w := httptest.NewRecorder() - req := httptest.NewRequest("POST", "/graphql", nil) - c, _ := gin.CreateTestContext(w) - ctx := context.WithValue(req.Context(), "GinContextKey", c) - - res, err := resolvers.Signup(ctx, model.SignUpInput{ - Email: "test@yopmail.com", - Password: "test", - ConfirmPassword: "test", +func commonSignupTest(s TestSetup, t *testing.T) { + email := "signup." + s.TestInfo.Email + res, err := resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password + "s", }) - log.Println("=> signup err:", err) - log.Println("=> singup res:", res) - assert.Equal(t, "success", "success") + assert.NotNil(t, err, "invalid password errors") + + res, err = resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + user := *res.User + assert.Equal(t, email, user.Email) + assert.Nil(t, res.AccessToken, "access token should be nil") + + res, err = resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + assert.NotNil(t, err, "should throw duplicate email error") + + verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String()) + assert.Nil(t, err) + assert.Equal(t, email, verificationRequest.Email) + cleanData(email) +} + +func TestSignUp(t *testing.T) { + s := testSetup() + defer s.Server.Close() + + if s.TestInfo.ShouldExecuteForSQL { + t.Run("signup for sql dbs should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.SQL + constants.DATABASE_TYPE = enum.Sqlite.String() + db.InitDB() + commonSignupTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForArango { + t.Run("signup for arangodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.ArangoDB + constants.DATABASE_TYPE = enum.Arangodb.String() + db.InitDB() + commonSignupTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForMongo { + t.Run("signup for mongodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.MongoDB + constants.DATABASE_TYPE = enum.Mongodb.String() + db.InitDB() + commonSignupTest(s, t) + }) + } } diff --git a/server/test/test.go b/server/test/test.go new file mode 100644 index 0000000..598ff7b --- /dev/null +++ b/server/test/test.go @@ -0,0 +1,117 @@ +package test + +import ( + "context" + "log" + "net/http" + "net/http/httptest" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" + "github.com/authorizerdev/authorizer/server/env" + "github.com/authorizerdev/authorizer/server/handlers" + "github.com/authorizerdev/authorizer/server/middlewares" + "github.com/authorizerdev/authorizer/server/session" + "github.com/gin-contrib/location" + "github.com/gin-gonic/gin" +) + +// common user data to share across tests +type TestData struct { + Email string + Password string + SQL string + MongoDB string + ArangoDB string + ShouldExecuteForSQL bool + ShouldExecuteForArango bool + ShouldExecuteForMongo bool +} + +type TestSetup struct { + GinEngine *gin.Engine + GinContext *gin.Context + Ctx context.Context + Server *httptest.Server + Req *http.Request + TestInfo TestData +} + +func testSetup() TestSetup { + testData := TestData{ + Email: "authorizer_tester@yopmail.com", + Password: "test", + SQL: "../../data.db", + ArangoDB: "http://root:root@localhost:8529", + MongoDB: "mongodb://localhost:27017", + ShouldExecuteForSQL: true, + ShouldExecuteForArango: true, + ShouldExecuteForMongo: true, + } + + constants.ENV_PATH = "../../.env.sample" + constants.DATABASE_URL = testData.SQL + env.InitEnv() + session.InitSession() + + w := httptest.NewRecorder() + c, r := gin.CreateTestContext(w) + r.Use(location.Default()) + r.Use(middlewares.GinContextToContextMiddleware()) + r.Use(middlewares.CORSMiddleware()) + + r.POST("/graphql", handlers.GraphqlHandler()) + + server := httptest.NewServer(r) + + req, _ := http.NewRequest( + "POST", + "http://"+server.Listener.Addr().String()+"/graphql", + nil, + ) + req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET) + c.Request = req + ctx := context.WithValue(req.Context(), "GinContextKey", c) + + return TestSetup{ + GinEngine: r, + GinContext: c, + Ctx: ctx, + Server: server, + Req: req, + TestInfo: testData, + } +} + +func cleanData(email string) { + verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String()) + if err == nil { + err = db.Mgr.DeleteVerificationRequest(verificationRequest) + } + + verificationRequest, err = db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String()) + if err == nil { + err = db.Mgr.DeleteVerificationRequest(verificationRequest) + } + + verificationRequest, err = db.Mgr.GetVerificationByEmail(email, enum.UpdateEmail.String()) + if err == nil { + err = db.Mgr.DeleteVerificationRequest(verificationRequest) + } + + dbUser, err := db.Mgr.GetUserByEmail(email) + if err != nil { + log.Println("error getting user:", err) + } else { + err = db.Mgr.DeleteUser(dbUser) + if err != nil { + log.Println("error deleting user:", err) + } + + err = db.Mgr.DeleteUserSession(dbUser.ID) + if err != nil { + log.Println("error deleting user session:", err) + } + } +} diff --git a/server/test/validator_test.go b/server/test/validator_test.go index 1ab6335..4999716 100644 --- a/server/test/validator_test.go +++ b/server/test/validator_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/enum" "github.com/authorizerdev/authorizer/server/utils" "github.com/stretchr/testify/assert" ) @@ -33,3 +34,10 @@ func TestIsValidOrigin(t *testing.T) { assert.True(t, utils.IsValidOrigin("http://xyxabc.in"), "it should be valid origin") assert.True(t, utils.IsValidOrigin("http://localhost:8080"), "it should be valid origin") } + +func TestIsValidIdentifier(t *testing.T) { + assert.False(t, utils.IsValidVerificationIdentifier("test"), "it should be invalid identifier") + assert.True(t, utils.IsValidVerificationIdentifier(enum.BasicAuthSignup.String()), "it should be valid identifier") + assert.True(t, utils.IsValidVerificationIdentifier(enum.UpdateEmail.String()), "it should be valid identifier") + assert.True(t, utils.IsValidVerificationIdentifier(enum.ForgotPassword.String()), "it should be valid identifier") +} diff --git a/server/test/verify_email_test.go b/server/test/verify_email_test.go new file mode 100644 index 0000000..d939b02 --- /dev/null +++ b/server/test/verify_email_test.go @@ -0,0 +1,68 @@ +package test + +import ( + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/enum" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func commonVerifyEmailTest(s TestSetup, t *testing.T) { + email := "verify_email." + s.TestInfo.Email + res, err := resolvers.Signup(s.Ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + + user := *res.User + assert.Equal(t, email, user.Email) + assert.Nil(t, res.AccessToken, "access token should be nil") + verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String()) + assert.Nil(t, err) + assert.Equal(t, email, verificationRequest.Email) + + verifyRes, err := resolvers.VerifyEmail(s.Ctx, model.VerifyEmailInput{ + Token: verificationRequest.Token, + }) + assert.Nil(t, err) + assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty") + + cleanData(email) +} + +func TestVerifyEmail(t *testing.T) { + s := testSetup() + defer s.Server.Close() + + if s.TestInfo.ShouldExecuteForSQL { + t.Run("verify email for sql dbs should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.SQL + constants.DATABASE_TYPE = enum.Sqlite.String() + db.InitDB() + commonVerifyEmailTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForArango { + t.Run("verify email for arangodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.ArangoDB + constants.DATABASE_TYPE = enum.Arangodb.String() + db.InitDB() + commonVerifyEmailTest(s, t) + }) + } + + if s.TestInfo.ShouldExecuteForMongo { + t.Run("verify email for mongodb should pass", func(t *testing.T) { + constants.DATABASE_URL = s.TestInfo.MongoDB + constants.DATABASE_TYPE = enum.Mongodb.String() + db.InitDB() + commonVerifyEmailTest(s, t) + }) + } +} diff --git a/server/utils/create_session.go b/server/utils/create_session.go new file mode 100644 index 0000000..b0283b5 --- /dev/null +++ b/server/utils/create_session.go @@ -0,0 +1,16 @@ +package utils + +import ( + "github.com/authorizerdev/authorizer/server/db" + "github.com/gin-gonic/gin" +) + +func CreateSession(userId string, c *gin.Context) { + sessionData := db.Session{ + UserID: userId, + UserAgent: GetUserAgent(c.Request), + IP: GetIP(c.Request), + } + + db.Mgr.AddSession(sessionData) +} diff --git a/server/utils/validator.go b/server/utils/validator.go index 1d3ffb0..4c0c6ed 100644 --- a/server/utils/validator.go +++ b/server/utils/validator.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/enum" "github.com/gin-gonic/gin" ) @@ -69,6 +70,13 @@ func IsValidRoles(userRoles []string, roles []string) bool { return valid } +func IsValidVerificationIdentifier(identifier string) bool { + if identifier != enum.BasicAuthSignup.String() && identifier != enum.ForgotPassword.String() && identifier != enum.UpdateEmail.String() { + return false + } + return true +} + func IsStringArrayEqual(a, b []string) bool { if len(a) != len(b) { return false diff --git a/server/utils/verification_token.go b/server/utils/verification_token.go index d5a76f6..7dbef67 100644 --- a/server/utils/verification_token.go +++ b/server/utils/verification_token.go @@ -24,7 +24,6 @@ func CreateVerificationToken(email string, tokenType string) (string, error) { t.Claims = &CustomClaim{ &jwt.StandardClaims{ - ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), }, tokenType,