From 4f81d1969ebe8679a1bf6ef472e67ddbc6ce76b6 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Sat, 13 Aug 2022 11:34:24 +0530 Subject: [PATCH 1/2] fix email template - fix verification types - add design to cassandra db provider for email_template - fix default email verification types to include update_email --- dashboard/package-lock.json | 21 +++-- .../components/UpdateEmailTemplateModal.tsx | 2 +- dashboard/src/graphql/queries/index.ts | 88 +++++++++---------- dashboard/src/pages/Users.tsx | 10 ++- server/constants/verification_types.go | 2 +- .../providers/cassandradb/email_template.go | 14 +-- server/db/providers/cassandradb/provider.go | 5 +- server/email/email.go | 2 +- server/resolvers/forgot_password.go | 7 +- server/resolvers/update_profile.go | 2 +- server/test/add_email_template_test.go | 16 +++- server/test/update_email_template_test.go | 2 + 12 files changed, 100 insertions(+), 71 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 797c43f..ce1ea42 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -2605,7 +2605,8 @@ "@chakra-ui/css-reset": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz", - "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==" + "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==", + "requires": {} }, "@chakra-ui/descendant": { "version": "2.1.1", @@ -3209,7 +3210,8 @@ "@graphql-typed-document-node/core": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", - "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==" + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "requires": {} }, "@popperjs/core": { "version": "2.11.0", @@ -3481,7 +3483,8 @@ "draftjs-utils": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz", - "integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg==" + "integrity": "sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg==", + "requires": {} }, "error-ex": { "version": "1.3.2", @@ -3755,7 +3758,8 @@ "html-to-draftjs": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/html-to-draftjs/-/html-to-draftjs-1.5.0.tgz", - "integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==" + "integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==", + "requires": {} }, "import-fresh": { "version": "3.3.0", @@ -3945,7 +3949,8 @@ "react-email-editor": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/react-email-editor/-/react-email-editor-1.6.1.tgz", - "integrity": "sha512-pEWpRmTY0ok03cwTGqEOoEldnzThhuRGTrcMnv8W3/jc5MTfcr9USU/IQ9HrVvFStLKoxYBIQnSKY+iCYWOtSQ==" + "integrity": "sha512-pEWpRmTY0ok03cwTGqEOoEldnzThhuRGTrcMnv8W3/jc5MTfcr9USU/IQ9HrVvFStLKoxYBIQnSKY+iCYWOtSQ==", + "requires": {} }, "react-fast-compare": { "version": "3.2.0", @@ -3968,7 +3973,8 @@ "react-icons": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", - "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==" + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", + "requires": {} }, "react-is": { "version": "16.13.1", @@ -4159,7 +4165,8 @@ "use-callback-ref": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", - "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==" + "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", + "requires": {} }, "use-sidecar": { "version": "1.0.5", diff --git a/dashboard/src/components/UpdateEmailTemplateModal.tsx b/dashboard/src/components/UpdateEmailTemplateModal.tsx index e2d61c5..3f903e0 100644 --- a/dashboard/src/components/UpdateEmailTemplateModal.tsx +++ b/dashboard/src/components/UpdateEmailTemplateModal.tsx @@ -333,7 +333,7 @@ const UpdateEmailTemplate = ({ {templateVariables.map((i) => ( - {`{{${i.text}}}`} + {`{{.${i.text}}}`} Roles Verified Access - MFA + + + MFA + + Actions @@ -404,13 +408,13 @@ export default function Users() { multiFactorAuthUpdateHandler(user)} > - Disable MFA + Disable MultiFactor Authentication ) : ( multiFactorAuthUpdateHandler(user)} > - Enable MFA + Enable MultiFactor Authentication )} diff --git a/server/constants/verification_types.go b/server/constants/verification_types.go index 0a83df8..599d83a 100644 --- a/server/constants/verification_types.go +++ b/server/constants/verification_types.go @@ -12,5 +12,5 @@ const ( // VerificationTypeInviteMember is the invite_member verification type VerificationTypeInviteMember = "invite_member" // VerificationTypeOTP is the otp verification type - VerificationTypeOTP = "otp" + VerificationTypeOTP = "verify_otp" ) diff --git a/server/db/providers/cassandradb/email_template.go b/server/db/providers/cassandradb/email_template.go index ea18fce..7cb64cb 100644 --- a/server/db/providers/cassandradb/email_template.go +++ b/server/db/providers/cassandradb/email_template.go @@ -29,7 +29,7 @@ func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.Em return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName) } - insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt) + insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, design, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Design, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt) err := p.db.Query(insertQuery).Exec() if err != nil { return nil, err @@ -103,14 +103,14 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin // 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, event_name, subject, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset) + query := fmt.Sprintf("SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset) scanner := p.db.Query(query).Iter().Scanner() counter := int64(0) for scanner.Next() { if counter >= pagination.Offset { var emailTemplate models.EmailTemplate - err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) + err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) if err != nil { return nil, err } @@ -128,8 +128,8 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin // GetEmailTemplateByID to get EmailTemplate by id func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) { var emailTemplate models.EmailTemplate - query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID) - err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) + query := fmt.Sprintf(`SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID) + err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) if err != nil { return nil, err } @@ -139,8 +139,8 @@ func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID str // GetEmailTemplateByEventName to get EmailTemplate by event_name func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) { var emailTemplate models.EmailTemplate - query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName) - err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) + query := fmt.Sprintf(`SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName) + err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt) if err != nil { return nil, err } diff --git a/server/db/providers/cassandradb/provider.go b/server/db/providers/cassandradb/provider.go index 0d0b193..5acb656 100644 --- a/server/db/providers/cassandradb/provider.go +++ b/server/db/providers/cassandradb/provider.go @@ -224,10 +224,11 @@ func NewProvider() (*provider, error) { return nil, err } // add subject on email_templates table - emailTemplateAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD subject text;`, KeySpace, models.Collections.EmailTemplate) + emailTemplateAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (subject text, design text);`, KeySpace, models.Collections.EmailTemplate) err = session.Query(emailTemplateAlterQuery).Exec() if err != nil { - return nil, err + log.Debug("Failed to alter table as column exists: ", err) + // continue } otpCollection := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, otp text, expires_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.OTP) diff --git a/server/email/email.go b/server/email/email.go index 730ae56..fe5f7ff 100644 --- a/server/email/email.go +++ b/server/email/email.go @@ -18,7 +18,7 @@ import ( func getDefaultTemplate(event string) *model.EmailTemplate { switch event { - case constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeMagicLinkLogin: + case constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeMagicLinkLogin, constants.VerificationTypeUpdateEmail: return &model.EmailTemplate{ Subject: emailVerificationSubject, Template: emailVerificationTemplate, diff --git a/server/resolvers/forgot_password.go b/server/resolvers/forgot_password.go index 77ff265..c04bf1c 100644 --- a/server/resolvers/forgot_password.go +++ b/server/resolvers/forgot_password.go @@ -15,6 +15,7 @@ import ( "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/parsers" + "github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/validators" @@ -61,9 +62,9 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu log.Debug("Failed to generate nonce: ", err) return res, err } - redirectURL := parsers.GetAppURL(gc) + "/reset-password" - if params.RedirectURI != nil { - redirectURL = *params.RedirectURI + redirectURL := parsers.GetAppURL(gc) + if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" { + redirectURL = refs.StringValue(params.RedirectURI) } verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL) diff --git a/server/resolvers/update_profile.go b/server/resolvers/update_profile.go index 9888aa5..3263974 100644 --- a/server/resolvers/update_profile.go +++ b/server/resolvers/update_profile.go @@ -245,7 +245,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput) } // exec it as go routine so that we can reduce the api latency - go email.SendEmail([]string{user.Email}, constants.VerificationTypeBasicAuthSignup, map[string]interface{}{ + go email.SendEmail([]string{user.Email}, verificationType, map[string]interface{}{ "user": user.ToMap(), "organization": utils.GetOrganization(), "verification_url": utils.GetEmailVerificationURL(verificationToken, hostname), diff --git a/server/test/add_email_template_test.go b/server/test/add_email_template_test.go index 40cf94e..593e4f1 100644 --- a/server/test/add_email_template_test.go +++ b/server/test/add_email_template_test.go @@ -31,7 +31,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) { assert.Nil(t, emailTemplate) }) - t.Run("should not add email template for empty template", func(t *testing.T) { + t.Run("should not add email template for empty subject", func(t *testing.T) { emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{ EventName: s.TestInfo.TestEmailTemplateEventTypes[0], Template: " test ", @@ -50,12 +50,25 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) { assert.Error(t, err) assert.Nil(t, emailTemplate) }) + + t.Run("should not add email template with empty design", func(t *testing.T) { + emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{ + EventName: s.TestInfo.TestEmailTemplateEventTypes[0], + Template: "test", + Subject: "test", + Design: " ", + }) + assert.Error(t, err) + assert.Nil(t, emailTemplate) + }) + for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes { t.Run("should add email template for "+eventType, func(t *testing.T) { emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{ EventName: eventType, Template: "Test email", Subject: "Test email", + Design: "Test design", }) assert.NoError(t, err) assert.NotNil(t, emailTemplate) @@ -65,6 +78,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) { assert.NoError(t, err) assert.Equal(t, et.EventName, eventType) assert.Equal(t, "Test email", et.Subject) + assert.Equal(t, "Test design", et.Design) }) } }) diff --git a/server/test/update_email_template_test.go b/server/test/update_email_template_test.go index 4f2a999..94b9de5 100644 --- a/server/test/update_email_template_test.go +++ b/server/test/update_email_template_test.go @@ -32,6 +32,7 @@ func updateEmailTemplateTest(t *testing.T, s TestSetup) { ID: emailTemplate.ID, Template: refs.NewStringRef("Updated test template"), Subject: refs.NewStringRef("Updated subject"), + Design: refs.NewStringRef("Updated design"), }) assert.NoError(t, err) @@ -44,5 +45,6 @@ func updateEmailTemplateTest(t *testing.T, s TestSetup) { assert.Equal(t, emailTemplate.ID, updatedEmailTemplate.ID) assert.Equal(t, updatedEmailTemplate.Template, "Updated test template") assert.Equal(t, updatedEmailTemplate.Subject, "Updated subject") + assert.Equal(t, updatedEmailTemplate.Design, "Updated design") }) } From 4f1597e5d267279b7827ef8bdf8ea0bd81b34be9 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Sat, 13 Aug 2022 11:57:03 +0530 Subject: [PATCH 2/2] fix: update note on features --- .../src/components/EnvComponents/Features.tsx | 33 +++++++++++++------ dashboard/src/graphql/queries/index.ts | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/dashboard/src/components/EnvComponents/Features.tsx b/dashboard/src/components/EnvComponents/Features.tsx index 095146d..6da3028 100644 --- a/dashboard/src/components/EnvComponents/Features.tsx +++ b/dashboard/src/components/EnvComponents/Features.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Flex, Stack, Text } from '@chakra-ui/react'; +import { Divider, Flex, Stack, Text } from '@chakra-ui/react'; import InputField from '../InputField'; import { SwitchInputType } from '../../constants'; @@ -10,7 +10,7 @@ const Features = ({ variables, setVariables }: any) => { Disable Features - + Disable Login Page: @@ -83,9 +83,15 @@ const Features = ({ variables, setVariables }: any) => { /> - - - Disable Multi Factor Authentication: + + + + Disable Multi Factor Authentication (MFA): + + + Note: Enabling this will ignore Enforcing MFA shown below and will + also ignore the user MFA setting. + { - + + Enable Features - - - - Enforce Multi Factor Authentication: + + + + + Enforce Multi Factor Authentication (MFA): + + + Note: If you disable enforcing after it was enabled, it will still + keep MFA enabled for older users. +