Skip to content

Commit 28355be

Browse files
authored
Typerandrich (#3)
* remove manual implementations and remove migration and add it in to db * add migrations here * refactor some funtions and add rich and typer * add some prompts * add exprot funtion and add config currently do nothing
1 parent 109e17e commit 28355be

14 files changed

+408
-166
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ wheels/
99

1010
# Virtual environments
1111
.venv
12+
data/*.csv

app.py

Lines changed: 143 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
import argparse
1+
import csv
22
import os
3+
import time
4+
from datetime import datetime
5+
from typing import Annotated
6+
7+
import questionary
8+
import typer
9+
from rich import print
10+
from rich.console import Console
11+
from rich.progress import Progress, SpinnerColumn, TextColumn
12+
from rich.prompt import Prompt
13+
from rich.table import Table
314

4-
from libwardenpy.colors import colored_string, print_colored
515
from libwardenpy.db import get_connection
616
from libwardenpy.funtionality import (
717
AuthenticatedData,
@@ -14,55 +24,102 @@
1424
list_passwords,
1525
register_user,
1626
)
17-
from libwardenpy.migrations import migrate_DB
1827
from libwardenpy.passgen import generate_password
1928

2029
authenticated = False
2130

2231
data: UnAuthData = UnAuthData("", "")
2332
auth_data: AuthenticatedData = AuthenticatedData("", b"")
2433

34+
app = typer.Typer()
35+
36+
37+
@app.command()
38+
def login(
39+
username: Annotated[
40+
str,
41+
typer.Option("--username", "-u"),
42+
],
43+
password: Annotated[
44+
str,
45+
typer.Option("--password", "-p", prompt=True, hide_input=True),
46+
],
47+
):
48+
global authenticated, auth_data, data
49+
data.username = username
50+
data.master_password = password
51+
auth_data.username = username
52+
key = authenticate_user(get_connection(), data)
53+
if key is not None:
54+
auth_data.key = key
55+
authenticated = True
56+
main()
57+
2558

26-
def init_store(args) -> None:
59+
@app.command()
60+
def create(
61+
username: str,
62+
password: Annotated[
63+
str,
64+
typer.Option(
65+
"--password", "-p", prompt=True, confirmation_prompt=True, hide_input=True
66+
),
67+
],
68+
):
2769
global data
28-
migrate_DB()
29-
USER_NAME_EXIST = False
70+
data.username, data.master_password = username, password
3071
with get_connection() as conn:
31-
cursor = conn.execute("SELECT username FROM users;")
32-
data.username = args.username
33-
if (data.username,) in cursor.fetchall():
34-
USER_NAME_EXIST = True
35-
if not USER_NAME_EXIST:
36-
tips_text1 = (
37-
"create a strong and memorable password\n"
38-
"guide: https://anonymousplanet.org/guide.html#appendix-a2-guidelines-for-passwords-and-passphrases\n"
72+
cursor = conn.execute(
73+
"SELECT COUNT(*) FROM users WHERE username = ?", (username,)
3974
)
40-
print_colored(tips_text1, "red")
41-
data.master_password = input("Create Strong and Memorable Master Password: ")
42-
register_user(get_connection(), data)
43-
else:
44-
print("Username exit")
45-
exit()
75+
76+
if cursor.fetchone()[0] == 0:
77+
if register_user(get_connection(), data):
78+
print(f"User {username} registered successfully!")
79+
else:
80+
print("Username exists")
81+
82+
83+
@app.command()
84+
def export(
85+
username: str,
86+
password: Annotated[
87+
str,
88+
typer.Option(
89+
"--password", "-p", prompt=True, confirmation_prompt=True, hide_input=True
90+
),
91+
],
92+
):
93+
global authenticated, auth_data, data
94+
data.username = username
95+
data.master_password = password
96+
auth_data.username = username
97+
key = authenticate_user(get_connection(), data)
98+
if key is not None:
99+
auth_data.key = key
100+
authenticated = True
101+
passwords = list_passwords(get_connection(), auth_data)
102+
if passwords is not None:
103+
with Progress(
104+
SpinnerColumn(),
105+
TextColumn("[progress.description]{task.description}"),
106+
transient=True,
107+
) as progress:
108+
progress.add_task(description="Writing data to csv...", total=None)
109+
time.sleep(1.5)
110+
with open(
111+
f"data/export-{datetime.today().date()}.csv", "w", newline=""
112+
) as csvfile:
113+
writer = csv.writer(
114+
csvfile, delimiter=",", quotechar="|", quoting=csv.QUOTE_MINIMAL
115+
)
116+
for entry in passwords:
117+
writer.writerow(entry)
118+
print("Done!")
46119

47120

48121
def main() -> None:
49-
global authenticated, auth_data, data, pool
50-
args = parse_arguments()
51-
data.username = args.username
52-
data.master_password = args.password
53-
if args.password is not None and args.username is not None:
54-
key = authenticate_user(get_connection(), data)
55-
if key is not None:
56-
auth_data.key = key
57-
authenticated = True
58-
elif args.username is not None and args.password is None:
59-
data.master_password = input("Enter Master Password: ")
60-
key = authenticate_user(get_connection(), data)
61-
if key is not None:
62-
authenticated = True
63-
auth_data.key = key
64-
65-
auth_data.username = data.username
122+
global authenticated
66123
if authenticated:
67124
banner = r"""
68125
__ __ _ ______ __
@@ -71,106 +128,99 @@ def main() -> None:
71128
\ V V / (_| | | | (_| | __/ | | | __/ | |
72129
\_/\_/ \__,_|_| \__,_|\___|_| |_|_| |_|
73130
-- created by supun
74-
75131
type .help or ? for help and x or .exit to exit.
76132
77133
1.) Add a Entry [A]
78134
2.) Search Entry [S]
79135
3.) List Entries [L]
80136
4.) Delete Entry [D]
137+
138+
PRESS X TO QUITE
81139
"""
82140
print(banner)
83141
main_logic()
84142

85143

86144
def main_logic():
87145
global auth_data
88-
print(auth_data.key)
89146
while True:
90-
user_input = input("> ")
91-
if user_input.upper() == ".CLEAR":
147+
user_input = Prompt.ask("[bold green]> [/bold green]").upper()
148+
149+
if user_input == ".CLEAR":
92150
os.system("clear")
93-
if user_input == "?" or user_input.upper() == ".HELP":
151+
152+
if user_input in ("?", ".HELP"):
94153
print(help_msg)
95-
if (
96-
user_input == "1"
97-
or user_input.upper() == "A"
98-
or user_input.upper() == ".ADD"
99-
):
154+
155+
if user_input in ("1", "A", ".ADD"):
100156
while True:
101157
site = input(".add website_url > ").strip()
102158
if not site:
103-
print(colored_string("You Must Add a Website .^.", "RED"))
159+
print("[bold red]You Must Add a Website .^.[/bold red]")
104160
else:
105161
break
106162
site_pass = input(".add password (leave this blank for random password) > ")
107163
if not site_pass:
108164
site_pass = generate_password()
109165
entry: Entry = Entry(site, site_pass)
110166
add_password(get_connection(), auth_data, entry)
111-
if (
112-
user_input == "2"
113-
or user_input.upper() == "S"
114-
or user_input.upper() == ".SEARCH"
115-
):
167+
168+
if user_input in ("2", "S", ".SEARCH"):
116169
site = input(".search > ")
117170
a = get_password(get_connection(), auth_data, site)
118171
print(a)
119172

120-
if (
121-
user_input == "3"
122-
or user_input.upper() == "L"
123-
or user_input.upper() == ".LIST"
124-
):
173+
if user_input in ("3", "L", ".LIST"):
125174
passwords = list_passwords(get_connection(), auth_data)
126175
if passwords is None:
127176
print(f"No passwords for user {auth_data.username}")
128-
print(passwords)
129-
if user_input.upper() == "D" or user_input.upper() == ".DEL":
177+
else:
178+
table = Table(title="List of Passwords")
179+
table.add_column("Site/Url", style="blue")
180+
table.add_column("Password", style="red")
181+
for item in passwords:
182+
table.add_row(item[0], item[1])
183+
console = Console()
184+
console.print(table)
185+
186+
if user_input in ("D", "4", ".DEL"):
130187
while True:
131188
site = input(".search entry > ").strip()
132189
if not site:
133-
print(colored_string("You Must Add a Website .^.", "RED"))
190+
print("[bold red]You Must Add a Website .^.[bold red]")
134191
else:
135192
break
136193
list_of_entries = get_password(get_connection(), auth_data, site)
194+
137195
if list_of_entries is None:
138196
print(f"no entries have {site}")
139197
if list_of_entries is not None:
140-
print(list_of_entries)
141-
id = input("Give the id of the entry you want to delete > ")
142-
id = str(id).strip()
143-
delete_passwod(get_connection(), auth_data, id)
144-
if user_input.upper() == "X" or user_input.upper() == ".EXIT":
198+
table = Table(title="List of Entries")
199+
table.add_column("id", style="bold blue")
200+
table.add_column("Site/Url", style="green")
201+
table.add_column("Password", style="red")
202+
for item in list_of_entries:
203+
table.add_row(str(item[0]), item[1], item[2].decode("utf-8"))
204+
console = Console()
205+
console.print(table)
206+
choices = [str(id[0]) for id in list_of_entries]
207+
id = questionary.select(
208+
"Which entry do you want to delet?",
209+
choices=choices,
210+
qmark="> ",
211+
pointer=">",
212+
).ask()
213+
delete_the_entry = Prompt.ask(
214+
f"Are You sure You want to delete [bold red]{id}[/bold red] {'[y/N]'} ?",
215+
default=False,
216+
)
217+
if delete_the_entry:
218+
delete_passwod(get_connection(), auth_data, id)
219+
220+
if user_input in ("X", ".EXIT"):
145221
break
146222

147223

148-
def parse_arguments() -> argparse.Namespace:
149-
parser = argparse.ArgumentParser()
150-
subparser = parser.add_subparsers(
151-
title="Commands",
152-
)
153-
parser.add_argument("-u", "--username", help="use the username given here")
154-
parser.add_argument("-p", "--password", help="use the password given here")
155-
parser.add_argument("-a", "--add", help="add password")
156-
157-
init_parser = subparser.add_parser(
158-
"init", aliases="i", help="Inizialize password repo"
159-
)
160-
init_parser.add_argument(
161-
"username", help="username for initialize the password store"
162-
)
163-
init_parser.set_defaults(func=init_store)
164-
subparser.add_parser("new", help="Inizialize new password repo").set_defaults(
165-
func=init_store
166-
)
167-
168-
args = parser.parse_args()
169-
if hasattr(args, "func"):
170-
args.func(args)
171-
return args
172-
173-
174224
help_msg = """
175225
.help, ? Show this menu
176226
.clear Clear the screen
@@ -183,4 +233,4 @@ def parse_arguments() -> argparse.Namespace:
183233

184234

185235
if __name__ == "__main__":
186-
main()
236+
app()

libwardenpy/colors.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

libwardenpy/configuration.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass
5+
class AppConfig:
6+
EXPORT_DIRECTORY: str = "data/"
7+
PASSWORD_LENGTH: int = 20

libwardenpy/db.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,33 @@
88
def get_connection(DB_PATH="data/db.sqlite3"):
99
conn = sqlite3.connect(DB_PATH)
1010
try:
11+
conn.execute(
12+
"""
13+
CREATE TABLE IF NOT EXISTS users (
14+
username TEXT PRIMARY KEY,
15+
password_hash TEXT NOT NULL,
16+
salt BLOB NOT NULL)
17+
"""
18+
)
19+
conn.execute(
20+
"""
21+
CREATE TABLE IF NOT EXISTS passwords (
22+
id INTEGER PRIMARY KEY AUTOINCREMENT,
23+
username TEXT NOT NULL,
24+
site TEXT NOT NULL,
25+
encrypted_password BLOB NOT NULL,
26+
nonce BLOB NOT NULL,
27+
FOREIGN KEY (username) REFERENCES users(username))
28+
"""
29+
)
1130
yield conn
31+
1232
except Exception as e:
1333
conn.rollback()
1434
raise e
35+
1536
else:
1637
conn.commit()
38+
1739
finally:
1840
conn.close()

0 commit comments

Comments
 (0)