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