diff --git a/internal/auth0/branding.go b/internal/auth0/branding.go index 82fa7301f..4fd35b1a2 100644 --- a/internal/auth0/branding.go +++ b/internal/auth0/branding.go @@ -5,6 +5,7 @@ import "gopkg.in/auth0.v5/management" type BrandingAPI interface { Read(opts ...management.RequestOption) (b *management.Branding, err error) + Update(t *management.Branding, opts ...management.RequestOption) (err error) UniversalLogin(opts ...management.RequestOption) (ul *management.BrandingUniversalLogin, err error) SetUniversalLogin(ul *management.BrandingUniversalLogin, opts ...management.RequestOption) (err error) DeleteUniversalLogin(opts ...management.RequestOption) (err error) diff --git a/internal/cli/template.go b/internal/cli/branding.go similarity index 56% rename from internal/cli/template.go rename to internal/cli/branding.go index f724ddc1f..d6cb324be 100644 --- a/internal/cli/template.go +++ b/internal/cli/branding.go @@ -23,6 +23,46 @@ var ( IsRequired: true, } + brandingAccent = Flag{ + Name: "Accent Color", + LongForm: "accent", + ShortForm: "a", + Help: "Accent color.", + AlwaysPrompt: true, + } + + brandingBackground = Flag{ + Name: "Background Color", + LongForm: "background", + ShortForm: "b", + Help: "Page background color", + AlwaysPrompt: true, + } + + brandingLogo = Flag{ + Name: "Logo URL", + LongForm: "logo", + ShortForm: "l", + Help: "URL for the logo. Must use HTTPS.", + AlwaysPrompt: true, + } + + brandingFavicon = Flag{ + Name: "Favicon URL", + LongForm: "favicon", + ShortForm: "f", + Help: "URL for the favicon. Must use HTTPS.", + AlwaysPrompt: true, + } + + brandingFont = Flag{ + Name: "Custom Font URL", + LongForm: "font", + ShortForm: "c", + Help: "URL for the custom font. The URL must point to a font file and not a stylesheet. Must use HTTPS.", + AlwaysPrompt: true, + } + customTemplateOptions = pickerOptions{ {"Basic", branding.DefaultTemplate}, {"Login box + image", branding.ImageTemplate}, @@ -40,6 +80,8 @@ func brandingCmd(cli *cli) *cobra.Command { } cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(showBrandingCmd(cli)) + cmd.AddCommand(updateBrandingCmd(cli)) cmd.AddCommand(templateCmd(cli)) return cmd } @@ -57,6 +99,138 @@ func templateCmd(cli *cli) *cobra.Command { return cmd } +func showBrandingCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "show", + Args: cobra.NoArgs, + Short: "Display the custom branding settings for Universal Login", + Long: "Display the custom branding settings for Universal Login.", + Example: "auth0 branding show", + RunE: func(cmd *cobra.Command, args []string) error { + var branding *management.Branding // Load app by id + + if err := ansi.Waiting(func() error { + var err error + branding, err = cli.api.Branding.Read() + return err + }); err != nil { + return fmt.Errorf("Unable to load branding settings due to an unexpected error: %w", err) + } + + cli.renderer.BrandingShow(branding) + + return nil + }, + } + + return cmd +} + +func updateBrandingCmd(cli *cli) *cobra.Command { + var inputs struct { + AccentColor string + BackgroundColor string + LogoURL string + FaviconURL string + CustomFontURL string + } + + 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.", + Example: `auth0 branding update +auth0 branding update --accent '#B24592' --background '#F2DDEC' +auth0 branding update -a '#B24592' -b '#F2DDEC --logo 'https://example.com/logo.png`, + RunE: func(cmd *cobra.Command, args []string) error { + var current *management.Branding + + if err := ansi.Waiting(func() error { + var err error + current, err = cli.api.Branding.Read() + return err + }); err != nil { + return fmt.Errorf("Unable to load branding settings due to an unexpected error: %w", err) + } + + // Prompt for accent color + if err := brandingAccent.AskU(cmd, &inputs.AccentColor, auth0.String(current.GetColors().GetPrimary())); err != nil { + return err + } + + // Prompt for background color + if err := brandingBackground.AskU(cmd, &inputs.BackgroundColor, auth0.String(current.GetColors().GetPageBackground())); err != nil { + return err + } + + // Load updated values into a fresh branding instance + b := &management.Branding{} + + if b.Colors == nil { + b.Colors = &management.BrandingColors{} + } + + if len(inputs.AccentColor) == 0 { + b.Colors.Primary = current.GetColors().Primary + } else { + b.Colors.Primary = &inputs.AccentColor + } + + if len(inputs.BackgroundColor) == 0 { + b.Colors.PageBackground = current.GetColors().PageBackground + } else { + b.Colors.PageBackground = &inputs.BackgroundColor + } + + if len(inputs.LogoURL) == 0 { + b.LogoURL = current.LogoURL + } else { + b.LogoURL = &inputs.LogoURL + } + + if len(inputs.FaviconURL) == 0 { + b.FaviconURL = current.FaviconURL + } else { + b.FaviconURL = &inputs.FaviconURL + } + + // API2 will produce an error if we send an empty font struct + if b.Font == nil && inputs.CustomFontURL != "" { + b.Font = &management.BrandingFont{URL: &inputs.CustomFontURL} + } + + if b.Font != nil { + if len(inputs.CustomFontURL) == 0 { + b.Font.URL = current.Font.URL + } else { + b.Font.URL = &inputs.CustomFontURL + } + } + + // Update branding + if err := ansi.Waiting(func() error { + return cli.api.Branding.Update(b) + }); err != nil { + return fmt.Errorf("Unable to update branding settings: %v", err) + } + + // Render result + cli.renderer.BrandingUpdate(b) + + return nil + }, + } + + brandingAccent.RegisterStringU(cmd, &inputs.AccentColor, "") + brandingBackground.RegisterStringU(cmd, &inputs.BackgroundColor, "") + brandingLogo.RegisterStringU(cmd, &inputs.LogoURL, "") + brandingFavicon.RegisterStringU(cmd, &inputs.FaviconURL, "") + brandingFont.RegisterStringU(cmd, &inputs.CustomFontURL, "") + + return cmd +} + func showBrandingTemplateCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "show", @@ -65,11 +239,17 @@ func showBrandingTemplateCmd(cli *cli) *cobra.Command { Long: "Display the custom template for Universal Login.", Example: "auth0 branding templates show", RunE: func(cmd *cobra.Command, args []string) error { - template, err := cli.api.Branding.UniversalLogin() - if err != nil { - return fmt.Errorf("Unable to load tenants due to an unexpected error: %w", err) + var template *management.BrandingUniversalLogin // Load app by id + + if err := ansi.Waiting(func() error { + var err error + template, err = cli.api.Branding.UniversalLogin() + return err + }); err != nil { + return fmt.Errorf("Unable to load the Universal Login template due to an unexpected error: %w", err) } + cli.renderer.Heading("template") fmt.Println(*template.Body) return nil diff --git a/internal/display/branding.go b/internal/display/branding.go new file mode 100644 index 000000000..bde84ed2d --- /dev/null +++ b/internal/display/branding.go @@ -0,0 +1,51 @@ +package display + +import ( + "gopkg.in/auth0.v5/management" +) + +type brandingView struct { + AccentColor string + BackgroundColor string + LogoURL string + FaviconURL string + CustomFontURL string +} + +func (v *brandingView) AsTableHeader() []string { + return []string{} +} + +func (v *brandingView) AsTableRow() []string { + return []string{} +} + +func (v *brandingView) KeyValues() [][]string { + return [][]string{ + {"ACCENT COLOR", v.AccentColor}, + {"BACKGROUND COLOR", v.BackgroundColor}, + {"LOGO URL", v.LogoURL}, + {"FAVICON URL", v.FaviconURL}, + {"CUSTOM FONT URL", v.CustomFontURL}, + } +} + +func (r *Renderer) BrandingShow(data *management.Branding) { + r.Heading("branding") + r.Result(makeBrandingView(data)) +} + +func (r *Renderer) BrandingUpdate(data *management.Branding) { + r.Heading("branding updated") + r.Result(makeBrandingView(data)) +} + +func makeBrandingView(data *management.Branding) *brandingView { + return &brandingView{ + AccentColor: data.GetColors().GetPrimary(), + BackgroundColor: data.GetColors().GetPageBackground(), + LogoURL: data.GetLogoURL(), + FaviconURL: data.GetFaviconURL(), + CustomFontURL: data.GetFont().GetURL(), + } +}