From b1801f46a2f9fc4ad50e56924e92db505e8d9a8a Mon Sep 17 00:00:00 2001 From: Avan Thakkar Date: Fri, 19 Mar 2021 18:29:22 +0530 Subject: [PATCH] mgr/dashboard: Unable to login to ceph dashboard until clearing cookies Fixes: https://tracker.ceph.com/issues/49897 Signed-off-by: Avan Thakkar Clears the cookie for token after logout. (cherry picked from commit 7eb59c0250d69ea05d87276f47299f91e4069b30) --- .../mgr/dashboard/controllers/__init__.py | 26 ++++++++++++------- src/pybind/mgr/dashboard/controllers/auth.py | 10 ++++--- src/pybind/mgr/dashboard/controllers/saml2.py | 9 ++++--- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/__init__.py b/src/pybind/mgr/dashboard/controllers/__init__.py index 11b6323255c2e..77b5b4747364d 100644 --- a/src/pybind/mgr/dashboard/controllers/__init__.py +++ b/src/pybind/mgr/dashboard/controllers/__init__.py @@ -949,6 +949,23 @@ class RESTController(BaseController): return _wrapper +class ControllerAuthMixin(object): + @staticmethod + def _delete_token_cookie(token): + cherrypy.response.cookie['token'] = token + cherrypy.response.cookie['token']['expires'] = 0 + cherrypy.response.cookie['token']['max-age'] = 0 + + @staticmethod + def _set_token_cookie(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' + + # Role-based access permissions decorators def _set_func_permissions(func, permissions): @@ -1031,12 +1048,3 @@ 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 03408572fffc6..2e45b7c92e184 100644 --- a/src/pybind/mgr/dashboard/controllers/auth.py +++ b/src/pybind/mgr/dashboard/controllers/auth.py @@ -9,8 +9,8 @@ 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, set_cookies +from . import ApiController, ControllerAuthMixin, ControllerDoc, EndpointDoc, \ + RESTController, allow_empty_body # Python 3.8 introduced `samesite` attribute: # https://docs.python.org/3/library/http.cookies.html#morsel-objects @@ -31,10 +31,11 @@ AUTH_CHECK_SCHEMA = { @ApiController('/auth', secure=False) @ControllerDoc("Initiate a session with Ceph", "Auth") -class Auth(RESTController): +class Auth(RESTController, ControllerAuthMixin): """ Provide authenticates and returns JWT token. """ + def create(self, username, password): user_data = AuthManager.authenticate(username, password) user_perms, pwd_expiration_date, pwd_update_required = None, None, None @@ -56,7 +57,7 @@ class Auth(RESTController): # For backward-compatibility: PyJWT versions < 2.0.0 return bytes. token = token.decode('utf-8') if isinstance(token, bytes) else token - set_cookies(url_prefix, token) + self._set_token_cookie(url_prefix, token) return { 'token': token, 'username': username, @@ -89,6 +90,7 @@ class Auth(RESTController): logger.debug('Logout successful') token = JwtManager.get_token_from_header() JwtManager.blocklist_token(token) + self._delete_token_cookie(token) redirect_url = '#/login' if mgr.SSO_DB.protocol == 'saml2': redirect_url = 'auth/saml2/slo' diff --git a/src/pybind/mgr/dashboard/controllers/saml2.py b/src/pybind/mgr/dashboard/controllers/saml2.py index 84e8a132a0bc2..fb8278068b1a6 100644 --- a/src/pybind/mgr/dashboard/controllers/saml2.py +++ b/src/pybind/mgr/dashboard/controllers/saml2.py @@ -16,11 +16,11 @@ 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, set_cookies +from . import BaseController, Controller, ControllerAuthMixin, Endpoint, allow_empty_body @Controller('/auth/saml2', secure=False) -class Saml2(BaseController): +class Saml2(BaseController, ControllerAuthMixin): @staticmethod def _build_req(request, post_data): @@ -71,7 +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) + self._set_token_cookie(url_prefix, token) raise cherrypy.HTTPRedirect("{}/#/login?access_token={}".format(url_prefix, token)) return { @@ -105,6 +105,7 @@ class Saml2(BaseController): # pylint: disable=unused-argument Saml2._check_python_saml() JwtManager.reset_user() - cherrypy.response.cookie['token'] = {'expires': 0, 'max-age': 0} + token = JwtManager.get_token_from_header() + self._delete_token_cookie(token) url_prefix = prepare_url_prefix(mgr.get_module_option('url_prefix', default='')) raise cherrypy.HTTPRedirect("{}/#/login".format(url_prefix)) -- 2.39.5