Commit 7ba26c84 authored by MrMan's avatar MrMan

re-enable prelim tests, disable full login test pending #22

parent f0cd8c0b
......@@ -5,7 +5,7 @@ COPY . .
# Install postfix
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install ca-certificates make postfix \
rsyslog telnet dovecot-imapd dovecot-pop3d
rsyslog telnet dovecot-imapd dovecot-pop3d dovecot-sqlite dovecot-pgsql
RUN ln -s /usr/sbin/postfix /usr/bin/postfix
# Add submission group for postfix to use
......
......@@ -288,7 +288,7 @@ pub struct DovecotConfTemplate {
}
#[derive(Template)]
#[template(path = "config/dovecot/dovecot-sql.conf")]
#[template(path = "config/dovecot/dovecot-sql.conf.ext")]
pub struct DovecotSQLConfTemplate {
filename: String,
generation_time: String,
......@@ -304,7 +304,7 @@ impl SupportsDovecotAuth for SQLiteDB {
fn dovecot_config_dir(&self) -> Result<&'static str, Error> { Ok("/etc/dovecot") }
fn dovecot_config_filename(&self) -> Result<&'static str, Error> { Ok("dovecot.conf") }
fn dovecot_sql_config_filename(&self) -> Result<&'static str, Error> { Ok("dovecot-sql.conf") }
fn dovecot_sql_config_filename(&self) -> Result<&'static str, Error> { Ok("dovecot-sql.conf.ext") }
fn write_dovecot_config_files(&self) -> Result<(), Error> {
// Create the dovecot config directory if it doesn't exist
......@@ -335,7 +335,7 @@ impl SupportsDovecotAuth for SQLiteDB {
let template = DovecotConfTemplate {
filename: config_filename.to_string(),
generation_time: Local::now().to_string(),
sql_config_file_abs_path: sql_config_file_abs_path_str,
sql_config_file_abs_path: sql_config_file_abs_path_str.clone(),
abs_db_path: self.make_absolute_db_path()?
};
......@@ -344,19 +344,17 @@ impl SupportsDovecotAuth for SQLiteDB {
let mut config_file = File::create(config_file_abs_path)?;
config_file.write_all(template.render()?.as_bytes())?;
// // Build the template for the SQLite config file
// let template = DovecotSQLConfTemplate {
// filename: config_filename.to_string(),
// generation_time: Local::now().to_string(),
// sql_config_file_abs_path: sql_config_file_abs_path,
// abs_db_path: self.make_absolute_db_path()?
// };
// // Write the main config file to disk
// debug!("writing dovecot config file @ [{}]", sql_config_file_abs_path);
// let mut config_file = File::create(sql_config_file_abs_path)?;
// config_file.write_all(template.render()?.as_bytes())?;
// Build the template for the SQLite config file
let template = DovecotSQLConfTemplate {
filename: config_filename.to_string(),
generation_time: Local::now().to_string(),
abs_db_path: self.make_absolute_db_path()?
};
// Write the main config file to disk
debug!("writing dovecot sql config file @ [{}]", sql_config_file_abs_path_str);
let mut config_file = File::create(sql_config_file_abs_path)?;
config_file.write_all(template.render()?.as_bytes())?;
Ok(())
}
......
### WARNING: this file was auto-generated by postmgr, avoid editing it by hand ###
# Filename: {{ filename }}
# Generated at: {{ generation_time }}
driver = sqlite
connect = {{ abs_db_path }}
password_query = SELECT username, domain, password \
FROM mailbox_users WHERE username = '%n' AND domain = '%d'
user_query = SELECT home, uid, gid FROM users WHERE username = '%n' AND domain = '%d'
# For using doveadm -A:
iterate_query = SELECT username, domain FROM users
\ No newline at end of file
### WARNING: this file was auto-generated by postmgr, avoid editing it by hand ###
# Filename: {{ filename }}
# Generated at: {{ generation_time }}
# This file is commonly accessed via passdb {} or userdb {} section in
# conf.d/auth-sql.conf.ext
# This file is opened as root, so it should be owned by root and mode 0600.
#
# http://wiki2.dovecot.org/AuthDatabase/SQL
#
# For the sql passdb module, you'll need a database with a table that
# contains fields for at least the username and password. If you want to
# use the [email protected] syntax, you might want to have a separate domain
# field as well.
#
# If your users all have the same uig/gid, and have predictable home
# directories, you can use the static userdb module to generate the home
# dir based on the username and domain. In this case, you won't need fields
# for home, uid, or gid in the database.
#
# If you prefer to use the sql userdb module, you'll want to add fields
# for home, uid, and gid. Here is an example table:
#
# CREATE TABLE users (
# username VARCHAR(128) NOT NULL,
# domain VARCHAR(128) NOT NULL,
# password VARCHAR(64) NOT NULL,
# home VARCHAR(255) NOT NULL,
# uid INTEGER NOT NULL,
# gid INTEGER NOT NULL,
# active CHAR(1) DEFAULT 'Y' NOT NULL
# );
# Database driver: mysql, pgsql, sqlite
#driver =
# Database connection string. This is driver-specific setting.
#
# HA / round-robin load-balancing is supported by giving multiple host
# settings, like: host=sql1.host.org host=sql2.host.org
#
# pgsql:
# For available options, see the PostgreSQL documention for the
# PQconnectdb function of libpq.
# Use maxconns=n (default 5) to change how many connections Dovecot can
# create to pgsql.
#
# mysql:
# Basic options emulate PostgreSQL option names:
# host, port, user, password, dbname
#
# But also adds some new settings:
# client_flags - See MySQL manual
# ssl_ca, ssl_ca_path - Set either one or both to enable SSL
# ssl_cert, ssl_key - For sending client-side certificates to server
# ssl_cipher - Set minimum allowed cipher security (default: HIGH)
# ssl_verify_server_cert - Verify that the name in the server SSL certificate
# matches the host (default: no)
# option_file - Read options from the given file instead of
# the default my.cnf location
# option_group - Read options from the given group (default: client)
#
# You can connect to UNIX sockets by using host: host=/var/run/mysql.sock
# Note that currently you can't use spaces in parameters.
#
# sqlite:
# The path to the database file.
#
# Examples:
# connect = host=192.168.1.1 dbname=users
# connect = host=sql.example.com dbname=virtual user=virtual password=blarg
# connect = /etc/dovecot/authdb.sqlite
#
#connect =
# Default password scheme.
#
# List of supported schemes is in
# http://wiki2.dovecot.org/Authentication/PasswordSchemes
#
#default_pass_scheme = MD5
# passdb query to retrieve the password. It can return fields:
# password - The user's password. This field must be returned.
# user - [email protected] from the database. Needed with case-insensitive lookups.
# username and domain - An alternative way to represent the "user" field.
#
# The "user" field is often necessary with case-insensitive lookups to avoid
# e.g. "name" and "nAme" logins creating two different mail directories. If
# your user and domain names are in separate fields, you can return "username"
# and "domain" fields instead of "user".
#
# The query can also return other fields which have a special meaning, see
# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields
#
# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables
# for full list):
# %u = entire [email protected]
# %n = user part of [email protected]
# %d = domain part of [email protected]
#
# Note that these can be used only as input to SQL query. If the query outputs
# any of these substitutions, they're not touched. Otherwise it would be
# difficult to have eg. usernames containing '%' characters.
#
# Example:
# password_query = SELECT userid AS user, pw AS password \
# FROM users WHERE userid = '%u' AND active = 'Y'
#
#password_query = \
# SELECT username, domain, password \
# FROM users WHERE username = '%n' AND domain = '%d'
# userdb query to retrieve the user information. It can return fields:
# uid - System UID (overrides mail_uid setting)
# gid - System GID (overrides mail_gid setting)
# home - Home directory
# mail - Mail location (overrides mail_location setting)
#
# None of these are strictly required. If you use a single UID and GID, and
# home or mail directory fits to a template string, you could use userdb static
# instead. For a list of all fields that can be returned, see
# http://wiki2.dovecot.org/UserDatabase/ExtraFields
#
# Examples:
# user_query = SELECT home, uid, gid FROM users WHERE userid = '%u'
# user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'
# user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'
#
#user_query = \
# SELECT home, uid, gid \
# FROM users WHERE username = '%n' AND domain = '%d'
# If you wish to avoid two SQL lookups (passdb + userdb), you can use
# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll
# also have to return userdb fields in password_query prefixed with "userdb_"
# string. For example:
#password_query = \
# SELECT userid AS user, password, \
# home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \
# FROM users WHERE userid = '%u'
# Query to get a list of all usernames.
#iterate_query = SELECT username AS user FROM users
driver = sqlite
connect = {{ abs_db_path }}
password_query = SELECT username, domain, password \
FROM mailbox_users WHERE username = '%n' AND domain = '%d'
user_query = SELECT home, uid, gid FROM users WHERE username = '%n' AND domain = '%d'
# For using doveadm -A:
iterate_query = SELECT username, domain FROM users
\ No newline at end of file
passdb {
driver = sql
args = {{ sql_config_file_abs_path }}
}
userdb {
driver = sql
args = {{ sql_config_file_abs_path }}
}
mail_uid = vmail
mail_gid = vmail
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
}
auth_mechanisms = plain login
\ No newline at end of file
......@@ -194,6 +194,68 @@ pub fn read_ehlo(reader: &mut BufReader<TcpStream>) -> &mut BufReader<TcpStream>
reader
}
/// Send MAIL FROM and verify response
fn send_mail_from(
address: &str,
mut stream: TcpStream,
mut reader: BufReader<TcpStream>
) -> (TcpStream, BufReader<TcpStream>) {
let cmd = format!("MAIL FROM: {}\n", address);
stream.write(cmd.as_bytes()).unwrap();
let mut line = String::new();
reader.read_line(&mut line).unwrap();
println!("MAIL FROM response: [{}]", line.trim());
assert!(line.contains("250"), "MAIL FROM response succeeded");
(stream, reader)
}
/// Send RCPT TO and verify response
fn send_rcpt_to(
recipient: &str,
mut stream: TcpStream,
mut reader: BufReader<TcpStream>
) -> (TcpStream, BufReader<TcpStream>) {
let cmd = format!("RCPT TO: {}\n", recipient);
stream.write(cmd.as_bytes()).unwrap();
let mut line = String::new();
reader.read_line(&mut line).unwrap();
println!("RCPT TO response: [{}]", line.trim());
assert!(line.contains("250"), "RCPT TO response succeeded");
(stream, reader)
}
/// Send a test message with a given subject/body
fn send_test_message(
subject: &str,
body: &str,
mut stream: TcpStream,
mut reader: BufReader<TcpStream>
) -> (TcpStream, BufReader<TcpStream>) {
let mut line = String::new();
// Send data start
let cmd = format!("DATA\n");
stream.write(cmd.as_bytes()).unwrap();
reader.read_line(&mut line).unwrap();
// Postfix sends 354 here to indicate how to end the data
println!("DATA response: [{}]", line.trim());
assert!(line.contains("354") && line.contains("End data with"), "DATA command succeeded");
line.clear();
// Send the test
let cmd = format!("subject: {}\n{}\n{}", subject, body, CR_LF_END);
stream.write(cmd.as_bytes()).unwrap();
reader.read_line(&mut line).unwrap();
println!("Message send response: [{}]", line.trim());
assert!(line.contains("250") && line.contains("Ok:"), "Message successfully queued");
(stream, reader)
}
/// Send EHLO as a client
pub fn send_ehlo(
hostname: &str,
......
......@@ -19,147 +19,83 @@ use common::{
login_with_username_password,
};
#[test]
#[ignore]
/// Test basic SMTP connectivity
pub fn test_connect() {
// Start postmgr in a docker container
let postmgr = DockerizedPostmgr::new().start();
/// Send MAIL FROM and verify response
fn send_mail_from(
address: &str,
mut stream: TcpStream,
mut reader: BufReader<TcpStream>
) -> (TcpStream, BufReader<TcpStream>) {
let cmd = format!("MAIL FROM: {}\n", address);
stream.write(cmd.as_bytes()).unwrap();
let mut line = String::new();
reader.read_line(&mut line).unwrap();
println!("MAIL FROM response: [{}]", line.trim());
assert!(line.contains("250"), "MAIL FROM response succeeded");
(stream, reader)
}
// Connect to the postmgr over port 25
let mut stream = TcpStream::connect(postmgr.get_smtp_address()).unwrap();
let mut reader = BufReader::new(stream.try_clone().unwrap());
/// Send RCPT TO and verify response
fn send_rcpt_to(
recipient: &str,
mut stream: TcpStream,
mut reader: BufReader<TcpStream>
) -> (TcpStream, BufReader<TcpStream>) {
let cmd = format!("RCPT TO: {}\n", recipient);
stream.write(cmd.as_bytes()).unwrap();
let mut line = String::new();
reader.read_line(&mut line).unwrap();
println!("RCPT TO response: [{}]", line.trim());
assert!(line.contains("250"), "RCPT TO response succeeded");
(stream, reader)
// Read the response (connection should send SMTP response)
read_ehlo(&mut reader);
}
/// Send a test message with a given subject/body
fn send_test_message(
subject: &str,
body: &str,
mut stream: TcpStream,
mut reader: BufReader<TcpStream>
) -> (TcpStream, BufReader<TcpStream>) {
let mut line = String::new();
// Send data start
let cmd = format!("DATA\n");
stream.write(cmd.as_bytes()).unwrap();
reader.read_line(&mut line).unwrap();
// Postfix sends 354 here to indicate how to end the data
println!("DATA response: [{}]", line.trim());
assert!(line.contains("354") && line.contains("End data with"), "DATA command succeeded");
line.clear();
// Send the test
let cmd = format!("subject: {}\n{}\n{}", subject, body, CR_LF_END);
stream.write(cmd.as_bytes()).unwrap();
reader.read_line(&mut line).unwrap();
println!("Message send response: [{}]", line.trim());
assert!(line.contains("250") && line.contains("Ok:"), "Message successfully queued");
(stream, reader)
#[test]
#[ignore]
/// Test basic SMTP email sending
pub fn test_email_send() {
// Start postmgr in a docker container
let postmgr = DockerizedPostmgr::new().start();
// Connect to the postmgr over port 25
let stream = TcpStream::connect(postmgr.get_smtp_address()).unwrap();
let mut reader = BufReader::new(stream.try_clone().unwrap());
// Ensure EHLO was received
read_ehlo(&mut reader);
// MAIL FROM
let (stream, reader) = send_mail_from(POSTMASTER_AT_LOCALHOST, stream, reader);
// RCPT TO
let (stream, reader) = send_rcpt_to(POSTMASTER_AT_LOCALHOST, stream, reader);
// send message contents
send_test_message("test email", "this is a test", stream, reader);
}
// #[test]
// #[ignore]
// /// Test basic SMTP connectivity
// pub fn test_connect() {
// pub fn test_user_creation() {
// /// Test user creation & interaction over basic SMTP relay
// // Start postmgr in a docker container
// let postmgr = DockerizedPostmgr::new().start();
// // Connect to the postmgr over port 25
// let mut stream = TcpStream::connect(postmgr.get_smtp_address()).unwrap();
// let mut reader = BufReader::new(stream.try_clone().unwrap());
// // TODO: Create a new user through the API
// let base_url = postmgr.get_http_address();
// let client = reqwest::Client::new();
// // Read the response (connection should send SMTP response)
// read_ehlo(&mut reader);
// }
// // Add a new user
// let url = format!("http://{}/{}", &base_url, "api/v1/users");
// let test_user = fixtures::test_user();
// let mut resp = client.post(url.as_str())
// .json(&test_user)
// .send()
// .expect("failed to POST new user");
// #[test]
// #[ignore]
// /// Test basic SMTP email sending
// pub fn test_email_send() {
// // Start postmgr in a docker container
// let postmgr = DockerizedPostmgr::new().start();
// assert_eq!(resp.status(), 201, "new user creation worked");
// // Connect to the postmgr over port 25
// let stream = TcpStream::connect(postmgr.get_smtp_address()).unwrap();
// let mut reader = BufReader::new(stream.try_clone().unwrap());
// // Connect to the postmgr over (unprotected) port 25
// let (stream, mut reader) = postmgr.connect_tcp().unwrap();
// // Ensure EHLO was received
// read_ehlo(&mut reader);
// // MAIL FROM
// let (stream, reader) = send_mail_from(POSTMASTER_AT_LOCALHOST, stream, reader);
// // RCPT TO
// let (stream, reader) = send_rcpt_to(POSTMASTER_AT_LOCALHOST, stream, reader);
// // send message contents
// send_test_message("test email", "this is a test", stream, reader);
// // Send a client EHLO
// let (stream, reader) = send_ehlo(DOMAIN_NAME, stream, reader);
// // Login with username/pw
// let user = fixtures::test_user();
// login_with_username_password(
// &user.username,
// &user.password,
// stream,
// reader,
// );
// }
#[test]
#[ignore]
pub fn test_user_creation() {
/// Test user creation & interaction over basic SMTP relay
// Start postmgr in a docker container
let postmgr = DockerizedPostmgr::new().start();
// TODO: Create a new user through the API
let base_url = postmgr.get_http_address();
let client = reqwest::Client::new();
// Add a new user
let url = format!("http://{}/{}", &base_url, "api/v1/users");
let test_user = fixtures::test_user();
let mut resp = client.post(url.as_str())
.json(&test_user)
.send()
.expect("failed to POST new user");
assert_eq!(resp.status(), 201, "new user creation worked");
// Connect to the postmgr over (unprotected) port 25
let (stream, mut reader) = postmgr.connect_tcp().unwrap();
// Ensure EHLO was received
read_ehlo(&mut reader);
// Send a client EHLO
let (stream, reader) = send_ehlo(DOMAIN_NAME, stream, reader);
// Login with username/pw
let user = fixtures::test_user();
login_with_username_password(
&user.username,
&user.password,
stream,
reader,
);
}
// TODO: test create user & login
// TODO: test sending email between two local created
// TODO: test sending email between two local accounts
// TODO: test user delete (after deleting through the API a user should be unable to log in)
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