From c048ebcfbbe60b2b6100c733a9d38673c2787c3d Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Mon, 25 Sep 2017 22:44:47 +0100 Subject: [PATCH] Add login dialog and username/password command line options --- gramps/cli/arghandler.py | 6 ++- gramps/cli/argparser.py | 10 +++++ gramps/cli/grampscli.py | 13 ++++--- gramps/gen/config.py | 2 - gramps/gen/const.py | 4 +- gramps/gen/db/base.py | 5 +++ gramps/gen/db/generic.py | 8 ++-- gramps/gui/configure.py | 8 ---- gramps/gui/dbloader.py | 56 ++++++++++++++++++++++++++- gramps/plugins/db/bsddb/write.py | 3 +- gramps/plugins/db/dbapi/dbapi.py | 2 +- gramps/plugins/db/dbapi/postgresql.py | 13 ++++--- gramps/plugins/db/dbapi/sqlite.py | 2 +- 13 files changed, 100 insertions(+), 32 deletions(-) diff --git a/gramps/cli/arghandler.py b/gramps/cli/arghandler.py index 1d718fa6b..daeffc431 100644 --- a/gramps/cli/arghandler.py +++ b/gramps/cli/arghandler.py @@ -180,6 +180,8 @@ class ArgHandler: self.imports = [] self.exports = [] self.removes = parser.removes + self.username = parser.username + self.password = parser.password self.open = self.__handle_open_option(parser.open, parser.create) self.sanitize_args(parser.imports, parser.exports) @@ -505,7 +507,7 @@ class ArgHandler: newdb.write_version(self.imp_db_path) try: - self.smgr.open_activate(self.imp_db_path) + self.smgr.open_activate(self.imp_db_path, self.username, self.password) msg = _("Created empty Family Tree successfully") print(msg, file=sys.stderr) except: @@ -532,7 +534,7 @@ class ArgHandler: # we load this file for use try: - self.smgr.open_activate(self.open) + self.smgr.open_activate(self.open, self.username, self.password) print(_("Opened successfully!"), file=sys.stderr) except: print(_("Error opening the file."), file=sys.stderr) diff --git a/gramps/cli/argparser.py b/gramps/cli/argparser.py index fcf755a92..82d398b05 100644 --- a/gramps/cli/argparser.py +++ b/gramps/cli/argparser.py @@ -62,6 +62,8 @@ Help options Application options -O, --open=FAMILY_TREE Open Family Tree + -U, --username=USERNAME Database username + -P, --password=PASSWORD Database password -C, --create=FAMILY_TREE Create on open if new Family Tree -i, --import=FILENAME Import file -e, --export=FILENAME Export file @@ -138,6 +140,8 @@ class ArgParser: The valid options are: -O, --open=FAMILY_TREE Open Family Tree + -U, --username=USERNAME Database username + -P, --password=PASSWORD Database password -C, --create=FAMILY_TREE Create on open if new Family Tree -i, --import=FILENAME Import file -e, --export=FILENAME Export file @@ -196,6 +200,8 @@ class ArgParser: self.open_gui = None self.open = None + self.username = None + self.password = None self.exports = [] self.actions = [] self.imports = [] @@ -279,6 +285,10 @@ class ArgParser: self.open = value elif option in ['-C', '--create']: self.create = value + elif option in ['-U', '--username']: + self.username = value + elif option in ['-P', '--password']: + self.password = value elif option in ['-i', '--import']: family_tree_format = None if (opt_ix < len(options) - 1 diff --git a/gramps/cli/grampscli.py b/gramps/cli/grampscli.py index 0caec8cb6..21f00f9fb 100644 --- a/gramps/cli/grampscli.py +++ b/gramps/cli/grampscli.py @@ -132,7 +132,7 @@ class CLIDbLoader: """ pass - def read_file(self, filename): + def read_file(self, filename, username, password): """ This method takes care of changing database, and loading the data. In 3.0 we only allow reading of real databases of filetype @@ -173,7 +173,8 @@ class CLIDbLoader: self._begin_progress() try: - self.dbstate.db.load(filename, self._pulse_progress, mode) + self.dbstate.db.load(filename, self._pulse_progress, mode, + username=username, password=password) except DbEnvironmentError as msg: self.dbstate.no_database() self._errordialog(_("Cannot open database"), str(msg)) @@ -240,11 +241,11 @@ class CLIManager: self._pmgr = BasePluginManager.get_instance() self.user = user - def open_activate(self, path): + def open_activate(self, path, username, password): """ Open and make a family tree active """ - self._read_recent_file(path) + self._read_recent_file(path, username, password) def _errordialog(self, title, errormessage): """ @@ -253,7 +254,7 @@ class CLIManager: print(_('ERROR: %s') % errormessage, file=sys.stderr) sys.exit(1) - def _read_recent_file(self, filename): + def _read_recent_file(self, filename, username, password): """ Called when a file needs to be loaded """ @@ -274,7 +275,7 @@ class CLIManager: "that the database is not in use.")) return - if self.db_loader.read_file(filename): + if self.db_loader.read_file(filename, username, password): # Attempt to figure out the database title path = os.path.join(filename, "name.txt") try: diff --git a/gramps/gen/config.py b/gramps/gen/config.py index 66024cf6c..7cbde8ea3 100644 --- a/gramps/gen/config.py +++ b/gramps/gen/config.py @@ -163,8 +163,6 @@ register('database.backup-path', USER_HOME) register('database.backup-on-exit', True) register('database.autobackup', 0) register('database.path', os.path.join(HOME_DIR, 'grampsdb')) -register('database.user', '') -register('database.password', '') register('database.host', '') register('database.port', '') diff --git a/gramps/gen/const.py b/gramps/gen/const.py index 8b5b95225..1a4942cd1 100644 --- a/gramps/gen/const.py +++ b/gramps/gen/const.py @@ -288,6 +288,8 @@ LONGOPTS = [ "oaf-ior-fd=", "oaf-private", "open=", + "username=", + "password=", "create=", "options=", "screen=", @@ -303,7 +305,7 @@ LONGOPTS = [ "quiet", ] -SHORTOPTS = "O:C:i:e:f:a:p:d:c:r:lLthuv?syq" +SHORTOPTS = "O:U:P:C:i:e:f:a:p:d:c:r:lLthuv?syq" GRAMPS_UUID = uuid.UUID('516cd010-5a41-470f-99f8-eb22f1098ad6') diff --git a/gramps/gen/db/base.py b/gramps/gen/db/base.py index d4940a757..13345f340 100644 --- a/gramps/gen/db/base.py +++ b/gramps/gen/db/base.py @@ -1427,6 +1427,11 @@ class DbReadBase: """ raise NotImplementedError + def requires_login(self): + """ + Returns True for backends that require a login dialog, else False. + """ + return False class DbWriteBase(DbReadBase): """ diff --git a/gramps/gen/db/generic.py b/gramps/gen/db/generic.py index b2f8234ef..5187e9dd0 100644 --- a/gramps/gen/db/generic.py +++ b/gramps/gen/db/generic.py @@ -575,7 +575,7 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback): if directory: self.load(directory) - def _initialize(self, directory): + def _initialize(self, directory, username, password): """ Initialize database backend. """ @@ -586,12 +586,14 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback): force_bsddb_upgrade=False, force_bsddb_downgrade=False, force_python_upgrade=False, - update=True): + update=True, + username=None, + password=None): """ If update is False: then don't update any files """ # run backend-specific code: - self._initialize(directory) + self._initialize(directory, username, password) # We use the existence of the person table as a proxy for the database # being new diff --git a/gramps/gui/configure.py b/gramps/gui/configure.py index b301d5299..a51f7c708 100644 --- a/gramps/gui/configure.py +++ b/gramps/gui/configure.py @@ -1471,14 +1471,6 @@ class GrampsPreferences(ConfigureDialog): current_line += 1 self.connection_widgets = [] - entry = self.add_entry(grid, _('Username'), current_line, - 'database.user', col_attach=1) - self.connection_widgets.append(entry) - current_line += 1 - entry = self.add_entry(grid, _('Password'), current_line, - 'database.password', col_attach=1) - self.connection_widgets.append(entry) - current_line += 1 entry = self.add_entry(grid, _('Host'), current_line, 'database.host', col_attach=1) self.connection_widgets.append(entry) diff --git a/gramps/gui/dbloader.py b/gramps/gui/dbloader.py index 040895f08..9b878ac2f 100644 --- a/gramps/gui/dbloader.py +++ b/gramps/gui/dbloader.py @@ -144,7 +144,7 @@ class DbLoader(CLIDbLoader): return "" return self.import_info.info_text() - def read_file(self, filename): + def read_file(self, filename, username=None, password=None): """ This method takes care of changing database, and loading the data. In 3.0 we only allow reading of real databases of filetype @@ -181,6 +181,13 @@ class DbLoader(CLIDbLoader): db.disable_signals() self.dbstate.no_database() + if db.requires_login() and username is None: + login = GrampsLoginDialog(self.uistate) + credentials = login.run() + if credentials is None: + return + username, password = credentials + self._begin_progress() force_schema_upgrade = False @@ -194,7 +201,9 @@ class DbLoader(CLIDbLoader): mode, force_schema_upgrade, force_bsddb_upgrade, force_bsddb_downgrade, - force_python_upgrade) + force_python_upgrade, + username=username, + password=password) if self.dbstate.is_open(): self.dbstate.db.close( user=User(callback=self._pulse_progress, @@ -383,6 +392,49 @@ def format_maker(): box.show_all() return (box, type_selector) +class GrampsLoginDialog(ManagedWindow): + + def __init__(self, uistate): + """ + A login dialog to obtain credentials to connect to a database + """ + self.title = _("Login") + ManagedWindow.__init__(self, uistate, [], self.__class__, modal=True) + + dialog = Gtk.Dialog(parent=uistate.window) + grid = Gtk.Grid() + grid.set_border_width(6) + grid.set_row_spacing(6) + grid.set_column_spacing(6) + label = Gtk.Label(label=_('Username: ')) + grid.attach(label, 0, 0, 1, 1) + self.username = Gtk.Entry() + self.username.set_hexpand(True) + grid.attach(self.username, 1, 0, 1, 1) + label = Gtk.Label(label=_('Password: ')) + grid.attach(label, 0, 1, 1, 1) + self.password = Gtk.Entry() + self.password.set_hexpand(True) + self.password.set_visibility(False) + self.password.set_input_purpose(Gtk.InputPurpose.PASSWORD) + grid.attach(self.password, 1, 1, 1, 1) + dialog.vbox.pack_start(grid, True, True, 0) + dialog.add_buttons(_('_Cancel'), Gtk.ResponseType.CANCEL, + _('Login'), Gtk.ResponseType.OK) + self.set_window(dialog, None, self.title) + + def run(self): + self.show() + response = self.window.run() + username = self.username.get_text() + password = self.password.get_text() + if response == Gtk.ResponseType.CANCEL: + self.close() + return None + elif response == Gtk.ResponseType.OK: + self.close() + return (username, password) + class GrampsImportFileDialog(ManagedWindow): def __init__(self, dbstate, uistate, callback=None): diff --git a/gramps/plugins/db/bsddb/write.py b/gramps/plugins/db/bsddb/write.py index 4839f9537..358055e13 100644 --- a/gramps/plugins/db/bsddb/write.py +++ b/gramps/plugins/db/bsddb/write.py @@ -536,7 +536,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): @catch_db_error def load(self, name, callback=None, mode=DBMODE_W, force_schema_upgrade=False, force_bsddb_upgrade=False, force_bsddb_downgrade=False, - force_python_upgrade=False, update=True): + force_python_upgrade=False, update=True, + username=None, password=None): """ If update is False: then don't update any files; open read-only """ diff --git a/gramps/plugins/db/dbapi/dbapi.py b/gramps/plugins/db/dbapi/dbapi.py index da0bd75cb..0cf6892ee 100644 --- a/gramps/plugins/db/dbapi/dbapi.py +++ b/gramps/plugins/db/dbapi/dbapi.py @@ -65,7 +65,7 @@ class DBAPI(DbGeneric): with open(versionpath, "w") as version_file: version_file.write(self.__class__.__name__.lower()) - def _initialize(self, directory): + def _initialize(self, directory, username, password): raise NotImplementedError def _create_schema(self): diff --git a/gramps/plugins/db/dbapi/postgresql.py b/gramps/plugins/db/dbapi/postgresql.py index eb42a2928..92e019144 100644 --- a/gramps/plugins/db/dbapi/postgresql.py +++ b/gramps/plugins/db/dbapi/postgresql.py @@ -66,13 +66,14 @@ class PostgreSQL(DBAPI): }) return summary - def _initialize(self, directory): + def requires_login(self): + return True + + def _initialize(self, directory, username, password): config_file = os.path.join(directory, 'settings.ini') config_mgr = ConfigManager(config_file) config_mgr.register('database.dbname', '') config_mgr.register('database.host', '') - config_mgr.register('database.user', '') - config_mgr.register('database.password', '') config_mgr.register('database.port', '') if not os.path.exists(config_file): @@ -81,8 +82,6 @@ class PostgreSQL(DBAPI): dbname = file.readline().strip() config_mgr.set('database.dbname', dbname) config_mgr.set('database.host', config.get('database.host')) - config_mgr.set('database.user', config.get('database.user')) - config_mgr.set('database.password', config.get('database.password')) config_mgr.set('database.port', config.get('database.port')) config_mgr.save() @@ -93,6 +92,10 @@ class PostgreSQL(DBAPI): value = config_mgr.get('database.' + key) if value: dbkwargs[key] = value + if username: + dbkwargs['user'] = username + if password: + dbkwargs['password'] = password try: self.dbapi = Connection(**dbkwargs) diff --git a/gramps/plugins/db/dbapi/sqlite.py b/gramps/plugins/db/dbapi/sqlite.py index 26a6ecfef..b402398fc 100644 --- a/gramps/plugins/db/dbapi/sqlite.py +++ b/gramps/plugins/db/dbapi/sqlite.py @@ -64,7 +64,7 @@ class SQLite(DBAPI): }) return summary - def _initialize(self, directory): + def _initialize(self, directory, username, password): if directory == ':memory:': path_to_db = ':memory:' else: