full rewrite for dotclear 2.24

This commit is contained in:
Jean-Christian Denis 2022-12-31 01:48:40 +01:00
parent f79f1c7046
commit c5544b81b3
Signed by: JcDenis
GPG key ID: 1B5B8C5B90B6C951
10 changed files with 656 additions and 627 deletions

View file

@ -1,27 +1,26 @@
<?php
# -- BEGIN LICENSE BLOCK ----------------------------------
# This file is part of httpPassword, a plugin for Dotclear.
#
# Copyright (c) 2007-2009 Frederic PLE
# dotclear@frederic.ple.name
#
# Licensed under the GPL version 2.0 license.
# A copy of this license is available in LICENSE file or at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# -- END LICENSE BLOCK ------------------------------------
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return null;
}
if (!defined('DC_CONTEXT_ADMIN')) { return; }
$_menu['Plugins']->addItem('httpPassword',
'plugin.php?p=httpPassword',
'index.php?pf=httpPassword/icon.png',
preg_match('/plugin.php\?p=httpPassword(&.*)?$/',
$_SERVER['REQUEST_URI']),
$core->auth->check('usage,contentadmin',$core->blog->id)
dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem(
__('Http password'),
dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)),
urldecode(dcPage::getPF(basename(__DIR__) . '/icon.png')),
preg_match('/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__))) . '(&.*)?$/', $_SERVER['REQUEST_URI']),
dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_USAGE,
initHttpPassword::PERMISSION,
]), dcCore::app()->blog->id)
);
$core->auth->setPermissionType(
'httpPassword',
'Gestion de la protection du site httpPassword'
);
?>

View file

@ -1,21 +1,33 @@
<?php
# -- BEGIN LICENSE BLOCK ----------------------------------
# This file is part of httpPassword, a plugin for Dotclear.
#
# Copyright (c) 2007-2009 Frederic PLE
# dotclear@frederic.ple.name
#
# Licensed under the GPL version 2.0 license.
# A copy of this license is available in LICENSE file or at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# -- END LICENSE BLOCK ------------------------------------
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
$this->registerModule(
/* Name */ "httpPassword",
/* Description*/ "Manage .htpasswd file to make the blog private",
/* Author */ "Frederic PLE <dotclear@frederic.ple.name>",
/* Version */ '0.5.10',
/* Permissions */ 'httpPassword'
'Http password',
'Manage .htpasswd file to make the blog private',
'Frederic PLE and contributors',
'1.0',
[
'requires' => [['core', '2.24']],
'permissions' => dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_USAGE,
initHttpPassword::PERMISSION,
]),
'type' => 'plugin',
'support' => 'https://github.com/JcDenis/' . basename(__DIR__),
'details' => 'http://plugins.dotaddict.org/dc2/details/' . basename(__DIR__),
'repository' => 'https://raw.githubusercontent.com/JcDenis/' . basename(__DIR__) . '/master/dcstore.xml',
]
);
?>

21
_init.php Normal file
View file

@ -0,0 +1,21 @@
<?php
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
class initHttpPassword
{
public const PERMISSION = 'httpPassword';
public const FILE_PASSWORD = '.htpasswd';
}

View file

@ -1,36 +1,37 @@
<?php
# -- BEGIN LICENSE BLOCK ----------------------------------
# This file is part of httpPassword, a plugin for Dotclear.
#
# Copyright (c) 2007-2009 Frederic PLE
# dotclear@frederic.ple.name
#
# Licensed under the GPL version 2.0 license.
# A copy of this license is available in LICENSE file or at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# -- END LICENSE BLOCK ------------------------------------
if (!defined('DC_CONTEXT_ADMIN')) { return; }
$m_version = $core->plugins->moduleInfo('httpPassword','version');
$i_version = $core->getVersion('httpPassword');
if (version_compare($i_version,$m_version,'>=')) {
return;
}
# Création du setting (s'il existe, il ne sera pas écrasé)
$settings = new dcSettings($core,null);
$settings->setNamespace('httppassword');
$mydomain = preg_replace('/^.*\.([^.]+[^.])$/','$1',gethostbyaddr($_SERVER['SERVER_ADDR']));
$defaultcrypt = '';
$settings->put('httppassword_active',false,'boolean','Activer',false,false);
$settings->put('httppassword_crypt',$defaultcrypt,'string','Fonction de cryptage',false,false);
$settings->put('httppassword_message','Zone Privee','String','Message personnalisable dans le popup d\'authentification',false,false);
$settings->put('httppassword_trace',false,'boolean','Activation des traces (debug)',false,false);
$settings->put('httppassword_debugmode',false,'boolean','Activation du mode Debug',false,false);
$core->setVersion('httpPassword',$m_version);
?>
<?php
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return;
}
try {
// Check versions
if (!dcCore::app()->newVersion(
basename(__DIR__),
dcCore::app()->plugins->moduleInfo(basename(__DIR__), 'version')
)) {
return null;
}
// Set settings
$s = dcCore::app()->blog->settings->get(basename(__DIR__));
$s->put('active', false, 'boolean', 'Enable plugin', false, false);
$s->put('crypt', 'crypt_md5', 'string', 'Crypt algorithm', false, false);
$s->put('message', 'Private space', 'String', 'Personalized message on Authentication popup', false, false);
return true;
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
}
return false;

24
_prepend.php Normal file
View file

@ -0,0 +1,24 @@
<?php
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
Clearbricks::lib()->autoload([
'httpPassword' => implode(DIRECTORY_SEPARATOR, [__DIR__, 'inc', 'class.httppassword.php']),
]);
dcCore::app()->auth->setPermissionType(
initHttpPassword::PERMISSION,
__('Manage http password blog protection')
);

View file

@ -1,101 +1,65 @@
<?php
# -- BEGIN LICENSE BLOCK ----------------------------------
# This file is part of httpPassword, a plugin for Dotclear.
#
# Copyright (c) 2007-2009 Frederic PLE
# dotclear@frederic.ple.name
#
# Licensed under the GPL version 2.0 license.
# A copy of this license is available in LICENSE file or at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# -- END LICENSE BLOCK ------------------------------------
if ($core->blog->settings->httppassword_active) {
$core->addBehavior('publicPrepend',array('httpPassword','Check'));
//$core->addBehavior('publicPrepend',array('httpPassword','LastLogin'));
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!dcCore::app()->blog->settings->get(basename(__DIR__))->get('active')) {
return null;
}
class httpPassword {
dcCore::app()->addBehavior('publicPrependV2', function (): void {
$PHP_AUTH_USER = $PHP_AUTH_PW = '';
private static function __debuglog ($core,$trace) {
static $fic = false;
if (!$core->blog->settings->httppassword_trace)
return;
if ($fic === false)
$fic = fopen($core->blog->public_path . '/.htpasswd.trc.txt','a');
if ($fic !== false) {
fprintf($fic,"%s - %s\n",date('Ymd-His'),$trace);
}
}
if (isset($_SERVER['PHP_AUTH_USER']) and isset($_SERVER['PHP_AUTH_PW'])) {
$PHP_AUTH_USER = $_SERVER['PHP_AUTH_USER'];
$PHP_AUTH_PW = $_SERVER['PHP_AUTH_PW'];
} elseif (isset($_ENV['REMOTE_USER'])) {
[$PHP_AUTH_PW, $PHP_AUTH_USER] = explode(' ', $_ENV['REMOTE_USER'], 2);
[$PHP_AUTH_USER, $PHP_AUTH_PW] = explode(':', base64_decode($PHP_AUTH_USER));
}
if ($PHP_AUTH_PW === '' or $PHP_AUTH_USER === '') {
httpPassword::sendHttp401();
}
private static function __debugmode ($core) {
$fic = fopen($core->blog->public_path . '/.debugmode','a');
fprintf($fic,"\n%s\n%s\n", str_repeat('-', 30), date('Ymd-His'));
fprintf($fic,".... \$_SERVER =\n%s\n",var_export($_SERVER,true));
fprintf($fic,".... \$_ENV =\n%s\n",var_export($_ENV,true));
fprintf($fic,".... Apache headers =\n%s\n",var_export(apache_request_headers(),true));
}
private static function __HTTP401($core) {
httpPassword::__debuglog($core,__FUNCTION__);
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="'. utf8_decode(htmlspecialchars_decode($core->blog->settings->httppassword_message)) .'"');
exit(0);
}
public static function Check($core) {
httpPassword::__debuglog($core,'ENV = ' . var_export($_ENV,true));
if ($core->blog->settings->httppassword_debugmode)
httpPassword::__debugmode($core);
if (isset($_SERVER['PHP_AUTH_USER']) and isset($_SERVER['PHP_AUTH_PW'])) {
$PHP_AUTH_USER = $_SERVER['PHP_AUTH_USER'];
$PHP_AUTH_PW = $_SERVER['PHP_AUTH_PW'];
httpPassword::__debuglog($core,__FUNCTION__ . ' user identication found in $_SERVER');
} else if (isset($_ENV['REMOTE_USER'])) {
list($PHP_AUTH_PW,$PHP_AUTH_USER) = explode(' ',$_ENV['REMOTE_USER'],2);
list($PHP_AUTH_USER,$PHP_AUTH_PW) = explode(':',base64_decode($PHP_AUTH_USER));
httpPassword::__debuglog($core,__FUNCTION__ . ' user identication found in $_ENV');
}
if (!isset($PHP_AUTH_USER) or !isset($PHP_AUTH_PW) or $PHP_AUTH_USER === '')
httpPassword::__HTTP401($core);
if (!is_file(dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . initHttpPassword::FILE_PASSWORD)) {
header('HTTP/1.0 500 Internal Server Error');
echo 'httpPassword plugin is not well configured.';
exit(1);
}
httpPassword::__debuglog($core,'Testing user: '.$PHP_AUTH_USER.' pass: '.$PHP_AUTH_PW);
if (!is_file($core->blog->public_path . '/.htpasswd')) {
header('HTTP/1.0 500 Internal Server Error');
echo "Le plugin httppassword pr&eacute;sente une anomalie de configuration";
exit(1);
}
$htpasswd = file($core->blog->public_path . '/.htpasswd',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
$authenticated = false;
foreach($htpasswd as $ligne) {
list($cur_user,$cur_pass) = explode(':',trim($ligne),2);
httpPassword::__debuglog($core,'cur_user: '.$cur_user.' cur_pass: '.$cur_pass);
if ($cur_user == $PHP_AUTH_USER and crypt($PHP_AUTH_PW,$cur_pass) == $cur_pass) {
$authenticated = true;
httpPassword::__debuglog($core,' OK');
}
if ($authenticated) break;
}
unset($htpasswd);
if (!$authenticated) httpPassword::__HTTP401($core);
else httpPassword::LastLogin($core,$PHP_AUTH_USER);
return(true);
}
public static function LastLogin($core,$user) {
$fic = $core->blog->public_path . '/.lastlogin';
$httpPasswordLastLogin = array();
if (is_file($fic))
$httpPasswordLastLogin = unserialize(file_get_contents($fic));
$httpPasswordLastLogin[$user] = date('Y-m-d H:i');
file_put_contents($fic,serialize($httpPasswordLastLogin));
return(true);
}
}
?>
$htpasswd = file(dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . initHttpPassword::FILE_PASSWORD, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$authenticated = false;
foreach ($htpasswd as $ligne) {
[$cur_user, $cur_pass] = explode(':', trim($ligne), 2);
if ($cur_user == $PHP_AUTH_USER and crypt($PHP_AUTH_PW, $cur_pass) == $cur_pass) {
$authenticated = true;
}
if ($authenticated) {
break;
}
}
unset($htpasswd);
if (!$authenticated) {
httpPassword::sendHttp401();
} else {
$logs = dcCore::app()->log->getLogs(['log_table' => basename(__DIR__), 'log_msg' => $PHP_AUTH_USER]);
if (!$logs->isEmpty()) {
$ids = [];
while ($logs->fetch()) {
$ids[] = $logs->__get('log_id');
}
$logs = dcCore::app()->log->delLogs($ids);
}
$cursor = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME);
$cursor->__set('log_table', basename(__DIR__));
$cursor->__set('log_msg', $PHP_AUTH_USER);
dcCore::app()->log->addLog($cursor);
}
});

107
inc/class.httppassword.php Normal file
View file

@ -0,0 +1,107 @@
<?php
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
class httpPassword
{
public static function id(): string
{
return basename(dirname(__DIR__));
}
public static function crypt(?string $secret): string
{
switch (dcCore::app()->blog->settings->get(self::id())->get('crypt')) {
case 'plaintext':
$saltlen = -1;
$salt = '';
break;
case 'crypt_std_des':
$saltlen = 2;
$salt = '';
break;
case 'crypt_ext_des':
$saltlen = 9;
$salt = '';
break;
case 'crypt_md5':
$saltlen = 12;
$salt = '$1$';
break;
case 'crypt_blowfish':
$saltlen = 16;
$salt = '$2$';
break;
case 'crypt_sha256':
$saltlen = 16;
$salt = '$5$';
break;
case 'crypt_sha512':
$saltlen = 16;
$salt = '$6$';
break;
default:
return '';
}
if ($saltlen > 0) {
$salt .= substr(
sha1(dcCore::app()->getNonce() . date('U')),
2,
$saltlen - strlen($salt)
);
$secret = crypt($secret, $salt);
}
return($secret);
}
public static function isWritable(): bool
{
if (false === ($fp = fopen(dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . initHttpPassword::FILE_PASSWORD, 'a+'))) {
return false;
}
fclose($fp);
return true;
}
public static function getCryptCombo(): array
{
return [
__('No encryption') => 'plaintext',
__('Crypt DES standard') => 'crypt_std_des',
__('Crypt DES étendu') => 'crypt_ext_des',
__('Crypt MD5') => 'crypt_md5',
__('Crypt Blowfish') => 'crypt_blowfish',
__('Crypt SHA256') => 'crypt_sha256',
__('Crypt SHA512') => 'crypt_sha512',
];
}
public static function sendHttp401(): void
{
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="' . utf8_decode(htmlspecialchars_decode(dcCore::app()->blog->settings->get(self::id())->get('message'))) . '"');
exit(0);
}
}

714
index.php
View file

@ -1,475 +1,279 @@
<?php
# -- BEGIN LICENSE BLOCK ----------------------------------
# This file is part of httpPassword, a plugin for Dotclear.
#
# Copyright (c) 2007-2009 Frederic PLE
# dotclear@frederic.ple.name
#
# Licensed under the GPL version 2.0 license.
# A copy of this license is available in LICENSE file or at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# -- END LICENSE BLOCK ------------------------------------
function htpasswd_crypt(&$core,$secret)
{
switch ($core->blog->settings->httppassword_crypt) {
case "plaintext":
$saltlen = -1;
break;
case "crypt_std_des":
$saltlen = 2;
$salt = "";
break;
case "crypt_ext_des":
$saltlen = 9;
$salt = "";
break;
case "crypt_md5":
$saltlen = 12;
$salt = '$1$';
break;
case "crypt_blowfish":
$saltlen = 16;
$salt = '$2$';
break;
default:
return(false);
}
if ($saltlen > 0) {
$salt .= substr(
sha1($core->getNonce() . date('U')),
2,
$saltlen - strlen($salt)
);
$secret = crypt($secret,$salt);
}
return($secret);
/**
* @brief httpPassword, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Frederic PLE and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return null;
}
if (!defined('DC_CONTEXT_ADMIN')) { return; }
$s = dcCore::app()->blog->settings->get(basename(__DIR__));
$pwd_file = dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . initHttpPassword::FILE_PASSWORD;
$action = $_POST['action'] ?? '';
$redir = $_REQUEST['redir'] ?? '';
$part = $_REQUEST['part'] ?? 'settings';
$passwords = [];
$writable = httpPassword::isWritable();
$section_menu = [
__('Settings') => 'settings',
__('Logins history') => 'logins',
__('Authorized users') => 'passwords',
];
$crypt_algo = array(
'plaintext' => 'Aucun',
);
if (CRYPT_STD_DES == 1)
$crypt_algo['crypt_std_des'] = 'Crypt DES standard';
if (CRYPT_EXT_DES == 1)
$crypt_algo['crypt_ext_des'] = 'Crypt DES &eacute;tendu';
if (CRYPT_MD5 == 1)
$crypt_algo['crypt_md5'] = 'Crypt MD5';
if (CRYPT_BLOWFISH == 1)
$crypt_algo['crypt_blowfish'] = 'Crypt Blowfish';
$htpasswdfile = $core->blog->public_path . '/.htpasswd' ;
$htp = file($htpasswdfile);
if (!is_array($htp)) $htp = array();
sort($htp);
$u = array();
$v = array();
foreach($htp as $ligne) {
list($login, $pwd) = explode(':', $ligne, 2);
$u[trim($login)] = trim($pwd);
if (!in_array($part, $section_menu) || !$writable) {
$part = 'settings';
}
unset($ftp);
$txt = !empty($_POST['txt']) ? $_POST['txt'] : null;
$action = !empty($_POST['httppasswordaction'])
? $_POST['httppasswordaction']
: null;
$core->blog->settings->setNamespace('httppassword');
$debugmodefile = $core->blog->public_path . '/.debugmode';
switch($action) {
case "mod":
// traitement des donnees du formulaire
foreach(preg_split('/\n/m',$txt) as $ligne)
{
if (strpos($ligne, ':') === false)
$ligne = trim($ligne) . ':';
list($login, $pwd) = explode(':', $ligne);
$v[trim($login)] = trim($pwd);
}
// Rechercher les suppressions
foreach(array_keys($u) as $login)
{
if (!isset($v[$login]))
unset($u[$login]);
}
// Rechercher les modifs + nouveaux
foreach(array_keys($v) as $login)
{
if ($v[$login] != "") {
$u[$login] = htpasswd_crypt(
$core,
$v[$login]
);
if ($u[$login] === false)
unset($u[$login]);
}
}
$txt = "";
foreach(array_keys($u) as $login)
$txt .= $login.":".$u[$login]."\r\n";
file_put_contents($htpasswdfile,$txt);
break;
case "desactive":
case "active":
$active = !$core->blog->settings->httppassword_active;
$core->blog->settings->put(
'httppassword_active',
$active,
'boolean'
);
$core->blog->settings->httppassword_active = $active;
break;
case "cryptfunc":
$httppassword_crypt = trim($_POST['cryptage']);
if (in_array($httppassword_crypt,array_keys($crypt_algo))) {
$core->blog->settings->put(
'httppassword_crypt',
$httppassword_crypt,
'string'
);
$core->blog->settings->httppassword_crypt =
$httppassword_crypt;
}
break;
case "auth_message":
$message = htmlspecialchars($_POST['auth_message']);
$core->blog->settings->put(
'httppassword_message',
$message,
'string'
);
$core->blog->settings->httppassword_message = $message;
break;
case "debugmode":
if ($_POST['debugmode'] === "true")
$debugmode = true;
else {
$debugmode = false;
if (is_file($debugmodefile))
unlink($debugmodefile);
}
$core->blog->settings->put(
'httppassword_debugmode',
$debugmode,
'boolean'
);
$core->blog->settings->httppassword_debugmode = $debugmode;
break;
if (empty($redir)) {
$redir = dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['part' => $part]);
}
if (!$writable) {
dcAdminNotices::addWarningNotice(
__('No write permissions on blogs directories.')
);
}
$fic = $core->blog->public_path . '/.lastlogin';
if (is_file($fic)) {
$httpPasswordLastLogin = unserialize(file_get_contents($fic));
if ($httpPasswordLastLogin === false) $httpPasswordLastLogin = array();
} else
$httpPasswordLastLogin = array();
$form_block=' style="display: none;"';
if (strlen($core->blog->settings->httppassword_crypt) > 0) $form_block="";
?><html>
<head>
<title>httpPassword</title>
<?php echo dcPage::jsPageTabs($part); ?>
<style type="text/css">
.ns-name {
background: #ccc;
color: #000;
padding-top: 0.3em;
padding-bottom: 0.3em;
font-size: 1.1em;
}
.fp-code {
border-left: #d0d0d0 solid 4px;
margin-left: 40px;
padding: 5px;
}
tr.row-odd { background: #bbbbbb; }
tr.row-even { background: #dddddd; }
h3 { border-top: #d0d0d0 dotted 2px; margin-top: 15px; }
</style>
</head>
<body>
<h2><?php echo html::escapeHTML($core->blog->name); ?> &rsaquo; httpPasswd</h2>
<div id="local" class="multi-part" title="Gestion des acc&egrave;s restreints">
<table><tr>
<td>
<div<?php echo $form_block; ?>>
<h3>Activation du plugin</h3>
<?php
$canwrite = true;
foreach(array('.htpasswd','.lastlogin') as $f) {
$fp = fopen(dirname($htpasswdfile) . '/' . $f,'a+');
if ($fp === false)
$canwrite = false;
else
fclose($fp);
if ('passwords' == $part) {
$lines = file($pwd_file);
if (!is_array($lines)) {
$lines = [];
}
sort($lines);
foreach ($lines as $line) {
[$login, $pwd] = explode(':', $line, 2);
$passwords[trim($login)] = trim($pwd);
}
unset($lines);
}
$fichier_existe = is_file(dirname($htpasswdfile) . '/.htpasswd');
$fichier_modifier = true;
if ('savesettings' == $action) {
$s->put('active', !empty($_POST['active']));
$s->put('crypt', in_array((string) $_POST['crypt'], httpPassword::getCryptCombo()) ? $_POST['crypt'] : 'paintext');
$s->put('message', (string) $_POST['message']);
if (!$canwrite) { ?>
<p><strong>Pour utiliser cette extension, vous devez avoir les permissions
pour &eacute;crire dans les fichiers&nbsp;:</strong></p>
<ul>
<li><tt><?php echo $htpasswdfile; ?></tt></li>
<li><tt><?php echo dirname($htpasswdfile) . '/.lastlogin'; ?></tt></li>
</ul>
<?php } elseif ($core->blog->settings->httppassword_active) { ?>
<p><strong>Protection ACTIV&Eacute;E</strong></p>
<form method="post">
Cliquer sur ce bouton pour d&eacute;sactiver la protection&nbsp;:
<input type="submit" value="D&eacute;sactiver" />
<?php
echo
$core->formNonce() .
form::hidden(array('p'),'httpPassword') .
form::hidden(array('httppasswordaction'),'desactive');
?>
</form>
dcCore::app()->blog->triggerBlog();
<?php } else { ?>
utilisateur valide !</strong></p>
<form method="post">
Cliquer sur ce bouton pour activer la protection&nbsp;:
<input type="submit" value="Activer" />
<?php
echo
$core->formNonce().
form::hidden(array('p'),'httpPassword').
form::hidden(array('httppasswordaction'),'active');
?>
</form>
dcAdminNotices::addSuccessNotice(
__('Settings successfully updated.')
);
<?php } ?>
</div>
<div>
<h3>S&eacute;curit&eacute; des mots de passe</h3>
<p>Pour modifier la fonction de "cryptage".</p>
<p><strong>Attention, le changement de
cryptage s'appliquera individuellement &agrave; la prochaine modification
de chacun des comptes (cr&eacute;tion ou changement de mot de passe)</strong></p>
<form method="post">
<?php
foreach($crypt_algo as $algo_code => $algo_libelle) {
echo '<input type="radio" name="cryptage" value="' . $algo_code . '" ';
if ($core->blog->settings->httppassword_crypt == $algo_code)
echo 'checked ';
echo '/>&nbsp;' . $algo_libelle . '<br />';
} ?><input type="submit" value="Modifier" />
<?php
echo
$core->formNonce().
form::hidden(array('p'),'httpPassword').
form::hidden(array('httppasswordaction'),'cryptfunc');
?>
</form>
</div>
<div<?php echo $form_block; ?>>
<h3>Message d'authentification</h3>
<form method="post">
<p><input type="text" name="auth_message" size="50"
value="<?php echo $core->blog->settings->httppassword_message; ?>" />
<br />
<input type="submit" value="Modifier"/>
<?php
echo
$core->formNonce().
form::hidden(array('p'),'httpPassword').
form::hidden(array('httppasswordaction'),'auth_message');
?> </p>
</form>
</div>
</td>
<td>
<div<?php echo $form_block; ?>>
<form method="post">
<p><textarea name="txt" rows="30" cols="30">
<?php
foreach(array_keys($u) as $login)
echo "$login\n";
?>
</textarea><br />
<input type="submit" value="Modifier" />
<?php
echo
$core->formNonce().
form::hidden(array('p'),'httpPassword').
form::hidden(array('httppasswordaction'),'mod');
?> </p>
</form>
</div>
</td></tr></table>
</div>
<div id="histo" class="multi-part"
title="Historique des derni&egrave;res connexions">
<p>Nous sommes le <?php echo date('d-m-Y H:i'); ?></p>
<table>
<?php
if (count($httpPasswordLastLogin)>0) {
$i = 0;
$logins = array_keys($httpPasswordLastLogin);
sort($logins);
foreach($logins as $login)
echo '<tr style="row-' .
(($i++ % 2 == 0) ? 'odd' : 'even') .
'"><td>' . $login . '</td><td>' .
$httpPasswordLastLogin[$login] .
'</td></tr>' . "\n";
dcCore::app()->adminurl->redirect(
'admin.plugin.' . basename(__DIR__),
['part' => $part]
);
}
?>
</table>
</div>
<div id="aide" class="multi-part" title="Aide">
<h3>Gestion des acc&egrave;s restreints</h3>
<p>Ce plugin permet la gestion d'identifiants et de mots de
passe pour limiter les acc&egrave;s &agrave; votre blog aux
personnes que vous aurez choisies.</p>
<p>Le formulaire de droite pr&eacute;sente la liste des
utilisateurs existants (sans leur mot de passe)</p>
<h3>Ajout d'un utilisateur</h3>
<p>Pour ajouter un utilisateur, ajouter une nouvelle ligne
de la forme&nbsp;:</p>
<p class="fp-code"><tt>login:motdepasse</tt></p>
<h3>Modifier un mot de passe</h3>
<p>Pour modifier un mot de passe d'un utilisateur, ajouter
&agrave; la suite de son identifiant (sur la m&ecirc;me ligne)
le texte suivant&nbsp;:</p>
<p class="fp-code"><tt>:motdepasse</tt></p>
<h3>Suppression d'un utilisateur</h3>
<p>Pour supprimer un utilisateur, supprimer la ligne de
l'utilisateur.</p>
</div>
if ('savelogins' == $action) {
$logs = dcCore::app()->log->getLogs(['log_table' => basename(__DIR__)]);
if (!$logs->isEmpty()) {
$ids = [];
while ($logs->fetch()) {
$ids[] = $logs->__get('log_id');
}
$logs = dcCore::app()->log->delLogs($ids);
dcAdminNotices::addSuccessNotice(
__('Logs successfully cleared.')
);
dcCore::app()->adminurl->redirect(
'admin.plugin.' . basename(__DIR__),
['part' => $part]
);
}
}
if ('savepasswords' == $action) {
$lines = [];
if (!empty($_POST['login']) && !empty($_POST['password'])) {
$lines[$_POST['login']] = httpPassword::crypt($_POST['password']);
}
foreach ($passwords as $l => $p) {
// add login
if (array_key_exists($l, $lines)) {
continue;
}
// delete login
if (!empty($_POST['delete']) && array_key_exists($l, $_POST['delete'])) {
continue;
}
// change password
if (!empty($_POST['edit']) && array_key_exists($l, $_POST['edit'])
&& !empty($_POST['newpassword']) && array_key_exists($l, $_POST['newpassword'])
) {
$lines[$l] = httpPassword::crypt($_POST['newpassword'][$l]);
} else {
$lines[$l] = $p;
}
}
$contents = '';
foreach ($lines as $l => $p) {
$contents .= sprintf("%s:%s\r\n", $l, $p);
}
file_put_contents($pwd_file, $contents);
dcCore::app()->blog->triggerBlog();
dcAdminNotices::addSuccessNotice(
__('Logins successfully updated.')
);
dcCore::app()->adminurl->redirect(
'admin.plugin.' . basename(__DIR__),
['part' => $part]
);
}
<div id="moddebug" class="multi-part" title="Debug">
<p>Le plugin a &eacute;t&eacute; d&eacute;velopp&eacute; pour
fonctionner sur une installation "standard" de serveur Web
(PHP en module Apache).</p>
<p>Certains h&eacute;bergeurs utilisent des installations de
PHP en mode CGI, parfois assez sp&eacute;cifiques et
sur lesquelles ce plugin ne fonctionnera pas</p>
<p>Le mode debug permet de collecter des informations
n&eacute;cessaires au d&eacute;veloppeur pour adapter le plugin
&agrave; des contextes particuliers.</p>
<h3>Quand l'activer</h3>
<ul>
<li>Vous avez install&eacute; la derni&egrave;re version du plugin
(voir sur http://lab.dotclear.org/plugin/httpPassword)</li>
<li>Vous avez activ&eacute; le plugin</li>
<li>Vous avez cr&eacute;&eacute; un compte</li>
<li>Lorsque vous vous authentifiez sur le site, vous ne parvenez
pas &agrave; acc&eacute;der &agrave; la partie publique avec le
compte que vous avez cr&eacute;&eacute;</li>
</ul>
<h3>MISE EN GARDE</h3>
<p>le mode debug est
dangeureux. Il est imperatif de le desactiver juste apres
les tests.</p>
<h3>Protocole &agrave; suivre</h3>
<p>Le protocole est le suivant. Merci de le suivre pas &agrave; pas.</p>
<ol>
<li>Creer un compte "debug" dont le mot de passe est "test"</li>
<li>Activer le plugin</li>
<li>Activer le mode debug</li>
<li>Faire un essai d'authentification</li>
<li>Revenir sur cette page et copier le texte de la section "Resultats"
dans un mail</li>
<li>Joindre a ce mail le fichier .debugmode que vous trouverez
dans le répertoire public du blog</li>
<li>Envoyer le mail &agrave; dotclear@frederic.ple.name</li>
<li>Desactiver le mode debug</li>
<li>Supprimer le de compte "debug"</li>
<li>Attendre patiemment la r&eacute;ponse du gentil
d&eacute;veloppeur</li>
</ol>
<h3>Activer / D&eacute;dactiver le mode DEBUG</h3>
<form method="post">
<p><input type="radio" name="debugmode" value="false" <?php if ($core->blog->settings->httppassword_debugmode === false) echo 'checked="checked" '; ?>/>Mode normal</p>
<p><input type="radio" name="debugmode" value="true" <?php if ($core->blog->settings->httppassword_debugmode === true) echo 'checked="checked" '; ?>/>Mode Debug</p>
<p><input type="submit" value="Modifier"/></p>
<?php
echo
$core->formNonce().
form::hidden(array('p'),'httpPassword').
form::hidden(array('httppasswordaction'),'debugmode');
?> </p>
</form>
<?php
if (is_file($debugmodefile)) {
//if (true) {
?>
<h3>R&eacute;sultats</h3>
<div class="fp-code"><tt>
<?php
echo "* INFOS BLOG *<br />\n";
echo "URL: " . $core->blog->url . "<br />\n";
echo "IP: " . $_SERVER['SERVER_ADDR'] . "<br />\n";
echo "DocumentRoot: " . $_SERVER['DOCUMENT_ROOT'] . "<br />\n";
echo "DC2 version: " . $core->getVersion('core') . "<br />\n";
echo "DC2 path: " ."-" . "<br />\n";
echo "Plugins path: " . realpath(dirname(__FILE__) . '/..') . "<br />\n";
echo "Public path: " . $core->blog->public_path . "<br />\n";
echo "* INFOS HTTPPASSWD *<br />\n";
echo "Version: " . $core->getVersion('httpPassword') . "<br />\n";
//echo ".... \$_SERVER ....<br />" . str_replace("\n","<br />\n",var_export($_SERVER,true)) . "<br />\n";
//echo ".... \$_ENV ....<br />" . str_replace("\n","<br />\n",var_export($_ENV,true)) . "<br />\n";
//echo ".... HTTP Apache HEADERS ....<br />" . str_replace("\n","<br />\n",var_export(apache_request_headers(),true)) . "<br />\n";
//echo str_replace("\n","<br />\n",htmlentities(file_get_contents($debugmodefile)));
?>
</tt></div>
<?php } ?>
</div>
'<html><head><title>' . __('Http password') . '</title>' .
dcPage::jsPageTabs() .
dcPage::jsModuleLoad(basename(__DIR__) . '/js/index.js') .
'</head><body>' .
dcPage::breadcrumb([
__('Plugins') => '',
__('Http password') => dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)),
array_search($part, $section_menu) => '',
]) .
dcPage::notices() .
<div id="credits" class="multi-part" title="Cr&eacute;dits">
<h3>Plugin</h3>
<ul>
<li><a href="http://frederic.ple.name/DC2-plugin-httpPassword">
Plugin Dotclear httpPassword</a></li>
<li>Ce plugin est distribu&eacute; sous licence GPLv2</li>
</ul>
# Filters select menu list
'<form method="get" action="' . dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)) . '" id="section_menu">' .
'<p class="anchor-nav"><label for="part" class="classic">' . __('Select section:') . ' </label>' .
form::combo('part', $section_menu, $part) . ' ' .
'<input type="submit" value="' . __('Ok') . '" />' .
form::hidden('p', basename(__DIR__)) . '</p>' .
'</form>' .
'<h3>' . array_search($part, $section_menu) . '</h3>';
<h3>D&eacute;veloppeur</h3>
<ul>
<li>Fr&eacute;d&eacute;ric PL&Eacute; &lt;dotclear@frederic.ple.name&gt;</li>
</ul>
<h3>Remerciements</h3>
<ul>
<li>Aux d&eacute;veloppeurs de Dotclear pour la grande qualit&eacute; du code</li>
<li>A Tomtom33, Moe, et les autres qui m'ont aid&eacute; sur le
<a href="http://forum.dotclear.net/">forum</a></li>
<li>A Pep de <a href="http://www.dotaddict.org/">Dotaddict</a></li>
<li>Stephanie "piloue" pour ses tests, ses suggestions et sa patience</li>
<li>Gabriel Recope pour ses tests et reports de bugs.</li>
</ul>
if ('settings' == $part) {
echo '
<form method="post" action="' . dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['part' => 'settings']) . '">
<div style="text-align: right; font-size: 0.8em; border-top: dashed 3px #d0d0d0; padding: 2px 10px 0 0;margin-top: 15px;">Plugin r&eacute;alis&eacute; v.<?php echo $core->getVersion('httpPassword'); ?> par <a href="http://frederic.ple.name/" style="text-decoration: none; color: black;">Fr&eacute;d&eacute;ric PL&Eacute;</a>
&lt;dotclear@frederic.ple.name&gt;</div>
</div>
<p><label for="active">' .
form::checkbox('active', '1', (bool) $s->get('active')) .
__('Enable http password protection on this blog') . '</label></p>
</body>
</html>
<p><label for="crypt">' . __('Crypt algorithm:') . '</label> ' .
form::combo('crypt', httpPassword::getCryptCombo(), (string) $s->get('crypt')) . '</p>
<p class="form-note">' .
__('Some web servers does not surpport plaintext (no) encryption.') . ' ' .
__('If you change crypt algo, you must edit and resave each users passwords.') .
'</p>
<p><label for="message">' . __('Authentication message:') . '</label>' .
form::field('message', 60, 255, html::escapeHTML((string) $s->get('message'))) . '
</p>
<div class="clear">
<p>' .
dcCore::app()->formNonce() .
form::hidden(['action'], 'savesettings') .
form::hidden(['part'], $part) . '
<input type="submit" name="save" value="' . __('Save') . '" />
</p></form>';
}
if ('logins' == $part) {
$logs = dcCore::app()->log->getLogs(['log_table' => basename(__DIR__)]);
if ($logs->isEmpty()) {
echo
'<p>' . __('Logins history is empty.') . '</p>';
} else {
echo '
<form method="post" action="' . dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['part' => 'logins']) . '">
<p>' .
dcCore::app()->formNonce() .
form::hidden(['action'], 'savelogins') .
form::hidden(['part'], $part) . '
<input type="submit" name="save" value="' . __('Clear logs') . '" />
</p></form>' .
'<div class="table-outer"><table>' .
'<caption>' . sprintf(__('List of %s last logins.'), $logs->count()) . '</caption>' .
'<thead><tr>' .
'<th scope="col" class="first">' . __('Login') . '</th>' .
'<th scope="col">' . __('Date') . '</th>' .
'</tr></thead<tbody>';
while ($logs->fetch()) {
echo
'<tr class="line">' .
'<td class="nowrap maximal">' . html::escapeHTML($logs->__get('log_msg')) . '</td>' .
'<td class="nowrap count">' . html::escapeHTML(dt::dt2str(__('%Y-%m-%d %H:%M'), $logs->__get('log_dt'))) . '</td>' .
'</tr>';
}
echo
'</table></div>';
}
}
if ('passwords' == $part) {
if (empty($passwords)) {
echo
'<p>' . __('Authorized users list is empty.') . '</p>';
} else {
echo
'<form method="post" action="' . dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['part' => $part]) . '">' .
'<div class="table-outer"><table>' .
'<caption>' . sprintf(__('List of %s authorized users.'), count($passwords)) . '</caption>' .
'<thead><tr>' .
'<th scope="col" class="first nowrap">' . __('Login') . '</th>' .
'<th scope="col" class="first nowrap">' . __('New password') . '</th>' .
'<th scope="col" class="nowrap">' . __('Action') . '</th>' .
'</tr></thead<tbody>';
foreach ($passwords as $login => $pwd) {
echo
'<tr class="line">' .
'<td class="nowrap maximal">' .
html::escapeHTML($login) .
'</td>' .
'<td class="nowrap">' .
form::field(['newpassword[' . html::escapeHTML($login) . ']'], 60, 255, '') .
'</td>' .
'<td class="nowrap">' .
'<input type="submit" name="edit[' . html::escapeHTML($login) . ']" value="' . __('Change password') . '" /> ' .
'<input type="submit" class="delete" name="delete[' . html::escapeHTML($login) . ']" value="' . __('Delete') . '" />' .
'</td>' .
'</tr>';
}
echo
'</table></div>
<p>' .
dcCore::app()->formNonce() .
form::hidden(['action'], 'savepasswords') .
form::hidden(['part'], $part) . '
</p></form>';
}
echo '
<form method="post" action="' . dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['part' => $part]) . '">
<h3>' . __('Add a user') . '</h3>
<p><label for="login">' . __('Login:') . '</label>' .
form::field('login', 60, 255, '') . '
</p>
<p><label for="password">' . __('Password:') . '</label>' .
form::field('password', 60, 255, '') . '
</p>
<p>' .
dcCore::app()->formNonce() .
form::hidden(['action'], 'savepasswords') .
form::hidden(['part'], $part) . '
<input type="submit" name="add" value="' . __('Save') . '" />
</p></form>';
}
echo
'</body></html>';

13
js/index.js Normal file
View file

@ -0,0 +1,13 @@
/*global $, dotclear */
'use strict';
$(function () {
$('#section_menu input[type=submit]').hide();
$('#section_menu #part').on('change', function () {this.form.submit();});
$('.checkboxes-helpers').each(function () {
dotclear.checkboxesHelpers(this, undefined, '#form-records td input[type=checkbox]', '#form-records #del-action');
});
$('#form-records td input[type=checkbox]').enableShiftClick();
dotclear.condSubmit('#form-records td input[type=checkbox]', '#form-records #del-action');
});

84
locales/fr/main.po Normal file
View file

@ -0,0 +1,84 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: httpPassword 1.0\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2022-12-31T00:45:53+00:00\n"
"Last-Translator: Jean-Christian Denis\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "Manage http password blog protection"
msgstr "Gérer la protection du blog par mot de passe HTTP"
msgid "No encryption"
msgstr "pas de cryptage"
msgid "Logins history"
msgstr "Historique des logins"
msgid "Authorized users"
msgstr "Utilisateurs autorisés"
msgid "No write permissions on blogs directories."
msgstr "Aucun droit d'écriture sur le répertoire du blog."
msgid "Settings successfully updated."
msgstr "Paramètres mis à jour."
msgid "Logs successfully cleared."
msgstr "Logs effacé."
msgid "Logins successfully updated."
msgstr "Logins mis à jour."
msgid "Select section:"
msgstr "Sélectionner une section :"
msgid "Enable http password protection on this blog"
msgstr "Activer la protection du blog par mot de passe http"
msgid "Crypt algorithm:"
msgstr "Algorithme de cryptage :"
msgid "Some web servers does not surpport plaintext (no) encryption."
msgstr "Certains serveurs web ne supportent pas le cryptage \"plaintext\"."
msgid "If you change crypt algo, you must edit and resave each users passwords."
msgstr "Si vous changer l'algorithme de cryptage, vous devrez modifier et sauver tous les mots de passes."
msgid "Authentication message:"
msgstr "Message d'authentification :"
msgid "Logins history is empty."
msgstr "L'historique des logins est vide."
msgid "Clear logs"
msgstr "Effacer les logs"
msgid "List of %s last logins."
msgstr "Liste des %s derniers logins."
msgid "Authorized users list is empty."
msgstr "La listes des utilisateurs autorisés est vide."
msgid "List of %s authorized users."
msgstr "Liste des %s utilisateurs autorisés."
msgid "New password"
msgstr "Nouveau mot de passe"
msgid "Change password"
msgstr "Modifier le mot de passe"
msgid "Add a user"
msgstr "Ajouter un utilisateur"
msgid "Login:"
msgstr "Identifiant :"
msgid "Manage .htpasswd file to make the blog private"
msgstr "Gestion du fichier .htpasswd pour rendre le blog privé."