def from_file(filename: str): decoded_data = decode(Image.open(filename)) if path.isfile(filename): remove(filename) try: # See https://github.com/google/google-authenticator/wiki/Key-Uri-Format # for a description of the URL format url = urlparse(decoded_data[0].data.decode()) query_params = parse_qsl(url.query) url_data = dict(query_params) username = None label = unquote(url.path.lstrip("/")) if ":" in label: provider, username = label.split(":", maxsplit=1) else: provider = label # provider information could also be in the query params provider = url_data.get("issuer", provider) token = url_data.get("secret") assert OTP.is_valid(token) return { 'username': username, 'provider': provider, 'token': token } except (KeyError, IndexError): Logger.error("Invalid QR image")
def search_accounts(self, terms): if terms: filters = " ".join(terms) if filters: filters = "%" + filters + "%" query = """ SELECT A.* FROM accounts A JOIN providers P ON A.provider = P.id WHERE A.username LIKE ? OR P.name LIKE ? GROUP BY provider ORDER BY A.username ASC """ try: data = self.conn.cursor().execute(query, ( filters, filters, )) accounts = data.fetchall() return [Account(*account) for account in accounts] except Exception as error: Logger.error("[SQL]: Couldn't search for an account") Logger.error(str(error)) return []
def __count(self, table_name: str) -> int: query = "SELECT COUNT(id) AS count FROM " + table_name try: data = self.conn.cursor().execute(query) return data.fetchone()[0] except Exception as error: Logger.error("[SQL]: Couldn't count the results from " + table_name) Logger.error(str(error)) return None
def import_accounts(accounts: [dict]): accounts_widget = AccountsWidget.get_default() accounts_manager = AccountsManager.get_default() for account in accounts: try: new_account = Account.create_from_json(account) accounts_manager.add(new_account.provider, new_account) accounts_widget.append(new_account) except Exception as e: Logger.error("[Restore] Failed to import accounts") Logger.error(str(e))
def get_providers(self, **kwargs): only_used = kwargs.get("only_used", ) query = "SELECT * FROM providers" if only_used: query += " WHERE id IN (SELECT DISTINCT provider FROM accounts)" try: data = self.conn.cursor().execute(query) providers = data.fetchall() return [Provider(*provider) for provider in providers] except Exception as error: Logger.error("[SQL] Couldn't fetch providers list") Logger.error(str(error)) return None
def account_by_id(self, id_): """ Get an account by the ID :param id_: int the account id :return: Account: The account data """ query = "SELECT * FROM accounts WHERE id=?" try: data = self.conn.cursor().execute(query, (id_, )) return Account(*data.fetchone()) except Exception as error: Logger.error("[SQL] Couldn't get account with ID={}".format(id_)) Logger.error(str(error)) return None
def __delete(self, table_name, id_): """ Remove a row by ID. :param id_: the row ID :type id_: int """ query = "DELETE FROM {} WHERE id=?".format(table_name) try: self.conn.execute(query, (id_, )) self.conn.commit() except Exception as error: Logger.error("[SQL] Couldn't remove the row '{}'".format(id_)) Logger.error(str(error))
def provider_by_id(self, id_): """ Get a provider by the ID :param id_: int the provider id :return: Provider: The provider data """ query = "SELECT * FROM providers WHERE id=?" try: data = self.conn.cursor().execute(query, (id_, )) return Provider(*data.fetchone()) except Exception as error: Logger.error("[SQL] Couldn't get provider with ID={}".format(id_)) Logger.error(str(error)) return None
def accounts(self): """ Retrieve the list of accounts. :return list """ query = "SELECT * FROM accounts" try: data = self.conn.cursor().execute(query) accounts = data.fetchall() return [Account(*account) for account in accounts] except Exception as error: Logger.error("[SQL] Couldn't fetch accounts list") Logger.error(str(error)) return None
def insert_account(self, username, token_id, provider): """ Insert a new account to the database :param username: Account name :param token_id: The token identifier stored using libsecret :param provider: The provider foreign key """ query = "INSERT INTO accounts (username, token_id, provider) VALUES (?, ?, ?)" cursor = self.conn.cursor() try: cursor.execute(query, [username, token_id, provider]) self.conn.commit() return Account(cursor.lastrowid, username, token_id, provider) except Exception as error: Logger.error("[SQL] Couldn't add a new account") Logger.error(str(error))
def provider_by_name(self, provider_name: str) -> Provider: """ Get a provider by the ID :param id_: int the provider id :return: Provider: The provider data """ query = "SELECT * FROM providers WHERE name LIKE ? " try: data = self.conn.cursor().execute(query, (provider_name, )) provider = data.fetchone() return Provider(*provider) if provider else None except Exception as error: Logger.error("[SQL] Couldn't get provider with name={}".format( provider_name)) Logger.error(str(error)) return None
def __init__(self, _id, username, token_id, provider): GObject.GObject.__init__(self) self.id = _id self.username = username self.provider = provider self._token_id = token_id token = Keyring.get_default().get_by_id(self._token_id) self.connect("otp_out_of_date", self._on_otp_out_of_date) if token: self.otp = OTP(token) self._code_generated = True else: self.otp = None self._code_generated = False Logger.error("Could not read the secret code," "the keyring keys were reset manually")
def insert_provider(self, name, website, doc_url=None, image=None): """ Insert a new provider to the database :param name: The provider name :param website: The provider website, used to extract favicon :param doc_url: The provider doc_url, used to allow user get the docs :param image: The image path of a provider """ query = "INSERT INTO providers (name, website, doc_url, image) VALUES (?, ?, ?, ?)" cursor = self.conn.cursor() try: cursor.execute(query, [name, website, doc_url, image]) self.conn.commit() return Provider(cursor.lastrowid, name, website, doc_url, image) except Exception as error: Logger.error("[SQL] Couldn't add a new account") Logger.error(str(error))
def __update_by_id(self, table_name, data, id_): query = "UPDATE {} SET ".format(table_name) resources = [] i = 0 for table_column, value in data.items(): query += " {}=?".format(table_column) if i != len(data) - 1: query += "," resources.append(value) i += 1 resources.append(id_) query += "WHERE id=?" try: self.conn.execute(query, resources) self.conn.commit() except Exception as error: Logger.error("[SQL] Couldn't update row by id") Logger.error(error)
def read(self): decoded_data = decode(Image.open(self.filename)) if path.isfile(self.filename): remove(self.filename) try: # See https://github.com/google/google-authenticator/wiki/Key-Uri-Format # for a description of the URL format url = urlparse(decoded_data[0].data.decode()) query_params = parse_qsl(url.query) self._codes = dict(query_params) label = unquote(url.path.lstrip("/")) if ":" in label: self.provider, self.username = label.split(":", maxsplit=1) else: self.provider = label # provider information could also be in the query params self.provider = self._codes.get("issuer", self.provider) return self._codes.get("secret") except (KeyError, IndexError): Logger.error("Invalid QR image") return None
Authenticator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Authenticator. If not, see <http://www.gnu.org/licenses/>. """ import binascii from Authenticator.models import Logger try: from pyotp import TOTP except ImportError: Logger.error("Impossible to import TOTP, please install PyOTP first") class OTP(TOTP): """ OTP (One-time password) handler using PyOTP. """ def __init__(self, token): """ :param token: the OTP token. """ TOTP.__init__(self, token) self.pin = None self.update()