Add login dialog and username/password command line options

This commit is contained in:
Nick Hall 2017-09-25 22:44:47 +01:00
parent 1f9f08c3c5
commit c048ebcfbb
13 changed files with 100 additions and 32 deletions

View File

@ -180,6 +180,8 @@ class ArgHandler:
self.imports = [] self.imports = []
self.exports = [] self.exports = []
self.removes = parser.removes self.removes = parser.removes
self.username = parser.username
self.password = parser.password
self.open = self.__handle_open_option(parser.open, parser.create) self.open = self.__handle_open_option(parser.open, parser.create)
self.sanitize_args(parser.imports, parser.exports) self.sanitize_args(parser.imports, parser.exports)
@ -505,7 +507,7 @@ class ArgHandler:
newdb.write_version(self.imp_db_path) newdb.write_version(self.imp_db_path)
try: 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") msg = _("Created empty Family Tree successfully")
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
except: except:
@ -532,7 +534,7 @@ class ArgHandler:
# we load this file for use # we load this file for use
try: try:
self.smgr.open_activate(self.open) self.smgr.open_activate(self.open, self.username, self.password)
print(_("Opened successfully!"), file=sys.stderr) print(_("Opened successfully!"), file=sys.stderr)
except: except:
print(_("Error opening the file."), file=sys.stderr) print(_("Error opening the file."), file=sys.stderr)

View File

@ -62,6 +62,8 @@ Help options
Application options Application options
-O, --open=FAMILY_TREE Open Family Tree -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 -C, --create=FAMILY_TREE Create on open if new Family Tree
-i, --import=FILENAME Import file -i, --import=FILENAME Import file
-e, --export=FILENAME Export file -e, --export=FILENAME Export file
@ -138,6 +140,8 @@ class ArgParser:
The valid options are: The valid options are:
-O, --open=FAMILY_TREE Open Family Tree -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 -C, --create=FAMILY_TREE Create on open if new Family Tree
-i, --import=FILENAME Import file -i, --import=FILENAME Import file
-e, --export=FILENAME Export file -e, --export=FILENAME Export file
@ -196,6 +200,8 @@ class ArgParser:
self.open_gui = None self.open_gui = None
self.open = None self.open = None
self.username = None
self.password = None
self.exports = [] self.exports = []
self.actions = [] self.actions = []
self.imports = [] self.imports = []
@ -279,6 +285,10 @@ class ArgParser:
self.open = value self.open = value
elif option in ['-C', '--create']: elif option in ['-C', '--create']:
self.create = value self.create = value
elif option in ['-U', '--username']:
self.username = value
elif option in ['-P', '--password']:
self.password = value
elif option in ['-i', '--import']: elif option in ['-i', '--import']:
family_tree_format = None family_tree_format = None
if (opt_ix < len(options) - 1 if (opt_ix < len(options) - 1

View File

@ -132,7 +132,7 @@ class CLIDbLoader:
""" """
pass pass
def read_file(self, filename): def read_file(self, filename, username, password):
""" """
This method takes care of changing database, and loading the data. This method takes care of changing database, and loading the data.
In 3.0 we only allow reading of real databases of filetype In 3.0 we only allow reading of real databases of filetype
@ -173,7 +173,8 @@ class CLIDbLoader:
self._begin_progress() self._begin_progress()
try: 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: except DbEnvironmentError as msg:
self.dbstate.no_database() self.dbstate.no_database()
self._errordialog(_("Cannot open database"), str(msg)) self._errordialog(_("Cannot open database"), str(msg))
@ -240,11 +241,11 @@ class CLIManager:
self._pmgr = BasePluginManager.get_instance() self._pmgr = BasePluginManager.get_instance()
self.user = user self.user = user
def open_activate(self, path): def open_activate(self, path, username, password):
""" """
Open and make a family tree active Open and make a family tree active
""" """
self._read_recent_file(path) self._read_recent_file(path, username, password)
def _errordialog(self, title, errormessage): def _errordialog(self, title, errormessage):
""" """
@ -253,7 +254,7 @@ class CLIManager:
print(_('ERROR: %s') % errormessage, file=sys.stderr) print(_('ERROR: %s') % errormessage, file=sys.stderr)
sys.exit(1) 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 Called when a file needs to be loaded
""" """
@ -274,7 +275,7 @@ class CLIManager:
"that the database is not in use.")) "that the database is not in use."))
return return
if self.db_loader.read_file(filename): if self.db_loader.read_file(filename, username, password):
# Attempt to figure out the database title # Attempt to figure out the database title
path = os.path.join(filename, "name.txt") path = os.path.join(filename, "name.txt")
try: try:

View File

@ -163,8 +163,6 @@ register('database.backup-path', USER_HOME)
register('database.backup-on-exit', True) register('database.backup-on-exit', True)
register('database.autobackup', 0) register('database.autobackup', 0)
register('database.path', os.path.join(HOME_DIR, 'grampsdb')) register('database.path', os.path.join(HOME_DIR, 'grampsdb'))
register('database.user', '')
register('database.password', '')
register('database.host', '') register('database.host', '')
register('database.port', '') register('database.port', '')

View File

