Skip to content
This repository was archived by the owner on Jul 12, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 7 additions & 14 deletions cmd/server/assets/realmadmin/_form_email.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<div id="smtp-form" class="collapse{{if not $realm.UseSystemEmailConfig}} show{{end}}">
<div class="form-label-group">
<input type="text" name="smtp_account" id="smtp-account" class="form-control text-monospace{{if $emailConfig.ErrorsFor "smtpAccount"}} is-invalid{{end}}"
placeholder="SMTP account" value="{{if $emailConfig}}{{$emailConfig.SMTPAccount}}{{end}}" />
placeholder="SMTP account" value="{{if $emailConfig.SMTPAccount}}{{$emailConfig.SMTPAccount}}{{end}}" />
<label for="smtp-account">SMTP account</label>
{{template "errorable" $emailConfig.ErrorsFor "smtpAccount"}}
<small class="form-text text-muted">
Expand All @@ -43,16 +43,9 @@
</div>

<div class="form-label-group">
<div class="input-group">
<input type="password" name="smtp_password" id="smtp-password" class="form-control text-monospace{{if $emailConfig.ErrorsFor "smtpPassword"}} is-invalid{{end}}" autocomplete="new-password"
placeholder="SMTP password" value="{{if $emailConfig}}{{$emailConfig.SMTPPassword}}{{end}}">
<label for="smtp-password">SMTP password</label>
<div class="input-group-append">
<a class="input-group-text" data-toggle-password="smtp-password">
<span class="oi oi-lock-locked" aria-hidden="true"></span>
</a>
</div>
</div>
<input type="password" name="smtp_password" id="smtp-password" class="form-control text-monospace{{if $emailConfig.ErrorsFor "smtpPassword"}} is-invalid{{end}}" autocomplete="new-password"
placeholder="SMTP password" value="{{if $emailConfig.SMTPPassword}}{{.passwordSentinel}}{{end}}">
<label for="smtp-password">SMTP password</label>
{{template "errorable" $emailConfig.ErrorsFor "smtpPassword"}}
<small class="form-text text-muted">
This is the password for your SMTP email.
Expand All @@ -61,7 +54,7 @@

<div class="form-label-group">
<input name="smtp_host" id="smtp-host" class="form-control text-monospace{{if $emailConfig.ErrorsFor "smtpPort"}} is-invalid{{end}}"
placeholder="SMTP host" value="{{if $emailConfig}}{{$emailConfig.SMTPHost}}{{end}}" />
placeholder="SMTP host" value="{{if $emailConfig.SMTPHost}}{{$emailConfig.SMTPHost}}{{end}}" />
<label for="smtp-port">SMTP host</label>
{{template "errorable" $emailConfig.ErrorsFor "smtpHost"}}
<small class="form-text text-muted">
Expand All @@ -71,7 +64,7 @@

<div class="form-label-group">
<input name="smtp_port" id="smtp-port" class="form-control text-monospace{{if $emailConfig.ErrorsFor "smtpPort"}} is-invalid{{end}}"
placeholder="SMTP port" value="{{if $emailConfig}}{{$emailConfig.SMTPPort}}{{else}}587{{end}}" />
placeholder="SMTP port" value="{{if $emailConfig.SMTPPort}}{{$emailConfig.SMTPPort}}{{else}}587{{end}}" />
<label for="smtp-port">SMTP port</label>
{{template "errorable" $emailConfig.ErrorsFor "smtpPort"}}
<small class="form-text text-muted">
Expand Down Expand Up @@ -117,7 +110,7 @@
</div>

<div class="mt-4">
<input type="submit" class="btn btn-primary btn-block" value="Update email settings" />
<input type="submit" id="update-smtp" class="btn btn-primary btn-block" value="Update email settings" />
</div>
</form>

Expand Down
19 changes: 6 additions & 13 deletions cmd/server/assets/realmadmin/_form_sms.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<div id="sms-form" class="collapse{{if not $realm.UseSystemSMSConfig}} show{{end}}">
<div class="form-label-group">
<input type="text" name="twilio_account_sid" id="twilio-account-sid" class="form-control text-monospace{{if $smsConfig.ErrorsFor "twilioAccountSid"}} is-invalid{{end}}"
placeholder="Twilio account" value="{{if $smsConfig}}{{$smsConfig.TwilioAccountSid}}{{end}}" />
placeholder="Twilio account" value="{{if $smsConfig.TwilioAccountSid}}{{$smsConfig.TwilioAccountSid}}{{end}}" />
<label for="twilio-account-sid">Twilio account</label>
{{template "errorable" $smsConfig.ErrorsFor "twilioAccountSid"}}
<small class="form-text text-muted">
Expand All @@ -44,16 +44,9 @@
</div>

