feat: add integration tests for signup, login, reset_password, forgot_password, verify_email

This commit is contained in:
Lakhan Samani 2021-12-23 10:31:52 +05:30
parent 969395ccdb
commit beae4502d4
33 changed files with 722 additions and 152 deletions

View File

@ -2,8 +2,11 @@ ENV=production
DATABASE_URL=data.db DATABASE_URL=data.db
DATABASE_TYPE=sqlite DATABASE_TYPE=sqlite
ADMIN_SECRET=admin ADMIN_SECRET=admin
DISABLE_EMAIL_VERIFICATION=true
JWT_SECRET=random_string JWT_SECRET=random_string
SENDER_EMAIL=username
SENDER_PASSWORD=password
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
JWT_TYPE=HS256 JWT_TYPE=HS256
ROLES=user ROLES=user
DEFAULT_ROLES=user DEFAULT_ROLES=user

View File

@ -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 return arangodb, err
} }

View File

@ -26,8 +26,9 @@ type Manager interface {
GetVerificationByToken(token string) (VerificationRequest, error) GetVerificationByToken(token string) (VerificationRequest, error)
DeleteVerificationRequest(verificationRequest VerificationRequest) error DeleteVerificationRequest(verificationRequest VerificationRequest) error
GetVerificationRequests() ([]VerificationRequest, error) GetVerificationRequests() ([]VerificationRequest, error)
GetVerificationByEmail(email string) (VerificationRequest, error) GetVerificationByEmail(email string, identifier string) (VerificationRequest, error)
AddSession(session Session) error AddSession(session Session) error
DeleteUserSession(userId string) error
} }
type manager struct { type manager struct {
@ -94,8 +95,8 @@ func InitDB() {
Mgr = &manager{ Mgr = &manager{
sqlDB: nil, sqlDB: nil,
mongodb: nil,
arangodb: arangodb, arangodb: arangodb,
mongodb: nil,
} }
break break

View File

@ -65,6 +65,13 @@ func initMongodb() (*mongo.Database, error) {
}, options.CreateIndexes()) }, options.CreateIndexes())
mongodb.CreateCollection(ctx, Collections.Session, options.CreateCollection()) 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 return mongodb, nil
} }

View File

