From 9fda8c01f58de46ee5c6fe7cd87ffb9c66e10307 Mon Sep 17 00:00:00 2001 From: lemonScaletech Date: Fri, 1 Sep 2023 19:36:47 +0530 Subject: [PATCH] feat: * added toggle in dashboard * fixing issue with env set --- .../src/components/EnvComponents/Features.tsx | 44 ++++++ dashboard/src/constants.ts | 4 + dashboard/src/graphql/queries/index.ts | 2 + dashboard/src/pages/Environment.tsx | 2 + server/constants/env.go | 7 +- server/env/env.go | 45 ++++++ server/env/persist_env.go | 7 +- server/graph/generated/generated.go | 144 +++++++++++++++++- server/graph/model/models_gen.go | 4 + server/graph/schema.graphqls | 4 + server/memorystore/memory_store.go | 2 + server/memorystore/providers/redis/store.go | 2 +- server/resolvers/env.go | 2 + server/resolvers/login.go | 16 +- server/resolvers/update_env.go | 11 ++ 15 files changed, 289 insertions(+), 7 deletions(-) diff --git a/dashboard/src/components/EnvComponents/Features.tsx b/dashboard/src/components/EnvComponents/Features.tsx index 929da5b..9f0c445 100644 --- a/dashboard/src/components/EnvComponents/Features.tsx +++ b/dashboard/src/components/EnvComponents/Features.tsx @@ -4,6 +4,7 @@ import InputField from '../InputField'; import { SwitchInputType } from '../../constants'; const Features = ({ variables, setVariables }: any) => { + // window.alert(variables) return (
{' '} @@ -24,6 +25,8 @@ const Features = ({ variables, setVariables }: any) => { /> + + Email Verification: @@ -97,6 +100,7 @@ const Features = ({ variables, setVariables }: any) => { also ignore the user MFA setting. + { /> + + { + !variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && + + + TOTP: + + Note: to enable totp mfa + + + + + + + + } + {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && + + + EMAIL OTP: + + Note: to enable email otp mfa + + + + + + + } + diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts index 2dbf412..7a12131 100644 --- a/dashboard/src/constants.ts +++ b/dashboard/src/constants.ts @@ -85,6 +85,8 @@ export const SwitchInputType = { DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION', ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION', DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND', + DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN', + DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN', }; export const DateInputType = { @@ -169,6 +171,8 @@ export interface envVarTypes { DEFAULT_AUTHORIZE_RESPONSE_TYPE: string; DEFAULT_AUTHORIZE_RESPONSE_MODE: string; DISABLE_PLAYGROUND: boolean; + DISABLE_TOTP_LOGIN: boolean; + DISABLE_MAIL_OTP_LOGIN: boolean; } export const envSubViews = { diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts index c5152f5..ffa8cd9 100644 --- a/dashboard/src/graphql/queries/index.ts +++ b/dashboard/src/graphql/queries/index.ts @@ -74,6 +74,8 @@ export const EnvVariablesQuery = ` DEFAULT_AUTHORIZE_RESPONSE_TYPE DEFAULT_AUTHORIZE_RESPONSE_MODE DISABLE_PLAYGROUND + DISABLE_TOTP_LOGIN + DISABLE_MAIL_OTP_LOGIN } } `; diff --git a/dashboard/src/pages/Environment.tsx b/dashboard/src/pages/Environment.tsx index c8d405c..8871f4a 100644 --- a/dashboard/src/pages/Environment.tsx +++ b/dashboard/src/pages/Environment.tsx @@ -94,6 +94,8 @@ const Environment = () => { DEFAULT_AUTHORIZE_RESPONSE_TYPE: '', DEFAULT_AUTHORIZE_RESPONSE_MODE: '', DISABLE_PLAYGROUND: false, + DISABLE_TOTP_LOGIN: false, + DISABLE_MAIL_OTP_LOGIN: true, }); const [fieldVisibility, setFieldVisibility] = React.useState< diff --git a/server/constants/env.go b/server/constants/env.go index 649603c..e89984b 100644 --- a/server/constants/env.go +++ b/server/constants/env.go @@ -160,9 +160,12 @@ const ( // EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION // this variable is used to completely disable multi factor authentication. It will have no effect on profile preference EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION" - // EnvKeyDisableTotpAuthentication is key for env variable DISABLE_TOTP_AUTHENTICATION + // EnvKeyDisableTOTPLogin is key for env variable DISABLE_TOTP_LOGIN // this variable is used to completely disable totp verification - EnvKeyDisableTotpAuthentication = "DISABLE_TOTP_AUTHENTICATION" + EnvKeyDisableTOTPLogin = "DISABLE_TOTP_LOGIN" + // EnvKeyDisableMailOTPLogin is key for env variable DISABLE_MAIL_OTP_LOGIN + // this variable is used to completely disable totp verification + EnvKeyDisableMailOTPLogin = "DISABLE_MAIL_OTP_LOGIN" // EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION // this variable is used to disable phone verification EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION" diff --git a/server/env/env.go b/server/env/env.go index 3f65cf2..46ae2f4 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -104,6 +104,8 @@ func InitAllEnv() error { osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword) osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication) osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication) + osDisableTOTPLogin := os.Getenv(constants.EnvKeyDisableTOTPLogin) + osDisableMailOTPLogin := os.Getenv(constants.EnvKeyDisableMailOTPLogin) // phone verification var osDisablePhoneVerification := os.Getenv(constants.EnvKeyDisablePhoneVerification) osDisablePlayground := os.Getenv(constants.EnvKeyDisablePlayGround) @@ -689,6 +691,7 @@ func InitAllEnv() error { envData[constants.EnvKeyDisableEmailVerification] = true envData[constants.EnvKeyDisableMagicLinkLogin] = true envData[constants.EnvKeyIsEmailServiceEnabled] = false + envData[constants.EnvKeyDisableMailOTPLogin] = true } if envData[constants.EnvKeySmtpHost] != "" && envData[constants.EnvKeySmtpUsername] != "" && envData[constants.EnvKeySmtpPassword] != "" && envData[constants.EnvKeySenderEmail] != "" && envData[constants.EnvKeySmtpPort] != "" { @@ -705,6 +708,7 @@ func InitAllEnv() error { if envData[constants.EnvKeyDisableEmailVerification].(bool) { envData[constants.EnvKeyDisableMagicLinkLogin] = true + envData[constants.EnvKeyDisableMailOTPLogin] = true } if val, ok := envData[constants.EnvKeyAllowedOrigins]; !ok || val == "" { @@ -840,6 +844,47 @@ func InitAllEnv() error { } } + if _, ok := envData[constants.EnvKeyDisableTOTPLogin]; !ok { + envData[constants.EnvKeyDisableTOTPLogin] = osDisableTOTPLogin == "false" + } + if osDisableTOTPLogin != "" { + boolValue, err := strconv.ParseBool(osDisableTOTPLogin) + if err != nil { + return err + } + if boolValue != envData[constants.EnvKeyDisableTOTPLogin].(bool) { + envData[constants.EnvKeyDisableTOTPLogin] = boolValue + } + } + + if _, ok := envData[constants.EnvKeyDisableMailOTPLogin]; !ok { + envData[constants.EnvKeyDisableMailOTPLogin] = osDisableMailOTPLogin == "true" + } + if osDisableMailOTPLogin != "" { + boolValue, err := strconv.ParseBool(osDisableMailOTPLogin) + if err != nil { + return err + } + if boolValue != envData[constants.EnvKeyDisableMailOTPLogin].(bool) { + envData[constants.EnvKeyDisableMailOTPLogin] = boolValue + } + } + + if envData[constants.EnvKeyDisableTOTPLogin] == false && envData[constants.EnvKeyDisableMailOTPLogin].(bool) == false { + errors.New("can't enable both mfa") + } + + if envData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) { + envData[constants.EnvKeyDisableTOTPLogin] = true + envData[constants.EnvKeyDisableMailOTPLogin] = true + } else { + if !envData[constants.EnvKeyDisableMailOTPLogin].(bool) && !envData[constants.EnvKeyDisableTOTPLogin].(bool) { + errors.New("can't enable both mfa methods at same time") + envData[constants.EnvKeyDisableMailOTPLogin] = false + envData[constants.EnvKeyDisableTOTPLogin] = true + } + } + err = memorystore.Provider.UpdateEnvStore(envData) if err != nil { log.Debug("Error while updating env store: ", err) diff --git a/server/env/persist_env.go b/server/env/persist_env.go index eb0b64f..56142c5 100644 --- a/server/env/persist_env.go +++ b/server/env/persist_env.go @@ -196,7 +196,7 @@ func PersistEnv() error { envValue := strings.TrimSpace(os.Getenv(key)) if envValue != "" { switch key { - case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification, constants.EnvKeyDisablePlayGround: + case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification, constants.EnvKeyDisablePlayGround, constants.EnvKeyDisableTOTPLogin, constants.EnvKeyDisableMailOTPLogin: if envValueBool, err := strconv.ParseBool(envValue); err == nil { if value.(bool) != envValueBool { storeData[key] = envValueBool @@ -227,6 +227,11 @@ func PersistEnv() error { storeData[constants.EnvKeyDisableMagicLinkLogin] = true hasChanged = true } + + if !storeData[constants.EnvKeyDisableMailOTPLogin].(bool) { + storeData[constants.EnvKeyDisableMailOTPLogin] = true + hasChanged = true + } } err = memorystore.Provider.UpdateEnvStore(storeData) diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index 84523cc..2694bc5 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -97,11 +97,13 @@ type ComplexityRoot struct { DisableEmailVerification func(childComplexity int) int DisableLoginPage func(childComplexity int) int DisableMagicLinkLogin func(childComplexity int) int + DisableMailOtpLogin func(childComplexity int) int DisableMultiFactorAuthentication func(childComplexity int) int DisablePlayground func(childComplexity int) int DisableRedisForEnv func(childComplexity int) int DisableSignUp func(childComplexity int) int DisableStrongPassword func(childComplexity int) int + DisableTotpLogin func(childComplexity int) int EnforceMultiFactorAuthentication func(childComplexity int) int FacebookClientID func(childComplexity int) int FacebookClientSecret func(childComplexity int) int @@ -699,6 +701,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Env.DisableMagicLinkLogin(childComplexity), true + case "Env.DISABLE_MAIL_OTP_LOGIN": + if e.complexity.Env.DisableMailOtpLogin == nil { + break + } + + return e.complexity.Env.DisableMailOtpLogin(childComplexity), true + case "Env.DISABLE_MULTI_FACTOR_AUTHENTICATION": if e.complexity.Env.DisableMultiFactorAuthentication == nil { break @@ -734,6 +743,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Env.DisableStrongPassword(childComplexity), true + case "Env.DISABLE_TOTP_LOGIN": + if e.complexity.Env.DisableTotpLogin == nil { + break + } + + return e.complexity.Env.DisableTotpLogin(childComplexity), true + case "Env.ENFORCE_MULTI_FACTOR_AUTHENTICATION": if e.complexity.Env.EnforceMultiFactorAuthentication == nil { break @@ -2384,6 +2400,8 @@ type Env { DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String DISABLE_PLAYGROUND: Boolean! + DISABLE_MAIL_OTP_LOGIN: Boolean! + DISABLE_TOTP_LOGIN: Boolean! } type ValidateJWTTokenResponse { @@ -2507,6 +2525,8 @@ input UpdateEnvInput { DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String DISABLE_PLAYGROUND: Boolean + DISABLE_MAIL_OTP_LOGIN: Boolean + DISABLE_TOTP_LOGIN: Boolean } input AdminLoginInput { @@ -6895,6 +6915,94 @@ func (ec *executionContext) fieldContext_Env_DISABLE_PLAYGROUND(ctx context.Cont return fc, nil } +func (ec *executionContext) _Env_DISABLE_MAIL_OTP_LOGIN(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Env_DISABLE_MAIL_OTP_LOGIN(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.DisableMailOtpLogin, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Env_DISABLE_MAIL_OTP_LOGIN(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Env", + 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) _Env_DISABLE_TOTP_LOGIN(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Env_DISABLE_TOTP_LOGIN(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.DisableTotpLogin, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Env_DISABLE_TOTP_LOGIN(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Env", + 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) _Error_message(ctx context.Context, field graphql.CollectedField, obj *model.Error) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Error_message(ctx, field) if err != nil { @@ -10810,6 +10918,10 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g return ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx, field) case "DISABLE_PLAYGROUND": return ec.fieldContext_Env_DISABLE_PLAYGROUND(ctx, field) + case "DISABLE_MAIL_OTP_LOGIN": + return ec.fieldContext_Env_DISABLE_MAIL_OTP_LOGIN(ctx, field) + case "DISABLE_TOTP_LOGIN": + return ec.fieldContext_Env_DISABLE_TOTP_LOGIN(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Env", field.Name) }, @@ -17196,7 +17308,7 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob asMap[k] = v } - fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "SENDER_NAME", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE", "DISABLE_PLAYGROUND"} + fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "SENDER_NAME", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE", "DISABLE_PLAYGROUND", "DISABLE_MAIL_OTP_LOGIN", "DISABLE_TOTP_LOGIN"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -17627,6 +17739,22 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob if err != nil { return it, err } + case "DISABLE_MAIL_OTP_LOGIN": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISABLE_MAIL_OTP_LOGIN")) + it.DisableMailOtpLogin, err = ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + case "DISABLE_TOTP_LOGIN": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISABLE_TOTP_LOGIN")) + it.DisableTotpLogin, err = ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } } } @@ -18625,6 +18753,20 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._Env_DISABLE_PLAYGROUND(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "DISABLE_MAIL_OTP_LOGIN": + + out.Values[i] = ec._Env_DISABLE_MAIL_OTP_LOGIN(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "DISABLE_TOTP_LOGIN": + + out.Values[i] = ec._Env_DISABLE_TOTP_LOGIN(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go index 49adb71..7430472 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -123,6 +123,8 @@ type Env struct { DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"` DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"` DisablePlayground bool `json:"DISABLE_PLAYGROUND"` + DisableMailOtpLogin bool `json:"DISABLE_MAIL_OTP_LOGIN"` + DisableTotpLogin bool `json:"DISABLE_TOTP_LOGIN"` } type Error struct { @@ -382,6 +384,8 @@ type UpdateEnvInput struct { DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"` DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"` DisablePlayground *bool `json:"DISABLE_PLAYGROUND"` + DisableMailOtpLogin *bool `json:"DISABLE_MAIL_OTP_LOGIN"` + DisableTotpLogin *bool `json:"DISABLE_TOTP_LOGIN"` } type UpdateProfileInput struct { diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index 44a90ba..433144a 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -174,6 +174,8 @@ type Env { DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String DISABLE_PLAYGROUND: Boolean! + DISABLE_MAIL_OTP_LOGIN: Boolean! + DISABLE_TOTP_LOGIN: Boolean! } type ValidateJWTTokenResponse { @@ -297,6 +299,8 @@ input UpdateEnvInput { DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String DISABLE_PLAYGROUND: Boolean + DISABLE_MAIL_OTP_LOGIN: Boolean + DISABLE_TOTP_LOGIN: Boolean } input AdminLoginInput { diff --git a/server/memorystore/memory_store.go b/server/memorystore/memory_store.go index 4004d68..a143d04 100644 --- a/server/memorystore/memory_store.go +++ b/server/memorystore/memory_store.go @@ -36,9 +36,11 @@ func InitMemStore() error { constants.EnvKeyIsSMSServiceEnabled: false, constants.EnvKeyEnforceMultiFactorAuthentication: false, constants.EnvKeyDisableMultiFactorAuthentication: false, + constants.EnvKeyDisableTOTPLogin: false, constants.EnvKeyAppCookieSecure: true, constants.EnvKeyAdminCookieSecure: true, constants.EnvKeyDisablePlayGround: true, + constants.EnvKeyDisableMailOTPLogin: true, } requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv() diff --git a/server/memorystore/providers/redis/store.go b/server/memorystore/providers/redis/store.go index d761ce1..a1b21db 100644 --- a/server/memorystore/providers/redis/store.go +++ b/server/memorystore/providers/redis/store.go @@ -176,7 +176,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) { return nil, err } for key, value := range data { - if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure || key == constants.EnvKeyDisablePlayGround { + if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure || key == constants.EnvKeyDisablePlayGround || key == constants.EnvKeyDisableTOTPLogin || key == constants.EnvKeyDisableMailOTPLogin { boolValue, err := strconv.ParseBool(value) if err != nil { return res, err diff --git a/server/resolvers/env.go b/server/resolvers/env.go index b7a949a..5eb86bd 100644 --- a/server/resolvers/env.go +++ b/server/resolvers/env.go @@ -203,6 +203,8 @@ func EnvResolver(ctx context.Context) (*model.Env, error) { res.AdminCookieSecure = store[constants.EnvKeyAdminCookieSecure].(bool) res.AppCookieSecure = store[constants.EnvKeyAppCookieSecure].(bool) res.DisablePlayground = store[constants.EnvKeyDisablePlayGround].(bool) + res.DisableMailOtpLogin = store[constants.EnvKeyDisableMailOTPLogin].(bool) + res.DisableTotpLogin = store[constants.EnvKeyDisableTOTPLogin].(bool) return res, nil } diff --git a/server/resolvers/login.go b/server/resolvers/login.go index bc1f3d9..1472697 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -110,8 +110,18 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes log.Debug("MFA service not enabled: ", err) } + isTOTPLoginDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin) + if err != nil || !isTOTPLoginDisabled { + log.Debug("totp service not enabled: ", err) + } + + isMailOTPDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMailOTPLogin) + if err != nil || !isMailOTPDisabled { + log.Debug("mail OTP service not enabled: ", err) + } + // If email service is not enabled continue the process in any way - if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled { + if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMailOTPDisabled && !isMFADisabled { otp := utils.GenerateOTP() expires := time.Now().Add(1 * time.Minute).Unix() otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{ @@ -150,14 +160,16 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes }, nil } - if !isMFADisabled && refs.BoolValue(user.IsMultiFactorAuthEnabled) { + if !isMFADisabled && refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isTOTPLoginDisabled { if user.TotpSecret == nil { base64URL, err := db.Provider.GenerateTotp(ctx, user.ID) if err != nil { log.Debug("error while generating base64 url: ", err) } res.TotpBase64url = base64URL + } + } code := "" diff --git a/server/resolvers/update_env.go b/server/resolvers/update_env.go index 96388aa..6b3fe0e 100644 --- a/server/resolvers/update_env.go +++ b/server/resolvers/update_env.go @@ -263,6 +263,17 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model } } + if updatedData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) { + updatedData[constants.EnvKeyDisableTOTPLogin] = true + updatedData[constants.EnvKeyDisableMailOTPLogin] = true + } else { + if !updatedData[constants.EnvKeyDisableMailOTPLogin].(bool) && !updatedData[constants.EnvKeyDisableTOTPLogin].(bool) { + errors.New("can't enable both mfa methods at same time") + updatedData[constants.EnvKeyDisableMailOTPLogin] = true + updatedData[constants.EnvKeyDisableTOTPLogin] = false + } + } + if updatedData[constants.EnvKeySmtpHost] != "" || updatedData[constants.EnvKeySmtpUsername] != "" || updatedData[constants.EnvKeySmtpPassword] != "" || updatedData[constants.EnvKeySenderEmail] != "" && updatedData[constants.EnvKeySmtpPort] != "" { updatedData[constants.EnvKeyIsEmailServiceEnabled] = true }