<div class="form-label-group">
<div class="input-group">
<input type="password" name="twilio_auth_token" id="twilio-auth-token" class="form-control text-monospace{{if $smsConfig.ErrorsFor "twilioAuthToken"}} is-invalid{{end}}" autocomplete="new-password"
placeholder="Twilio auth token" value="{{if $smsConfig}}{{$smsConfig.TwilioAuthToken}}{{end}}">
<label for="twilio-auth-token">Twilio auth token</label>
<div class="input-group-append">
<a class="input-group-text" data-toggle-password="twilio-auth-token">
<span class="oi oi-lock-locked" aria-hidden="true"></span>
</a>
</div>
</div>
<input type="password" name="twilio_auth_token" id="twilio-auth-token" class="form-control text-monospace{{if $smsConfig.ErrorsFor "twilioAuthToken"}} is-invalid{{end}}" autocomplete="new-password"
placeholder="Twilio auth token" value="{{if $smsConfig.TwilioAuthToken}}{{.passwordSentinel}}{{end}}">
<label for="twilio-auth-token">Twilio auth token</label>
{{template "errorable" $smsConfig.ErrorsFor "twilioAuthToken"}}
<small class="form-text text-muted">
This is the Twilio Auth Token. Get this value from the Twilio console.
Expand All @@ -62,7 +55,7 @@

<div class="form-label-group">
<input type="tel" name="twilio_from_number" id="twilio-from-number" class="form-control text-monospace{{if $smsConfig.ErrorsFor "twilioFromNumber"}} is-invalid{{end}}" autocomplete="new-password"
placeholder="Twilio number" value="{{if $smsConfig}}{{$smsConfig.TwilioFromNumber}}{{end}}" />
placeholder="Twilio number" value="{{if $smsConfig.TwilioFromNumber}}{{$smsConfig.TwilioFromNumber}}{{end}}" />
<label for="twilio-from-number">Twilio number</label>
{{template "errorable" $smsConfig.ErrorsFor "twilioFromNumber"}}
<small class="form-text text-muted">
Expand Down Expand Up @@ -90,7 +83,7 @@
</div>

<div class="mt-4">
<input type="submit" class="btn btn-primary btn-block" value="Update SMS settings" />
<input type="submit" id="update-sms" class="btn btn-primary btn-block" value="Update SMS settings" />
</div>
</form>

Expand Down
15 changes: 13 additions & 2 deletions pkg/controller/realmadmin/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import (
"github.com/google/exposure-notifications-verification-server/pkg/sms"
)

const (
// passwordSentinel is the password string inserted into forms.
passwordSentinel = "very-nice-try-maybe-next-time"
)

