Browse Source

Add a way to disable/enable users (#3056)

If you want to block users without deleting their account, you can now
disable them from the interface.
Alexis Degrugillier 5 years ago
parent
commit
caeb660f29

+ 5 - 0
app/Controllers/authController.php

@@ -131,6 +131,11 @@ class FreshRSS_auth_Controller extends Minz_ActionController {
 				return;
 			}
 
+			if (!$conf->enabled) {
+				Minz_Error::error(403, array(_t('feedback.auth.login.invalid')), false);
+				return;
+			}
+
 			$ok = FreshRSS_FormAuth::checkCredentials(
 				$username, $conf->passwordHash, $nonce, $challenge
 			);

+ 22 - 5
app/Controllers/userController.php

@@ -191,6 +191,12 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 				case 'demote':
 					$this->demoteAction();
 					break;
+				case 'enable':
+					$this->enableAction();
+					break;
+				case 'disable':
+					$this->disableAction();
+					break;
 			}
 		}
 
@@ -332,6 +338,7 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			$ok = self::createUser($new_user_name, $email, $passwordPlain, array(
 				'language' => Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language),
 				'is_admin' => Minz_Request::paramBoolean('new_user_is_admin'),
+				'enabled' => true,
 			));
 			Minz_Request::_param('new_user_passwordPlain');	//Discard plain-text password ASAP
 			$_POST['new_user_passwordPlain'] = '';
@@ -550,14 +557,22 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 	}
 
 	public function promoteAction() {
-		$this->switchAdminAction(true);
+		$this->toggleAction('is_admin', true);
 	}
 
 	public function demoteAction() {
-		$this->switchAdminAction(false);
+		$this->toggleAction('is_admin', false);
+	}
+
+	public function enableAction() {
+		$this->toggleAction('enabled', true);
+	}
+
+	public function disableAction() {
+		$this->toggleAction('enabled', false);
 	}
 
-	private function switchAdminAction($isAdmin) {
+	private function toggleAction($field, $value) {
 		if (!FreshRSS_Auth::hasAccess('admin')) {
 			Minz_Error::error(403);
 		}
@@ -575,9 +590,10 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			Minz_Error::error(500);
 		}
 
-		$userConfig->_param('is_admin', $isAdmin);
+		$userConfig->_param($field, $value);
 
 		$ok = $userConfig->save();
+		FreshRSS_UserDAO::touch($username);
 
 		if ($ok) {
 			Minz_Request::good(_t('feedback.user.updated', $username), array('c' => 'user', 'a' => 'manage'));
@@ -597,7 +613,6 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			Minz_Error::error(404);
 		}
 
-		$this->view->isDefaultUser = $username === FreshRSS_Context::$system_conf->default_user;
 		$this->view->username = $username;
 		$this->view->details = $this->retrieveUserDetails($username);
 	}
@@ -615,8 +630,10 @@ class FreshRSS_user_Controller extends Minz_ActionController {
 			'database_size' => $databaseDAO->size(),
 			'language' => $userConfiguration->language,
 			'mail_login' => $userConfiguration->mail_login,
+			'enabled' => $userConfiguration->enabled,
 			'is_admin' => $userConfiguration->is_admin,
 			'last_user_activity' => date('c', FreshRSS_UserDAO::mtime($username)),
+			'is_default' => FreshRSS_Context::$system_conf->default_user === $username,
 		);
 	}
 }

+ 4 - 0
app/actualize_script.php

