feat: notifications (#109)

* implement notification providers

* email provider

* notification handler

* notify users

* implement code sent on user eventstore

* send email implementation

* send init code

* handle codes

* fix project member handler

* add some logs for debug

* send emails

* text changes

* send sms

* notification process

* send password code

* format phone number

* test format phone

* remove fmts

* remove unused code

* rename files

* add mocks

* merge master

* Update internal/notification/providers/email/message.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/notification/repository/eventsourcing/handler/notification.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/notification/repository/eventsourcing/handler/notification.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/notification/providers/email/provider.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* requested changes of mr

* move locker to eventstore pkg

* Update internal/notification/providers/chat/message.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* move locker to eventstore pkg

* linebreak

* Update internal/notification/providers/email/provider.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/notification/repository/eventsourcing/handler/notification.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/notification/repository/eventsourcing/handler/notification.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

Co-authored-by: Silvan <silvan.reusser@gmail.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi
2020-05-20 14:28:08 +02:00
committed by GitHub
parent c365a98cc8
commit e318139b37
67 changed files with 3278 additions and 119 deletions

View File

@@ -20,3 +20,19 @@ export ZITADEL_KEY_PATH="$BASEDIR/local_keys.yaml"
export ZITADEL_USER_VERIFICATION_KEY=UserVerificationKey_1
export ZITADEL_OTP_VERIFICATION_KEY=OTPVerificationKey_1
# Notifications
export DEBUG_MODE=TRUE
export TWILIO_SERVICE_SID=$(gopass citadel-secrets/citadel/developer/default/twilio-sid)
export TWILIO_TOKEN=$(gopass citadel-secrets/citadel/developer/default/twilio-auth-token)
export TWILIO_SENDER_NAME=CAOS AG
export SMTP_HOST=smtp.gmail.com:465
export SMTP_USER=zitadel@caos.ch
export SMTP_PASSWORD=$(gopass citadel-secrets/citadel/google/emailappkey)
export EMAIL_SENDER_ADDRESS=noreply@caos.ch
export EMAIL_SENDER_NAME=CAOS AG
export SMTP_TLS=TRUE
export CHAT_URL=$(gopass citadel-secrets/citadel/developer/default/google-chat-url | base64 -D)
# Zitadel
export ZITADEL_ACCOUNTS=http://localhost:61121

View File

@@ -9,6 +9,7 @@ import (
authz "github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/config"
"github.com/caos/zitadel/internal/notification"
tracing "github.com/caos/zitadel/internal/tracing/config"
"github.com/caos/zitadel/pkg/admin"
"github.com/caos/zitadel/pkg/auth"
@@ -18,11 +19,12 @@ import (
)
type Config struct {
Mgmt management.Config
Auth auth.Config
Login login.Config
Admin admin.Config
Console console.Config
Mgmt management.Config
Auth auth.Config
Login login.Config
Admin admin.Config
Console console.Config
Notification notification.Config
Log logging.Config
Tracing tracing.TracingConfig
@@ -38,6 +40,7 @@ func main() {
loginEnabled := flag.Bool("login", true, "enable login ui")
adminEnabled := flag.Bool("admin", true, "enable admin api")
consoleEnabled := flag.Bool("console", true, "enable console ui")
notificationEnabled := flag.Bool("notification", true, "enable notification handler")
flag.Parse()
conf := new(Config)
@@ -58,6 +61,9 @@ func main() {
if *adminEnabled {
admin.Start(ctx, conf.Admin, conf.AuthZ, conf.SystemDefaults)
}
if *notificationEnabled {
notification.Start(ctx, conf.Notification, conf.SystemDefaults)
}
if *consoleEnabled {
err = console.Start(ctx, conf.Console)
logging.Log("MAIN-3Dfuc").OnError(err).Fatal("error starting console ui")

View File

@@ -110,3 +110,33 @@ Admin:
Console:
Port: 50050
StaticDir: /app/console/dist
Notification:
Repository:
Eventstore:
ServiceName: 'Notification'
Repository:
SQL:
Host: $ZITADEL_EVENTSTORE_HOST
Port: $ZITADEL_EVENTSTORE_PORT
User: 'notification'
Database: 'eventstore'
SSLmode: disable
Cache:
Type: 'fastcache'
Config:
MaxCacheSizeInByte: 10485760 #10mb
View:
Host: $ZITADEL_EVENTSTORE_HOST
Port: $ZITADEL_EVENTSTORE_PORT
User: 'notification'
Database: 'notification'
SSLmode: disable
Spooler:
ConcurrentTasks: 4
BulkLimit: 100
FailureCountUntilSkip: 5
Handlers:
Notification:
MinimumCycleDuration: 10s

View File

@@ -109,3 +109,54 @@ SystemDefaults:
AuthMethodType: 'AUTH_TYPE_NONE'
Owners:
- 'zitadel-admin@caos.ch'
Notifications:
DebugMode: $DEBUG_MODE
Endpoints:
InitCode: '$ZITADEL_ACCOUNTS/user/init?userID={{.UserID}}&code={{.Code}}'
PasswordReset: '$ZITADEL_ACCOUNTS/password/init?userID={{.UserID}}&code={{.Code}}'
VerifyEmail: '$ZITADEL_ACCOUNTS/mail/verification?userID={{.UserID}}&code={{.Code}}'
Providers:
Chat:
Url: $CHAT_URL
SplitCount: 4000
Email:
SMTP:
Host: $SMTP_HOST
User: $SMTP_USER
Password: $SMTP_PASSWORD
From: $EMAIL_SENDER_ADDRESS
FromName: $EMAIL_SENDER_NAME
Tls: $SMTP_TLS
Twilio:
SID: $TWILIO_SERVICE_SID
Token: $TWILIO_TOKEN
From: $TWILIO_SENDER_NAME
TemplateData:
InitCode:
Title: 'Zitadel - User Initialisieren'
PreHeader: 'User Initialisieren'
Subject: 'User Initialisieren'
Greeting: 'Hallo {{.FirstName}} {{.LastName}},'
Text: 'Dieser Benutzer wurde soeben im Zitadel erstellt. Du kannst den Button unten verwenden, um die Initialisierung abzuschliesen. Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren.'
ButtonText: 'Initialisierung abschliessen'
PasswordReset:
Title: 'Zitadel - Passwort zurücksetzen'
PreHeader: 'Passwort zurücksetzen'
Subject: 'Passwort zurücksetzen'
Greeting: 'Hallo {{.FirstName}} {{.LastName}},'
Text: 'Wir haben eine Anfrage für das Zurücksetzen deines Passwortes bekommen. Du kannst den untenstehenden Button verwenden, um dein Passwort zurückzusetzen. Falls du dieses Mail nicht angefordert hast, kannst du es ignorieren.'
ButtonText: 'Passwort zurücksetzen'
VerifyEmail:
Title: 'Zitadel - Email Verifizieren'
PreHeader: 'Email verifizieren'
Subject: 'Email verifizieren'
Greeting: 'Hallo {{.FirstName}} {{.LastName}},'
Text: 'Eine neue E-Mail Adresse wurde hinzugefügt. Bitte verwende den untenstehenden Button um diese zu verifizieren. Falls du deine E-Mail Adresse nicht selber hinzugefügt hast, kannst du dieses E-Mail ignorieren.'
ButtonText: 'Passwort zurücksetzen'
VerifyPhone:
Title: 'Zitadel - Telefonnummer Verifizieren'
PreHeader: 'Telefonnummer Verifizieren'
Subject: 'Telefonnummer Verifizieren'
Greeting: 'Hallo {{.FirstName}} {{.LastName}},'
Text: 'Eine Telefonnummer wurde hinzugefügt. Bitte verifiziere diese in dem du folgenden Code eingibst: {{.Code}}'
ButtonText: 'Telefon verifizieren'

451
cmd/zitadel/template.html Normal file
View File

@@ -0,0 +1,451 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{.Title}}</title>
<style>
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap');
@font-face {
font-family: Ailerons;
src: url("https://www.caos.ch/fonts/Ailerons-Typeface.otf") format("opentype");
}
/* -------------------------------------
GLOBAL RESETS
------------------------------------- */
img {
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%;
}
body {
background: #222324 url('waves-bottomleft.png') bottom left no-repeat;
background-size: 150px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%; }
table td {
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-weight: 300;
vertical-align: top;
color: #fff;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
.body {
background: url('waves-topright.png') top right no-repeat;
background-size: 150px;
width: 100%;
}
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
display: block;
margin: 0 auto !important;
/* makes it centered */
max-width: 580px;
padding: 40px;
width: 580px;
}
/* This should also be a block element, so that it will fill 100% of the .container */
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
max-width: 580px;
padding: 10px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background: #191919;
width: 100%;
border-radius: 16px;
}
.wrapper {
box-sizing: border-box;
padding: 50px;
}
.content-block {
padding-bottom: 10px;
padding-top: 10px;
}
.footer {
clear: both;
margin-top: 10px;
text-align: center;
width: 100%;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #999999;
font-size: 12px;
text-align: center;
}
.footer a{
color: #FE00FF;
text-decoration: none;
font-size: 14px;
font-weight: 400;
}
.footer a:hover{
text-decoration: underline;
}
.apple-link{
margin-bottom: 10px;
display: inline-block;
}
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3,
h4 {
color: #fff;
font-family: 'Roboto', sans-serif;
font-weight: 300;
line-height: 1.4;
margin: 0;
margin-bottom: 30px;
}
h1 {
font-size: 35px;
font-weight: 300;
text-align: center;
text-transform: capitalize;
}
p,
ul,
ol {
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-weight: 300;
margin: 0;
margin-bottom: 15px;
}
p li,
ul li,
ol li {
list-style-position: inside;
margin-left: 5px;
}
a {
color: #0FBDA6;
text-decoration: underline;
}
/* -------------------------------------
BUTTONS
------------------------------------- */
.btn {
box-sizing: border-box;
width: 100%; }
.btn > tbody > tr > td {
padding-bottom: 15px; }
.btn table {
width: auto;
}
.btn table td {
background-color: #ffffff;
border-radius: 5px;
text-align: center;
}
.btn a {
background-color: #ffffff;
border: solid 1px #0FBDA6;
border-radius: 5px;
box-sizing: border-box;
color: #0FBDA6;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: 400;
margin: 0;
padding: 20px 60px;
text-decoration: none;
}
.btn-primary {
margin-top: 50px;
}
.btn-primary table td {
background-color: #0FBDA6;
}
.btn-primary a {
background-color: #0FBDA6;
border-color: #0FBDA6;
color: #ffffff;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.mheader {
background-color: #131313;
}
.headercell {
color: #FFFFFF;
}
.headertitle{
padding: 30px 30px 70px;
margin: 0;
font-size: 42px;
font-family: 'Ailerons', sans-serif;
text-align: center;
}
.logo{
height: 50px;
margin: 10px;
margin-left: 30px;
}
.hello {
font-size: 22px;
}
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.subject {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
hr {
border: 0;
border-bottom: 1px solid #f6f6f6;
margin: 20px 0;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
.btn-primary table td:hover {
background-color: #1ADFC5 !important;
}
.btn-primary a:hover {
background-color: #1ADFC5 !important;
border-color: #1ADFC5 !important;
}
}
</style>
</head>
<body class="">
<span class="preheader">{{.PreHeader}}</span>
<span class="subject">{{.Subject}}</span>
<table role="pheader" class="mheader" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="mheadercell">
</td>
<td class="logo">
<img class="logo" src="https://caos.ch/images/LogoCaos.png" alt="CAOS AG - Logo">
</td>
</tr>
</table>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td></td>
<td class="container">
<div class="content">
<!-- START CENTERED WHITE CONTAINER -->
<table role="pheader" class="tableheader" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="headercell">
<p class="headertitle">Zitadel</p>
</td>
</tr>
</table>
<table role="presentation" class="main">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper">
<table role="presentation" class="maincontent" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<p class="hello">{{.Greeting}}</p>
<p>{{.Text}}</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td align="center">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td> <a href="{{.URL}}" target="_blank">{{.ButtonText}}</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- END CENTERED WHITE CONTAINER -->
<!-- START FOOTER -->
<div class="footer">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="content-block">
<span class="apple-link">CAOS AG &nbsp;&nbsp; | &nbsp;&nbsp; Teufener Strasse 19 &nbsp;&nbsp; | &nbsp;&nbsp; CH-9000 St.Gallen</span>
<br> <a href="http://www.caos.ch">caos.ch</a>.
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
</body>
</html>