-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
IAM 978 User invite - send email for identity creation with recovery code and link
- Loading branch information
Showing
17 changed files
with
580 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<!-- Copyright 2024 Canonical Ltd. --> | ||
<!-- SPDX-License-Identifier: AGPL-3.0 --> | ||
|
||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta content="width=device-width, initial-scale=1.0" name="viewport"> | ||
<title>Verify Your Account</title> | ||
<style> | ||
body { | ||
font-family: Arial, sans-serif; | ||
background-color: #f6f6f6; | ||
color: #333; | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
.container { | ||
width: 100%; | ||
max-width: 600px; | ||
margin: 20px auto; | ||
background-color: #ffffff; | ||
padding: 20px; | ||
border-radius: 8px; | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | ||
} | ||
|
||
.header { | ||
text-align: center; | ||
padding: 20px 0; | ||
} | ||
|
||
.header h1 { | ||
color: #007BFF; | ||
margin: 0; | ||
} | ||
|
||
.content { | ||
text-align: center; | ||
} | ||
|
||
.content p { | ||
font-size: 16px; | ||
line-height: 1.5; | ||
margin: 20px 0; | ||
} | ||
|
||
.verify-button { | ||
display: inline-block; | ||
background-color: #007BFF; | ||
color: #ffffff; | ||
text-decoration: none; | ||
padding: 10px 20px; | ||
border-radius: 5px; | ||
font-size: 18px; | ||
margin-top: 20px; | ||
} | ||
|
||
.footer { | ||
text-align: center; | ||
font-size: 12px; | ||
color: #666; | ||
margin-top: 20px; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<div class="header"> | ||
<h1>Verify Your Account</h1> | ||
</div> | ||
<div class="content"> | ||
<p>Hello,</p> | ||
<p>Your account with the email address <strong>{{ .Email }}</strong> was recently created. To complete your | ||
registration, click the button below:</p> | ||
<p>Verification code <span><strong>{{ .RecoveryCode }}</strong></span></p> | ||
<a class="verify-button" href="{{ .InviteUrl }}">Verify Account</a> | ||
</div> | ||
<div class="footer"> | ||
<p>©Copyright 2024 Canonical Ltd.</p> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright 2024 Canonical Ltd. | ||
// SPDX-License-Identifier: AGPL-3.0 | ||
|
||
package mail | ||
|
||
import ( | ||
"context" | ||
"html/template" | ||
|
||
mail2 "github.com/wneessen/go-mail" | ||
) | ||
|
||
type EmailServiceInterface interface { | ||
Send(context.Context, string, string, *template.Template, any) error | ||
} | ||
|
||
type MailClientInterface interface { | ||
DialAndSendWithContext(context.Context, ...*mail2.Msg) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright 2024 Canonical Ltd. | ||
// SPDX-License-Identifier: AGPL-3.0 | ||
|
||
package mail | ||
|
||
import ( | ||
"context" | ||
"html/template" | ||
"time" | ||
|
||
"github.com/wneessen/go-mail" | ||
"go.opentelemetry.io/otel/trace" | ||
|
||
"github.com/canonical/identity-platform-admin-ui/internal/logging" | ||
"github.com/canonical/identity-platform-admin-ui/internal/monitoring" | ||
) | ||
|
||
type Config struct { | ||
Host string `validate:"required"` | ||
Port int `validate:"required"` | ||
Username string | ||
Password string | ||
FromAddress string `validate:"required"` | ||
SendTimeout time.Duration | ||
} | ||
|
||
func NewConfig(host string, port int, username, password, from string, sendTimeout int) *Config { | ||
c := new(Config) | ||
|
||
c.Host = host | ||
c.Port = port | ||
c.Username = username | ||
c.Password = password | ||
c.FromAddress = from | ||
c.SendTimeout = time.Duration(sendTimeout) * time.Second | ||
|
||
return c | ||
} | ||
|
||
type EmailService struct { | ||
from string | ||
client MailClientInterface | ||
|
||
tracer trace.Tracer | ||
monitor monitoring.MonitorInterface | ||
logger logging.LoggerInterface | ||
} | ||
|
||
func (e *EmailService) Send(ctx context.Context, to, subject string, template *template.Template, templateArgs any) error { | ||
ctx, span := e.tracer.Start(ctx, "mail.EmailService.Send") | ||
defer span.End() | ||
|
||
msg := mail.NewMsg() | ||
|
||
if err := msg.From(e.from); err != nil { | ||
return err | ||
} | ||
|
||
if err := msg.SetBodyHTMLTemplate(template, templateArgs); err != nil { | ||
return err | ||
} | ||
|
||
if err := msg.To(to); err != nil { | ||
return err | ||
} | ||
|
||
msg.Subject(subject) | ||
|
||
return e.client.DialAndSendWithContext(ctx, msg) | ||
} | ||
|
||
func NewEmailService(config *Config, tracer trace.Tracer, monitor monitoring.MonitorInterface, logger logging.LoggerInterface) *EmailService { | ||
s := new(EmailService) | ||
s.from = config.FromAddress | ||
|
||
var err error | ||
mailOpts := []mail.Option{ | ||
mail.WithPort(config.Port), | ||
mail.WithTLSPolicy(mail.TLSOpportunistic), | ||
mail.WithTimeout(config.SendTimeout), | ||
} | ||
|
||
// treat smtp connection as authenticated only if username is passed | ||
if config.Username != "" { | ||
mailOpts = append( | ||
mailOpts, | ||
[]mail.Option{mail.WithSMTPAuth(mail.SMTPAuthPlain), mail.WithUsername(config.Username), mail.WithPassword(config.Password)}..., | ||
) | ||
} | ||
|
||
s.client, err = mail.NewClient( | ||
config.Host, | ||
mailOpts..., | ||
) | ||
|
||
if err != nil { | ||
logger.Fatalf("failed to create email client: %s", err) | ||
} | ||
|
||
s.monitor = monitor | ||
s.tracer = tracer | ||
s.logger = logger | ||
|
||
return s | ||
} |
Oops, something went wrong.