From c62b85e37d84cfaa38da2f23ba4cbeb227bc2c14 Mon Sep 17 00:00:00 2001
From: Dylan Smith <dylan@lightblast.info>
Date: Wed, 25 Oct 2017 19:28:56 +1100
Subject: [PATCH] Less restrictive email handler - replace gmail with generic

---
 config.template.yml                               |  6 ++++--
 config.test.yml                                   |  6 ++++--
 server/src/lib/Server.ts                          |  8 ++++----
 server/src/lib/configuration/Configuration.d.ts   |  5 +++--
 server/src/lib/configuration/Validator.ts         |  8 ++++----
 .../{GMailNotifier.ts => EMailNotifier.ts}        |  6 +++---
 server/src/lib/notifiers/IMailSenderBuilder.ts    |  4 ++--
 server/src/lib/notifiers/MailSenderBuilder.ts     | 10 +++++-----
 server/src/lib/notifiers/NotifierFactory.ts       |  8 ++++----
 server/test/ServerConfiguration.test.ts           |  5 +++--
 .../configuration/ConfigurationParser.test.ts     | 15 +++++++++------
 .../LdapConfigurationAdaptation.test.ts           |  5 +++--
 server/test/configuration/Validator.test.ts       |  7 ++++---
 .../test/mocks/notifiers/MailSenderBuilderStub.ts | 10 +++++-----
 ...MailNotifier.test.ts => EMailNotifier.test.ts} | 14 ++++++++------
 server/test/notifiers/MailSenderBuilder.test.ts   |  8 +++++---
 server/test/notifiers/NotifierFactory.test.ts     | 11 ++++++-----
 server/test/server/PrivatePages.ts                |  5 +++--
 server/test/server/PublicPages.ts                 |  5 +++--
 19 files changed, 82 insertions(+), 64 deletions(-)
 rename server/src/lib/notifiers/{GMailNotifier.ts => EMailNotifier.ts} (75%)
 rename server/test/notifiers/{GMailNotifier.test.ts => EMailNotifier.test.ts} (80%)

diff --git a/config.template.yml b/config.template.yml
index 83f1fed7..80f277ab 100644
--- a/config.template.yml
+++ b/config.template.yml
@@ -203,11 +203,13 @@ notifier:
   # filesystem:
   #   filename: /tmp/authelia/notification.txt
 
-  # Use your gmail account to send the notifications. You can use an app password.
-  # gmail:
+  # Use your email account to send the notifications. You can use an app password.
+  # List of valid services can be found here: https://nodemailer.com/smtp/well-known/  
+  # email:
   #   username: user@example.com
   #   password: yourpassword
   #   sender: admin@example.com
+  #   service: gmail
   
   # Use a SMTP server for sending notifications
   smtp:
diff --git a/config.test.yml b/config.test.yml
index ca70f527..842e7765 100644
--- a/config.test.yml
+++ b/config.test.yml
@@ -178,11 +178,13 @@ storage:
 # registration or a TOTP registration.
 # Use only an available configuration: filesystem, gmail
 notifier:
-  # Use your gmail account to send the notifications. You can use an app password.
-  # gmail:
+  # Use your email account to send the notifications. You can use an app password.
+  # List of valid services can be found here: https://nodemailer.com/smtp/well-known/  
+  # email:
   #   username: user@example.com
   #   password: yourpassword
   #   sender: admin@example.com
+  #   service: gmail
   
   # Use a SMTP server for sending notifications
   smtp:
