From 4aeedb5a2ea455c503721de29a35e8a3c1fa0a9d Mon Sep 17 00:00:00 2001 From: plegall Date: Thu, 31 Dec 2015 19:59:08 +0100 Subject: feature #392, authentication keys, album notification * On album notification (for a group), sends one distinct email for each user with a new authentication key. * When someone clicks the link with auth= in URL, if the user is not already connected, Piwigo will automatically connect the user. --- admin/album_notification.php | 26 ++-- include/config_default.inc.php | 4 + include/constants.php | 2 + include/functions_mail.inc.php | 30 +++-- include/functions_user.inc.php | 132 +++++++++++++++++++++ include/user.inc.php | 6 + .../template/mail/text/html/cat_group_info.tpl | 2 +- 7 files changed, 181 insertions(+), 21 deletions(-) diff --git a/admin/album_notification.php b/admin/album_notification.php index cafaad170..4dd578b1a 100644 --- a/admin/album_notification.php +++ b/admin/album_notification.php @@ -54,6 +54,8 @@ if (isset($_POST['submitEmail']) and !empty($_POST['group'])) is empty find child representative_picture_id */ if (!empty($category['representative_picture_id'])) { + $img = array(); + $query = ' SELECT id, file, path, representative_ext FROM '.IMAGES_TABLE.' @@ -65,21 +67,19 @@ SELECT id, file, path, representative_ext { $element = pwg_db_fetch_assoc($result); - $img_url = ''; + $img = array( + 'link' => make_picture_url( + array( + 'image_id' => $element['id'], + 'image_file' => $element['file'], + 'category' => $category + ) + ), + 'src' => DerivativeImage::url(IMG_THUMB, $element), + ); } } - if (!isset($img_url)) - { - $img_url = ''; - } - pwg_mail_group( $_POST['group'], array( @@ -90,7 +90,7 @@ SELECT id, file, path, representative_ext array( 'filename' => 'cat_group_info', 'assign' => array( - 'IMG_URL' => $img_url, + 'IMG' => $img, 'CAT_NAME' => trigger_change('render_category_name', $category['name'], 'admin_cat_list'), 'LINK' => make_index_url(array( 'category' => array( diff --git a/include/config_default.inc.php b/include/config_default.inc.php index eafb9d5a9..2de75764d 100644 --- a/include/config_default.inc.php +++ b/include/config_default.inc.php @@ -646,6 +646,10 @@ $conf['recent_post_dates'] = array( // the author shown in the RSS feed element $conf['rss_feed_author'] = 'Piwigo notifier'; +// how long does the authentication key stays valid, in seconds. 3 days by +// default. 0 to disable. +$conf['auth_key_duration'] = 3*24*60*60; + // +-----------------------------------------------------------------------+ // | Set admin layout | // +-----------------------------------------------------------------------+ diff --git a/include/constants.php b/include/constants.php index ef321a4bc..f9a032d0f 100644 --- a/include/constants.php +++ b/include/constants.php @@ -81,6 +81,8 @@ if (!defined('USER_FEED_TABLE')) define('USER_FEED_TABLE', $prefixeTable.'user_feed'); if (!defined('RATE_TABLE')) define('RATE_TABLE', $prefixeTable.'rate'); +if (!defined('USER_AUTH_KEYS_TABLE')) + define('USER_AUTH_KEYS_TABLE', $prefixeTable.'user_auth_keys'); if (!defined('USER_CACHE_TABLE')) define('USER_CACHE_TABLE', $prefixeTable.'user_cache'); if (!defined('USER_CACHE_CATEGORIES_TABLE')) diff --git a/include/functions_mail.inc.php b/include/functions_mail.inc.php index ed1081713..529f2da0d 100644 --- a/include/functions_mail.inc.php +++ b/include/functions_mail.inc.php @@ -514,6 +514,8 @@ SELECT DISTINCT language // get subset of users in this group for a specific language $query = ' SELECT + ui.user_id, + ui.status, u.'.$conf['user_fields']['username'].' AS name, u.'.$conf['user_fields']['email'].' AS email FROM '.USER_GROUP_TABLE.' AS ug @@ -534,13 +536,27 @@ SELECT switch_lang_to($language); - $return&= pwg_mail(null, - array_merge( - $args, - array('Bcc' => $users) - ), - $tpl - ); + foreach ($users as $u) + { + $authkey = create_user_auth_key($u['user_id'], $u['status']); + + $user_tpl = $tpl; + + if ($authkey !== false) + { + $user_tpl['assign']['LINK'] = add_url_params($tpl['assign']['LINK'], array('auth' => $authkey['auth_key'])); + + if (isset($user_tpl['assign']['IMG']['link'])) + { + $user_tpl['assign']['IMG']['link'] = add_url_params( + $user_tpl['assign']['IMG']['link'], + array('auth' => $authkey['auth_key']) + ); + } + } + + $return &= pwg_mail($u['email'], $args, $user_tpl); + } switch_lang_back(); } diff --git a/include/functions_user.inc.php b/include/functions_user.inc.php index 5f503b36e..915b7dbd0 100644 --- a/include/functions_user.inc.php +++ b/include/functions_user.inc.php @@ -1462,4 +1462,136 @@ function get_recent_photos_sql($db_field) .pwg_db_get_recent_period_expression($user['recent_period']) .','.pwg_db_get_recent_period_expression(1,$user['last_photo_date']).')'; } + +/** + * Performs auto-connection if authentication key is valid. + * + * @since 2.8 + * + * @return bool + */ +function auth_key_login($auth_key) +{ + global $conf, $user; + + if ($user['id'] != $conf['guest_id']) + { + return false; + } + + if (!preg_match('/^[a-z0-9]{30}$/i', $auth_key)) + { + return false; + } + + $query = ' +SELECT + *, + '.$conf['user_fields']['username'].' AS username, + NOW() AS dbnow + FROM '.USER_AUTH_KEYS_TABLE.' AS uak + JOIN '.USER_INFOS_TABLE.' AS ui ON uak.user_id = ui.user_id + JOIN '.USERS_TABLE.' AS u ON u.'.$conf['user_fields']['id'].' = ui.user_id + WHERE auth_key = \''.$auth_key.'\' +;'; + $keys = query2array($query); + + if (count($keys) == 0) + { + return false; + } + + $key = $keys[0]; + + // is the key still valid? + if (strtotime($key['expired_on']) < strtotime($key['dbnow'])) + { + return false; + } + + // admin/webmaster/guest can't get connected with authentication keys + if (!in_array($key['status'], array('normal','generic'))) + { + return false; + } + + $user['id'] = $key['user_id']; + log_user($user['id'], false); + trigger_notify('login_success', stripslashes($key['username'])); + + return true; +} + +/** + * Creates an authentication key. + * + * @since 2.8 + * @param int $user_id + * @return array + */ +function create_user_auth_key($user_id, $user_status=null) +{ + global $conf; + + if (0 == $conf['auth_key_duration']) + { + return false; + } + + if (!isset($user_status)) + { + // we have to find the user status + $query = ' +SELECT + status + FROM '.USER_INFOS_TABLE.' + WHERE user_id = '.$user_id.' +;'; + $user_infos = query2array($query); + + if (count($user_infos) == 0) + { + return false; + } + + $user_status = $user_infos[0]['status']; + } + + if (!in_array($user_status, array('normal','generic'))) + { + return false; + } + + $candidate = generate_key(30); + + $query = ' +SELECT + COUNT(*), + NOW(), + ADDDATE(NOW(), INTERVAL '.$conf['auth_key_duration'].' SECOND) + FROM '.USER_AUTH_KEYS_TABLE.' + WHERE auth_key = \''.$candidate.'\' +;'; + list($counter, $now, $expiration) = pwg_db_fetch_row(pwg_query($query)); + if (0 == $counter) + { + $key = array( + 'auth_key' => $candidate, + 'user_id' => $user_id, + 'created_on' => $now, + 'duration' => $conf['auth_key_duration'], + 'expired_on' => $expiration, + ); + + single_insert(USER_AUTH_KEYS_TABLE, $key); + + $key['auth_key_id'] = pwg_db_insert_id(); + + return $key; + } + else + { + return create_user_auth_key($user_id); + } +} ?> \ No newline at end of file diff --git a/include/user.inc.php b/include/user.inc.php index 4de5cc6c3..c02fcb0ac 100644 --- a/include/user.inc.php +++ b/include/user.inc.php @@ -65,6 +65,12 @@ if ($conf['apache_authentication']) } } +// automatic login by authentication key +if (isset($_GET['auth'])) +{ + auth_key_login($_GET['auth']); +} + $user = build_user( $user['id'], ( defined('IN_ADMIN') and IN_ADMIN ) ? false : true // use cache ? ); diff --git a/themes/default/template/mail/text/html/cat_group_info.tpl b/themes/default/template/mail/text/html/cat_group_info.tpl index e8d7d7c10..6a136c63c 100644 --- a/themes/default/template/mail/text/html/cat_group_info.tpl +++ b/themes/default/template/mail/text/html/cat_group_info.tpl @@ -1,6 +1,6 @@

{'Informations'|@translate}

-

{$IMG_URL}

+

{'Hello,'|@translate}

{'Discover album:'|@translate} {$CAT_NAME}

{$CPL_CONTENT}

-- cgit v1.2.3