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