Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email settings UI #293

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
df10417
User management
armisss4 Dec 29, 2022
4b5057e
Security fixes
armisss4 Dec 29, 2022
4cc79da
Merge pull request #1 from armisss4/develop
armisss4 Dec 29, 2022
93731c3
User control patch
armisss4 Dec 30, 2022
f8a1041
Merge pull request #2 from armisss4/User-control-patch
armisss4 Dec 30, 2022
c31636b
Initial email settings UI commit
armisss4 Dec 30, 2022
e9b628c
Minor fixes
armisss4 Dec 30, 2022
09a26b3
Qr code filename fix for downloading
armisss4 Dec 30, 2022
83bbdef
Merge pull request #3 from armisss4/QR-code-filename-fix
armisss4 Dec 30, 2022
cba30b2
Revert "Qr code filename fix for downloading"
armisss4 Jan 4, 2023
6eeaf09
Merge pull request #4 from armisss4/revert-3-QR-code-filename-fix
armisss4 Jan 4, 2023
b6eb046
Revert "User control patch"
armisss4 Jan 4, 2023
b06eb96
Merge pull request #5 from armisss4/revert-2-User-control-patch
armisss4 Jan 4, 2023
43148ce
Revert "Merge from development branch"
armisss4 Jan 4, 2023
fdb36e4
Merge pull request #6 from armisss4/revert-1-develop
armisss4 Jan 4, 2023
10fc890
Merge branch 'email-settings-UI' into email-settings-UI-old
armisss4 Jan 4, 2023
60cc154
Merge pull request #8 from armisss4/email-settings-UI-old
armisss4 Jan 4, 2023
92e3964
Update routes.go
armisss4 Jan 4, 2023
0722c0f
Merge branch 'master' into email-settings-UI
armisss4 Mar 7, 2023
571e34e
Merge branch 'master' into email-settings-UI
armisss4 Mar 15, 2023
d1b0239
Update router.go
armisss4 Mar 15, 2023
070da88
Update base.html
armisss4 Mar 15, 2023
154ca17
Update routes.go
armisss4 Mar 15, 2023
e2d767e
Merge branch 'master' into email-settings-UI
armisss4 May 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 70 additions & 3 deletions handler/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ func NewClient(db store.IStore) echo.HandlerFunc {
}

// EmailClient handler to send the configuration via email
func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailContent string) echo.HandlerFunc {
func EmailClient(db store.IStore) echo.HandlerFunc {
type clientIdEmailPayload struct {
ID string `json:"id"`
Email string `json:"email"`
Expand All @@ -499,6 +499,15 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
}

// init mailer
var mailer emailer.Emailer
emailSetting, _ := db.GetEmailSettings()
if emailSetting.SendgridApiKey != "" {
mailer = emailer.NewSendgridApiMail(emailSetting.SendgridApiKey, emailSetting.EmailFromName, emailSetting.EmailFrom)
} else {
mailer = emailer.NewSmtpMail(emailSetting.SmtpHostname, emailSetting.SmtpPort, emailSetting.SmtpUsername, emailSetting.SmtpPassword, emailSetting.SmtpNoTLSCheck, emailSetting.SmtpAuthType, emailSetting.EmailFromName, emailSetting.EmailFrom, emailSetting.SmtpEncryption)
}

// build config
server, _ := db.GetServer()
globalSettings, _ := db.GetGlobalSettings()
Expand All @@ -519,8 +528,8 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
err = mailer.Send(
clientData.Client.Name,
payload.Email,
emailSubject,
emailContent,
emailSetting.DefaultEmailSubject,
emailSetting.DefaultEmailContent,
attachments,
)

Expand Down Expand Up @@ -758,6 +767,64 @@ func GlobalSettings(db store.IStore) echo.HandlerFunc {
}
}

// EmailSettings handler
func EmailSettings(db store.IStore) echo.HandlerFunc {
return func(c echo.Context) error {

emailSettings, err := db.GetEmailSettings()
if err != nil {
log.Error("Cannot get email settings: ", err)
}

return c.Render(http.StatusOK, "email_settings.html", map[string]interface{}{
"baseData": model.BaseData{Active: "email-settings", CurrentUser: currentUser(c), Admin: isAdmin(c)},
"emailSettings": emailSettings,
})
}
}

// EmailSettingsSubmit handler to update the email settings
func EmailSettingsSubmit(db store.IStore) echo.HandlerFunc {
return func(c echo.Context) error {
data := make(map[string]interface{})
err := json.NewDecoder(c.Request().Body).Decode(&data)

if err != nil {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Bad post data"})
}

emailSetting, err := db.GetEmailSettings()
if err != nil {
log.Error("Cannot get email settings: ", err)
}

if len(data) == 2 {
emailSetting.DefaultEmailSubject = data["default_email_subject"].(string)
emailSetting.DefaultEmailContent = data["default_email_content"].(string)
} else {
emailSetting.SendgridApiKey = data["sendgrid_api_key"].(string)
emailSetting.EmailFromName = data["email_from_name"].(string)
emailSetting.EmailFrom = data["email_from"].(string)
emailSetting.SmtpHostname = data["smtp_hostname"].(string)
emailSetting.SmtpPort = int(data["smtp_port"].(float64))
emailSetting.SmtpUsername = data["smtp_username"].(string)
emailSetting.SmtpPassword = data["smtp_password"].(string)
emailSetting.SmtpNoTLSCheck = data["smtp_no_tls_check"].(bool)
emailSetting.SmtpAuthType = data["smtp_auth_type"].(string)
emailSetting.SmtpEncryption = data["smtp_encryption"].(string)
}

// write config to the database
if err := db.SaveEmailSettings(emailSetting); err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot update email settings"})
}

log.Infof("Updated email settings: %v", emailSetting)

return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated email settings successfully"})
}
}