diff --git a/server/src/lib/Server.ts b/server/src/lib/Server.ts
index f043d4f3..a5e6162b 100644
--- a/server/src/lib/Server.ts
+++ b/server/src/lib/Server.ts
@@ -71,15 +71,15 @@ export default class Server {
 
     displayableUserConfiguration.ldap.password = STARS;
     displayableUserConfiguration.session.secret = STARS;
-    if (displayableUserConfiguration.notifier && displayableUserConfiguration.notifier.gmail)
-      displayableUserConfiguration.notifier.gmail.password = STARS;
+    if (displayableUserConfiguration.notifier && displayableUserConfiguration.notifier.email)
+      displayableUserConfiguration.notifier.email.password = STARS;
     if (displayableUserConfiguration.notifier && displayableUserConfiguration.notifier.smtp)
       displayableUserConfiguration.notifier.smtp.password = STARS;
 
     displayableAppConfiguration.ldap.password = STARS;
     displayableAppConfiguration.session.secret = STARS;
-    if (displayableAppConfiguration.notifier && displayableAppConfiguration.notifier.gmail)
-      displayableAppConfiguration.notifier.gmail.password = STARS;
+    if (displayableAppConfiguration.notifier && displayableAppConfiguration.notifier.email)
+      displayableAppConfiguration.notifier.email.password = STARS;
     if (displayableAppConfiguration.notifier && displayableAppConfiguration.notifier.smtp)
       displayableAppConfiguration.notifier.smtp.password = STARS;
 
diff --git a/server/src/lib/configuration/Configuration.d.ts b/server/src/lib/configuration/Configuration.d.ts
index 199a84bc..cda35e14 100644
--- a/server/src/lib/configuration/Configuration.d.ts
+++ b/server/src/lib/configuration/Configuration.d.ts
@@ -66,10 +66,11 @@ interface SessionCookieConfiguration {
     redis?: SessionRedisOptions;
 }
 
-export interface GmailNotifierConfiguration {
+export interface EmailNotifierConfiguration {
     username: string;
     password: string;
     sender: string;
+    service: string;
 }
 
 export interface SmtpNotifierConfiguration {
@@ -86,7 +87,7 @@ export interface FileSystemNotifierConfiguration {
 }
 
 export interface NotifierConfiguration {
-    gmail?: GmailNotifierConfiguration;
+    email?: EmailNotifierConfiguration;
     smtp?: SmtpNotifierConfiguration;
     filesystem?: FileSystemNotifierConfiguration;
 }
diff --git a/server/src/lib/configuration/Validator.ts b/server/src/lib/configuration/Validator.ts
index 5058d08d..26a98394 100644
--- a/server/src/lib/configuration/Validator.ts
+++ b/server/src/lib/configuration/Validator.ts
@@ -54,19 +54,19 @@ function validateStorage(storage: any) {
 }
 
 function validateNotifier(notifier: NotifierConfiguration) {
-  const ERROR = "Notifier must be either 'filesystem', 'gmail' or 'smtp'";
+  const ERROR = "Notifier must be either 'filesystem', 'email' or 'smtp'";
 
   if (!notifier)
     return [];
 
-  const errors = validateUnknownKeys("notifier", notifier, ["filesystem", "gmail", "smtp"]);
+  const errors = validateUnknownKeys("notifier", notifier, ["filesystem", "email", "smtp"]);
   if (errors.length > 0)
     return errors;
 
-  if (notifier && notifier.filesystem && notifier.gmail && notifier.smtp)
+  if (notifier && notifier.filesystem && notifier.email && notifier.smtp)
     return [ERROR];
 
-  if (notifier && !notifier.filesystem && !notifier.gmail && !notifier.smtp)
+  if (notifier && !notifier.filesystem && !notifier.email && !notifier.smtp)
     return [ERROR];
 
   return [];
diff --git a/server/src/lib/notifiers/GMailNotifier.ts b/server/src/lib/notifiers/EMailNotifier.ts
similarity index 75%
rename from server/src/lib/notifiers/GMailNotifier.ts
rename to server/src/lib/notifiers/EMailNotifier.ts
index caa81581..5366dfae 100644
--- a/server/src/lib/notifiers/GMailNotifier.ts
+++ b/server/src/lib/notifiers/EMailNotifier.ts
@@ -2,14 +2,14 @@
 import * as BluebirdPromise from "bluebird";
 
 import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier";
-import { GmailNotifierConfiguration } from "../configuration/Configuration";
+import { EmailNotifierConfiguration } from "../configuration/Configuration";
 import { IMailSender } from "./IMailSender";
 
-export class GMailNotifier extends AbstractEmailNotifier {
+export class EMailNotifier extends AbstractEmailNotifier {
   private mailSender: IMailSender;
   private sender: string;
 
-  constructor(options: GmailNotifierConfiguration, mailSender: IMailSender) {
+  constructor(options: EmailNotifierConfiguration, mailSender: IMailSender) {
     super();
     this.mailSender = mailSender;
     this.sender = options.sender;
diff --git a/server/src/lib/notifiers/IMailSenderBuilder.ts b/server/src/lib/notifiers/IMailSenderBuilder.ts
index 25aff7e7..59aa677b 100644
--- a/server/src/lib/notifiers/IMailSenderBuilder.ts
+++ b/server/src/lib/notifiers/IMailSenderBuilder.ts
@@ -1,7 +1,7 @@
 import { IMailSender } from "./IMailSender";
-import { SmtpNotifierConfiguration, GmailNotifierConfiguration } from "../configuration/Configuration";
+import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/Configuration";
 
 export interface IMailSenderBuilder {
-  buildGmail(options: GmailNotifierConfiguration): IMailSender;
+  buildEmail(options: EmailNotifierConfiguration): IMailSender;
   buildSmtp(options: SmtpNotifierConfiguration): IMailSender;
 }
\ No newline at end of file
diff --git a/server/src/lib/notifiers/MailSenderBuilder.ts b/server/src/lib/notifiers/MailSenderBuilder.ts
index 1b44f7b4..4b22ad74 100644
--- a/server/src/lib/notifiers/MailSenderBuilder.ts
+++ b/server/src/lib/notifiers/MailSenderBuilder.ts
@@ -3,7 +3,7 @@ import { IMailSenderBuilder } from "./IMailSenderBuilder";
 import { MailSender } from "./MailSender";
 import Nodemailer = require("nodemailer");
 import NodemailerSmtpTransport = require("nodemailer-smtp-transport");
-import { SmtpNotifierConfiguration, GmailNotifierConfiguration } from "../configuration/Configuration";
+import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/Configuration";
 
 export class MailSenderBuilder implements IMailSenderBuilder {
   private nodemailer: typeof Nodemailer;
@@ -12,15 +12,15 @@ export class MailSenderBuilder implements IMailSenderBuilder {
     this.nodemailer = nodemailer;
   }
 
-  buildGmail(options: GmailNotifierConfiguration): IMailSender {
-    const gmailOptions = {
-      service: "gmail",
+  buildEmail(options: EmailNotifierConfiguration): IMailSender {
+    const emailOptions = {
+      service: options.service,
       auth: {
         user: options.username,
         pass: options.password
       }
     };
-    return new MailSender(gmailOptions, this.nodemailer);
+    return new MailSender(emailOptions, this.nodemailer);
   }
 
   buildSmtp(options: SmtpNotifierConfiguration): IMailSender {
diff --git a/server/src/lib/notifiers/NotifierFactory.ts b/server/src/lib/notifiers/NotifierFactory.ts
index 0b200cd3..c03de76a 100644
--- a/server/src/lib/notifiers/NotifierFactory.ts
+++ b/server/src/lib/notifiers/NotifierFactory.ts
@@ -4,16 +4,16 @@ import Nodemailer = require("nodemailer");
 import { INotifier } from "./INotifier";
 
 import { FileSystemNotifier } from "./FileSystemNotifier";
-import { GMailNotifier } from "./GMailNotifier";
+import { EMailNotifier } from "./EMailNotifier";
 import { SmtpNotifier } from "./SmtpNotifier";
 import { IMailSender } from "./IMailSender";
 import { IMailSenderBuilder } from "./IMailSenderBuilder";
 
 export class NotifierFactory {
   static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier {
-    if ("gmail" in options) {
-      const mailSender = mailSenderBuilder.buildGmail(options.gmail);
-      return new GMailNotifier(options.gmail, mailSender);
+    if ("email" in options) {
+      const mailSender = mailSenderBuilder.buildEmail(options.email);
+      return new EMailNotifier(options.email, mailSender);
     }
     else if ("smtp" in options) {
       const mailSender = mailSenderBuilder.buildSmtp(options.smtp);
diff --git a/server/test/ServerConfiguration.test.ts b/server/test/ServerConfiguration.test.ts
index e3327765..71536352 100644
--- a/server/test/ServerConfiguration.test.ts
+++ b/server/test/ServerConfiguration.test.ts
@@ -53,10 +53,11 @@ describe("test server configuration", function () {
         base_dn: "dc=example,dc=com"
       },
       notifier: {
-        gmail: {
+        email: {
           username: "user@example.com",
           password: "password",
-          sender: "test@authelia.com"
+          sender: "test@authelia.com",
+          service: "gmail"
         }
       },
       regulation: {
diff --git a/server/test/configuration/ConfigurationParser.test.ts b/server/test/configuration/ConfigurationParser.test.ts
index f851dde0..4134cec3 100644
--- a/server/test/configuration/ConfigurationParser.test.ts
+++ b/server/test/configuration/ConfigurationParser.test.ts
@@ -34,10 +34,11 @@ describe("test config parser", function () {
       },
       logs_level: "debug",
       notifier: {
-        gmail: {
+        email: {
           username: "user",
           password: "password",
-          sender: "admin@example.com"
+          sender: "admin@example.com",
+          service: "gmail"
         }
       }
     };
@@ -83,18 +84,20 @@ describe("test config parser", function () {
   it("should get the notifier config", function () {
     const userConfig = buildYamlConfig();
     userConfig.notifier = {
-      gmail: {
+      email: {
         username: "user",
         password: "pass",
-        sender: "admin@example.com"
+        sender: "admin@example.com",
+        service: "gmail"
       }
     };
     const config = ConfigurationParser.parse(userConfig);
     Assert.deepEqual(config.notifier, {
-      gmail: {
+      email: {
         username: "user",
         password: "pass",
-        sender: "admin@example.com"
+        sender: "admin@example.com",
+        service: "gmail"
       }
     });
   });
diff --git a/server/test/configuration/LdapConfigurationAdaptation.test.ts b/server/test/configuration/LdapConfigurationAdaptation.test.ts
index 2d91a4ba..bfcc8fc0 100644
--- a/server/test/configuration/LdapConfigurationAdaptation.test.ts
+++ b/server/test/configuration/LdapConfigurationAdaptation.test.ts
@@ -31,10 +31,11 @@ describe("test ldap configuration adaptation", function () {
       },
       logs_level: "debug",
       notifier: {
-        gmail: {
+        email: {
           username: "user",
           password: "password",
-          sender: "admin@example.com"
+          sender: "admin@example.com",
+          service: "email"
         }
       }
     };
diff --git a/server/test/configuration/Validator.test.ts b/server/test/configuration/Validator.test.ts
index 69d3afca..a797a12f 100644
--- a/server/test/configuration/Validator.test.ts
+++ b/server/test/configuration/Validator.test.ts
@@ -28,7 +28,7 @@ describe("test validator", function () {
         "data.regulation should have required property 'max_retries'",
         "data.session should have required property 'secret'",
         "Storage must be either 'local' or 'mongo'",
-        "Notifier must be either 'filesystem', 'gmail' or 'smtp'"
+        "Notifier must be either 'filesystem', 'email' or 'smtp'"
       ]);
 
     Assert.deepStrictEqual(Validator.isValid({
@@ -67,10 +67,11 @@ describe("test validator", function () {
         user: "user"
       },
       notifier: {
-        gmail: {
+        email: {
           username: "user@gmail.com",
           password: "pass",
-          sender: "admin@example.com"
+          sender: "admin@example.com",
+          service: "gmail"
         }
       },
       regulation: {
diff --git a/server/test/mocks/notifiers/MailSenderBuilderStub.ts b/server/test/mocks/notifiers/MailSenderBuilderStub.ts
index ac701c7d..fa38b068 100644
--- a/server/test/mocks/notifiers/MailSenderBuilderStub.ts
+++ b/server/test/mocks/notifiers/MailSenderBuilderStub.ts
@@ -3,19 +3,19 @@ import BluebirdPromise = require("bluebird");
 import Nodemailer = require("nodemailer");
 import Sinon = require("sinon");
 import { IMailSender } from "../../../src/lib/notifiers/IMailSender";
-import { SmtpNotifierConfiguration, GmailNotifierConfiguration } from "../../../src/lib/configuration/Configuration";
+import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/Configuration";
 
 export class MailSenderBuilderStub implements IMailSenderBuilder {
-  buildGmailStub: Sinon.SinonStub;
+  buildEmailStub: Sinon.SinonStub;
   buildSmtpStub: Sinon.SinonStub;
 
   constructor() {
-    this.buildGmailStub = Sinon.stub();
+    this.buildEmailStub = Sinon.stub();
     this.buildSmtpStub = Sinon.stub();
   }
 
-  buildGmail(options: GmailNotifierConfiguration): IMailSender {
-    return this.buildGmailStub(options);
+  buildEmail(options: EmailNotifierConfiguration): IMailSender {
+    return this.buildEmailStub(options);
   }
 
   buildSmtp(options: SmtpNotifierConfiguration): IMailSender {
diff --git a/server/test/notifiers/GMailNotifier.test.ts b/server/test/notifiers/EMailNotifier.test.ts
similarity index 80%
rename from server/test/notifiers/GMailNotifier.test.ts
rename to server/test/notifiers/EMailNotifier.test.ts
index 8d598d5d..ca796fca 100644
--- a/server/test/notifiers/GMailNotifier.test.ts
+++ b/server/test/notifiers/EMailNotifier.test.ts
@@ -3,20 +3,21 @@ import * as Assert from "assert";
 import BluebirdPromise = require("bluebird");
 
 import { MailSenderStub } from "../mocks/notifiers/MailSenderStub";
-import GMailNotifier = require("../../src/lib/notifiers/GMailNotifier");
+import EMailNotifier = require("../../src/lib/notifiers/EMailNotifier");
 
 
-describe("test gmail notifier", function () {
+describe("test email notifier", function () {
   it("should send an email to given user", function () {
     const mailSender = new MailSenderStub();
     const options = {
       username: "user_gmail",
       password: "pass_gmail",
-      sender: "admin@example.com"
+      sender: "admin@example.com",
+      service: "gmail"
     };
 
     mailSender.sendStub.returns(BluebirdPromise.resolve());
-    const sender = new GMailNotifier.GMailNotifier(options, mailSender);
+    const sender = new EMailNotifier.EMailNotifier(options, mailSender);
     const subject = "subject";
     const url = "http://test.com";
 
@@ -33,11 +34,12 @@ describe("test gmail notifier", function () {
     const options = {
       username: "user_gmail",
       password: "pass_gmail",
-      sender: "admin@example.com"
+      sender: "admin@example.com",
+      service: "gmail"
     };
 
     mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail")));
-    const sender = new GMailNotifier.GMailNotifier(options, mailSender);
+    const sender = new EMailNotifier.EMailNotifier(options, mailSender);
     const subject = "subject";
     const url = "http://test.com";
 
diff --git a/server/test/notifiers/MailSenderBuilder.test.ts b/server/test/notifiers/MailSenderBuilder.test.ts
index 6bdc0f5f..e328ac56 100644
--- a/server/test/notifiers/MailSenderBuilder.test.ts
+++ b/server/test/notifiers/MailSenderBuilder.test.ts
@@ -14,15 +14,17 @@ describe("test MailSenderBuilder", function() {
     createTransportStub.restore();
   });
 
-  it("should create a gmail mail sender", function() {
+  it("should create a email mail sender", function() {
     const mailSenderBuilder = new MailSenderBuilder(Nodemailer);
-    mailSenderBuilder.buildGmail({
+    mailSenderBuilder.buildEmail({
       username: "user_gmail",
       password: "pass_gmail",
-      sender: "admin@example.com"
+      sender: "admin@example.com",
+      service: "gmail"
     });
     Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail");
     Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail");
+    Assert.equal(createTransportStub.getCall(0).args[0].service, "gmail");
   });
 
   describe("build smtp mail sender", function() {
diff --git a/server/test/notifiers/NotifierFactory.test.ts b/server/test/notifiers/NotifierFactory.test.ts
index 6dd25d4a..7ae7d0c3 100644
--- a/server/test/notifiers/NotifierFactory.test.ts
+++ b/server/test/notifiers/NotifierFactory.test.ts
@@ -4,23 +4,24 @@ import * as BluebirdPromise from "bluebird";
 import * as assert from "assert";
 
 import { NotifierFactory } from "../../src/lib/notifiers/NotifierFactory";
-import { GMailNotifier } from "../../src/lib/notifiers/GMailNotifier";
+import { EMailNotifier } from "../../src/lib/notifiers/EMailNotifier";
 import { SmtpNotifier } from "../../src/lib/notifiers/SmtpNotifier";
 import { MailSenderBuilderStub } from "../mocks/notifiers/MailSenderBuilderStub";
 
 
 describe("test notifier factory", function () {
   let mailSenderBuilderStub: MailSenderBuilderStub;
-  it("should build a Gmail Notifier", function () {
+  it("should build a Email Notifier", function () {
     const options = {
-      gmail: {
+      email: {
         username: "abc",
         password: "password",
-        sender: "admin@example.com"
+        sender: "admin@example.com",
+        service: "gmail"
       }
     };
     mailSenderBuilderStub = new MailSenderBuilderStub();
-    assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof GMailNotifier);
+    assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EMailNotifier);
   });
 
   it("should build a SMTP Notifier", function () {
diff --git a/server/test/server/PrivatePages.ts b/server/test/server/PrivatePages.ts
index 8fd9f698..a4421160 100644
--- a/server/test/server/PrivatePages.ts
+++ b/server/test/server/PrivatePages.ts
@@ -52,10 +52,11 @@ describe("Private pages of the server must not be accessible without session", f
         }
       },
       notifier: {
-        gmail: {
+        email: {
           username: "user@example.com",
           password: "password",
-          sender: "admin@example.com"
+          sender: "admin@example.com",
+          service: "gmail"
         }
       }
     };
diff --git a/server/test/server/PublicPages.ts b/server/test/server/PublicPages.ts
index 21f09917..12e4cc32 100644
--- a/server/test/server/PublicPages.ts
+++ b/server/test/server/PublicPages.ts
@@ -52,10 +52,11 @@ describe("Public pages of the server must be accessible without session", functi
         find_time: 5 * 60
       },
       notifier: {
-        gmail: {
+        email: {
           username: "user@example.com",
           password: "password",
-          sender: "admin@example.com"
+          sender: "admin@example.com",
+          service: "gmail"
         }
       }
     };