summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2016-02-17 14:49:04 +0100
committerThomas Müller <thomas.mueller@tmit.eu>2016-02-17 14:49:04 +0100
commit7af7d18cfa2f1fab239e9a21e989bd8061cf23bb (patch)
treef11a35340c168a2a866fcf5c82c9d21d847206aa
parentadd696b057fa40696c251d4a52ed9d2a997c7aa0 (diff)
parentb99c6f1f67a207984b8b5355703cabd89d1e7c73 (diff)
downloadnextcloud-server-7af7d18cfa2f1fab239e9a21e989bd8061cf23bb.tar.gz
nextcloud-server-7af7d18cfa2f1fab239e9a21e989bd8061cf23bb.zip
Merge pull request #16783 from owncloud/handle-redirects-global
Adding global error handler for ajax calls which run into redirection…
-rw-r--r--apps/files/js/filelist.js7
-rw-r--r--apps/files/tests/js/filelistSpec.js7
-rw-r--r--core/js/files/client.js2
-rw-r--r--core/js/js.js64
-rw-r--r--core/js/setupchecks.js9
-rw-r--r--core/js/tests/specHelper.js7
-rw-r--r--core/js/tests/specs/coreSpec.js42
-rw-r--r--lib/private/api.php11
-rw-r--r--lib/private/json.php1
9 files changed, 130 insertions, 20 deletions
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index dd03b0c895a..81dfea2114d 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -1433,13 +1433,6 @@
delete this._reloadCall;
this.hideMask();
- if (status === 401) {
- // TODO: append current URL to be able to get back after logging in again
- OC.redirect(OC.generateUrl('apps/files'));
- OC.Notification.show(result);
- return false;
- }
-
// Firewall Blocked request?
if (status === 403) {
// Go home
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index ed56011866e..a83c8c4c0bc 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -2462,13 +2462,6 @@ describe('OCA.Files.FileList tests', function() {
getFolderContentsStub.restore();
fileList = undefined;
});
- it('redirects to files app in case of auth error', function () {
- deferredList.reject(401, 'Authentication error');
-
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files');
- expect(getFolderContentsStub.calledOnce).toEqual(true);
- });
it('redirects to root folder in case of forbidden access', function () {
deferredList.reject(403);
diff --git a/core/js/files/client.js b/core/js/files/client.js
index 627630e8b03..a7f393d325f 100644
--- a/core/js/files/client.js
+++ b/core/js/files/client.js
@@ -137,6 +137,8 @@
});
return result;
};
+
+ OC.registerXHRForErrorProcessing(xhr);
return xhr;
},
diff --git a/core/js/js.js b/core/js/js.js
index 83658a537b8..fac9c45f668 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -235,6 +235,13 @@ var OC={
},
/**
+ * Reloads the current page
+ */
+ reload: function() {
+ window.location.reload();
+ },
+
+ /**
* Protocol that is used to access this ownCloud instance
* @return {string} Used protocol
*/
@@ -727,6 +734,56 @@ var OC={
isUserAdmin: function() {
return oc_isadmin;
},
+
+ /**
+ * Process ajax error, redirects to main page
+ * if an error/auth error status was returned.
+ */
+ _processAjaxError: function(xhr) {
+ // purposefully aborted request ?
+ if (xhr.status === 0 && (xhr.statusText === 'abort' || xhr.statusText === 'timeout')) {
+ return;
+ }
+
+ if (_.contains([0, 302, 307, 401], xhr.status)) {
+ OC.reload();
+ }
+ },
+
+ /**
+ * Registers XmlHttpRequest object for global error processing.
+ *
+ * This means that if this XHR object returns 401 or session timeout errors,
+ * the current page will automatically be reloaded.
+ *
+ * @param {XMLHttpRequest} xhr
+ */
+ registerXHRForErrorProcessing: function(xhr) {
+ var loadCallback = function() {
+ if (xhr.readyState !== 4) {
+ return;
+ }
+
+ if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
+ return;
+ }
+
+ // fire jquery global ajax error handler
+ $(document).trigger(new $.Event('ajaxError'), xhr);
+ };
+
+ var errorCallback = function() {
+ // fire jquery global ajax error handler
+ $(document).trigger(new $.Event('ajaxError'), xhr);
+ };
+
+ // FIXME: also needs an IE8 way
+ if (xhr.addEventListener) {
+ xhr.addEventListener('load', loadCallback);
+ xhr.addEventListener('error', errorCallback);
+ }
+
+ }
};
/**
@@ -1311,6 +1368,13 @@ function initCore() {
$('html').addClass('edge');
}
+ $(document).on('ajaxError.main', function( event, request, settings ) {
+ if (settings && settings.allowAuthErrors) {
+ return;
+ }
+ OC._processAjaxError(request);
+ });
+
/**
* Calls the server periodically to ensure that session doesn't
* time out
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index de41b66ec32..1819b5a9c1e 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -40,7 +40,8 @@
'<d:propfind xmlns:d="DAV:">' +
'<d:prop><d:resourcetype/></d:prop>' +
'</d:propfind>',
- complete: afterCall
+ complete: afterCall,
+ allowAuthErrors: true
});
return deferred.promise();
},
@@ -157,7 +158,8 @@
$.ajax({
type: 'GET',
- url: OC.generateUrl('settings/ajax/checksetup')
+ url: OC.generateUrl('settings/ajax/checksetup'),
+ allowAuthErrors: true
}).then(afterCall, afterCall);
return deferred.promise();
},
@@ -181,7 +183,8 @@
$.ajax({
type: 'GET',
- url: OC.generateUrl('heartbeat')
+ url: OC.generateUrl('heartbeat'),
+ allowAuthErrors: true
}).then(afterCall, afterCall);
return deferred.promise();
diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js
index d13691845a7..f9bdeae0d64 100644
--- a/core/js/tests/specHelper.js
+++ b/core/js/tests/specHelper.js
@@ -116,7 +116,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent);
// global setup for all tests
(function setupTests() {
var fakeServer = null,
- $testArea = null;
+ $testArea = null,
+ ajaxErrorStub = null;
/**
* Utility functions for testing
@@ -162,6 +163,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent);
// dummy select2 (which isn't loaded during the tests)
$.fn.select2 = function() { return this; };
+
+ ajaxErrorStub = sinon.stub(OC, '_processAjaxError');
});
afterEach(function() {
@@ -172,6 +175,8 @@ window.isPhantom = /phantom/i.test(navigator.userAgent);
$testArea.remove();
delete($.fn.select2);
+
+ ajaxErrorStub.restore();
});
})();
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
index 2e970f7e707..32eb8df32d1 100644
--- a/core/js/tests/specs/coreSpec.js
+++ b/core/js/tests/specs/coreSpec.js
@@ -302,6 +302,7 @@ describe('Core base tests', function() {
/* jshint camelcase: false */
window.oc_config = oldConfig;
routeStub.restore();
+ $(document).off('ajaxError');
});
it('sends heartbeat half the session lifetime when heartbeat enabled', function() {
/* jshint camelcase: false */
@@ -473,6 +474,7 @@ describe('Core base tests', function() {
});
afterEach(function() {
clock.restore();
+ $(document).off('ajaxError');
});
it('Sets up menu toggle', function() {
window.initCore();
@@ -841,5 +843,45 @@ describe('Core base tests', function() {
// verification is done in afterEach
});
});
+ describe('global ajax errors', function() {
+ var reloadStub, ajaxErrorStub;
+
+ beforeEach(function() {
+ reloadStub = sinon.stub(OC, 'reload');
+ // unstub the error processing method
+ ajaxErrorStub = OC._processAjaxError;
+ ajaxErrorStub.restore();
+ window.initCore();
+ });
+ afterEach(function() {
+ reloadStub.restore();
+ $(document).off('ajaxError');
+ });
+
+ it('reloads current page in case of auth error', function () {
+ var dataProvider = [
+ [200, false],
+ [400, false],
+ [401, true],
+ [302, true],
+ [307, true]
+ ];
+
+ for (var i = 0; i < dataProvider.length; i++) {
+ var xhr = { status: dataProvider[i][0] };
+ var expectedCall = dataProvider[i][1];
+
+ reloadStub.reset();
+
+ $(document).trigger(new $.Event('ajaxError'), xhr);
+
+ if (expectedCall) {
+ expect(reloadStub.calledOnce).toEqual(true);
+ } else {
+ expect(reloadStub.notCalled).toEqual(true);
+ }
+ }
+ });
+ })
});
diff --git a/lib/private/api.php b/lib/private/api.php
index 452612d4c16..6c6be233c9d 100644
--- a/lib/private/api.php
+++ b/lib/private/api.php
@@ -377,9 +377,16 @@ class OC_API {
* @param string $format the format xml|json
*/
public static function respond($result, $format='xml') {
+ $request = \OC::$server->getRequest();
+
// Send 401 headers if unauthorised
if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) {
- header('WWW-Authenticate: Basic realm="Authorisation Required"');
+ // If request comes from JS return dummy auth request
+ if($request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
+ header('WWW-Authenticate: DummyBasic realm="Authorisation Required"');
+ } else {
+ header('WWW-Authenticate: Basic realm="Authorisation Required"');
+ }
header('HTTP/1.0 401 Unauthorized');
}
@@ -389,7 +396,7 @@ class OC_API {
$meta = $result->getMeta();
$data = $result->getData();
- if (self::isV2(\OC::$server->getRequest())) {
+ if (self::isV2($request)) {
$statusCode = self::mapStatusCodes($result->getStatusCode());
if (!is_null($statusCode)) {
$meta['statuscode'] = $statusCode;
diff --git a/lib/private/json.php b/lib/private/json.php
index adee28a1593..74aebd476fb 100644
--- a/lib/private/json.php
+++ b/lib/private/json.php
@@ -66,6 +66,7 @@ class OC_JSON{
public static function checkLoggedIn() {
if( !OC_User::isLoggedIn()) {
$l = \OC::$server->getL10N('lib');
+ http_response_code(\OCP\AppFramework\Http::STATUS_UNAUTHORIZED);
self::error(array( 'data' => array( 'message' => $l->t('Authentication error'), 'error' => 'authentication_error' )));
exit();
}