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 DSN works 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 conn = gmPG2.get_connection(verbose = True, connection_name = 'GNUmed-[DbListenerThread]', pooled = False) 279 listener = gmBackendListener.gmBackendListener(conn = conn) 280 break 281 282 dlg.DestroyLater() 283 284 return connected
285 286 #================================================================
287 -def get_dbowner_connection(procedure=None, dbo_password=None, dbo_account='gm-dbo'):
288 if procedure is None: 289 procedure = _('<restricted procedure>') 290 291 # 1) get password for gm-dbo 292 if dbo_password is None: 293 dbo_password = wx.GetPasswordFromUser ( 294 message = _(""" 295 [%s] 296 297 This is a restricted procedure. We need the 298 current password for the GNUmed database owner. 299 300 Please enter the current password for <%s>:""") % ( 301 procedure, 302 dbo_account 303 ), 304 caption = procedure 305 ) 306 if dbo_password == '': 307 return None 308 309 gmLog2.add_word2hide(dbo_password) 310 311 # 2) connect as gm-dbo 312 pool = gmConnectionPool.gmConnectionPool() 313 conn = None 314 try: 315 conn = pool.get_dbowner_connection ( 316 readonly = False, 317 verbose = True, 318 dbo_password = dbo_password, 319 dbo_account = dbo_account 320 ) 321 except Exception: 322 _log.exception('cannot connect') 323 gmGuiHelpers.gm_show_error ( 324 aMessage = _('Cannot connect as the GNUmed database owner <%s>.') % dbo_account, 325 aTitle = procedure 326 ) 327 gmPG2.log_database_access(action = 'failed to connect as database owner for [%s]' % procedure) 328 return conn
329 330 #================================================================
331 -def change_gmdbowner_password():
332 333 title = _('Changing GNUmed database owner password') 334 335 dbo_account = wx.GetTextFromUser ( 336 message = _("Enter the account name of the GNUmed database owner:"), 337 caption = title, 338 default_value = '' 339 ) 340 341 if dbo_account.strip() == '': 342 return False 343 344 dbo_conn = get_dbowner_connection ( 345 procedure = title, 346 dbo_account = dbo_account 347 ) 348 if dbo_conn is None: 349 return False 350 351 dbo_pwd_new_1 = wx.GetPasswordFromUser ( 352 message = _("Enter the NEW password for the GNUmed database owner:"), 353 caption = title 354 ) 355 if dbo_pwd_new_1.strip() == '': 356 return False 357 358 gmLog2.add_word2hide(dbo_pwd_new_1) 359 360 dbo_pwd_new_2 = wx.GetPasswordFromUser ( 361 message = _("""Enter the NEW password for the GNUmed database owner, again. 362 363 (This will protect you from typos.) 364 """), 365 caption = title 366 ) 367 if dbo_pwd_new_2.strip() == '': 368 return False 369 370 if dbo_pwd_new_1 != dbo_pwd_new_2: 371 return False 372 373 # pwd2 == pwd1 at this point so no need to hide (again) 374 375 """ On Mon, Mar 13, 2017 at 12:19:22PM -0400, Tom Lane wrote: 376 > Date: Mon, 13 Mar 2017 12:19:22 -0400 377 > From: Tom Lane <tgl@sss.pgh.pa.us> 378 > To: Adrian Klaver <adrian.klaver@aklaver.com> 379 > cc: Schmid Andreas <Andreas.Schmid@bd.so.ch>, 380 > "'pgsql-general@postgresql.org'" <pgsql-general@postgresql.org> 381 > Subject: Re: [GENERAL] createuser: How to specify a database to connect to 382 > 383 > Adrian Klaver <adrian.klaver@aklaver.com> writes: 384 > > On 03/13/2017 08:52 AM, Tom Lane wrote: 385 > >> If by "history" you're worried about the server-side statement log, this 386 > >> is merest fantasy: the createuser program is not magic, it just constructs 387 > >> and sends a CREATE USER command for you. You'd actually be more secure 388 > >> using psql, where (if you're superuser) you could shut off log_statement 389 > >> for your session first. 390 > 391 > > There is a difference though: 392 > 393 > > psql> CREATE USER: 394 > 395 > > postgres-2017-03-13 09:03:27.147 PDT-0LOG: statement: create user 396 > > dummy_user with login password '1234'; 397 > 398 > Well, what you're supposed to do is 399 > 400 > postgres=# create user dummy_user; 401 > postgres=# \password dummy_user 402 > Enter new password: 403 > Enter it again: 404 > postgres=# 405 > 406 > which will result in sending something like 407 > 408 > ALTER USER dummy_user PASSWORD 'md5c5e9567bc40082671d02c654260e0e09' 409 > 410 > You can additionally protect that by wrapping it into one transaction 411 > (if you have a setup where the momentary existence of the role without a 412 > password would be problematic) and/or shutting off logging beforehand. 413 """ 414 415 # this REALLY should be prefixed with md5 and the md5sum sent rather than the pwd 416 cmd = """ALTER ROLE "%s" ENCRYPTED PASSWORD '%s';""" % ( 417 dbo_account, 418 dbo_pwd_new_2 419 ) 420 gmPG2.run_rw_queries(link_obj = dbo_conn, queries = [{'cmd': cmd}], end_tx = True) 421 return True
422 423 #================================================================
424 -class cBackendProfile:
425 pass
426 427 #================================================================
428 -class cLoginDialog(wx.Dialog):
429 """cLoginDialog - window holding cLoginPanel""" 430
431 - def __init__(self, parent, id, title = _("Welcome to"), client_version = '*** unknown ***'):
432 wx.Dialog.__init__(self, parent, id, title) 433 self.panel = cLoginPanel(self, -1, isDialog=1, client_version = client_version) 434 self.Fit() # needed for Windoze. 435 self.Centre() 436 437 self.SetIcon(gmTools.get_icon(wx = wx))
438 439 #================================================================
440 -class cLoginPanel(wx.Panel):
441 """GUI panel class that interactively gets Postgres login parameters. 442 443 It features combo boxes which "remember" any number of 444 previously entered settings. 445 """
446 - def __init__(self, parent, id, 447 pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.TAB_TRAVERSAL, 448 isDialog = 0, client_version = '*** unknown ***'):
449 """Create login panel. 450 451 isDialog: if this panel is the main panel of a dialog, the panel will 452 resize the dialog automatically to display everything neatly 453 if isDialog is set to True 454 """ 455 wx.Panel.__init__(self, parent, id, pos, size, style) 456 self.parent = parent 457 458 #True if dialog was cancelled by user 459 #if the dialog is closed manually, login should be cancelled 460 self.cancelled = True 461 462 # True if this panel is displayed within a dialog (will resize the dialog automatically then) 463 self.isDialog = isDialog 464 465 self.topsizer = wx.BoxSizer(wx.VERTICAL) 466 467 # find bitmap 468 paths = gmTools.gmPaths(app_name = 'gnumed', wx = wx) 469 bitmap = os.path.join(paths.system_app_data_dir, 'bitmaps', 'gnumedlogo.png') 470 try: 471 png = wx.Image(bitmap, wx.BITMAP_TYPE_PNG).ConvertToBitmap() 472 bmp = wx.StaticBitmap(self, -1, png, wx.Point(10, 10), wx.Size(png.GetWidth(), png.GetHeight())) 473 self.topsizer.Add ( 474 bmp, 475 proportion = 0, 476 flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 477 border = 10 478 ) 479 except Exception: 480 self.topsizer.Add ( 481 wx.StaticText ( 482 self, 483 -1, 484 label = _("Cannot find image") + bitmap, 485 style = wx.ALIGN_CENTRE 486 ), 487 proportion = 0, 488 flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 489 border = 10 490 ) 491 492 paramsbox_caption = _('Workplace "%s" (version %s)') % (gmPraxis.gmCurrentPraxisBranch().active_workplace, client_version) 493 494 # FIXME: why doesn't this align in the centre ? 495 self.paramsbox = wx.StaticBox( self, -1, paramsbox_caption, style = wx.ALIGN_CENTRE_HORIZONTAL) 496 self.paramsboxsizer = wx.StaticBoxSizer( self.paramsbox, wx.VERTICAL ) 497 self.paramsbox.SetForegroundColour(wx.Colour(35, 35, 142)) 498 self.paramsbox.SetFont(wx.Font( 499 pointSize = 12, 500 family = wx.SWISS, 501 style = wx.NORMAL, 502 weight = wx.BOLD, 503 underline = False 504 )) 505 self.pboxgrid = wx.FlexGridSizer(5, 2, 5, 5) 506 self.pboxgrid.AddGrowableCol(1) 507 508 # PROFILE COMBO 509 label = wx.StaticText( self, -1, _('Log into'), wx.DefaultPosition, wx.DefaultSize, 0) 510 label.SetForegroundColour(wx.Colour(35, 35, 142)) 511 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 512 self.__backend_profiles = self.__get_backend_profiles() 513 self._CBOX_profile = wx.ComboBox ( 514 self, 515 -1, 516 list(self.__backend_profiles.keys())[0], 517 wx.DefaultPosition, 518 size = wx.Size(550,-1), 519 choices = list(self.__backend_profiles.keys()), 520 style = wx.CB_READONLY 521 ) 522 self.pboxgrid.Add (self._CBOX_profile, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 523 524 # USER NAME COMBO 525 label = wx.StaticText( self, -1, _("Username"), wx.DefaultPosition, wx.DefaultSize, 0 ) 526 label.SetForegroundColour(wx.Colour(35, 35, 142)) 527 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 528 self.__previously_used_accounts = self.__get_previously_used_accounts() 529 self._CBOX_user = wx.ComboBox ( 530 self, 531 -1, 532 self.__previously_used_accounts[0], 533 wx.DefaultPosition, 534 wx.Size(150,-1), 535 self.__previously_used_accounts, 536 wx.CB_DROPDOWN 537 ) 538 self.pboxgrid.Add( self._CBOX_user, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 539 540 #PASSWORD TEXT ENTRY 541 label = wx.StaticText( self, -1, _("Password"), wx.DefaultPosition, wx.DefaultSize, 0 ) 542 label.SetForegroundColour(wx.Colour(35, 35, 142)) 543 self.pboxgrid.Add( label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) 544 self.pwdentry = wx.TextCtrl( self, 1, '', wx.DefaultPosition, wx.Size(80,-1), wx.TE_PASSWORD ) 545 # set focus on password entry 546 self.pwdentry.SetFocus() 547 self.pboxgrid.Add( self.pwdentry, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 548 549 # --debug checkbox 550 label = wx.StaticText(self, -1, _('Options'), wx.DefaultPosition, wx.DefaultSize, 0) 551 label.SetForegroundColour(wx.Colour(35, 35, 142)) 552 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 553 self._CHBOX_debug = wx.CheckBox(self, -1, _('&Debug mode')) 554 self._CHBOX_debug.SetToolTip(_('Check this to run GNUmed client in debugging mode.')) 555 self.pboxgrid.Add(self._CHBOX_debug, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 556 557 # --slave checkbox 558 label = wx.StaticText(self, -1, '', wx.DefaultPosition, wx.DefaultSize, 0) 559 label.SetForegroundColour(wx.Colour(35, 35, 142)) 560 self.pboxgrid.Add(label, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) 561 self._CHBOX_slave = wx.CheckBox(self, -1, _('Enable &remote control')) 562 self._CHBOX_slave.SetToolTip(_('Check this to run GNUmed client in slave mode for remote control.')) 563 self.pboxgrid.Add(self._CHBOX_slave, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) 564 565 #---------------------------------------------------------------------- 566 #new button code inserted rterry 06Sept02 567 #button order re-arraged to make it consistant with usual dialog format 568 #in most operating systems ie btns ok and cancel are standard and 569 #in that order 570 #ie Order is now help, ok and cancel 571 #The order of creation is the tab order 572 #login-ok button automatically is the default when tabbing (or <enter>) 573 #from password 574 #this eliminates the heavy border when you use the default 575 #?is the default word needed for any other reason? 576 #---------------------------------------------------------------------- 577 self.button_gridsizer = wx.GridSizer(1,3,0,0) 578 #--------------------- 579 #1:create login ok button 580 #--------------------- 581 ID_BUTTON_LOGIN = wx.NewId() 582 button_login_ok = wx.Button(self, ID_BUTTON_LOGIN, _("&Ok"), wx.DefaultPosition, wx.DefaultSize, 0 ) 583 button_login_ok.SetToolTip(wx.ToolTip(_("Proceed with login.")) ) 584 button_login_ok.SetDefault() 585 586 #--------------------- 587 #2:create cancel button 588 #--------------------- 589 ID_BUTTON_CANCEL = wx.NewId() 590 button_cancel = wx.Button(self, ID_BUTTON_CANCEL, _("&Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) 591 button_cancel.SetToolTip(wx.ToolTip(_("Cancel Login.")) ) 592 #--------------------- 593 #3:create Help button 594 #--------------------- 595 ID_BUTTON_HELP = wx.NewId() 596 button_help = wx.Button(self, ID_BUTTON_HELP, _("&Help"), wx.DefaultPosition, wx.DefaultSize, 0 ) 597 button_help.SetToolTip(wx.ToolTip(_("Help for login screen"))) 598 #---------------------------- 599 #Add buttons to the gridsizer 600 #---------------------------- 601 self.button_gridsizer.Add (button_help,0,wx.EXPAND|wx.ALL,5) 602 self.button_gridsizer.Add (button_login_ok,0,wx.EXPAND|wx.ALL,5) 603 self.button_gridsizer.Add (button_cancel,0,wx.EXPAND|wx.ALL,5) 604 605 self.paramsboxsizer.Add(self.pboxgrid, 1, wx.GROW|wx.ALL, 10) 606 self.topsizer.Add(self.paramsboxsizer, 1, wx.GROW|wx.ALL, 10) 607 self.topsizer.Add( self.button_gridsizer, 0, wx.GROW|wx.ALL, 5) 608 609 self.__load_state() 610 611 self.SetAutoLayout(True) 612 self.SetSizer( self.topsizer) 613 self.topsizer.Fit( self ) 614 if self.isDialog: 615 self.topsizer.SetSizeHints(parent) 616 617 button_help.Bind(wx.EVT_BUTTON, self.OnHelp) 618 button_login_ok.Bind(wx.EVT_BUTTON, self.__on_login_button_pressed) 619 button_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
620 #wx.EVT_BUTTON(self, ID_BUTTON_HELP, self.OnHelp) 621 #wx.EVT_BUTTON(self, ID_BUTTON_LOGIN, self.__on_login_button_pressed) 622 #wx.EVT_BUTTON(self, ID_BUTTON_CANCEL, self.OnCancel) 623 624 #---------------------------------------------------------- 625 # internal helper methods 626 #----------------------------------------------------------
628 629 accounts = gmTools.coalesce ( 630 _cfg.get ( 631 group = 'backend', 632 option = 'logins', 633 source_order = [ 634 ('explicit', 'extend'), 635 ('user', 'extend'), 636 ('workbase', 'extend') 637 ] 638 ), 639 ['any-doc'] 640 ) 641 # FIXME: make unique 642 643 return accounts
644 #----------------------------------------------------
645 - def __get_backend_profiles(self):
646 """Get server profiles from the configuration files. 647 648 1) from system-wide file 649 2) from user file 650 651 Profiles in the user file which have the same name 652 as a profile in the system file will override the 653 system file. 654 """ 655 # find active profiles 656 src_order = [ 657 ('explicit', 'extend'), 658 ('system', 'extend'), 659 ('user', 'extend'), 660 ('workbase', 'extend') 661 ] 662 663 profile_names = gmTools.coalesce ( 664 _cfg.get(group = 'backend', option = 'profiles', source_order = src_order), 665 [] 666 ) 667 668 # find data for active profiles 669 src_order = [ 670 ('explicit', 'return'), 671 ('workbase', 'return'), 672 ('user', 'return'), 673 ('system', 'return') 674 ] 675 676 profiles = {} 677 678 for profile_name in profile_names: 679 # FIXME: once the profile has been found always use the corresponding source ! 680 # FIXME: maybe not or else we cannot override parts of the profile 681 profile = cBackendProfile() 682 profile_section = 'profile %s' % profile_name 683 684 profile.name = profile_name 685 profile.host = gmTools.coalesce(_cfg.get(profile_section, 'host', src_order), '').strip() 686 port = gmTools.coalesce(_cfg.get(profile_section, 'port', src_order), 5432) 687 try: 688 profile.port = int(port) 689 if profile.port < 1024: 690 raise ValueError('refusing to use priviledged port (< 1024)') 691 except ValueError: 692 _log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name) 693 continue 694 profile.database = gmTools.coalesce(_cfg.get(profile_section, 'database', src_order), '').strip() 695 if profile.database == '': 696 _log.warning('database name not specified, skipping profile [%s]', profile_name) 697 continue 698 profile.encoding = gmTools.coalesce(_cfg.get(profile_section, 'encoding', src_order), 'UTF8') 699 profile.public_db = bool(_cfg.get(profile_section, 'public/open access', src_order)) 700 profile.helpdesk = _cfg.get(profile_section, 'help desk', src_order) 701 702 label = '%s (%s@%s)' % (profile_name, profile.database, profile.host) 703 profiles[label] = profile 704 705 # sort out profiles with incompatible database versions if not --debug 706 # NOTE: this essentially hardcodes the database name in production ... 707 if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')): 708 profiles2remove = [] 709 for label in profiles: 710 if profiles[label].database != current_db_name: 711 profiles2remove.append(label) 712 for label in profiles2remove: 713 del profiles[label] 714 715 if len(profiles) == 0: 716 host = 'publicdb.gnumed.de' 717 label = 'public GNUmed database (%s@%s)' % (current_db_name, host) 718 profiles[label] = cBackendProfile() 719 profiles[label].name = label 720 profiles[label].host = host 721 profiles[label].port = 5432 722 profiles[label].database = current_db_name 723 profiles[label].encoding = 'UTF8' 724 profiles[label].public_db = True 725 profiles[label].helpdesk = 'http://wiki.gnumed.de' 726 727 return profiles
728 #----------------------------------------------------------
729 - def __load_state(self):
730 731 src_order = [ 732 ('explicit', 'return'), 733 ('user', 'return'), 734 ] 735 736 self._CBOX_user.SetValue ( 737 gmTools.coalesce ( 738 _cfg.get('preferences', 'login', src_order), 739 self.__previously_used_accounts[0] 740 ) 741 ) 742 743 last_used_profile_label = _cfg.get('preferences', 'profile', src_order) 744 if last_used_profile_label in self.__backend_profiles.keys(): 745 self._CBOX_profile.SetValue(last_used_profile_label) 746 else: 747 self._CBOX_profile.SetValue(list(self.__backend_profiles.keys())[0]) 748 749 self._CHBOX_debug.SetValue(_cfg.get(option = 'debug')) 750 self._CHBOX_slave.SetValue(_cfg.get(option = 'slave'))
751 #----------------------------------------------------
752 - def save_state(self):
753 """Save parameter settings to standard configuration file""" 754 prefs_name = _cfg.get(option = 'user_preferences_file') 755 _log.debug('saving login preferences in [%s]', prefs_name) 756 757 gmCfg2.set_option_in_INI_file ( 758 filename = prefs_name, 759 group = 'preferences', 760 option = 'login', 761 value = self._CBOX_user.GetValue() 762 ) 763 764 gmCfg2.set_option_in_INI_file ( 765 filename = prefs_name, 766 group = 'preferences', 767 option = 'profile', 768 value = self._CBOX_profile.GetValue() 769 )
770 ############################################################################# 771 # Retrieve current settings from user interface widgets 772 #############################################################################
773 - def GetLoginInfo(self):
774 """convenience function for compatibility with gmLoginInfo.LoginInfo""" 775 if self.cancelled: 776 return None 777 778 # FIXME: do not assume conf file is latin1 ! 779 profile = self.__backend_profiles[self._CBOX_profile.GetValue().strip()] 780 _log.info('backend profile "%s" selected', profile.name) 781 _log.info(' details: <%s> on %s@%s:%s (%s, %s)', 782 self._CBOX_user.GetValue(), 783 profile.database, 784 profile.host, 785 profile.port, 786 profile.encoding, 787 gmTools.bool2subst(profile.public_db, 'public', 'private') 788 ) 789 _log.info(' helpdesk: "%s"', profile.helpdesk) 790 login = gmLoginInfo.LoginInfo ( 791 user = self._CBOX_user.GetValue(), 792 password = self.pwdentry.GetValue(), 793 host = profile.host, 794 database = profile.database, 795 port = profile.port 796 ) 797 _cfg.set_option ( 798 option = 'is_public_db', 799 value = profile.public_db 800 ) 801 _cfg.set_option ( 802 option = 'helpdesk', 803 value = profile.helpdesk 804 ) 805 _cfg.set_option ( 806 option = 'backend_profile', 807 value = profile.name 808 ) 809 return login
810 #---------------------------- 811 # event handlers 812 #----------------------------
813 - def OnHelp(self, event):
814 praxis = gmPraxis.gmCurrentPraxisBranch() 815 wx.MessageBox(_( 816 """Unable to connect to the database ? 817 818 "PostgreSQL: FATAL: password authentication failed ..." 819 820 The default user name and password are {any-doc, any-doc} 821 for the public and any new GNUmed databases. 822 823 "... could not connect to server ..." 824 825 Mostly this is a case of new users who did not yet install 826 or configure a PostgreSQL server and/or a GNUmed database 827 of their own, which you must do before you can connect to 828 anything other than the public demonstration database, see 829 830 http://wiki.gnumed.de/bin/view/Gnumed/GmManualServerInstall 831 832 For assistance on using GNUmed please consult the wiki: 833 834 http://wiki.gnumed.de/bin/view/Gnumed/GnumedManual 835 836 For more help than the above, please contact: 837 838 GNUmed Development List <gnumed-bugs@gnu.org> 839 840 For local assistance please contact: 841 842 %s""") % praxis.helpdesk, 843 caption = _('HELP for GNUmed main login screen'))
844 845 #----------------------------
846 - def __on_login_button_pressed(self, event):
847 848 root_logger = logging.getLogger() 849 if self._CHBOX_debug.GetValue(): 850 _log.info('debug mode enabled') 851 _cfg.set_option(option = 'debug', value = True) 852 root_logger.setLevel(logging.DEBUG) 853 else: 854 _log.info('debug mode disabled') 855 _cfg.set_option(option = 'debug', value = False) 856 if _cfg.get(option = '--quiet', source_order = [('cli', 'return')]): 857 root_logger.setLevel(logging.ERROR) 858 else: 859 root_logger.setLevel(logging.WARNING) 860 861 if self._CHBOX_slave.GetValue(): 862 _log.info('slave mode enabled') 863 _cfg.set_option(option = 'slave', value = True) 864 else: 865 _log.info('slave mode disabled') 866 _cfg.set_option(option = 'slave', value = False) 867 868 self.backend_profile = self.__backend_profiles[self._CBOX_profile.GetValue().strip()] 869 # self.user = self._CBOX_user.GetValue().strip() 870 # self.password = self.GetPassword() 871 self.cancelled = False 872 self.parent.Close()
873 #----------------------------
874 - def OnCancel(self, event):
875 self.cancelled = True 876 self.parent.Close()
877 878 #================================================================ 879 # main 880 #---------------------------------------------------------------- 881 if __name__ == "__main__": 882 883 if len(sys.argv) < 2: 884 sys.exit() 885 886 if sys.argv[1] != 'test': 887 sys.exit() 888 889 # we don't have tests yet 890 sys.exit() 891 892 from Gnumed.pycommon import gmI18N 893 894 logging.basicConfig(level = logging.DEBUG) 895 896 gmI18N.activate_locale() 897 gmI18N.install_domain(domain='gnumed') 898 #----------------------------------------------- 899 #-----------------------------------------------
900 - def test():
901 app = wx.PyWidgetTester(size = (300,400)) 902 #show the login panel in a main window 903 # app.SetWidget(cLoginPanel, -1) 904 #and pop the login dialog up modally 905 dlg = cLoginDialog(None, -1) #, png_bitmap = 'bitmaps/gnumedlogo.png') 906 dlg.ShowModal() 907 #demonstration how to access the login dialog values 908 lp = dlg.panel.GetLoginInfo() 909 if lp is None: 910 wx.MessageBox(_("Dialog was cancelled by user")) 911 else: 912 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())) 913 dlg.DestroyLater()
914 # app.MainLoop() 915 916 #================================================================ 917