var (
shortCodeLengths = []int{6, 7, 8}
shortCodeMinutes = []int{}
Expand Down Expand Up @@ -265,7 +270,9 @@ func (c *Controller) HandleSettings() http.Handler {
// record.
smsConfig.ProviderType = sms.ProviderTypeTwilio
smsConfig.TwilioAccountSid = form.TwilioAccountSid
smsConfig.TwilioAuthToken = form.TwilioAuthToken
if v := form.TwilioAuthToken; v != passwordSentinel {
smsConfig.TwilioAuthToken = v
}
smsConfig.TwilioFromNumber = form.TwilioFromNumber

if err := c.db.SaveSMSConfig(smsConfig); err != nil {
Expand Down Expand Up @@ -305,7 +312,9 @@ func (c *Controller) HandleSettings() http.Handler {
// record.
emailConfig.ProviderType = email.ProviderTypeSMTP
emailConfig.SMTPAccount = form.SMTPAccount
emailConfig.SMTPPassword = form.SMTPPassword
if v := form.SMTPPassword; v != passwordSentinel {
emailConfig.SMTPPassword = form.SMTPPassword
}
emailConfig.SMTPHost = form.SMTPHost
emailConfig.SMTPPort = form.SMTPPort

Expand Down Expand Up @@ -450,5 +459,7 @@ func (c *Controller) renderSettings(
m["quotaLimit"] = quotaLimit
m["quotaRemaining"] = quotaRemaining

m["passwordSentinel"] = passwordSentinel

c.h.RenderHTML(w, "realmadmin/edit", m)
}
210 changes: 210 additions & 0 deletions pkg/controller/realmadmin/sms_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package realmadmin_test

import (
"context"
"testing"
"time"

"github.com/google/exposure-notifications-verification-server/internal/browser"
"github.com/google/exposure-notifications-verification-server/internal/envstest"
"github.com/google/exposure-notifications-verification-server/pkg/controller"
"github.com/google/exposure-notifications-verification-server/pkg/database"

"github.com/chromedp/chromedp"
)

func TestHandleSettings_SMS(t *testing.T) {
harness := envstest.NewServer(t)

// Get the default realm
realm, err := harness.Database.FindRealm(1)
if err != nil {
t.Fatal(err)
}

// Create a user
admin := &database.User{
Email: "admin@example.com",
Name: "Admin",
Realms: []*database.Realm{realm},
AdminRealms: []*database.Realm{realm},
}
if err := harness.Database.SaveUser(admin, database.System); err != nil {
t.Fatal(err)
}

// Log in the user.
session, err := harness.LoggedInSession(nil, admin.Email)
if err != nil {
t.Fatal(err)
}

// Set the current realm.
controller.StoreSessionRealm(session, realm)

// Mint a cookie for the session.
cookie, err := harness.SessionCookie(session)
if err != nil {
t.Fatal(err)
}

// Create a browser runner.
browserCtx := browser.New(t)
taskCtx, done := context.WithTimeout(browserCtx, 30*time.Second)
defer done()

var twilioAccountSid string
var twilioAuthToken string
var twilioFromNumber string

if err := chromedp.Run(taskCtx,
// Pre-authenticate the user.
browser.SetCookie(cookie),

// Visit page.
chromedp.Navigate(`http://`+harness.Server.Addr()+`/realm/settings#sms`),

// Wait for render.
chromedp.WaitVisible(`div#sms`, chromedp.ByQuery),

// Fill out the form.
chromedp.SetValue(`input#twilio-account-sid`, "accountSid", chromedp.ByQuery),
chromedp.SetValue(`input#twilio-auth-token`, "authToken", chromedp.ByQuery),
chromedp.SetValue(`input#twilio-from-number`, "1234567890", chromedp.ByQuery),

// Click submit.
chromedp.Click(`input#update-sms`, chromedp.ByQuery),

// Wait for the page to reload.
chromedp.WaitVisible(`div#sms`, chromedp.ByQuery),

chromedp.Value(`input#twilio-account-sid`, &twilioAccountSid, chromedp.ByQuery),
chromedp.Value(`input#twilio-auth-token`, &twilioAuthToken, chromedp.ByQuery),
chromedp.Value(`input#twilio-from-number`, &twilioFromNumber, chromedp.ByQuery),
); err != nil {
t.Fatal(err)
}

// Check form
if got, want := twilioAccountSid, "accountSid"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
if got, want := twilioAuthToken, "very-nice-try-maybe-next-time"; got != want {
// very-nice-try-maybe-next-time comes from passwordSentinel
t.Errorf("expected %q to be %q", got, want)
}
if got, want := twilioFromNumber, "1234567890"; got != want {
t.Errorf("expected %q to be %q", got, want)
}

{
// Check database
smsConfig, err := realm.SMSConfig(harness.Database)
if err != nil {
t.Fatal(err)
}
if smsConfig == nil {
t.Fatal("expected smsConfig")
}

if got, want := smsConfig.TwilioAccountSid, "accountSid"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
if got, want := smsConfig.TwilioAuthToken, "authToken"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
if got, want := smsConfig.TwilioFromNumber, "1234567890"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}

// Update
if err := chromedp.Run(taskCtx,
// Pre-authenticate the user.
browser.SetCookie(cookie),

// Visit page.
chromedp.Navigate(`http://`+harness.Server.Addr()+`/realm/settings#sms`),

// Wait for render.
chromedp.WaitVisible(`div#sms`, chromedp.ByQuery),

// Fill out the form.
chromedp.SetValue(`input#twilio-account-sid`, "accountSid-new", chromedp.ByQuery),
chromedp.SetValue(`input#twilio-from-number`, "1234567890-new", chromedp.ByQuery),

// Click submit.
chromedp.Click(`input#update-sms`, chromedp.ByQuery),

// Wait for the page to reload.
chromedp.WaitVisible(`div#sms`, chromedp.ByQuery),
); err != nil {
t.Fatal(err)
}

{
// Check database again
smsConfig, err := realm.SMSConfig(harness.Database)
if err != nil {
t.Fatal(err)
}
if smsConfig == nil {
t.Fatal("expected smsConfig")
}

if got, want := smsConfig.TwilioAccountSid, "accountSid-new"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
if got, want := smsConfig.TwilioAuthToken, "authToken"; got != want {
// should not change
t.Errorf("expected %q to be %q", got, want)
}
if got, want := smsConfig.TwilioFromNumber, "1234567890-new"; got != want {
t.Errorf("expected %q to be %q", got, want)
}
}

// Delete
if err := chromedp.Run(taskCtx,
// Pre-authenticate the user.
browser.SetCookie(cookie),

// Visit page.
chromedp.Navigate(`http://`+harness.Server.Addr()+`/realm/settings#sms`),

// Wait for render.
chromedp.WaitVisible(`div#sms`, chromedp.ByQuery),

// Fill out the form.
chromedp.SetValue(`input#twilio-account-sid`, "", chromedp.ByQuery),
chromedp.SetValue(`input#twilio-auth-token`, "", chromedp.ByQuery),
chromedp.SetValue(`input#twilio-from-number`, "", chromedp.ByQuery),

// Click submit.
chromedp.Click(`input#update-sms`, chromedp.ByQuery),

// Wait for the page to reload.
chromedp.WaitVisible(`div#sms`, chromedp.ByQuery),
); err != nil {
t.Fatal(err)
}

// Check database again
if _, err := realm.SMSConfig(harness.Database); !database.IsNotFound(err) {
t.Fatal("expected smsConfig to be deleted")
}
}
Loading