forked from bitly/oauth2_proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reload authenticated-emails-file upon update
This change extracts the UserMap class from NewValidator() so that its LoadAuthenticatedEmailsFile() method can be called concurrently. This method is called by a goroutine containing a fsnotify.Watcher watching the authenticated emails file. Watching isn't forever aborted when the authenticated emails file disappears. The goroutine will call os.Stat() up to twenty times a second if the file is persistently missing, but that's the pathological case, not the common one. The common case is that some editors (including Vim) will perform a rename-and-replace when updating a file, triggering fsnotify.Rename events, and the file will temporarily disappear. This watcher goroutine handles that case. Also, on some platforms (notably Arch Linux), a remove will be preceded by a fsnotify.Chmod, causing a race between the upcoming fsnotify.Remove and the call to UserMap.LoadAuthenticatedEmailsFile(). Hence, we treat fsnotify.Chmod the same as fsnotify.Remove and fsnotify.Rename. There's no significant penalty to re-adding a file to the watcher. Also contains the following small changes from the summary of commits below: - Minor optimization of email domain search - Fixed api_test.go on Windows - Add deferred File.Close() calls where needed - Log error and return if emails file doesn't parse These are the original commits from bitly#89 squashed into this one: 0c6f2b6 Refactor validator_test to prepare for more tests e0c792b Add more test cases to validator_test a9a9d93 Minor optimization of email domain search b763ea5 Extract LoadAuthenticatedEmailsFile() 8cdaf7f Introduce synchronized UserMap type 1b84eef Add UserMap methods, locking af15dcf Reload authenticated-emails-file upon update 6d95548 Make UserMap operations lock-free Per: - http://stackoverflow.com/questions/21447463/is-assigning-a-pointer-atomic-in-golang - https://groups.google.com/forum/#!msg/golang-nuts/ueSvaEKgyLY/ZW_74IC4PekJ 75755d5 Fix tests on Windows d0eab2e Ignore email file watcher Chmod events 0b9798b Fix watcher on Ubuntu 12.04 3a8251a WaitForReplacement() to retry emails file watch a57fd29 Add deferred File.Close() calls where needed Because correctness: Don't leak file handles anywhere, and prepare for future panics and early returns. 52ed3fd Log error and return if emails file doesn't parse 40100d4 Add gopkg.in/fsnotify.v1 dependency to Godeps file 17dfbbc Avoid a race when Remove is preceded by Chmod
- Loading branch information
Mike Bland
committed
May 12, 2015
1 parent
26170c5
commit ca91b5e
Showing
9 changed files
with
398 additions
and
29 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,23 +7,117 @@ import ( | |
"testing" | ||
) | ||
|
||
func TestValidatorComparisonsAreCaseInsensitive(t *testing.T) { | ||
auth_email_file, err := ioutil.TempFile("", "test_auth_emails_") | ||
type ValidatorTest struct { | ||
auth_email_file *os.File | ||
} | ||
|
||
func NewValidatorTest(t *testing.T) *ValidatorTest { | ||
vt := &ValidatorTest{} | ||
var err error | ||
vt.auth_email_file, err = ioutil.TempFile("", "test_auth_emails_") | ||
if err != nil { | ||
t.Fatal("failed to create temp file: " + err.Error()) | ||
} | ||
defer os.Remove(auth_email_file.Name()) | ||
return vt | ||
} | ||
|
||
auth_email_file.WriteString( | ||
strings.Join([]string{"[email protected]"}, "\n")) | ||
err = auth_email_file.Close() | ||
if err != nil { | ||
t.Fatal("failed to close temp file " + auth_email_file.Name() + | ||
": " + err.Error()) | ||
func (vt *ValidatorTest) TearDown() { | ||
os.Remove(vt.auth_email_file.Name()) | ||
} | ||
|
||
// This will close vt.auth_email_file. | ||
func (vt *ValidatorTest) WriteEmails(t *testing.T, emails []string) { | ||
defer vt.auth_email_file.Close() | ||
vt.auth_email_file.WriteString(strings.Join(emails, "\n")) | ||
if err := vt.auth_email_file.Close(); err != nil { | ||
t.Fatal("failed to close temp file " + | ||
vt.auth_email_file.Name() + ": " + err.Error()) | ||
} | ||
} | ||
|
||
func TestValidatorEmpty(t *testing.T) { | ||
vt := NewValidatorTest(t) | ||
defer vt.TearDown() | ||
|
||
vt.WriteEmails(t, []string(nil)) | ||
domains := []string(nil) | ||
validator := NewValidator(domains, vt.auth_email_file.Name()) | ||
|
||
if validator("[email protected]") { | ||
t.Error("nothing should validate when the email and " + | ||
"domain lists are empty") | ||
} | ||
} | ||
|
||
func TestValidatorSingleEmail(t *testing.T) { | ||
vt := NewValidatorTest(t) | ||
defer vt.TearDown() | ||
|
||
vt.WriteEmails(t, []string{"[email protected]"}) | ||
domains := []string(nil) | ||
validator := NewValidator(domains, vt.auth_email_file.Name()) | ||
|
||
if !validator("[email protected]") { | ||
t.Error("email should validate") | ||
} | ||
if validator("[email protected]") { | ||
t.Error("email from same domain but not in list " + | ||
"should not validate when domain list is empty") | ||
} | ||
} | ||
|
||
func TestValidatorSingleDomain(t *testing.T) { | ||
vt := NewValidatorTest(t) | ||
defer vt.TearDown() | ||
|
||
vt.WriteEmails(t, []string(nil)) | ||
domains := []string{"example.com"} | ||
validator := NewValidator(domains, vt.auth_email_file.Name()) | ||
|
||
if !validator("[email protected]") { | ||
t.Error("email should validate") | ||
} | ||
if !validator("[email protected]") { | ||
t.Error("email from same domain should validate") | ||
} | ||
} | ||
|
||
func TestValidatorMultipleEmailsMultipleDomains(t *testing.T) { | ||
vt := NewValidatorTest(t) | ||
defer vt.TearDown() | ||
|
||
vt.WriteEmails(t, []string{ | ||
"[email protected]", | ||
"[email protected]", | ||
}) | ||
domains := []string{"example0.com", "example1.com"} | ||
validator := NewValidator(domains, vt.auth_email_file.Name()) | ||
|
||
if !validator("[email protected]") { | ||
t.Error("email from first domain should validate") | ||
} | ||
if !validator("[email protected]") { | ||
t.Error("email from second domain should validate") | ||
} | ||
if !validator("[email protected]") { | ||
t.Error("first email in list should validate") | ||
} | ||
if !validator("[email protected]") { | ||
t.Error("second email in list should validate") | ||
} | ||
if validator("[email protected]") { | ||
t.Error("email not in list that matches no domains " + | ||
"should not validate") | ||
} | ||
} | ||
|
||
func TestValidatorComparisonsAreCaseInsensitive(t *testing.T) { | ||
vt := NewValidatorTest(t) | ||
defer vt.TearDown() | ||
|
||
vt.WriteEmails(t, []string{"[email protected]"}) | ||
domains := []string{"Frobozz.Com"} | ||
validator := NewValidator(domains, auth_email_file.Name()) | ||
validator := NewValidator(domains, vt.auth_email_file.Name()) | ||
|
||
if !validator("[email protected]") { | ||
t.Error("loaded email addresses are not lower-cased") | ||
|
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,50 @@ | ||
// +build go1.3 | ||
// +build !plan9,!solaris,!windows | ||
|
||
// Turns out you can't copy over an existing file on Windows. | ||
|
||
package main | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"testing" | ||
) | ||
|
||
func (vt *ValidatorTest) UpdateEmailFileViaCopyingOver( | ||
t *testing.T, emails []string) { | ||
orig_file := vt.auth_email_file | ||
var err error | ||
vt.auth_email_file, err = ioutil.TempFile("", "test_auth_emails_") | ||
if err != nil { | ||
t.Fatal("failed to create temp file for copy: " + err.Error()) | ||
} | ||
vt.WriteEmails(t, emails) | ||
err = os.Rename(vt.auth_email_file.Name(), orig_file.Name()) | ||
if err != nil { | ||
t.Fatal("failed to copy over temp file: " + err.Error()) | ||
} | ||
vt.auth_email_file = orig_file | ||
} | ||
|
||
func TestValidatorOverwriteEmailListViaCopyingOver(t *testing.T) { | ||
vt := NewValidatorTest(t) | ||
defer vt.TearDown() | ||
|
||
vt.WriteEmails(t, []string{"[email protected]"}) | ||
domains := []string(nil) | ||
updated := make(chan bool) | ||
validator := newValidatorImpl(domains, vt.auth_email_file.Name(), | ||
func() { updated <- true }) | ||
|
||
if !validator("[email protected]") { | ||
t.Error("email in list should validate") | ||
} | ||
|
||
vt.UpdateEmailFileViaCopyingOver(t, []string{"[email protected]"}) | ||
<-updated | ||
|
||
if validator("[email protected]") { | ||
t.Error("email removed from list should not validate") | ||
} | ||
} |
Oops, something went wrong.