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 exc = gmPG2.make_pg_exception_fields_unicode(exc) 196 _log.error("login attempt failed: %s", 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.u_pgerror 207 # ) % gmPG2.extract_msg_from_pg_exception(e) 208 msg = regex.sub(r'password=[^\s]+', 'password=%s' % gmTools.u_replacement_character, msg) 209 gmGuiHelpers.gm_show_error ( 210 msg, 211 _('Connecting to backend') 212 ) 213 del exc 214 continue 215 216 conn.close() 217 218 # connect was successful 219 gmPG2.set_default_login(login = login) 220 gmPG2.set_default_client_encoding(encoding = dlg.panel.backend_profile.encoding) 221 222 seems_bootstrapped = gmPG2.schema_exists(schema = 'gm') 223 if not seems_bootstrapped: 224 _log.error('schema [gm] does not exist - database not bootstrapped ?') 225 msg = _( 226 'The database you connected to does not seem\n' 227 'to have been boostrapped properly.\n' 228 '\n' 229 'Make sure you have run the GNUmed database\n' 230 'bootstrapper tool to create a new database.\n' 231 '\n' 232 'Further help can be found on the website at\n' 233 '\n' 234 ' http://wiki.gnumed.de\n' 235 '\n' 236 'or on the GNUmed mailing list.' 237 ) 238 gmGuiHelpers.gm_show_error(msg, _('Verifying database')) 239 connected = False 240 break 241 242 compatible = gmPG2.database_schema_compatible(version = expected_version) 243 if compatible or not require_version: 244 dlg.panel.save_state() 245 246 if not compatible: 247 connected_db_version = gmPG2.get_schema_version() 248 msg = msg_generic % ( 249 client_version, 250 connected_db_version, 251 expected_version, 252 gmTools.coalesce(login.host, '<localhost>'), 253 login.database, 254 login.user 255 ) 256 if require_version: 257 gmGuiHelpers.gm_show_error(msg + msg_fail, _('Verifying database version')) 258 connected = False 259 continue 260 gmGuiHelpers.gm_show_info(msg + msg_override, _('Verifying database version')) 261 262 # FIXME: make configurable 263 max_skew = 1 # minutes 264 if _cfg.get(option = 'debug'): 265 max_skew = 10 266 if not gmPG2.sanity_check_time_skew(tolerance = (max_skew * 60)): 267 if _cfg.get(option = 'debug'): 268 gmGuiHelpers.gm_show_warning(msg_time_skew_warn % max_skew, _('Verifying database settings')) 269 else: 270 gmGuiHelpers.gm_show_error(msg_time_skew_fail % max_skew, _('Verifying database settings')) 271 connected = False 272 continue 273 274 sanity_level, message = gmPG2.sanity_check_database_settings() 275 if sanity_level != 0: 276 gmGuiHelpers.gm_show_error((msg_insanity % message), _('Verifying database settings')) 277 if sanity_level == 2: 278 connected = False 279 continue 280 281 gmExceptionHandlingWidgets.set_is_public_database(login.public_db) 282 gmExceptionHandlingWidgets.set_helpdesk(login.helpdesk) 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.Destroy() 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 login = gmPG2.get_default_login() 319 dsn = gmPG2.make_psycopg2_dsn ( 320 database = login.database, 321 host = login.host, 322 port = login.port, 323 user = dbo_account, 324 password = dbo_password 325 ) 326 try: 327 conn = gmPG2.get_connection ( 328 dsn = dsn, 329 readonly = False, 330 verbose = True, 331 pooled = False 332 ) 333 except: 334 _log.exception('cannot connect') 335 gmGuiHelpers.gm_show_error ( 336 aMessage = _('Cannot connect as the GNUmed database owner <%s>.') % dbo_account, 337 aTitle = procedure 338 ) 339 gmPG2.log_database_access(action = 'failed to connect as database owner for [%s]' % procedure) 340 return None 341 342 return conn
343 344 #================================================================
345 -def change_gmdbowner_password():
346 347 title = _('Changing GNUmed database owner password') 348 349 dbo_account = wx.GetTextFromUser ( 350 message = _("Enter the account name of the GNUmed database owner:"), 351 caption = title, 352 default_value = '' 353 ) 354 355 if dbo_account.strip() == '': 356 return False 357 358 dbo_conn = get_dbowner_connection ( 359 procedure = title, 360 dbo_account = dbo_account 361 ) 362 if dbo_conn is None: 363 return False 364 365 dbo_pwd_new_1 = wx.GetPasswordFromUser ( 366 message = _("Enter the NEW password for the GNUmed database owner:"), 367 caption = title 368 ) 369 if dbo_pwd_new_1.strip() == '': 370 return False 371 372 gmLog2.add_word2hide(dbo_pwd_new_1) 373 374 dbo_pwd_new_2 = wx.GetPasswordFromUser ( 375 message = _("""Enter the NEW password for the GNUmed database owner, again. 376 377 (This will protect you from typos.) 378 """), 379 caption = title 380 ) 381 if dbo_pwd_new_2.strip() == '': 382 return False 383 384 if dbo_pwd_new_1 != dbo_pwd_new_2: 385 return False 386 387 # pwd2 == pwd1 at this point so no need to hide (again) 388 389 """ On Mon, Mar 13, 2017 at 12:19:22PM -0400, Tom Lane wrote: 390 > Date: Mon, 13 Mar 2017 12:19:22 -0400 391 > From: Tom Lane <tgl@sss.pgh.pa.us> 392 > To: Adrian Klaver <adrian.klaver@aklaver.com> 393 > cc: Schmid Andreas <Andreas.Schmid@bd.so.ch>, 394 > "'pgsql-general@postgresql.org'" <pgsql-general@postgresql.org> 395 > Subject: Re: [GENERAL] createuser: How to specify a database to connect to 396 > 397 > Adrian Klaver <adrian.klaver@aklaver.com> writes: 398 > > On 03/13/2017 08:52 AM, Tom Lane wrote: 399 > >> If by "history" you're worried about the server-side statement log, this 400 > >> is merest fantasy: the createuser program is not magic, it just constructs 401 > >> and sends a CREATE USER command for you. You'd actually be more secure 402 > >> using psql, where (if you're superuser) you could shut off log_statement 403 > >> for your session first. 404 > 405 > > There is a difference though: 406 > 407 > > psql> CREATE USER: 408 > 409 > > postgres-2017-03-13 09:03:27.147 PDT-0LOG: statement: create user 410 > > dummy_user with login password '1234'; 411 > 412 > Well, what you're supposed to do is 413 > 414 > postgres=# create user dummy_user; 415 > postgres=# \password dummy_user 416 > Enter new password: 417 > Enter it again: 418 > postgres=# 419 > 420 > which will result in sending something like 421 > 422 > ALTER USER dummy_user PASSWORD 'md5c5e9567bc40082671d02c654260e0e09' 423 > 424 > You can additionally protect that by wrapping it into one transaction 425 > (if you have a setup where the momentary existence of the role without a 426 > password would be problematic) and/or shutting off logging beforehand. 427 """ 428 429 # this REALLY should be prefixed with md5 and the md5sum sent rather than the pwd 430 cmd = """ALTER ROLE "%s" ENCRYPTED PASSWORD '%s';""" % ( 431 dbo_account, 432 dbo_pwd_new_2 433 ) 434 gmPG2.run_rw_queries(link_obj = dbo_conn, queries = [{'cmd': cmd}], end_tx = True) 435 436 return True
437 438 #================================================================
439 -class cBackendProfile:
440 pass
441 442 #================================================================
443 -class cLoginDialog(wx.Dialog):
444 """cLoginDialog - window holding cLoginPanel""" 445
446 - def __init__(self, parent, id, title = _("Welcome to"), client_version = '*** unknown ***'):
447 wx.Dialog.__init__(self, parent, id, title) 448 self.panel = cLoginPanel(self, -1, isDialog=1, client_version = client_version) 449 self.Fit() # needed for Windoze. 450 self.Centre() 451 452 self.SetIcon(gmTools.get_icon(wx = wx))
453 454 #================================================================
455 -class cLoginPanel(wx.Panel):
456 """GUI panel class that interactively gets Postgres login parameters. 457 458 It features combo boxes which "remember" any number of 459 previously entered settings. 460 """
461 - def __init__(self, parent, id, 462 pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL, 463 isDialog = 0, client_version = '*** unknown ***'):
464 """Create login panel. 465 466 isDialog: if this panel is the main panel of a dialog, the panel will 467 resize the dialog automatically to display everything neatly 468 if isDialog is set to True 469 """ 470 wx.Panel.__init__(self, parent, id, pos, size, style) 471 self.parent = parent 472 473 #True if dialog was cancelled by user 474 #if the dialog is closed manually, login should be cancelled 475 self.cancelled = True 476 477 # True if this panel is displayed within a dialog (will resize the dialog automatically then) 478 self.isDialog = isDialog 479 480 self.topsizer = wx.BoxSizer(wx.VERTICAL) 481 482 # find bitmap 483 paths = gmTools.gmPaths(app_name = 'gnumed', wx = wx) 484 bitmap = os.path.join(paths.system_app_data_dir, 'bitmaps', 'gnumedlogo.png') 485 try: 486 png = wx.Image(bitmap, wx.BITMAP_TYPE_PNG).ConvertToBitmap() 487 bmp = wx.StaticBitmap(self, -1, png, wx.Point(10, 10), wx.Size(png.GetWidth(), png.GetHeight())) 488 self.topsizer.Add ( 489 bmp, 490 proportion = 0, 491 flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 492 border = 10 493 ) 494 except: 495 self.topsizer.Add ( 496 wx.StaticText ( 497 self, 498 -1, 499 label = _("Cannot find image") + bitmap, 500 style = wx.ALIGN_CENTRE 501 ), 502 proportion = 0, 503 flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 504 border = 10 505 ) 506 507 paramsbox_caption = _('Workplace "%s" (version %s)') % (gmPraxis.gmCurrentPraxisBranch().active_workplace, client_version) 508 509 # FIXME: why doesn't this align in the centre ? 510 self.paramsbox = wx.StaticBox( self, -1, paramsbox_caption, style = wx.ALIGN_CENTRE_HORIZONTAL) 511 self.paramsboxsizer = wx.StaticBoxSizer( self.paramsbox, wx.VERTICAL ) 512 self.paramsbox.SetForegroundColour(wx.Colour(35, 35, 142)) 513 self.paramsbox.SetFont(wx.Font( 514 pointSize = 12, 515 family = wx.SWISS, 516 style = wx.NORMAL, 517 weight = wx.BOLD, 518 underline = False 519 )) 520 self.pboxgrid = wx.FlexGridSizer(5, 2, 5, 5) 521 self.pboxgrid.AddGrowableCol(1) 522 523 # PROFILE COMBO 524 label = wx.StaticText( self, -1, _('Log into'), wx.DefaultPosition, wx.DefaultSize, 0) 525 label.SetForegroundColour(wx.Colour(35, 35, 142)) 526 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 527 self.__backend_profiles = self.__get_backend_profiles() 528 self._CBOX_profile = wx.ComboBox ( 529 self, 530 -1, 531 list(self.__backend_profiles.keys())[0], 532 wx.DefaultPosition, 533 size = wx.Size(550,-1), 534 choices = list(self.__backend_profiles.keys()), 535 style = wx.CB_READONLY 536 ) 537 self.pboxgrid.Add (self._CBOX_profile, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 538 539 # USER NAME COMBO 540 label = wx.StaticText( self, -1, _("Username"), wx.DefaultPosition, wx.DefaultSize, 0 ) 541 label.SetForegroundColour(wx.Colour(35, 35, 142)) 542 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 543 self.__previously_used_accounts = self.__get_previously_used_accounts() 544 self._CBOX_user = wx.ComboBox ( 545 self, 546 -1, 547 self.__previously_used_accounts[0], 548 wx.DefaultPosition, 549 wx.Size(150,-1), 550 self.__previously_used_accounts, 551 wx.CB_DROPDOWN 552 ) 553 self.pboxgrid.Add( self._CBOX_user, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 554 555 #PASSWORD TEXT ENTRY 556 label = wx.StaticText( self, -1, _("Password"), 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.pwdentry = wx.TextCtrl( self, 1, '', wx.DefaultPosition, wx.Size(80,-1), wx.TE_PASSWORD ) 560 # set focus on password entry 561 self.pwdentry.SetFocus() 562 self.pboxgrid.Add( self.pwdentry, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 563 564 # --debug checkbox 565 label = wx.StaticText(self, -1, _('Options'), wx.DefaultPosition, wx.DefaultSize, 0) 566 label.SetForegroundColour(wx.Colour(35, 35, 142)) 567 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 568 self._CHBOX_debug = wx.CheckBox(self, -1, _('&Debug mode')) 569 self._CHBOX_debug.SetToolTip(_('Check this to run GNUmed client in debugging mode.')) 570 self.pboxgrid.Add(self._CHBOX_debug, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 571 572 # --slave checkbox 573 label = wx.StaticText(self, -1, '', wx.DefaultPosition, wx.DefaultSize, 0) 574 label.SetForegroundColour(wx.Colour(35, 35, 142)) 575 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 576 self._CHBOX_slave = wx.CheckBox(self, -1, _('Enable &remote control')) 577 self._CHBOX_slave.SetToolTip(_('Check this to run GNUmed client in slave mode for remote control.')) 578 self.pboxgrid.Add(self._CHBOX_slave, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 579 580 #---------------------------------------------------------------------- 581 #new button code inserted rterry 06Sept02 582 #button order re-arraged to make it consistant with usual dialog format 583 #in most operating systems ie btns ok and cancel are standard and 584 #in that order 585 #ie Order is now help, ok and cancel 586 #The order of creation is the tab order 587 #login-ok button automatically is the default when tabbing (or <enter>) 588 #from password 589 #this eliminates the heavy border when you use the default 590 #?is the default word needed for any other reason? 591 #---------------------------------------------------------------------- 592 self.button_gridsizer = wx.GridSizer(1,3,0,0) 593 #--------------------- 594 #1:create login ok button 595 #--------------------- 596 ID_BUTTON_LOGIN = wx.NewId() 597 button_login_ok = wx.Button(self, ID_BUTTON_LOGIN, _("&Ok"), wx.DefaultPosition, wx.DefaultSize, 0 ) 598 button_login_ok.SetToolTip(wx.ToolTip(_("Proceed with login.")) ) 599 button_login_ok.SetDefault() 600 601 #--------------------- 602 #2:create cancel button 603 #--------------------- 604 ID_BUTTON_CANCEL = wx.NewId() 605 button_cancel = wx.Button(self, ID_BUTTON_CANCEL, _("&Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) 606 button_cancel.SetToolTip(wx.ToolTip(_("Cancel Login.")) ) 607 #--------------------- 608 #3:create Help button 609 #--------------------- 610 ID_BUTTON_HELP = wx.NewId() 611 button_help = wx.Button(self, ID_BUTTON_HELP, _("&Help"), wx.DefaultPosition, wx.DefaultSize, 0 ) 612 button_help.SetToolTip(wx.ToolTip(_("Help for login screen"))) 613 #---------------------------- 614 #Add buttons to the gridsizer 615 #---------------------------- 616 self.button_gridsizer.Add (button_help,0,wx.EXPAND|wx.ALL,5) 617 self.button_gridsizer.Add (button_login_ok,0,wx.EXPAND|wx.ALL,5) 618 self.button_gridsizer.Add (button_cancel,0,wx.EXPAND|wx.ALL,5) 619 620 self.paramsboxsizer.Add(self.pboxgrid, 1, wx.GROW|wx.ALL, 10) 621 self.topsizer.Add(self.paramsboxsizer, 1, wx.GROW|wx.ALL, 10) 622 self.topsizer.Add( self.button_gridsizer, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 623 624 self.__load_state() 625 626 self.SetAutoLayout(True) 627 self.SetSizer( self.topsizer) 628 self.topsizer.Fit( self ) 629 if self.isDialog: 630 self.topsizer.SetSizeHints(parent) 631 632 button_help.Bind(wx.EVT_BUTTON, self.OnHelp) 633 button_login_ok.Bind(wx.EVT_BUTTON, self.__on_login_button_pressed) 634 button_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
635 #wx.EVT_BUTTON(self, ID_BUTTON_HELP, self.OnHelp) 636 #wx.EVT_BUTTON(self, ID_BUTTON_LOGIN, self.__on_login_button_pressed) 637 #wx.EVT_BUTTON(self, ID_BUTTON_CANCEL, self.OnCancel) 638 639 #---------------------------------------------------------- 640 # internal helper methods 641 #----------------------------------------------------------
643 644 accounts = gmTools.coalesce ( 645 _cfg.get ( 646 group = 'backend', 647 option = 'logins', 648 source_order = [ 649 ('explicit', 'extend'), 650 ('user', 'extend'), 651 ('workbase', 'extend') 652 ] 653 ), 654 ['any-doc'] 655 ) 656 # FIXME: make unique 657 658 return accounts
659 #----------------------------------------------------
660 - def __get_backend_profiles(self):
661 """Get server profiles from the configuration files. 662 663 1) from system-wide file 664 2) from user file 665 666 Profiles in the user file which have the same name 667 as a profile in the system file will override the 668 system file. 669 """ 670 # find active profiles 671 src_order = [ 672 ('explicit', 'extend'), 673 ('system', 'extend'), 674 ('user', 'extend'), 675 ('workbase', 'extend') 676 ] 677 678 profile_names = gmTools.coalesce ( 679 _cfg.get(group = 'backend', option = 'profiles', source_order = src_order), 680 [] 681 ) 682 683 # find data for active profiles 684 src_order = [ 685 ('explicit', 'return'), 686 ('workbase', 'return'), 687 ('user', 'return'), 688 ('system', 'return') 689 ] 690 691 profiles = {} 692 693 for profile_name in profile_names: 694 # FIXME: once the profile has been found always use the corresponding source ! 695 # FIXME: maybe not or else we cannot override parts of the profile 696 profile = cBackendProfile() 697 profile_section = 'profile %s' % profile_name 698 699 profile.name = profile_name 700 profile.host = gmTools.coalesce(_cfg.get(profile_section, 'host', src_order), '').strip() 701 port = gmTools.coalesce(_cfg.get(profile_section, 'port', src_order), 5432) 702 try: 703 profile.port = int(port) 704 if profile.port < 1024: 705 raise ValueError('refusing to use priviledged port (< 1024)') 706 except ValueError: 707 _log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name) 708 continue 709 profile.database = gmTools.coalesce(_cfg.get(profile_section, 'database', src_order), '').strip() 710 if profile.database == '': 711 _log.warning('database name not specified, skipping profile [%s]', profile_name) 712 continue 713 profile.encoding = gmTools.coalesce(_cfg.get(profile_section, 'encoding', src_order), 'UTF8') 714 profile.public_db = bool(_cfg.get(profile_section, 'public/open access', src_order)) 715 profile.helpdesk = _cfg.get(profile_section, 'help desk', src_order) 716 717 label = '%s (%s@%s)' % (profile_name, profile.database, profile.host) 718 profiles[label] = profile 719 720 # sort out profiles with incompatible database versions if not --debug 721 # NOTE: this essentially hardcodes the database name in production ... 722 if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')): 723 profiles2remove = [] 724 for label in profiles: 725 if profiles[label].database != current_db_name: 726 profiles2remove.append(label) 727 for label in profiles2remove: 728 del profiles[label] 729 730 if len(profiles) == 0: 731 host = 'publicdb.gnumed.de' 732 label = 'public GNUmed database (%s@%s)' % (current_db_name, host) 733 profiles[label] = cBackendProfile() 734 profiles[label].name = label 735 profiles[label].host = host 736 profiles[label].port = 5432 737 profiles[label].database = current_db_name 738 profiles[label].encoding = 'UTF8' 739 profiles[label].public_db = True 740 profiles[label].helpdesk = 'http://wiki.gnumed.de' 741 742 return profiles
743 #----------------------------------------------------------
744 - def __load_state(self):
745 746 src_order = [ 747 ('explicit', 'return'), 748 ('user', 'return'), 749 ] 750 751 self._CBOX_user.SetValue ( 752 gmTools.coalesce ( 753 _cfg.get('preferences', 'login', src_order), 754 self.__previously_used_accounts[0] 755 ) 756 ) 757 758 last_used_profile_label = _cfg.get('preferences', 'profile', src_order) 759 if last_used_profile_label in self.__backend_profiles.keys(): 760 self._CBOX_profile.SetValue(last_used_profile_label) 761 else: 762 self._CBOX_profile.SetValue(list(self.__backend_profiles.keys())[0]) 763 764 self._CHBOX_debug.SetValue(_cfg.get(option = 'debug')) 765 self._CHBOX_slave.SetValue(_cfg.get(option = 'slave'))
766 #----------------------------------------------------
767 - def save_state(self):
768 """Save parameter settings to standard configuration file""" 769 prefs_name = _cfg.get(option = 'user_preferences_file') 770 _log.debug('saving login preferences in [%s]', prefs_name) 771 772 gmCfg2.set_option_in_INI_file ( 773 filename = prefs_name, 774 group = 'preferences', 775 option = 'login', 776 value = self._CBOX_user.GetValue() 777 ) 778 779 gmCfg2.set_option_in_INI_file ( 780 filename = prefs_name, 781 group = 'preferences', 782 option = 'profile', 783 value = self._CBOX_profile.GetValue() 784 )
785 ############################################################################# 786 # Retrieve current settings from user interface widgets 787 #############################################################################
788 - def GetLoginInfo(self):
789 """convenience function for compatibility with gmLoginInfo.LoginInfo""" 790 if self.cancelled: 791 return None 792 793 # FIXME: do not assume conf file is latin1 ! 794 profile = self.__backend_profiles[self._CBOX_profile.GetValue().strip()] 795 _log.info('backend profile "%s" selected', profile.name) 796 _log.info(' details: <%s> on %s@%s:%s (%s, %s)', 797 self._CBOX_user.GetValue(), 798 profile.database, 799 profile.host, 800 profile.port, 801 profile.encoding, 802 gmTools.bool2subst(profile.public_db, 'public', 'private') 803 ) 804 _log.info(' helpdesk: "%s"', profile.helpdesk) 805 login = gmLoginInfo.LoginInfo ( 806 user = self._CBOX_user.GetValue(), 807 password = self.pwdentry.GetValue(), 808 host = profile.host, 809 database = profile.database, 810 port = profile.port 811 ) 812 login.public_db = profile.public_db 813 login.helpdesk = profile.helpdesk 814 login.backend_profile = profile.name 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.Destroy()
920 # app.MainLoop() 921 922 #================================================================ 923