[PATCH V2 0/2] bug fix: redirect to the protected page after login

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V1 -> V2: remove kimchisession hook ShaoHe Feng (2): bug fix: redirect to the protected page after login remove kimchisession hook and add the same logic to root.get src/kimchi/auth.py | 31 ++++++++++++------------------- src/kimchi/config.py.in | 4 ---- src/kimchi/root.py | 40 +++++++++++++++++++++++++--------------- src/kimchi/server.py | 2 -- tests/test_config.py.in | 4 ---- ui/pages/login.html.tmpl | 3 ++- 6 files changed, 39 insertions(+), 45 deletions(-) -- 1.9.3

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> add a 'next' attribute in login html page. if "next" is not None, means there is a html protected page user want to access. after authentication success, redirect to it. if "next" is None, then redirect to the last page recorde in th the cookie. Test this case: 1. input a protected page URL in the browser. such as: https://localhost:8001/spice.html?port=64667&listen=localhost&token=vmname&encrypt=1 2. input a wrong password or username: it should report: "The username or password you entered is incorrect. Please try again." 3. input the right password and username: It should redirect to the former page. https://localhost:8001/spice.html?port=64667&listen=localhost&token=vmname&encrypt=1 Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/auth.py | 18 ++++++++++++------ src/kimchi/root.py | 34 +++++++++++++++++++--------------- ui/pages/login.html.tmpl | 3 ++- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 59889ed..c7f94a7 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -28,7 +28,6 @@ import re import termios import time -import urllib2 from kimchi import template @@ -43,9 +42,13 @@ def redirect_login(): - next_url = urllib2.quote( - cherrypy.request.path_info.encode('utf-8'), safe="") - raise cherrypy.HTTPRedirect("/login.html?next=%s" % next_url, 303) + url = "/login.html" + if cherrypy.request.path_info.endswith(".html"): + next_url = cherrypy.serving.request.request_line.split()[1] + next_url = base64.urlsafe_b64encode(next_url) + url = "/login.html?next=%s" % next_url + + raise cherrypy.HTTPRedirect(url, 303) def debug(msg): @@ -194,7 +197,7 @@ def check_auth_httpba(): return login(username, password) -def login(username, password): +def login(username, password, **kwargs): try: if not authenticate(username, password): debug("User cannot be verified with the supplied password") @@ -202,7 +205,10 @@ def login(username, password): except PAM.error, (resp, code): if (cherrypy.request.path_info == "/login" and not template.can_accept('application/json')): - raise cherrypy.HTTPRedirect("/login.html?error=userPassWrong", 303) + next_url = kwargs.get("next") + url = "/login.html?error=userPassWrong" + url = url if next_url is None else url + "&next=%s" % next_url + raise cherrypy.HTTPRedirect(url, 303) msg_args = {'username': username, 'code': code} raise OperationFailed("KCHAUTH0001E", msg_args) diff --git a/src/kimchi/root.py b/src/kimchi/root.py index f2b6a53..82bd97b 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -17,6 +17,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +import base64 import cherrypy import json import os @@ -122,25 +123,28 @@ def __init__(self, model, dev_env): def login(self, *args, **kwargs): username = kwargs.get('username') password = kwargs.get('password') - # forms base authentication + # traditional form base authentication + kwa = {} if username is not None: - next_url = cherrypy.request.cookie.get("lastPage") + # UI can parser the redirect url by "next" query parameter + next_url = kwargs.get('next') + next_url = next_url[0] if(type(next_url) is list) else next_url if next_url is None: - # UI can parser the redirect url by "next" query parameter - next_url = kwargs.get('next', "/") - next_url = next_url[0] if(type(next_url) is list) else next_url + lastPage = cherrypy.request.cookie.get("lastPage") + next_url = lastPage.value if lastPage is not None else "/" else: - next_url = next_url.value - auth.login(username, password) + kwa = {"next": next_url.encode("utf-8")} + next_url = base64.urlsafe_b64decode(next_url.encode("utf-8")) + auth.login(username, password, **kwa) raise cherrypy.HTTPRedirect(next_url, 303) - else: - try: - params = parse_request() - username = params['username'] - password = params['password'] - except KeyError, item: - e = MissingParameter('KCHAUTH0003E', {'item': str(item)}) - raise cherrypy.HTTPError(400, e.message) + + try: + params = parse_request() + username = params['username'] + password = params['password'] + except KeyError, item: + e = MissingParameter('KCHAUTH0003E', {'item': str(item)}) + raise cherrypy.HTTPError(400, e.message) try: user_info = auth.login(username, password) diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl index 0fa7122..f8f683d 100644 --- a/ui/pages/login.html.tmpl +++ b/ui/pages/login.html.tmpl @@ -21,6 +21,7 @@ #silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang) #silent _ = t.gettext #silent _t = t.gettext +#silent next = "?next=%s" % $getVar('data.next', '') if $getVar('data.next', '') else "" #from kimchi.config import get_version <!DOCTYPE html> <html lang="$lang.lang[0]"> @@ -99,7 +100,7 @@ function init() { <div id="messUserPass" class="err-mess" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div> <div id="messSession" class="err-mess" style="display: none;">$_("Session timeout, please re-login.")</div> </div> - <form id="form-login" action="/login" method="POST" class="login-panel" onsubmit="updateBtnLabel();"> + <form id="form-login" action="/login$next" method="POST" class="login-panel" onsubmit="updateBtnLabel();"> <div class="row"> <input type="text" id="username" name="username" required="required" placeholder="$_("User Name")" autofocus/> <div id="username-msg" class="msg-required"></div> -- 1.9.3

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> kimchisession hook is heavy overhead. root.get just return the kimchi-ui.html, it is not a restful API. consider root.get is the only entry of UI. So move the logic code to root.get. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/auth.py | 13 ------------- src/kimchi/config.py.in | 4 ---- src/kimchi/root.py | 6 ++++++ src/kimchi/server.py | 2 -- tests/test_config.py.in | 4 ---- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index c7f94a7..6a4a610 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -269,16 +269,3 @@ def kimchiauth(admin_methods=None): e = InvalidOperation('KCHAUTH0002E') raise cherrypy.HTTPError(401, e.message.encode('utf-8')) - - -def kimchisession(admin_methods=None): - session = cherrypy.request.cookie.get("kimchi") - last_page = cherrypy.request.cookie.get("lastPage") - headers = cherrypy.request.headers - authheader = headers.get('AUTHORIZATION') - # when client browser first login in, both the session and lastPage cookie - # are None. - # when session timeout, only session cookie is None. - if (session is None and last_page is None and authheader is None and - ("Accept" in headers and not template.can_accept('application/json'))): - redirect_login() diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in index 74c8fa5..3aef8cf 100644 --- a/src/kimchi/config.py.in +++ b/src/kimchi/config.py.in @@ -179,7 +179,6 @@ class KimchiConfig(dict): 'tools.sessions.locking': 'explicit', 'tools.sessions.storage_type': 'ram', 'tools.sessions.timeout': SESSIONSTIMEOUT, - 'tools.kimchisession.on': True, 'tools.kimchiauth.on': False }, '/vnc_auto.html': { @@ -191,9 +190,6 @@ class KimchiConfig(dict): '/kimchi-ui.html': { 'tools.kimchiauth.on': True }, - '/login.html': { - 'tools.kimchisession.on': False, - }, '/data/screenshots': { 'tools.staticdir.on': True, 'tools.staticdir.dir': get_screenshot_path(), diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 82bd97b..2daec64 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -77,6 +77,12 @@ def error_development_handler(self, status, message, traceback, version): return res def get(self): + last_page = cherrypy.request.cookie.get("lastPage") + # when session timeout, only session cookie is None. + # when first login, both session and lastPage are None. + if cherrypy.session.originalid is None and last_page is None: + raise cherrypy.HTTPRedirect("/login.html", 303) + return self.default(self.default_page) @cherrypy.expose diff --git a/src/kimchi/server.py b/src/kimchi/server.py index 30140ce..7344349 100644 --- a/src/kimchi/server.py +++ b/src/kimchi/server.py @@ -77,8 +77,6 @@ def __init__(self, options): cherrypy.tools.nocache = cherrypy.Tool('on_end_resource', set_no_cache) cherrypy.tools.kimchiauth = cherrypy.Tool('before_handler', auth.kimchiauth) - cherrypy.tools.kimchisession = cherrypy.Tool('before_request_body', - auth.kimchisession) # Setting host to 127.0.0.1. This makes kimchi runs # as a localhost app, inaccessible to the outside # directly. You must go through the proxy. diff --git a/tests/test_config.py.in b/tests/test_config.py.in index a78cb04..f349419 100644 --- a/tests/test_config.py.in +++ b/tests/test_config.py.in @@ -104,7 +104,6 @@ class ConfigTests(unittest.TestCase): 'tools.sessions.locking': 'explicit', 'tools.sessions.storage_type': 'ram', 'tools.sessions.timeout': SESSIONSTIMEOUT, - 'tools.kimchisession.on': True, 'tools.kimchiauth.on': False }, '/vnc_auto.html': { @@ -116,9 +115,6 @@ class ConfigTests(unittest.TestCase): '/kimchi-ui.html': { 'tools.kimchiauth.on': True }, - '/login.html': { - 'tools.kimchisession.on': False, - }, '/css': { 'tools.staticdir.on': True, 'tools.staticdir.dir': '%s/ui/css' % paths.prefix, -- 1.9.3
participants (1)
-
shaohef@linux.vnet.ibm.com