Merge pull request #97 from clems4ever/smtp-notifier
Add SMTP notifier as an available option in configurationpull/98/head
commit
92ef190202
|
@ -29,3 +29,5 @@ dist/
|
||||||
|
|
||||||
# Specific files
|
# Specific files
|
||||||
/config.yml
|
/config.yml
|
||||||
|
|
||||||
|
example/ldap/private.ldif
|
||||||
|
|
25
README.md
25
README.md
|
@ -39,14 +39,15 @@ as 2nd factor.
|
||||||
address.
|
address.
|
||||||
* Access restriction after too many authentication attempts.
|
* Access restriction after too many authentication attempts.
|
||||||
* Session management using Redis key/value store.
|
* Session management using Redis key/value store.
|
||||||
|
* User-defined access control per subdomain and resource.
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
If you don't have any LDAP and/or nginx setup yet, I advise you to follow the
|
If you don't have any LDAP and/or nginx setup yet, I advise you to follow the
|
||||||
[Getting Started](#Getting-started) section. That way, you can test it right away
|
[Getting Started](#Getting-started) section. That way, you can test it right away
|
||||||
without even configure anything.
|
without even configuring anything.
|
||||||
|
|
||||||
Otherwise here are the available steps to deploy **Authelia** on your machine given
|
Otherwise, here are the available steps to deploy **Authelia** on your machine given
|
||||||
your configuration file is **/path/to/your/config.yml**. Note that you can create your
|
your configuration file is **/path/to/your/config.yml**. Note that you can create your
|
||||||
own the configuration file from [config.template.yml] at the root of the repo.
|
own the configuration file from [config.template.yml] at the root of the repo.
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ gave *Docker version 17.03.1-ce, build c6d412e*.
|
||||||
gave *docker-compose version 1.14.0, build c7bdf9e*.
|
gave *docker-compose version 1.14.0, build c7bdf9e*.
|
||||||
|
|
||||||
#### Available port
|
#### Available port
|
||||||
Make sure you don't have anything listening on port 8080.
|
Make sure you don't have anything listening on port 8080 (webserver) and 8085 (webmail).
|
||||||
|
|
||||||
#### Subdomain aliases
|
#### Subdomain aliases
|
||||||
|
|
||||||
|
@ -141,10 +142,16 @@ You can find an example of the configuration of the LDAP backend in [config.temp
|
||||||
### Second factor with TOTP
|
### Second factor with TOTP
|
||||||
In **Authelia**, you can register a per user TOTP (Time-Based One Time Password) secret before
|
In **Authelia**, you can register a per user TOTP (Time-Based One Time Password) secret before
|
||||||
authenticating. To do that, you need to click on the register button. It will
|
authenticating. To do that, you need to click on the register button. It will
|
||||||
|
<<<<<<< 7a2b45a66fba8ad1862f25cfa727df03d218ba83
|
||||||
send a link to the user email address defined in the LDAP.
|
send a link to the user email address defined in the LDAP.
|
||||||
Since this is an example, no email will be sent, the link is rather delivered in the file
|
Since this is an example, no email will be sent, the link is rather delivered in the file
|
||||||
**/tmp/notifications/notification.txt**. Paste the link in your browser and you'll get
|
**/tmp/notifications/notification.txt**. Paste the link in your browser and you'll get
|
||||||
your secret in QRCode and Base32 format. You can use
|
your secret in QRCode and Base32 format. You can use
|
||||||
|
=======
|
||||||
|
send a link to the user email address stored in LDAP. Since this is an example, the email is sent
|
||||||
|
to a fake email address you can access from the webmail at [http://localhost:8085](http://localhost:8085).
|
||||||
|
Click on **Continue** and you'll get your secret in QRCode and Base32 formats. You can use
|
||||||
|
>>>>>>> Add SMTP notifier as an available option in configuration
|
||||||
[Google Authenticator]
|
[Google Authenticator]
|
||||||
to store them and get the generated tokens with the app.
|
to store them and get the generated tokens with the app.
|
||||||
|
|
||||||
|
@ -155,11 +162,19 @@ to store them and get the generated tokens with the app.
|
||||||
USB security keys. U2F is one of the most secure authentication protocol and is
|
USB security keys. U2F is one of the most secure authentication protocol and is
|
||||||
already available for Google, Facebook, Github accounts and more.
|
already available for Google, Facebook, Github accounts and more.
|
||||||
|
|
||||||
|
<<<<<<< 7a2b45a66fba8ad1862f25cfa727df03d218ba83
|
||||||
Like TOTP, U2F requires you register a security key before authenticating.
|
Like TOTP, U2F requires you register a security key before authenticating.
|
||||||
To do so, click on the register link. This will send a link to the
|
To do so, click on the register link. This will send a link to the
|
||||||
user email address. Since this is an example, no email will be sent, the
|
user email address. Since this is an example, no email will be sent, the
|
||||||
link is rather delivered in the file **/tmp/notifications/notification.txt**. Paste
|
link is rather delivered in the file **/tmp/notifications/notification.txt**. Paste
|
||||||
the link in your browser and you'll be asking to touch the token of your device
|
the link in your browser and you'll be asking to touch the token of your device
|
||||||
|
=======
|
||||||
|
Like TOTP, U2F requires you register your security key before authenticating.
|
||||||
|
To do so, click on the register button. This will send a link to the
|
||||||
|
user email address. Since this is an example, the email is sent
|
||||||
|
to a fake email address you can access from the webmail at [http://localhost:8085](http://localhost:8085).
|
||||||
|
Click on **Continue** and you'll be asking to touch the token of your device
|
||||||
|
>>>>>>> Add SMTP notifier as an available option in configuration
|
||||||
to register. Upon successful registration, you can authenticate using your U2F
|
to register. Upon successful registration, you can authenticate using your U2F
|
||||||
device by simply touching the token. Easy, right?!
|
device by simply touching the token. Easy, right?!
|
||||||
|
|
||||||
|
@ -169,8 +184,8 @@ device by simply touching the token. Easy, right?!
|
||||||
With **Authelia**, you can also reset your password in no time. Click on the
|
With **Authelia**, you can also reset your password in no time. Click on the
|
||||||
**Forgot password?** link in the login page, provide the username of the user requiring
|
**Forgot password?** link in the login page, provide the username of the user requiring
|
||||||
a password reset and **Authelia** will send an email with an link to the user
|
a password reset and **Authelia** will send an email with an link to the user
|
||||||
email address. For the sake of the example, the email is delivered in the file
|
email address. For the sake of the example, the email is delivered in a fake webmail deployed
|
||||||
**/tmp/notifications/notification.txt**.
|
for you and accessible at [http://localhost:8085](http://localhost:8085).
|
||||||
Paste the link in your browser and you should be able to reset the password.
|
Paste the link in your browser and you should be able to reset the password.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/clems4ever/authelia/master/images/reset_password.png" width="400">
|
<img src="https://raw.githubusercontent.com/clems4ever/authelia/master/images/reset_password.png" width="400">
|
||||||
|
|
|
@ -182,12 +182,15 @@ storage:
|
||||||
# registration or a TOTP registration.
|
# registration or a TOTP registration.
|
||||||
# Use only an available configuration: filesystem, gmail
|
# Use only an available configuration: filesystem, gmail
|
||||||
notifier:
|
notifier:
|
||||||
# For testing purpose, notifications can be sent in a file
|
|
||||||
filesystem:
|
|
||||||
filename: /var/lib/authelia/notifications/notification.txt
|
|
||||||
|
|
||||||
# Use your gmail account to send the notifications. You can use an app password.
|
# Use your gmail account to send the notifications. You can use an app password.
|
||||||
# gmail:
|
# gmail:
|
||||||
# username: user@example.com
|
# username: user@example.com
|
||||||
# password: yourpassword
|
# password: yourpassword
|
||||||
|
|
||||||
|
# Use a SMTP server for sending notifications
|
||||||
|
smtp:
|
||||||
|
username: test
|
||||||
|
password: test
|
||||||
|
secure: false
|
||||||
|
host: 'smtp'
|
||||||
|
port: 1025
|
||||||
|
|
|
@ -162,12 +162,16 @@ storage:
|
||||||
# registration or a TOTP registration.
|
# registration or a TOTP registration.
|
||||||
# Use only an available configuration: filesystem, gmail
|
# Use only an available configuration: filesystem, gmail
|
||||||
notifier:
|
notifier:
|
||||||
# For testing purpose, notifications can be sent in a file
|
|
||||||
filesystem:
|
|
||||||
filename: /var/lib/authelia/notifications/notification.txt
|
|
||||||
|
|
||||||
# Use your gmail account to send the notifications. You can use an app password.
|
# Use your gmail account to send the notifications. You can use an app password.
|
||||||
# gmail:
|
# gmail:
|
||||||
# username: user@example.com
|
# username: user@example.com
|
||||||
# password: yourpassword
|
# password: yourpassword
|
||||||
|
|
||||||
|
# Use a SMTP server for sending notifications
|
||||||
|
smtp:
|
||||||
|
username: test
|
||||||
|
password: test
|
||||||
|
secure: false
|
||||||
|
host: 'smtp'
|
||||||
|
port: 1025
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.template.yml:/etc/authelia/config.yml:ro
|
- ./config.template.yml:/etc/authelia/config.yml:ro
|
||||||
- /tmp/notifications:/var/lib/authelia/notifications
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
networks:
|
networks:
|
||||||
|
|
|
@ -25,7 +25,7 @@ dn: cn=john,ou=users,dc=example,dc=com
|
||||||
cn: john
|
cn: john
|
||||||
objectclass: inetOrgPerson
|
objectclass: inetOrgPerson
|
||||||
objectclass: top
|
objectclass: top
|
||||||
mail: john.doe@example.com
|
mail: john.doe@authelia.com
|
||||||
sn: John Doe
|
sn: John Doe
|
||||||
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ dn: cn=harry,ou=users,dc=example,dc=com
|
||||||
cn: harry
|
cn: harry
|
||||||
objectclass: inetOrgPerson
|
objectclass: inetOrgPerson
|
||||||
objectclass: top
|
objectclass: top
|
||||||
mail: harry.potter@example.com
|
mail: harry.potter@authelia.com
|
||||||
sn: Harry Potter
|
sn: Harry Potter
|
||||||
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ dn: cn=bob,ou=users,dc=example,dc=com
|
||||||
cn: bob
|
cn: bob
|
||||||
objectclass: inetOrgPerson
|
objectclass: inetOrgPerson
|
||||||
objectclass: top
|
objectclass: top
|
||||||
mail: bob.dylan@example.com
|
mail: bob.dylan@authelia.com
|
||||||
sn: Bob Dylan
|
sn: Bob Dylan
|
||||||
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ dn: cn=james,ou=users,dc=example,dc=com
|
||||||
cn: james
|
cn: james
|
||||||
objectclass: inetOrgPerson
|
objectclass: inetOrgPerson
|
||||||
objectclass: top
|
objectclass: top
|
||||||
mail: james.dean@example.com
|
mail: james.dean@authelia.com
|
||||||
sn: James Dean
|
sn: James Dean
|
||||||
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
|
@ -57,6 +57,6 @@ dn: cn=blackhat,ou=users,dc=example,dc=com
|
||||||
cn: blackhat
|
cn: blackhat
|
||||||
objectclass: inetOrgPerson
|
objectclass: inetOrgPerson
|
||||||
objectclass: top
|
objectclass: top
|
||||||
mail: billy.blackhat@example.com
|
mail: billy.blackhat@authelia.com
|
||||||
sn: Billy BlackHat
|
sn: Billy BlackHat
|
||||||
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
smtp:
|
||||||
|
image: schickling/mailcatcher
|
||||||
|
ports:
|
||||||
|
- "8085:1080"
|
||||||
|
networks:
|
||||||
|
- example-network
|
|
@ -66,6 +66,7 @@
|
||||||
"@types/randomstring": "^1.1.5",
|
"@types/randomstring": "^1.1.5",
|
||||||
"@types/redis": "^2.6.0",
|
"@types/redis": "^2.6.0",
|
||||||
"@types/request": "0.0.46",
|
"@types/request": "0.0.46",
|
||||||
|
"@types/request-promise": "^4.1.37",
|
||||||
"@types/selenium-webdriver": "^3.0.4",
|
"@types/selenium-webdriver": "^3.0.4",
|
||||||
"@types/sinon": "^2.2.1",
|
"@types/sinon": "^2.2.1",
|
||||||
"@types/speakeasy": "^2.0.1",
|
"@types/speakeasy": "^2.0.1",
|
||||||
|
@ -94,6 +95,7 @@
|
||||||
"proxyquire": "^1.8.0",
|
"proxyquire": "^1.8.0",
|
||||||
"query-string": "^4.3.4",
|
"query-string": "^4.3.4",
|
||||||
"request": "^2.81.0",
|
"request": "^2.81.0",
|
||||||
|
"request-promise": "^4.2.2",
|
||||||
"selenium-webdriver": "^3.5.0",
|
"selenium-webdriver": "^3.5.0",
|
||||||
"should": "^11.1.1",
|
"should": "^11.1.1",
|
||||||
"sinon": "^2.3.8",
|
"sinon": "^2.3.8",
|
||||||
|
|
|
@ -9,5 +9,6 @@ docker-compose \
|
||||||
-f example/mongo/docker-compose.yml \
|
-f example/mongo/docker-compose.yml \
|
||||||
-f example/redis/docker-compose.yml \
|
-f example/redis/docker-compose.yml \
|
||||||
-f example/nginx/docker-compose.yml \
|
-f example/nginx/docker-compose.yml \
|
||||||
|
-f example/smtp/docker-compose.yml \
|
||||||
-f example/ldap/docker-compose.admin.yml \
|
-f example/ldap/docker-compose.admin.yml \
|
||||||
-f example/ldap/docker-compose.yml $*
|
-f example/ldap/docker-compose.yml $*
|
||||||
|
|
|
@ -8,4 +8,5 @@ docker-compose \
|
||||||
-f example/mongo/docker-compose.yml \
|
-f example/mongo/docker-compose.yml \
|
||||||
-f example/redis/docker-compose.yml \
|
-f example/redis/docker-compose.yml \
|
||||||
-f example/nginx/docker-compose.yml \
|
-f example/nginx/docker-compose.yml \
|
||||||
|
-f example/smtp/docker-compose.yml \
|
||||||
-f example/ldap/docker-compose.yml $*
|
-f example/ldap/docker-compose.yml $*
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
||||||
|
|
||||||
$DC_SCRIPT build
|
$DC_SCRIPT build
|
||||||
$DC_SCRIPT up -d mongo redis openldap authelia nginx
|
$DC_SCRIPT up -d mongo redis openldap authelia nginx smtp
|
||||||
|
|
|
@ -8,4 +8,5 @@ docker-compose \
|
||||||
-f example/mongo/docker-compose.yml \
|
-f example/mongo/docker-compose.yml \
|
||||||
-f example/redis/docker-compose.yml \
|
-f example/redis/docker-compose.yml \
|
||||||
-f example/nginx/docker-compose.yml \
|
-f example/nginx/docker-compose.yml \
|
||||||
|
-f example/smtp/docker-compose.yml \
|
||||||
-f example/ldap/docker-compose.yml $*
|
-f example/ldap/docker-compose.yml $*
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
DC_SCRIPT=./scripts/example-dockerhub/dc-example.sh
|
DC_SCRIPT=./scripts/example-dockerhub/dc-example.sh
|
||||||
|
|
||||||
#$DC_SCRIPT build
|
#$DC_SCRIPT build
|
||||||
$DC_SCRIPT up -d mongo redis openldap authelia nginx
|
$DC_SCRIPT up -d mongo redis openldap authelia nginx smtp
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
||||||
EXPECTED_SERVICES_COUNT=5
|
EXPECTED_SERVICES_COUNT=6
|
||||||
|
|
||||||
start_services() {
|
start_services() {
|
||||||
$DC_SCRIPT up -d mongo redis openldap authelia nginx
|
$DC_SCRIPT up -d mongo redis openldap authelia nginx smtp
|
||||||
sleep 3
|
sleep 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ expect_services_count() {
|
||||||
run_integration_tests() {
|
run_integration_tests() {
|
||||||
echo "Start services..."
|
echo "Start services..."
|
||||||
start_services
|
start_services
|
||||||
expect_services_count $EXPECTED_SERVICES_COUNT
|
expect_services_count $EXPECTED_SERVICES_COUNT
|
||||||
|
|
||||||
sleep 5
|
sleep 5
|
||||||
./node_modules/.bin/grunt run:integration-tests
|
./node_modules/.bin/grunt run:integration-tests
|
||||||
|
@ -41,13 +41,13 @@ run_other_tests() {
|
||||||
npm install --only=dev
|
npm install --only=dev
|
||||||
./node_modules/.bin/grunt build-dist
|
./node_modules/.bin/grunt build-dist
|
||||||
./scripts/example-commit/deploy-example.sh
|
./scripts/example-commit/deploy-example.sh
|
||||||
expect_services_count 5
|
expect_services_count $EXPECTED_SERVICES_COUNT
|
||||||
}
|
}
|
||||||
|
|
||||||
run_other_tests_docker() {
|
run_other_tests_docker() {
|
||||||
echo "Test dev docker deployment (commands in README)"
|
echo "Test dev docker deployment (commands in README)"
|
||||||
./scripts/example-dockerhub/deploy-example.sh
|
./scripts/example-dockerhub/deploy-example.sh
|
||||||
expect_services_count 5
|
expect_services_count $EXPECTED_SERVICES_COUNT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -71,12 +71,21 @@ export interface GmailNotifierConfiguration {
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SmtpNotifierConfiguration {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
secure: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileSystemNotifierConfiguration {
|
export interface FileSystemNotifierConfiguration {
|
||||||
filename: string;
|
filename: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotifierConfiguration {
|
export interface NotifierConfiguration {
|
||||||
gmail?: GmailNotifierConfiguration;
|
gmail?: GmailNotifierConfiguration;
|
||||||
|
smtp?: SmtpNotifierConfiguration;
|
||||||
filesystem?: FileSystemNotifierConfiguration;
|
filesystem?: FileSystemNotifierConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
import { INotifier } from "../notifiers/INotifier";
|
||||||
|
import { Identity } from "../../../types/Identity";
|
||||||
|
|
||||||
|
import Fs = require("fs");
|
||||||
|
import Path = require("path");
|
||||||
|
import Ejs = require("ejs");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
|
const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8");
|
||||||
|
|
||||||
|
export abstract class AbstractEmailNotifier implements INotifier {
|
||||||
|
|
||||||
|
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void> {
|
||||||
|
const d = {
|
||||||
|
url: link,
|
||||||
|
button_title: "Continue",
|
||||||
|
title: subject
|
||||||
|
};
|
||||||
|
return this.sendEmail(identity.email, subject, Ejs.render(email_template, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract sendEmail(email: string, subject: string, content: string): BluebirdPromise<void>;
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
|
|
||||||
import * as BluebirdPromise from "bluebird";
|
|
||||||
import * as util from "util";
|
|
||||||
import * as Fs from "fs";
|
|
||||||
import { INotifier } from "./INotifier";
|
|
||||||
import { Identity } from "../../../types/Identity";
|
|
||||||
|
|
||||||
import { FileSystemNotifierConfiguration } from "../configuration/Configuration";
|
|
||||||
|
|
||||||
export class FileSystemNotifier implements INotifier {
|
|
||||||
private filename: string;
|
|
||||||
|
|
||||||
constructor(options: FileSystemNotifierConfiguration) {
|
|
||||||
this.filename = options.filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void> {
|
|
||||||
const content = util.format("Date: %s\nUser: %s\nSubject: %s\nLink: %s", new Date().toString(), identity.userid,
|
|
||||||
subject, link);
|
|
||||||
const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile);
|
|
||||||
return writeFilePromised(this.filename, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
|
|
||||||
import * as BluebirdPromise from "bluebird";
|
import * as BluebirdPromise from "bluebird";
|
||||||
import * as fs from "fs";
|
|
||||||
import * as ejs from "ejs";
|
|
||||||
import nodemailer = require("nodemailer");
|
import nodemailer = require("nodemailer");
|
||||||
|
|
||||||
import { Nodemailer } from "../../../types/Dependencies";
|
import { Nodemailer } from "../../../types/Dependencies";
|
||||||
import { Identity } from "../../../types/Identity";
|
import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier";
|
||||||
import { INotifier } from "../notifiers/INotifier";
|
|
||||||
import { GmailNotifierConfiguration } from "../configuration/Configuration";
|
import { GmailNotifierConfiguration } from "../configuration/Configuration";
|
||||||
import path = require("path");
|
|
||||||
|
|
||||||
const email_template = fs.readFileSync(path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8");
|
export class GMailNotifier extends AbstractEmailNotifier {
|
||||||
|
|
||||||
export class GMailNotifier implements INotifier {
|
|
||||||
private transporter: any;
|
private transporter: any;
|
||||||
|
|
||||||
constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) {
|
constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) {
|
||||||
|
super();
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
service: "gmail",
|
service: "gmail",
|
||||||
auth: {
|
auth: {
|
||||||
|
@ -26,18 +21,12 @@ export class GMailNotifier implements INotifier {
|
||||||
this.transporter = BluebirdPromise.promisifyAll(transporter);
|
this.transporter = BluebirdPromise.promisifyAll(transporter);
|
||||||
}
|
}
|
||||||
|
|
||||||
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void> {
|
sendEmail(email: string, subject: string, content: string) {
|
||||||
const d = {
|
|
||||||
url: link,
|
|
||||||
button_title: "Continue",
|
|
||||||
title: subject
|
|
||||||
};
|
|
||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
from: "authelia@authelia.com",
|
from: "authelia@authelia.com",
|
||||||
to: identity.email,
|
to: email,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
html: ejs.render(email_template, d)
|
html: content
|
||||||
};
|
};
|
||||||
return this.transporter.sendMailAsync(mailOptions);
|
return this.transporter.sendMailAsync(mailOptions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,18 @@ import { Nodemailer } from "../../../types/Dependencies";
|
||||||
import { INotifier } from "./INotifier";
|
import { INotifier } from "./INotifier";
|
||||||
|
|
||||||
import { GMailNotifier } from "./GMailNotifier";
|
import { GMailNotifier } from "./GMailNotifier";
|
||||||
import { FileSystemNotifier } from "./FileSystemNotifier";
|
import { SmtpNotifier } from "./SmtpNotifier";
|
||||||
|
|
||||||
export class NotifierFactory {
|
export class NotifierFactory {
|
||||||
static build(options: NotifierConfiguration, nodemailer: Nodemailer): INotifier {
|
static build(options: NotifierConfiguration, nodemailer: Nodemailer): INotifier {
|
||||||
if ("gmail" in options) {
|
if ("gmail" in options) {
|
||||||
return new GMailNotifier(options.gmail, nodemailer);
|
return new GMailNotifier(options.gmail, nodemailer);
|
||||||
}
|
}
|
||||||
else if ("filesystem" in options) {
|
else if ("smtp" in options) {
|
||||||
return new FileSystemNotifier(options.filesystem);
|
return new SmtpNotifier(options.smtp, nodemailer);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("No available notifier option detected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
|
||||||
|
import * as BluebirdPromise from "bluebird";
|
||||||
|
import Nodemailer = require("nodemailer");
|
||||||
|
|
||||||
|
import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier";
|
||||||
|
import { SmtpNotifierConfiguration } from "../configuration/Configuration";
|
||||||
|
|
||||||
|
export class SmtpNotifier extends AbstractEmailNotifier {
|
||||||
|
private transporter: any;
|
||||||
|
|
||||||
|
constructor(options: SmtpNotifierConfiguration, nodemailer: typeof Nodemailer) {
|
||||||
|
super();
|
||||||
|
const smtpOptions = {
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
secure: options.secure, // upgrade later with STARTTLS
|
||||||
|
auth: {
|
||||||
|
user: options.username,
|
||||||
|
pass: options.password
|
||||||
|
}
|
||||||
|
};
|
||||||
|
console.log(smtpOptions);
|
||||||
|
const transporter = nodemailer.createTransport(smtpOptions);
|
||||||
|
this.transporter = BluebirdPromise.promisifyAll(transporter);
|
||||||
|
|
||||||
|
// verify connection configuration
|
||||||
|
console.log("Checking SMTP server connection.");
|
||||||
|
transporter.verify(function (error, success) {
|
||||||
|
if (error) {
|
||||||
|
throw new Error("Unable to connect to SMTP server. \
|
||||||
|
Please check the service is running and your credentials are correct.");
|
||||||
|
} else {
|
||||||
|
console.log("SMTP Server is ready to take our messages");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEmail(email: string, subject: string, content: string) {
|
||||||
|
const mailOptions = {
|
||||||
|
from: "authelia@authelia.com",
|
||||||
|
to: email,
|
||||||
|
subject: subject,
|
||||||
|
html: content
|
||||||
|
};
|
||||||
|
return this.transporter.sendMail(mailOptions, (error: Error, data: string) => {
|
||||||
|
if (error) {
|
||||||
|
return console.log(error);
|
||||||
|
}
|
||||||
|
console.log("Message sent: %s", JSON.stringify(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,10 @@ Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
When("I click on the link of the email", function () {
|
When("I click on the link of the email", function () {
|
||||||
const notif = Fs.readFileSync("/tmp/notifications/notification.txt").toString();
|
|
||||||
const regexp = new RegExp(/Link: (.+)/);
|
|
||||||
const match = regexp.exec(notif);
|
|
||||||
const link = match[1];
|
|
||||||
const that = this;
|
const that = this;
|
||||||
|
return this.retrieveLatestMail()
|
||||||
return this.driver.get(link);
|
.then(function (link: string) {
|
||||||
|
return that.driver.get(link);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -4,6 +4,8 @@ import Cucumber = require("cucumber");
|
||||||
import Fs = require("fs");
|
import Fs = require("fs");
|
||||||
import Speakeasy = require("speakeasy");
|
import Speakeasy = require("speakeasy");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
import Request = require("request-promise");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
function CustomWorld() {
|
function CustomWorld() {
|
||||||
const that = this;
|
const that = this;
|
||||||
|
@ -49,7 +51,7 @@ function CustomWorld() {
|
||||||
.click();
|
.click();
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return that.driver.sleep(500);
|
return that.driver.sleep(1000);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,18 +71,37 @@ function CustomWorld() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.retrieveLatestMail = function () {
|
||||||
|
return Request({
|
||||||
|
method: "GET",
|
||||||
|
uri: "http://localhost:8085/messages",
|
||||||
|
json: true
|
||||||
|
})
|
||||||
|
.then(function (data: any) {
|
||||||
|
const messageId = data[data.length - 1].id;
|
||||||
|
return Request({
|
||||||
|
method: "GET",
|
||||||
|
uri: `http://localhost:8085/messages/${messageId}.html`
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function (data: any) {
|
||||||
|
const regexp = new RegExp(/<a href="(.+)" class="button">Continue<\/a>/);
|
||||||
|
const match = regexp.exec(data);
|
||||||
|
const link = match[1];
|
||||||
|
return BluebirdPromise.resolve(link);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.registerTotpSecret = function (totpSecretHandle: string) {
|
this.registerTotpSecret = function (totpSecretHandle: string) {
|
||||||
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.className("register-totp")), 4000)
|
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.className("register-totp")), 4000)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return that.driver.findElement(seleniumWebdriver.By.className("register-totp")).click();
|
return that.driver.findElement(seleniumWebdriver.By.className("register-totp")).click();
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
const notif = Fs.readFileSync("/tmp/notifications/notification.txt").toString();
|
return that.retrieveLatestMail();
|
||||||
const regexp = new RegExp(/Link: (.+)/);
|
})
|
||||||
const match = regexp.exec(notif);
|
.then(function (url: string) {
|
||||||
const link = match[1];
|
return that.driver.get(url);
|
||||||
console.log("Link: " + link);
|
|
||||||
return that.driver.get(link);
|
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("secret")), 5000);
|
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("secret")), 5000);
|
||||||
|
@ -140,4 +161,4 @@ function CustomWorld() {
|
||||||
|
|
||||||
Cucumber.defineSupportCode(function ({ setWorldConstructor }) {
|
Cucumber.defineSupportCode(function ({ setWorldConstructor }) {
|
||||||
setWorldConstructor(CustomWorld);
|
setWorldConstructor(CustomWorld);
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,10 +13,12 @@ export function NodemailerMock(): NodemailerMock {
|
||||||
|
|
||||||
export interface NodemailerTransporterMock {
|
export interface NodemailerTransporterMock {
|
||||||
sendMail: sinon.SinonStub;
|
sendMail: sinon.SinonStub;
|
||||||
|
verify: sinon.SinonStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NodemailerTransporterMock() {
|
export function NodemailerTransporterMock() {
|
||||||
return {
|
return {
|
||||||
sendMail: sinon.stub()
|
sendMail: sinon.stub(),
|
||||||
|
verify: sinon.stub()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
|
|
||||||
import * as sinon from "sinon";
|
|
||||||
import * as assert from "assert";
|
|
||||||
import { FileSystemNotifier } from "../../../../src/server/lib/notifiers/FileSystemNotifier";
|
|
||||||
import * as tmp from "tmp";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
|
|
||||||
const NOTIFICATIONS_DIRECTORY = "notifications";
|
|
||||||
|
|
||||||
describe("test FS notifier", function() {
|
|
||||||
let tmpDir: tmp.SynchrounousResult;
|
|
||||||
before(function() {
|
|
||||||
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
after(function() {
|
|
||||||
tmpDir.removeCallback();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should write the notification in a file", function() {
|
|
||||||
const options = {
|
|
||||||
filename: tmpDir.name + "/" + NOTIFICATIONS_DIRECTORY
|
|
||||||
};
|
|
||||||
|
|
||||||
const sender = new FileSystemNotifier(options);
|
|
||||||
const subject = "subject";
|
|
||||||
|
|
||||||
const identity = {
|
|
||||||
userid: "user",
|
|
||||||
email: "user@example.com"
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = "http://test.com";
|
|
||||||
|
|
||||||
return sender.notify(identity, subject, url)
|
|
||||||
.then(function() {
|
|
||||||
const content = fs.readFileSync(options.filename, "UTF-8");
|
|
||||||
assert(content.length > 0);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -5,7 +5,7 @@ import * as assert from "assert";
|
||||||
|
|
||||||
import { NotifierFactory } from "../../../../src/server/lib/notifiers/NotifierFactory";
|
import { NotifierFactory } from "../../../../src/server/lib/notifiers/NotifierFactory";
|
||||||
import { GMailNotifier } from "../../../../src/server/lib/notifiers/GMailNotifier";
|
import { GMailNotifier } from "../../../../src/server/lib/notifiers/GMailNotifier";
|
||||||
import { FileSystemNotifier } from "../../../../src/server/lib/notifiers/FileSystemNotifier";
|
import { SmtpNotifier } from "../../../../src/server/lib/notifiers/SmtpNotifier";
|
||||||
|
|
||||||
import NodemailerMock = require("../mocks/nodemailer");
|
import NodemailerMock = require("../mocks/nodemailer");
|
||||||
|
|
||||||
|
@ -20,17 +20,25 @@ describe("test notifier factory", function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
nodemailerMock = NodemailerMock.NodemailerMock();
|
nodemailerMock = NodemailerMock.NodemailerMock();
|
||||||
nodemailerMock.createTransport.returns(sinon.spy());
|
const transporterMock = NodemailerMock.NodemailerTransporterMock();
|
||||||
|
nodemailerMock.createTransport.returns(transporterMock);
|
||||||
assert(NotifierFactory.build(options, nodemailerMock) instanceof GMailNotifier);
|
assert(NotifierFactory.build(options, nodemailerMock) instanceof GMailNotifier);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should build a FS Notifier", function() {
|
it("should build a SMTP Notifier", function() {
|
||||||
const options = {
|
const options = {
|
||||||
filesystem: {
|
smtp: {
|
||||||
filename: "abc"
|
username: "user",
|
||||||
|
password: "pass",
|
||||||
|
secure: true,
|
||||||
|
host: "localhost",
|
||||||
|
port: 25
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(NotifierFactory.build(options, nodemailerMock) instanceof FileSystemNotifier);
|
nodemailerMock = NodemailerMock.NodemailerMock();
|
||||||
|
const transporterMock = NodemailerMock.NodemailerTransporterMock();
|
||||||
|
nodemailerMock.createTransport.returns(transporterMock);
|
||||||
|
assert(NotifierFactory.build(options, nodemailerMock) instanceof SmtpNotifier);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue