diff --git a/src/Backend.php b/src/Backend.php index 9ed469f..a4bd220 100644 --- a/src/Backend.php +++ b/src/Backend.php @@ -10,17 +10,42 @@ * @copyright Jean-Christian Denis * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html */ -if (!defined('DC_CONTEXT_ADMIN')) { - return null; -} +declare(strict_types=1); -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) -); +namespace Dotclear\Plugin\httpPassword; + +use dcAuth; +use dcAdmin; +use dcCore; +use dcPage; +use dcNsProcess; + +class Backend extends dcNsProcess +{ + public static function init(): bool + { + self::$init = defined('DC_CONTEXT_ADMIN'); + + return self::$init; + } + + public static function process(): bool + { + if (!self::$init) { + return false; + } + + dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem( + My::name(), + dcCore::app()->adminurl->get('admin.plugin.' . My::id()), + dcPage::getPF(My::id() . '/icon.png'), + preg_match('/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.' . My::id())) . '(&.*)?$/', $_SERVER['REQUEST_URI']), + dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([ + dcAuth::PERMISSION_USAGE, + My::PERMISSION, + ]), dcCore::app()->blog->id) + ); + + return true; + } +} diff --git a/src/Frontend.php b/src/Frontend.php index 233c44e..d62e8e6 100644 --- a/src/Frontend.php +++ b/src/Frontend.php @@ -10,56 +10,81 @@ * @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; -} +declare(strict_types=1); -dcCore::app()->addBehavior('publicPrependV2', function (): void { - $PHP_AUTH_USER = $PHP_AUTH_PW = ''; +namespace Dotclear\Plugin\httpPassword; - 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(); +use dcCore; +use dcLog; +use dcNsProcess; + +class Frontend extends dcNsProcess +{ + public static function init(): bool + { + self::$init = defined('DC_RC_PATH'); + + return self::$init; } - 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); - } - - $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; + public static function process(): bool + { + if (!self::$init || !Utils::isActive()) { + return false; } - 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'); + + dcCore::app()->addBehavior('publicPrependV2', function (): void { + $PHP_AUTH_USER = $PHP_AUTH_PW = ''; + + 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)); } - $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); + if ($PHP_AUTH_PW === '' or $PHP_AUTH_USER === '') { + Utils::sendHttp401(); + } + + if (!is_file(dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . My::FILE_PASSWORD)) { + header('HTTP/1.0 500 Internal Server Error'); + echo 'httpPassword plugin is not well configured.'; + exit(1); + } + + $htpasswd = file(dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . My::FILE_PASSWORD, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $authenticated = false; + if ($htpasswd !== 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) { + Utils::sendHttp401(); + } else { + $logs = dcCore::app()->log->getLogs(['log_table' => My::id(), 'log_msg' => $PHP_AUTH_USER]); + if (!$logs->isEmpty()) { + $ids = []; + while ($logs->fetch()) { + $ids[] = (int) $logs->f('log_id'); + } + $logs = dcCore::app()->log->delLogs($ids); + } + $cursor = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcLog::LOG_TABLE_NAME); + $cursor->setField('log_table', My::id()); + $cursor->setField('log_msg', $PHP_AUTH_USER); + dcCore::app()->log->addLog($cursor); + } + }); + + return true; } -}); +} diff --git a/src/Install.php b/src/Install.php index b070e43..4fc3d4e 100644 --- a/src/Install.php +++ b/src/Install.php @@ -10,28 +10,41 @@ * @copyright Jean-Christian Denis * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html */ -if (!defined('DC_CONTEXT_ADMIN')) { - return; -} +declare(strict_types=1); -try { - // Check versions - if (!dcCore::app()->newVersion( - basename(__DIR__), - dcCore::app()->plugins->moduleInfo(basename(__DIR__), 'version') - )) { - return null; +namespace Dotclear\Plugin\httpPassword; + +use dcCore; +use dcNsProcess; +use Exception; + +class Install extends dcNsProcess +{ + public static function init(): bool + { + self::$init = defined('DC_CONTEXT_ADMIN') && dcCore::app()->newVersion(My::id(), dcCore::app()->plugins->moduleInfo(My::id(), 'version')); + + return self::$init; } - // 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); + public static function process(): bool + { + if (!self::$init) { + return false; + } - return true; -} catch (Exception $e) { - dcCore::app()->error->add($e->getMessage()); + try { + // Set settings + $s = dcCore::app()->blog->settings->get(My::id()); + $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 true; + } } - -return false; diff --git a/src/Manage.php b/src/Manage.php index 818592b..a585de8 100644 --- a/src/Manage.php +++ b/src/Manage.php @@ -10,270 +10,342 @@ * @copyright Jean-Christian Denis * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html */ -if (!defined('DC_CONTEXT_ADMIN')) { - return null; -} +declare(strict_types=1); -$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', -]; +namespace Dotclear\Plugin\httpPassword; -if (!in_array($part, $section_menu) || !$writable) { - $part = 'settings'; -} -if (empty($redir)) { - $redir = dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__), ['part' => $part]); -} -if (!$writable) { - dcAdminNotices::addWarningNotice( - __('No write permissions on blogs directories.') - ); -} +use dcCore; +use dcNsProcess; +use dcPage; +use Dotclear\Helper\Html\Html; +use Dotclear\Helper\Html\Form\{ + Checkbox, + Div, + Form, + Hidden, + Input, + Label, + Note, + Para, + Select, + Submit, + Text +}; +use dt; -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); -} +/** + * Manage contributions list + */ +class Manage extends dcNsProcess +{ + public static function init(): bool + { + if (defined('DC_CONTEXT_ADMIN')) { + dcPage::check(dcCore::app()->auth->makePermissions([ + My::PERMISSION, + ])); -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']); - - dcCore::app()->blog->triggerBlog(); - - dcAdminNotices::addSuccessNotice( - __('Settings successfully updated.') - ); - - dcCore::app()->adminurl->redirect( - 'admin.plugin.' . basename(__DIR__), - ['part' => $part] - ); -} - -if ('savelogins' == $action) { - $logs = dcCore::app()->log->getLogs(['log_table' => basename(__DIR__)]); - if (!$logs->isEmpty()) { - $ids = []; - while ($logs->fetch()) { - $ids[] = $logs->__get('log_id'); + self::$init = true; } - $logs = dcCore::app()->log->delLogs($ids); - dcAdminNotices::addSuccessNotice( - __('Logs successfully cleared.') + return self::$init; + } + + public static function process(): bool + { + if (!self::$init) { + return false; + } + + if (!Utils::isWritable()) { + dcPage::addWarningNotice( + __('No write permissions on blogs directories.') + ); + } + + $part = self::getSection(); + $action = $_POST['action'] ?? ''; + if (empty($action)) { + return true; + } + + if ('savesettings' == $action) { + $s = dcCore::app()->blog->settings->get(My::id()); + $s->put('active', !empty($_POST['active'])); + $s->put('crypt', in_array((string) $_POST['crypt'], My::cryptCombo()) ? $_POST['crypt'] : 'paintext'); + $s->put('message', (string) $_POST['message']); + + dcCore::app()->blog->triggerBlog(); + + dcPage::addSuccessNotice( + __('Settings successfully updated.') + ); + + dcCore::app()->adminurl->redirect( + 'admin.plugin.' . My::id(), + ['part' => $part] + ); + } + + if ('savelogins' == $action) { + $logs = dcCore::app()->log->getLogs(['log_table' => My::id()]); + if (!$logs->isEmpty()) { + $ids = []; + while ($logs->fetch()) { + $ids[] = $logs->__get('log_id'); + } + $logs = dcCore::app()->log->delLogs($ids); + + dcPage::addSuccessNotice( + __('Logs successfully cleared.') + ); + + dcCore::app()->adminurl->redirect( + 'admin.plugin.' . My::id(), + ['part' => $part] + ); + } + } + + if ('savepasswords' == $action) { + $passwords = self::getPasswords(); + $lines = []; + if (!empty($_POST['login']) && !empty($_POST['password'])) { + $lines[$_POST['login']] = Utils::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] = Utils::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(Utils::passwordFile(), $contents); + + dcCore::app()->blog->triggerBlog(); + + dcPage::addSuccessNotice( + __('Logins successfully updated.') + ); + + dcCore::app()->adminurl->redirect( + 'admin.plugin.' . My::id(), + ['part' => $part] + ); + } + + return true; + } + + public static function render(): void + { + if (!self::$init) { + return; + } + + $part = self::getSection(); + + dcPage::openModule( + My::name(), + dcPage::jsPageTabs() . + dcPage::jsModuleLoad(My::id() . '/js/backend.js') ); - 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] - ); -} - -echo -'' . __('Http password') . '' . -dcPage::jsPageTabs() . -dcPage::jsModuleLoad(basename(__DIR__) . '/js/index.js') . -'' . -dcPage::breadcrumb([ - __('Plugins') => '', - __('Http password') => dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)), - array_search($part, $section_menu) => '', -]) . -dcPage::notices() . - -# Filters select menu list -'
' . -'

' . -form::combo('part', $section_menu, $part) . ' ' . -'' . -form::hidden('p', basename(__DIR__)) . '

' . -'
' . -'

' . array_search($part, $section_menu) . '

'; - -if ('settings' == $part) { - echo ' -
- -

- -

' . - form::combo('crypt', httpPassword::getCryptCombo(), (string) $s->get('crypt')) . '

-

' . - __('Some web servers does not surpport plaintext (no) encryption.') . ' ' . - __('If you change crypt algo, you must edit and resave each users passwords.') . - '

- -

' . - form::field('message', 60, 255, html::escapeHTML((string) $s->get('message'))) . ' -

- -
-

' . - dcCore::app()->formNonce() . - form::hidden(['action'], 'savesettings') . - form::hidden(['part'], $part) . ' - -

'; -} - -if ('logins' == $part) { - $logs = dcCore::app()->log->getLogs(['log_table' => basename(__DIR__)]); - if ($logs->isEmpty()) { echo - '

' . __('Logins history is empty.') . '

'; - } else { - echo ' -
-

' . - dcCore::app()->formNonce() . - form::hidden(['action'], 'savelogins') . - form::hidden(['part'], $part) . ' - -

' . + dcPage::breadcrumb([ + __('Plugins') => '', + My::name() => dcCore::app()->adminurl->get('admin.plugin.' . My::id()), + array_search($part, My::sectionCombo()) => '', + ]) . + dcPage::notices() . - '
' . - '' . - '' . - '' . - '' . - ''; + # Filters select menu list + (new Form('section_menu'))->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id()))->method('get')->fields([ + (new Para())->class('anchor-nav')->items([ + (new Label(__('Select section:')))->for('part')->class('classic'), + (new Select('part'))->default($part)->items(My::sectionCombo()), + (new Submit(['go']))->value(__('Ok')), + (new Hidden(['p'], My::id())), + ]), + ])->render() . - while ($logs->fetch()) { + '

' . array_search($part, My::sectionCombo()) . '

'; + + if ('settings' == $part) { echo - '' . - '' . - '' . - ''; + (new Form('section_settings'))->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => 'settings']))->method('post')->fields([ + // active + (new Para())->items([ + (new Checkbox('active', Utils::isActive()))->value(1), + (new Label(__('Enable http password protection on this blog'), Label::OUTSIDE_LABEL_AFTER))->for('active')->class('classic'), + ]), + // crypt + (new Para())->items([ + (new Label(__('Crypt algorithm:'), Label::OUTSIDE_LABEL_BEFORE))->for('crypt')->class('classic'), + (new Select('crypt'))->default(Utils::cryptMethod())->items(My::cryptCombo()), + ]), + (new Note())->text(__('Some web servers does not surpport plaintext (no) encryption.'))->class('form-note'), + (new Note())->text(__('If you change crypt algo, you must edit and resave each users passwords.'))->class('form-note'), + // message + (new Para())->items([ + (new Label(__('Authentication message:')))->for('message'), + (new Input('message'))->size(60)->maxlenght(255)->value(Utils::httpMessage()), + ]), + (new Div())->class('clear')->items([ + (new Submit(['save']))->value(__('Save')), + (new Hidden(['action'], 'savesettings')), + (new Hidden(['part'], $part)), + (new Text('', dcCore::app()->formNonce())), + ]), + ])->render(); } - echo - '
' . sprintf(__('List of %s last logins.'), $logs->count()) . '
' . __('Login') . '' . __('Date') . '
' . html::escapeHTML($logs->__get('log_msg')) . '' . html::escapeHTML(dt::dt2str(__('%Y-%m-%d %H:%M'), $logs->__get('log_dt'))) . '
'; - } -} + if ('logins' == $part) { + $logs = dcCore::app()->log->getLogs(['log_table' => My::id()]); + if ($logs->isEmpty()) { + echo + '

' . __('Logins history is empty.') . '

'; + } else { + echo + (new Form('section_logins'))->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => 'logins']))->method('post')->fields([ + (new Para())->items([ + (new Submit(['save']))->value(__('Clear logs')), + (new Hidden(['action'], 'savelogins')), + (new Hidden(['part'], $part)), + (new Text('', dcCore::app()->formNonce())), + ]), + ])->render() . -if ('passwords' == $part) { - if (empty($passwords)) { - echo - '

' . __('Authorized users list is empty.') . '

'; - } else { - echo - '
' . - '
' . - '' . - '' . - '' . - '' . - '' . - ''; + '
' . sprintf(__('List of %s authorized users.'), count($passwords)) . '
' . __('Login') . '' . __('New password') . '' . __('Action') . '
' . + '' . + '' . + '' . + '' . + ''; + + while ($logs->fetch()) { + echo + '' . + '' . + '' . + ''; + } + + echo + '
' . sprintf(__('List of %s last logins.'), $logs->count()) . '
' . __('Login') . '' . __('Date') . '
' . Html::escapeHTML($logs->f('log_msg')) . '' . Html::escapeHTML(dt::dt2str(__('%Y-%m-%d %H:%M'), $logs->f('log_dt'))) . '
'; + } + } + + if ('passwords' == $part) { + $passwords = self::getPasswords(); + + if (empty($passwords)) { + echo + '

' . __('Authorized users list is empty.') . '

'; + } else { + $lines = ''; + foreach ($passwords as $login => $pwd) { + $lines .= '' . + '' . + Html::escapeHTML($login) . + '' . + '' . + (new Input(['newpassword[' . Html::escapeHTML($login) . ']']))->size(60)->maxlenght(255)->render() . + '' . + '' . + (new Submit(['edit[' . Html::escapeHTML($login) . ']']))->value(__('Change password'))->render() . + (new Submit(['delete[' . Html::escapeHTML($login) . ']']))->value(__('Delete'))->class('delete')->render() . + '' . + ''; + } + + echo + (new Form('section_passwords'))->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => $part]))->method('post')->fields([ + (new Text( + '', + '
' . + '' . + '' . + '' . + '' . + '' . + '' . + $lines . + '
' . sprintf(__('List of %s authorized users.'), count($passwords)) . '
' . __('Login') . '' . __('New password') . '' . __('Action') . '
' + )), + (new Para())->items([ + (new Hidden(['action'], 'savepasswords')), + (new Hidden(['part'], $part)), + (new Text('', dcCore::app()->formNonce())), + ]), + ])->render(); + } - foreach ($passwords as $login => $pwd) { echo - '' . - '' . - html::escapeHTML($login) . - '' . - '' . - form::field(['newpassword[' . html::escapeHTML($login) . ']'], 60, 255, '') . - '' . - '' . - ' ' . - '' . - '' . - ''; + (new Form('section_new'))->action(dcCore::app()->adminurl->get('admin.plugin.' . My::id(), ['part' => $part]))->method('post')->fields([ + (new Text('h3', Html::escapeHTML(__('Add a user')))), + // login + (new Para())->items([ + (new Label(__('Login:')))->for('login'), + (new Input('login'))->size(60)->maxlenght(255), + ]), + // password + (new Para())->items([ + (new Label(__('Password:')))->for('password'), + (new Input('password'))->size(60)->maxlenght(255), + ]), + (new Para())->items([ + (new Submit(['add']))->value(__('Save')), + (new Hidden(['action'], 'savepasswords')), + (new Hidden(['part'], $part)), + (new Text('', dcCore::app()->formNonce())), + ]), + ])->render(); } - echo - '
-

' . - dcCore::app()->formNonce() . - form::hidden(['action'], 'savepasswords') . - form::hidden(['part'], $part) . ' -

'; + dcPage::closeModule(); } - echo ' -
-

' . __('Add a user') . '

+ private static function getSection(): string + { + $part = $_REQUEST['part'] ?? 'settings'; + if (!in_array($part, My::sectionCombo()) || !Utils::isWritable()) { + $part = 'settings'; + } -

' . - form::field('login', 60, 255, '') . ' -

+ return $part; + } -

' . - form::field('password', 60, 255, '') . ' -

+ private static function getPasswords(): array + { + $passwords = []; + $lines = file(Utils::passwordFile()); + if (!is_array($lines)) { + $lines = []; + } + sort($lines); + foreach ($lines as $line) { + [$login, $pwd] = explode(':', $line, 2); + $passwords[trim($login)] = trim($pwd); + } + unset($lines); -

' . - dcCore::app()->formNonce() . - form::hidden(['action'], 'savepasswords') . - form::hidden(['part'], $part) . ' - -

'; + return $passwords; + } } - -echo -''; diff --git a/src/My.php b/src/My.php new file mode 100644 index 0000000..b38b3c5 --- /dev/null +++ b/src/My.php @@ -0,0 +1,70 @@ +plugins->moduleInfo(self::id(), 'name')); + } + + /** + * Encryption methods combo + */ + public static function cryptCombo(): 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', + ]; + } + + /** + * Admin section menu + */ + public static function sectionCombo(): array + { + return [ + __('Settings') => 'settings', + __('Logins history') => 'logins', + __('Authorized users') => 'passwords', + ]; + } +} diff --git a/src/Prepend.php b/src/Prepend.php index ae410e7..f0e51a4 100644 --- a/src/Prepend.php +++ b/src/Prepend.php @@ -10,15 +10,33 @@ * @copyright Jean-Christian Denis * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html */ -if (!defined('DC_RC_PATH')) { - return null; +declare(strict_types=1); + +namespace Dotclear\Plugin\httpPassword; + +use dcCore; +use dcNsProcess; + +class Prepend extends dcNsProcess +{ + public static function init(): bool + { + self::$init = true; + + return self::$init; + } + + public static function process(): bool + { + if (!self::$init) { + return false; + } + + dcCore::app()->auth->setPermissionType( + My::PERMISSION, + __('Manage http password blog protection') + ); + + return true; + } } - -Clearbricks::lib()->autoload([ - 'httpPassword' => implode(DIRECTORY_SEPARATOR, [__DIR__, 'inc', 'class.httppassword.php']), -]); - -dcCore::app()->auth->setPermissionType( - initHttpPassword::PERMISSION, - __('Manage http password blog protection') -); diff --git a/src/Utils.php b/src/Utils.php index d2b7c30..716c5b3 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -10,20 +10,20 @@ * @copyright Jean-Christian Denis * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html */ -if (!defined('DC_RC_PATH')) { - return null; -} +declare(strict_types=1); -class httpPassword +namespace Dotclear\Plugin\httpPassword; + +use dcCore; + +class Utils { - public static function id(): string - { - return basename(dirname(__DIR__)); - } - + /** + * Crypt password + */ public static function crypt(?string $secret): string { - switch (dcCore::app()->blog->settings->get(self::id())->get('crypt')) { + switch (self::cryptMethod()) { case 'plaintext': $saltlen = -1; $salt = ''; @@ -75,9 +75,44 @@ class httpPassword return($secret); } + /** + * Setting: active + */ + public static function isActive(): bool + { + return (bool) dcCore::app()->blog->settings->get(My::id())->get('active'); + } + + /** + * Setting: crypt + */ + public static function cryptMethod(): string + { + return (string) dcCore::app()->blog->settings->get(My::id())->get('crypt'); + } + + /** + * Setting: message + */ + public static function httpMessage(): string + { + return (string) dcCore::app()->blog->settings->get(My::id())->get('message'); + } + + /** + * Get passwords file path + */ + public static function passwordFile(): string + { + return dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . My::FILE_PASSWORD; + } + + /** + * Check passwords file + */ public static function isWritable(): bool { - if (false === ($fp = fopen(dcCore::app()->blog->public_path . DIRECTORY_SEPARATOR . initHttpPassword::FILE_PASSWORD, 'a+'))) { + if (false === ($fp = fopen(self::passwordFile(), 'a+'))) { return false; } fclose($fp); @@ -85,23 +120,13 @@ class httpPassword 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', - ]; - } - + /** + * Send HTTP message + */ 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'))) . '"'); + header('WWW-Authenticate: Basic realm="' . utf8_decode(htmlspecialchars_decode(self::httpMessage())) . '"'); exit(0); } }