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

Source Code for Module Gnumed.wxpython.gmAuthWidgets

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