clean up the code
8
.gitignore
vendored
|
@ -75,10 +75,10 @@ gnome-twofactorauth
|
|||
stamp-h1
|
||||
config.h
|
||||
missing
|
||||
ltmain.sh
|
||||
ltmain.sh
|
||||
configure
|
||||
config.status
|
||||
config.log
|
||||
config.log
|
||||
config.h.in
|
||||
Makefile
|
||||
Makefile.in
|
||||
|
@ -90,8 +90,8 @@ TwoFactorAuth/models/Makefile
|
|||
TwoFactorAuth/models/Makefile.in
|
||||
data/Makefile
|
||||
data/Makefile.in
|
||||
data/logos/Makefile
|
||||
data/logos/Makefile.in
|
||||
data/applications/Makefile
|
||||
data/applications/Makefile.in
|
||||
schemas/Makefile.in
|
||||
schemas/Makefile
|
||||
schemas/*.valid
|
||||
|
|
0
AUTHORS
1
COPYING
|
@ -1 +0,0 @@
|
|||
/usr/share/automake-1.15/COPYING
|
1
INSTALL
|
@ -1 +0,0 @@
|
|||
/usr/share/automake-1.15/INSTALL
|
0
NEWS
0
README
|
@ -1,8 +1,16 @@
|
|||
app_PYTHON = \
|
||||
application.py \
|
||||
__init__.py
|
||||
__init__.py
|
||||
do_substitution = sed -e 's,[@]pythondir[@],$(pythondir),g' \
|
||||
-e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
|
||||
-e 's,[@]localedir[@],$(localedir),g' \
|
||||
-e 's,[@]PACKAGE[@],$(PACKAGE),g' \
|
||||
-e 's,[@]VERSION[@],$(VERSION),g' \
|
||||
-e 's,[@]pyexecdir[@],$(pyexecdir),g'
|
||||
|
||||
appdir = $(pythondir)/TwoFactorAuth
|
||||
|
||||
|
||||
appdir = $(pythondir)/Gnome-TwoFactorAuth
|
||||
|
||||
SUBDIRS = models \
|
||||
widgets
|
||||
|
|
|
@ -23,22 +23,24 @@ class Application(Gtk.Application):
|
|||
|
||||
settings_window = None
|
||||
settings_action = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
for key in kwargs:
|
||||
setattr(self, key, kwargs[key])
|
||||
Gtk.Application.__init__(self,
|
||||
application_id='org.gnome.TwoFactorAuth',
|
||||
application_id="org.gnome.TwoFactorAuth",
|
||||
flags=Gio.ApplicationFlags.FLAGS_NONE)
|
||||
GLib.set_application_name("TwoFactorAuth")
|
||||
DATA_DIR = self.pkgdatadir
|
||||
GLib.set_application_name(self.package)
|
||||
GLib.set_prgname(self.package)
|
||||
|
||||
current_desktop = env.get("XDG_CURRENT_DESKTOP")
|
||||
if current_desktop:
|
||||
self.use_GMenu = current_desktop.lower() == "gnome"
|
||||
else:
|
||||
self.use_GMenu = False
|
||||
|
||||
result = GK.unlock_sync("TwoFactorAuth", None)
|
||||
result = GK.unlock_sync(APP_NAME, None)
|
||||
if result == GK.Result.CANCELLED:
|
||||
self.quit()
|
||||
|
||||
|
@ -46,15 +48,16 @@ class Application(Gtk.Application):
|
|||
if self.cfg.read("state", "login"):
|
||||
self.locked = True
|
||||
provider = Gtk.CssProvider()
|
||||
css_file = self.pkgdatadir + "/data/style.css"
|
||||
cssProviderFile = Gio.File.new_for_uri('resource:///org/gnome/TwoFactorAuth/style.css')
|
||||
print(cssProviderFile)
|
||||
try:
|
||||
provider.load_from_path(css_file)
|
||||
provider.load_from_path(cssProviderFile)
|
||||
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
|
||||
provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
logging.debug("Loading css file %s" % css_file)
|
||||
logging.debug("Loading css file %s" % cssProviderFile)
|
||||
except Exception as e:
|
||||
logging.debug("File not found %s" % css_file)
|
||||
logging.debug("File not found %s" % cssProviderFile)
|
||||
logging.debug("Error message %s" % str(e))
|
||||
|
||||
def do_startup(self):
|
||||
|
@ -66,14 +69,16 @@ class Application(Gtk.Application):
|
|||
def generate_menu(self):
|
||||
# Settings section
|
||||
settings_content = Gio.Menu.new()
|
||||
settings_content.append_item(Gio.MenuItem.new(_("Settings"), "app.settings"))
|
||||
settings_content.append_item(
|
||||
Gio.MenuItem.new(_("Settings"), "app.settings"))
|
||||
settings_section = Gio.MenuItem.new_section(None, settings_content)
|
||||
self.menu.append_item(settings_section)
|
||||
|
||||
# Help section
|
||||
help_content = Gio.Menu.new()
|
||||
if Gtk.get_major_version() >= 3 and Gtk.get_minor_version() >= 20:
|
||||
help_content.append_item(Gio.MenuItem.new(_("Shortcuts"), "app.shortcuts"))
|
||||
help_content.append_item(Gio.MenuItem.new(
|
||||
_("Shortcuts"), "app.shortcuts"))
|
||||
|
||||
help_content.append_item(Gio.MenuItem.new(_("About"), "app.about"))
|
||||
help_content.append_item(Gio.MenuItem.new(_("Quit"), "app.quit"))
|
||||
|
@ -109,7 +114,8 @@ class Application(Gtk.Application):
|
|||
|
||||
def refresh_menu(self):
|
||||
if self.use_GMenu:
|
||||
self.settings_action.set_enabled(not self.settings_action.get_enabled())
|
||||
self.settings_action.set_enabled(
|
||||
not self.settings_action.get_enabled())
|
||||
|
||||
def on_toggle_lock(self, *args):
|
||||
if not self.locked:
|
||||
|
@ -133,7 +139,7 @@ class Application(Gtk.Application):
|
|||
settings_window.show_window()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def on_quit(self, *args):
|
||||
"""
|
||||
Close the application, stops all threads
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
appdir = $(pythondir)/TwoFactorAuth/models
|
||||
do_substitution = sed -e 's,[@]pythondir[@],$(pythondir),g' \
|
||||
-e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
|
||||
-e 's,[@]PACKAGE[@],$(PACKAGE),g' \
|
||||
-e 's,[@]VERSION[@],$(VERSION),g' \
|
||||
-e 's,[@]pyexecdir[@],$(pyexecdir),g'
|
||||
|
||||
app_PYTHON = \
|
||||
code.py \
|
||||
|
|
|
@ -5,6 +5,7 @@ from gi.repository import GdkPixbuf, Gtk
|
|||
from gi.repository import GnomeKeyring as GK
|
||||
from hashlib import sha256
|
||||
|
||||
|
||||
class Authenticator:
|
||||
|
||||
def __init__(self):
|
||||
|
@ -26,7 +27,8 @@ class Authenticator:
|
|||
# Connect to database
|
||||
self.conn = sqlite3.connect(database_file)
|
||||
if not self.is_table_exists():
|
||||
logging.debug("SQL: Table 'applications' does not exists, creating it now...")
|
||||
logging.debug(
|
||||
"SQL: Table 'applications' does not exists, creating it now...")
|
||||
self.create_table()
|
||||
logging.debug("SQL: Table 'applications' created successfully")
|
||||
|
||||
|
@ -74,7 +76,8 @@ class Authenticator:
|
|||
data = c.execute(query, (uid,))
|
||||
return data.fetchone()[0]
|
||||
except Exception as e:
|
||||
logging.error("SQL: Couldn't get application secret code : %s " % str(e))
|
||||
logging.error(
|
||||
"SQL: Couldn't get application secret code : %s " % str(e))
|
||||
return None
|
||||
|
||||
def remove_by_id(self, uid):
|
||||
|
@ -92,7 +95,7 @@ class Authenticator:
|
|||
if result == GK.Result.OK:
|
||||
if item.get_display_name().strip("'") == secret_code:
|
||||
found = True
|
||||
break;
|
||||
break
|
||||
if found:
|
||||
GK.item_delete_sync("TwoFactorAuth", gid)
|
||||
query = "DELETE FROM applications WHERE uid=?"
|
||||
|
@ -100,7 +103,8 @@ class Authenticator:
|
|||
self.conn.execute(query, (uid,))
|
||||
self.conn.commit()
|
||||
except Exception as e:
|
||||
logging.error("SQL: Couldn't remove application by uid : %s with error : %s" % (uid, str(e)))
|
||||
logging.error(
|
||||
"SQL: Couldn't remove application by uid : %s with error : %s" % (uid, str(e)))
|
||||
|
||||
def count(self):
|
||||
"""
|
||||
|
@ -113,7 +117,8 @@ class Authenticator:
|
|||
data = c.execute(query)
|
||||
return data.fetchone()[0]
|
||||
except Exception as e:
|
||||
logging.error("SQL: Couldn't count applications list : %s " % str(e))
|
||||
logging.error(
|
||||
"SQL: Couldn't count applications list : %s " % str(e))
|
||||
return None
|
||||
|
||||
def fetch_apps(self):
|
||||
|
@ -131,14 +136,13 @@ class Authenticator:
|
|||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_auth_icon(image, pkgdatadir):
|
||||
def get_auth_icon(image):
|
||||
"""
|
||||
Generate a GdkPixbuf image
|
||||
:param image: icon name or image path
|
||||
:param pkgdatadir: default installation dir of apps
|
||||
:return: GdkPixbux Image
|
||||
"""
|
||||
directory = pkgdatadir + "/data/logos/"
|
||||
directory = DATA_DIR + "/data/applications/"
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
if path.isfile(directory + image) and path.exists(directory + image):
|
||||
icon = GdkPixbuf.Pixbuf.new_from_file(directory + image)
|
||||
|
@ -181,7 +185,8 @@ class Authenticator:
|
|||
self.conn.execute(query)
|
||||
self.conn.commit()
|
||||
except Exception as e:
|
||||
logging.error( "SQL: impossible to create table 'applications' %s " % str(e))
|
||||
logging.error(
|
||||
"SQL: impossible to create table 'applications' %s " % str(e))
|
||||
|
||||
def is_table_exists(self):
|
||||
"""
|
||||
|
|
|
@ -3,8 +3,10 @@ appdir = $(pythondir)/TwoFactorAuth/widgets
|
|||
app_PYTHON = \
|
||||
add_account.py \
|
||||
confirmation.py \
|
||||
listrow.py \
|
||||
account_row.py \
|
||||
applications_list.py \
|
||||
application_row.py \
|
||||
change_password.py \
|
||||
search_bar.py \
|
||||
settings.py \
|
||||
window.py
|
||||
|
|
|
@ -10,7 +10,7 @@ import logging
|
|||
from gettext import gettext as _
|
||||
|
||||
|
||||
class ListBoxRow(Thread, Gtk.ListBoxRow):
|
||||
class AccountRow(Thread, Gtk.ListBoxRow):
|
||||
counter_max = 30
|
||||
counter = 30
|
||||
timer = 0
|
||||
|
@ -33,10 +33,12 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
self.code = Code(self.secret_code)
|
||||
else:
|
||||
self.code_generated = False
|
||||
logging.error("Could not read the secret code from, the keyring keys were reset manually")
|
||||
logging.error(
|
||||
"Could not read the secret code from, the keyring keys were reset manually")
|
||||
self.logo = logo
|
||||
# Create needed widgets
|
||||
self.code_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.revealer = Gtk.Revealer()
|
||||
self.checkbox = Gtk.CheckButton()
|
||||
self.application_name = Gtk.Label(xalign=0)
|
||||
self.code_label = Gtk.Label(xalign=0)
|
||||
|
@ -53,15 +55,15 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
"""
|
||||
return self.id
|
||||
|
||||
def get_label(self):
|
||||
def get_name(self):
|
||||
"""
|
||||
Get the application label
|
||||
:return: (str): application label
|
||||
Get the application name
|
||||
:return: (str): application name
|
||||
"""
|
||||
return self.application_name.get_label().strip()
|
||||
return self.name
|
||||
|
||||
def get_code(self):
|
||||
return self.code_label.get_text()
|
||||
def get_code(self):
|
||||
return self.code
|
||||
|
||||
def get_code_label(self):
|
||||
return self.code_label
|
||||
|
@ -71,7 +73,7 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
Get ListBowRow's checkbox
|
||||
:return: (Gtk.Checkbox)
|
||||
"""
|
||||
return self.checkbox
|
||||
return self.checkbox
|
||||
|
||||
def get_code_box(self):
|
||||
"""
|
||||
|
@ -80,16 +82,11 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
"""
|
||||
return self.code_box
|
||||
|
||||
|
||||
def toggle_code_box(self):
|
||||
"""
|
||||
Toggle code box
|
||||
"""
|
||||
code_box = self.get_code_box()
|
||||
is_visible = code_box.get_visible()
|
||||
code_box.set_visible(not is_visible)
|
||||
code_box.set_no_show_all(is_visible)
|
||||
code_box.show_all()
|
||||
self.revealer.set_reveal_child(not self.revealer.get_reveal_child())
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
|
@ -126,17 +123,8 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
h_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.code_box.set_visible(False)
|
||||
|
||||
# ID
|
||||
label_id = Gtk.Label()
|
||||
label_id.set_text(str(self.id))
|
||||
label_id.set_visible(False)
|
||||
label_id.set_no_show_all(True)
|
||||
|
||||
box.pack_start(h_box, True, True, 0)
|
||||
box.pack_start(self.code_box, True, True, 0)
|
||||
box.pack_end(label_id, False, False, 0)
|
||||
box.pack_start(self.revealer, True, True, 0)
|
||||
|
||||
# Checkbox
|
||||
self.checkbox.set_visible(False)
|
||||
|
@ -145,7 +133,7 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
h_box.pack_start(self.checkbox, False, True, 6)
|
||||
|
||||
# Application logo
|
||||
auth_icon = Authenticator.get_auth_icon(self.logo, self.parent.app.pkgdatadir)
|
||||
auth_icon = Authenticator.get_auth_icon(self.logo)
|
||||
auth_logo = Gtk.Image(xalign=0)
|
||||
auth_logo.set_from_pixbuf(auth_icon)
|
||||
h_box.pack_start(auth_logo, False, True, 6)
|
||||
|
@ -160,7 +148,8 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
# Copy button
|
||||
copy_event = Gtk.EventBox()
|
||||
copy_button = Gtk.Image(xalign=0)
|
||||
copy_button.set_from_icon_name("edit-copy-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
|
||||
copy_button.set_from_icon_name(
|
||||
"edit-copy-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
|
||||
copy_button.set_tooltip_text(_("Copy the generated code"))
|
||||
copy_event.connect("button-press-event", self.copy_code)
|
||||
copy_event.add(copy_button)
|
||||
|
@ -169,23 +158,26 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
# Remove button
|
||||
remove_event = Gtk.EventBox()
|
||||
remove_button = Gtk.Image(xalign=0)
|
||||
remove_button.set_from_icon_name("user-trash-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
|
||||
remove_button.set_from_icon_name(
|
||||
"user-trash-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
|
||||
remove_button.set_tooltip_text(_("Remove the application"))
|
||||
remove_event.add(remove_button)
|
||||
remove_event.connect("button-press-event", self.parent.remove_application)
|
||||
remove_event.connect("button-press-event",
|
||||
self.parent.remove_application)
|
||||
h_box.pack_end(remove_event, False, True, 6)
|
||||
|
||||
self.timer_label.set_label(_("Expires in %s seconds") % self.counter)
|
||||
self.code_label.get_style_context().add_class("application-secret-code")
|
||||
self.timer_label.get_style_context().add_class("account-timer")
|
||||
self.code_label.get_style_context().add_class("account-secret-code")
|
||||
if self.code_generated:
|
||||
self.update_code(self.code_label)
|
||||
else:
|
||||
self.code_label.set_text(_("Error during the generation of code"))
|
||||
self.code_box.set_no_show_all(True)
|
||||
self.code_box.set_visible(False)
|
||||
|
||||
self.code_box.pack_end(self.timer_label, False, True, 6)
|
||||
self.code_box.pack_start(self.code_label, False, True, 6)
|
||||
|
||||
self.revealer.add(self.code_box)
|
||||
self.revealer.set_reveal_child(False)
|
||||
self.add(box)
|
||||
|
||||
def get_counter(self):
|
||||
|
@ -231,4 +223,3 @@ class ListBoxRow(Thread, Gtk.ListBoxRow):
|
|||
|
||||
def update_timer_label(self):
|
||||
self.timer_label.set_label(_("Expires in %s seconds") % self.counter)
|
||||
|
|
@ -12,7 +12,7 @@ class AddAccount(Gtk.Window):
|
|||
|
||||
def __init__(self, window):
|
||||
self.parent = window
|
||||
|
||||
|
||||
self.selected_image = None
|
||||
self.step = 1
|
||||
self.logo_image = Gtk.Image(xalign=0)
|
||||
|
@ -26,7 +26,7 @@ class AddAccount(Gtk.Window):
|
|||
|
||||
def generate_window(self):
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL, title=_("Add a new account"),
|
||||
modal=True, destroy_with_parent=True)
|
||||
modal=True, destroy_with_parent=True)
|
||||
self.connect("delete-event", self.close_window)
|
||||
self.resize(410, 300)
|
||||
self.set_border_width(18)
|
||||
|
@ -68,14 +68,16 @@ class AddAccount(Gtk.Window):
|
|||
labels_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||
logo_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
||||
hbox_title = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||
hbox_title = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||
title_label = Gtk.Label()
|
||||
title_label.set_text(_("Account Name"))
|
||||
|
||||
hbox_title.pack_end(self.name_entry, False, True, 0)
|
||||
hbox_title.pack_end(title_label, False, True, 0)
|
||||
|
||||
hbox_two_factor = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||
hbox_two_factor = Gtk.Box(
|
||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||
two_factor_label = Gtk.Label()
|
||||
two_factor_label.set_text(_("Secret Code"))
|
||||
self.secret_code.connect("changed", self.validate_ascii_code)
|
||||
|
@ -83,7 +85,7 @@ class AddAccount(Gtk.Window):
|
|||
hbox_two_factor.pack_end(self.secret_code, False, True, 0)
|
||||
hbox_two_factor.pack_end(two_factor_label, False, True, 0)
|
||||
|
||||
auth_icon = Authenticator.get_auth_icon("image-missing", self.parent.app.pkgdatadir)
|
||||
auth_icon = Authenticator.get_auth_icon("image-missing")
|
||||
self.logo_image.set_from_pixbuf(auth_icon)
|
||||
self.logo_image.get_style_context().add_class("application-logo-add")
|
||||
logo_box.pack_start(self.logo_image, True, False, 6)
|
||||
|
@ -107,9 +109,9 @@ class AddAccount(Gtk.Window):
|
|||
"""
|
||||
Update image logo
|
||||
"""
|
||||
directory = self.parent.app.pkgdatadir + "/data/logos/"
|
||||
directory = DATA_DIR + "/data/applications/"
|
||||
self.selected_image = image
|
||||
auth_icon = Authenticator.get_auth_icon(image, self.parent.app.pkgdatadir)
|
||||
auth_icon = Authenticator.get_auth_icon(image)
|
||||
self.logo_image.clear()
|
||||
self.logo_image.set_from_pixbuf(auth_icon)
|
||||
|
||||
|
@ -121,9 +123,11 @@ class AddAccount(Gtk.Window):
|
|||
is_valid = Code.is_valid(ascii_code)
|
||||
self.apply_button.set_sensitive(is_valid)
|
||||
if not is_valid and len(ascii_code) != 0:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "dialog-error-symbolic")
|
||||
entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, "dialog-error-symbolic")
|
||||
else:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, None)
|
||||
|
||||
def add_application(self, *args):
|
||||
"""
|
||||
|
@ -136,7 +140,8 @@ class AddAccount(Gtk.Window):
|
|||
self.parent.app.auth.add_application(name_entry, secret_entry,
|
||||
image_entry)
|
||||
uid = self.parent.app.auth.get_latest_id()
|
||||
self.parent.append_list_box(uid, name_entry, secret_entry, image_entry)
|
||||
self.parent.append_list_box(
|
||||
uid, name_entry, secret_entry, image_entry)
|
||||
self.parent.refresh_window()
|
||||
self.close_window()
|
||||
except Exception as e:
|
||||
|
|
44
TwoFactorAuth/widgets/application_row.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
from gi import require_version
|
||||
require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk
|
||||
import logging
|
||||
from gettext import gettext as _
|
||||
|
||||
|
||||
class ApplicationRow(Gtk.ListBoxRow):
|
||||
|
||||
def __init__(self, name, logo):
|
||||
Gtk.ListBoxRow.__init__(self)
|
||||
self.name = name
|
||||
self.logo = logo
|
||||
# Create the list row
|
||||
self.create_row()
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Get the application label
|
||||
:return: (str): application label
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def create_row(self):
|
||||
"""
|
||||
Create ListBoxRow
|
||||
"""
|
||||
self.get_style_context().add_class("application-list-row")
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
# Application logo
|
||||
application_image = Gtk.Image(xalign=0)
|
||||
application_image.set_from_pixbuf(self.logo)
|
||||
hbox.pack_start(application_image, False, True, 6)
|
||||
|
||||
# Application name
|
||||
application_name = Gtk.Label(xalign=0)
|
||||
application_name.get_style_context().add_class("application-name")
|
||||
application_name.set_text(self.name)
|
||||
hbox.pack_start(application_name, True, True, 6)
|
||||
|
||||
vbox.pack_start(hbox, True, True, 6)
|
||||
self.add(vbox)
|
|
@ -2,6 +2,8 @@ from gi import require_version
|
|||
require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gio, Gdk
|
||||
from TwoFactorAuth.models.authenticator import Authenticator
|
||||
from TwoFactorAuth.widgets.search_bar import SearchBar
|
||||
from TwoFactorAuth.widgets.application_row import ApplicationRow
|
||||
from os import path, listdir
|
||||
from gettext import gettext as _
|
||||
|
||||
|
@ -10,10 +12,12 @@ class ApplicationChooserWindow(Gtk.Window):
|
|||
|
||||
def __init__(self, window):
|
||||
# directory that contains the main icons
|
||||
directory = window.parent.app.pkgdatadir + "/data/logos/"
|
||||
self.logos = listdir(directory)
|
||||
self.logos = listdir(DATA_DIR + "/data/applications/")
|
||||
self.logos.sort()
|
||||
self.parent = window
|
||||
|
||||
self.search_button = Gtk.ToggleButton()
|
||||
self.listbox = Gtk.ListBox()
|
||||
self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
self.generate_window()
|
||||
|
@ -25,7 +29,8 @@ class ApplicationChooserWindow(Gtk.Window):
|
|||
"""
|
||||
Generate the main window
|
||||
"""
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL, modal=True, destroy_with_parent=True)
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL, modal=True,
|
||||
destroy_with_parent=True)
|
||||
self.connect("destroy", self.close_window)
|
||||
self.resize(410, 550)
|
||||
self.set_size_request(410, 550)
|
||||
|
@ -47,7 +52,6 @@ class ApplicationChooserWindow(Gtk.Window):
|
|||
scrolled_win.add_with_viewport(box_outer)
|
||||
self.main_box.pack_start(scrolled_win, True, True, 0)
|
||||
|
||||
self.listbox = Gtk.ListBox()
|
||||
self.listbox.get_style_context().add_class("applications-list")
|
||||
self.listbox.set_adjustment()
|
||||
self.listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||
|
@ -56,27 +60,9 @@ class ApplicationChooserWindow(Gtk.Window):
|
|||
while i < len(self.logos):
|
||||
img_path = self.logos[i]
|
||||
app_name = path.splitext(img_path)[0].strip(".").title()
|
||||
row = Gtk.ListBoxRow()
|
||||
row.get_style_context().add_class("application-list-row")
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
# Application logo
|
||||
auth_icon = Authenticator.get_auth_icon(img_path,
|
||||
self.parent.parent.app.pkgdatadir)
|
||||
auth_img = Gtk.Image(xalign=0)
|
||||
auth_img.set_from_pixbuf(auth_icon)
|
||||
hbox.pack_start(auth_img, False, True, 6)
|
||||
|
||||
# Application name
|
||||
application_name = Gtk.Label(xalign=0)
|
||||
application_name.get_style_context().add_class("application-name")
|
||||
application_name.set_text(app_name)
|
||||
hbox.pack_start(application_name, True, True, 6)
|
||||
|
||||
vbox.pack_start(hbox, True, True, 6)
|
||||
row.add(vbox)
|
||||
self.listbox.add(row)
|
||||
app_logo = Authenticator.get_auth_icon(img_path)
|
||||
self.listbox.add(ApplicationRow(app_name, app_logo))
|
||||
i += 1
|
||||
|
||||
def generate_header_bar(self):
|
||||
|
@ -94,32 +80,31 @@ class ApplicationChooserWindow(Gtk.Window):
|
|||
cancel_button.get_style_context().add_class("destructive-action")
|
||||
left_box.add(cancel_button)
|
||||
|
||||
search_icon = Gio.ThemedIcon(name="system-search-symbolic")
|
||||
search_image = Gtk.Image.new_from_gicon(
|
||||
search_icon, Gtk.IconSize.BUTTON)
|
||||
self.search_button.set_tooltip_text(_("Search"))
|
||||
self.search_button.set_image(search_image)
|
||||
|
||||
next_button = Gtk.Button.new_with_label(_("Next"))
|
||||
next_button.get_style_context().add_class("suggested-action")
|
||||
next_button.connect("clicked", self.select_logo)
|
||||
right_box.add(next_button)
|
||||
|
||||
self.hb.pack_start(left_box)
|
||||
right_box.pack_start(self.search_button, False, False, 6)
|
||||
right_box.pack_start(next_button, False, False, 6)
|
||||
|
||||
self.hb.pack_end(right_box)
|
||||
self.hb.pack_start(left_box)
|
||||
self.set_titlebar(self.hb)
|
||||
|
||||
def generate_search_bar(self):
|
||||
"""
|
||||
Generate the search bar
|
||||
"""
|
||||
self.search_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.search_bar = SearchBar(self.listbox)
|
||||
self.search_button.connect("toggled", self.search_bar.toggle)
|
||||
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.search_entry = Gtk.Entry()
|
||||
self.search_entry.connect("changed", self.filter_logos)
|
||||
self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, "system-search-symbolic")
|
||||
|
||||
self.search_box.set_visible(False)
|
||||
self.search_box.set_no_show_all(True)
|
||||
box.pack_start(self.search_entry, False, True, 6)
|
||||
|
||||
self.search_box.pack_start(box, True, False, 6)
|
||||
self.main_box.pack_start(self.search_box, False, False, 6)
|
||||
self.main_box.pack_start(self.search_bar, False, True, 0)
|
||||
|
||||
def on_key_press(self, label, key_event):
|
||||
"""
|
||||
|
@ -127,62 +112,17 @@ class ApplicationChooserWindow(Gtk.Window):
|
|||
"""
|
||||
key_pressed = Gdk.keyval_name(key_event.keyval).lower()
|
||||
if key_pressed == "escape":
|
||||
if self.search_box.get_visible():
|
||||
self.toggle_search_box()
|
||||
if self.search_bar.is_visible():
|
||||
self.search_bar.toggle()
|
||||
else:
|
||||
self.close_window()
|
||||
elif key_pressed == "f":
|
||||
if key_event.state == Gdk.ModifierType.CONTROL_MASK:
|
||||
self.toggle_search_box()
|
||||
self.search_button.set_active(
|
||||
not self.search_button.get_active())
|
||||
elif key_pressed == "return":
|
||||
self.select_logo()
|
||||
|
||||
def filter_func(self, row, data, notify_destroy):
|
||||
"""
|
||||
Filter the result of research
|
||||
"""
|
||||
app_label = row.get_children()[0].get_children()[0].get_children()
|
||||
data = data.strip().lower()
|
||||
if len(data) > 0:
|
||||
return data in app_label[1].get_text().lower()
|
||||
else:
|
||||
return True
|
||||
|
||||
def filter_logos(self, entry):
|
||||
"""
|
||||
Filter logos
|
||||
"""
|
||||
data = entry.get_text()
|
||||
if len(data) != 0:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
||||
"edit-clear-symbolic")
|
||||
entry.connect("icon-press", self.on_icon_pressed)
|
||||
else:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
||||
None)
|
||||
self.listbox.set_filter_func(self.filter_func, data, False)
|
||||
|
||||
def on_icon_pressed(self, entry, icon_pos, event):
|
||||
"""
|
||||
Remove icon click event
|
||||
"""
|
||||
if icon_pos == Gtk.EntryIconPosition.SECONDARY:
|
||||
entry.set_text("")
|
||||
|
||||
def toggle_search_box(self):
|
||||
"""
|
||||
Toggle the visibility of the search box
|
||||
"""
|
||||
is_visible = self.search_box.get_no_show_all()
|
||||
self.search_box.set_no_show_all(not is_visible)
|
||||
self.search_box.set_visible(is_visible)
|
||||
self.search_box.show_all()
|
||||
if is_visible:
|
||||
self.search_entry.grab_focus_without_selecting()
|
||||
else:
|
||||
self.listbox.set_filter_func(
|
||||
lambda x, y, z: True, None, False)
|
||||
|
||||
def select_logo(self, *args):
|
||||
"""
|
||||
Select a logo and return its path to the add application window
|
||||
|
|
|
@ -25,7 +25,7 @@ class PasswordWindow(Gtk.Window):
|
|||
|
||||
def generate_window(self):
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL, title=_("Change password"),
|
||||
modal=True, destroy_with_parent=True)
|
||||
modal=True, destroy_with_parent=True)
|
||||
self.connect("delete-event", self.close_window)
|
||||
self.resize(300, 100)
|
||||
self.set_border_width(18)
|
||||
|
@ -92,7 +92,8 @@ class PasswordWindow(Gtk.Window):
|
|||
"""
|
||||
Update user password
|
||||
"""
|
||||
password = sha256(self.new_entry.get_text().encode("utf-8")).hexdigest()
|
||||
password = sha256(
|
||||
self.new_entry.get_text().encode("utf-8")).hexdigest()
|
||||
self.cfg.update("password", password, "login")
|
||||
logging.debug("Password changed successfully")
|
||||
self.close_window()
|
||||
|
@ -123,10 +124,13 @@ class PasswordWindow(Gtk.Window):
|
|||
else:
|
||||
old_is_ok = True
|
||||
if old_is_ok:
|
||||
self.old_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.old_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, None)
|
||||
if not are_diff:
|
||||
self.new_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.new2_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.new_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.new2_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.apply_button.set_sensitive(not are_diff and old_is_ok)
|
||||
|
||||
def generate_header_bar(self):
|
||||
|
|
|
@ -10,9 +10,10 @@ class ConfirmationMessage(Gtk.Window):
|
|||
def __init__(self, parent, message):
|
||||
try:
|
||||
self.dialog = Gtk.MessageDialog(
|
||||
parent=parent,
|
||||
flags=Gtk.DialogFlags.MODAL,
|
||||
message_format=message,
|
||||
transient_for=parent,
|
||||
modal=True,
|
||||
destroy_with_parent=True,
|
||||
text=message,
|
||||
buttons=Gtk.ButtonsType.OK_CANCEL)
|
||||
logging.debug("Confirmation message created successfully")
|
||||
except Exception as e:
|
||||
|
|
68
TwoFactorAuth/widgets/search_bar.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
from gi import require_version
|
||||
require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, Gio
|
||||
import logging
|
||||
|
||||
|
||||
class SearchBar(Gtk.Box):
|
||||
|
||||
def __init__(self, list_accounts):
|
||||
self.search_entry = Gtk.Entry()
|
||||
self.list_accounts = list_accounts
|
||||
self.generate()
|
||||
|
||||
def generate(self):
|
||||
Gtk.ListBoxRow.__init__(self, orientation=Gtk.Orientation.VERTICAL)
|
||||
self.revealer = Gtk.Revealer()
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
self.search_entry.set_width_chars(28)
|
||||
self.search_entry.connect("changed", self.filter_applications)
|
||||
self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,
|
||||
"system-search-symbolic")
|
||||
|
||||
box.pack_start(self.search_entry, True, False, 12)
|
||||
self.revealer.add(box)
|
||||
self.revealer.set_reveal_child(False)
|
||||
self.pack_start(self.revealer, True, False, 0)
|
||||
|
||||
def toggle(self, *args):
|
||||
if self.revealer.get_reveal_child():
|
||||
self.revealer.set_reveal_child(False)
|
||||
self.list_accounts.set_filter_func(lambda x, y, z: True,
|
||||
None, False)
|
||||
else:
|
||||
self.revealer.set_reveal_child(True)
|
||||
self.search_entry.grab_focus_without_selecting()
|
||||
|
||||
def filter_func(self, row, data, notify_destroy):
|
||||
"""
|
||||
Filter function, used to check if the entered data exists on the application ListBox
|
||||
"""
|
||||
app_label = row.get_name()
|
||||
data = data.lower()
|
||||
if len(data) > 0:
|
||||
return data in app_label.lower()
|
||||
else:
|
||||
return True
|
||||
|
||||
def is_visible(self):
|
||||
return self.revealer.get_reveal_child()
|
||||
|
||||
def is_empty(self):
|
||||
return len(self.search_entry.get_text()) == 0
|
||||
|
||||
def on_icon_pressed(self, entry, icon_pos, event):
|
||||
if icon_pos == Gtk.EntryIconPosition.SECONDARY:
|
||||
self.search_entry.set_text("")
|
||||
|
||||
def filter_applications(self, entry):
|
||||
data = entry.get_text().strip()
|
||||
if len(data) != 0:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
||||
"edit-clear-symbolic")
|
||||
entry.connect("icon-press", self.on_icon_pressed)
|
||||
else:
|
||||
entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.list_accounts.set_filter_func(self.filter_func, data, False)
|
|
@ -21,8 +21,8 @@ class SettingsWindow(Gtk.Window):
|
|||
self.generate_components()
|
||||
|
||||
def generate_window(self):
|
||||
Gtk.Window.__init__(self,title=_("Settings"),type=Gtk.WindowType.TOPLEVEL,
|
||||
destroy_with_parent=True, modal=True)
|
||||
Gtk.Window.__init__(self, title=_("Settings"), type=Gtk.WindowType.TOPLEVEL,
|
||||
destroy_with_parent=True, modal=True)
|
||||
self.connect("delete-event", self.close_window)
|
||||
self.resize(400, 300)
|
||||
self.set_size_request(400, 300)
|
||||
|
@ -48,11 +48,13 @@ class SettingsWindow(Gtk.Window):
|
|||
self.add(self.notebook)
|
||||
user_settings = self.generate_user_settings()
|
||||
user_settings.set_border_width(10)
|
||||
self.notebook.append_page(user_settings, Gtk.Label().new(_('Behavior')))
|
||||
self.notebook.append_page(
|
||||
user_settings, Gtk.Label().new(_('Behavior')))
|
||||
|
||||
login_settings = self.generate_login_settings()
|
||||
login_settings.set_border_width(10)
|
||||
self.notebook.append_page(login_settings, Gtk.Label().new(_('Account')))
|
||||
self.notebook.append_page(
|
||||
login_settings, Gtk.Label().new(_('Account')))
|
||||
|
||||
def generate_login_settings(self):
|
||||
"""
|
||||
|
@ -75,7 +77,7 @@ class SettingsWindow(Gtk.Window):
|
|||
self.password_button.set_sensitive(lock_enabled)
|
||||
|
||||
password_box.pack_start(self.enable_switch, False, True, 6)
|
||||
password_box.pack_start(password_label, False, True, 6)
|
||||
password_box.pack_start(password_label, False, True, 6)
|
||||
password_box.pack_start(self.password_button, False, True, 6)
|
||||
|
||||
main_box.pack_start(password_box, False, True, 6)
|
||||
|
@ -98,9 +100,10 @@ class SettingsWindow(Gtk.Window):
|
|||
default_value = self.cfg.read("auto-lock-time", "preferences")
|
||||
if default_value < 1 or default_value > 10:
|
||||
default_value = 3
|
||||
adjustment = Gtk.Adjustment(value=default_value, lower=1, upper=10,
|
||||
adjustment = Gtk.Adjustment(value=default_value, lower=1, upper=10,
|
||||
step_increment=1, page_increment=1, page_size=0)
|
||||
self.auto_lock_time.connect("value-changed", self.on_auto_lock_time_changed)
|
||||
self.auto_lock_time.connect(
|
||||
"value-changed", self.on_auto_lock_time_changed)
|
||||
self.auto_lock_time.set_adjustment(adjustment)
|
||||
self.auto_lock_time.set_sensitive(is_auto_lock_active)
|
||||
self.auto_lock_time.set_value(default_value)
|
||||
|
@ -114,7 +117,7 @@ class SettingsWindow(Gtk.Window):
|
|||
default_value = self.cfg.read("refresh-time", "preferences")
|
||||
if default_value < 30 or default_value > 120:
|
||||
default_value = 30
|
||||
adjustment = Gtk.Adjustment(value=default_value, lower=10, upper=120,
|
||||
adjustment = Gtk.Adjustment(value=default_value, lower=10, upper=120,
|
||||
step_increment=1, page_increment=10, page_size=0)
|
||||
self.time_spin_button.connect("value-changed", self.on_time_changed)
|
||||
self.time_spin_button.set_adjustment(adjustment)
|
||||
|
@ -138,13 +141,15 @@ class SettingsWindow(Gtk.Window):
|
|||
"""
|
||||
Update time tog generate a new secret code
|
||||
"""
|
||||
self.cfg.update("refresh-time", spin_button.get_value_as_int(), "preferences")
|
||||
self.cfg.update(
|
||||
"refresh-time", spin_button.get_value_as_int(), "preferences")
|
||||
|
||||
def on_auto_lock_time_changed(self, spin_button):
|
||||
"""
|
||||
Update time tog generate a new secret code
|
||||
"""
|
||||
self.cfg.update("auto-lock-time", spin_button.get_value_as_int(), "preferences")
|
||||
self.cfg.update("auto-lock-time",
|
||||
spin_button.get_value_as_int(), "preferences")
|
||||
|
||||
def on_switch_activated(self, switch, *args):
|
||||
"""
|
||||
|
@ -170,4 +175,4 @@ class SettingsWindow(Gtk.Window):
|
|||
"""
|
||||
Close the window
|
||||
"""
|
||||
self.destroy()
|
||||
self.destroy()
|
||||
|
|
|
@ -3,7 +3,8 @@ require_version("Gtk", "3.0")
|
|||
from gi.repository import Gtk, Gio, Gdk, GObject, GLib
|
||||
from TwoFactorAuth.widgets.add_account import AddAccount
|
||||
from TwoFactorAuth.widgets.confirmation import ConfirmationMessage
|
||||
from TwoFactorAuth.widgets.listrow import ListBoxRow
|
||||
from TwoFactorAuth.widgets.account_row import AccountRow
|
||||
from TwoFactorAuth.widgets.search_bar import SearchBar
|
||||
import logging
|
||||
from hashlib import sha256
|
||||
from gettext import gettext as _
|
||||
|
@ -17,7 +18,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
|
||||
hb = Gtk.HeaderBar()
|
||||
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
search_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
login_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
no_apps_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||
apps_list_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||
|
@ -38,7 +38,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
settings_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
pop_settings = Gtk.ModelButton.new()
|
||||
password_entry = Gtk.Entry()
|
||||
search_entry = Gtk.Entry()
|
||||
|
||||
def __init__(self, application):
|
||||
self.app = application
|
||||
|
@ -58,8 +57,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
Gtk.ApplicationWindow.__init__(self, type=Gtk.WindowType.TOPLEVEL,
|
||||
application=self.app)
|
||||
self.move_latest_position()
|
||||
self.set_wmclass(self.app.package, "Gnome TwoFactorAuth")
|
||||
self.set_icon_name(self.app.package)
|
||||
self.set_wmclass("Gnome-TwoFactorAuth", "Gnome TwoFactorAuth")
|
||||
self.set_icon_name("Gnome-TwoFactorAuth")
|
||||
self.resize(410, 550)
|
||||
self.set_size_request(410, 550)
|
||||
self.set_resizable(False)
|
||||
|
@ -80,8 +79,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.copy_code()
|
||||
elif keypress == "f":
|
||||
if key_event.state == control_mask:
|
||||
self.search_button.set_active(not self.search_button.get_active())
|
||||
|
||||
self.search_button.set_active(
|
||||
not self.search_button.get_active())
|
||||
elif keypress == "s":
|
||||
if key_event.state == control_mask:
|
||||
self.toggle_select()
|
||||
|
@ -98,13 +97,20 @@ class Window(Gtk.ApplicationWindow):
|
|||
index = 0
|
||||
self.list_box.get_row_at_index(index).toggle_code_box()
|
||||
elif keypress == "backspace":
|
||||
if len(self.search_entry.get_text()) == 0:
|
||||
if self.search_bar.is_empty():
|
||||
self.search_button.set_active(False)
|
||||
elif keypress == "escape":
|
||||
if self.search_box.get_visible():
|
||||
if self.search_bar.is_visible():
|
||||
self.search_button.set_active(False)
|
||||
if not self.select_button.get_visible():
|
||||
self.toggle_select()
|
||||
elif keypress == "up" or keypress == "down":
|
||||
dx = -1 if keypress == "up" else 1
|
||||
if count != 0:
|
||||
index = self.list_box.get_selected_row().get_index()
|
||||
index = (index + dx)%count
|
||||
selected_row = self.list_box.get_row_at_index(index)
|
||||
self.list_box.select_row(selected_row)
|
||||
else:
|
||||
if keypress == "return":
|
||||
self.on_unlock_clicked()
|
||||
|
@ -121,34 +127,14 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.toggle_app_lock()
|
||||
return True
|
||||
|
||||
def filter_applications(self, entry):
|
||||
data = entry.get_text().strip()
|
||||
if len(data) != 0:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "edit-clear-symbolic")
|
||||
entry.connect("icon-press", self.on_icon_pressed)
|
||||
else:
|
||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.list_box.set_filter_func(self.filter_func, data, False)
|
||||
|
||||
|
||||
def on_icon_pressed(self, entry, icon_pos, event):
|
||||
if icon_pos == Gtk.EntryIconPosition.SECONDARY:
|
||||
self.search_entry.set_text("")
|
||||
|
||||
def generate_search_bar(self):
|
||||
"""
|
||||
Generate search bar box and entry
|
||||
"""
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.search_bar = SearchBar(self.list_box)
|
||||
self.search_button.connect("toggled", self.search_bar.toggle)
|
||||
|
||||
self.search_entry.set_width_chars(28)
|
||||
self.search_entry.connect("changed", self.filter_applications)
|
||||
self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, "system-search-symbolic")
|
||||
|
||||
box.pack_start(self.search_entry, False, True, 0)
|
||||
self.search_box.pack_start(box, True, False, 6)
|
||||
self.search_box.set_no_show_all(True)
|
||||
self.apps_box.pack_start(self.search_box, False, True, 6)
|
||||
self.apps_box.pack_start(self.search_bar, False, True, 0)
|
||||
self.main_box.pack_start(self.apps_box, True, True, 0)
|
||||
|
||||
def remove_selected(self, *args):
|
||||
|
@ -198,11 +184,13 @@ class Window(Gtk.ApplicationWindow):
|
|||
ecrypted_pass = sha256(typed_pass.encode("utf-8")).hexdigest()
|
||||
login_pass = self.app.cfg.read("password", "login")
|
||||
if ecrypted_pass == login_pass or login_pass == typed_pass:
|
||||
self.password_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.password_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, None)
|
||||
self.toggle_app_lock()
|
||||
self.password_entry.set_text("")
|
||||
else:
|
||||
self.password_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "dialog-error-symbolic")
|
||||
self.password_entry.set_icon_from_icon_name(
|
||||
Gtk.EntryIconPosition.SECONDARY, "dialog-error-symbolic")
|
||||
|
||||
def toggle_app_lock(self):
|
||||
"""
|
||||
|
@ -237,14 +225,15 @@ class Window(Gtk.ApplicationWindow):
|
|||
"""
|
||||
Generate a header bar box
|
||||
"""
|
||||
count = self.app.auth.count()
|
||||
count = self.app.auth.count()
|
||||
self.hb.set_show_close_button(True)
|
||||
|
||||
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
remove_icon = Gio.ThemedIcon(name="user-trash-symbolic")
|
||||
remove_image = Gtk.Image.new_from_gicon(remove_icon, Gtk.IconSize.BUTTON)
|
||||
remove_image = Gtk.Image.new_from_gicon(
|
||||
remove_icon, Gtk.IconSize.BUTTON)
|
||||
self.remove_button.set_tooltip_text(_("Remove selected applications"))
|
||||
self.remove_button.set_image(remove_image)
|
||||
self.remove_button.set_sensitive(False)
|
||||
|
@ -257,7 +246,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.add_button.set_image(add_image)
|
||||
self.add_button.connect("clicked", self.add_application)
|
||||
|
||||
|
||||
pass_enabled = self.app.cfg.read("state", "login")
|
||||
can_be_locked = not self.app.locked and pass_enabled
|
||||
lock_icon = Gio.ThemedIcon(name="changes-prevent-symbolic")
|
||||
|
@ -272,7 +260,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
left_box.add(self.lock_button)
|
||||
|
||||
select_icon = Gio.ThemedIcon(name="object-select-symbolic")
|
||||
select_image = Gtk.Image.new_from_gicon(select_icon, Gtk.IconSize.BUTTON)
|
||||
select_image = Gtk.Image.new_from_gicon(
|
||||
select_icon, Gtk.IconSize.BUTTON)
|
||||
self.select_button.set_tooltip_text(_("Selection mode"))
|
||||
self.select_button.set_image(select_image)
|
||||
self.select_button.connect("clicked", self.toggle_select)
|
||||
|
@ -280,10 +269,10 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.select_button.set_visible(count > 0)
|
||||
|
||||
search_icon = Gio.ThemedIcon(name="system-search-symbolic")
|
||||
search_image = Gtk.Image.new_from_gicon(search_icon, Gtk.IconSize.BUTTON)
|
||||
search_image = Gtk.Image.new_from_gicon(
|
||||
search_icon, Gtk.IconSize.BUTTON)
|
||||
self.search_button.set_tooltip_text(_("Search"))
|
||||
self.search_button.set_image(search_image)
|
||||
self.search_button.connect("toggled", self.toggle_search_box)
|
||||
self.search_button.set_no_show_all(not count > 0)
|
||||
self.search_button.set_visible(count > 0)
|
||||
|
||||
|
@ -291,7 +280,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.cancel_button.connect("clicked", self.toggle_select)
|
||||
self.cancel_button.set_no_show_all(True)
|
||||
|
||||
|
||||
right_box.add(self.search_button)
|
||||
right_box.add(self.select_button)
|
||||
right_box.add(self.cancel_button)
|
||||
|
@ -305,12 +293,14 @@ class Window(Gtk.ApplicationWindow):
|
|||
|
||||
def generate_popover(self, box):
|
||||
settings_icon = Gio.ThemedIcon(name="open-menu-symbolic")
|
||||
settings_image = Gtk.Image.new_from_gicon(settings_icon, Gtk.IconSize.BUTTON)
|
||||
settings_image = Gtk.Image.new_from_gicon(
|
||||
settings_icon, Gtk.IconSize.BUTTON)
|
||||
self.settings_button.set_tooltip_text(_("Settings"))
|
||||
self.settings_button.set_image(settings_image)
|
||||
self.settings_button.connect("clicked", self.toggle_popover)
|
||||
|
||||
self.popover = Gtk.Popover.new_from_model(self.settings_button, self.app.menu)
|
||||
self.popover = Gtk.Popover.new_from_model(
|
||||
self.settings_button, self.app.menu)
|
||||
self.popover.props.width_request = 200
|
||||
box.add(self.settings_button)
|
||||
|
||||
|
@ -328,22 +318,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
add_account = AddAccount(self)
|
||||
add_account.show_window()
|
||||
|
||||
def toggle_search_box(self, *args):
|
||||
"""
|
||||
Toggle search box, only if there's an application
|
||||
"""
|
||||
if self.app.auth.count() > 0:
|
||||
is_visible = self.search_box.get_no_show_all()
|
||||
self.search_box.set_no_show_all(not is_visible)
|
||||
self.search_box.set_visible(is_visible)
|
||||
self.search_box.show_all()
|
||||
self.search_button.get_style_context().remove_class("toggled")
|
||||
if is_visible:
|
||||
self.search_entry.grab_focus_without_selecting()
|
||||
else:
|
||||
self.list_box.set_filter_func(lambda x, y, z: True, None, False)
|
||||
|
||||
|
||||
def toggle_select(self, *args):
|
||||
"""
|
||||
Toggle select mode
|
||||
|
@ -360,7 +334,7 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.settings_button.set_visible(is_visible)
|
||||
|
||||
pass_enabled = self.app.cfg.read("state", "login")
|
||||
self.lock_button.set_visible( is_visible and pass_enabled)
|
||||
self.lock_button.set_visible(is_visible and pass_enabled)
|
||||
self.add_button.set_visible(is_visible)
|
||||
self.select_button.set_visible(is_visible)
|
||||
|
||||
|
@ -387,7 +361,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.select_application(checkbox)
|
||||
code_label.get_style_context().add_class("application-secret-code-select-mode")
|
||||
else:
|
||||
code_label.get_style_context().remove_class("application-secret-code-select-mode")
|
||||
code_label.get_style_context().remove_class(
|
||||
"application-secret-code-select-mode")
|
||||
|
||||
checkbox.set_visible(not visible)
|
||||
checkbox.set_no_show_all(visible)
|
||||
|
@ -410,17 +385,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
self.selected_count -= 1
|
||||
self.remove_button.set_sensitive(self.selected_count > 0)
|
||||
|
||||
def filter_func(self, row, data, notify_destroy):
|
||||
"""
|
||||
Filter function, used to check if the entered data exists on the application ListBox
|
||||
"""
|
||||
app_label = row.get_label()
|
||||
data = data.lower()
|
||||
if len(data) > 0:
|
||||
return data in app_label.lower()
|
||||
else:
|
||||
return True
|
||||
|
||||
def select_row(self, list_box, listbox_row):
|
||||
"""
|
||||
Select row @override the clicked event by default for ListBoxRow
|
||||
|
@ -432,7 +396,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
checkbox.set_active(not checkbox.get_active())
|
||||
else:
|
||||
if self.selected_app_idx:
|
||||
selected_row = self.list_box.get_row_at_index(self.selected_app_idx)
|
||||
selected_row = self.list_box.get_row_at_index(
|
||||
self.selected_app_idx)
|
||||
if selected_row:
|
||||
self.list_box.unselect_row(selected_row)
|
||||
self.selected_app_idx = index
|
||||
|
@ -459,8 +424,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
i = 0
|
||||
count = len(apps)
|
||||
while i < count:
|
||||
self.list_box.add(ListBoxRow(self, apps[i][0], apps[i][1], apps[i][2],
|
||||
apps[i][3]))
|
||||
self.list_box.add(AccountRow(self, apps[i][0], apps[i][1], apps[i][2],
|
||||
apps[i][3]))
|
||||
i += 1
|
||||
|
||||
def generate_no_apps_box(self):
|
||||
|
@ -487,7 +452,7 @@ class Window(Gtk.ApplicationWindow):
|
|||
:param image: application image path or icon name
|
||||
"""
|
||||
secret_code = sha256(secret_code.encode('utf-8')).hexdigest()
|
||||
self.list_box.add(ListBoxRow(self, uid, name, secret_code, image))
|
||||
self.list_box.add(AccountRow(self, uid, name, secret_code, image))
|
||||
self.list_box.show_all()
|
||||
|
||||
def copy_code(self, *args):
|
||||
|
@ -495,7 +460,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
Copy the secret code to clipboard
|
||||
"""
|
||||
if len(args) > 0:
|
||||
# if the code is called by clicking on copy button, select the right ListBowRow
|
||||
# if the code is called by clicking on copy button, select the
|
||||
# right ListBowRow
|
||||
row = args[0].get_parent().get_parent().get_parent()
|
||||
self.list_box.select_row(row)
|
||||
selected_row = self.list_box.get_selected_row()
|
||||
|
@ -522,10 +488,12 @@ class Window(Gtk.ApplicationWindow):
|
|||
else:
|
||||
if count == 0:
|
||||
self.toggle_boxes(False, True, False)
|
||||
self.toggle_hb_buttons(False, True, False, False, False, True, can_be_locked)
|
||||
self.toggle_hb_buttons(
|
||||
False, True, False, False, False, True, can_be_locked)
|
||||
else:
|
||||
self.toggle_boxes(True, False, False)
|
||||
self.toggle_hb_buttons(False, True, True, True, False, True, can_be_locked)
|
||||
self.toggle_hb_buttons(
|
||||
False, True, True, True, False, True, can_be_locked)
|
||||
|
||||
self.pop_settings.set_sensitive(not is_locked)
|
||||
self.main_box.show_all()
|
||||
|
@ -604,7 +572,7 @@ class Window(Gtk.ApplicationWindow):
|
|||
Shows about dialog
|
||||
"""
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(self.app.pkgdatadir + "/data/about.ui")
|
||||
builder.add_from_resource('/org/gnome/TwoFactorAuth/about.ui')
|
||||
|
||||
dialog = builder.get_object("AboutDialog")
|
||||
dialog.set_transient_for(self)
|
||||
|
@ -617,8 +585,8 @@ class Window(Gtk.ApplicationWindow):
|
|||
"""
|
||||
if Gtk.get_major_version() >= 3 and Gtk.get_minor_version() >= 20:
|
||||
builder = Gtk.Builder()
|
||||
builder.add_from_file(self.app.pkgdatadir + "/data/shortcuts.ui")
|
||||
|
||||
builder.add_from_resource('/org/gnome/TwoFactorAuth/shortcuts.ui')
|
||||
# builder.add_from_file(DATA_DIR + "/data/shortcuts.ui")
|
||||
shortcuts = builder.get_object("shortcuts")
|
||||
shortcuts.set_transient_for(self)
|
||||
shortcuts.show()
|
||||
|
|
|
@ -27,7 +27,7 @@ AC_SUBST(GLIB_COMPILE_RESOURCES)
|
|||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
data/Makefile
|
||||
data/logos/Makefile
|
||||
data/applications/Makefile
|
||||
data/gnome-twofactorauth.desktop
|
||||
TwoFactorAuth/Makefile
|
||||
TwoFactorAuth/models/Makefile
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
SUBDIRS = logos
|
||||
resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate-dependencies $(builddir)/gnome-twofactorauth.gresource.xml)
|
||||
gnome-twofactorauth.gresource: gnome-twofactorauth.gresource.xml $(resource_files)
|
||||
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) $<
|
||||
|
||||
resourcedir = $(pkgdatadir)
|
||||
resource_DATA = gnome-twofactorauth.gresource
|
||||
|
||||
SUBDIRS = applications
|
||||
|
||||
desktopdir = $(datadir)/applications
|
||||
desktop_DATA = gnome-twofactorauth.desktop
|
||||
|
||||
uidir = $(pkgdatadir)/data
|
||||
ui_DATA = about.ui \
|
||||
shortcuts.ui \
|
||||
style.css
|
||||
|
||||
appdatadir = $(pkgdatadir)/appdata
|
||||
appdata_DATA = \
|
||||
gnome-twofactorauth.appdata.xml
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(ui_DATA) \
|
||||
$(resource_files) \
|
||||
$(desktop_DATA) \
|
||||
$(appdata_DATA)
|
||||
|
||||
CLEANFILES = \
|
||||
$(appdata_DATA) \
|
||||
$(desktop_DATA)
|
||||
$(ui_DATA)
|
||||
$(desktop_DATA) \
|
||||
$(resource_files) \
|
||||
|
||||
install-data-hook:
|
||||
$(UPDATE_DESKTOP)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
logosdir = $(pkgdatadir)/data/logos
|
||||
logos_DATA = amazon.png \
|
||||
applicationsdir = $(pkgdatadir)/applications
|
||||
applications_DATA = amazon.png \
|
||||
dropbox.png \
|
||||
facebook.png \
|
||||
flickr.png \
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 1,001 B After Width: | Height: | Size: 1,001 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 668 B After Width: | Height: | Size: 668 B |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 626 B After Width: | Height: | Size: 626 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -1,11 +1,12 @@
|
|||
[Desktop Entry]
|
||||
_Name=Gnome TwoFactorAuth
|
||||
_GenericName=Two Factor Authentication
|
||||
_Comment=Two-Factor Authentication code generator
|
||||
Name=Gnome TwoFactorAuth
|
||||
GenericName=Two Factor Authentication
|
||||
Comment=Two-Factor Authentication code generator
|
||||
Type=Application
|
||||
Exec=gnome-twofactorauth
|
||||
Terminal=false
|
||||
Categories=GNOME;GTK;
|
||||
_Keywords=Gnome;GTK;Verification;
|
||||
Keywords=Gnome;GTK;Verification;
|
||||
Icon=gnome-twofactorauth
|
||||
StartupNotify=true
|
||||
StartupWMClass=Gnome-TwoFactorAuth
|
||||
|
|
8
data/gnome-twofactorauth.gresource.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/gnome/TwoFactorAuth">
|
||||
<file>style.css</file>
|
||||
<file preprocess="xml-stripblanks">about.ui</file>
|
||||
<file preprocess="xml-stripblanks">shortcuts.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
|
@ -6,21 +6,21 @@
|
|||
}
|
||||
.application-name {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.application-logo-add {
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.application-secret-code {
|
||||
font-size: 10px;
|
||||
.account-secret-code,
|
||||
.account-timer {
|
||||
font-size: 13px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 65px;
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.application-secret-code-select-mode{
|
||||
margin-left: 105px;
|
||||
.application-secret-code-select-mode {
|
||||
margin-left: 100px;
|
||||
}
|
||||
.choose-popover GtkLabel {
|
||||
padding: 10px;
|
||||
|
|
|
@ -30,6 +30,7 @@ import argparse
|
|||
import faulthandler
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
locale.bindtextdomain('TwoFactorAuth', "@localedir@")
|
||||
locale.textdomain('TwoFactorAuth')
|
||||
gettext.bindtextdomain('TwoFactorAuth', "@localedir@")
|
||||
|
@ -46,14 +47,14 @@ if __name__ == "__main__":
|
|||
if args.debug:
|
||||
level = logging.DEBUG
|
||||
faulthandler.enable()
|
||||
|
||||
|
||||
logging.basicConfig(level=level,
|
||||
format='[%(levelname)s] %(message)s',
|
||||
)
|
||||
if args.version:
|
||||
sys.exit("Version : @VERSION@")
|
||||
app = application.Application(package="@PACKAGE@",
|
||||
version="@VERSION@",
|
||||
version="@VERSION@"
|
||||
pkgdatadir="@pkgdatadir@")
|
||||
exit_status = app.run(None)
|
||||
sys.exit(exit_status)
|
||||
|
|