diff --git a/CHANGELOG b/CHANGELOG index 2b49a8c827..5e9613d7b2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Manage custom login process * Add clear icon on Many2One * Re-position icons on Many2One widget diff --git a/tryton/common/common.py b/tryton/common/common.py index ec1c9e0859..cde806af64 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -1082,31 +1082,25 @@ def process_exception(exception, *args, **kwargs): elif exception.faultCode.startswith('403'): from tryton.gui.main import Main if not PLOCK.acquire(False): - return False - hostname = rpc._HOST - port = rpc._PORT + return + language = CONFIG['client.lang'] + func = lambda parameters: rpc.login( + rpc._HOST, rpc._PORT, rpc._DATABASE, rpc._USERNAME, parameters, + language) try: - while True: - password = ask(_('Password:'), visibility=False) - if password is None: - Main.get_main().sig_quit() - res = rpc.login(rpc._USERNAME, password, hostname, port, - rpc._DATABASE) - if res == -1: - message(_('Connection error!\n' - 'Unable to connect to the server!')) - return False - if res < 0: - continue - if args: - try: - return rpc_execute(*args) - except TrytonServerError, exception: - return process_exception(exception, *args, - rpc_execute=rpc_execute) - return True + Login(func) + except TrytonError, exception: + if exception.faultCode == 'QueryCanceled': + Main.get_main().sig_quit() + raise finally: PLOCK.release() + if args: + try: + return rpc_execute(*args) + except TrytonServerError, exception: + return process_exception(exception, *args, + rpc_execute=rpc_execute) elif isinstance(exception, (socket.error, TrytonServerUnavailable)): warning(str(exception), _('Network Error!')) return False @@ -1120,6 +1114,36 @@ def process_exception(exception, *args, **kwargs): return False +class Login(object): + def __init__(self, func): + parameters = {} + while True: + try: + func(parameters) + except TrytonServerError, exception: + if exception.faultCode.startswith('403'): + parameters.clear() + continue + if exception.faultCode != 'LoginException': + raise + name, message, type = exception.args + value = getattr(self, 'get_%s' % type)(message) + if value is None: + raise TrytonError('QueryCanceled') + parameters[name] = value + continue + else: + return + + @classmethod + def get_char(self, message): + return ask(message) + + @classmethod + def get_password(self, message): + return ask(message, visibility=False) + + def node_attributes(node): result = {} attrs = node.attributes diff --git a/tryton/gui/main.py b/tryton/gui/main.py index 29a92d0b4b..2b58049066 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -874,32 +874,25 @@ class Main(object): self._sig_remove_book(widget, self.notebook.get_nth_page(self.notebook.get_current_page())) - def sig_login(self, widget=None, res=None): + def sig_login(self, widget=None): if not self.sig_logout(widget, disconnect=False): return - if not res: - try: - res = DBLogin().run() - except TrytonError, exception: - if exception.faultCode == 'QueryCanceled': - return False - except TrytonServerError, exception: - common.process_exception(exception) - return + language = CONFIG['client.lang'] + host, port, database, username = DBLogin().run() + func = lambda parameters: rpc.login( + host, port, database, username, parameters, language) try: - log_response = rpc.login(*res) - except TrytonServerError, exception: + common.Login(func) + except Exception, exception: + if (isinstance(exception, TrytonError) + and exception.faultCode == 'QueryCanceled'): + return + if (isinstance(exception, TrytonServerError) + and exception.faultCode.startswith('403')): + return self.sig_login() common.process_exception(exception) return - if log_response > 0: - self.get_preferences() - elif log_response == -1: - common.message(_('Connection error!\n' - 'Unable to connect to the server!')) - elif log_response == -2: - common.message(_('Connection error!\n' - 'Bad username or password!')) - return self.sig_login() + self.get_preferences() self.favorite_unset() self.menuitem_favorite.set_sensitive(True) self.menuitem_user.set_sensitive(True) diff --git a/tryton/gui/window/dblogin.py b/tryton/gui/window/dblogin.py index d441552b7e..349407ce23 100644 --- a/tryton/gui/window/dblogin.py +++ b/tryton/gui/window/dblogin.py @@ -443,19 +443,9 @@ class DBLogin(object): self.table_main.attach(self.label_database, 0, 1, 5, 6, xoptions=gtk.FILL) self.table_main.attach(self.entry_database, 1, 3, 5, 6) - self.entry_password = gtk.Entry() - self.entry_password.set_visibility(False) - self.entry_password.set_activates_default(True) - self.table_main.attach(self.entry_password, 1, 3, 7, 8) self.entry_login = gtk.Entry() self.entry_login.set_activates_default(True) self.table_main.attach(self.entry_login, 1, 3, 6, 7) - label_password = gtk.Label(str=_("Password:")) - label_password.set_justify(gtk.JUSTIFY_RIGHT) - label_password.set_alignment(1, 0.5) - label_password.set_padding(3, 3) - label_password.set_mnemonic_widget(self.entry_password) - self.table_main.attach(label_password, 0, 1, 7, 8, xoptions=gtk.FILL) label_username = gtk.Label(str=_("User name:")) label_username.set_alignment(1, 0.5) label_username.set_padding(3, 3) @@ -572,10 +562,7 @@ class DBLogin(object): self.entry_login.set_text(CONFIG['login.login']) self.dialog.show_all() - if not self.entry_login.get_text(): - self.entry_login.grab_focus() - else: - self.entry_password.grab_focus() + self.entry_login.grab_focus() # Reshow dialog for gtk-quarks self.dialog.reshow_with_initial_size() @@ -583,10 +570,10 @@ class DBLogin(object): # The previous action did not called expand_hostspec self.expand_hostspec(self.expander) - res, result = None, ('', '', '', '', '') + response, result = None, ('', '', '', '') while not all(result): - res = self.dialog.run() - if res != gtk.RESPONSE_OK: + response = self.dialog.run() + if response != gtk.RESPONSE_OK: break active_profile = self.combo_profile.get_active() if active_profile != -1: @@ -610,12 +597,12 @@ class DBLogin(object): CONFIG['login.db'] = database CONFIG['login.expanded'] = self.expander.props.expanded CONFIG['login.login'] = login - result = (self.entry_login.get_text(), - self.entry_password.get_text(), host, port, database) + result = ( + host, port, database, self.entry_login.get_text()) self.parent.present() self.dialog.destroy() - if res != gtk.RESPONSE_OK: + if response != gtk.RESPONSE_OK: rpc.logout() raise TrytonError('QueryCanceled') return result diff --git a/tryton/gui/window/preference.py b/tryton/gui/window/preference.py index d81f7897af..1239b1f014 100644 --- a/tryton/gui/window/preference.py +++ b/tryton/gui/window/preference.py @@ -6,9 +6,9 @@ import gtk import copy from tryton.gui.window.view_form.screen import Screen from tryton.config import TRYTON_ICON -import tryton.common as common -from tryton.common import RPCExecute, RPCException +from tryton.common import RPCExecute, RPCException, process_exception, Login from tryton.gui.window.nomodal import NoModal +from tryton.exceptions import TrytonError import tryton.rpc as rpc _ = gettext.gettext @@ -84,17 +84,16 @@ class Preference(NoModal): if response_id == gtk.RESPONSE_OK: if self.screen.current_record.validate(): vals = copy.copy(self.screen.get()) - if 'password' in vals: - password = common.ask(_('Current Password:'), - visibility=False) - if not password: - return - else: - password = False + context = rpc.CONTEXT.copy() + func = lambda parameters: rpc.execute( + 'model', 'res.user', 'set_preferences', vals, parameters, + context) try: - RPCExecute('model', 'res.user', 'set_preferences', - vals, password) - except RPCException: + Login(func) + except TrytonError, exception: + if exception.faultCode == 'QueryCanceled': + return + process_exception(exception) return self.parent.present() self.destroy() diff --git a/tryton/rpc.py b/tryton/rpc.py index ee7288c953..bf4c5f4a29 100644 --- a/tryton/rpc.py +++ b/tryton/rpc.py @@ -69,27 +69,20 @@ def server_version(host, port): raise -def login(username, password, host, port, database): +def login(host, port, database, username, parameters, language=None): global CONNECTION, _USER, _USERNAME, _HOST, _PORT, _DATABASE global _VIEW_CACHE, _TOOLBAR_CACHE, _KEYWORD_CACHE _VIEW_CACHE = {} _TOOLBAR_CACHE = {} _KEYWORD_CACHE = {} - try: - if CONNECTION is not None: - CONNECTION.close() - CONNECTION = ServerPool(host, port, database) - logging.getLogger(__name__).info('common.db.login(%s, %s)' % - (username, 'x' * 10)) - with CONNECTION() as conn: - result = conn.common.db.login(username, password) - logging.getLogger(__name__).debug(repr(result)) - except socket.error: - _USER = None - return -1 - if not result: - _USER = None - return -2 + if CONNECTION is not None: + CONNECTION.close() + CONNECTION = ServerPool(host, port, database) + logging.getLogger(__name__).info('common.db.login(%s, %s, %s)' + % (username, 'x' * 10, language)) + with CONNECTION() as conn: + result = conn.common.db.login(username, parameters, language) + logging.getLogger(__name__).debug(repr(result)) _USER = result[0] _USERNAME = username session = ':'.join(map(str, [username] + result)) @@ -98,7 +91,6 @@ def login(username, password, host, port, database): _PORT = port _DATABASE = database IPCServer(host, port, database).run() - return 1 def logout():