// Status handler
func Status(db store.IStore) echo.HandlerFunc {
type PeerVM struct {
Expand Down
49 changes: 34 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ var (
flagBasePath string
)

const (
defaultEmailSubject = "Your wireguard configuration"
defaultEmailContent = `Hi,</br>
<p>In this email you can find your personal configuration for our wireguard server.</p>

<p>Best</p>
`
)

// embed the "templates" directory
//
Expand All @@ -65,6 +57,8 @@ var embeddedAssets embed.FS
func init() {

// command-line flags and env variables
// TODO move ENV_VAR variables and default values to config.go

flag.BoolVar(&flagDisableLogin, "disable-login", util.LookupEnvOrBool("DISABLE_LOGIN", flagDisableLogin), "Disable authentication on the app. This is potentially dangerous.")
flag.StringVar(&flagBindAddress, "bind-address", util.LookupEnvOrString("BIND_ADDRESS", flagBindAddress), "Address:Port to which the app will be bound.")
flag.StringVar(&flagSmtpHostname, "smtp-hostname", util.LookupEnvOrString("SMTP_HOSTNAME", flagSmtpHostname), "SMTP Hostname")
Expand Down Expand Up @@ -140,6 +134,34 @@ func main() {
// create the wireguard config on start, if it doesn't exist
initServerConfig(db, tmplDir)

// check and update email settings if necessary
if util.SendgridApiKey != "" {
emailSetting, _ := db.GetEmailSettings()
emailSetting.SendgridApiKey = util.SendgridApiKey
emailSetting.EmailFromName = util.EmailFromName
emailSetting.EmailFrom = util.EmailFrom
err := db.SaveEmailSettings(emailSetting)
if err != nil {
panic(err)
}
}
if util.SmtpUsername != "" {
emailSetting, _ := db.GetEmailSettings()
emailSetting.EmailFromName = util.EmailFromName
emailSetting.EmailFrom = util.EmailFrom
emailSetting.SmtpHostname = util.SmtpHostname
emailSetting.SmtpPort = util.SmtpPort
emailSetting.SmtpUsername = util.SmtpUsername
emailSetting.SmtpPassword = util.SmtpPassword
emailSetting.SmtpNoTLSCheck = util.SmtpNoTLSCheck
emailSetting.SmtpAuthType = util.SmtpAuthType
emailSetting.SmtpEncryption = util.SmtpEncryption
err := db.SaveEmailSettings(emailSetting)
if err != nil {
panic(err)
}
}

// register routes
app := router.New(tmplDir, extraData, util.SessionSecret)

Expand All @@ -158,20 +180,14 @@ func main() {
app.GET(util.BasePath+"/api/user/:username", handler.GetUser(db), handler.ValidSession)
}

var sendmail emailer.Emailer
if util.SendgridApiKey != "" {
sendmail = emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom)
} else {
sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom, util.SmtpEncryption)
}

app.GET(util.BasePath+"/test-hash", handler.GetHashesChanges(db), handler.ValidSession)
app.GET(util.BasePath+"/about", handler.AboutPage())
app.GET(util.BasePath+"/_health", handler.Health())
app.GET(util.BasePath+"/favicon", handler.Favicon())
app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/email-client", handler.EmailClient(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/client/set-status", handler.SetClientStatus(db), handler.ValidSession, handler.ContentTypeJson)
app.POST(util.BasePath+"/remove-client", handler.RemoveClient(db), handler.ValidSession, handler.ContentTypeJson)
app.GET(util.BasePath+"/download", handler.DownloadClient(db), handler.ValidSession)
Expand All @@ -180,6 +196,9 @@ func main() {
app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.NeedsAdmin)
app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
app.GET(util.BasePath+"/email-settings", handler.EmailSettings(db), handler.ValidSession, handler.NeedsAdmin)
app.POST(util.BasePath+"/email-settings", handler.EmailSettingsSubmit(db), handler.ValidSession, handler.NeedsAdmin)

app.GET(util.BasePath+"/status", handler.Status(db), handler.ValidSession)
app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession)
app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession)
Expand Down
15 changes: 15 additions & 0 deletions model/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,18 @@ type GlobalSetting struct {
ConfigFilePath string `json:"config_file_path"`
UpdatedAt time.Time `json:"updated_at"`
}