@ -1,10 +1,12 @@
package db package db
import ( import (
"fmt"
"log" "log"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
) )
@ -12,7 +14,7 @@ import (
type Session struct { type Session struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"` 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:"-"` User User `json:"-" bson:"-"`
UserAgent string `json:"user_agent" bson:"user_agent"` UserAgent string `json:"user_agent" bson:"user_agent"`
IP string `json:"ip" bson:"ip"` IP string `json:"ip" bson:"ip"`
@ -63,3 +65,38 @@ func (mgr *manager) AddSession(session Session) error {
return nil 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
}

View File

@ -7,6 +7,7 @@ import (
"github.com/arangodb/go-driver" "github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver" arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/google/uuid" "github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
@ -41,6 +42,10 @@ func (mgr *manager) AddUser(user User) (User, error) {
user.ID = uuid.New().String() user.ID = uuid.New().String()
} }
if user.Roles == "" {
user.Roles = constants.DEFAULT_ROLES[0]
}
if IsORMSupported { if IsORMSupported {
// copy id as value for fields required for mongodb & arangodb // copy id as value for fields required for mongodb & arangodb
user.Key = user.ID user.Key = user.ID
@ -111,7 +116,7 @@ func (mgr *manager) UpdateUser(user User) (User, error) {
if IsMongoDB { if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection()) 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 { if err != nil {
log.Println("error updating user:", err) log.Println("error updating user:", err)
return user, err return user, err
@ -240,7 +245,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) {
} }
if IsArangoDB { 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{}{ bindVars := map[string]interface{}{
"id": id, "id": id,
} }
@ -267,7 +272,7 @@ func (mgr *manager) GetUserByID(id string) (User, error) {
if IsMongoDB { if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection()) 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 { if err != nil {
return user, err return user, err
} }
@ -297,7 +302,7 @@ func (mgr *manager) DeleteUser(user User) error {
if IsMongoDB { if IsMongoDB {
userCollection := mgr.mongodb.Collection(Collections.User, options.Collection()) 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 { if err != nil {
log.Println("error deleting user:", err) log.Println("error deleting user:", err)
return err return err

View File

@ -179,10 +179,10 @@ func (mgr *manager) GetVerificationByToken(token string) (VerificationRequest, e
return verification, nil return verification, nil
} }
func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, error) { func (mgr *manager) GetVerificationByEmail(email string, identifier string) (VerificationRequest, error) {
var verification VerificationRequest var verification VerificationRequest
if IsORMSupported { 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 { if result.Error != nil {
log.Println(`error getting verification token:`, result.Error) log.Println(`error getting verification token:`, result.Error)
@ -191,9 +191,10 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e
} }
if IsArangoDB { 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{}{ bindVars := map[string]interface{}{
"email": email, "email": email,
"identifier": identifier,
} }
cursor, err := mgr.arangodb.Query(nil, query, bindVars) cursor, err := mgr.arangodb.Query(nil, query, bindVars)
@ -218,7 +219,7 @@ func (mgr *manager) GetVerificationByEmail(email string) (VerificationRequest, e
if IsMongoDB { if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection()) 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 { if err != nil {
return verification, err return verification, err
} }
@ -248,7 +249,7 @@ func (mgr *manager) DeleteVerificationRequest(verificationRequest VerificationRe
if IsMongoDB { if IsMongoDB {
verificationRequestCollection := mgr.mongodb.Collection(Collections.VerificationRequest, options.Collection()) 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 { if err != nil {
log.Println("error deleting verification request::", err) log.Println("error deleting verification request::", err)
return err return err

15
server/env/env.go vendored
View File

@ -1,7 +1,6 @@
package env package env
import ( import (
"flag"
"log" "log"
"os" "os"
"strings" "strings"
@ -25,13 +24,8 @@ func InitEnv() {
if constants.ENV_PATH == "" { if constants.ENV_PATH == "" {
constants.ENV_PATH = `.env` 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 != nil && *ARG_ENV_FILE != "" {
if *ARG_ENV_FILE != "" {
constants.ENV_PATH = *ARG_ENV_FILE constants.ENV_PATH = *ARG_ENV_FILE
} }
@ -65,8 +59,9 @@ func InitEnv() {
if constants.DATABASE_TYPE == "" { if constants.DATABASE_TYPE == "" {
constants.DATABASE_TYPE = os.Getenv("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 constants.DATABASE_TYPE = *ARG_DB_TYPE
} }
@ -78,7 +73,7 @@ func InitEnv() {
if constants.DATABASE_URL == "" { if constants.DATABASE_URL == "" {
constants.DATABASE_URL = os.Getenv("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 constants.DATABASE_URL = *ARG_DB_URL
} }
@ -129,7 +124,7 @@ func InitEnv() {
if constants.AUTHORIZER_URL == "" { if constants.AUTHORIZER_URL == "" {
constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("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 constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
} }
} }

View File

@ -82,7 +82,7 @@ type ComplexityRoot struct {
Query struct { Query struct {
Meta func(childComplexity int) int Meta func(childComplexity int) int
Profile 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 Users func(childComplexity int) int
VerificationRequests func(childComplexity int) int VerificationRequests func(childComplexity int) int
} }
@ -137,7 +137,7 @@ type MutationResolver interface {
} }
type QueryResolver interface { type QueryResolver interface {
Meta(ctx context.Context) (*model.Meta, error) 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) Profile(ctx context.Context) (*model.User, error)
Users(ctx context.Context) ([]*model.User, error) Users(ctx context.Context) ([]*model.User, error)
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, 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 return e.complexity.Query.Profile(childComplexity), true
case "Query.token": case "Query.session":
if e.complexity.Query.Token == nil { if e.complexity.Query.Session == nil {
break break
} }
args, err := ec.field_Query_token_args(context.TODO(), rawArgs) args, err := ec.field_Query_session_args(context.TODO(), rawArgs)
if err != nil { if err != nil {
return 0, false 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": case "Query._users":
if e.complexity.Query.Users == nil { if e.complexity.Query.Users == nil {
@ -746,6 +746,7 @@ input VerifyEmailInput {
input ResendVerifyEmailInput { input ResendVerifyEmailInput {
email: String! email: String!
identifier: String!
} }
input UpdateProfileInput { input UpdateProfileInput {
@ -813,7 +814,7 @@ type Mutation {
type Query { type Query {
meta: Meta! meta: Meta!
token(roles: [String!]): AuthResponse session(roles: [String!]): AuthResponse
profile: User! profile: User!
# admin only apis # admin only apis
_users: [User!]! _users: [User!]!
@ -992,7 +993,7 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
return args, nil 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 var err error
args := map[string]interface{}{} args := map[string]interface{}{}
var arg0 []string 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) 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() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) 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) ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables) 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 { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
return graphql.Null return graphql.Null
@ -2006,7 +2007,7 @@ func (ec *executionContext) _Query_token(ctx context.Context, field graphql.Coll
fc.Args = args fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children 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 { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -4272,6 +4273,14 @@ func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Con
if err != nil { if err != nil {
return it, err 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 return res
}) })
case "token": case "session":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() { defer func() {
@ -4913,7 +4922,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
} }
}() }()
res = ec._Query_token(ctx, field) res = ec._Query_session(ctx, field)
return res return res
}) })
case "profile": case "profile":

View File

@ -44,7 +44,8 @@ type Meta struct {
} }
type ResendVerifyEmailInput struct { type ResendVerifyEmailInput struct {
Email string `json:"email"` Email string `json:"email"`
Identifier string `json:"identifier"`
} }
type ResetPasswordInput struct { type ResetPasswordInput struct {

View File

@ -89,6 +89,7 @@ input VerifyEmailInput {
input ResendVerifyEmailInput { input ResendVerifyEmailInput {
email: String! email: String!
identifier: String!
} }
input UpdateProfileInput { input UpdateProfileInput {
@ -156,7 +157,7 @@ type Mutation {
type Query { type Query {
meta: Meta! meta: Meta!
token(roles: [String!]): AuthResponse session(roles: [String!]): AuthResponse
profile: User! profile: User!
# admin only apis # admin only apis
_users: [User!]! _users: [User!]!

View File

@ -59,7 +59,7 @@ func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.Meta(ctx) 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) return resolvers.Token(ctx, roles)
} }

View File

@ -264,15 +264,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles) accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, inputRoles)
utils.SetCookie(c, accessToken) utils.SetCookie(c, accessToken)
session.SetToken(userIdStr, accessToken, refreshToken) session.SetToken(userIdStr, accessToken, refreshToken)
go func() { utils.CreateSession(user.ID, c)
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(c.Request),
IP: utils.GetIP(c.Request),
}
db.Mgr.AddSession(sessionData)
}()
c.Redirect(http.StatusTemporaryRedirect, redirectURL) c.Redirect(http.StatusTemporaryRedirect, redirectURL)
} }

View File

@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"fmt"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -54,22 +53,13 @@ func VerifyEmailHandler() gin.HandlerFunc {
// delete from verification table // delete from verification table
db.Mgr.DeleteVerificationRequest(verificationRequest) db.Mgr.DeleteVerificationRequest(verificationRequest)
userIdStr := fmt.Sprintf("%v", user.ID)
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles) refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken) session.SetToken(user.ID, accessToken, refreshToken)
go func() { utils.CreateSession(user.ID, c)
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(c.Request),
IP: utils.GetIP(c.Request),
}
db.Mgr.AddSession(sessionData)
}()
utils.SetCookie(c, accessToken) utils.SetCookie(c, accessToken)
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL) c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
} }

View File

@ -1,6 +1,8 @@
package main package main
import ( import (
"flag"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/env" "github.com/authorizerdev/authorizer/server/env"
@ -12,6 +14,12 @@ import (
) )
func main() { 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() env.InitEnv()
db.InitDB() db.InitDB()
session.InitSession() session.InitSession()
@ -20,7 +28,8 @@ func main() {
router := router.InitRouter() 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/*") router.LoadHTMLGlob("templates/*")
app := router.Group("/app") app := router.Group("/app")
{ {

View File

@ -60,15 +60,7 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(user.ID, accessToken, refreshToken) session.SetToken(user.ID, accessToken, refreshToken)
go func() { utils.CreateSession(user.ID, gc)
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
res = &model.AuthResponse{ res = &model.AuthResponse{
Message: `Logged in successfully`, Message: `Logged in successfully`,

View File

@ -20,18 +20,28 @@ func ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput)
return res, fmt.Errorf("invalid email") 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 { if err != nil {
return res, fmt.Errorf(`verification request not found`) 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 { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Mgr.AddVerification(db.VerificationRequest{ db.Mgr.AddVerification(db.VerificationRequest{
Token: token, Token: token,
Identifier: verificationRequest.Identifier, Identifier: params.Identifier,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: params.Email, Email: params.Email,
}) })

View File

@ -145,15 +145,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken) session.SetToken(userIdStr, accessToken, refreshToken)
go func() { utils.CreateSession(user.ID, gc)
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
res = &model.AuthResponse{ res = &model.AuthResponse{
Message: `Signed up successfully.`, Message: `Signed up successfully.`,
AccessToken: &accessToken, AccessToken: &accessToken,

View File

@ -66,15 +66,7 @@ func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
session.DeleteVerificationRequest(userIdStr, token) session.DeleteVerificationRequest(userIdStr, token)
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles) token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
session.SetToken(userIdStr, token, currentRefreshToken) session.SetToken(userIdStr, token, currentRefreshToken)
go func() { utils.CreateSession(user.ID, gc)
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
} }
utils.SetCookie(gc, token) utils.SetCookie(gc, token)

View File

@ -43,22 +43,13 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
// delete from verification table // delete from verification table
db.Mgr.DeleteVerificationRequest(verificationRequest) db.Mgr.DeleteVerificationRequest(verificationRequest)
userIdStr := fmt.Sprintf("%v", user.ID)
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles) refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles) accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
session.SetToken(userIdStr, accessToken, refreshToken) session.SetToken(user.ID, accessToken, refreshToken)
go func() { utils.CreateSession(user.ID, gc)
sessionData := db.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
}
db.Mgr.AddSession(sessionData)
}()
res = &model.AuthResponse{ res = &model.AuthResponse{
Message: `Email verified successfully.`, Message: `Email verified successfully.`,

View File

@ -2,44 +2,25 @@ package test
import ( import (
"net/http" "net/http"
"net/http/httptest"
"testing" "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" "github.com/stretchr/testify/assert"
) )
func TestCors(t *testing.T) { 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 allowedOrigin := "http://localhost:8080" // The allowed origin that you want to check
notAllowedOrigin := "http://myapp.com" notAllowedOrigin := "http://myapp.com"
server := httptest.NewServer(router) s := testSetup()
defer server.Close() defer s.Server.Close()
client := &http.Client{} 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 // You should get your origin (or a * depending on your config) if the
// passed origin is allowed. // 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.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")
} }

View File

@ -4,20 +4,16 @@ import (
"testing" "testing"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestEnvs(t *testing.T) { func TestEnvs(t *testing.T) {
constants.ENV_PATH = "../../.env.sample" constants.ENV_PATH = "../../.env.sample"
// env.InitEnv()
assert.Equal(t, constants.ADMIN_SECRET, "admin") assert.Equal(t, constants.ADMIN_SECRET, "admin")
assert.Equal(t, constants.ENV, "production") assert.Equal(t, constants.ENV, "production")
assert.Equal(t, constants.DATABASE_URL, "../../data.db") assert.False(t, constants.DISABLE_EMAIL_VERIFICATION)
assert.Equal(t, constants.DATABASE_TYPE, enum.Sqlite.String()) assert.False(t, constants.DISABLE_MAGIC_LINK_LOGIN)
assert.True(t, constants.DISABLE_EMAIL_VERIFICATION)
assert.True(t, constants.DISABLE_MAGIC_LINK_LOGIN)
assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION) assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION)
assert.Equal(t, constants.JWT_TYPE, "HS256") assert.Equal(t, constants.JWT_TYPE, "HS256")
assert.Equal(t, constants.JWT_SECRET, "random_string") assert.Equal(t, constants.JWT_SECRET, "random_string")

View File

@ -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)
})
}
}

90
server/test/login_test.go Normal file
View File

@ -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)
})
}
}

View File

@ -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)
})
}
}

View File

@ -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)
})
}
}

View File

@ -1,29 +1,77 @@
package test package test
import ( import (
"context"
"log"
"net/http/httptest"
"testing" "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/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestSQLSignUp(t *testing.T) { func commonSignupTest(s TestSetup, t *testing.T) {
w := httptest.NewRecorder() email := "signup." + s.TestInfo.Email
req := httptest.NewRequest("POST", "/graphql", nil) res, err := resolvers.Signup(s.Ctx, model.SignUpInput{
c, _ := gin.CreateTestContext(w) Email: email,
ctx := context.WithValue(req.Context(), "GinContextKey", c) Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password + "s",
res, err := resolvers.Signup(ctx, model.SignUpInput{
Email: "test@yopmail.com",
Password: "test",
ConfirmPassword: "test",
}) })
log.Println("=> signup err:", err) assert.NotNil(t, err, "invalid password errors")
log.Println("=> singup res:", res)
assert.Equal(t, "success", "success") 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)
})
}
} }

117
server/test/test.go Normal file
View File

@ -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)
}
}
}

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert" "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://xyxabc.in"), "it should be valid origin")
assert.True(t, utils.IsValidOrigin("http://localhost:8080"), "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")
}

View File

@ -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)
})
}
}

View File

@ -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)
}

View File

@ -6,6 +6,7 @@ import (
"strings" "strings"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -69,6 +70,13 @@ func IsValidRoles(userRoles []string, roles []string) bool {
return valid 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 { func IsStringArrayEqual(a, b []string) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false

View File

@ -24,7 +24,6 @@ func CreateVerificationToken(email string, tokenType string) (string, error) {
t.Claims = &CustomClaim{ t.Claims = &CustomClaim{
&jwt.StandardClaims{ &jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
}, },
tokenType, tokenType,