diff --git a/nailgun/nailgun/api/v1/urls.py b/nailgun/nailgun/api/v1/urls.py index 61701af8fe..7d9d82294b 100644 --- a/nailgun/nailgun/api/v1/urls.py +++ b/nailgun/nailgun/api/v1/urls.py @@ -366,5 +366,4 @@ def public_urls(): return { r'/nodes/?$': ['POST'], r'/nodes/agent/?$': ['PUT'], - r'/version/?$': ['GET'] } diff --git a/nailgun/nailgun/test/integration/test_public_api.py b/nailgun/nailgun/test/integration/test_public_api.py index aec8f61aec..8fab173f2c 100644 --- a/nailgun/nailgun/test/integration/test_public_api.py +++ b/nailgun/nailgun/test/integration/test_public_api.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -from mock import patch from oslo_serialization import jsonutils from nailgun.test.base import BaseAuthenticationIntegrationTest @@ -47,37 +46,3 @@ class TestPublicHandlers(BaseAuthenticationIntegrationTest): headers=self.default_headers) self.assertEqual(201, resp.status_code) - - def test_version_api(self): - resp = self.app.get( - reverse('VersionHandler'), - headers=self.default_headers - ) - self.assertEqual(200, resp.status_code) - - @patch('nailgun.api.v1.handlers.version.utils.get_fuel_release_versions') - def test_500_no_html_dev(self, handler_get): - exc_text = "Here goes an exception" - handler_get.side_effect = Exception(exc_text) - resp = self.app.get( - reverse('VersionHandler'), - headers=self.default_headers, - expect_errors=True - ) - self.assertEqual(500, resp.status_code) - self.assertIn(exc_text, resp.body) - self.assertIn("Traceback", resp.body) - self.assertNotIn("html", resp.body) - - @patch('nailgun.api.v1.handlers.version.utils.get_fuel_release_versions') - def test_500_no_html_production(self, handler_get): - exc_text = "Here goes an exception" - handler_get.side_effect = Exception(exc_text) - with patch('nailgun.settings.settings.DEVELOPMENT', 0): - resp = self.app.get( - reverse('VersionHandler'), - headers=self.default_headers, - expect_errors=True - ) - self.assertEqual(500, resp.status_code) - self.assertEqual(exc_text, resp.body) diff --git a/nailgun/nailgun/test/integration/test_version_api.py b/nailgun/nailgun/test/integration/test_version_api.py new file mode 100644 index 0000000000..26190a9dd5 --- /dev/null +++ b/nailgun/nailgun/test/integration/test_version_api.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +# Copyright 2016 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +from mock import patch + +from nailgun.test.base import BaseAuthenticationIntegrationTest +from nailgun.test.base import reverse + + +class TestVersionApi(BaseAuthenticationIntegrationTest): + """Test the version api + + Test the version api to make sure it requires authentication + and works when passed a valid auth token. + """ + + def setUp(self): + super(TestVersionApi, self).setUp() + self.token = self.get_auth_token() + self.headers = copy.deepcopy(self.default_headers) + + def test_version_api_noauth(self): + """Check that version api requires auth.""" + resp = self.app.get( + reverse('VersionHandler'), + headers=self.default_headers, + expect_errors=True + ) + self.assertEqual(401, resp.status_code) + + def test_version_api_auth(self): + """Check that version api works with auth.""" + self.headers['X-Auth-Token'] = self.token + resp = self.app.get( + reverse('VersionHandler'), + headers=self.headers + ) + self.assertEqual(200, resp.status_code) + + @patch('nailgun.api.v1.handlers.version.utils.get_fuel_release_versions') + def test_500_no_html_dev(self, handler_get): + exc_text = "Here goes an exception" + handler_get.side_effect = Exception(exc_text) + self.headers['X-Auth-Token'] = self.token + resp = self.app.get( + reverse('VersionHandler'), + headers=self.headers, + expect_errors=True + ) + self.assertEqual(500, resp.status_code) + self.assertIn(exc_text, resp.body) + self.assertIn("Traceback", resp.body) + self.assertNotIn("html", resp.body) + + @patch('nailgun.api.v1.handlers.version.utils.get_fuel_release_versions') + def test_500_no_html_production(self, handler_get): + exc_text = "Here goes an exception" + handler_get.side_effect = Exception(exc_text) + self.headers['X-Auth-Token'] = self.token + with patch('nailgun.settings.settings.DEVELOPMENT', 0): + resp = self.app.get( + reverse('VersionHandler'), + headers=self.headers, + expect_errors=True + ) + self.assertEqual(500, resp.status_code) + self.assertEqual(exc_text, resp.body) diff --git a/nailgun/static/app.js b/nailgun/static/app.js index 9fc9d88579..9279da0718 100644 --- a/nailgun/static/app.js +++ b/nailgun/static/app.js @@ -153,29 +153,35 @@ function($, _, i18n, Backbone, React, utils, layoutComponents, Coccyx, models, K this.mountNode = $('#main-container'); this.router = new Router(); - this.keystoneClient = new KeystoneClient('/keystone', { - cacheTokenFor: 10 * 60 * 1000, - tenant: 'admin' - }); this.version = new models.FuelVersion(); this.settings = new models.FuelSettings(); this.user = new models.User(); this.statistics = new models.NodesStatistics(); this.notifications = new models.Notifications(); - + this.keystoneClient = new KeystoneClient('/keystone', { + cacheTokenFor: 10 * 60 * 1000, + tenant: 'admin', + token: this.user.get('token') + }); this.fetchData(); } _.extend(App.prototype, { fetchData: function() { - this.version.fetch().then(_.bind(function() { + this.version.fetch().then(null, _.bind(function(response) { + if (response.status == 401) { + this.version.set({auth_required: true}); + return $.Deferred().resolve(); + } + }, this)).then(_.bind(function() { this.user.set({authenticated: !this.version.get('auth_required')}); this.patchBackboneSync(); if (this.version.get('auth_required')) { _.extend(this.keystoneClient, this.user.pick('token')); return this.keystoneClient.authenticate() - .done(_.bind(function() { + .then(_.bind(function() { this.user.set({authenticated: true}); + return this.version.fetch({cache: true}); }, this)); } return $.Deferred().resolve(); @@ -233,7 +239,7 @@ function($, _, i18n, Backbone, React, utils, layoutComponents, Coccyx, models, K if (method == 'patch') { method = 'update'; } - if (app.version.get('auth_required') && !this.authExempt) { + if (app.version && app.version.get('auth_required')) { // FIXME(vkramskikh): manually moving success/error callbacks // to deferred-style callbacks. Everywhere in the code we use // deferreds, but backbone uses success/error callbacks. It @@ -249,6 +255,7 @@ function($, _, i18n, Backbone, React, utils, layoutComponents, Coccyx, models, K app.logout(); }) .then(_.bind(function() { + app.user.set('token', app.keystoneClient.token); options = options || {}; options.headers = options.headers || {}; options.headers['X-Auth-Token'] = app.keystoneClient.token; diff --git a/nailgun/static/keystone_client.js b/nailgun/static/keystone_client.js index 3b068c471d..6f605d8fde 100644 --- a/nailgun/static/keystone_client.js +++ b/nailgun/static/keystone_client.js @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. **/ -define(['jquery', 'underscore', 'js-cookie'], function($, _, Cookies) { +define(['jquery', 'underscore'], function($, _) { 'use strict'; function KeystoneClient(url, options) { @@ -57,9 +57,6 @@ define(['jquery', 'underscore', 'js-cookie'], function($, _, Cookies) { this.userId = result.access.user.id; this.token = result.access.token.id; this.tokenUpdateTime = new Date(); - - Cookies.set('token', result.access.token.id); - return deferred; } catch(e) { return $.Deferred().reject(); @@ -88,9 +85,6 @@ define(['jquery', 'underscore', 'js-cookie'], function($, _, Cookies) { try { this.token = result.access.token.id; this.tokenUpdateTime = new Date(); - - Cookies.set('token', result.access.token.id); - return deferred; } catch(e) { return $.Deferred().reject(); @@ -111,8 +105,6 @@ define(['jquery', 'underscore', 'js-cookie'], function($, _, Cookies) { delete this.token; delete this.tokenUpdateTime; - Cookies.remove('token'); - this.tokenRemoveRequest = $.ajax(this.url + '/v2.0/tokens/' + token, { type: 'DELETE', dataType: 'json', diff --git a/nailgun/static/models.js b/nailgun/static/models.js index 8bf8cea6dd..23d359faa9 100644 --- a/nailgun/static/models.js +++ b/nailgun/static/models.js @@ -22,8 +22,9 @@ define([ 'expression', 'expression/objects', 'jsx!views/custom_controls', + 'js-cookie', 'deepModel' -], function($, _, i18n, Backbone, utils, Expression, expressionObjects, customControls) { +], function($, _, i18n, Backbone, utils, Expression, expressionObjects, customControls, Cookies) { 'use strict'; var models = {}; @@ -83,6 +84,7 @@ define([ if (this.cacheFor && options && options.cache && this.lastSyncTime && (this.cacheFor > (new Date() - this.lastSyncTime))) { return $.Deferred().resolve(); } + if (options) delete options.cache; return this._super('fetch', arguments); }, sync: function() { @@ -998,10 +1000,10 @@ define([ urlRoot: '/api/ostf' }); - models.FuelVersion = BaseModel.extend({ + models.FuelVersion = BaseModel.extend(cacheMixin).extend({ + cacheFor: 60 * 1000, constructorName: 'FuelVersion', - urlRoot: '/api/version', - authExempt: true + urlRoot: '/api/version' }); models.User = BaseModel.extend({ @@ -1021,6 +1023,14 @@ define([ } }); }, this); + this.on('change:token', function() { + var token = this.get('token'); + if (_.isUndefined(token)) { + Cookies.remove('token'); + } else { + Cookies.set('token', token); + } + }, this); } }); diff --git a/nailgun/static/views/login_page.jsx b/nailgun/static/views/login_page.jsx index c57ccdfed3..1a4f342e9f 100644 --- a/nailgun/static/views/login_page.jsx +++ b/nailgun/static/views/login_page.jsx @@ -40,12 +40,6 @@ function($, _, i18n, React, dispatcher, utils) { -
- {_.contains(app.version.get('feature_groups'), 'mirantis') && -

{i18n('common.copyright')}

- } -

{i18n('common.version')}: {app.version.get('release')}

-
); } @@ -71,7 +65,7 @@ function($, _, i18n, React, dispatcher, utils) { dispatcher.trigger('showDefaultPasswordWarning'); } - return app.settings.fetch({cache: true}); + return $.when(app.version.fetch({cache: true}), app.settings.fetch({cache: true})); }, this)) .done(_.bind(function() { var nextUrl = ''; diff --git a/run_tests.sh b/run_tests.sh index f9a25f28df..8d1b6c269d 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -630,7 +630,7 @@ function run_server() { local http_code=$(curl -s -w %{http_code} -o /dev/null $check_url) - if [[ "$http_code" = "200" ]]; then return 0; fi + if [[ "$http_code" != "000" ]]; then return 0; fi sleep 0.1 i=$((i + 1))