type EmailSetting struct {
SendgridApiKey string `json:"sendgrid_api_key"`
EmailFromName string `json:"email_from_name"`
EmailFrom string `json:"email_from"`
SmtpHostname string `json:"smtp_hostname"`
SmtpPort int `json:"smtp_port"`
SmtpUsername string `json:"smtp_username"`
SmtpPassword string `json:"smtp_password"`
SmtpNoTLSCheck bool `json:"smtp_no_tls_check"`
SmtpAuthType string `json:"smtp_auth_type"`
SmtpEncryption string `json:"smtp_encryption"`
DefaultEmailSubject string `json:"default_email_subject"`
DefaultEmailContent string `json:"default_email_content"`
}
7 changes: 7 additions & 0 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ func New(tmplDir fs.FS, extraData map[string]string, secret []byte) *echo.Echo {
log.Fatal(err)
}


tmplEmailSettingsString, err := util.StringFromEmbedFile("email_settings.html")
if err != nil {
log.Fatal(err)
}

tmplUsersSettingsString, err := util.StringFromEmbedFile(tmplDir, "users_settings.html")
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -113,6 +119,7 @@ func New(tmplDir fs.FS, extraData map[string]string, secret []byte) *echo.Echo {
templates["clients.html"] = template.Must(template.New("clients").Funcs(funcs).Parse(tmplBaseString + tmplClientsString))
templates["server.html"] = template.Must(template.New("server").Funcs(funcs).Parse(tmplBaseString + tmplServerString))
templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString))
templates["email_settings.html"] = template.Must(template.New("email_settings").Funcs(funcs).Parse(tmplBaseString + tmplEmailSettingsString))
templates["users_settings.html"] = template.Must(template.New("users_settings").Funcs(funcs).Parse(tmplBaseString + tmplUsersSettingsString))
templates["status.html"] = template.Must(template.New("status").Funcs(funcs).Parse(tmplBaseString + tmplStatusString))
templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString))
Expand Down
32 changes: 32 additions & 0 deletions store/jsondb/jsondb.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (o *JsonDB) Init() error {
var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
var globalSettingPath string = path.Join(serverPath, "global_settings.json")
var emailSettingPath string = path.Join(serverPath, "email_settings.json")
var hashesPath string = path.Join(serverPath, "hashes.json")
var userPath string = path.Join(serverPath, "users.json")

Expand Down Expand Up @@ -115,6 +116,27 @@ func (o *JsonDB) Init() error {
o.conn.Write("server", "hashes", clientServerHashes)
}

// email settings
// TODO move ENV_VAR variables and default values to config.go
if _, err := os.Stat(emailSettingPath); os.IsNotExist(err) {
emailSetting := new(model.EmailSetting)
emailSetting.SendgridApiKey = util.LookupEnvOrString("SENDGRID_API_KEY", "")
emailSetting.EmailFromName = util.LookupEnvOrString("EMAIL_FROM_NAME", "WireGuard UI")
emailSetting.EmailFrom = util.LookupEnvOrString("EMAIL_FROM_ADDRESS", "")
emailSetting.SmtpHostname = util.LookupEnvOrString("SMTP_HOSTNAME", "127.0.0.1")
emailSetting.SmtpPort = util.LookupEnvOrInt("SMTP_PORT", 25)
emailSetting.SmtpUsername = util.LookupEnvOrString("SMTP_USERNAME", "")
emailSetting.SmtpPassword = util.LookupEnvOrString("SMTP_PASSWORD", "")
emailSetting.SmtpNoTLSCheck = util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", false)
emailSetting.SmtpAuthType = util.LookupEnvOrString("SMTP_AUTH_TYPE", "NONE")
emailSetting.SmtpEncryption = util.LookupEnvOrString("SMTP_ENCRYPTION", "STARTTLS")
emailSetting.DefaultEmailSubject = "Your wireguard configuration"
emailSetting.DefaultEmailContent = `Hi,</br>
<p>In this email you can find your personal configuration for our wireguard server.</p>
<p>Best</p>`
o.conn.Write("server", "email_settings", emailSetting)
}

// user info
results, err := o.conn.ReadAll("users")
if err != nil || len(results) < 1 {
Expand Down Expand Up @@ -303,6 +325,16 @@ func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
return o.conn.Write("server", "global_settings", globalSettings)
}

// GetEmailSettings func to query email settings from the database
func (o *JsonDB) GetEmailSettings() (model.EmailSetting, error) {
settings := model.EmailSetting{}
return settings, o.conn.Read("server", "email_settings", &settings)
}

func (o *JsonDB) SaveEmailSettings(emailSettings model.EmailSetting) error {
return o.conn.Write("server", "email_settings", emailSettings)
}

func (o *JsonDB) GetPath() string {
return o.dbPath
}
Expand Down
2 changes: 2 additions & 0 deletions store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type IStore interface {
SaveUser(user model.User) error
DeleteUser(username string) error
GetGlobalSettings() (model.GlobalSetting, error)
GetEmailSettings() (model.EmailSetting, error)
GetServer() (model.Server, error)
GetClients(hasQRCode bool) ([]model.ClientData, error)
GetClientByID(clientID string, qrCode model.QRCodeSettings) (model.ClientData, error)
Expand All @@ -19,6 +20,7 @@ type IStore interface {
SaveServerInterface(serverInterface model.ServerInterface) error
SaveServerKeyPair(serverKeyPair model.ServerKeypair) error
SaveGlobalSettings(globalSettings model.GlobalSetting) error
SaveEmailSettings(emailSettings model.EmailSetting) error
GetWakeOnLanHosts() ([]model.WakeOnLanHost, error)
GetWakeOnLanHost(macAddress string) (*model.WakeOnLanHost, error)
DeleteWakeOnHostLanHost(macAddress string) error
Expand Down
43 changes: 42 additions & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@
</p>
</a>
</li>
<li class="nav-item">
<a href="{{.basePath}}/email-settings" class="nav-link {{if eq .baseData.Active "email-settings" }}active{{end}}">
<i class="nav-icon fas fa-cog"></i>
<p>
Email Settings
</p>
</a>
</li>
<li class="nav-item">
<a href="{{.basePath}}/users-settings" class="nav-link {{if eq .baseData.Active "users-settings" }}active{{end}}">
<i class="nav-icon fas fa-cog"></i>
Expand All @@ -154,7 +162,6 @@
</a>
</li>
{{end}}

<li class="nav-header">UTILITIES</li>
<li class="nav-item">
<a href="{{.basePath}}/status" class="nav-link {{if eq .baseData.Active "status" }}active{{end}}">
Expand Down Expand Up @@ -246,6 +253,14 @@ <h4 class="modal-title">New Wireguard Client</h4>
</label>
</div>
</div>
<div class="form-group">
<div class="icheck-primary d-inline">
<input type="checkbox" id="send_email_after_creation" {{ if .client_defaults.EnableAfterCreation }}checked{{ end }}>
<label for="send_email_after_creation">
Send email after creation
</label>
</div>
</div>
<details>
<summary><strong>Public and Preshared Keys</strong>
<i class="fas fa-info-circle" data-toggle="tooltip"
Expand Down Expand Up @@ -445,6 +460,7 @@ <h1>{{template "page_title" .}}</h1>
success: function(resp) {
$("#modal_new_client").modal('hide');
toastr.success('Created new client successfully');
sendEmailAfterCreation(resp.id, resp.email);
// Update the home page (clients page) after adding successfully
if (window.location.pathname === "{{.basePath}}/") {
populateClient(resp.id);
Expand Down Expand Up @@ -477,6 +493,31 @@ <h1>{{template "page_title" .}}</h1>
}
});
}

function sendEmailAfterCreation(client_id, client_email) {
console.log("Do you even try?");
if ($("#send_email_after_creation").is(':checked')) {
if (client_email !== "" && client_id) {
const data = {"id": client_id, "email": client_email};
$.ajax({
cache: false,
method: 'POST',
url: '{{.basePath}}/email-client',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
success: function (resp) {
toastr.success('Sent email to client successfully');
},
error: function (jqXHR, exception) {
const responseJson = jQuery.parseJSON(jqXHR.responseText);
toastr.error(responseJson['message']);
}
});
}
}
}

</script>
<script>
//Initialize Select2 Elements
Expand Down
Loading