From 3c72dc309936b23e413dc1aee8ca49c795c48a0f Mon Sep 17 00:00:00 2001 From: Aashish Sharma Date: Tue, 24 Nov 2020 11:28:28 +0530 Subject: [PATCH] mgr/dashboard: Use secure cookies to store JWT Token This PR intends to store the jwt token in secure cookies instead of local storage Fixes: https://tracker.ceph.com/issues/44591 Signed-off-by: Aashish Sharma Signed-off-by: Avan Thakkar (cherry picked from commit 36703c63381e6723fff57266235f8230e6af1d92) --- qa/tasks/mgr/dashboard/helper.py | 99 +++++++---- qa/tasks/mgr/dashboard/test_auth.py | 158 ++++++++++++++++++ .../mgr/dashboard/controllers/__init__.py | 9 + src/pybind/mgr/dashboard/controllers/auth.py | 16 +- src/pybind/mgr/dashboard/controllers/docs.py | 5 +- src/pybind/mgr/dashboard/controllers/saml2.py | 4 +- .../integration/block/images.e2e-spec.ts | 3 + .../integration/block/iscsi.e2e-spec.ts | 1 + .../integration/block/mirroring.e2e-spec.ts | 1 + .../cluster/configuration.e2e-spec.ts | 1 + .../integration/cluster/crush-map.e2e-spec.ts | 1 + .../integration/cluster/hosts.e2e-spec.ts | 1 + .../integration/cluster/logs.e2e-spec.ts | 1 + .../cluster/mgr-modules.e2e-spec.ts | 1 + .../integration/cluster/monitors.e2e-spec.ts | 1 + .../integration/cluster/osds.e2e-spec.ts | 1 + .../filesystems/filesystems.e2e-spec.ts | 1 + .../orchestrator/01-hosts.e2e-spec.ts | 1 + .../02-hosts-inventory.e2e-spec.ts | 1 + .../orchestrator/03-inventory.e2e-spec.ts | 1 + .../orchestrator/04-osds.e2e-spec.ts | 1 + .../integration/pools/pools.e2e-spec.ts | 1 + .../integration/rgw/buckets.e2e-spec.ts | 1 + .../integration/rgw/daemons.e2e-spec.ts | 1 + .../cypress/integration/rgw/users.e2e-spec.ts | 1 + .../integration/ui/dashboard.e2e-spec.ts | 1 + .../integration/ui/language.e2e-spec.ts | 1 + .../integration/ui/navigation.e2e-spec.ts | 1 + .../integration/ui/notification.e2e-spec.ts | 3 + .../integration/ui/role-mgmt.e2e-spec.ts | 1 + .../integration/ui/user-mgmt.e2e-spec.ts | 1 + .../frontend/cypress/support/commands.ts | 1 - .../mgr/dashboard/frontend/package-lock.json | 8 - .../mgr/dashboard/frontend/package.json | 1 - .../dashboard/frontend/src/app/app.module.ts | 12 +- .../rbd-snapshot-list.component.spec.ts | 2 +- .../app/core/auth/login/login.component.ts | 1 - .../dashboard-help.component.ts | 9 +- .../src/app/shared/api/auth.service.spec.ts | 3 +- .../src/app/shared/api/auth.service.ts | 3 +- .../src/app/shared/models/login-response.ts | 1 - .../services/auth-storage.service.spec.ts | 4 +- .../shared/services/auth-storage.service.ts | 7 - src/pybind/mgr/dashboard/services/auth.py | 20 ++- 44 files changed, 298 insertions(+), 94 deletions(-) diff --git a/qa/tasks/mgr/dashboard/helper.py b/qa/tasks/mgr/dashboard/helper.py index bbf7eb4d5f4..78915eb2015 100644 --- a/qa/tasks/mgr/dashboard/helper.py +++ b/qa/tasks/mgr/dashboard/helper.py @@ -157,18 +157,19 @@ class DashboardTestCase(MgrTestCase): cls._task_post("/api/pool", data) @classmethod - def login(cls, username, password): + def login(cls, username, password, set_cookies=False): if cls._loggedin: cls.logout() - cls._post('/api/auth', {'username': username, 'password': password}) + cls._post('/api/auth', {'username': username, + 'password': password}, set_cookies=set_cookies) cls._assertEq(cls._resp.status_code, 201) cls._token = cls.jsonBody()['token'] cls._loggedin = True @classmethod - def logout(cls): + def logout(cls, set_cookies=False): if cls._loggedin: - cls._post('/api/auth/logout') + cls._post('/api/auth/logout', set_cookies=set_cookies) cls._assertEq(cls._resp.status_code, 200) cls._token = None cls._loggedin = False @@ -273,33 +274,54 @@ class DashboardTestCase(MgrTestCase): def tearDownClass(cls): super(DashboardTestCase, cls).tearDownClass() - # pylint: disable=inconsistent-return-statements, too-many-arguments + # pylint: disable=inconsistent-return-statements, too-many-arguments, too-many-branches @classmethod - def _request(cls, url, method, data=None, params=None, version=DEFAULT_VERSION): + def _request(cls, url, method, data=None, params=None, version=DEFAULT_VERSION, + set_cookies=False): url = "{}{}".format(cls._base_uri, url) log.info("Request %s to %s", method, url) headers = {} + cookies = {} if cls._token: - headers['Authorization'] = "Bearer {}".format(cls._token) - + if set_cookies: + cookies['token'] = cls._token + else: + headers['Authorization'] = "Bearer {}".format(cls._token) if version is None: headers['Accept'] = 'application/json' else: headers['Accept'] = 'application/vnd.ceph.api.v{}+json'.format(version) - if method == 'GET': - cls._resp = cls._session.get(url, params=params, verify=False, - headers=headers) - elif method == 'POST': - cls._resp = cls._session.post(url, json=data, params=params, - verify=False, headers=headers) - elif method == 'DELETE': - cls._resp = cls._session.delete(url, json=data, params=params, - verify=False, headers=headers) - elif method == 'PUT': - cls._resp = cls._session.put(url, json=data, params=params, - verify=False, headers=headers) + + if set_cookies: + if method == 'GET': + cls._resp = cls._session.get(url, params=params, verify=False, + headers=headers, cookies=cookies) + elif method == 'POST': + cls._resp = cls._session.post(url, json=data, params=params, + verify=False, headers=headers, cookies=cookies) + elif method == 'DELETE': + cls._resp = cls._session.delete(url, json=data, params=params, + verify=False, headers=headers, cookies=cookies) + elif method == 'PUT': + cls._resp = cls._session.put(url, json=data, params=params, + verify=False, headers=headers, cookies=cookies) + else: + assert False else: - assert False + if method == 'GET': + cls._resp = cls._session.get(url, params=params, verify=False, + headers=headers) + elif method == 'POST': + cls._resp = cls._session.post(url, json=data, params=params, + verify=False, headers=headers) + elif method == 'DELETE': + cls._resp = cls._session.delete(url, json=data, params=params, + verify=False, headers=headers) + elif method == 'PUT': + cls._resp = cls._session.put(url, json=data, params=params, + verify=False, headers=headers) + else: + assert False try: if not cls._resp.ok: # Output response for easier debugging. @@ -314,8 +336,8 @@ class DashboardTestCase(MgrTestCase): raise ex @classmethod - def _get(cls, url, params=None, version=DEFAULT_VERSION): - return cls._request(url, 'GET', params=params, version=version) + def _get(cls, url, params=None, version=DEFAULT_VERSION, set_cookies=False): + return cls._request(url, 'GET', params=params, version=version, set_cookies=set_cookies) @classmethod def _view_cache_get(cls, url, retries=5): @@ -336,16 +358,16 @@ class DashboardTestCase(MgrTestCase): return res @classmethod - def _post(cls, url, data=None, params=None, version=DEFAULT_VERSION): - cls._request(url, 'POST', data, params, version=version) + def _post(cls, url, data=None, params=None, version=DEFAULT_VERSION, set_cookies=False): + cls._request(url, 'POST', data, params, version=version, set_cookies=set_cookies) @classmethod - def _delete(cls, url, data=None, params=None, version=DEFAULT_VERSION): - cls._request(url, 'DELETE', data, params, version=version) + def _delete(cls, url, data=None, params=None, version=DEFAULT_VERSION, set_cookies=False): + cls._request(url, 'DELETE', data, params, version=version, set_cookies=set_cookies) @classmethod - def _put(cls, url, data=None, params=None, version=DEFAULT_VERSION): - cls._request(url, 'PUT', data, params, version=version) + def _put(cls, url, data=None, params=None, version=DEFAULT_VERSION, set_cookies=False): + cls._request(url, 'PUT', data, params, version=version, set_cookies=set_cookies) @classmethod def _assertEq(cls, v1, v2): @@ -364,8 +386,8 @@ class DashboardTestCase(MgrTestCase): # pylint: disable=too-many-arguments @classmethod - def _task_request(cls, method, url, data, timeout, version=DEFAULT_VERSION): - res = cls._request(url, method, data, version=version) + def _task_request(cls, method, url, data, timeout, version=DEFAULT_VERSION, set_cookies=False): + res = cls._request(url, method, data, version=version, set_cookies=set_cookies) cls._assertIn(cls._resp.status_code, [200, 201, 202, 204, 400, 403, 404]) if cls._resp.status_code == 403: @@ -417,16 +439,19 @@ class DashboardTestCase(MgrTestCase): return res_task['exception'] @classmethod - def _task_post(cls, url, data=None, timeout=60, version=DEFAULT_VERSION): - return cls._task_request('POST', url, data, timeout, version=version) + def _task_post(cls, url, data=None, timeout=60, version=DEFAULT_VERSION, set_cookies=False): + return cls._task_request('POST', url, data, timeout, version=version, + set_cookies=set_cookies) @classmethod - def _task_delete(cls, url, timeout=60, version=DEFAULT_VERSION): - return cls._task_request('DELETE', url, None, timeout, version=version) + def _task_delete(cls, url, timeout=60, version=DEFAULT_VERSION, set_cookies=False): + return cls._task_request('DELETE', url, None, timeout, version=version, + set_cookies=set_cookies) @classmethod - def _task_put(cls, url, data=None, timeout=60, version=DEFAULT_VERSION): - return cls._task_request('PUT', url, data, timeout, version=version) + def _task_put(cls, url, data=None, timeout=60, version=DEFAULT_VERSION, set_cookies=False): + return cls._task_request('PUT', url, data, timeout, version=version, + set_cookies=set_cookies) @classmethod def cookies(cls): diff --git a/qa/tasks/mgr/dashboard/test_auth.py b/qa/tasks/mgr/dashboard/test_auth.py index 9bc195bbbbd..ca7a0cd8229 100644 --- a/qa/tasks/mgr/dashboard/test_auth.py +++ b/qa/tasks/mgr/dashboard/test_auth.py @@ -36,6 +36,7 @@ class AuthTest(DashboardTestCase): self.create_user('admin2', '', ['administrator'], force_password=True) def test_a_set_login_credentials(self): + # test with Authorization header self.create_user('admin2', 'admin2', ['administrator']) self._post("/api/auth", {'username': 'admin2', 'password': 'admin2'}) self.assertStatus(201) @@ -43,7 +44,16 @@ class AuthTest(DashboardTestCase): self._validate_jwt_token(data['token'], "admin2", data['permissions']) self.delete_user('admin2') + # test with Cookies set + self.create_user('admin2', 'admin2', ['administrator']) + self._post("/api/auth", {'username': 'admin2', 'password': 'admin2'}, set_cookies=True) + self.assertStatus(201) + data = self.jsonBody() + self._validate_jwt_token(data['token'], "admin2", data['permissions']) + self.delete_user('admin2') + def test_login_valid(self): + # test with Authorization header self._post("/api/auth", {'username': 'admin', 'password': 'admin'}) self.assertStatus(201) data = self.jsonBody() @@ -57,7 +67,22 @@ class AuthTest(DashboardTestCase): }, allow_unknown=False)) self._validate_jwt_token(data['token'], "admin", data['permissions']) + # test with Cookies set + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(201) + data = self.jsonBody() + self.assertSchema(data, JObj(sub_elems={ + 'token': JLeaf(str), + 'username': JLeaf(str), + 'permissions': JObj(sub_elems={}, allow_unknown=True), + 'sso': JLeaf(bool), + 'pwdExpirationDate': JLeaf(int, none=True), + 'pwdUpdateRequired': JLeaf(bool) + }, allow_unknown=False)) + self._validate_jwt_token(data['token'], "admin", data['permissions']) + def test_login_invalid(self): + # test with Authorization header self._post("/api/auth", {'username': 'admin', 'password': 'inval'}) self.assertStatus(400) self.assertJsonBody({ @@ -67,6 +92,7 @@ class AuthTest(DashboardTestCase): }) def test_lockout_user(self): + # test with Authorization header self._ceph_cmd(['dashboard', 'set-account-lockout-attempts', '3']) for _ in range(3): self._post("/api/auth", {'username': 'admin', 'password': 'inval'}) @@ -91,7 +117,33 @@ class AuthTest(DashboardTestCase): }, allow_unknown=False)) self._validate_jwt_token(data['token'], "admin", data['permissions']) + # test with Cookies set + self._ceph_cmd(['dashboard', 'set-account-lockout-attempts', '3']) + for _ in range(3): + self._post("/api/auth", {'username': 'admin', 'password': 'inval'}, set_cookies=True) + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(400) + self.assertJsonBody({ + "component": "auth", + "code": "invalid_credentials", + "detail": "Invalid credentials" + }) + self._ceph_cmd(['dashboard', 'ac-user-enable', 'admin']) + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(201) + data = self.jsonBody() + self.assertSchema(data, JObj(sub_elems={ + 'token': JLeaf(str), + 'username': JLeaf(str), + 'permissions': JObj(sub_elems={}, allow_unknown=True), + 'sso': JLeaf(bool), + 'pwdExpirationDate': JLeaf(int, none=True), + 'pwdUpdateRequired': JLeaf(bool) + }, allow_unknown=False)) + self._validate_jwt_token(data['token'], "admin", data['permissions']) + def test_logout(self): + # test with Authorization header self._post("/api/auth", {'username': 'admin', 'password': 'admin'}) self.assertStatus(201) data = self.jsonBody() @@ -106,7 +158,23 @@ class AuthTest(DashboardTestCase): self.assertStatus(401) self.set_jwt_token(None) + # test with Cookies set + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(201) + data = self.jsonBody() + self._validate_jwt_token(data['token'], "admin", data['permissions']) + self.set_jwt_token(data['token']) + self._post("/api/auth/logout", set_cookies=True) + self.assertStatus(200) + self.assertJsonBody({ + "redirect_url": "#/login" + }) + self._get("/api/host", set_cookies=True) + self.assertStatus(401) + self.set_jwt_token(None) + def test_token_ttl(self): + # test with Authorization header self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '5']) self._post("/api/auth", {'username': 'admin', 'password': 'admin'}) self.assertStatus(201) @@ -119,7 +187,21 @@ class AuthTest(DashboardTestCase): self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '28800']) self.set_jwt_token(None) + # test with Cookies set + self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '5']) + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(201) + self.set_jwt_token(self.jsonBody()['token']) + self._get("/api/host", set_cookies=True) + self.assertStatus(200) + time.sleep(6) + self._get("/api/host", set_cookies=True) + self.assertStatus(401) + self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '28800']) + self.set_jwt_token(None) + def test_remove_from_blocklist(self): + # test with Authorization header self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '5']) self._post("/api/auth", {'username': 'admin', 'password': 'admin'}) self.assertStatus(201) @@ -139,11 +221,37 @@ class AuthTest(DashboardTestCase): self._post("/api/auth/logout") self.assertStatus(200) + # test with Cookies set + self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '5']) + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(201) + self.set_jwt_token(self.jsonBody()['token']) + # the following call adds the token to the blocklist + self._post("/api/auth/logout", set_cookies=True) + self.assertStatus(200) + self._get("/api/host", set_cookies=True) + self.assertStatus(401) + time.sleep(6) + self._ceph_cmd(['dashboard', 'set-jwt-token-ttl', '28800']) + self.set_jwt_token(None) + self._post("/api/auth", {'username': 'admin', 'password': 'admin'}, set_cookies=True) + self.assertStatus(201) + self.set_jwt_token(self.jsonBody()['token']) + # the following call removes expired tokens from the blocklist + self._post("/api/auth/logout", set_cookies=True) + self.assertStatus(200) + def test_unauthorized(self): + # test with Authorization header self._get("/api/host") self.assertStatus(401) + # test with Cookies set + self._get("/api/host", set_cookies=True) + self.assertStatus(401) + def test_invalidate_token_by_admin(self): + # test with Authorization header self._get("/api/host") self.assertStatus(401) self.create_user('user', 'user', ['read-only']) @@ -168,7 +276,33 @@ class AuthTest(DashboardTestCase): self.assertStatus(200) self.delete_user("user") + # test with Cookies set + self._get("/api/host", set_cookies=True) + self.assertStatus(401) + self.create_user('user', 'user', ['read-only']) + time.sleep(1) + self._post("/api/auth", {'username': 'user', 'password': 'user'}, set_cookies=True) + self.assertStatus(201) + self.set_jwt_token(self.jsonBody()['token']) + self._get("/api/host", set_cookies=True) + self.assertStatus(200) + time.sleep(1) + self._ceph_cmd_with_secret(['dashboard', 'ac-user-set-password', '--force-password', + 'user'], + 'user2') + time.sleep(1) + self._get("/api/host", set_cookies=True) + self.assertStatus(401) + self.set_jwt_token(None) + self._post("/api/auth", {'username': 'user', 'password': 'user2'}, set_cookies=True) + self.assertStatus(201) + self.set_jwt_token(self.jsonBody()['token']) + self._get("/api/host", set_cookies=True) + self.assertStatus(200) + self.delete_user("user") + def test_check_token(self): + # test with Authorization header self.login("admin", "admin") self._post("/api/auth/check", {"token": self.jsonBody()["token"]}) self.assertStatus(200) @@ -181,7 +315,21 @@ class AuthTest(DashboardTestCase): }, allow_unknown=False)) self.logout() + # test with Cookies set + self.login("admin", "admin", set_cookies=True) + self._post("/api/auth/check", {"token": self.jsonBody()["token"]}, set_cookies=True) + self.assertStatus(200) + data = self.jsonBody() + self.assertSchema(data, JObj(sub_elems={ + "username": JLeaf(str), + "permissions": JObj(sub_elems={}, allow_unknown=True), + "sso": JLeaf(bool), + "pwdUpdateRequired": JLeaf(bool) + }, allow_unknown=False)) + self.logout(set_cookies=True) + def test_check_wo_token(self): + # test with Authorization header self.login("admin", "admin") self._post("/api/auth/check", {"token": ""}) self.assertStatus(200) @@ -190,3 +338,13 @@ class AuthTest(DashboardTestCase): "login_url": JLeaf(str) }, allow_unknown=False)) self.logout() + + # test with Cookies set + self.login("admin", "admin", set_cookies=True) + self._post("/api/auth/check", {"token": ""}, set_cookies=True) + self.assertStatus(200) + data = self.jsonBody() + self.assertSchema(data, JObj(sub_elems={ + "login_url": JLeaf(str) + }, allow_unknown=False)) + self.logout(set_cookies=True) diff --git a/src/pybind/mgr/dashboard/controllers/__init__.py b/src/pybind/mgr/dashboard/controllers/__init__.py index 2f3c186b952..11b6323255c 100644 --- a/src/pybind/mgr/dashboard/controllers/__init__.py +++ b/src/pybind/mgr/dashboard/controllers/__init__.py @@ -1031,3 +1031,12 @@ def validate_ceph_type(validations, component=''): return func(*args, **kwargs) return validate_args return decorator + + +def set_cookies(url_prefix, token): + cherrypy.response.cookie['token'] = token + if url_prefix == 'https': + cherrypy.response.cookie['token']['secure'] = True + cherrypy.response.cookie['token']['HttpOnly'] = True + cherrypy.response.cookie['token']['path'] = '/' + cherrypy.response.cookie['token']['SameSite'] = 'Strict' diff --git a/src/pybind/mgr/dashboard/controllers/auth.py b/src/pybind/mgr/dashboard/controllers/auth.py index 264204d557a..cd50006e28d 100644 --- a/src/pybind/mgr/dashboard/controllers/auth.py +++ b/src/pybind/mgr/dashboard/controllers/auth.py @@ -1,15 +1,21 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import +import http.cookies import logging - -import cherrypy +import sys from .. import mgr from ..exceptions import InvalidCredentialsError, UserDoesNotExist from ..services.auth import AuthManager, JwtManager from ..settings import Settings -from . import ApiController, ControllerDoc, EndpointDoc, RESTController, allow_empty_body +from . import ApiController, ControllerDoc, EndpointDoc, RESTController, \ + allow_empty_body, set_cookies + +# Python 3.8 introduced `samesite` attribute: +# https://docs.python.org/3/library/http.cookies.html#morsel-objects +if sys.version_info < (3, 8): + http.cookies.Morsel._reserved["samesite"] = "SameSite" # type: ignore # pylint: disable=W0212 logger = logging.getLogger('controllers.auth') @@ -40,12 +46,14 @@ class Auth(RESTController): pwd_update_required = user_data.get('pwdUpdateRequired', False) if user_perms is not None: + url_prefix = 'https' if mgr.get_localized_module_option('ssl') else 'http' + logger.info('Login successful: %s', username) mgr.ACCESS_CTRL_DB.reset_attempt(username) mgr.ACCESS_CTRL_DB.save() token = JwtManager.gen_token(username) token = token.decode('utf-8') - cherrypy.response.headers['Authorization'] = "Bearer: {}".format(token) + set_cookies(url_prefix, token) return { 'token': token, 'username': username, diff --git a/src/pybind/mgr/dashboard/controllers/docs.py b/src/pybind/mgr/dashboard/controllers/docs.py index f4ff08ed786..295a36ad855 100644 --- a/src/pybind/mgr/dashboard/controllers/docs.py +++ b/src/pybind/mgr/dashboard/controllers/docs.py @@ -391,8 +391,11 @@ class Docs(BaseController): spec_url = "{}/docs/api.json".format(base) auth_header = cherrypy.request.headers.get('authorization') + auth_cookie = cherrypy.request.cookie['token'] jwt_token = "" - if auth_header is not None: + if auth_cookie is not None: + jwt_token = auth_cookie.value + elif auth_header is not None: scheme, params = auth_header.split(' ', 1) if scheme.lower() == 'bearer': jwt_token = params diff --git a/src/pybind/mgr/dashboard/controllers/saml2.py b/src/pybind/mgr/dashboard/controllers/saml2.py index 76a7e193a9a..84e8a132a0b 100644 --- a/src/pybind/mgr/dashboard/controllers/saml2.py +++ b/src/pybind/mgr/dashboard/controllers/saml2.py @@ -16,7 +16,7 @@ from .. import mgr from ..exceptions import UserDoesNotExist from ..services.auth import JwtManager from ..tools import prepare_url_prefix -from . import BaseController, Controller, Endpoint, allow_empty_body +from . import BaseController, Controller, Endpoint, allow_empty_body, set_cookies @Controller('/auth/saml2', secure=False) @@ -71,6 +71,7 @@ class Saml2(BaseController): token = JwtManager.gen_token(username) JwtManager.set_user(JwtManager.decode_token(token)) token = token.decode('utf-8') + set_cookies(url_prefix, token) raise cherrypy.HTTPRedirect("{}/#/login?access_token={}".format(url_prefix, token)) return { @@ -104,5 +105,6 @@ class Saml2(BaseController): # pylint: disable=unused-argument Saml2._check_python_saml() JwtManager.reset_user() + cherrypy.response.cookie['token'] = {'expires': 0, 'max-age': 0} url_prefix = prepare_url_prefix(mgr.get_module_option('url_prefix', default='')) raise cherrypy.HTTPRedirect("{}/#/login".format(url_prefix)) diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/block/images.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/block/images.e2e-spec.ts index 87900a0e1a5..cf8832bb9bb 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/block/images.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/block/images.e2e-spec.ts @@ -9,6 +9,7 @@ describe('Images page', () => { before(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); // Need pool for image testing pools.navigateTo('create'); pools.create(poolName, 8, 'rbd'); @@ -25,6 +26,7 @@ describe('Images page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); images.navigateTo(); }); @@ -68,6 +70,7 @@ describe('Images page', () => { before(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); // Need image for trash testing images.createImage(imageName, poolName, '1'); images.getFirstTableCell(imageName).should('exist'); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/block/iscsi.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/block/iscsi.e2e-spec.ts index 2788c4f9b9d..cef4874bed5 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/block/iscsi.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/block/iscsi.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Iscsi Page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); iscsi.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.e2e-spec.ts index c7bd4278238..ddee817e18e 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/block/mirroring.e2e-spec.ts @@ -7,6 +7,7 @@ describe('Mirroring page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); mirroring.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/configuration.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/configuration.e2e-spec.ts index 6c012166ebb..0acbc9100b8 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/configuration.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/configuration.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Configuration page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); configuration.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/crush-map.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/crush-map.e2e-spec.ts index 2c8d1322f4e..2274d72e7bc 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/crush-map.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/crush-map.e2e-spec.ts @@ -5,6 +5,7 @@ describe('CRUSH map page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); crushmap.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts index 045b18f60cd..c1935a78383 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/hosts.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Hosts page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); hosts.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/logs.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/logs.e2e-spec.ts index f5692bfe15f..731275e26d1 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/logs.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/logs.e2e-spec.ts @@ -15,6 +15,7 @@ describe('Logs page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); }); describe('breadcrumb and tab tests', () => { diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/mgr-modules.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/mgr-modules.e2e-spec.ts index f163b0c230f..82c68789699 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/mgr-modules.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/mgr-modules.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Manager modules page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); mgrmodules.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/monitors.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/monitors.e2e-spec.ts index 8324ff8b5b0..a23d071e6d7 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/monitors.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/monitors.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Monitors page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); monitors.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts index 647d4aadb47..e68d03bd542 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts @@ -5,6 +5,7 @@ describe('OSDs page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); osds.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/filesystems/filesystems.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/filesystems/filesystems.e2e-spec.ts index 63f59916f99..83f280b477a 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/filesystems/filesystems.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/filesystems/filesystems.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Filesystems page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); filesystems.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/01-hosts.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/01-hosts.e2e-spec.ts index e7609454a18..3e7e6733312 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/01-hosts.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/01-hosts.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Hosts page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); hosts.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/02-hosts-inventory.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/02-hosts-inventory.e2e-spec.ts index 3b74de223d3..2b9ca548d54 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/02-hosts-inventory.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/02-hosts-inventory.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Hosts page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); hosts.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/03-inventory.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/03-inventory.e2e-spec.ts index 41dcdecde3f..596653ab556 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/03-inventory.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/03-inventory.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Inventory page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); inventory.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/04-osds.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/04-osds.e2e-spec.ts index e80398d5a47..41f0933b7a0 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/04-osds.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/orchestrator/04-osds.e2e-spec.ts @@ -7,6 +7,7 @@ describe('OSDs page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); osds.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/pools/pools.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/pools/pools.e2e-spec.ts index ff925b8a36e..e5a28bfd4e2 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/pools/pools.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/pools/pools.e2e-spec.ts @@ -6,6 +6,7 @@ describe('Pools page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); pools.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts index e5e0daa4c1f..737c1126821 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts @@ -6,6 +6,7 @@ describe('RGW buckets page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); buckets.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.e2e-spec.ts index 03b2ca83421..4cad786c6fe 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.e2e-spec.ts @@ -5,6 +5,7 @@ describe('RGW daemons page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); daemons.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/users.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/users.e2e-spec.ts index a8d7d45b419..1b580db7dcf 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/users.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/users.e2e-spec.ts @@ -6,6 +6,7 @@ describe('RGW users page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); users.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/dashboard.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/dashboard.e2e-spec.ts index c33112be72e..9cb84480b64 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/dashboard.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/dashboard.e2e-spec.ts @@ -18,6 +18,7 @@ describe('Dashboard Main Page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); dashboard.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/language.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/language.e2e-spec.ts index fa20f0be542..ccf16c2b55c 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/language.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/language.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Shared pages', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); language.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/navigation.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/navigation.e2e-spec.ts index f9d74a08b39..71bfa4b9f24 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/navigation.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/navigation.e2e-spec.ts @@ -5,6 +5,7 @@ describe('Shared pages', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); shared.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/notification.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/notification.e2e-spec.ts index b69f26f58dc..2ee73a70632 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/notification.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/notification.e2e-spec.ts @@ -8,6 +8,7 @@ describe('Notification page', () => { before(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); pools.navigateTo('create'); pools.create(poolName, 8); pools.edit_pool_pg(poolName, 4, false); @@ -15,12 +16,14 @@ describe('Notification page', () => { after(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); pools.navigateTo(); pools.delete(poolName); }); beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); pools.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/role-mgmt.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/role-mgmt.e2e-spec.ts index 7e76f168e6d..c3f325dbbe1 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/role-mgmt.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/role-mgmt.e2e-spec.ts @@ -6,6 +6,7 @@ describe('Role Management page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); roleMgmt.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/user-mgmt.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/user-mgmt.e2e-spec.ts index 57818db0ae7..92dc772121b 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/user-mgmt.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/ui/user-mgmt.e2e-spec.ts @@ -6,6 +6,7 @@ describe('User Management page', () => { beforeEach(() => { cy.login(); + Cypress.Cookies.preserveOnce('token'); userMgmt.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/support/commands.ts b/src/pybind/mgr/dashboard/frontend/cypress/support/commands.ts index 3d6dfeafa18..60fa7a746c0 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/support/commands.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/support/commands.ts @@ -13,7 +13,6 @@ let auth: any; const fillAuth = () => { window.localStorage.setItem('dashboard_username', auth.username); - window.localStorage.setItem('access_token', auth.token); window.localStorage.setItem('dashboard_permissions', auth.permissions); window.localStorage.setItem('user_pwd_expiration_date', auth.pwdExpirationDate); window.localStorage.setItem('user_pwd_update_required', auth.pwdUpdateRequired); diff --git a/src/pybind/mgr/dashboard/frontend/package-lock.json b/src/pybind/mgr/dashboard/frontend/package-lock.json index b12e6fbcc0a..d43b6b4ee61 100644 --- a/src/pybind/mgr/dashboard/frontend/package-lock.json +++ b/src/pybind/mgr/dashboard/frontend/package-lock.json @@ -522,14 +522,6 @@ "tslib": "^2.0.0" } }, - "@auth0/angular-jwt": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-5.0.1.tgz", - "integrity": "sha512-djllMh6rthPscEj5n5T9zF223q8t+sDqnUuAYTJjdKoHvMAzYwwi2yP67HbojqjODG4ZLFAcPtRuzGgp+r7nDQ==", - "requires": { - "tslib": "^2.0.0" - } - }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", diff --git a/src/pybind/mgr/dashboard/frontend/package.json b/src/pybind/mgr/dashboard/frontend/package.json index ef75886fe29..0bdbdfd1fa2 100644 --- a/src/pybind/mgr/dashboard/frontend/package.json +++ b/src/pybind/mgr/dashboard/frontend/package.json @@ -84,7 +84,6 @@ "@angular/platform-browser": "10.1.5", "@angular/platform-browser-dynamic": "10.1.5", "@angular/router": "10.1.5", - "@auth0/angular-jwt": "5.0.1", "@circlon/angular-tree-component": "10.0.0", "@ng-bootstrap/ng-bootstrap": "7.0.0", "@swimlane/ngx-datatable": "18.0.0", diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts index dc8e81b39d4..2f59a0175ac 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts @@ -3,7 +3,6 @@ import { ErrorHandler, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { JwtModule } from '@auth0/angular-jwt'; import { ToastrModule } from 'ngx-toastr'; import { AppRoutingModule } from './app-routing.module'; @@ -14,10 +13,6 @@ import { ApiInterceptorService } from './shared/services/api-interceptor.service import { JsErrorHandler } from './shared/services/js-error-handler.service'; import { SharedModule } from './shared/shared.module'; -export function jwtTokenGetter() { - return localStorage.getItem('access_token'); -} - @NgModule({ declarations: [AppComponent], imports: [ @@ -32,12 +27,7 @@ export function jwtTokenGetter() { AppRoutingModule, CoreModule, SharedModule, - CephModule, - JwtModule.forRoot({ - config: { - tokenGetter: jwtTokenGetter - } - }) + CephModule ], exports: [SharedModule], providers: [ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts index 582eae7dc93..ca72007ee81 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts @@ -96,7 +96,7 @@ describe('RbdSnapshotListComponent', () => { rbdService = new RbdService(null, null); notificationService = new NotificationService(null, null, null); authStorageService = new AuthStorageService(); - authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] }); + authStorageService.set('user', { 'rbd-image': ['create', 'read', 'update', 'delete'] }); component = new RbdSnapshotListComponent( authStorageService, modalService, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts index 749fadd1a01..868ba66a002 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.ts @@ -51,7 +51,6 @@ export class LoginComponent implements OnInit { } else { this.authStorageService.set( login.username, - token, login.permissions, login.sso, login.pwdExpirationDate diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts index 6be29308312..a76275bbec9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts @@ -3,7 +3,6 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { Icons } from '~/app/shared/enum/icons.enum'; -import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; import { DocService } from '~/app/shared/services/doc.service'; import { ModalService } from '~/app/shared/services/modal.service'; import { AboutComponent } from '../about/about.component'; @@ -20,11 +19,7 @@ export class DashboardHelpComponent implements OnInit { modalRef: NgbModalRef; icons = Icons; - constructor( - private modalService: ModalService, - private authStorageService: AuthStorageService, - private docService: DocService - ) {} + constructor(private modalService: ModalService, private docService: DocService) {} ngOnInit() { this.docService.subscribeOnce('dashboard', (url: string) => { @@ -37,8 +32,6 @@ export class DashboardHelpComponent implements OnInit { } goToApiDocs() { - const tokenInput = this.docsFormElement.nativeElement.children[0]; - tokenInput.value = this.authStorageService.getToken(); this.docsFormElement.nativeElement.submit(); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.spec.ts index 5c759804d40..c32f0ea05fc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.spec.ts @@ -33,7 +33,7 @@ describe('AuthService', () => { it('should login and save the user', fakeAsync(() => { const fakeCredentials = { username: 'foo', password: 'bar' }; - const fakeResponse = { username: 'foo', token: 'tokenbytes' }; + const fakeResponse = { username: 'foo' }; service.login(fakeCredentials).subscribe(); const req = httpTesting.expectOne('api/auth'); expect(req.request.method).toBe('POST'); @@ -41,7 +41,6 @@ describe('AuthService', () => { req.flush(fakeResponse); tick(); expect(localStorage.getItem('dashboard_username')).toBe('foo'); - expect(localStorage.getItem('access_token')).toBe('tokenbytes'); })); it('should logout and remove the user', () => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.ts index b8bf9558323..6c8356af873 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/auth.service.ts @@ -28,7 +28,6 @@ export class AuthService { tap((resp: LoginResponse) => { this.authStorageService.set( resp.username, - resp.token, resp.permissions, resp.sso, resp.pwdExpirationDate, @@ -40,8 +39,8 @@ export class AuthService { logout(callback: Function = null) { return this.http.post('api/auth/logout', null).subscribe((resp: any) => { - this.router.navigate(['/login'], { skipLocationChange: true }); this.authStorageService.remove(); + this.router.navigate(['/login'], { skipLocationChange: true }); if (callback) { callback(); } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts index 7b9fc4b277e..12b4b8348ea 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/login-response.ts @@ -1,6 +1,5 @@ export class LoginResponse { username: string; - token: string; permissions: object; pwdExpirationDate: number; sso: boolean; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts index 67c093de6ec..f202c095f47 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts @@ -34,13 +34,13 @@ describe('AuthStorageService', () => { }); it('should be SSO', () => { - service.set(username, '', {}, true); + service.set(username, {}, true); expect(localStorage.getItem('sso')).toBe('true'); expect(service.isSSO()).toBe(true); }); it('should not be SSO', () => { - service.set(username, ''); + service.set(username); expect(localStorage.getItem('sso')).toBe('false'); expect(service.isSSO()).toBe(false); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.ts index 5752f78c28a..15e21f9ed54 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.ts @@ -13,14 +13,12 @@ export class AuthStorageService { set( username: string, - token: string, permissions = {}, sso = false, pwdExpirationDate: number = null, pwdUpdateRequired: boolean = false ) { localStorage.setItem('dashboard_username', username); - localStorage.setItem('access_token', token); localStorage.setItem('dashboard_permissions', JSON.stringify(new Permissions(permissions))); localStorage.setItem('user_pwd_expiration_date', String(pwdExpirationDate)); localStorage.setItem('user_pwd_update_required', String(pwdUpdateRequired)); @@ -28,16 +26,11 @@ export class AuthStorageService { } remove() { - localStorage.removeItem('access_token'); localStorage.removeItem('dashboard_username'); localStorage.removeItem('user_pwd_expiration_data'); localStorage.removeItem('user_pwd_update_required'); } - getToken(): string { - return localStorage.getItem('access_token'); - } - isLoggedIn() { return localStorage.getItem('dashboard_username') !== null; } diff --git a/src/pybind/mgr/dashboard/services/auth.py b/src/pybind/mgr/dashboard/services/auth.py index 610484b0e07..c44963ca7ed 100644 --- a/src/pybind/mgr/dashboard/services/auth.py +++ b/src/pybind/mgr/dashboard/services/auth.py @@ -67,12 +67,20 @@ class JwtManager(object): @classmethod def get_token_from_header(cls): - auth_header = cherrypy.request.headers.get('authorization') - if auth_header is not None: - scheme, params = auth_header.split(' ', 1) - if scheme.lower() == 'bearer': - return params - return None + auth_cookie_name = 'token' + try: + # use cookie + return cherrypy.request.cookie[auth_cookie_name].value + except KeyError: + try: + # fall-back: use Authorization header + auth_header = cherrypy.request.headers.get('authorization') + if auth_header is not None: + scheme, params = auth_header.split(' ', 1) + if scheme.lower() == 'bearer': + return params + except IndexError: + return None @classmethod def set_user(cls, username): -- 2.47.3