@@ -53,6 +53,10 @@ if ($system_conf->default_user !== '') {
 $limits = $system_conf->limits;
 $min_last_activity = time() - $limits['max_inactivity'];
 foreach ($users as $user) {
+	if (!get_user_configuration($user)->enabled) {
+		notice('FreshRSS skip disabled user ' . $user);
+		continue;
+	}
 	if (($user !== $system_conf->default_user) &&
 			(FreshRSS_UserDAO::mtime($user) < $min_last_activity)) {
 		notice('FreshRSS skip inactive user ' . $user);

+ 1 - 0
app/i18n/cz/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Delete user',	// TODO - Translation
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Jazyk',

+ 1 - 0
app/i18n/de/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Lösche Benutzer',
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Sprache',

+ 1 - 0
app/i18n/en/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',
 		'delete_users' => 'Delete user',
 		'email' => 'Email address',
+		'enabled' => 'Enabled',
 		'feed_count' => 'Feeds',
 		'is_admin' => 'Is admin',
 		'language' => 'Language',

+ 1 - 0
app/i18n/es/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Delete user',	// TODO - Translation
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Idioma',

+ 2 - 1
app/i18n/fr/admin.php

@@ -190,8 +190,9 @@ return array(
 		'database_size' => 'Volumétrie',
 		'delete_users' => 'Supprimer un utilisateur',
 		'email' => 'Adresse email',
+		'enabled' => 'Actif',
 		'feed_count' => 'Flux',
-		'is_admin' => 'Admin ?',
+		'is_admin' => 'Admin',
 		'language' => 'Langue',
 		'last_user_activity' => 'Dernière activité utilisateur',
 		'list' => 'Liste des utilisateurs',

+ 1 - 0
app/i18n/he/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Delete user',	// TODO - Translation
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'שפה',

+ 1 - 0
app/i18n/it/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Delete user',	// TODO - Translation
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Lingua',

+ 1 - 0
app/i18n/kr/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => '사용자 삭제',
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => '언어',

+ 1 - 0
app/i18n/nl/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Databasegrootte',
 		'delete_users' => 'Verwijder gebruiker',
 		'email' => 'Emailadres',
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',
 		'is_admin' => 'Is beheerder',
 		'language' => 'Taal',

+ 1 - 0
app/i18n/oc/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Talha basa de donadas',
 		'delete_users' => 'Suprimir un utilizaire',
 		'email' => 'Adreça electronica',
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Flux',
 		'is_admin' => 'Es admin',
 		'language' => 'Lenga',

+ 1 - 0
app/i18n/pt-br/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Tamanho do banco de dados',
 		'delete_users' => 'Deletar usuário',
 		'email' => 'Endereço de email',
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',
 		'is_admin' => 'É administrador',
 		'language' => 'Idioma',

+ 1 - 0
app/i18n/ru/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Delete user',	// TODO - Translation
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Язык',

+ 1 - 0
app/i18n/sk/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Zmazať používateľa',
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Jazyk',

+ 1 - 0
app/i18n/tr/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => 'Database size',	// TODO - Translation
 		'delete_users' => 'Delete user',	// TODO - Translation
 		'email' => 'Email address',	// TODO - Translation
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => 'Feeds',	// TODO - Translation
 		'is_admin' => 'Is admin',	// TODO - Translation
 		'language' => 'Dil',

+ 1 - 0
app/i18n/zh-cn/admin.php

@@ -190,6 +190,7 @@ return array(
 		'database_size' => '数据库大小',
 		'delete_users' => '删除用户',
 		'email' => '邮箱地址',
+		'enabled' => 'Enabled',	// TODO - Translation
 		'feed_count' => '订阅源数',
 		'is_admin' => '管理员',
 		'language' => '语言',

+ 1 - 0
app/install.php

@@ -224,6 +224,7 @@ function saveStep3() {
 				[
 					'language' => $_SESSION['language'],
 					'is_admin' => true,
+					'enabled' => true,
 				]
 			);
 		} catch (Exception $e) {

+ 8 - 1
app/views/user/details.phtml

@@ -1,6 +1,8 @@
 <?php $this->partial('aside_configure'); ?>
 
+<?php $isDefault = $this->details['is_default']; ?>
 <?php $isAdmin = $this->details['is_admin']; ?>
+<?php $enabled = $this->details['enabled']; ?>
 
 <div class="post">
     <a href="<?= _url('user', 'manage'); ?>"><?= _t('admin.user.back_to_manage'); ?></a>
@@ -68,11 +70,16 @@
                 <button type="submit" class="btn btn-important" name="action" value="update"><?= _t('gen.action.update') ?></button>
                 <button type="submit" class="btn btn-attention confirm" name="action" value="purge"><?= _t('gen.action.purge') ?></button>
                 <button type="submit" class="btn btn-attention confirm" name="action" value="delete"><?= _t('gen.action.remove') ?></button>
-                <?php if ($isAdmin && !$this->isDefaultUser): ?>
+                <?php if ($isAdmin && !$isDefault): ?>
                     <button type="submit" class="btn btn-attention confirm" name="action" value="demote"><?= _t('gen.action.demote') ?></button>
                 <?php elseif (!$isAdmin): ?>
                     <button type="submit" class="btn btn-attention confirm" name="action" value="promote"><?= _t('gen.action.promote') ?></button>
                 <?php endif; ?>
+                <?php if ($enabled && !$isDefault): ?>
+                    <button type="submit" class="btn btn-attention" name="action" value="disable"><?= _t('gen.action.disable') ?></button>
+                <?php elseif (!$enabled): ?>
+                    <button type="submit" class="btn btn-attention" name="action" value="enable"><?= _t('gen.action.enable') ?></button>
+                <?php endif; ?>
             <div>
         </div>
     </form>

+ 3 - 1
app/views/user/manage.phtml

@@ -71,6 +71,7 @@
 		<thead>
 			<tr>
 				<th><?= _t('admin.user.username') ?></th>
+				<th><?= _t('admin.user.enabled') ?></th>
 				<th><?= _t('admin.user.is_admin') ?></th>
 				<th><?= _t('admin.user.email') ?></th>
 				<th><?= _t('admin.user.language') ?></th>
@@ -82,8 +83,9 @@
 		</thead>
 		<tbody>
 			<?php foreach ($this->users as $username => $values) : ?>
-				<tr>
+				<tr <?php if ($values['is_default']):?>class="default-user"<?php endif; ?>>
 					<td><a href="<?= _url('user', 'details', 'username', $username) ?>"><?= $username ?></a></td>
+					<td><?= $values['enabled'] ? '✔' : ' ' ?></td>
 					<td><?= $values['is_admin'] ? '✔' : ' ' ?></td>
 					<td><?= $values['mail_login'] ?></td>
 					<td><?= _t("gen.lang.{$values['language']}") ?></td>

+ 16 - 10
cli/user-info.php

@@ -2,7 +2,7 @@
 <?php
 require(__DIR__ . '/_cli.php');
 
-const DATA_FORMAT = "%-7s | %-20s | %-25s | %-15s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-5s | %-10s\n";
+const DATA_FORMAT = "%-7s | %-20s | %-5s | %-7s | %-25s | %-15s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-5s | %-10s\n";
 
 $params = array(
 	'user:',
@@ -35,8 +35,10 @@ if ($formatJson) {
 if (array_key_exists('header', $options)) {
 	printf(
 		DATA_FORMAT,
-		'is_default',
+		'default',
 		'user',
+		'admin',
+		'enabled',
 		'last user activity',
 		'space used',
 		'categories',
@@ -64,16 +66,18 @@ foreach ($users as $username) {
 	$nbFavorites = $entryDAO->countUnreadReadFavorites();
 
 	$data = array(
-		'is_default' => $username === FreshRSS_Context::$system_conf->default_user ? '*' : '',
+		'default' => $username === FreshRSS_Context::$system_conf->default_user ? '*' : '',
 		'user' => $username,
+		'admin' => $userConfiguration->is_admin ? '*' : '',
+		'enabled' => $userConfiguration->enabled ? '*' : '',
 		'last_user_activity' => FreshRSS_UserDAO::mtime($username),
 		'database_size' => $databaseDAO->size(),
-		'categories' => $catDAO->count(),
-		'feeds' => count($feedDAO->listFeedsIds()),
-		'reads' => $nbEntries['read'],
-		'unreads' => $nbEntries['unread'],
-		'favourites' => $nbFavorites['all'],
-		'tags' => $tagDAO->count(),
+		'categories' => (int) $catDAO->count(),
+		'feeds' => (int) count($feedDAO->listFeedsIds()),
+		'reads' => (int) $nbEntries['read'],
+		'unreads' => (int) $nbEntries['unread'],
+		'favourites' => (int) $nbFavorites['all'],
+		'tags' => (int) $tagDAO->count(),
 		'lang' => $userConfiguration->language,
 		'mail_login' => $userConfiguration->mail_login,
 	);
@@ -82,7 +86,9 @@ foreach ($users as $username) {
 		$data['database_size'] = format_bytes($data['database_size']);
 	}
 	if ($formatJson) {
-		$data['is_default'] = !empty($data['is_default']);
+		$data['default'] = !empty($data['default']);
+		$data['admin'] = !empty($data['admin']);
+		$data['enabled'] = !empty($data['enabled']);
 		$data['last_user_activity'] = gmdate('Y-m-d\TH:i:s\Z', $data['last_user_activity']);
 		$jsonOutput[] = $data;
 	} else {

+ 1 - 0
config-user.default.php

@@ -4,6 +4,7 @@
 # `./data/config-user.custom.php` file instead, containing the keys you want to
 # override.
 return array (
+	'enabled' => true,
 	'is_admin' => false,
 	'language' => 'en',
 	'archiving' => [

+ 1 - 1
p/api/fever.php

@@ -163,7 +163,7 @@ class FeverAPI
 				$username = trim($username);
 				Minz_Session::_param('currentUser', $username);
 				$user_conf = get_user_configuration($username);
-				if ($user_conf != null && $feverKey === $user_conf->feverKey) {
+				if ($user_conf != null && $feverKey === $user_conf->feverKey && $user_conf->enabled) {
 					FreshRSS_Context::$user_conf = $user_conf;
 					Minz_Translate::init(FreshRSS_Context::$user_conf->language);
 					$this->entryDAO = FreshRSS_Factory::createEntryDao();

+ 4 - 0
p/api/greader.php

@@ -157,6 +157,10 @@ function authorizationToUser() {
 					Minz_Log::warning('Invalid API user ' . $user . ': configuration cannot be found.');
 					unauthorized();
 				}
+				if (!FreshRSS_Context::$user_conf->enabled) {
+					Minz_Log::warning('Invalid API user ' . $user . ': configuration cannot be found.');
+					unauthorized();
+				}
 				if ($headerAuthX[1] === sha1(FreshRSS_Context::$system_conf->salt . $user . FreshRSS_Context::$user_conf->apiPasswordHash)) {
 					return $user;
 				} else {

+ 5 - 0
p/api/pshb.php

@@ -140,6 +140,11 @@ foreach ($users as $userFilename) {
 			Minz_Translate::reset(FreshRSS_Context::$user_conf->language);
 		}
 
+		if (!FreshRSS_Context::$user_conf->enabled) {
+			Minz_Log::warning('FreshRSS skip disabled user ' . $username);
+			continue;
+		}
+
 		list($updated_feeds, $feed, $nb_new_articles) = FreshRSS_feed_Controller::actualizeFeed(0, $self, false, $simplePie);
 		if ($updated_feeds > 0 || $feed != false) {
 			$nb++;

+ 4 - 0
p/themes/base-theme/template.css

@@ -1203,6 +1203,10 @@ input:checked + .slide-container .properties {
 	white-space: pre-line;
 }
 
+.default-user {
+	font-style: italic;
+}
+
 /*=== READER */
 /*===========*/
 .reader .nav_menu .toggle_aside {

+ 4 - 0
p/themes/base-theme/template.rtl.css

@@ -1203,6 +1203,10 @@ input:checked + .slide-container .properties {
 	white-space: pre-line;
 }
 
+.default-user {
+	font-style: italic;
+}
+
 /*=== READER */
 /*===========*/
 .reader .nav_menu .toggle_aside {