git
This commit is contained in:
commit
739d10f15f
@ -1,163 +1,276 @@
|
||||
import json
|
||||
from os import path
|
||||
|
||||
import appdirs
|
||||
from encryption_handler import EncryptionHandler
|
||||
|
||||
|
||||
file_path: str = "/home/kosh/.local/share/kosh_pass/data.json"
|
||||
password_check_path: str = "/home/kosh/.local/share/kosh_pass/password_check"
|
||||
|
||||
class DataHandler:
|
||||
def __get_file_data(self) -> dict[str, dict[str, str]]:
|
||||
with open(file_path, "r", encoding="utf8") as file:
|
||||
return json.load(file)
|
||||
"""
|
||||
WARNING:-
|
||||
THere is no fail-safe for if the app is opened for the first time.
|
||||
|
||||
def __write_file_data(self, data: dict[str, dict[str, str]]) -> None:
|
||||
with open(file_path, "w", encoding="utf8") as file:
|
||||
json.dump(data, file)
|
||||
A class that handles the stored data.
|
||||
This class does NOT handle encryption directly. Look at ./encryption_handler.py
|
||||
When a instance is created, the instance should be used for one user ONLY.
|
||||
|
||||
def set_password(self, password: str) -> None:
|
||||
with open(password_check_path, "wb") as password_check_file:
|
||||
password_check_file.write(
|
||||
EncryptionHandler.encrypt("Correct".encode(), password.encode())
|
||||
)
|
||||
In this class "data" is used. THe following is the format of data:-
|
||||
data:- {
|
||||
entry_name: {
|
||||
field_name: field_value
|
||||
}
|
||||
}
|
||||
Example for "data":
|
||||
{
|
||||
"Amazon": {
|
||||
"Username": "Kosh",
|
||||
"Password": "Pass1234"
|
||||
},
|
||||
"Matrix": {
|
||||
"Username": "Kosh",
|
||||
"Email": "kosh@fake.com",
|
||||
"id": "@kosh:matrix.com"
|
||||
}
|
||||
}
|
||||
|
||||
def verify_password(self, password: str) -> None:
|
||||
with open(password_check_path, "rb") as password_check_file:
|
||||
EncryptionHandler.decrypt(password_check_file.read(), password.encode())
|
||||
|
||||
def change_password(self, old_password: str, new_password: str) -> None:
|
||||
self.verify_password(old_password)
|
||||
data: dict[str, dict[str, str]] = {}
|
||||
Attributes:-
|
||||
__file_path (private):- The path to the file where the users encrypted data is stored.
|
||||
__password (private):- The users password.
|
||||
"""
|
||||
|
||||
for entry_name in self.get_entry_names():
|
||||
data[entry_name] = {}
|
||||
for field_name in self.get_field_names(entry_name):
|
||||
data[entry_name][field_name] = \
|
||||
self.get_field_data(entry_name, field_name, old_password)
|
||||
__file_path: str
|
||||
__user_name: str
|
||||
__password: str
|
||||
|
||||
self.set_password(new_password)
|
||||
self.__write_file_data({})
|
||||
def __init__(self, user_name: str, password: str) -> None:
|
||||
"""
|
||||
Logs in the user
|
||||
Parameters:-
|
||||
user_name
|
||||
password
|
||||
Raises:-
|
||||
If user_name does not exist, ValueError will be thrown
|
||||
If password is wrong, ValueError will be thrown
|
||||
WARNING:-
|
||||
If this is the first time the app is opened, the user_name will be shown as wrong
|
||||
"""
|
||||
self.__file_path = appdirs.user_data_dir(appname="Unnamed_Password_Manager")
|
||||
self.__user_name = user_name
|
||||
self.__file_path = path.join(self.__file_path, self.__user_name)
|
||||
self.__password = password
|
||||
|
||||
for entry_name in data:
|
||||
self.add_entry(entry_name)
|
||||
for field_name in data[entry_name]:
|
||||
self.add_field(
|
||||
entry_name, field_name, data[entry_name][field_name], new_password
|
||||
)
|
||||
if not path.exists(self.__file_path):
|
||||
raise ValueError(f"User {user_name} does not exist.")
|
||||
|
||||
def add_entry(self, entry_name: str) -> None:
|
||||
data = self.__get_file_data()
|
||||
# This will call encryption handler,
|
||||
# which will throw an error if the password is wrong
|
||||
self.get_data()
|
||||
|
||||
@staticmethod
|
||||
def create_user(user_name: str, password: str) -> None:
|
||||
"""
|
||||
Creates a new user.
|
||||
Parameters:-
|
||||
user_name
|
||||
password
|
||||
Raises:-
|
||||
If user_name exists, ValueError is thrown
|
||||
"""
|
||||
file_path = path.join(
|
||||
appdirs.user_data_dir(appname="Unnamed_Password_Manager"),
|
||||
user_name
|
||||
)
|
||||
if path.exists(file_path):
|
||||
raise ValueError(f"User {user_name} already exists")
|
||||
with open(file_path, "wb") as file:
|
||||
file.write(EncryptionHandler.encrypt(b"{}", password.encode()))
|
||||
|
||||
def get_data(self) -> dict[str, dict[str, str]]:
|
||||
"""
|
||||
Get the data of the user.
|
||||
Return:-
|
||||
data:- Read the class documentation for more information.
|
||||
"""
|
||||
with open(self.__file_path, "rb") as file:
|
||||
data: str = \
|
||||
EncryptionHandler.decrypt(file.read(), self.__password.encode())\
|
||||
.decode()
|
||||
return json.loads(data)
|
||||
|
||||
def write_data(self, data: dict[str, dict[str, str]]) -> None:
|
||||
"""
|
||||
Write the data of the user.
|
||||
Parameters:-
|
||||
data:- Read the class documentation for more information
|
||||
"""
|
||||
json_data: str = json.dumps(data)
|
||||
encrypted_data: bytes = EncryptionHandler.encrypt(
|
||||
json_data.encode(),
|
||||
self.__password.encode()
|
||||
)
|
||||
with open(self.__file_path, "wb") as file:
|
||||
file.write(encrypted_data)
|
||||
|
||||
def change_password(self, password: str) -> None:
|
||||
"""
|
||||
Changes the password of the user.
|
||||
Parameters:-
|
||||
password
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
self.__password = password
|
||||
self.write_data(data)
|
||||
|
||||
def add_entry(self, entry_name: str, fields: dict[str, str]) -> None:
|
||||
"""
|
||||
Add a new entry:-
|
||||
Parameters:-
|
||||
entry_name
|
||||
fields:- {
|
||||
field_name_1: field_value_1,
|
||||
field_name_2: field_value_2
|
||||
}
|
||||
Raises:-
|
||||
A ValueError is raised if entry_name already exists.
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if entry_name in data:
|
||||
raise ValueError(f"An entry with the name {entry_name} already exists.")
|
||||
raise ValueError(f"Entry {entry_name} already exists.")
|
||||
|
||||
data[entry_name] = {}
|
||||
self.__write_file_data(data)
|
||||
data[entry_name] = fields
|
||||
self.write_data(data)
|
||||
|
||||
def delete_entry(self, entry_name: str) -> None:
|
||||
data = self.__get_file_data()
|
||||
try:
|
||||
data.pop(entry_name)
|
||||
except KeyError:
|
||||
raise ValueError(f"No entry with name \"{entry_name}\"") from KeyError
|
||||
self.__write_file_data(data)
|
||||
"""
|
||||
Deletes the given entry.
|
||||
Parameters:-
|
||||
entry_name
|
||||
Errors:-
|
||||
If the requested entry does not exist, an error will be thrown
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if entry_name not in data:
|
||||
raise ValueError(f"{entry_name} does not exist.")
|
||||
data.pop(entry_name)
|
||||
self.write_data(data)
|
||||
|
||||
def edit_entry_name(self, entry_name: str, new_entry_name: str) -> None:
|
||||
if new_entry_name in self.get_entry_names():
|
||||
def edit_entry_name(self, old_entry_name: str, new_entry_name: str) -> None:
|
||||
"""
|
||||
Changes the name of the entry.
|
||||
Parameters:-
|
||||
old_entry_name
|
||||
new_entry_name
|
||||
Errors:-
|
||||
If the old_entry_name does not exist, a ValueError will be thrown.
|
||||
If new_entry_name already exists, a ValueError will be raised.
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if new_entry_name in data:
|
||||
raise ValueError(f"\"{new_entry_name}\" already exists")
|
||||
if entry_name not in self.get_entry_names():
|
||||
raise ValueError(f"No entry with name \"{entry_name}\"")
|
||||
if old_entry_name not in data:
|
||||
raise ValueError(f"No entry with name \"{old_entry_name}\"")
|
||||
|
||||
fields: dict[str, str] = self.__get_file_data()[entry_name]
|
||||
self.delete_entry(entry_name)
|
||||
data = self.__get_file_data()
|
||||
data[new_entry_name] = fields
|
||||
self.__write_file_data(data)
|
||||
self.delete_entry(old_entry_name)
|
||||
self.add_entry(new_entry_name, data[old_entry_name])
|
||||
|
||||
def get_entry_names(self, ) -> list[str]:
|
||||
return list(self.__get_file_data().keys())
|
||||
|
||||
def add_field(self,
|
||||
def add_field(self,
|
||||
entry_name: str,
|
||||
field_name: str,
|
||||
field_value: str,
|
||||
password: str
|
||||
) -> None:
|
||||
self.verify_password(password)
|
||||
if entry_name not in self.get_entry_names():
|
||||
raise ValueError(f"No entry with name {entry_name}")
|
||||
if field_name in self.get_field_names(entry_name):
|
||||
"""
|
||||
Adds a new field to a given entry.
|
||||
Parameters:-
|
||||
entry_name
|
||||
field_name
|
||||
field_value
|
||||
Raises:-
|
||||
If entry_name does not exist, a ValueError will be raised.
|
||||
If field_name already exists, a ValueError will be raised.
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if entry_name not in data:
|
||||
raise ValueError(f"No entry with name \"{entry_name}\"")
|
||||
if field_name in data[entry_name]:
|
||||
raise ValueError(f"Field {field_name} already exists under entry {entry_name}")
|
||||
|
||||
data: dict[str, dict[str, str]] = self.__get_file_data()
|
||||
data[entry_name][field_name] = EncryptionHandler.encrypt(
|
||||
field_value.encode(),
|
||||
password.encode()
|
||||
).decode("unicode-escape")
|
||||
self.__write_file_data(data)
|
||||
data[entry_name][field_name] = field_value
|
||||
self.write_data(data)
|
||||
|
||||
def delete_field(self, entry_name: str, field_name: str) -> None:
|
||||
if entry_name not in self.get_entry_names():
|
||||
"""
|
||||
Adds a new field to a given entry.
|
||||
Parameters:-
|
||||
entry_name
|
||||
field_name
|
||||
Raises:-
|
||||
If entry_name does not exist, a ValueError will be raised.
|
||||
If field_name does not exists, a ValueError will be raised.
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if entry_name not in data:
|
||||
raise ValueError(f"No entry with name \"{entry_name}\"")
|
||||
if field_name not in self.get_field_names(entry_name):
|
||||
raise ValueError(f"{entry_name} does not have field {field_name}")
|
||||
data = self.__get_file_data()
|
||||
if field_name not in data[entry_name]:
|
||||
raise ValueError(f"No field {field_name} under entry {entry_name}")
|
||||
data[entry_name].pop(field_name)
|
||||
self.__write_file_data(data)
|
||||
self.write_data(data)
|
||||
|
||||
def edit_field_name(self, entry_name: str, field_name: str, new_field_name: str) -> None:
|
||||
if entry_name not in self.get_entry_names():
|
||||
def edit_field_name(
|
||||
self,
|
||||
entry_name: str,
|
||||
old_field_name: str,
|
||||
new_field_name: str
|
||||
) -> None:
|
||||
"""
|
||||
Change the name of a field under the given entry.
|
||||
Parameters:-
|
||||
entry_name
|
||||
old_field_name
|
||||
new_field_name
|
||||
Raises:-
|
||||
If entry_name does not exist, a ValueError will be raised.
|
||||
If old_field_name does not exists, a ValueError will be raised.
|
||||
If field_name already exists, a ValueError will be raised.
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if entry_name not in data:
|
||||
raise ValueError(f"No entry with name \"{entry_name}\"")
|
||||
if field_name not in self.get_field_names(entry_name):
|
||||
raise ValueError(f"{entry_name} does not have field {field_name}")
|
||||
if new_field_name in self.get_field_names(entry_name):
|
||||
raise ValueError(f"{entry_name} does has a field {new_field_name}")
|
||||
field_data = self.__get_file_data()[entry_name][field_name]
|
||||
self.delete_field(entry_name, field_name)
|
||||
data = self.__get_file_data()
|
||||
data[entry_name][new_field_name] = field_data
|
||||
self.__write_file_data(data)
|
||||
if old_field_name not in data[entry_name]:
|
||||
raise ValueError(f"No field {old_field_name} under entry {entry_name}")
|
||||
if new_field_name in data[entry_name]:
|
||||
raise ValueError(f"Field {new_field_name} already exists under {entry_name}")
|
||||
data[entry_name][new_field_name] = data[entry_name][old_field_name]
|
||||
data[entry_name].pop(old_field_name)
|
||||
self.write_data(data)
|
||||
|
||||
def edit_field_value(self,
|
||||
def edit_field_value(self,
|
||||
entry_name: str,
|
||||
field_name: str,
|
||||
new_value: str,
|
||||
password: str
|
||||
) -> None:
|
||||
self.verify_password(password)
|
||||
if entry_name not in self.get_entry_names():
|
||||
raise ValueError(f"No entry called {entry_name}")
|
||||
if field_name not in self.get_field_names(entry_name):
|
||||
raise ValueError(f"No field called {field_name} under {entry_name}")
|
||||
self.delete_field(entry_name, field_name)
|
||||
self.add_field(entry_name, field_name, new_value, password)
|
||||
|
||||
def get_field_data(self, entry_name: str, field_name: str, password: str) -> str:
|
||||
self.verify_password(password)
|
||||
if field_name not in self.get_field_names(entry_name):
|
||||
raise ValueError(f"No field with name {field_name} under {entry_name}.")
|
||||
encrypted_data = self.__get_file_data()[entry_name][field_name].\
|
||||
encode("ISO-8859-1")
|
||||
plain_data = EncryptionHandler.decrypt(
|
||||
encrypted_data,
|
||||
password.encode("ISO-8859-1")
|
||||
).decode("utf-8")
|
||||
return plain_data
|
||||
|
||||
def get_field_names(self, entry_name: str) -> list[str]:
|
||||
data = self.__get_file_data()
|
||||
"""
|
||||
Change the value of the given field under the given entry.
|
||||
Parameters:-
|
||||
entry_name
|
||||
field_name
|
||||
new_value
|
||||
Raises:-
|
||||
If entry_name does not exist, a ValueError will be raised.
|
||||
If field_name does not exists, a ValueError will be raised.
|
||||
"""
|
||||
data: dict[str, dict[str, str]] = self.get_data()
|
||||
if entry_name not in data:
|
||||
raise ValueError(f"No entry with name {entry_name}.")
|
||||
return list(data[entry_name].keys())
|
||||
raise ValueError(f"No entry with name \"{entry_name}\"")
|
||||
if field_name not in data[entry_name]:
|
||||
raise ValueError(f"No field {field_name} under entry {entry_name}")
|
||||
data[entry_name][field_name] = new_value
|
||||
self.write_data(data)
|
||||
|
||||
def add_entry_and_fields(
|
||||
self,
|
||||
entry_name: str,
|
||||
fields: dict[str, str],
|
||||
password: str
|
||||
) -> None:
|
||||
self.verify_password(password)
|
||||
if entry_name in self.get_entry_names():
|
||||
raise ValueError(f"Entry {entry_name} already exists.")
|
||||
self.add_entry(entry_name)
|
||||
for field_name, field_value in fields.items():
|
||||
print(field_name, field_value)
|
||||
self.add_field(entry_name, field_name, field_value, password)
|
||||
|
||||
def test():
|
||||
#DataHandler.create_user("Kosh", "p")
|
||||
d = DataHandler("Kosh", "p")
|
||||
d.edit_field_value("Not Amazon", "p", "v")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
||||
|
2
backend/requirements.txt
Normal file
2
backend/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
appdirs==1.4.4
|
||||
pycryptodome==3.18.0
|
Loading…
x
Reference in New Issue
Block a user