Skip to content

Commit

Permalink
Add branding emails showbranding emails update (#320)
Browse files Browse the repository at this point in the history
  • Loading branch information
Widcket authored Jul 2, 2021
1 parent de422e7 commit 84b051e
Show file tree
Hide file tree
Showing 7 changed files with 426 additions and 5 deletions.
1 change: 1 addition & 0 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var requiredScopes = []string{
"create:rules", "delete:rules", "read:rules", "update:rules",
"create:users", "delete:users", "read:users", "update:users",
"read:branding", "update:branding",
"read:email_templates", "update:email_templates",
"read:connections", "update:connections",
"read:client_keys", "read:logs", "read:tenant_settings",
"read:custom_domains", "create:custom_domains", "update:custom_domains", "delete:custom_domains",
Expand Down
2 changes: 2 additions & 0 deletions internal/auth0/auth0.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type API struct {
Client ClientAPI
Connection ConnectionAPI
CustomDomain CustomDomainAPI
EmailTemplate EmailTemplateAPI
Log LogAPI
LogStream LogStreamAPI
ResourceServer ResourceServerAPI
Expand All @@ -30,6 +31,7 @@ func NewAPI(m *management.Management) *API {
Branding: m.Branding,
Client: m.Client,
CustomDomain: m.CustomDomain,
EmailTemplate: m.EmailTemplate,
Log: m.Log,
LogStream: m.LogStream,
ResourceServer: m.ResourceServer,
Expand Down
24 changes: 24 additions & 0 deletions internal/auth0/email_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:generate mockgen -source=email_template.go -destination=email_template_mock.go -package=auth0

package auth0

import "gopkg.in/auth0.v5/management"

type EmailTemplateAPI interface {
// Retrieve an email template by pre-defined name.
//
// These names are `verify_email`, `reset_email`, `welcome_email`,
// `blocked_account`, `stolen_credentials`, `enrollment_email`, and
// `mfa_oob_code`.
//
// The names `change_password`, and `password_reset` are also supported for
// legacy scenarios.
//
// See: https://auth0.com/docs/api/management/v2#!/Email_Templates/get_email_templates_by_templateName
Read(template string, opts ...management.RequestOption) (e *management.EmailTemplate, err error)

// Modify an email template.
//
// See: https://auth0.com/docs/api/management/v2#!/Email_Templates/patch_email_templates_by_templateName
Update(template string, e *management.EmailTemplate, opts ...management.RequestOption) (err error)
}
2 changes: 1 addition & 1 deletion internal/cli/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ auth0 actions update <id> --n myaction -t post-login -d "lodash=4.0.0" -s "API_K
if err := actionCode.EditorPromptU(
cmd,
&inputs.Code,
actionTemplate(inputs.Trigger),
current.GetCode(),
inputs.Name+".*.js",
cli.actionEditorHint,
); err != nil {
Expand Down
23 changes: 19 additions & 4 deletions internal/cli/branding.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func brandingCmd(cli *cli) *cobra.Command {
cmd.AddCommand(updateBrandingCmd(cli))
cmd.AddCommand(templateCmd(cli))
cmd.AddCommand(customDomainsCmd(cli))
cmd.AddCommand(emailTemplateCmd(cli))
return cmd
}

Expand All @@ -100,6 +101,20 @@ func templateCmd(cli *cli) *cobra.Command {
return cmd
}

func emailTemplateCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "emails",
Short: "Manage custom email templates",
Long: "Manage custom email templates. This requires a custom email provider to be configured for the tenant.",
}

cmd.SetUsageTemplate(resourceUsageTemplate())
cmd.AddCommand(showEmailTemplateCmd(cli))
cmd.AddCommand(updateEmailTemplateCmd(cli))
return cmd
}


func showBrandingCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "show",
Expand Down Expand Up @@ -137,10 +152,10 @@ func updateBrandingCmd(cli *cli) *cobra.Command {
}

cmd := &cobra.Command{
Use: "update",
Args: cobra.NoArgs,
Short: "Update the custom branding settings for Universal Login",
Long: "Update the custom branding settings for Universal Login.",
Use: "update",
Args: cobra.NoArgs,
Short: "Update the custom branding settings for Universal Login",
Long: "Update the custom branding settings for Universal Login.",
Example: `auth0 branding update
auth0 branding update --accent '#B24592' --background '#F2DDEC'
auth0 branding update -a '#B24592' -b '#F2DDEC --logo 'https://example.com/logo.png`,
Expand Down
292 changes: 292 additions & 0 deletions internal/cli/email_templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
package cli

import (
"fmt"

"github.com/auth0/auth0-cli/internal/ansi"
"github.com/spf13/cobra"
"gopkg.in/auth0.v5"
"gopkg.in/auth0.v5/management"
)

const (
emailTemplateVerifyLink = "verify-link"
emailTemplateVerifyCode = "verify-code"
emailTemplateChangePassword = "change-password"
emailTemplateWelcome = "welcome"
emailTemplateBlockedAccount = "blocked-account"
emailTemplatePasswordBreach = "password-breach"
emailTemplateMFAEnrollment = "mfa-enrollment"
emailTemplateMFACode = "mfa-code"
emailTemplateUserInvitation = "user-invitation"
)

var (
emailTemplateTemplate = Argument{
Name: "Template",
Help: fmt.Sprintf("Template name. Can be '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' or '%s'",
emailTemplateVerifyLink,
emailTemplateVerifyCode,
emailTemplateChangePassword,
emailTemplateWelcome,
emailTemplateBlockedAccount,
emailTemplatePasswordBreach,
emailTemplateMFAEnrollment,
emailTemplateMFACode,
emailTemplateUserInvitation),
}

emailTemplateBody = Flag{
Name: "Body",
LongForm: "body",
ShortForm: "b",
Help: "Body of the email template.",
IsRequired: true,
}

emailTemplateFrom = Flag{
Name: "From",
LongForm: "from",
ShortForm: "f",
Help: "Sender's 'from' email address.",
AlwaysPrompt: true,
}

emailTemplateSubject = Flag{
Name: "Subject",
LongForm: "subject",
ShortForm: "s",
Help: "Subject line of the email.",
AlwaysPrompt: true,
}

emailTemplateEnabled = Flag{
Name: "Enabled",
LongForm: "enabled",
ShortForm: "e",
Help: "Whether the template is enabled (true) or disabled (false).",
AlwaysPrompt: true,
}

emailTemplateURL = Flag{
Name: "Result URL",
LongForm: "url",
ShortForm: "u",
Help: "URL to redirect the user to after a successful action.",
}

emailTemplateLifetime = Flag{
Name: "Result URL Lifetime",
LongForm: "lifetime",
ShortForm: "l",
Help: "Lifetime in seconds that the link within the email will be valid for.",
}

emailTemplateOptions = pickerOptions{
{"Verification Email (using Link)", emailTemplateVerifyLink},
{"Verification Email (using Code)", emailTemplateVerifyCode},
{"Change Password", emailTemplateChangePassword},
{"Welcome Email", emailTemplateWelcome, },
{"Blocked Account Email", emailTemplateBlockedAccount},
{"Password Breach Alert", emailTemplatePasswordBreach},
{"Enroll in Multifactor Authentication", emailTemplateMFAEnrollment},
{"Verification Code for Email MFA", emailTemplateMFACode},
{"User Invitation", emailTemplateUserInvitation},
}
)

func showEmailTemplateCmd(cli *cli) *cobra.Command {
var inputs struct {
Template string
}

cmd := &cobra.Command{
Use: "show",
Args: cobra.MaximumNArgs(1),
Short: "Show an email template",
Long: "Show an email template.",
Example: `auth0 branding emails show <template>
auth0 branding emails show welcome`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
err := emailTemplateTemplate.Pick(cmd, &inputs.Template, cli.emailTemplatePickerOptions)
if err != nil {
return err
}
} else {
inputs.Template = args[0]
}

var email *management.EmailTemplate

if err := ansi.Waiting(func() error {
var err error
email, err = cli.api.EmailTemplate.Read(apiEmailTemplateFor(inputs.Template))
return err
}); err != nil {
return fmt.Errorf("Unable to get the email template '%s': %w", inputs.Template, err)
}

cli.renderer.EmailTemplateShow(email)
return nil
},
}

return cmd
}

func updateEmailTemplateCmd(cli *cli) *cobra.Command {
var inputs struct {
Template string
Body string
From string
Subject string
Enabled bool
ResultURL string
ResultURLLifetime int
}

cmd := &cobra.Command{
Use: "update",
Args: cobra.MaximumNArgs(1),
Short: "Update an email template",
Long: "Update an email template.",
Example: `auth0 branding emails update <template>
auth0 branding emails update welcome`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
inputs.Template = args[0]
} else {
err := emailTemplateTemplate.Pick(cmd, &inputs.Template, cli.emailTemplatePickerOptions)
if err != nil {
return err
}
}

var current *management.EmailTemplate
err := ansi.Waiting(func() error {
var err error
current, err = cli.api.EmailTemplate.Read(apiEmailTemplateFor(inputs.Template))
return err
})
if err != nil {
return fmt.Errorf("Unable to get the email template '%s': %w", inputs.Template, err)
}

if err := emailTemplateFrom.AskU(cmd, &inputs.From, current.From); err != nil {
return err
}

if err := emailTemplateSubject.AskU(cmd, &inputs.Subject, current.Subject); err != nil {
return err
}

// TODO(cyx): we can re-think this once we have
// `--stdin` based commands. For now we don't have
// those yet, so keeping this simple.
if err := emailTemplateBody.EditorPromptU(
cmd,
&inputs.Body,
current.GetBody(),
inputs.Template+".*.liquid",
cli.emailTemplateEditorHint,
); err != nil {
return err
}
if err != nil {
return fmt.Errorf("Failed to capture input from the editor: %w", err)
}

if !ruleEnabled.IsSet(cmd) {
inputs.Enabled = auth0.BoolValue(current.Enabled)
}

if err := ruleEnabled.AskBoolU(cmd, &inputs.Enabled, current.Enabled); err != nil {
return err
}

if inputs.From == "" {
inputs.From = current.GetFrom()
}

if inputs.Subject == "" {
inputs.Subject = current.GetSubject()
}

template := apiEmailTemplateFor(inputs.Template)
// Prepare email template payload for update. This will also be
// re-hydrated by the SDK, which we'll use below during
// display.
emailTemplate := &management.EmailTemplate{
Template: &template,
Body: &inputs.Body,
From: &inputs.From,
Subject: &inputs.Subject,
Enabled: &inputs.Enabled,
}

if inputs.ResultURL == "" {
emailTemplate.ResultURL = current.ResultURL
} else {
emailTemplate.ResultURL = &inputs.ResultURL
}

if inputs.ResultURLLifetime == 0 {
emailTemplate.URLLifetimeInSecoonds = current.URLLifetimeInSecoonds
} else {
emailTemplate.URLLifetimeInSecoonds = &inputs.ResultURLLifetime
}

if err = ansi.Waiting(func() error {
return cli.api.EmailTemplate.Update(template, emailTemplate)
}); err != nil {
return err
}

cli.renderer.EmailTemplateUpdate(emailTemplate)
return nil
},
}

emailTemplateBody.RegisterStringU(cmd, &inputs.Body, "")
emailTemplateFrom.RegisterStringU(cmd, &inputs.From, "")
emailTemplateSubject.RegisterStringU(cmd, &inputs.Subject, "")
emailTemplateEnabled.RegisterBoolU(cmd, &inputs.Enabled, true)
emailTemplateURL.RegisterStringU(cmd, &inputs.ResultURL, "")
emailTemplateLifetime.RegisterIntU(cmd, &inputs.ResultURLLifetime, 0)

return cmd
}

func (c *cli) emailTemplateEditorHint() {
c.renderer.Infof("%s once you close the editor, the email template will be saved. To cancel, CTRL+C.", ansi.Faint("Hint:"))
}

func (c *cli) emailTemplatePickerOptions() (pickerOptions, error) {
return emailTemplateOptions, nil
}

func apiEmailTemplateFor(v string) string {
switch v {
case emailTemplateVerifyLink:
return "verify_email"
case emailTemplateVerifyCode:
return "verify_email_by_code"
case emailTemplateChangePassword:
return "reset_email"
case emailTemplateWelcome:
return "welcome_email"
case emailTemplateBlockedAccount:
return "blocked_account"
case emailTemplatePasswordBreach:
return "stolen_credentials"
case emailTemplateMFAEnrollment:
return "enrollment_email"
case emailTemplateMFACode:
return "mfa_oob_code"
case emailTemplateUserInvitation:
return "user_invitation"
default:
return v
}
}
Loading

0 comments on commit 84b051e

Please sign in to comment.