From e7dba0ce7cfaf5e84687593a8b0d58d89fbff302 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Sat, 9 Aug 2014 23:29:13 +0200
Subject: Add basic system of update
- Check on update.freshrss.org for new updates
- Download script
- Apply script
- Need translations and verifications
NOTE: current script on server indicates version 0.7.3 is an update
of 0.8-dev ==> IT'S ONLY FOR MY TESTS!
Script just does a backup of ./data actually...
See https://github.com/marienfressinaud/FreshRSS/issues/411
---
app/Controllers/updateController.php | 106 +++++++++++++++++++++++++++++++++++
1 file changed, 106 insertions(+)
create mode 100644 app/Controllers/updateController.php
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
new file mode 100644
index 000000000..a15cb9fd5
--- /dev/null
+++ b/app/Controllers/updateController.php
@@ -0,0 +1,106 @@
+view->loginOk && Minz_Configuration::isAdmin($current_user)) {
+ Minz_Error::error(
+ 403,
+ array('error' => array(_t('access_denied')))
+ );
+ }
+
+ Minz_View::prependTitle(_t('update_system') . ' · ');
+ }
+
+ public function indexAction() {
+ if (file_exists(UPDATE_FILENAME)) {
+ // There is an update file to apply!
+ $this->view->message = array(
+ 'status' => 'good',
+ 'title' => _t('ok'),
+ 'body' => _t('update_can_apply', _url('update', 'apply'))
+ );
+
+ return;
+ }
+ }
+
+ public function checkAction() {
+ $this->view->change_view('update', 'index');
+
+ if (file_exists(UPDATE_FILENAME)) {
+ // There is already an update file to apply: we don't need to check
+ // the webserver!
+ $this->view->message = array(
+ 'status' => 'good',
+ 'title' => _t('ok'),
+ 'body' => _t('update_can_apply', _url('update', 'apply'))
+ );
+
+ return;
+ }
+
+ $c = curl_init(FRESHRSS_UPDATE_WEBSITE);
+ curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+ $result = curl_exec($c);
+
+ if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) {
+ $res_array = explode("\n", $result, 2);
+ $status = $res_array[0];
+
+ if (strpos($status, 'UPDATE') === 0) {
+ $script = $res_array[1];
+ if (file_put_contents(UPDATE_FILENAME, $script) !== false) {
+ $this->view->message = array(
+ 'status' => 'good',
+ 'title' => _t('ok'),
+ 'body' => _t('update_can_apply', _url('update', 'apply'))
+ );
+ } else {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('update_problem')
+ );
+ }
+ } else {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('no_update')
+ );
+ }
+ } else {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE)
+ );
+ }
+ curl_close($c);
+ }
+
+ public function applyAction() {
+ require(UPDATE_FILENAME);
+ $res = apply_update();
+
+ if ($res === true) {
+ @unlink(UPDATE_FILENAME);
+
+ Minz_Session::_param('notification', array(
+ 'type' => 'good',
+ 'content' => Minz_Translate::t('update_finished')
+ ));
+
+ Minz_Request::forward(array(), true);
+ } else {
+ Minz_Session::_param('notification', array(
+ 'type' => 'bad',
+ 'content' => Minz_Translate::t('update_failed', $res)
+ ));
+
+ Minz_Request::forward(array('c' => 'update'), true);
+ }
+ }
+}
\ No newline at end of file
--
cgit v1.2.3
From 7ed111b1bf152613d17254808a4fcf89f5774297 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Sun, 10 Aug 2014 10:35:17 +0200
Subject: Add translations for update system
---
app/Controllers/updateController.php | 7 +++++--
app/i18n/en.php | 9 +++++++++
app/i18n/fr.php | 11 ++++++++++-
app/views/update/index.phtml | 4 +++-
4 files changed, 27 insertions(+), 4 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index a15cb9fd5..a94af4417 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -11,6 +11,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
Minz_View::prependTitle(_t('update_system') . ' · ');
+ $this->view->last_update_time = 'unknown'; // TODO
}
public function indexAction() {
@@ -61,7 +62,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
$this->view->message = array(
'status' => 'bad',
'title' => _t('damn'),
- 'body' => _t('update_problem')
+ 'body' => _t('update_problem', 'Cannot save the update script')
);
}
} else {
@@ -88,6 +89,8 @@ class FreshRSS_update_Controller extends Minz_ActionController {
if ($res === true) {
@unlink(UPDATE_FILENAME);
+ // TODO: record last update
+
Minz_Session::_param('notification', array(
'type' => 'good',
'content' => Minz_Translate::t('update_finished')
@@ -97,7 +100,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
} else {
Minz_Session::_param('notification', array(
'type' => 'bad',
- 'content' => Minz_Translate::t('update_failed', $res)
+ 'content' => Minz_Translate::t('update_problem', $res)
));
Minz_Request::forward(array('c' => 'update'), true);
diff --git a/app/i18n/en.php b/app/i18n/en.php
index 6110ccb11..6a0b4a139 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -192,6 +192,7 @@ return array (
'informations' => 'Information',
'damn' => 'Damn!',
+ 'ok' => 'Ok!',
'feed_in_error' => 'This feed has encountered a problem. Please verify that it is always reachable then actualize it.',
'feed_empty' => 'This feed is empty. Please verify that it is still maintained.',
'feed_description' => 'Description',
@@ -409,5 +410,13 @@ return array (
'stats_top_feed' => 'Top ten feeds',
'stats_entry_count' => 'Entry count',
+ 'update' => 'Update',
+ 'update_system' => 'Update system',
+ 'update_check' => 'Check for new updates',
+ 'update_last' => 'Last update: %s',
'update_can_apply' => 'There is an available update. Apply',
+ 'update_server_not_found' => 'Update server cannot be found. [%s]',
+ 'no_update' => 'No update to apply',
+ 'update_problem' => 'Update has encountered an error: %s',
+ 'update_finished' => 'Update is now finished!',
);
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index 5f88aa069..d0637b9f7 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -192,6 +192,7 @@ return array (
'informations' => 'Informations',
'damn' => 'Arf !',
+ 'ok' => 'Ok !',
'feed_in_error' => 'Ce flux a rencontré un problème. Veuillez vérifier qu’il est toujours accessible puis actualisez-le.',
'feed_empty' => 'Ce flux est vide. Veuillez vérifier qu’il est toujours maintenu.',
'feed_description' => 'Description',
@@ -409,5 +410,13 @@ return array (
'stats_top_feed' => 'Les dix plus gros flux',
'stats_entry_count' => 'Nombre d’articles',
- 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer',
+ 'update' => 'Mise à jour',
+ 'update_system' => 'Système de mise à jour',
+ 'update_check' => 'Vérifier les mises à jour',
+ 'update_last' => 'Dernière mise à jour : %s',
+ 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer la mise à jour',
+ 'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]',
+ 'no_update' => 'Aucune mise à jour à appliquer',
+ 'update_problem' => 'La mise à jour a rencontré un problème : %s',
+ 'update_finished' => 'La mise à jour est terminée !',
);
diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml
index a1a872845..8f6ee6269 100644
--- a/app/views/update/index.phtml
+++ b/app/views/update/index.phtml
@@ -12,8 +12,10 @@
message) || $this->message['status'] !== 'good') { ?>
-
last_update_time); ?>
+
+
+
--
cgit v1.2.3
From 9a5d6245fbeb413766362fd6b2c4f5f5b6a22a22 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Sun, 10 Aug 2014 10:55:51 +0200
Subject: Improve update API
Update script must implement 4 functions:
- apply_update() to perform the update (most important). Return true if
all is ok, else false.
- need_info_update() returns true if we need more info for update, else
false. If this function always returns false, you don't need to
implement following functions (but it's better to not forget)
- ask_info_update() should be a HTML form to ask infos. Method must be
post and action must point to _url('update', 'apply') (or leave it
blank)
- save_info_update() is called for POST requests (to save form from
ask_info_update())
---
app/Controllers/updateController.php | 37 +++++++++++++++++++++---------------
app/views/update/apply.phtml | 9 +++++++++
app/views/update/index.phtml | 2 ++
3 files changed, 33 insertions(+), 15 deletions(-)
create mode 100644 app/views/update/apply.phtml
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index a94af4417..1095f9da7 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -84,26 +84,33 @@ class FreshRSS_update_Controller extends Minz_ActionController {
public function applyAction() {
require(UPDATE_FILENAME);
- $res = apply_update();
- if ($res === true) {
- @unlink(UPDATE_FILENAME);
+ if (Minz_Request::isPost()) {
+ save_info_update();
+ }
- // TODO: record last update
+ if (!need_info_update()) {
+ $res = apply_update();
- Minz_Session::_param('notification', array(
- 'type' => 'good',
- 'content' => Minz_Translate::t('update_finished')
- ));
+ if ($res === true) {
+ @unlink(UPDATE_FILENAME);
- Minz_Request::forward(array(), true);
- } else {
- Minz_Session::_param('notification', array(
- 'type' => 'bad',
- 'content' => Minz_Translate::t('update_problem', $res)
- ));
+ // TODO: record last update
+
+ Minz_Session::_param('notification', array(
+ 'type' => 'good',
+ 'content' => Minz_Translate::t('update_finished')
+ ));
- Minz_Request::forward(array('c' => 'update'), true);
+ Minz_Request::forward(array(), true);
+ } else {
+ Minz_Session::_param('notification', array(
+ 'type' => 'bad',
+ 'content' => Minz_Translate::t('update_problem', $res)
+ ));
+
+ Minz_Request::forward(array('c' => 'update'), true);
+ }
}
}
}
\ No newline at end of file
diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml
new file mode 100644
index 000000000..d7ea466c5
--- /dev/null
+++ b/app/views/update/apply.phtml
@@ -0,0 +1,9 @@
+partial('aside_configure'); ?>
+
+
\ No newline at end of file
diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml
index 8f6ee6269..1824c02b8 100644
--- a/app/views/update/index.phtml
+++ b/app/views/update/index.phtml
@@ -1,6 +1,8 @@
partial('aside_configure'); ?>
+
+
message)) { ?>
--
cgit v1.2.3
From 3ca8c7ec4c55b4fa751fbcdc8e28f28351c4a967 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Sun, 10 Aug 2014 11:52:18 +0200
Subject: Litlle improvements (update system)
- Check UPDATE_FILENAME exists before applying update
- Add empty line at the end of files
---
app/Controllers/updateController.php | 7 ++++++-
app/views/update/apply.phtml | 2 +-
2 files changed, 7 insertions(+), 2 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 1095f9da7..fa62f4a70 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -79,10 +79,15 @@ class FreshRSS_update_Controller extends Minz_ActionController {
'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE)
);
}
+
curl_close($c);
}
public function applyAction() {
+ if (!file_exists(UPDATE_FILENAME)) {
+ Minz_Request::forward(array('c' => 'update'), true);
+ }
+
require(UPDATE_FILENAME);
if (Minz_Request::isPost()) {
@@ -113,4 +118,4 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml
index d7ea466c5..30566c7ab 100644
--- a/app/views/update/apply.phtml
+++ b/app/views/update/apply.phtml
@@ -6,4 +6,4 @@
-
\ No newline at end of file
+
--
cgit v1.2.3
From 909d8747ba09f9c9a6ac895f1f4f0763bdb27a55 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Tue, 12 Aug 2014 20:15:46 +0200
Subject: Update system now uses HTTPS connection
- Add some curl checks
- Refactor code
---
app/Controllers/updateController.php | 60 +++++++++++++++++++-----------------
constants.php | 2 +-
2 files changed, 33 insertions(+), 29 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index fa62f4a70..857d975b2 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -44,43 +44,47 @@ class FreshRSS_update_Controller extends Minz_ActionController {
$c = curl_init(FRESHRSS_UPDATE_WEBSITE);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
+ curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
$result = curl_exec($c);
+ $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE);
+ curl_close($c);
- if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) {
- $res_array = explode("\n", $result, 2);
- $status = $res_array[0];
-
- if (strpos($status, 'UPDATE') === 0) {
- $script = $res_array[1];
- if (file_put_contents(UPDATE_FILENAME, $script) !== false) {
- $this->view->message = array(
- 'status' => 'good',
- 'title' => _t('ok'),
- 'body' => _t('update_can_apply', _url('update', 'apply'))
- );
- } else {
- $this->view->message = array(
- 'status' => 'bad',
- 'title' => _t('damn'),
- 'body' => _t('update_problem', 'Cannot save the update script')
- );
- }
- } else {
- $this->view->message = array(
- 'status' => 'bad',
- 'title' => _t('damn'),
- 'body' => _t('no_update')
- );
- }
- } else {
+ if ($c_status !== 200) {
$this->view->message = array(
'status' => 'bad',
'title' => _t('damn'),
'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE)
);
+ return;
}
- curl_close($c);
+ $res_array = explode("\n", $result, 2);
+ $status = $res_array[0];
+ if (strpos($status, 'UPDATE') !== 0) {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('no_update')
+ );
+
+ return;
+ }
+
+ $script = $res_array[1];
+ if (file_put_contents(UPDATE_FILENAME, $script) !== false) {
+ $this->view->message = array(
+ 'status' => 'good',
+ 'title' => _t('ok'),
+ 'body' => _t('update_can_apply', _url('update', 'apply'))
+ );
+ } else {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('update_problem', 'Cannot save the update script')
+ );
+ }
}
public function applyAction() {
diff --git a/constants.php b/constants.php
index a968b82f4..ba9c508dc 100644
--- a/constants.php
+++ b/constants.php
@@ -1,7 +1,7 @@
Date: Mon, 8 Sep 2014 19:52:21 +0200
Subject: Check FRESHRSS_PATH is writable.
FRESHRSS_PATH needs to be writable before performing update.
---
app/Controllers/updateController.php | 41 ++++++++++++------------------------
app/i18n/en.php | 2 ++
app/i18n/fr.php | 2 ++
3 files changed, 18 insertions(+), 27 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 857d975b2..5d5ec3586 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -15,15 +15,19 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
public function indexAction() {
- if (file_exists(UPDATE_FILENAME)) {
+ if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) {
+ $this->view->message = array(
+ 'status' => 'bad',
+ 'title' => _t('damn'),
+ 'body' => _t('file_is_nok', FRESHRSS_PATH)
+ );
+ } elseif (file_exists(UPDATE_FILENAME)) {
// There is an update file to apply!
$this->view->message = array(
'status' => 'good',
'title' => _t('ok'),
'body' => _t('update_can_apply', _url('update', 'apply'))
);
-
- return;
}
}
@@ -33,11 +37,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
if (file_exists(UPDATE_FILENAME)) {
// There is already an update file to apply: we don't need to check
// the webserver!
- $this->view->message = array(
- 'status' => 'good',
- 'title' => _t('ok'),
- 'body' => _t('update_can_apply', _url('update', 'apply'))
- );
+ Minz_Request::forward(array('c' => 'update'));
return;
}
@@ -73,11 +73,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
$script = $res_array[1];
if (file_put_contents(UPDATE_FILENAME, $script) !== false) {
- $this->view->message = array(
- 'status' => 'good',
- 'title' => _t('ok'),
- 'body' => _t('update_can_apply', _url('update', 'apply'))
- );
+ Minz_Request::forward(array('c' => 'update'));
} else {
$this->view->message = array(
'status' => 'bad',
@@ -88,7 +84,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
public function applyAction() {
- if (!file_exists(UPDATE_FILENAME)) {
+ if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH)) {
Minz_Request::forward(array('c' => 'update'), true);
}
@@ -104,21 +100,12 @@ class FreshRSS_update_Controller extends Minz_ActionController {
if ($res === true) {
@unlink(UPDATE_FILENAME);
- // TODO: record last update
+ // TODO: record last update_finished
- Minz_Session::_param('notification', array(
- 'type' => 'good',
- 'content' => Minz_Translate::t('update_finished')
- ));
-
- Minz_Request::forward(array(), true);
+ Minz_Request::good(_t('update_finished'));
} else {
- Minz_Session::_param('notification', array(
- 'type' => 'bad',
- 'content' => Minz_Translate::t('update_problem', $res)
- ));
-
- Minz_Request::forward(array('c' => 'update'), true);
+ Minz_Request::bad(_t('update_problem', $res),
+ array('c' => 'update', 'a' => 'index'));
}
}
}
diff --git a/app/i18n/en.php b/app/i18n/en.php
index 95356af2c..c5911cde7 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -152,6 +152,8 @@ return array (
'public' => 'Public',
'invalid_login' => 'Login is invalid',
+ 'file_is_nok' => 'Check permissions on %s directory. HTTP server must have rights to write into.',
+
// VIEWS
'save' => 'Save',
'delete' => 'Delete',
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index 8437e872e..789a0bb98 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -152,6 +152,8 @@ return array (
'public' => 'Public',
'invalid_login' => 'L’identifiant est invalide !',
+ 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire %s. Le serveur HTTP doit être capable d’écrire dedans.',
+
// VIEWS
'save' => 'Enregistrer',
'delete' => 'Supprimer',
--
cgit v1.2.3
From d59eebf5423afb94ff68550aa9218674889ab4ad Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Mon, 8 Sep 2014 20:07:09 +0200
Subject: Add data/last_update.txt
Remember last update timestamp.
---
app/Controllers/updateController.php | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 5d5ec3586..5424792f4 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -11,7 +11,11 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
Minz_View::prependTitle(_t('update_system') . ' · ');
- $this->view->last_update_time = 'unknown'; // TODO
+ $this->view->last_update_time = 'unknown';
+ $timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
+ if (is_numeric($timestamp) && $timestamp > 0) {
+ $this->view->last_update_time = timestamptodate($timestamp);
+ }
}
public function indexAction() {
@@ -99,8 +103,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
if ($res === true) {
@unlink(UPDATE_FILENAME);
-
- // TODO: record last update_finished
+ @file_put_contents(DATA_PATH . '/last_update.txt', time());
Minz_Request::good(_t('update_finished'));
} else {
--
cgit v1.2.3
From 213bc2b9ddef88fd9e3cb50ac0893742f5fdd101 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Fri, 12 Sep 2014 21:07:53 +0200
Subject: Check if update has been done during last minute
Cancel check action if update has been done during last hour.
---
app/Controllers/updateController.php | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 5424792f4..ec6778d51 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -38,7 +38,11 @@ class FreshRSS_update_Controller extends Minz_ActionController {
public function checkAction() {
$this->view->change_view('update', 'index');
- if (file_exists(UPDATE_FILENAME)) {
+ // Get the last update. If already check during the last hour, do nothing.
+ $last_update = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
+ $check_last_hour = (time() - 3600) <= $last_update;
+
+ if (file_exists(UPDATE_FILENAME) || $check_last_hour) {
// There is already an update file to apply: we don't need to check
// the webserver!
Minz_Request::forward(array('c' => 'update'));
--
cgit v1.2.3
From 098f5e6d747cf442b72f75a22a9ce43d36605d65 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Fri, 12 Sep 2014 21:10:45 +0200
Subject: Log error if update.freshrss.org is unreachable
---
app/Controllers/updateController.php | 5 +++++
1 file changed, 5 insertions(+)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index ec6778d51..4c1dd002c 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -56,9 +56,14 @@ class FreshRSS_update_Controller extends Minz_ActionController {
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
$result = curl_exec($c);
$c_status = curl_getinfo($c, CURLINFO_HTTP_CODE);
+ $c_error = curl_error($c);
curl_close($c);
if ($c_status !== 200) {
+ Minz_Log::error(
+ 'Error during update (HTTP code ' . $c_status . '): ' . $c_error
+ );
+
$this->view->message = array(
'status' => 'bad',
'title' => _t('damn'),
--
cgit v1.2.3
From a4e43e9c53ac404d16af5d913a56eeb444b1ce10 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Tue, 16 Sep 2014 18:30:24 +0200
Subject: Message if last update checking is close
last_update.txt is updated even if there is no update
If last_update.txt has been modified during last hour, show a message
See https://github.com/marienfressinaud/FreshRSS/issues/480#issuecomment-55765373
---
app/Controllers/updateController.php | 11 ++++++-----
app/i18n/en.php | 2 +-
app/i18n/fr.php | 2 +-
app/views/update/index.phtml | 17 +++++++++++++----
4 files changed, 21 insertions(+), 11 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 4c1dd002c..72244e9c7 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -12,9 +12,11 @@ class FreshRSS_update_Controller extends Minz_ActionController {
Minz_View::prependTitle(_t('update_system') . ' · ');
$this->view->last_update_time = 'unknown';
+ $this->view->check_last_hour = false;
$timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
if (is_numeric($timestamp) && $timestamp > 0) {
$this->view->last_update_time = timestamptodate($timestamp);
+ $this->view->check_last_hour = (time() - 3600) <= $timestamp;
}
}
@@ -38,13 +40,10 @@ class FreshRSS_update_Controller extends Minz_ActionController {
public function checkAction() {
$this->view->change_view('update', 'index');
- // Get the last update. If already check during the last hour, do nothing.
- $last_update = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
- $check_last_hour = (time() - 3600) <= $last_update;
-
- if (file_exists(UPDATE_FILENAME) || $check_last_hour) {
+ if (file_exists(UPDATE_FILENAME) || $this->view->check_last_hour) {
// There is already an update file to apply: we don't need to check
// the webserver!
+ // Or if already check during the last hour, do nothing.
Minz_Request::forward(array('c' => 'update'));
return;
@@ -81,6 +80,8 @@ class FreshRSS_update_Controller extends Minz_ActionController {
'body' => _t('no_update')
);
+ @file_put_contents(DATA_PATH . '/last_update.txt', time());
+
return;
}
diff --git a/app/i18n/en.php b/app/i18n/en.php
index c5911cde7..8f39115ad 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -424,7 +424,7 @@ return array (
'update' => 'Update',
'update_system' => 'Update system',
'update_check' => 'Check for new updates',
- 'update_last' => 'Last update: %s',
+ 'update_last' => 'Last verification: %s',
'update_can_apply' => 'There is an available update. Apply',
'update_server_not_found' => 'Update server cannot be found. [%s]',
'no_update' => 'No update to apply',
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index 789a0bb98..48b4c1732 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -424,7 +424,7 @@ return array (
'update' => 'Mise à jour',
'update_system' => 'Système de mise à jour',
'update_check' => 'Vérifier les mises à jour',
- 'update_last' => 'Dernière mise à jour : %s',
+ 'update_last' => 'Dernière vérification : %s',
'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer la mise à jour',
'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]',
'no_update' => 'Aucune mise à jour à appliquer',
diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml
index 1824c02b8..5be8b1e8b 100644
--- a/app/views/update/index.phtml
+++ b/app/views/update/index.phtml
@@ -5,17 +5,26 @@
+
+ last_update_time); ?>
+
+
message)) { ?>
message['title']; ?>
message['body']; ?>
+ check_last_hour) { ?>
+
+
+
+
- message) || $this->message['status'] !== 'good') { ?>
-
- last_update_time); ?>
-
+ check_last_hour &&
+ (empty($this->message) || $this->message['status'] !== 'good')) {
+ ?>
--
cgit v1.2.3
From a3b5e72729be08f79585c782d497f49edd11c064 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Wed, 17 Sep 2014 13:26:32 +0200
Subject: Move button to apply update outside the message
---
app/Controllers/updateController.php | 4 +++-
app/i18n/en.php | 3 ++-
app/i18n/fr.php | 3 ++-
app/views/update/index.phtml | 4 ++++
4 files changed, 11 insertions(+), 3 deletions(-)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 72244e9c7..78d636163 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -11,6 +11,7 @@ class FreshRSS_update_Controller extends Minz_ActionController {
}
Minz_View::prependTitle(_t('update_system') . ' · ');
+ $this->view->update_to_apply = false;
$this->view->last_update_time = 'unknown';
$this->view->check_last_hour = false;
$timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
@@ -29,10 +30,11 @@ class FreshRSS_update_Controller extends Minz_ActionController {
);
} elseif (file_exists(UPDATE_FILENAME)) {
// There is an update file to apply!
+ $this->view->update_to_apply = true;
$this->view->message = array(
'status' => 'good',
'title' => _t('ok'),
- 'body' => _t('update_can_apply', _url('update', 'apply'))
+ 'body' => _t('update_can_apply')
);
}
}
diff --git a/app/i18n/en.php b/app/i18n/en.php
index 562697585..f84593cb5 100644
--- a/app/i18n/en.php
+++ b/app/i18n/en.php
@@ -428,7 +428,8 @@ return array (
'update_system' => 'Update system',
'update_check' => 'Check for new updates',
'update_last' => 'Last verification: %s',
- 'update_can_apply' => 'There is an available update. Apply',
+ 'update_can_apply' => 'There is an available update.',
+ 'update_apply' => 'Apply',
'update_server_not_found' => 'Update server cannot be found. [%s]',
'no_update' => 'No update to apply',
'update_problem' => 'Update has encountered an error: %s',
diff --git a/app/i18n/fr.php b/app/i18n/fr.php
index 9516f66cb..da5819529 100644
--- a/app/i18n/fr.php
+++ b/app/i18n/fr.php
@@ -428,7 +428,8 @@ return array (
'update_system' => 'Système de mise à jour',
'update_check' => 'Vérifier les mises à jour',
'update_last' => 'Dernière vérification : %s',
- 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer la mise à jour',
+ 'update_can_apply' => 'Il y’a une mise à jour à appliquer.',
+ 'update_apply' => 'Appliquer la mise à jour',
'update_server_not_found' => 'Le serveur de mise à jour n’a pas été trouvé. [%s]',
'no_update' => 'Aucune mise à jour à appliquer',
'update_problem' => 'La mise à jour a rencontré un problème : %s',
diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml
index 5be8b1e8b..401f6acd6 100644
--- a/app/views/update/index.phtml
+++ b/app/views/update/index.phtml
@@ -29,4 +29,8 @@
+
+ update_to_apply) { ?>
+
+
--
cgit v1.2.3
From 3b8c381689334a15e7c034425f8615860dc3fa13 Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Wed, 17 Sep 2014 13:37:39 +0200
Subject: No cache for update system
See https://github.com/marienfressinaud/FreshRSS/issues/616
---
app/Controllers/updateController.php | 2 ++
1 file changed, 2 insertions(+)
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index 78d636163..da5bddc65 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -10,6 +10,8 @@ class FreshRSS_update_Controller extends Minz_ActionController {
);
}
+ invalidateHttpCache();
+
Minz_View::prependTitle(_t('update_system') . ' · ');
$this->view->update_to_apply = false;
$this->view->last_update_time = 'unknown';
--
cgit v1.2.3
From 79aa5beaf44af13a1828bfa5fc824a08c62054dc Mon Sep 17 00:00:00 2001
From: Marien Fressinaud
Date: Mon, 6 Oct 2014 23:29:20 +0200
Subject: Refactor authentication system.
Big work, not finished. A lot of features have been removed.
See https://github.com/marienfressinaud/FreshRSS/issues/655
---
app/Controllers/categoryController.php | 2 +-
app/Controllers/configureController.php | 2 +-
app/Controllers/entryController.php | 2 +-
app/Controllers/feedController.php | 2 +-
app/Controllers/importExportController.php | 2 +-
app/Controllers/indexController.php | 296 ++++++-----------------------
app/Controllers/statsController.php | 2 +-
app/Controllers/subscriptionController.php | 2 +-
app/Controllers/updateController.php | 2 +-
app/Controllers/usersController.php | 2 +-
app/FreshRSS.php | 135 ++-----------
app/Models/Auth.php | 209 ++++++++++++++++++++
app/layout/aside_flux.phtml | 6 +-
app/layout/header.phtml | 32 +---
app/layout/nav_menu.phtml | 4 +-
app/views/helpers/view/normal_view.phtml | 6 +-
app/views/index/index.phtml | 2 +-
app/views/index/login.phtml | 1 -
app/views/index/logout.phtml | 1 -
app/views/index/resetAuth.phtml | 33 ----
20 files changed, 309 insertions(+), 434 deletions(-)
create mode 100644 app/Models/Auth.php
delete mode 100644 app/views/index/login.phtml
delete mode 100644 app/views/index/logout.phtml
delete mode 100644 app/views/index/resetAuth.phtml
(limited to 'app/Controllers/updateController.php')
diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php
index c79f37fa4..537a2b210 100644
--- a/app/Controllers/categoryController.php
+++ b/app/Controllers/categoryController.php
@@ -12,7 +12,7 @@ class FreshRSS_category_Controller extends Minz_ActionController {
*
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php
index 789e9dfb0..7e77a757a 100755
--- a/app/Controllers/configureController.php
+++ b/app/Controllers/configureController.php
@@ -10,7 +10,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController {
* underlying framework.
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php
index c46fbf346..a1dfacb4d 100755
--- a/app/Controllers/entryController.php
+++ b/app/Controllers/entryController.php
@@ -10,7 +10,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController {
* underlying framework.
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index 18829d252..2a7238eaf 100755
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -10,7 +10,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
* underlying framework.
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
// Token is useful in the case that anonymous refresh is forbidden
// and CRON task cannot be used with php command so the user can
// set a CRON task to refresh his feeds by using token inside url
diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php
index 57759f277..aaac1b68b 100644
--- a/app/Controllers/importExportController.php
+++ b/app/Controllers/importExportController.php
@@ -10,7 +10,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
* underlying framework.
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php
index 0d2eff700..3006480f9 100755
--- a/app/Controllers/indexController.php
+++ b/app/Controllers/indexController.php
@@ -8,7 +8,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$token = $this->view->conf->token;
// check if user is logged in
- if (!$this->view->loginOk && !Minz_Configuration::allowAnonymous()) {
+ if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) {
$token_param = Minz_Request::param('token', '');
$token_is_ok = ($token != '' && $token === $token_param);
if ($output === 'rss' && !$token_is_ok) {
@@ -20,7 +20,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
} elseif ($output !== 'rss') {
// "hard" redirection is not required, just ask dispatcher to
// forward to the login form without 302 redirection
- Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'));
+ Minz_Request::forward(array('c' => 'index', 'a' => 'login'));
return;
}
}
@@ -207,7 +207,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
}
public function logsAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
@@ -229,265 +229,91 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$this->view->logsPaginator->_currentPage($page);
}
+ /**
+ * This action handles the login page.
+ */
public function loginAction() {
- $this->view->_useLayout(false);
-
- $url = 'https://verifier.login.persona.org/verify';
- $assert = Minz_Request::param('assertion');
- $params = 'assertion=' . $assert . '&audience=' .
- urlencode(Minz_Url::display(null, 'php', true));
- $ch = curl_init();
- $options = array(
- CURLOPT_URL => $url,
- CURLOPT_RETURNTRANSFER => TRUE,
- CURLOPT_POST => 2,
- CURLOPT_POSTFIELDS => $params
- );
- curl_setopt_array($ch, $options);
- $result = curl_exec($ch);
- curl_close($ch);
-
- $res = json_decode($result, true);
-
- $loginOk = false;
- $reason = '';
- if ($res['status'] === 'okay') {
- $email = filter_var($res['email'], FILTER_VALIDATE_EMAIL);
- if ($email != '') {
- $personaFile = DATA_PATH . '/persona/' . $email . '.txt';
- if (($currentUser = @file_get_contents($personaFile)) !== false) {
- $currentUser = trim($currentUser);
- if (ctype_alnum($currentUser)) {
- try {
- $this->conf = new FreshRSS_Configuration($currentUser);
- $loginOk = strcasecmp($email, $this->conf->mail_login) === 0;
- } catch (Minz_Exception $e) {
- $reason = 'Invalid configuration for user [' . $currentUser . ']! ' . $e->getMessage(); //Permission denied or conf file does not exist
- }
- } else {
- $reason = 'Invalid username format [' . $currentUser . ']!';
- }
- }
- } else {
- $reason = 'Invalid email format [' . $res['email'] . ']!';
- }
- }
- if ($loginOk) {
- Minz_Session::_param('currentUser', $currentUser);
- Minz_Session::_param('mail', $email);
- $this->view->loginOk = true;
- invalidateHttpCache();
- } else {
- $res = array();
- $res['status'] = 'failure';
- $res['reason'] = $reason == '' ? _t('invalid_login') : $reason;
- Minz_Log::warning('Persona: ' . $res['reason']);
+ if (FreshRSS_Auth::hasAccess()) {
+ Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
}
- header('Content-Type: application/json; charset=UTF-8');
- $this->view->res = json_encode($res);
- }
-
- public function logoutAction() {
- $this->view->_useLayout(false);
invalidateHttpCache();
- Minz_Session::_param('currentUser');
- Minz_Session::_param('mail');
- Minz_Session::_param('passwordHash');
- }
-
- private static function makeLongTermCookie($username, $passwordHash) {
- do {
- $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true));
- $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt';
- } while (file_exists($tokenFile));
- if (@file_put_contents($tokenFile, $username . "\t" . $passwordHash) === false) {
- return false;
- }
- $expire = time() + 2629744; //1 month //TODO: Use a configuration instead
- Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire);
- Minz_Session::_param('token', $token);
- return $token;
- }
-
- private static function deleteLongTermCookie() {
- Minz_Session::deleteLongTermCookie('FreshRSS_login');
- $token = Minz_Session::param('token', null);
- if (ctype_alnum($token)) {
- @unlink(DATA_PATH . '/tokens/' . $token . '.txt');
- }
- Minz_Session::_param('token');
- if (rand(0, 10) === 1) {
- self::purgeTokens();
- }
- }
- private static function purgeTokens() {
- $oldest = time() - 2629744; //1 month //TODO: Use a configuration instead
- foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $fileInfo) {
- if ($fileInfo->getExtension() === 'txt' && $fileInfo->getMTime() < $oldest) {
- @unlink($fileInfo->getPathname());
- }
+ $auth_type = Minz_Configuration::authType();
+ switch ($auth_type) {
+ case 'form':
+ Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'));
+ break;
+ case 'http_auth':
+ case 'none':
+ // It should not happened!
+ Minz_Error::error(404);
+ default:
+ // TODO load plugin instead
+ Minz_Error::error(404);
}
}
+ /**
+ *
+ */
public function formLoginAction() {
- if ($this->view->loginOk) {
+ if (FreshRSS_Auth::hasAccess()) {
Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
}
- if (Minz_Request::isPost()) {
- $ok = false;
- $nonce = Minz_Session::param('nonce');
- $username = Minz_Request::param('username', '');
- $c = Minz_Request::param('challenge', '');
- if (ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce)) {
- if (!function_exists('password_verify')) {
- include_once(LIB_PATH . '/password_compat.php');
- }
- try {
- $conf = new FreshRSS_Configuration($username);
- $s = $conf->passwordHash;
- $ok = password_verify($nonce . $s, $c);
- if ($ok) {
- Minz_Session::_param('currentUser', $username);
- Minz_Session::_param('passwordHash', $s);
- if (Minz_Request::param('keep_logged_in', false)) {
- self::makeLongTermCookie($username, $s);
- } else {
- self::deleteLongTermCookie();
- }
- } else {
- Minz_Log::warning('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c);
- }
- } catch (Minz_Exception $me) {
- Minz_Log::warning('Login failure: ' . $me->getMessage());
- }
- } else {
- Minz_Log::debug('Invalid credential parameters: user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce);
- }
- if (!$ok) {
- $notif = array(
- 'type' => 'bad',
- 'content' => _t('invalid_login')
- );
- Minz_Session::_param('notification', $notif);
- }
- $this->view->_useLayout(false);
- Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
- } elseif (Minz_Configuration::unsafeAutologinEnabled() && isset($_GET['u']) && isset($_GET['p'])) {
- Minz_Session::_param('currentUser');
- Minz_Session::_param('mail');
- Minz_Session::_param('passwordHash');
- $username = ctype_alnum($_GET['u']) ? $_GET['u'] : '';
- $passwordPlain = $_GET['p'];
- Minz_Request::_param('p'); //Discard plain-text password ASAP
- $_GET['p'] = '';
- if (!function_exists('password_verify')) {
- include_once(LIB_PATH . '/password_compat.php');
- }
- try {
- $conf = new FreshRSS_Configuration($username);
- $s = $conf->passwordHash;
- $ok = password_verify($passwordPlain, $s);
- unset($passwordPlain);
- if ($ok) {
- Minz_Session::_param('currentUser', $username);
- Minz_Session::_param('passwordHash', $s);
- } else {
- Minz_Log::warning('Unsafe password mismatch for user ' . $username);
- }
- } catch (Minz_Exception $me) {
- Minz_Log::warning('Unsafe login failure: ' . $me->getMessage());
- }
- Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
- } elseif (!Minz_Configuration::canLogIn()) {
- Minz_Error::error(
- 403,
- array('error' => array(_t('access_denied')))
- );
- }
invalidateHttpCache();
- }
- public function formLogoutAction() {
- $this->view->_useLayout(false);
- invalidateHttpCache();
- Minz_Session::_param('currentUser');
- Minz_Session::_param('mail');
- Minz_Session::_param('passwordHash');
- self::deleteLongTermCookie();
- Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
- }
-
- public function resetAuthAction() {
- Minz_View::prependTitle(_t('auth_reset') . ' · ');
- Minz_View::appendScript(Minz_Url::display(
- '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')
- ));
-
- $this->view->no_form = false;
- // Enable changement of auth only if Persona!
- if (Minz_Configuration::authType() != 'persona') {
- $this->view->message = array(
- 'status' => 'bad',
- 'title' => _t('damn'),
- 'body' => _t('auth_not_persona')
- );
- $this->view->no_form = true;
- return;
- }
-
- $conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser());
- // Admin user must have set its master password.
- if (!$conf->passwordHash) {
- $this->view->message = array(
- 'status' => 'bad',
- 'title' => _t('damn'),
- 'body' => _t('auth_no_password_set')
- );
- $this->view->no_form = true;
- return;
- }
-
- invalidateHttpCache();
+ $file_mtime = @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js');
+ Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . $file_mtime));
if (Minz_Request::isPost()) {
$nonce = Minz_Session::param('nonce');
$username = Minz_Request::param('username', '');
- $c = Minz_Request::param('challenge', '');
- if (!(ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce))) {
- Minz_Log::debug('Invalid credential parameters:' .
- ' user=' . $username .
- ' challenge=' . $c .
- ' nonce=' . $nonce);
+ $challenge = Minz_Request::param('challenge', '');
+ try {
+ $conf = new FreshRSS_Configuration($username);
+ } catch(Minz_Exception $e) {
+ // $username is not a valid user, nor the configuration file!
+ Minz_Log::warning('Login failure: ' . $e->getMessage());
Minz_Request::bad(_t('invalid_login'),
- array('c' => 'index', 'a' => 'resetAuth'));
- }
-
- if (!function_exists('password_verify')) {
- include_once(LIB_PATH . '/password_compat.php');
+ array('c' => 'index', 'a' => 'login'));
}
- $s = $conf->passwordHash;
- $ok = password_verify($nonce . $s, $c);
+ $ok = FreshRSS_FormAuth::checkCredentials(
+ $username, $conf->passwordHash, $nonce, $challenge
+ );
if ($ok) {
- Minz_Configuration::_authType('form');
- $ok = Minz_Configuration::writeFile();
-
- if ($ok) {
- Minz_Request::good(_t('auth_form_set'));
+ // Set session parameter to give access to the user.
+ Minz_Session::_param('currentUser', $username);
+ Minz_Session::_param('passwordHash', $conf->passwordHash);
+ FreshRSS_Auth::giveAccess();
+
+ // Set cookie parameter if nedded.
+ if (Minz_Request::param('keep_logged_in', false)) {
+ FreshRSS_FormAuth::makeCookie($username, $conf->passwordHash);
} else {
- Minz_Request::bad(_t('auth_form_not_set'),
- array('c' => 'index', 'a' => 'resetAuth'));
+ FreshRSS_FormAuth::deleteCookie();
}
- } else {
- Minz_Log::debug('Password mismatch for user ' . $username .
- ', nonce=' . $nonce . ', c=' . $c);
+ // All is good, go back to the index.
+ Minz_Request::good(_t('login'),
+ array('c' => 'index', 'a' => 'index'));
+ } else {
+ Minz_Log::warning('Password mismatch for' .
+ ' user=' . $username .
+ ', nonce=' . $nonce .
+ ', c=' . $challenge);
Minz_Request::bad(_t('invalid_login'),
- array('c' => 'index', 'a' => 'resetAuth'));
+ array('c' => 'index', 'a' => 'login'));
}
}
}
+
+ public function logoutAction() {
+ invalidateHttpCache();
+ FreshRSS_Auth::removeAccess();
+ Minz_Request::good(_t('disconnected'),
+ array('c' => 'index', 'a' => 'index'));
+ }
}
diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php
index 99c57c809..0e3430fcc 100644
--- a/app/Controllers/statsController.php
+++ b/app/Controllers/statsController.php
@@ -118,7 +118,7 @@ class FreshRSS_stats_Controller extends Minz_ActionController {
* underlying framework.
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403, array('error' => array(_t('access_denied')))
);
diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php
index 7cc8179a0..a89168eb3 100644
--- a/app/Controllers/subscriptionController.php
+++ b/app/Controllers/subscriptionController.php
@@ -10,7 +10,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
* underlying framework.
*/
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php
index da5bddc65..9da1e8657 100644
--- a/app/Controllers/updateController.php
+++ b/app/Controllers/updateController.php
@@ -3,7 +3,7 @@
class FreshRSS_update_Controller extends Minz_ActionController {
public function firstAction() {
$current_user = Minz_Session::param('currentUser', '');
- if (!$this->view->loginOk && Minz_Configuration::isAdmin($current_user)) {
+ if (!FreshRSS_Auth::hasAccess() && Minz_Configuration::isAdmin($current_user)) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php
index 7d0171bc7..c2b1d163f 100644
--- a/app/Controllers/usersController.php
+++ b/app/Controllers/usersController.php
@@ -5,7 +5,7 @@ class FreshRSS_users_Controller extends Minz_ActionController {
const BCRYPT_COST = 9; //Will also have to be computed client side on mobile devices, so do not use a too high cost
public function firstAction() {
- if (!$this->view->loginOk) {
+ if (!FreshRSS_Auth::hasAccess()) {
Minz_Error::error(
403,
array('error' => array(_t('access_denied')))
diff --git a/app/FreshRSS.php b/app/FreshRSS.php
index efd302ecc..35a37b887 100644
--- a/app/FreshRSS.php
+++ b/app/FreshRSS.php
@@ -4,130 +4,33 @@ class FreshRSS extends Minz_FrontController {
if (!isset($_SESSION)) {
Minz_Session::init('FreshRSS');
}
- $loginOk = $this->accessControl(Minz_Session::param('currentUser', ''));
+
+ FreshRSS_Auth::init();
+ $this->loadConfiguration();
$this->loadParamsView();
if (Minz_Request::isPost() && !is_referer_from_same_domain()) {
- $loginOk = false; //Basic protection against XSRF attacks
+ //Basic protection against XSRF attacks
+ FreshRSS_Auth::removeAccess();
Minz_Error::error(
403,
array('error' => array(_t('access_denied') . ' [HTTP_REFERER=' .
- htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']'))
+ htmlspecialchars(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']) . ']'))
);
}
- Minz_View::_param('loginOk', $loginOk);
- $this->loadStylesAndScripts($loginOk); //TODO: Do not load that when not needed, e.g. some Ajax requests
+ $this->loadStylesAndScripts();
$this->loadNotifications();
$this->loadExtensions();
}
- private static function getCredentialsFromLongTermCookie() {
- $token = Minz_Session::getLongTermCookie('FreshRSS_login');
- if (!ctype_alnum($token)) {
- return array();
- }
- $tokenFile = DATA_PATH . '/tokens/' . $token . '.txt';
- $mtime = @filemtime($tokenFile);
- if ($mtime + 2629744 < time()) { //1 month //TODO: Use a configuration instead
- @unlink($tokenFile);
- return array(); //Expired or token does not exist
- }
- $credentials = @file_get_contents($tokenFile);
- return $credentials === false ? array() : explode("\t", $credentials, 2);
- }
-
- private function accessControl($currentUser) {
- if ($currentUser == '') {
- switch (Minz_Configuration::authType()) {
- case 'form':
- $credentials = self::getCredentialsFromLongTermCookie();
- if (isset($credentials[1])) {
- $currentUser = trim($credentials[0]);
- Minz_Session::_param('passwordHash', trim($credentials[1]));
- }
- $loginOk = $currentUser != '';
- if (!$loginOk) {
- $currentUser = Minz_Configuration::defaultUser();
- Minz_Session::_param('passwordHash');
- }
- break;
- case 'http_auth':
- $currentUser = httpAuthUser();
- $loginOk = $currentUser != '';
- break;
- case 'persona':
- $loginOk = false;
- $email = filter_var(Minz_Session::param('mail'), FILTER_VALIDATE_EMAIL);
- if ($email != '') { //TODO: Remove redundancy with indexController
- $personaFile = DATA_PATH . '/persona/' . $email . '.txt';
- if (($currentUser = @file_get_contents($personaFile)) !== false) {
- $currentUser = trim($currentUser);
- $loginOk = true;
- }
- }
- if (!$loginOk) {
- $currentUser = Minz_Configuration::defaultUser();
- }
- break;
- case 'none':
- $currentUser = Minz_Configuration::defaultUser();
- $loginOk = true;
- break;
- default:
- $currentUser = Minz_Configuration::defaultUser();
- $loginOk = false;
- break;
- }
- } else {
- $loginOk = true;
- }
-
- if (!ctype_alnum($currentUser)) {
- Minz_Session::_param('currentUser', '');
- die('Invalid username [' . $currentUser . ']!');
- }
-
+ private function loadConfiguration() {
+ $current_user = Minz_Session::param('currentUser');
try {
- $this->conf = new FreshRSS_Configuration($currentUser);
+ $this->conf = new FreshRSS_Configuration($current_user);
Minz_View::_param('conf', $this->conf);
- Minz_Session::_param('currentUser', $currentUser);
- } catch (Minz_Exception $me) {
- $loginOk = false;
- try {
- $this->conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser());
- Minz_Session::_param('currentUser', Minz_Configuration::defaultUser());
- Minz_View::_param('conf', $this->conf);
- $notif = array(
- 'type' => 'bad',
- 'content' => 'Invalid configuration for user [' . $currentUser . ']!',
- );
- Minz_Session::_param('notification', $notif);
- Minz_Log::warning($notif['content'] . ' ' . $me->getMessage());
- Minz_Session::_param('currentUser', '');
- } catch (Exception $e) {
- die($e->getMessage());
- }
- }
-
- if ($loginOk) {
- switch (Minz_Configuration::authType()) {
- case 'form':
- $loginOk = Minz_Session::param('passwordHash') === $this->conf->passwordHash;
- break;
- case 'http_auth':
- $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0;
- break;
- case 'persona':
- $loginOk = strcasecmp(Minz_Session::param('mail'), $this->conf->mail_login) === 0;
- break;
- case 'none':
- $loginOk = true;
- break;
- default:
- $loginOk = false;
- break;
- }
+ } catch(Minz_Exception $e) {
+ Minz_Log::error('Cannot load configuration file of user `' . $current_user . '`');
+ die($e->getMessage());
}
- return $loginOk;
}
private function loadParamsView() {
@@ -140,7 +43,7 @@ class FreshRSS extends Minz_FrontController {
}
}
- private function loadStylesAndScripts($loginOk) {
+ private function loadStylesAndScripts() {
$theme = FreshRSS_Themes::load($this->conf->theme);
if ($theme) {
foreach($theme['files'] as $file) {
@@ -158,16 +61,6 @@ class FreshRSS extends Minz_FrontController {
}
}
- switch (Minz_Configuration::authType()) {
- case 'form':
- if (!$loginOk) {
- Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')));
- }
- break;
- case 'persona':
- Minz_View::appendScript('https://login.persona.org/include.js');
- break;
- }
Minz_View::appendScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')));
Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js')));
Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js')));
diff --git a/app/Models/Auth.php b/app/Models/Auth.php
new file mode 100644
index 000000000..c4a3abd98
--- /dev/null
+++ b/app/Models/Auth.php
@@ -0,0 +1,209 @@
+getMessage());
+ }
+
+ switch (Minz_Configuration::authType()) {
+ case 'form':
+ self::$login_ok = Minz_Session::param('passwordHash') === $conf->passwordHash;
+ break;
+ case 'http_auth':
+ self::$login_ok = strcasecmp($current_user, httpAuthUser()) === 0;
+ break;
+ case 'none':
+ self::$login_ok = true;
+ break;
+ default:
+ // TODO: extensions
+ self::$login_ok = false;
+ }
+
+ Minz_Session::_param('loginOk', self::$login_ok);
+ }
+
+ /**
+ * Returns if current user is connected.
+ *
+ * @return boolean true if user is connected, false else.
+ */
+ public static function hasAccess() {
+ return self::$login_ok;
+ }
+
+ /**
+ * Removes all accesses for the current user.
+ */
+ public static function removeAccess() {
+ Minz_Session::_param('loginOk');
+ self::$login_ok = false;
+ Minz_Session::_param('currentUser', Minz_Configuration::defaultUser());
+
+ switch (Minz_Configuration::authType()) {
+ case 'form':
+ Minz_Session::_param('passwordHash');
+ FreshRSS_FormAuth::deleteCookie();
+ break;
+ case 'http_auth':
+ case 'none':
+ // Nothing to do...
+ break;
+ default:
+ // TODO: extensions
+ }
+ }
+}
+
+
+class FreshRSS_FormAuth {
+ public static function checkCredentials($username, $hash, $nonce, $challenge) {
+ if (!ctype_alnum($username) ||
+ !ctype_graph($challenge) ||
+ !ctype_alnum($nonce)) {
+ Minz_Log::debug('Invalid credential parameters:' .
+ ' user=' . $username .
+ ' challenge=' . $challenge .
+ ' nonce=' . $nonce);
+ return false;
+ }
+
+ if (!function_exists('password_verify')) {
+ include_once(LIB_PATH . '/password_compat.php');
+ }
+
+ return password_verify($nonce . $hash, $challenge);
+ }
+
+ public static function getCredentialsFromCookie() {
+ $token = Minz_Session::getLongTermCookie('FreshRSS_login');
+ if (!ctype_alnum($token)) {
+ return array();
+ }
+
+ $token_file = DATA_PATH . '/tokens/' . $token . '.txt';
+ $mtime = @filemtime($token_file);
+ if ($mtime + 2629744 < time()) {
+ // Token has expired (> 1 month) or does not exist.
+ // TODO: 1 month -> use a configuration instead
+ @unlink($token_file);
+ return array();
+ }
+
+ $credentials = @file_get_contents($token_file);
+ return $credentials === false ? array() : explode("\t", $credentials, 2);
+ }
+
+ public static function makeCookie($username, $password_hash) {
+ do {
+ $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true));
+ $token_file = DATA_PATH . '/tokens/' . $token . '.txt';
+ } while (file_exists($token_file));
+
+ if (@file_put_contents($token_file, $username . "\t" . $password_hash) === false) {
+ return false;
+ }
+
+ $expire = time() + 2629744; //1 month //TODO: Use a configuration instead
+ Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire);
+ return $token;
+ }
+
+ public static function deleteCookie() {
+ $token = Minz_Session::getLongTermCookie('FreshRSS_login');
+ Minz_Session::deleteLongTermCookie('FreshRSS_login');
+ if (ctype_alnum($token)) {
+ @unlink(DATA_PATH . '/tokens/' . $token . '.txt');
+ }
+
+ if (rand(0, 10) === 1) {
+ self::purgeTokens();
+ }
+ }
+
+ public static function purgeTokens() {
+ $oldest = time() - 2629744; // 1 month // TODO: Use a configuration instead
+ foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $file_info) {
+ // $extension = $file_info->getExtension(); doesn't work in PHP < 5.3.7
+ $extension = pathinfo($file_info->getFilename(), PATHINFO_EXTENSION);
+ if ($extension === 'txt' && $file_info->getMTime() < $oldest) {
+ @unlink($file_info->getPathname());
+ }
+ }
+ }
+}
diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml
index a8ae2f424..a66be2ed9 100644
--- a/app/layout/aside_flux.phtml
+++ b/app/layout/aside_flux.phtml
@@ -2,7 +2,7 @@
@@ -83,11 +83,11 @@