diff options
author | Thomas Müller <thomas.mueller@tmit.eu> | 2016-02-17 14:49:04 +0100 |
---|---|---|
committer | Thomas Müller <thomas.mueller@tmit.eu> | 2016-02-17 14:49:04 +0100 |
commit | 7af7d18cfa2f1fab239e9a21e989bd8061cf23bb (patch) | |
tree | f11a35340c168a2a866fcf5c82c9d21d847206aa | |
parent | add696b057fa40696c251d4a52ed9d2a997c7aa0 (diff) | |
parent | b99c6f1f67a207984b8b5355703cabd89d1e7c73 (diff) | |
download | nextcloud-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.js | 7 | ||||
-rw-r--r-- | apps/files/tests/js/filelistSpec.js | 7 | ||||
-rw-r--r-- | core/js/files/client.js | 2 | ||||
-rw-r--r-- | core/js/js.js | 64 | ||||
-rw-r--r-- | core/js/setupchecks.js | 9 | ||||
-rw-r--r-- | core/js/tests/specHelper.js | 7 | ||||
-rw-r--r-- | core/js/tests/specs/coreSpec.js | 42 | ||||
-rw-r--r-- | lib/private/api.php | 11 | ||||
-rw-r--r-- | lib/private/json.php | 1 |
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(); } |