@ -288,6 +288,8 @@ LONGOPTS = [
"oaf-ior-fd=", "oaf-ior-fd=",
"oaf-private", "oaf-private",
"open=", "open=",
"username=",
"password=",
"create=", "create=",
"options=", "options=",
"screen=", "screen=",
@ -303,7 +305,7 @@ LONGOPTS = [
"quiet", "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') GRAMPS_UUID = uuid.UUID('516cd010-5a41-470f-99f8-eb22f1098ad6')

View File

@ -1427,6 +1427,11 @@ class DbReadBase:
""" """
raise NotImplementedError raise NotImplementedError
def requires_login(self):
"""
Returns True for backends that require a login dialog, else False.
"""
return False
class DbWriteBase(DbReadBase): class DbWriteBase(DbReadBase):
""" """

View File

@ -575,7 +575,7 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
if directory: if directory:
self.load(directory) self.load(directory)
def _initialize(self, directory): def _initialize(self, directory, username, password):
""" """
Initialize database backend. Initialize database backend.
""" """
@ -586,12 +586,14 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
force_bsddb_upgrade=False, force_bsddb_upgrade=False,
force_bsddb_downgrade=False, force_bsddb_downgrade=False,
force_python_upgrade=False, force_python_upgrade=False,
update=True): update=True,
username=None,
password=None):
""" """
If update is False: then don't update any files If update is False: then don't update any files
""" """
# run backend-specific code: # 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 # We use the existence of the person table as a proxy for the database
# being new # being new

View File

@ -1471,14 +1471,6 @@ class GrampsPreferences(ConfigureDialog):
current_line += 1 current_line += 1
self.connection_widgets = [] 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, entry = self.add_entry(grid, _('Host'), current_line,
'database.host', col_attach=1) 'database.host', col_attach=1)
self.connection_widgets.append(entry) self.connection_widgets.append(entry)

View File

@ -144,7 +144,7 @@ class DbLoader(CLIDbLoader):
return "" return ""
return self.import_info.info_text() 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. This method takes care of changing database, and loading the data.
In 3.0 we only allow reading of real databases of filetype In 3.0 we only allow reading of real databases of filetype
@ -181,6 +181,13 @@ class DbLoader(CLIDbLoader):
db.disable_signals() db.disable_signals()
self.dbstate.no_database() 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() self._begin_progress()
force_schema_upgrade = False force_schema_upgrade = False
@ -194,7 +201,9 @@ class DbLoader(CLIDbLoader):
mode, force_schema_upgrade, mode, force_schema_upgrade,
force_bsddb_upgrade, force_bsddb_upgrade,
force_bsddb_downgrade, force_bsddb_downgrade,
force_python_upgrade) force_python_upgrade,
username=username,
password=password)
if self.dbstate.is_open(): if self.dbstate.is_open():
self.dbstate.db.close( self.dbstate.db.close(
user=User(callback=self._pulse_progress, user=User(callback=self._pulse_progress,
@ -383,6 +392,49 @@ def format_maker():
box.show_all() box.show_all()
return (box, type_selector) 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): class GrampsImportFileDialog(ManagedWindow):
def __init__(self, dbstate, uistate, callback=None): def __init__(self, dbstate, uistate, callback=None):

View File

@ -536,7 +536,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
@catch_db_error @catch_db_error
def load(self, name, callback=None, mode=DBMODE_W, force_schema_upgrade=False, def load(self, name, callback=None, mode=DBMODE_W, force_schema_upgrade=False,
force_bsddb_upgrade=False, force_bsddb_downgrade=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 If update is False: then don't update any files; open read-only
""" """

View File

@ -65,7 +65,7 @@ class DBAPI(DbGeneric):
with open(versionpath, "w") as version_file: with open(versionpath, "w") as version_file:
version_file.write(self.__class__.__name__.lower()) version_file.write(self.__class__.__name__.lower())
def _initialize(self, directory): def _initialize(self, directory, username, password):
raise NotImplementedError raise NotImplementedError
def _create_schema(self): def _create_schema(self):

View File

@ -66,13 +66,14 @@ class PostgreSQL(DBAPI):
}) })
return summary 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_file = os.path.join(directory, 'settings.ini')
config_mgr = ConfigManager(config_file) config_mgr = ConfigManager(config_file)
config_mgr.register('database.dbname', '') config_mgr.register('database.dbname', '')
config_mgr.register('database.host', '') config_mgr.register('database.host', '')
config_mgr.register('database.user', '')
config_mgr.register('database.password', '')
config_mgr.register('database.port', '') config_mgr.register('database.port', '')
if not os.path.exists(config_file): if not os.path.exists(config_file):
@ -81,8 +82,6 @@ class PostgreSQL(DBAPI):
dbname = file.readline().strip() dbname = file.readline().strip()
config_mgr.set('database.dbname', dbname) config_mgr.set('database.dbname', dbname)
config_mgr.set('database.host', config.get('database.host')) 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.set('database.port', config.get('database.port'))
config_mgr.save() config_mgr.save()
@ -93,6 +92,10 @@ class PostgreSQL(DBAPI):
value = config_mgr.get('database.' + key) value = config_mgr.get('database.' + key)
if value: if value:
dbkwargs[key] = value dbkwargs[key] = value
if username:
dbkwargs['user'] = username
if password:
dbkwargs['password'] = password
try: try:
self.dbapi = Connection(**dbkwargs) self.dbapi = Connection(**dbkwargs)

View File

@ -64,7 +64,7 @@ class SQLite(DBAPI):
}) })
return summary return summary
def _initialize(self, directory): def _initialize(self, directory, username, password):
if directory == ':memory:': if directory == ':memory:':
path_to_db = ':memory:' path_to_db = ':memory:'
else: else: