Adding support for password expiration. Please read README.password_expiration for more details

pull/199/head
Damien Martins 6 years ago
parent c3d3898eb2
commit e786609aa9

@ -0,0 +1,38 @@
*Description
This extension adds support for password expiration.
It is designed to have expiration on users passwords. An email is sent when the password is expiring in 30 days, then 14 days, then 7 days.
It is strongly inspired by https://abridge2devnull.com/posts/2014/09/29/dovecot-user-password-expiration-notifications-updated-4122015/, and adapted to fit with Postfix Admin & Roundcube's password plugin
*Installation
Perform the following changes:
**Changes in MySQL/MariaDB mailbox table (as defined in $CONF['database_tables'] from config.inc.php):
You are invited to backup your DB first, and ensure the table name is correct.
Execute the attached SQL script (password_expiration.sql) that will add the required columns. The expiration value for existing users will be set to 90 days. If you want a different value, edit the last line in the script and replace 90 by the required value.
**Changes in Postfix Admin :
To enable password expiration, add the following to your config.inc.php file:
$CONF['password_expiration_enabled'] = 'YES';
Do not forget to set the expiration value (in days)
$CONF['password_expiration_value'] = '90';
All my tests are performed using $CONF['encrypt'] = 'md5crypt';
**If you are using Roundcube's password plugin, you should also adapt the $config['password_query'] value.
I recommend to use:
$config['password_query'] = 'UPDATE mailbox SET password=%c, modified=now(),pw_expires_on=now() + interval 90 day, thirty=0,fourteen=0,seven=0 where username=%u';
of cource you may adapt to the expected expiration value
All my tests are performed using $config['password_algorithm'] = 'md5-crypt';
**Changes in Dovecot (adapt if you use another LDA)
Edit dovecot-mysql.conf file, and replace the user_query (and only this one) by this query:
user_query = SELECT concat('/var/vmail/', maildir) as home, concat('maildir:/var/vmail/', maildir) as mail, 20001 AS uid, 20001 AS gid, concat('dirsize:storage=', quota) AS quota FROM mailbox WHERE username = '%u' AND active = '1' AND pw_expires_on > now()
if course you may require to adapt the uid, gid, maildir and table to your setup
**Changes in system
You need to have a script running on a daily basis to check password expiration and send emails 30, 14 and 7 days before password expiration (script attached: check_mailpass_expiration.sh).
Edit the script to adapt the variables to your setup.
Ensure the user running check_mailpass_expiration.sh is allowed to access (read-write) your database.

@ -0,0 +1,34 @@
#!/bin/bash
#Adapt to your setup
POSTFIX_DB="postfix_test"
POSTFIX_USER="postfixadmin"
POSTFIX_PASSWORD="my_password_is_strong"
#All the rest should be OK
QUERY30DAYS="SELECT username,pw_expires_on FROM mailbox WHERE pw_expires_on > now() + interval 29 DAY AND pw_expires_on < now() + interval 30 day AND thirty = false;"
QUERY14DAYS="SELECT username,pw_expires_on FROM mailbox WHERE pw_expires_on > now() + interval 13 DAY AND pw_expires_on < now() + interval 14 day AND fourteen = false;"
QUERY7DAYS="SELECT username,pw_expires_on FROM mailbox WHERE pw_expires_on > now() + interval 6 DAY AND pw_expires_on < now() + interval 7 day AND seven = false;"
function notifyThirtyDays() {
mysql -B -u "$POSTFIX_USER" -p"$POSTFIX_PASSWORD" "$POSTFIX_DB" -e "$QUERY30DAYS" | while read -a RESULT; do
echo -e "Dear User, \n Your password will expire on ${RESULT[1]}" | mail -s "Password 30 days before expiration notication" -r noreply@eyetech.fr ${RESULT[0]}
echo "UPDATE mailbox SET thirty = true WHERE username = '${RESULT[0]}';" | mysql -u postfix postfix_test;done
}
function notifyFourteenDays() {
mysql -B -u "$POSTFIX_USER" -p"$POSTFIX_PASSWORD" "$POSTFIX_DB" -e "$QUERY14DAYS" | while read -a RESULT; do
echo -e "Dear User, \n Your password will expire on ${RESULT[1]}" | mail -s "Password 14 days before expiration notication" -r noreply@eyetech.fr ${RESULT[0]}
echo "UPDATE mailbox SET fourteen = true WHERE username = '${RESULT[0]}';" | mysql -u postfix postfix_test;done
}
function notifySevenDays() {
mysql -B -u "$POSTFIX_USER" -p"$POSTFIX_PASSWORD" "$POSTFIX_DB" -e "$QUERY7DAYS" | while read -a RESULT; do
echo -e "Dear User, \n Your password will expire on ${RESULT[1]}" | mail -s "Password 7 days before expiraiton notication" -r noreply@eyetech.fr ${RESULT[0]}
echo "UPDATE mailbox SET seven = true WHERE username = '${RESULT[0]}';" | mysql -u postfix postfix_test;done
}
notifyThirtyDays # Execute the function for 30 day notices
notifyFourteenDays # Execute the function for 14 day notices
notifySevenDays # Execute the function for 7 day notices

@ -661,6 +661,12 @@ $CONF['theme_custom_css'] = '';
// change to boolean true to enable xmlrpc
$CONF['xmlrpc_enabled'] = false;
//Account expiration info
//If you want to display the password expiracy status of the accounts (read-only)
//More details in README.password_expiration
$CONF['password_expiration_enable'] = 'YES';
$CONF['password_expiration_value'] = '90';
// If you want to keep most settings at default values and/or want to ensure
// that future updates work without problems, you can use a separate config
// file (config.local.php) instead of editing this file and override some

@ -1865,7 +1865,7 @@ function db_delete($table, $where, $delete, $additionalwhere='') {
* @param array (optional) - array of fields to set to now() - default: array('created', 'modified')
* @return int - number of inserted rows
*/
function db_insert($table, $values, $timestamp = array('created', 'modified')) {
function db_insert ($table, $values, $timestamp = array('created', 'modified'), $timestamp_expiration = array('pw_expires_on') ) {
$table = table_by_key($table);
foreach (array_keys($values) as $key) {
@ -1879,6 +1879,18 @@ function db_insert($table, $values, $timestamp = array('created', 'modified')) {
$values[$key] = "now()";
}
}
if ($table == 'mailbox') {
global $CONF;
if ($CONF['password_expiration_enabled'] == 'YES') {
$expires_warning_values = array('thirty', 'fourteen', 'seven');
foreach($expires_warning_values as $key) {
$values[$key] = escape_string($key) . "=0";
}
foreach($timestamp_expiration as $key) {
$values[$key] = "now() + interval " . $CONF['password_expiration_value'] . " day";
}
}
}
$sql_values = "(" . implode(",", escape_string(array_keys($values))).") VALUES (".implode(",", $values).")";
@ -1927,6 +1939,17 @@ function db_update_q($table, $where, $values, $timestamp = array('modified')) {
$sql_values[$key] = escape_string($key) . "=now()";
}
}
if ($table == 'mailbox') {
global $CONF;
if ($CONF['password_expiration_enabled'] == 'YES') {
$key = 'pw_expires_on';
$sql_values[$key] = escape_string($key) . "=now() + interval " . $CONF['password_expiration_value'] . " day";
$expires_warning_values = array('thirty', 'fourteen', 'seven');
foreach($expires_warning_values as $key) {
$sql_values[$key] = escape_string($key) . "=0";
}
}
}
$sql="UPDATE $table SET " . implode(",", $sql_values) . " WHERE $where";

@ -407,6 +407,7 @@ $PALANG['pFetchmail_desc_returned_text'] = 'Text message from last polling';
$PALANG['dateformat_pgsql'] = 'YYYY-mm-dd'; # translators: rearrange to your local date format, but make sure it's a valid PostgreSQL date format
$PALANG['dateformat_mysql'] = '%Y-%m-%d'; # translators: rearrange to your local date format, but make sure it's a valid MySQL date format
$PALANG['password_expiration'] = 'Pass expires';
$PALANG['please_keep_this_as_last_entry'] = ''; # needed for language-check.sh
/* vim: set expandtab ft=php softtabstop=3 tabstop=3 shiftwidth=3: */

@ -398,6 +398,7 @@ $PALANG['pFetchmail_desc_date'] = 'Date de la dernière vérification/changement
$PALANG['pFetchmail_desc_returned_text'] = 'Message de la dernière vérification';
$PALANG['dateformat_pgsql'] = 'dd-mm-YYYY';
$PALANG['dateformat_mysql'] = '%d-%m-%Y';
$PALANG['password_expiration'] = 'Expiration du mot de passe';
$PALANG['please_keep_this_as_last_entry'] = ''; # needed for language-check.sh
/* vim: set expandtab ft=php softtabstop=3 tabstop=3 shiftwidth=3: */

@ -49,6 +49,7 @@ class MailboxHandler extends PFAHandler {
'token_validity' => pacol(1, 0, 0, 'ts', '' , '', date("Y-m-d H:i:s",time())),
'created' => pacol(0, 0, 1, 'ts', 'created' , '' ),
'modified' => pacol(0, 0, 1, 'ts', 'last_modified' , '' ),
'pw_expires_on' => pacol( 0, 0, 1, 'ts', 'password_expiration' , '' ),
# TODO: add virtual 'notified' column and allow to display who received a vacation response?
);

@ -0,0 +1,5 @@
ALTER TABLE mailbox ADD COLUMN pw_expires_on TIMESTAMP DEFAULT now() not null;
ALTER TABLE mailbox ADD COLUMN thirty boolean not null DEFAULT false;
ALTER TABLE mailbox ADD COLUMN fourteen boolean not null DEFAULT false;
ALTER TABLE mailbox ADD COLUMN seven boolean not null DEFAULT false;
UPDATE mailbox set pw_expires_on = now() + interval 90 day;

@ -165,6 +165,7 @@ $tAlias = $handler->result();
#
$display_mailbox_aliases = Config::bool('alias_control_admin');
$password_expiration = Config::bool('password_expiration');
# build the sql query
$sql_select = "SELECT $table_mailbox.* ";
@ -190,6 +191,10 @@ if ($display_mailbox_aliases) {
$sql_join .= " LEFT JOIN $table_alias ON $table_mailbox.username=$table_alias.address ";
}
if ($password_expiration) {
$sql_select .= ", $table_mailbox.pw_expires_on as password_expiration ";
}
if (Config::bool('vacation_control_admin')) {
$table_vacation = table_by_key('vacation');
$sql_select .= ", $table_vacation.active AS v_active ";

@ -13,6 +13,9 @@
<td>{$PALANG.name}</td>
{if $CONF.quota===YES}<td>{$PALANG.pOverview_mailbox_quota}</td>{/if}
<td>{$PALANG.last_modified}</td>
{if $CONF.password_expiration===YES}
<td>{$PALANG.password_expiration}</td>
{/if}
<td>{$PALANG.active}</td>
{assign var="colspan" value="`$colspan-6`"}
<td colspan="{$colspan}">&nbsp;</td>
@ -74,6 +77,9 @@
</td>
{/if}
<td>{$item.modified}</td>
{if $CONF.password_expiration===YES}
<td>{$item.password_expiration}</td>
{/if}
<td><a href="{#url_editactive#}mailbox&amp;id={$item.username|escape:"url"}&amp;active={if ($item.active==0)}1{else}0{/if}&amp;token={$smarty.session.PFA_token|escape:"url"}"
>{if $item.active==1}{$PALANG.YES}{else}{$PALANG.NO}{/if}</a></td>
{if $CONF.vacation_control_admin===YES && $CONF.vacation===YES}

Loading…
Cancel
Save