test-cases:

* completed test cases
* tested for all dbs
This commit is contained in:
lemonScaletech 2023-09-12 19:09:37 +05:30
parent 80e8b3aaac
commit 96fdc38c3c
13 changed files with 230 additions and 58 deletions

View File

@ -267,6 +267,14 @@ func NewProvider() (*provider, error) {
if err != nil {
return nil, err
}
// add totp_secret and totp_verified on users table
totpTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (totp_verified boolean, totp_secret text, app_data text)`, KeySpace, models.Collections.User)
err = session.Query(totpTableAlterQuery).Exec()
if err != nil {
log.Debug("Failed to alter table as column exists: ", err)
// return nil, err
}
return &provider{
db: session,
}, err

View File

@ -39,6 +39,7 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
user.TotpVerified = false
bytes, err := json.Marshal(user)
if err != nil {
@ -177,13 +178,19 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
// there is no offset in cassandra
// so we fetch till limit + offset
// and return the results from offset to limit
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset)
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, "+
"nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled,"+
" created_at, updated_at, totp_verified FROM %s LIMIT %d", KeySpace+"."+models.Collections.User,
pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0)
for scanner.Next() {
if counter >= pagination.Offset {
var user models.User
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods,
&user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber,
&user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled,
&user.CreatedAt, &user.UpdatedAt, &user.TotpVerified)
if err != nil {
return nil, err
}
@ -200,8 +207,8 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
// GetUserByEmail to get user information from database using email address
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
var user models.User
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, email)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at, totp_secret, totp_verified, app_data FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, email)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt, &user.TotpSecret, &user.TotpVerified, &user.AppData)
if err != nil {
return nil, err
}
@ -211,8 +218,8 @@ func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.Us
// GetUserByID to get user information from database using user ID
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
var user models.User
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at, totp_secret, totp_verified, app_data FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt, &user.TotpSecret, &user.TotpVerified, &user.AppData)
if err != nil {
return nil, err
}

View File

@ -103,7 +103,7 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
// GetUserByEmail to get user information from database using email address
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
var user *models.User
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE email = $1 LIMIT 1", p.scopeName, models.Collections.User)
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at, totp_secret, totp_verified FROM %s.%s WHERE email = $1 LIMIT 1", p.scopeName, models.Collections.User)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
@ -122,7 +122,7 @@ func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.Us
// GetUserByID to get user information from database using user ID
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
var user *models.User
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE _id = $1 LIMIT 1", p.scopeName, models.Collections.User)
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at, totp_secret, totp_verified FROM %s.%s WHERE _id = $1 LIMIT 1", p.scopeName, models.Collections.User)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,

View File

@ -13,6 +13,7 @@ require (
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/gocql/gocql v1.2.0
github.com/gokyle/twofactor v1.0.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
@ -26,6 +27,7 @@ require (
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.8.0
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d
github.com/twilio/twilio-go v1.7.2
github.com/vektah/gqlparser/v2 v2.5.1
go.mongodb.org/mongo-driver v1.8.1

View File

@ -56,6 +56,8 @@ github.com/aws/aws-sdk-go v1.44.298/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY=
github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
@ -131,6 +133,8 @@ github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/gokyle/twofactor v1.0.1 h1:uRhvx0S4Hb82RPIDALnf7QxbmPL49LyyaCkJDpWx+Ek=
github.com/gokyle/twofactor v1.0.1/go.mod h1:4gxzH1eaE/F3Pct/sCDNOylP0ClofUO5j4XZN9tKtLE=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
@ -256,6 +260,8 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw=
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
github.com/maruel/rs v1.1.0 h1:dh4OceAF5yD06EASOrb+DS358LI4g0B90YApSdjCP6U=
github.com/maruel/rs v1.1.0/go.mod h1:vzwMjzSJJxLIXmU62qHj6O5QRn5kvCKxFrfaFCxBcUY=
github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@ -323,6 +329,8 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d h1:4x1FeGJRB00cvxnKXnRJDT89fvG/Lzm2ecm0vlr/qDs=
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d/go.mod h1:uSELzeIcTceNCgzbKdJuJa0ouCqqtkyzL+6bnA3rM+M=
github.com/twilio/twilio-go v1.7.2 h1:tX38DXbSuDWWIK+tKAE2AJSMR6d8i7lf9ksY8J29VLE=
github.com/twilio/twilio-go v1.7.2/go.mod h1:tdnfQ5TjbewoAu4lf9bMsGvfuJ/QU9gYuv9yx3TSIXU=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
@ -747,5 +755,7 @@ modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.6.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -272,6 +272,7 @@ type ComplexityRoot struct {
RevokedTimestamp func(childComplexity int) int
Roles func(childComplexity int) int
SignupMethods func(childComplexity int) int
TotpVerified func(childComplexity int) int
UpdatedAt func(childComplexity int) int
}
@ -1900,6 +1901,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.SignupMethods(childComplexity), true
case "User.totp_verified":
if e.complexity.User.TotpVerified == nil {
break
}
return e.complexity.User.TotpVerified(childComplexity), true
case "User.updated_at":
if e.complexity.User.UpdatedAt == nil {
break
@ -2317,6 +2325,7 @@ type User {
revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean
app_data: Map
totp_verified: Boolean
}
type Users {
@ -4013,6 +4022,8 @@ func (ec *executionContext) fieldContext_AuthResponse_user(ctx context.Context,
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -7524,6 +7535,8 @@ func (ec *executionContext) fieldContext_InviteMembersResponse_Users(ctx context
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -9357,6 +9370,8 @@ func (ec *executionContext) fieldContext_Mutation__update_user(ctx context.Conte
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -10669,6 +10684,8 @@ func (ec *executionContext) fieldContext_Query_profile(ctx context.Context, fiel
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -10938,6 +10955,8 @@ func (ec *executionContext) fieldContext_Query__user(ctx context.Context, field
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -12846,6 +12865,47 @@ func (ec *executionContext) fieldContext_User_app_data(ctx context.Context, fiel
return fc, nil
}
func (ec *executionContext) _User_totp_verified(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_User_totp_verified(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.TotpVerified, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*bool)
fc.Result = res
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_User_totp_verified(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "User",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Users_pagination(ctx, field)
if err != nil {
@ -12979,6 +13039,8 @@ func (ec *executionContext) fieldContext_Users_users(ctx context.Context, field
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -13194,6 +13256,8 @@ func (ec *executionContext) fieldContext_ValidateSessionResponse_user(ctx contex
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
case "totp_verified":
return ec.fieldContext_User_totp_verified(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@ -20329,6 +20393,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._User_app_data(ctx, field, obj)
case "totp_verified":
out.Values[i] = ec._User_totp_verified(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}

View File

@ -455,6 +455,7 @@ type User struct {
RevokedTimestamp *int64 `json:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
AppData map[string]interface{} `json:"app_data"`
TotpVerified *bool `json:"totp_verified"`
}
type Users struct {

View File

@ -52,6 +52,7 @@ type User {
revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean
app_data: Map
totp_verified: Boolean
}
type Users {

View File

@ -54,6 +54,9 @@ func resendOTPTest(t *testing.T, s TestSetup) {
})
assert.NoError(t, err)
assert.NotNil(t, updateRes)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMailOTPLogin, false)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableTOTPLogin, true)
// Resend otp should return error as no initial opt is being sent
resendOtpRes, err := resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{
Email: refs.NewStringRef(email),

View File

@ -28,6 +28,8 @@ func revokeAccessTest(t *testing.T, s TestSetup) {
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
fmt.Println("\n", verifyRes)
fmt.Println("\n", err)
assert.NoError(t, err)
assert.NotNil(t, verifyRes.AccessToken)

View File

@ -23,6 +23,8 @@ func verificationRequestsTest(t *testing.T, s TestSetup) {
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
fmt.Println("res", res)
fmt.Println("err", err)
assert.NoError(t, err)
assert.NotNil(t, res)
limit := int64(10)

View File

@ -54,6 +54,8 @@ func verifyOTPTest(t *testing.T, s TestSetup) {
})
assert.NoError(t, err)
assert.NotEmpty(t, updateProfileRes.Message)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMailOTPLogin, false)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableTOTPLogin, true)
// Login should not return error but access token should be empty as otp should have been sent
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{

View File

@ -1,18 +1,30 @@
package test
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/token"
"github.com/gokyle/twofactor"
"github.com/stretchr/testify/assert"
"github.com/tuotoo/qrcode"
"strings"
"testing"
)
func verifyTOTPTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should verify totp`, func(t *testing.T) {
_, ctx := createContext(s)
email := "verify_otp." + s.TestInfo.Email
req, ctx := createContext(s)
email := "verify_totp." + s.TestInfo.Email
cleanData(email)
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
@ -28,54 +40,108 @@ func verifyTOTPTest(t *testing.T, s TestSetup) {
})
assert.Error(t, err)
assert.Nil(t, loginRes)
_, err = db.Provider.GenerateTotp(ctx, loginRes.User.ID)
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeBasicAuthSignup)
assert.Nil(t, err)
//assert.Equal(t, ??, string)
// verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
// Token: verificationRequest.Token,
// })
// assert.Nil(t, err)
// assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
//
// // Using access token update profile
// s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken))
// ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
// updateProfileRes, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
// IsMultiFactorAuthEnabled: refs.NewBoolRef(true),
// })
// assert.NoError(t, err)
// assert.NotEmpty(t, updateProfileRes.Message)
//
// // Login should not return error but access token should be empty as otp should have been sent
// loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
// Email: email,
// Password: s.TestInfo.Password,
// })
// assert.NoError(t, err)
// assert.NotNil(t, loginRes)
// assert.Nil(t, loginRes.AccessToken)
//
// // Get otp from db
// otp, err := db.Provider.GetOTPByEmail(ctx, email)
// assert.NoError(t, err)
// assert.NotEmpty(t, otp.Otp)
// // Get user by email
// user, err := db.Provider.GetUserByEmail(ctx, email)
// assert.NoError(t, err)
// assert.NotNil(t, user)
// // Set mfa cookie session
// mfaSession := uuid.NewString()
// memorystore.Provider.SetMfaSession(user.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
// cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
// cookie = strings.TrimSuffix(cookie, ";")
// req.Header.Set("Cookie", cookie)
// verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
// Email: &email,
// Otp: otp.Otp,
// })
// assert.Nil(t, err)
// assert.NotEqual(t, verifyOtpRes.AccessToken, "", "access token should not be empty")
// cleanData(email)
//})
assert.Equal(t, email, verificationRequest.Email)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
assert.Nil(t, err)
assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
// Using access token update profile
s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken))
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
updateProfileRes, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
IsMultiFactorAuthEnabled: refs.NewBoolRef(true),
})
assert.NoError(t, err)
assert.NotEmpty(t, updateProfileRes.Message)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableTOTPLogin, false)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMailOTPLogin, true)
// Login should not return error but access token should be empty
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, loginRes)
assert.NotNil(t, loginRes.TotpBase64url)
assert.NotNil(t, loginRes.TokenTotp)
assert.Nil(t, loginRes.AccessToken)
assert.Equal(t, loginRes.Message, `Proceed to totp screen`)
// get totp url for validation
pngBytes, err := base64.StdEncoding.DecodeString(*loginRes.TotpBase64url)
assert.NoError(t, err)
qrmatrix, err := qrcode.Decode(bytes.NewReader(pngBytes))
assert.NoError(t, err)
tf, label, err := twofactor.FromURL(qrmatrix.Content)
data := strings.Split(label, ":")
assert.NoError(t, err)
assert.Equal(t, email, data[1])
assert.NotNil(t, tf)
code := tf.OTP()
assert.NotEmpty(t, code)
valid, err := resolvers.VerifyTotpResolver(ctx, model.VerifyTOTPRequest{
Otp: code,
Token: *loginRes.TokenTotp,
})
accessToken := *valid.AccessToken
assert.NoError(t, err)
assert.NotNil(t, accessToken)
assert.Equal(t, `Logged in successfully`, valid.Message)
assert.NotEmpty(t, accessToken)
claims, err := token.ParseJWTToken(accessToken)
assert.NoError(t, err)
assert.NotEmpty(t, claims)
loginMethod := claims["login_method"]
sessionKey := verifyRes.User.ID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + verifyRes.User.ID
}
sessionToken, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string))
assert.NoError(t, err)
assert.NotEmpty(t, sessionToken)
cookie := fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", sessionToken)
cookie = strings.TrimSuffix(cookie, ";")
req.Header.Set("Cookie", cookie)
//logged out
logout, err := resolvers.LogoutResolver(ctx)
assert.NoError(t, err)
assert.Equal(t, logout.Message, `Logged out successfully`)
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, loginRes)
assert.NotNil(t, loginRes.TokenTotp)
assert.Nil(t, loginRes.TotpBase64url)
assert.Nil(t, loginRes.AccessToken)
assert.Equal(t, loginRes.Message, `Proceed to totp screen`)
code = tf.OTP()
assert.NotEmpty(t, code)
valid, err = resolvers.VerifyTotpResolver(ctx, model.VerifyTOTPRequest{
Otp: code,
Token: *loginRes.TokenTotp,
})
assert.NoError(t, err)
assert.NotNil(t, *valid.AccessToken)
assert.Equal(t, `Logged in successfully`, valid.Message)
cleanData(email)
})
}