diff options
| author | 2016-10-22 12:58:06 +0200 | |
|---|---|---|
| committer | 2016-10-22 12:58:06 +0200 | |
| commit | e1f214e9e2e09a83a9920e33fbf617dfe48fbb7e (patch) | |
| tree | 916b23c04ce53891e8d9e2af6ed413c7f7379491 | |
| parent | 40b5d3f933e27f37894f58f99c63409f2cdd2d5a (diff) | |
CLI list-users and create-user
https://github.com/FreshRSS/FreshRSS/issues/1095
https://github.com/FreshRSS/FreshRSS/issues/1090
| -rw-r--r-- | app/Controllers/userController.php | 125 | ||||
| -rw-r--r-- | app/Models/Context.php | 2 | ||||
| -rw-r--r-- | app/Models/Feed.php | 2 | ||||
| -rwxr-xr-x | app/actualize_script.php | 4 | ||||
| -rw-r--r-- | cli/.htaccess | 3 | ||||
| -rw-r--r-- | cli/_cli.php | 39 | ||||
| -rw-r--r-- | cli/create-user.php | 41 | ||||
| -rw-r--r-- | cli/index.html | 13 | ||||
| -rw-r--r-- | cli/list-users.php | 14 | ||||
| -rw-r--r-- | lib/lib_rss.php | 9 |
10 files changed, 184 insertions, 68 deletions
diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index c259ffde9..f880b951d 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -24,6 +24,16 @@ class FreshRSS_user_Controller extends Minz_ActionController { } } + private static function hashPassword($passwordPlain) { + if (!function_exists('password_hash')) { + include_once(LIB_PATH . '/password_compat.php'); + } + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); + $passwordPlain = ''; + $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + return $passwordHash == '' ? '' : $passwordHash; + } + /** * This action displays the user profile page. */ @@ -41,12 +51,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { if ($passwordPlain != '') { Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP $_POST['newPasswordPlain'] = ''; - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); - $passwordPlain = ''; - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + $passwordHash = self::hashPassword($passwordPlain); $ok &= ($passwordHash != ''); FreshRSS_Context::$user_conf->passwordHash = $passwordHash; } @@ -54,12 +59,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $passwordPlain = Minz_Request::param('apiPasswordPlain', '', true); if ($passwordPlain != '') { - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); - $passwordPlain = ''; - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + $passwordHash = self::hashPassword($passwordPlain); $ok &= ($passwordHash != ''); FreshRSS_Context::$user_conf->apiPasswordHash = $passwordHash; } @@ -99,6 +99,53 @@ class FreshRSS_user_Controller extends Minz_ActionController { $this->view->size_user = $entryDAO->size(); } + public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array()) { + if (!is_array($userConfig)) { + $userConfig = array(); + } + + $ok = ($new_user_name != '') && ctype_alnum($new_user_name); + + if ($ok) { + $languages = Minz_Translate::availableLanguages(); + if (empty($userConfig['language']) || !in_array($userConfig['language'], $languages)) { + $userConfig['language'] = 'en'; + } + + $default_user = FreshRSS_Context::$system_conf->default_user; + $ok &= (strcasecmp($new_user_name, $default_user) !== 0); //It is forbidden to alter the default user + + $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive + + $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); + $ok &= !file_exists($configPath); + } + if ($ok) { + $passwordHash = ''; + if ($passwordPlain != '') { + $passwordHash = self::hashPassword($passwordPlain); + $ok &= ($passwordHash != ''); + } + + $apiPasswordHash = ''; + if ($apiPasswordPlain != '') { + $apiPasswordHash = self::hashPassword($apiPasswordPlain); + $ok &= ($apiPasswordHash != ''); + } + } + if ($ok) { + mkdir(join_path(DATA_PATH, 'users', $new_user_name)); + $userConfig['passwordHash'] = $passwordHash; + $userConfig['apiPasswordHash'] = $apiPasswordHash; + $ok &= (file_put_contents($configPath, "<?php\n return " . var_export($userConfig, true) . ';') !== false); + } + if ($ok) { + $userDAO = new FreshRSS_UserDAO(); + $ok &= $userDAO->createUser($new_user_name, $userConfig['language']); + } + return $ok; + } + /** * This action creates a new user. * @@ -116,57 +163,13 @@ class FreshRSS_user_Controller extends Minz_ActionController { FreshRSS_Auth::hasAccess('admin') || !max_registrations_reached() )) { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - - $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); - $languages = Minz_Translate::availableLanguages(); - if (!in_array($new_user_language, $languages)) { - $new_user_language = FreshRSS_Context::$user_conf->language; - } - $new_user_name = Minz_Request::param('new_user_name'); - $ok = ($new_user_name != '') && ctype_alnum($new_user_name); - - if ($ok) { - $default_user = FreshRSS_Context::$system_conf->default_user; - $ok &= (strcasecmp($new_user_name, $default_user) !== 0); //It is forbidden to alter the default user - - $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive + $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); + $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); - $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); - $ok &= !file_exists($configPath); - } - if ($ok) { - $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); - $passwordHash = ''; - if ($passwordPlain != '') { - Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP - $_POST['new_user_passwordPlain'] = ''; - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); - $passwordPlain = ''; - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js - $ok &= ($passwordHash != ''); - } - if (empty($passwordHash)) { - $passwordHash = ''; - } - } - if ($ok) { - mkdir(join_path(DATA_PATH, 'users', $new_user_name)); - $config_array = array( - 'language' => $new_user_language, - 'passwordHash' => $passwordHash, - ); - $ok &= (file_put_contents($configPath, "<?php\n return " . var_export($config_array, true) . ';') !== false); - } - if ($ok) { - $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->createUser($new_user_name, $new_user_language); - } + $ok = self::createUser($new_user_name, $passwordPlain, '', array('language' => $new_user_language)); + Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP + $_POST['new_user_passwordPlain'] = ''; invalidateHttpCache(); $notif = array( diff --git a/app/Models/Context.php b/app/Models/Context.php index fe4fa6281..fd0e79fc1 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -37,7 +37,7 @@ class FreshRSS_Context { public static $id_max = ''; public static $sinceHours = 0; - public static $isCron = false; + public static $isCli = false; /** * Initialize the context. diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 55c2db4d6..97cb1c47e 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -141,7 +141,7 @@ class FreshRSS_Feed extends Minz_Model { if (!file_exists($txt)) { file_put_contents($txt, $url); } - if (FreshRSS_Context::$isCron) { + if (FreshRSS_Context::$isCli) { $ico = $favicons_dir . $this->hash() . '.ico'; $ico_mtime = @filemtime($ico); $txt_mtime = @filemtime($txt); diff --git a/app/actualize_script.php b/app/actualize_script.php index 78712d721..deaa1bf7c 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -28,13 +28,13 @@ $app = new FreshRSS(); $system_conf = Minz_Configuration::get('system'); $system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!) -FreshRSS_Context::$isCron = true; +FreshRSS_Context::$isCli = true; // Create the list of users to actualize. // Users are processed in a random order but always start with admin $users = listUsers(); shuffle($users); -if ($system_conf->default_user !== ''){ +if ($system_conf->default_user !== '') { array_unshift($users, $system_conf->default_user); $users = array_unique($users); } diff --git a/cli/.htaccess b/cli/.htaccess new file mode 100644 index 000000000..9e768397d --- /dev/null +++ b/cli/.htaccess @@ -0,0 +1,3 @@ +Order Allow,Deny +Deny from all +Satisfy all diff --git a/cli/_cli.php b/cli/_cli.php new file mode 100644 index 000000000..cb6d8ec32 --- /dev/null +++ b/cli/_cli.php @@ -0,0 +1,39 @@ +<?php +if (php_sapi_name() !== 'cli') { + die('FreshRSS error: This PHP script may only be invoked from command line!'); +} + +require(dirname(__FILE__) . '/../constants.php'); +require(LIB_PATH . '/lib_rss.php'); + +Minz_Configuration::register('system', + DATA_PATH . '/config.php', + DATA_PATH . '/config.default.php'); +FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); +Minz_Translate::init(); + +FreshRSS_Context::$isCli = true; + +function fail($message) { + fwrite(STDERR, $message . "\n"); + die(1); +} + +function cliInitUser($username) { + if (!ctype_alnum($username)) { + fail('FreshRSS error: invalid username: ' . $username . "\n"); + } + + $usernames = listUsers(); + if (!in_array($username, $usernames)) { + fail('FreshRSS error: user not found: ' . $username . "\n"); + } + + FreshRSS_Context::$user_conf = get_user_configuration($username); + if (FreshRSS_Context::$user_conf == null) { + fail('FreshRSS error: invalid configuration for user: ' . $username . "\n"); + } + new Minz_ModelPdo($username); + + return $username; +} diff --git a/cli/create-user.php b/cli/create-user.php new file mode 100644 index 000000000..08c057af8 --- /dev/null +++ b/cli/create-user.php @@ -0,0 +1,41 @@ +#!/usr/bin/php +<?php +require('_cli.php'); + +$options = getopt('', array( + 'user:', + 'password::', + 'api-password::', + 'language::', + 'email::', + 'token::', + )); + +if (empty($options['user'])) { + fail('Usage: ' . basename(__FILE__) . " --user=username --password='password' --api-password='api_password'" . + " --language=en --email=user@example.net --token='longRandomString'"); +} +$new_user_name = $options['user']; +if (!ctype_alnum($new_user_name)) { + fail('FreshRSS error: invalid username “' . $new_user_name . '”'); +} + +$usernames = listUsers(); +if (preg_grep("/^$new_user_name$/i", $usernames)) { + fail('FreshRSS error: username already taken “' . $new_user_name . '”'); +} + +echo 'FreshRSS creating user “', $new_user_name, "”…\n"; + +$ok = FreshRSS_user_Controller::createUser($new_user_name, + empty($options['password']) ? '' : $options['password'], + empty($options['api-password']) ? '' : $options['api-password'], + array( + 'language' => empty($options['language']) ? '' : $options['language'], + 'token' => empty($options['token']) ? '' : $options['token'], + )); + +invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); + +echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; +exit($ok ? 0 : 1); diff --git a/cli/index.html b/cli/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/cli/index.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB"> +<head> +<meta charset="UTF-8" /> +<meta http-equiv="Refresh" content="0; url=/" /> +<title>Redirection</title> +<meta name="robots" content="noindex" /> +</head> + +<body> +<p><a href="/">Redirection</a></p> +</body> +</html> diff --git a/cli/list-users.php b/cli/list-users.php new file mode 100644 index 000000000..cc1cf5269 --- /dev/null +++ b/cli/list-users.php @@ -0,0 +1,14 @@ +#!/usr/bin/php +<?php +require('_cli.php'); + +$users = listUsers(); +sort($users); +if ($system_conf->default_user !== '') { + array_unshift($users, $system_conf->default_user); + $users = array_unique($users); +} + +foreach ($users as $user) { + echo $user, "\n"; +} diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 75046fd54..143b55bee 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -282,9 +282,12 @@ function uSecString() { return str_pad($t['usec'], 6, '0'); } -function invalidateHttpCache() { - Minz_Session::_param('touch', uTimeString()); - return touch(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt')); +function invalidateHttpCache($username = '') { + if (($username == '') || (!ctype_alnum($username))) { + Minz_Session::_param('touch', uTimeString()); + $username = Minz_Session::param('currentUser', '_'); + } + return touch(join_path(DATA_PATH, 'users', $username, 'log.txt')); } function listUsers() { |
