Commit 3843cea9 authored by Matthew Holt's avatar Matthew Holt

letsencrypt: Allow (but warn about) empty emails

parent e99b3af0
......@@ -156,10 +156,6 @@ func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config,
continue
}
leEmail := getEmail(configs[i])
if leEmail == "" {
// TODO: This may not be an error; just a poor choice by the user
return nil, errors.New("must have email address to serve HTTPS without existing certificate and key")
}
initMap[leEmail] = append(initMap[leEmail], &configs[i])
}
return initMap, nil
......
......@@ -48,12 +48,18 @@ func (s Storage) Users() string {
// User gets the account folder for the user with email.
func (s Storage) User(email string) string {
if email == "" {
email = emptyEmail
}
return filepath.Join(s.Users(), email)
}
// UserRegFile gets the path to the registration file for
// the user with the given email address.
func (s Storage) UserRegFile(email string) string {
if email == "" {
email = emptyEmail
}
fileName := emailUsername(email)
if fileName == "" {
fileName = "registration"
......@@ -64,7 +70,9 @@ func (s Storage) UserRegFile(email string) string {
// UserKeyFile gets the path to the private key file for
// the user with the given email address.
func (s Storage) UserKeyFile(email string) string {
// TODO: Read the KeyFile property in the registration file instead?
if email == "" {
email = emptyEmail
}
fileName := emailUsername(email)
if fileName == "" {
fileName = "private"
......
......@@ -35,6 +35,17 @@ func TestStorage(t *testing.T) {
if expected, actual := filepath.Join("letsencrypt", "users", "me@example.com", "me.key"), storage.UserKeyFile("me@example.com"); actual != expected {
t.Errorf("Expected UserKeyFile() to return '%s' but got '%s'", expected, actual)
}
// Test with empty emails
if expected, actual := filepath.Join("letsencrypt", "users", emptyEmail), storage.User(emptyEmail); actual != expected {
t.Errorf("Expected User(\"\") to return '%s' but got '%s'", expected, actual)
}
if expected, actual := filepath.Join("letsencrypt", "users", emptyEmail, emptyEmail+".json"), storage.UserRegFile(""); actual != expected {
t.Errorf("Expected UserRegFile(\"\") to return '%s' but got '%s'", expected, actual)
}
if expected, actual := filepath.Join("letsencrypt", "users", emptyEmail, emptyEmail+".key"), storage.UserKeyFile(""); actual != expected {
t.Errorf("Expected UserKeyFile(\"\") to return '%s' but got '%s'", expected, actual)
}
}
func TestEmailUsername(t *testing.T) {
......@@ -61,6 +72,10 @@ func TestEmailUsername(t *testing.T) {
input: "@foobar.com",
expect: "foobar.com",
},
{
input: emptyEmail,
expect: emptyEmail,
},
} {
if actual := emailUsername(test.input); actual != test.expect {
t.Errorf("Test %d: Expected username to be '%s' but was '%s'", i, test.expect, actual)
......
......@@ -115,6 +115,8 @@ func newUser(email string) (User, error) {
// getEmail does everything it can to obtain an email
// address from the user to use for TLS for cfg. If it
// cannot get an email address, it returns empty string.
// (It will warn the user of the consequences of an
// empty email.)
func getEmail(cfg server.Config) string {
// First try the tls directive from the Caddyfile
leEmail := cfg.TLS.LetsEncryptEmail
......@@ -124,7 +126,6 @@ func getEmail(cfg server.Config) string {
}
if leEmail == "" {
// Then try to get most recent user email ~/.caddy/users file
// TODO: Probably better to open the user's json file and read the email out of there...
userDirs, err := ioutil.ReadDir(storage.Users())
if err == nil {
var mostRecent os.FileInfo
......@@ -143,9 +144,13 @@ func getEmail(cfg server.Config) string {
}
if leEmail == "" {
// Alas, we must bother the user and ask for an email address
// TODO/BUG: This doesn't work when Caddyfile is piped into caddy
reader := bufio.NewReader(stdin)
fmt.Print("Email address: ") // TODO: More explanation probably, and show ToS?
fmt.Println("Your sites will be served over HTTPS automatically using Let's Encrypt.")
fmt.Println("By continuing, you agree to the Let's Encrypt Subscriber Agreement at:")
fmt.Println(" <TODO: link>")
fmt.Println("Please enter your email address so you can recover your account if needed.")
fmt.Println("You can leave it blank, but you lose the ability to recover your account.")
fmt.Print("Email address: ")
var err error
leEmail, err = reader.ReadString('\n')
if err != nil {
......@@ -169,7 +174,7 @@ func promptUserAgreement(agreementURL string, changed bool) bool {
fmt.Print("Do you agree to the terms? (y/n): ")
}
reader := bufio.NewReader(stdin) // TODO/BUG: This doesn't work when Caddyfile is piped into caddy
reader := bufio.NewReader(stdin)
answer, err := reader.ReadString('\n')
if err != nil {
return false
......@@ -182,3 +187,7 @@ func promptUserAgreement(agreementURL string, changed bool) bool {
// stdin is used to read the user's input if prompted;
// this is changed by tests during tests.
var stdin = io.ReadWriter(os.Stdin)
// The name of the folder for accounts where the email
// address was not provided; default 'username' if you will.
const emptyEmail = "default"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment