diff --git a/frappe/app.py b/frappe/app.py index 2cfeafb426..5feecb7823 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -134,6 +134,12 @@ def handle_exception(e): # code 409 represents conflict http_status_code = 508 + if http_status_code==401: + frappe.respond_as_web_page(_("Session Expired"), + _("Your session has expired, please login again to continue."), + http_status_code=http_status_code, indicator_color='red') + return_as_message = True + if http_status_code==403: frappe.respond_as_web_page(_("Not Permitted"), _("You do not have enough permissions to complete the action"), diff --git a/frappe/exceptions.py b/frappe/exceptions.py index c09ebd26e0..1ffbf96391 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -14,6 +14,9 @@ class ValidationError(Exception): class AuthenticationError(Exception): http_status_code = 401 +class SessionExpired(Exception): + http_status_code = 401 + class PermissionError(Exception): http_status_code = 403 diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 11c6375cc7..804ed9d0e1 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -311,13 +311,61 @@ frappe.Application = Class.extend({ method:'logout', callback: function(r) { if(r.exc) { - console.log(r.exc); return; } me.redirect_to_login(); } }) }, + handle_session_expired: function() { + if(!frappe.app.session_expired_dialog) { + var dialog = new frappe.ui.Dialog({ + title: __('Session Expired'), + fields: [ + { fieldtype:'Password', fieldname:'password', + label: __('Please Enter Your Password to Continue') }, + ], + onhide: () => { + if (!dialog.logged_in) { + frappe.app.redirect_to_login(); + } + } + }); + dialog.set_primary_action(__('Login'), () => { + frappe.call({ + method: 'login', + args: { + usr: frappe.session.user, + pwd: dialog.get_values().password + }, + callback: (r) => { + if (r.message==='Logged In') { + dialog.logged_in = true; + + // revert backdrop + $('.modal-backdrop').css({ + 'opacity': '', + 'background-color': '#334143' + }); + } + dialog.hide(); + }, + statusCode: () => { + dialog.hide(); + } + }); + }); + frappe.app.session_expired_dialog = dialog; + } + if(!frappe.app.session_expired_dialog.display) { + frappe.app.session_expired_dialog.show(); + // add backdrop + $('.modal-backdrop').css({ + 'opacity': 1, + 'background-color': '#EBEFF2' + }); + } + }, redirect_to_login: function() { window.location.href = '/'; }, diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 41a2aef40b..e6ed3ccc18 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -67,15 +67,22 @@ frappe.request.call = function(opts) { opts.success_callback && opts.success_callback(data, xhr.responseText); }, 401: function(xhr) { - msgprint({message:__("You have been logged out"), indicator: 'red'}); - frappe.app.logout(); + if(frappe.app.session_expired_dialog && frappe.app.session_expired_dialog.display) { + frappe.app.redirect_to_login(); + } else { + frappe.app.handle_session_expired(); + }; }, 404: function(xhr) { msgprint({title:__("Not found"), indicator:'red', message: __('The resource you are looking for is not available')}); }, 403: function(xhr) { - if (xhr.responseJSON && xhr.responseJSON._server_messages) { + if (frappe.get_cookie('sid')==='Guest') { + // session expired + frappe.app.handle_session_expired(); + } + else if (xhr.responseJSON && xhr.responseJSON._server_messages) { var _server_messages = JSON.parse(xhr.responseJSON._server_messages); // avoid double messages @@ -83,10 +90,13 @@ frappe.request.call = function(opts) { return; } } + else { + frappe.msgprint({ + title:__("Not permitted"), indicator:'red', + message: __('You do not have enough permissions to access this resource. Please contact your manager to get access.')}); + } + - frappe.utils.play_sound("error"); - msgprint({title:__("Not permitted"), indicator:'red', - message: __('You do not have enough permissions to access this resource. Please contact your manager to get access.')}); }, 508: function(xhr) { frappe.utils.play_sound("error"); @@ -233,11 +243,7 @@ frappe.request.cleanup = function(opts, r) { // session expired? - Guest has no business here! if(r.session_expired || frappe.get_cookie("sid")==="Guest") { - if(!frappe.app.logged_out) { - localStorage.setItem("session_last_route", location.hash); - msgprint(__('Session Expired. Logging you out')); - frappe.app.logout(); - } + frappe.app.handle_session_expired(); return; } diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index 18ef247519..8ab1019a54 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -47,7 +47,8 @@ frappe.ui.Dialog = frappe.ui.FieldGroup.extend({ cur_dialog = null; } } - me.onhide && me.onhide.apply(me); + me.onhide && me.onhide(); + me.on_hide && me.on_hide(); }) .on("shown.bs.modal", function() { // focus on first input diff --git a/frappe/sessions.py b/frappe/sessions.py index 86ee1a6d12..296fd4e531 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -286,6 +286,7 @@ class Session: """get session record, or return the standard Guest Record""" from frappe.auth import clear_cookies r = self.get_session_data() + if not r: frappe.response["session_expired"] = 1 clear_cookies()