Package Gnumed :: Package wxpython :: Module gmAuthWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmAuthWidgets

  1  # -*- coding: utf8 -*- 
  2  """GNUmed authentication widgets. 
  3   
  4  This module contains widgets and GUI 
  5  functions for authenticating users. 
  6  """ 
  7  #================================================================ 
  8  __author__ = "karsten.hilbert@gmx.net, H.Herb, H.Berger, R.Terry" 
  9  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
 10   
 11   
 12  # stdlib 
 13  import sys 
 14  import os.path 
 15  import logging 
 16  import re as regex 
 17   
 18   
 19  # 3rd party 
 20  import wx 
 21   
 22   
 23  # GNUmed 
 24  if __name__ == '__main__': 
 25          sys.path.insert(0, '../../') 
 26  from Gnumed.pycommon import gmLoginInfo 
 27  from Gnumed.pycommon import gmPG2 
 28  from Gnumed.pycommon import gmBackendListener 
 29  from Gnumed.pycommon import gmTools 
 30  from Gnumed.pycommon import gmCfg2 
 31  from Gnumed.pycommon import gmI18N 
 32   
 33  from Gnumed.business import gmPraxis 
 34   
 35  from Gnumed.wxpython import gmGuiHelpers 
 36  from Gnumed.wxpython import gmExceptionHandlingWidgets 
 37   
 38   
 39  _log = logging.getLogger('gm.ui') 
 40  _cfg = gmCfg2.gmCfgData() 
 41   
 42  try: 
 43          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
 44  except NameError: 
 45          _ = lambda x:x 
 46   
 47   
 48  msg_generic = _(""" 
 49  GNUmed database version mismatch. 
 50   
 51  This database version cannot be used with this client: 
 52   
 53   client version: %s 
 54   database version detected: %s 
 55   database version needed: %s 
 56   
 57  Currently connected to database: 
 58   
 59   host: %s 
 60   database: %s 
 61   user: %s 
 62  """) 
 63   
 64  msg_time_skew_fail = _("""\ 
 65  The server and client clocks are off 
 66  by more than %s minutes ! 
 67   
 68  You must fix the time settings before 
 69  you can use this database with this 
 70  client. 
 71   
 72  You may have to contact your 
 73  administrator for help.""") 
 74   
 75  msg_time_skew_warn = _("""\ 
 76  The server and client clocks are off 
 77  by more than %s minutes ! 
 78   
 79  You should fix the time settings. 
 80  Otherwise clinical data may appear to 
 81  have been entered at the wrong time. 
 82   
 83  You may have to contact your 
 84  administrator for help.""") 
 85   
 86  msg_insanity = _(""" 
 87  There is a serious problem with the database settings: 
 88   
 89  %s 
 90   
 91  You may have to contact your administrator for help.""") 
 92   
 93  msg_fail = _(""" 
 94  You must connect to a different database in order 
 95  to use the GNUmed client. You may have to contact 
 96  your administrator for help.""") 
 97   
 98  msg_override = _(""" 
 99  The client will, however, continue to start up because 
100  you are running a development/test version of GNUmed. 
101   
102  There may be schema related errors. Please report and/or 
103  fix them. Do not rely on this database to work properly 
104  in all cases !""") 
105   
106  #================================================================ 
107  # convenience functions 
108  #---------------------------------------------------------------- 
109 -def connect_to_database(max_attempts=3, expected_version=None, require_version=True):
110 """Display the login dialog and try to log into the backend. 111 112 - up to max_attempts times 113 - returns True/False 114 """ 115 # force programmer to set a valid expected_version 116 expected_hash = gmPG2.known_schema_hashes[expected_version] 117 client_version = _cfg.get(option = u'client_version') 118 global current_db_name 119 current_db_name = u'gnumed_v%s' % expected_version 120 121 attempt = 0 122 123 dlg = cLoginDialog(None, -1, client_version = client_version) 124 dlg.Centre(wx.BOTH) 125 126 while attempt < max_attempts: 127 128 _log.debug('login attempt %s of %s', (attempt+1), max_attempts) 129 130 connected = False 131 132 dlg.ShowModal() 133 login = dlg.panel.GetLoginInfo() 134 if login is None: 135 _log.info("user cancelled login dialog") 136 break 137 138 # try getting a connection to verify the DSN works 139 dsn = gmPG2.make_psycopg2_dsn ( 140 database = login.database, 141 host = login.host, 142 port = login.port, 143 user = login.user, 144 password = login.password 145 ) 146 try: 147 conn = gmPG2.get_raw_connection(dsn = dsn, verbose = True, readonly = True) 148 connected = True 149 150 except gmPG2.cAuthenticationError, e: 151 attempt += 1 152 _log.error(u"login attempt failed: %s", e) 153 if attempt < max_attempts: 154 if (u'host=127.0.0.1' in (u'%s' % e)) or (u'host=' not in (u'%s' % e)): 155 msg = _( 156 'Unable to connect to database:\n\n' 157 '%s\n\n' 158 "Are you sure you have got a local database installed ?\n" 159 '\n' 160 "Please retry with proper credentials or cancel.\n" 161 '\n' 162 ' (for the public and any new GNUmed data-\n' 163 ' bases the default user name and password\n' 164 ' are {any-doc, any-doc})\n' 165 '\n' 166 'You may also need to check the PostgreSQL client\n' 167 'authentication configuration in pg_hba.conf. For\n' 168 'details see:\n' 169 '\n' 170 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL' 171 ) 172 else: 173 msg = _( 174 "Unable to connect to database:\n\n" 175 "%s\n\n" 176 "Please retry with proper credentials or cancel.\n" 177 "\n" 178 "For the public and any new GNUmed databases the\n" 179 "default user name and password are {any-doc, any-doc}.\n" 180 "\n" 181 'You may also need to check the PostgreSQL client\n' 182 'authentication configuration in pg_hba.conf. For\n' 183 'details see:\n' 184 '\n' 185 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL' 186 ) 187 msg = msg % e 188 msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg) 189 gmGuiHelpers.gm_show_error ( 190 msg, 191 _('Connecting to backend') 192 ) 193 del e 194 continue 195 196 except gmPG2.dbapi.OperationalError, e: 197 _log.error(u"login attempt failed: %s", e) 198 msg = _( 199 "Unable to connect to database:\n\n" 200 "%s\n\n" 201 "Please retry another backend / user / password combination !\n" 202 "\n" 203 " (for the public and any new GNUmed databases\n" 204 " the default user name and password are\n" 205 " {any-doc, any-doc})\n" 206 "\n" 207 ) % gmPG2.extract_msg_from_pg_exception(e) 208 msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg) 209 gmGuiHelpers.gm_show_error ( 210 msg, 211 _('Connecting to backend') 212 ) 213 del e 214 continue 215 216 # connect was successful 217 gmPG2.set_default_login(login = login) 218 gmPG2.set_default_client_encoding(encoding = dlg.panel.backend_profile.encoding) 219 220 seems_bootstrapped = gmPG2.schema_exists(schema = 'gm') 221 if not seems_bootstrapped: 222 _log.error('schema [gm] does not exist - database not bootstrapped ?') 223 msg = _( 224 'The database you connected to does not seem\n' 225 'to have been boostrapped properly.\n' 226 '\n' 227 'Make sure you have run the GNUmed database\n' 228 'bootstrapper tool to create a new database.\n' 229 '\n' 230 'Further help can be found on the website at\n' 231 '\n' 232 ' http://wiki.gnumed.de\n' 233 '\n' 234 'or on the GNUmed mailing list.' 235 ) 236 gmGuiHelpers.gm_show_error(msg, _('Verifying database')) 237 connected = False 238 break 239 240 compatible = gmPG2.database_schema_compatible(version = expected_version) 241 if compatible or not require_version: 242 dlg.panel.save_state() 243 244 if not compatible: 245 connected_db_version = gmPG2.get_schema_version() 246 msg = msg_generic % ( 247 client_version, 248 connected_db_version, 249 expected_version, 250 gmTools.coalesce(login.host, '<localhost>'), 251 login.database, 252 login.user 253 ) 254 if require_version: 255 gmGuiHelpers.gm_show_error(msg + msg_fail, _('Verifying database version')) 256 connected = False 257 continue 258 gmGuiHelpers.gm_show_info(msg + msg_override, _('Verifying database version')) 259 260 # FIXME: make configurable 261 max_skew = 1 # minutes 262 if _cfg.get(option = 'debug'): 263 max_skew = 10 264 if not gmPG2.sanity_check_time_skew(tolerance = (max_skew * 60)): 265 if _cfg.get(option = 'debug'): 266 gmGuiHelpers.gm_show_warning(msg_time_skew_warn % max_skew, _('Verifying database settings')) 267 else: 268 gmGuiHelpers.gm_show_error(msg_time_skew_fail % max_skew, _('Verifying database settings')) 269 connected = False 270 continue 271 272 sanity_level, message = gmPG2.sanity_check_database_settings() 273 if sanity_level != 0: 274 gmGuiHelpers.gm_show_error((msg_insanity % message), _('Verifying database settings')) 275 if sanity_level == 2: 276 connected = False 277 continue 278 279 gmExceptionHandlingWidgets.set_is_public_database(login.public_db) 280 gmExceptionHandlingWidgets.set_helpdesk(login.helpdesk) 281 282 listener = gmBackendListener.gmBackendListener(conn = conn) 283 break 284 285 dlg.Destroy() 286 287 return connected
288 #================================================================
289 -def get_dbowner_connection(procedure=None, dbo_password=None, dbo_account=u'gm-dbo'):
290 if procedure is None: 291 procedure = _('<restricted procedure>') 292 293 # 1) get password for gm-dbo 294 if dbo_password is None: 295 dbo_password = wx.GetPasswordFromUser ( 296 message = _(""" 297 [%s] 298 299 This is a restricted procedure. We need the 300 current password for the GNUmed database owner. 301 302 Please enter the current password for <%s>:""") % ( 303 procedure, 304 dbo_account 305 ), 306 caption = procedure 307 ) 308 if dbo_password == '': 309 return None 310 311 # 2) connect as gm-dbo 312 login = gmPG2.get_default_login() 313 dsn = gmPG2.make_psycopg2_dsn ( 314 database = login.database, 315 host = login.host, 316 port = login.port, 317 user = dbo_account, 318 password = dbo_password 319 ) 320 try: 321 conn = gmPG2.get_connection ( 322 dsn = dsn, 323 readonly = False, 324 verbose = True, 325 pooled = False 326 ) 327 except: 328 _log.exception('cannot connect') 329 gmGuiHelpers.gm_show_error ( 330 aMessage = _('Cannot connect as the GNUmed database owner <%s>.') % dbo_account, 331 aTitle = procedure 332 ) 333 gmPG2.log_database_access(action = u'failed to connect as database owner for [%s]' % procedure) 334 return None 335 336 return conn
337 #================================================================
338 -def change_gmdbowner_password():
339 340 title = _(u'Changing GNUmed database owner password') 341 342 dbo_account = wx.GetTextFromUser ( 343 message = _(u"Enter the account name of the GNUmed database owner:"), 344 caption = title, 345 default_value = u'' 346 ) 347 348 if dbo_account.strip() == u'': 349 return False 350 351 dbo_conn = get_dbowner_connection ( 352 procedure = title, 353 dbo_account = dbo_account 354 ) 355 if dbo_conn is None: 356 return False 357 358 dbo_pwd_new_1 = wx.GetPasswordFromUser ( 359 message = _(u"Enter the NEW password for the GNUmed database owner:"), 360 caption = title 361 ) 362 if dbo_pwd_new_1.strip() == u'': 363 return False 364 365 dbo_pwd_new_2 = wx.GetPasswordFromUser ( 366 message = _(u"""Enter the NEW password for the GNUmed database owner, again. 367 368 (This will protect you from typos.) 369 """), 370 caption = title 371 ) 372 if dbo_pwd_new_2.strip() == u'': 373 return False 374 375 if dbo_pwd_new_1 != dbo_pwd_new_2: 376 return False 377 378 cmd = u"""ALTER ROLE "%s" ENCRYPTED PASSWORD '%s';""" % ( 379 dbo_account, 380 dbo_pwd_new_2 381 ) 382 gmPG2.run_rw_queries(link_obj = dbo_conn, queries = [{'cmd': cmd}], end_tx = True) 383 384 return True
385 #================================================================
386 -class cBackendProfile:
387 pass
388 #================================================================
389 -class cLoginDialog(wx.Dialog):
390 """cLoginDialog - window holding cLoginPanel""" 391
392 - def __init__(self, parent, id, title = _("Welcome to"), client_version = u'*** unknown ***'):
393 wx.Dialog.__init__(self, parent, id, title) 394 self.panel = cLoginPanel(self, -1, isDialog=1, client_version = client_version) 395 self.Fit() # needed for Windoze. 396 self.Centre() 397 398 self.SetIcon(gmTools.get_icon(wx = wx))
399 #================================================================
400 -class cLoginPanel(wx.Panel):
401 """GUI panel class that interactively gets Postgres login parameters. 402 403 It features combo boxes which "remember" any number of 404 previously entered settings. 405 """
406 - def __init__(self, parent, id, 407 pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL, 408 isDialog = 0, client_version = u'*** unknown ***'):
409 """Create login panel. 410 411 isDialog: if this panel is the main panel of a dialog, the panel will 412 resize the dialog automatically to display everything neatly 413 if isDialog is set to True 414 """ 415 wx.Panel.__init__(self, parent, id, pos, size, style) 416 self.parent = parent 417 418 #True if dialog was cancelled by user 419 #if the dialog is closed manually, login should be cancelled 420 self.cancelled = True 421 422 # True if this panel is displayed within a dialog (will resize the dialog automatically then) 423 self.isDialog = isDialog 424 425 self.topsizer = wx.BoxSizer(wx.VERTICAL) 426 427 # find bitmap 428 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 429 bitmap = os.path.join(paths.system_app_data_dir, 'bitmaps', 'gnumedlogo.png') 430 try: 431 png = wx.Image(bitmap, wx.BITMAP_TYPE_PNG).ConvertToBitmap() 432 bmp = wx.StaticBitmap(self, -1, png, wx.Point(10, 10), wx.Size(png.GetWidth(), png.GetHeight())) 433 self.topsizer.Add ( 434 bmp, 435 proportion = 0, 436 flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 437 border = 10 438 ) 439 except: 440 self.topsizer.Add ( 441 wx.StaticText ( 442 self, 443 -1, 444 label = _("Cannot find image") + bitmap, 445 style = wx.ALIGN_CENTRE 446 ), 447 proportion = 0, 448 flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 449 border = 10 450 ) 451 452 paramsbox_caption = _('Workplace "%s" (version %s)') % (gmPraxis.gmCurrentPraxisBranch().active_workplace, client_version) 453 454 # FIXME: why doesn't this align in the centre ? 455 self.paramsbox = wx.StaticBox( self, -1, paramsbox_caption, style = wx.ALIGN_CENTRE_HORIZONTAL) 456 self.paramsboxsizer = wx.StaticBoxSizer( self.paramsbox, wx.VERTICAL ) 457 self.paramsbox.SetForegroundColour(wx.Colour(35, 35, 142)) 458 self.paramsbox.SetFont(wx.Font( 459 pointSize = 12, 460 family = wx.SWISS, 461 style = wx.NORMAL, 462 weight = wx.BOLD, 463 underline = False 464 )) 465 self.pboxgrid = wx.FlexGridSizer(5, 2, 5, 5) 466 self.pboxgrid.AddGrowableCol(1) 467 468 # PROFILE COMBO 469 label = wx.StaticText( self, -1, _('Log into'), wx.DefaultPosition, wx.DefaultSize, 0) 470 label.SetForegroundColour(wx.Colour(35, 35, 142)) 471 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 472 self.__backend_profiles = self.__get_backend_profiles() 473 self._CBOX_profile = wx.ComboBox ( 474 self, 475 -1, 476 self.__backend_profiles.keys()[0], 477 wx.DefaultPosition, 478 size = wx.Size(550,-1), 479 choices = self.__backend_profiles.keys(), 480 style = wx.CB_READONLY 481 ) 482 self.pboxgrid.Add (self._CBOX_profile, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 483 484 # USER NAME COMBO 485 label = wx.StaticText( self, -1, _("Username"), wx.DefaultPosition, wx.DefaultSize, 0 ) 486 label.SetForegroundColour(wx.Colour(35, 35, 142)) 487 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 488 self.__previously_used_accounts = self.__get_previously_used_accounts() 489 self._CBOX_user = wx.ComboBox ( 490 self, 491 -1, 492 self.__previously_used_accounts[0], 493 wx.DefaultPosition, 494 wx.Size(150,-1), 495 self.__previously_used_accounts, 496 wx.CB_DROPDOWN 497 ) 498 self.pboxgrid.Add( self._CBOX_user, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 499 500 #PASSWORD TEXT ENTRY 501 label = wx.StaticText( self, -1, _("Password"), wx.DefaultPosition, wx.DefaultSize, 0 ) 502 label.SetForegroundColour(wx.Colour(35, 35, 142)) 503 self.pboxgrid.Add( label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 504 self.pwdentry = wx.TextCtrl( self, 1, '', wx.DefaultPosition, wx.Size(80,-1), wx.TE_PASSWORD ) 505 # set focus on password entry 506 self.pwdentry.SetFocus() 507 self.pboxgrid.Add( self.pwdentry, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 508 509 # --debug checkbox 510 label = wx.StaticText(self, -1, _('Options'), wx.DefaultPosition, wx.DefaultSize, 0) 511 label.SetForegroundColour(wx.Colour(35, 35, 142)) 512 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 513 self._CHBOX_debug = wx.CheckBox(self, -1, _('&Debug mode')) 514 self._CHBOX_debug.SetToolTipString(_('Check this to run GNUmed client in debugging mode.')) 515 self.pboxgrid.Add(self._CHBOX_debug, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 516 517 # --slave checkbox 518 label = wx.StaticText(self, -1, '', wx.DefaultPosition, wx.DefaultSize, 0) 519 label.SetForegroundColour(wx.Colour(35, 35, 142)) 520 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 521 self._CHBOX_slave = wx.CheckBox(self, -1, _('Enable &remote control')) 522 self._CHBOX_slave.SetToolTipString(_('Check this to run GNUmed client in slave mode for remote control.')) 523 self.pboxgrid.Add(self._CHBOX_slave, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 524 525 #---------------------------------------------------------------------- 526 #new button code inserted rterry 06Sept02 527 #button order re-arraged to make it consistant with usual dialog format 528 #in most operating systems ie btns ok and cancel are standard and 529 #in that order 530 #ie Order is now help, ok and cancel 531 #The order of creation is the tab order 532 #login-ok button automatically is the default when tabbing (or <enter>) 533 #from password 534 #this eliminates the heavy border when you use the default 535 #?is the default word needed for any other reason? 536 #---------------------------------------------------------------------- 537 self.button_gridsizer = wx.GridSizer(1,3,0,0) 538 #--------------------- 539 #3:create login ok button 540 #--------------------- 541 ID_BUTTON_LOGIN = wx.NewId() 542 button_login_ok = wx.Button(self, ID_BUTTON_LOGIN, _("&Ok"), wx.DefaultPosition, wx.DefaultSize, 0 ) 543 button_login_ok.SetToolTip(wx.ToolTip(_("Proceed with login.")) ) 544 button_login_ok.SetDefault() 545 546 #--------------------- 547 #3:create cancel button 548 #--------------------- 549 ID_BUTTON_CANCEL = wx.NewId() 550 button_cancel = wx.Button(self, ID_BUTTON_CANCEL, _("&Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) 551 button_cancel.SetToolTip(wx.ToolTip(_("Cancel Login.")) ) 552 #--------------------- 553 #2:create Help button 554 #--------------------- 555 ID_BUTTON_HELP = wx.NewId() 556 button_help = wx.Button(self, ID_BUTTON_HELP, _("&Help"), wx.DefaultPosition, wx.DefaultSize, 0 ) 557 button_help.SetToolTip(wx.ToolTip(_("Help for login screen"))) 558 #---------------------------- 559 #Add buttons to the gridsizer 560 #---------------------------- 561 self.button_gridsizer.Add (button_help,0,wx.EXPAND|wx.ALL,5) 562 self.button_gridsizer.Add (button_login_ok,0,wx.EXPAND|wx.ALL,5) 563 self.button_gridsizer.Add (button_cancel,0,wx.EXPAND|wx.ALL,5) 564 565 self.paramsboxsizer.Add(self.pboxgrid, 1, wx.GROW|wx.ALL, 10) 566 self.topsizer.Add(self.paramsboxsizer, 1, wx.GROW|wx.ALL, 10) 567 self.topsizer.Add( self.button_gridsizer, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 568 569 self.__load_state() 570 571 self.SetAutoLayout(True) 572 self.SetSizer( self.topsizer) 573 self.topsizer.Fit( self ) 574 if self.isDialog: 575 self.topsizer.SetSizeHints(parent) 576 577 wx.EVT_BUTTON(self, ID_BUTTON_HELP, self.OnHelp) 578 wx.EVT_BUTTON(self, ID_BUTTON_LOGIN, self.__on_login_button_pressed) 579 wx.EVT_BUTTON(self, ID_BUTTON_CANCEL, self.OnCancel)
580 581 #---------------------------------------------------------- 582 # internal helper methods 583 #----------------------------------------------------------
585 586 accounts = gmTools.coalesce ( 587 _cfg.get ( 588 group = u'backend', 589 option = u'logins', 590 source_order = [ 591 (u'explicit', u'extend'), 592 (u'user', u'extend'), 593 (u'workbase', u'extend') 594 ] 595 ), 596 ['any-doc'] 597 ) 598 # FIXME: make unique 599 600 return accounts
601 #----------------------------------------------------
602 - def __get_backend_profiles(self):
603 """Get server profiles from the configuration files. 604 605 1) from system-wide file 606 2) from user file 607 608 Profiles in the user file which have the same name 609 as a profile in the system file will override the 610 system file. 611 """ 612 # find active profiles 613 src_order = [ 614 (u'explicit', u'extend'), 615 (u'system', u'extend'), 616 (u'user', u'extend'), 617 (u'workbase', u'extend') 618 ] 619 620 profile_names = gmTools.coalesce ( 621 _cfg.get(group = u'backend', option = u'profiles', source_order = src_order), 622 [] 623 ) 624 625 # find data for active profiles 626 src_order = [ 627 (u'explicit', u'return'), 628 (u'workbase', u'return'), 629 (u'user', u'return'), 630 (u'system', u'return') 631 ] 632 633 profiles = {} 634 635 for profile_name in profile_names: 636 # FIXME: once the profile has been found always use the corresponding source ! 637 # FIXME: maybe not or else we cannot override parts of the profile 638 profile = cBackendProfile() 639 profile_section = 'profile %s' % profile_name 640 641 profile.name = profile_name 642 profile.host = gmTools.coalesce(_cfg.get(profile_section, u'host', src_order), u'').strip() 643 port = gmTools.coalesce(_cfg.get(profile_section, u'port', src_order), 5432) 644 try: 645 profile.port = int(port) 646 if profile.port < 1024: 647 raise ValueError('refusing to use priviledged port (< 1024)') 648 except ValueError: 649 _log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name) 650 continue 651 profile.database = gmTools.coalesce(_cfg.get(profile_section, u'database', src_order), u'').strip() 652 if profile.database == u'': 653 _log.warning('database name not specified, skipping profile [%s]', profile_name) 654 continue 655 profile.encoding = gmTools.coalesce(_cfg.get(profile_section, u'encoding', src_order), u'UTF8') 656 profile.public_db = bool(_cfg.get(profile_section, u'public/open access', src_order)) 657 profile.helpdesk = _cfg.get(profile_section, u'help desk', src_order) 658 659 label = u'%s (%s@%s)' % (profile_name, profile.database, profile.host) 660 profiles[label] = profile 661 662 # sort out profiles with incompatible database versions if not --debug 663 # NOTE: this essentially hardcodes the database name in production ... 664 if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')): 665 profiles2remove = [] 666 for label in profiles: 667 if profiles[label].database != current_db_name: 668 profiles2remove.append(label) 669 for label in profiles2remove: 670 del profiles[label] 671 672 if len(profiles) == 0: 673 host = u'publicdb.gnumed.de' 674 label = u'public GNUmed database (%s@%s)' % (current_db_name, host) 675 profiles[label] = cBackendProfile() 676 profiles[label].name = label 677 profiles[label].host = host 678 profiles[label].port = 5432 679 profiles[label].database = current_db_name 680 profiles[label].encoding = u'UTF8' 681 profiles[label].public_db = True 682 profiles[label].helpdesk = u'http://wiki.gnumed.de' 683 684 return profiles
685 #----------------------------------------------------------
686 - def __load_state(self):
687 688 src_order = [ 689 (u'explicit', u'return'), 690 (u'user', u'return'), 691 ] 692 693 self._CBOX_user.SetValue ( 694 gmTools.coalesce ( 695 _cfg.get(u'preferences', u'login', src_order), 696 self.__previously_used_accounts[0] 697 ) 698 ) 699 700 last_used_profile_label = _cfg.get(u'preferences', u'profile', src_order) 701 if last_used_profile_label in self.__backend_profiles.keys(): 702 self._CBOX_profile.SetValue(last_used_profile_label) 703 else: 704 self._CBOX_profile.SetValue(self.__backend_profiles.keys()[0]) 705 706 self._CHBOX_debug.SetValue(_cfg.get(option = 'debug')) 707 self._CHBOX_slave.SetValue(_cfg.get(option = 'slave'))
708 #----------------------------------------------------
709 - def save_state(self):
710 """Save parameter settings to standard configuration file""" 711 prefs_name = _cfg.get(option = 'user_preferences_file') 712 _log.debug(u'saving login preferences in [%s]', prefs_name) 713 714 gmCfg2.set_option_in_INI_file ( 715 filename = prefs_name, 716 group = 'preferences', 717 option = 'login', 718 value = self._CBOX_user.GetValue() 719 ) 720 721 gmCfg2.set_option_in_INI_file ( 722 filename = prefs_name, 723 group = 'preferences', 724 option = 'profile', 725 value = self._CBOX_profile.GetValue() 726 )
727 ############################################################################# 728 # Retrieve current settings from user interface widgets 729 #############################################################################
730 - def GetLoginInfo(self):
731 """convenience function for compatibility with gmLoginInfo.LoginInfo""" 732 if self.cancelled: 733 return None 734 735 # FIXME: do not assume conf file is latin1 ! 736 #profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('latin1').strip()] 737 profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('utf8').strip()] 738 _log.info(u'backend profile "%s" selected', profile.name) 739 _log.info(u' details: <%s> on %s@%s:%s (%s, %s)', 740 self._CBOX_user.GetValue(), 741 profile.database, 742 profile.host, 743 profile.port, 744 profile.encoding, 745 gmTools.bool2subst(profile.public_db, u'public', u'private') 746 ) 747 _log.info(u' helpdesk: "%s"', profile.helpdesk) 748 login = gmLoginInfo.LoginInfo ( 749 user = self._CBOX_user.GetValue(), 750 password = self.pwdentry.GetValue(), 751 host = profile.host, 752 database = profile.database, 753 port = profile.port 754 ) 755 login.public_db = profile.public_db 756 login.helpdesk = profile.helpdesk 757 login.backend_profile = profile.name 758 return login
759 #---------------------------- 760 # event handlers 761 #----------------------------
762 - def OnHelp(self, event):
763 praxis = gmPraxis.gmCurrentPraxisBranch() 764 wx.MessageBox(_( 765 u"""Unable to connect to the database ? 766 767 "PostgreSQL: FATAL: password authentication failed ..." 768 769 The default user name and password are {any-doc, any-doc} 770 for the public and any new GNUmed databases. 771 772 "... could not connect to server ..." 773 774 Mostly this is a case of new users who did not yet install 775 or configure a PostgreSQL server and/or a GNUmed database 776 of their own, which you must do before you can connect to 777 anything other than the public demonstration database, see 778 779 http://wiki.gnumed.de/bin/view/Gnumed/GmManualServerInstall 780 781 For assistance on using GNUmed please consult the wiki: 782 783 http://wiki.gnumed.de/bin/view/Gnumed/GnumedManual 784 785 For more help than the above, please contact: 786 787 GNUmed Development List <gnumed-bugs@gnu.org> 788 789 For local assistance please contact: 790 791 %s""") % praxis.helpdesk, 792 caption = _('HELP for GNUmed main login screen'))
793 794 #----------------------------
795 - def __on_login_button_pressed(self, event):
796 797 root_logger = logging.getLogger() 798 if self._CHBOX_debug.GetValue(): 799 _log.info('debug mode enabled') 800 _cfg.set_option(option = 'debug', value = True) 801 root_logger.setLevel(logging.DEBUG) 802 else: 803 _log.info('debug mode disabled') 804 _cfg.set_option(option = 'debug', value = False) 805 if _cfg.get(option = '--quiet', source_order = [('cli', 'return')]): 806 root_logger.setLevel(logging.ERROR) 807 else: 808 root_logger.setLevel(logging.WARNING) 809 810 if self._CHBOX_slave.GetValue(): 811 _log.info('slave mode enabled') 812 _cfg.set_option(option = 'slave', value = True) 813 else: 814 _log.info('slave mode disabled') 815 _cfg.set_option(option = 'slave', value = False) 816 817 self.backend_profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('latin1').strip()] 818 # self.user = self._CBOX_user.GetValue().strip() 819 # self.password = self.GetPassword() 820 self.cancelled = False 821 self.parent.Close()
822 #----------------------------
823 - def OnCancel(self, event):
824 self.cancelled = True 825 self.parent.Close()
826 827 #================================================================ 828 # main 829 #---------------------------------------------------------------- 830 if __name__ == "__main__": 831 832 if len(sys.argv) < 2: 833 sys.exit() 834 835 if sys.argv[1] != 'test': 836 sys.exit() 837 838 # we don't have tests yet 839 sys.exit() 840 841 from Gnumed.pycommon import gmI18N 842 843 logging.basicConfig(level = logging.DEBUG) 844 845 gmI18N.activate_locale() 846 gmI18N.install_domain(domain='gnumed') 847 #----------------------------------------------- 848 #-----------------------------------------------
849 - def test():
850 app = wx.PyWidgetTester(size = (300,400)) 851 #show the login panel in a main window 852 # app.SetWidget(cLoginPanel, -1) 853 #and pop the login dialog up modally 854 dlg = cLoginDialog(None, -1) #, png_bitmap = 'bitmaps/gnumedlogo.png') 855 dlg.ShowModal() 856 #demonstration how to access the login dialog values 857 lp = dlg.panel.GetLoginInfo() 858 if lp is None: 859 wx.MessageBox(_("Dialog was cancelled by user")) 860 else: 861 wx.MessageBox(_("You tried to log in as [%s] with password [%s].\nHost:%s, DB: %s, Port: %s") % (lp.GetUser(),lp.GetPassword(),lp.GetHost(),lp.GetDatabase(),lp.GetPort())) 862 dlg.Destroy()
863 # app.MainLoop() 864 865 #================================================================ 866