-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Google Authenticator desktop GUI and script application in Python with JSON secrets.
- Loading branch information
Showing
4 changed files
with
294 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ "atomjoy_github": "JBSWY3DPEHPK3PXP", "username_github": "A7SWY3DPEHPK3PXD" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#!/usr/bin/python3 | ||
|
||
import pyotp, time, json, os, shutil, base64 | ||
|
||
|
||
class JsonFile: | ||
data = [] | ||
data_code = [] | ||
filename = "" | ||
|
||
def __init__(self, filename="secrets.json"): | ||
self.data = [] | ||
self.data_code = [] | ||
self.filename = filename | ||
self.__loadJson() | ||
self.__updateCode() | ||
|
||
def getAll(self): | ||
return self.data_code | ||
|
||
def getAllData(self): | ||
return self.data | ||
|
||
def loadJson(self): | ||
try: | ||
rel = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) | ||
with open(os.path.join(rel, self.filename), "r") as f: | ||
secrets = json.load(f) | ||
self.data = list(secrets.items()) | ||
print("Loaded", self.data) | ||
except (ImportError, Exception): | ||
print("Load error") | ||
|
||
def addItem(self, name, secret): | ||
if len(name) >= 3: | ||
if len(secret) >= 16: | ||
if self.isBase32(secret): | ||
item = tuple([name, secret]) | ||
self.data.append(item) | ||
self.__updateCode() | ||
self.saveJson() | ||
print("Appended", self.data) | ||
|
||
def updateCode(self): | ||
self.data_code = [] | ||
for tuple_val in self.data: | ||
item = list(tuple_val) | ||
item.append(self.otpCode(str(item[1]))) | ||
self.data_code.append(tuple(item)) | ||
|
||
def otpCode(self, secret): | ||
try: | ||
return str(pyotp.TOTP(secret).now()) | ||
except Exception: | ||
print("Otp code error") | ||
return str("000000") | ||
|
||
def saveJson(self): | ||
json_obj = {} | ||
for key, secret in self.data: | ||
json_obj[key] = secret | ||
try: | ||
self.backupFile() | ||
rel = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) | ||
with open(os.path.join(rel, self.filename), "w") as outfile: | ||
json.dump(json_obj, outfile) | ||
print("Saved json", json_obj) | ||
except (ImportError, Exception): | ||
print("Save error") | ||
|
||
def backupFile(self): | ||
tm = str(time.time()).replace(".", "_") | ||
shutil.copy(self.filename, "backup/secrets_copy_" + tm + ".json") | ||
|
||
def isBase32(self, str): | ||
try: | ||
base64.b32decode(str) | ||
return True | ||
except Exception: | ||
print("Invalid base32:", str) | ||
return False | ||
|
||
def removeItem(self, name): | ||
if len(name) >= 3: | ||
res = [i for i in self.data if i[0] != name] | ||
self.data = res | ||
self.saveJson() | ||
print(res) | ||
|
||
__loadJson = loadJson # private copy of original update() method | ||
__updateCode = updateCode # private copy of original update() method |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
#!/usr/bin/python3 | ||
|
||
import pyotp, time, json, os, shutil | ||
import tkinter | ||
from tkinter import ttk | ||
from tkinter import messagebox | ||
from json_file import JsonFile | ||
|
||
jf = JsonFile("secrets.json") | ||
jf.addItem("benny_github", "JBSWY3DPEHPK3PX3") | ||
jf.saveJson() | ||
print("Json file", jf.getAll()) | ||
|
||
window = tkinter.Tk() | ||
window.title("2FA Google Authenticator") | ||
# window.geometry('300x200') | ||
# window.resizable(False, False) | ||
|
||
fr = tkinter.Frame(window) | ||
fr.pack() | ||
|
||
|
||
# Callbacks | ||
def load_data(): | ||
rel = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) | ||
with open(os.path.join(rel, "secrets.json"), "r") as f: | ||
secrets = json.load(f) | ||
items = list(secrets.items()) | ||
code_items = [] | ||
for tuple_val in items: | ||
item = list(tuple_val) | ||
item.append(pyotp.TOTP(item[1]).now()) | ||
code_items.append(tuple(item)) | ||
print(code_items) | ||
return code_items | ||
|
||
|
||
def selectItem(event): | ||
tree = event.widget | ||
current_item = tree.focus() | ||
item = tree.item(current_item) | ||
print("Item", item) | ||
if hasattr(item, "values"): | ||
try: | ||
messagebox.showinfo("Current code", f'{item["values"][2]}') | ||
print("Id:", current_item, "Item:", item, "Code:", item["values"][2]) | ||
except IndexError: | ||
print("") | ||
|
||
|
||
def update(): | ||
t = time.strftime("%I:%M:%S", time.localtime()) | ||
treeview.delete(*treeview.get_children()) | ||
i = 0 | ||
for item_tuple in load_data(): | ||
treeview.insert("", tkinter.END, id=i, values=item_tuple) | ||
i = i + 1 | ||
window.after(30000, update) | ||
print("Update", t) | ||
|
||
|
||
# Show user codes | ||
fr_codes = tkinter.LabelFrame(fr, text="2FA Codes", font="times 21") | ||
fr_codes.grid(row=0, column=0) | ||
|
||
treeFrame = ttk.Frame(fr_codes) | ||
treeFrame.grid(row=0, column=0, pady=10) | ||
treeScroll = ttk.Scrollbar(treeFrame) | ||
treeScroll.pack(side="right", fill="y") | ||
|
||
cols = ("Name", "Secret", "Code") | ||
treeview = ttk.Treeview( | ||
treeFrame, | ||
show="headings", | ||
yscrollcommand=treeScroll.set, | ||
columns=cols, | ||
height=13, | ||
selectmode="browse", | ||
) | ||
treeview.column("Name", width=250) | ||
treeview.bind("<<TreeviewSelect>>", selectItem) | ||
# treeview.bind('<ButtonRelease-1>', selectItem) | ||
|
||
for col_name in cols: | ||
treeview.heading(col_name, text=col_name) | ||
|
||
i = 0 | ||
|
||
for item_tuple in load_data(): | ||
treeview.insert("", tkinter.END, id=i, values=item_tuple) | ||
i = i + 1 | ||
|
||
treeview.pack() | ||
treeScroll.config(command=treeview.yview) | ||
|
||
# # Saving user secret callbacks | ||
# def callback_button(): | ||
# name = input_name.get() | ||
# secret = input_secret.get() | ||
# print('Name', name, 'Secret', secret) | ||
# | ||
# # Saving user secret | ||
# fr_add_secret = tkinter.LabelFrame(fr, text="Add secret", font='times 21') | ||
# fr_add_secret.grid(row=1, column=0) | ||
|
||
# label_name = tkinter.Label(fr_add_secret, text="App name") | ||
# label_name.grid(row=0, column=0) | ||
|
||
# label_secret = tkinter.Label(fr_add_secret, text="App secret") | ||
# label_secret.grid(row=0, column=1) | ||
|
||
# input_name = tkinter.Entry(fr_add_secret) | ||
# input_name.grid(row=1, column=0) | ||
|
||
# input_secret = tkinter.Entry(fr_add_secret) | ||
# input_secret.grid(row=1, column=1) | ||
|
||
# button_add = tkinter.Button(fr_add_secret, text="Save", command=callback_button) | ||
# button_add.grid(row=2, column=1) | ||
|
||
update() | ||
|
||
window.mainloop() |