Skip to content

Commit

Permalink
HTML email using templates #1258 (#1690)
Browse files Browse the repository at this point in the history
  • Loading branch information
ramanan-ravi authored Oct 26, 2023
1 parent ba1cca0 commit 6d565c2
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ deepfence_ctl/vendor

go.work*

pkg/
./pkg/
deepfence_agent/tools/apache/scope/docker/deepfenceutil
deepfence_agent/tools/apache/scope/docker/deepfence_exe
deepfence_agent/tools/apache/scope/docker/deepfence
Expand Down
42 changes: 38 additions & 4 deletions deepfence_server/handler/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,14 +349,31 @@ func (h *Handler) InviteUser(w http.ResponseWriter, r *http.Request) {
h.respondError(err, w)
return
}

htmlEmail, err := sendemail.RenderEmailTemplate(
sendemail.UserInviteTemplate,
sendemail.UserInvite{
Project: utils.Project,
Username: "",
RequestedBy: fmt.Sprintf("%s %s (%s)", user.FirstName, user.LastName, user.Email),
InviteLink: inviteURL,
},
)
if err != nil {
log.Error().Err(err).Msg("error rendering UserInviteTemplate")
h.respondError(err, w)
return
}

err = emailSender.Send(
[]string{inviteUserRequest.Email},
"Deepfence - Invitation to join ThreatMapper",
fmt.Sprintf(sendemail.UserInviteEmail, user.FirstName, user.LastName, user.Email, inviteURL),
fmt.Sprintf(sendemail.UserInviteEmailSubject, utils.Project),
"",
htmlEmail,
nil,
)
if err != nil {
log.Error().Err(err).Msg("error sending user invite email")
h.respondError(err, w)
return
}
Expand Down Expand Up @@ -665,15 +682,32 @@ func (h *Handler) ResetPasswordRequest(w http.ResponseWriter, r *http.Request) {
return
}
resetPasswordURL := fmt.Sprintf("%s/auth/reset-password?code=%s&namespace=%s", consoleUrl, resetCode, user.CompanyNamespace)

htmlEmail, err := sendemail.RenderEmailTemplate(
sendemail.PasswordResetTemplate,
sendemail.PasswordReset{
Project: utils.Project,
Username: user.FirstName + " " + user.LastName,
InviteLink: resetPasswordURL,
},
)
if err != nil {
pgClient.DeletePasswordResetByUserEmail(ctx, user.Email)
log.Error().Err(err).Msg("error rendering PasswordResetTemplate")
h.respondError(err, w)
return
}

err = emailSender.Send(
[]string{resetPasswordRequest.Email},
"Deepfence - Password Reset",
fmt.Sprintf(sendemail.PasswordResetEmail, user.FirstName, user.LastName, resetPasswordURL, 10),
sendemail.PasswordResetEmailSubject,
"",
htmlEmail,
nil,
)
if err != nil {
pgClient.DeletePasswordResetByUserEmail(ctx, user.Email)
log.Error().Err(err).Msg("error sending password reset email")
h.respondError(err, w)
return
}
Expand Down
49 changes: 47 additions & 2 deletions deepfence_server/pkg/sendemail/templates.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,52 @@
package sendemail

import (
"bytes"
"embed"
_ "embed"
"html/template"

"github.com/Masterminds/sprig/v3"
)

type EmailTemplateType string

const (
PasswordResetTemplate EmailTemplateType = "reset-password.html"
UserInviteTemplate EmailTemplateType = "invite-user.html"
)

const (
PasswordResetEmailSubject = "Deepfence - Reset Password"
UserInviteEmailSubject = "Deepfence - Invitation to join %s"
)

var (
PasswordResetEmail = "Hello %s %s, \n\nPlease click here to reset your Deepfence ThreatMapper password: %s (Link valid for %d minutes)\n"
//go:embed templates/*.html
emailTemplatesContent embed.FS

UserInviteEmail = "Hello, \n\nYou have been invited to join Deepfence ThreatMapper by %s %s (%s). \nPlease click here to register: %s\n"
EmailTemplates = template.Must(
template.New("").Funcs(sprig.FuncMap()).ParseFS(emailTemplatesContent, []string{"templates/*.html"}...))
)

type PasswordReset struct {
Project string
Username string
InviteLink string
}

type UserInvite struct {
Project string
Username string
RequestedBy string
InviteLink string
}

func RenderEmailTemplate(emailTemplateType EmailTemplateType, data interface{}) (string, error) {
var rendered bytes.Buffer
err := EmailTemplates.ExecuteTemplate(&rendered, string(emailTemplateType), data)
if err != nil {
return "", err
}
return rendered.String(), nil
}
84 changes: 84 additions & 0 deletions deepfence_server/pkg/sendemail/templates/invite-user.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head data-id="__react-email-head">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
@font-face {
font-family: 'Arial';
font-style: normal;
font-weight: 400;
mso-font-alt: 'Helvetica';

}

* {
font-family: 'Arial', Helvetica;
}
</style>
</head>
<div id="__react-email-preview" style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">
Join Deepfence {{.Project}}
<div>
 ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏
</div>
</div>
<body data-id="__react-email-body" style="background-color:rgb(255,255,255)">
<table align="center" width="100%" data-id="__react-email-container" role="presentation" cellSpacing="0" cellPadding="0"
border="0" style="max-width:37.5em;margin-top:40px;margin-bottom:40px;padding:20px">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" data-id="react-email-section" border="0" cellPadding="0" cellSpacing="0"
role="presentation" style="margin-top:32px">
<tbody>
<tr>
<td>
<table align="center" width="100%" data-id="react-email-row" role="presentation" cellSpacing="0"
cellPadding="0" border="0">
<tbody style="width:100%">
<tr style="width:100%">
<td align="right" data-id="__react-email-column" style="width:44%"><img
data-id="react-email-img" alt="Deepfence Logo"
src="https://deepfence-public.s3.amazonaws.com/deepfence-logo-58-38.png"
style="display:block;outline:none;border:none;text-decoration:none"/></td>
<td align="left" data-id="__react-email-column"><p data-id="react-email-text"
style="font-size:22px;line-height:1;margin:16px 0;color:rgb(0,0,0);font-weight:400">
Deepfence</p></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p data-id="react-email-text"
style="font-size:14px;line-height:24px;margin:16px 0;color:rgb(0,0,0);font-weight:400">Hello
{{.Username}},</p>
<p data-id="react-email-text"
style="font-size:14px;line-height:24px;margin:16px 0;color:rgb(0,0,0);font-weight:400">You have been
invited to join Deepfence <strong>{{.Project}}</strong> by {{.RequestedBy}} (<a
href="mailto:{{.RequestedByEmail}}" data-id="react-email-link" target="_blank"
style="color:rgb(28,100,242);text-decoration:none;text-decoration-line:none">{{.RequestedByEmail}}</a>).
</p>
<table align="center" width="100%" data-id="react-email-section" border="0" cellPadding="0" cellSpacing="0"
role="presentation" style="text-align:center;margin-top:24px;margin-bottom:24px">
<tbody>
<tr>
<td><a href="{{.InviteLink}}" data-id="react-email-button" target="_blank"
style="line-height:16px;text-decoration:none;display:inline-block;max-width:100%;padding:0px 0px;background-color:rgb(12,167,255);padding-left:0.75rem;padding-right:0.75rem;padding-top:0.625rem;padding-bottom:0.625rem;font-size:11px;letter-spacing:0.1em;font-weight:800;color:rgb(255,255,255);border-radius:0.25rem;text-transform:uppercase"><span></span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:0">Join the team</span><span></span></a>
</td>
</tr>
</tbody>
</table>
<p data-id="react-email-text"
style="font-size:14px;line-height:24px;margin:0px;color:rgb(0,0,0);font-weight:400">or copy and paste
this URL into your browser: </p><a href="{{.InviteLink}}" data-id="react-email-link" target="_blank"
style="color:rgb(28,100,242);text-decoration:none;font-size:14px;line-height:24px;font-weight:400;text-decoration-line:none">{{.InviteLink}}</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
82 changes: 82 additions & 0 deletions deepfence_server/pkg/sendemail/templates/reset-password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head data-id="__react-email-head">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
@font-face {
font-family: 'Arial';
font-style: normal;
font-weight: 400;
mso-font-alt: 'Helvetica';

}

* {
font-family: 'Arial', Helvetica;
}
</style>
</head>
<div id="__react-email-preview" style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">
Password reset - Deepfence {{.Project}}
<div>
 ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏
</div>
</div>
<body data-id="__react-email-body" style="background-color:rgb(255,255,255)">
<table align="center" width="100%" data-id="__react-email-container" role="presentation" cellSpacing="0" cellPadding="0"
border="0"
style="max-width:37.5em;margin-top:40px;margin-bottom:40px;margin-left:auto;margin-right:auto;padding:20px">
<tbody>
<tr style="width:100%">
<td>
<table align="center" width="100%" data-id="react-email-section" border="0" cellPadding="0" cellSpacing="0"
role="presentation" style="margin-top:32px">
<tbody>
<tr>
<td>
<table align="center" width="100%" data-id="react-email-row" role="presentation" cellSpacing="0"
cellPadding="0" border="0">
<tbody style="width:100%">
<tr style="width:100%">
<td align="right" data-id="__react-email-column" style="width:44%"><img
data-id="react-email-img" alt="Deepfence Logo"
src="https://deepfence-public.s3.amazonaws.com/deepfence-logo-58-38.png"
style="display:block;outline:none;border:none;text-decoration:none"/></td>
<td align="left" data-id="__react-email-column"><p data-id="react-email-text"
style="font-size:22px;line-height:1;margin:16px 0;color:rgb(0,0,0);font-weight:400">
Deepfence</p></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p data-id="react-email-text"
style="font-size:14px;line-height:24px;margin:16px 0;color:rgb(0,0,0);font-weight:400">Hello
{{.Username}},</p>
<p data-id="react-email-text"
style="font-size:14px;line-height:24px;margin:16px 0;color:rgb(0,0,0);font-weight:400">If you wish to
reset your Deepfence <strong>{{.Project}}</strong> password, click the button below:</p>
<table align="center" width="100%" data-id="react-email-section" border="0" cellPadding="0" cellSpacing="0"
role="presentation" style="text-align:center;margin-top:24px;margin-bottom:24px">
<tbody>
<tr>
<td><a href="{{.InviteLink}}" data-id="react-email-button" target="_blank"
style="line-height:16px;text-decoration:none;display:inline-block;max-width:100%;padding:0px 0px;background-color:rgb(12,167,255);padding-left:0.75rem;padding-right:0.75rem;padding-top:0.625rem;padding-bottom:0.625rem;font-size:11px;letter-spacing:0.1em;font-weight:800;color:rgb(255,255,255);border-radius:0.25rem;text-transform:uppercase"><span></span><span
style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:0">Reset password</span><span></span></a>
</td>
</tr>
</tbody>
</table>
<p data-id="react-email-text"
style="font-size:14px;line-height:24px;margin:0px;color:rgb(0,0,0);font-weight:400">or copy and paste
this URL into your browser: </p><a href="{{.InviteLink}}" data-id="react-email-link" target="_blank"
style="color:rgb(28,100,242);text-decoration:none;font-size:14px;line-height:24px;font-weight:400;text-decoration-line:none">{{.InviteLink}}</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
4 changes: 4 additions & 0 deletions deepfence_utils/utils/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const (
ErrorUserNotFound = "user not found"
)

const (
Project = "ThreatMapper"
)

// kafka topics
const (
AUDIT_LOGS = "audit-logs"
Expand Down

0 comments on commit 6d565c2

Please sign in to comment.