diff --git a/docs/images/ListNonFriends.png b/docs/images/ListNonFriends.png new file mode 100644 index 0000000..4adef76 Binary files /dev/null and b/docs/images/ListNonFriends.png differ diff --git a/docs/images/ScanIP.png b/docs/images/ScanIP.png new file mode 100644 index 0000000..0d7ff0c Binary files /dev/null and b/docs/images/ScanIP.png differ diff --git a/docs/images/file_send.png b/docs/images/file_send.png new file mode 100644 index 0000000..6a22808 Binary files /dev/null and b/docs/images/file_send.png differ diff --git a/docs/images/filesending.png b/docs/images/filesending.png new file mode 100644 index 0000000..663069b Binary files /dev/null and b/docs/images/filesending.png differ diff --git a/docs/images/login.png b/docs/images/login.png new file mode 100644 index 0000000..10c506c Binary files /dev/null and b/docs/images/login.png differ diff --git a/docs/images/mainwindow.png b/docs/images/mainwindow.png new file mode 100644 index 0000000..f8ac7a0 Binary files /dev/null and b/docs/images/mainwindow.png differ diff --git a/docs/images/notAvailable.png b/docs/images/notAvailable.png new file mode 100644 index 0000000..c28219a Binary files /dev/null and b/docs/images/notAvailable.png differ diff --git a/docs/images/queue.png b/docs/images/queue.png new file mode 100644 index 0000000..7280f8c Binary files /dev/null and b/docs/images/queue.png differ diff --git a/docs/images/register.png b/docs/images/register.png new file mode 100644 index 0000000..4189e39 Binary files /dev/null and b/docs/images/register.png differ diff --git a/docs/main.pdf b/docs/main.pdf new file mode 100644 index 0000000..a52621b Binary files /dev/null and b/docs/main.pdf differ diff --git a/docs/main.tex b/docs/main.tex new file mode 100644 index 0000000..2027683 --- /dev/null +++ b/docs/main.tex @@ -0,0 +1,278 @@ +%%%%%%%%%%%%%%%%%%%%%% +% NASTAVENÍ FEKT.TEX % +%%%%%%%%%%%%%%%%%%%%%% + +% Pokud následující řádky zakomentujete, na titulní straně se nezobrazí. + +% Nadpis dokumentu (kód předmětu) +\newcommand{\name}{Documentation} +% Podnadpis dokumentu (název předmětu) +\newcommand{\subname}{Sensitive file sharing application} +% Seznam autorů +\newcommand{\authors}{Martin Bezecný, Karel Ramiš, Marek Szymutko, Adam Tuček} +% Seznam korektorů +%\newcommand{\corrections}{} +% Popis dokumentu +%\newcommand{\docdesc}{} +% Zařazení dokumentu (studijní program) +% \newcommand{\docgroup}{Informační bezpečnost, FEKT VUT} +% Odkaz +% \newcommand{\docurl}{https://github.com/VUT-FEKT-IBE/FEKT.tex} + +% Přepsáním argumentu na 'true' zapnete balíček 'minted' pro sázení kódu. +% Pro jeho použití lokálně musíte mít v systému dostupný Python 3, python +% knihovnu 'minted' a PDFLaTeX musíte spouštět s argumentem '-shell-escape'. +% Místo něj můžete použít prostředí 'lstlisting'. +\newcommand{\docminted}{false} + + +%%%%%%%%%%%%%%%%%%%% +% OBECNÉ NASTAVENÍ % +%%%%%%%%%%%%%%%%%%%% + +\newcommand{\fekttexversion}{2.0} + +\documentclass[ + % Velikost základního písma je 12 bodů + 12pt, + % Formát papíru je A4 + a4paper, + % Oboustranný tisk + twoside, + % Záložky a metainformace ve výsledném PDF budou v kódování unicode + unicode, +]{article} + +% Kódování zdrojových souborů +\usepackage[utf8]{inputenc} +% Kódování výstupního souboru +\usepackage[T1]{fontenc} + +% Geometrie stránky +\usepackage[ + % Horní a dolní okraj + tmargin=25mm, + bmargin=25mm, + % Vnitřní a vnější okraj + lmargin=30mm, + rmargin=20mm, + % Velikost zápatí + footskip=17mm, + % Vypnutí záhlaví + nohead, +]{geometry} + +% Zajištění kopírovatelnosti a prohledávanosti vytvořených PDF +\usepackage{cmap} +% Podmínky (pro použití v titulní straně) +\usepackage{ifthen} + +%%%%%%%%%%%%%%% +% FORMÁTOVÁNÍ % +%%%%%%%%%%%%%%% + +% Nastavení stylu nadpisů +\usepackage{sectsty} +% Formátování obsahů +\usepackage{tocloft} +\setcounter{tocdepth}{1} +% Odstranění mezer mezi řádky v seznamech +\usepackage{enumitem} +\setlist{nosep} +\setitemize{leftmargin=1em} +\setenumerate{leftmargin=1.5em} +\renewcommand{\labelitemi}{--} +\renewcommand{\labelitemii}{$\circ$} +\renewcommand{\labelitemiii}{$\cdot$} +\renewcommand{\labelitemiv}{--} +% Sázení správných uvozovek pomocí '\enquote{}' +\usepackage{csquotes} +% Vynucení umístění poznámek pod čarou vespod stránky +\usepackage[bottom]{footmisc} +% Automatické zarovnání textu k předcházení vdov a parchantů +\usepackage[defaultlines=3,all=true]{nowidow} +% Zalomení části textu pokud není na současné stránce dost místa +\usepackage{needspace} +% Nastavení řádkování +\usepackage{setspace} +\onehalfspacing +% Změna odsazení odstavců +\setlength{\parskip}{1em} +\setlength{\parindent}{0em} + +% Bezpatkové sázení nadpisů +\allsectionsfont{\sffamily} +% Změna formátování nadpisu a podnadpisů v Obsahu +\renewcommand{\cfttoctitlefont}{\Large\bfseries\sffamily} +\renewcommand{\cftsubsecdotsep}{\cftdotsep} + +% Použití moderní/aktualizované sady písem +\usepackage{lmodern} + +%%%%%%%%%%% +% NADPISY % +%%%%%%%%%%% + +\usepackage{titlesec} + +\titlespacing*{\section}{0pt}{10pt}{-0.2\baselineskip} +\titlespacing*{\subsection}{0pt}{0.2\baselineskip}{-0.2\baselineskip} +\titlespacing*{\subsubsection}{0pt}{0.2\baselineskip}{-0.2\baselineskip} +\titlespacing*{\paragraph}{0pt}{0pt}{1em} + +%%%%%%%%%% +% ODKAZY % +%%%%%%%%%% + +% Tvorba hypertextových odkazů +\usepackage[ + breaklinks=true, + hypertexnames=false, +]{hyperref} +% Nastavení barvení odkazů +\hypersetup{ + colorlinks, + citecolor=black, + filecolor=black, + linkcolor=black, + urlcolor=blue +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%% +% OBRÁZKY, GRAFY, TABULKY % +%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Vkládání obrázků +\usepackage{graphicx} +\usepackage{subfig} +% Nastavení popisů obrázků, výpisů a tabulek +\usepackage{caption} +\captionsetup{justification=centering} +% Grafy a vektorové obrázky +\usepackage{tikz} +\usetikzlibrary{shapes,arrows} +% Složitější tabulky +\usepackage{tabularx} +\usepackage{multicol} + +% Sázení osamocených float prostředí v horní části stránky +\makeatletter +\setlength{\@fptop}{0pt plus 10pt minus 0pt} +\makeatother + +% Vynucení vypsání floating prostředí pomocí \FloatBarrier +\usepackage{placeins} + +% Rámečky +\usepackage{mdframed} + +%%%%%%%%%%%%%% +% MATEMATIKA % +%%%%%%%%%%%%%% + +% Sázení matematiky a matematických symbolů ('\mathbb{}') +\usepackage{amsmath} +\usepackage{amssymb} +% Sázení fyzikálních veličin +\usepackage{siunitx} + +%%%%%%%%%%%%%%%%% +% ZDROJOVÉ KÓDY % +%%%%%%%%%%%%%%%%% + +% Sazba zdrojových kódů +\usepackage[formats]{listings} +% Přepnutí prostředí 'code' do režimu výpisu kódu +\newenvironment{code}{\captionsetup{type=listing}}{} + +\lstset{ + basicstyle=\small\ttfamily, + numbers=left, + numberstyle=\tiny, + tabsize=4, + columns=fixed, + showstringspaces=false, + showtabs=false, + keepspaces, +} + +% Balíček 'minted' budeme používat pouze pokud je jeho hodnota nastavena na 'true' +\providecommand{\docminted}{false} +\ifthenelse{\equal{\docminted}{true}} +{ + % Sazba zdrojových kódů + \usepackage[newfloat]{minted} + % Nastavení barev 'minted' kódů + \usemintedstyle{pastie} +} +{ + % \docminted není 'true', nic neprovádíme + % Pokud je v dokumentu 'minted' prostředí, dokument se nepodaří přeložit. +} + +%%%%%%%%%%%%%%%%%%% +% VLASTNÍ PŘÍKAZY % +%%%%%%%%%%%%%%%%%%% + +\newcounter{todo} +\newcommand{\TODO}[1]{% + \addtocounter{todo}{1}% + \textcolor{red}{% + \textbf{\sffamily\small{TODO \thetodo}% + \ifthenelse{\equal{#1}{}}{}{:}% + } % + #1% + }% +} + +%%%%%%%%%%% +% TITULKA % +%%%%%%%%%%% + +\newcommand{\titulka}{ + \vspace*{2em} + \begin{center} + \ifthenelse{\isundefined{\name}}{}{{\Huge \bfseries \name{}}} + + \ifthenelse{\isundefined{\subname}}{}{{\huge \bfseries \subname{}}} + + \vspace*{2em} + + \ifthenelse{\isundefined{\docdesc}}{}{{\Large \docdesc}} + + \vspace*{1em} + + \ifthenelse{\isundefined{\docgroup}}{}{\docgroup} + + \ifthenelse{\isundefined{\docurl}}{}{\url{\docurl}} + \end{center} + + \vfill + + \ifthenelse{\isundefined{\authors}}{}{\authors{}} + \ifthenelse{\isundefined{\corrections}}{}{\\\small (korektury \corrections{})} + + + \thispagestyle{empty} + \newpage +} + +%%%%%%%%%%%% +% DOKUMENT % +%%%%%%%%%%%% + +\begin{document} + +\titulka{} + +\tableofcontents +\thispagestyle{empty} + +\setcounter{page}{0} + +\include{text/intro.tex} +\include{text/installation.tex} +\include{text/gui.tex} +\include{text/function.tex} + +\end{document} diff --git a/docs/text/function.tex b/docs/text/function.tex new file mode 100644 index 0000000..cd83fb8 --- /dev/null +++ b/docs/text/function.tex @@ -0,0 +1,65 @@ +\section{Functionality description} +This section goes over the app functionalities and how they are designed and implemented. + +\subsection{Login} +This is a section going over logging in to the application. If it is the first time the user uses the application they will have to create an account with username +and password. Otherwise only password is required. If user inserts password that does not match their corresponding password stored in database the +"Invalid password" message is displayed. If the password is correct, the main window of the application is shown. + +This is implemented in the file \texttt{main.py} in the function \texttt{start\_app()}. + +\subsection{File selection} +Selecting files is handled by the button \texttt{Choose a file to be sent}. When the button is pressed, a dialog window is opened where the user can select +a file. The implementation of this is done using module \texttt{filedialog} from \texttt{tkinter} library. If a file is not selected, the window wont let them +select only path that is not pointing to a file. If the window is closed without selecting the filepath is kept empty. + +The code itself can be seen in file \texttt{file\_share/app/app.py} in the function \texttt{get\_file()}. + +\subsection{Listing users} +There are two functionalities that are related to listing users. The first one is listing all non-friend users that are in the database. The second one +is listing all users that user has added as a friends. Both of these open a new windows where the users are listed. The difference between these two windows +is that in window where non-friend users are listed, there is a button to add them as a friend. + +The list friends functionality is simpler and it is done by retrieving all users from the database and filtering out the ones that are friends with the user. +The method that handles this is the \texttt{get\_all\_users} from \texttt{file\_share/database/\_\_init.py\_\_}, where there is friend parameter which is by default set to \texttt{True}.\\ +The non-friends is a bit more complicated first the list of all users except friends is collected from the database. It is listed in the window. If user wants +to add a friend, he selects that user from the list and pressed button "Befriend this user", this calls a function \texttt{befriend} in \texttt{file\_share/database/\_\_init.py\_\_}. +This adds the user as a friend by setting the \texttt{is\_friend} attribute to \texttt{True}. + +\subsection{File sending initialization} +For this core functionality there is attached a flow chart for easier understandability. It is implemented in function \texttt{send\_file} in \texttt{file\_share/app/app.py}. +The function first checks if the file for transfer is selected, if that is so then the file is prepared for transfer and file transfer is attepmted sending is done by +function \texttt{send\_or\_store\_file} from \texttt{file\_share/sender/sender.py}. As a return there is a \texttt{SendStatus} object, which is enum created for this application, +there are 5 states that the object can be in, those can be viewed in \texttt{file\_share/definitions/enums.py}. Based on the the state of the user which is passed to the function, +the file will either be sent, stored in outgoing queue or nothing will happen.\\ + +If the file is to be stored then it is scheduled to be added to files table in the database using \texttt{store\_file} from \texttt{file\_share/database/\_\_init.py\_\_}. + +\begin{figure}[ht] + \centering + \includegraphics[scale = 0.5]{images/filesending.png} + \caption{Flowchart of file sending initialization} + \label{fig:flowchart} +\end{figure} +\subsection{File sending} +In this section the process of file transfer itself is described. It is accompanied by a figure that shows the sides of the communication and the steps +that each of the sides takes.\\ +This whole process is based on using a one time use API key which is generated by the recieving side of the communication. This key is then used by the sender in +the header of the file transfer as a verification of the communication. +Firstly the sender extracts from the file object that is passed as a parameter the username of target user, then on the basis of this username retrieves the +address of this user from database, unless the file has address override in it. In which case the override adress is used as a target address.\\ +Then the SSL context of given username is taken and using aiohttp session a request is made to the target user. The target then verifies if the sender is a friend, +if not then exception 401 is raised and the communication is terminated by this exception. If the sender is a known friend, then an API key is generated, encoded using the senders public +key extracted from their certificate and sent back to the sender as a response. This verification and key generating is done in \texttt{file\_share/receiver/receiver\_api.py} in method \texttt{auth()}.\\ +When sender recieves this API key, it is decrypted using the senders private key, then a FormData object is created, the file is added to it and finally a sending +is done with this FormData object, where as the header is included this API key.\\ +On the recieving side, when the file transfer is done the API key is removed from database as it is one time use only. If the key is not found in database then +a 401 exception is raised and this ends the communication. Otherwise a new file object is created with the incoming attribute set to True, the object is timestamped, +and sender username is associated with it. Then it is added to the database table and the file's name is returned as a response.\\ + +\begin{figure} + \centering + \includegraphics[scale = 0.5]{images/file_send.png} + \caption{Flowchart of file transfer} + \label{fig:filetransfer} +\end{figure} \ No newline at end of file diff --git a/docs/text/gui.tex b/docs/text/gui.tex new file mode 100644 index 0000000..031e72d --- /dev/null +++ b/docs/text/gui.tex @@ -0,0 +1,85 @@ +\section{GUI} +The GUI start by running command \texttt{python3 main.py} in the virtual enviroment. + +\subsection{Login screen} +When the app is opened, the user is greeted by a login screen. The user can either login with an existing account if you used the app in the past as shown in Figure \ref{fig:login} +or create a new one wiht username and password as in Figure \ref{fig:reg}. +\begin{figure}[h] + \centering + \includegraphics[width=0.5\textwidth]{images/login.png} + \caption{Login screen} + \label{fig:login} +\end{figure} + +\begin{figure}[h] + \centering + \includegraphics[width=0.5\textwidth]{images/register.png} + \caption{Register screen} + \label{fig:reg} +\end{figure} + +\newpage + +\subsection{Main window} +After the login the user will se the main application window. +\begin{figure}[h] + \centering + \includegraphics[width=0.5\textwidth]{images/mainwindow.png} + \caption{Main window} + \label{fig:main} +\end{figure} + +\subsubsection{File selection} +Selecting file for transfer is done by the button \texttt{Choose a file to be sent} that is shown in Figure \ref{fig:main}. +After pressing the button, the file selection window will open and user can select the file they want to send. + +\subsubsection{User discovery and friend addition} +There are 2 ways to discorver users using the app. Either user can scan the whole network for the active apps that are running using \texttt{List Non friends in DB} button. +\begin{figure}[h] + \centering + \includegraphics[width=0.5\textwidth]{images/ListNonFriends.png} + \caption{Adding users to db by scanning the network} + \label{fig:LNF} +\end{figure} +The other way is by using button \texttt{Scan IP}. User enters the address he wants to scan in the text field next to the button and then presses the button. +\begin{figure}[h] + \centering + \includegraphics[width=0.5\textwidth]{images/ScanIP.png} + \caption{Adding user to db by scanning given IP} + \label{fig:scanIP} +\end{figure} + +Adding friends is handled by pressing the button \texttt{Befriend this user} that we can see in Figure \ref{fig:LNF}. + +\subsubsection{Listing of users} +There are 2 ways you can list users that user can interact with. Either list only those users who user added to their friend list or list all users in database. +For this there are 2 buttons shown on Figure \ref{fig:main}. Those are \texttt{List friends} and \texttt{List Non friends in DB} respectively. + +\subsubsection{Listing queued files} +Files that cannot be sent immediately are queued and will be sent when the user is available. The user can see the list of queued files by pressing the button +\texttt{List outgoing queue}.\\ + +\subsubsection{Friend selection} +Choosing a friend who will be the target of the file transfer is done by choosing one friend from the rolldown menu next to the \texttt{List friends} button.\\ + +\subsubsection{File sharing} +After selecting the file and the friend to send it to, the user can press the button \texttt{Send file} to send the file to the selected friend. +If the target is not available, the app will prompt the user with following message and put the file in the queue to be sent later. +\begin{figure}[h] + \centering + \includegraphics[width=0.5\textwidth]{images/notAvailable.png} + \caption{Target not available} + \label{fig:notAvail} +\end{figure} + +Recieving the file is done by the target user pressing the button \texttt{List incoming queue} which opens window with all files that are waiting to be +recieved. The user can either save or ignore a single file or accept all files in the queue.\\ + +\begin{figure}[ht] + \centering + \includegraphics[width=0.5\textwidth]{images/queue.png} + \caption{Incoming queue} + \label{fig:inQueue} +\end{figure} + +If the user chooses to save the file he is prompted with window to select the location where the file will be saved.\\ \ No newline at end of file diff --git a/docs/text/installation.tex b/docs/text/installation.tex new file mode 100644 index 0000000..35e687c --- /dev/null +++ b/docs/text/installation.tex @@ -0,0 +1,16 @@ +\section{Installation} + +Installation is done using the following steps for RPM based system. For APT based systems, replace \texttt{dnf} with \texttt{apt}. +For MacOS use \href{https://brew.sh/}{Homebrew} package manager. +\begin{lstlisting}[language=bash] + sudo dnf install python3 python3-pip python3-virtualenv + python3 -m venv .venv + source .venv/bin/activate + python3 -m pip install pdm + pdm install +\end{lstlisting} + +For running the application you can use the following command: +\begin{lstlisting}[language=bash] + python3 main.py +\end{lstlisting} diff --git a/docs/text/intro.tex b/docs/text/intro.tex new file mode 100644 index 0000000..03cf104 --- /dev/null +++ b/docs/text/intro.tex @@ -0,0 +1,19 @@ +\section{Introduction} +The aim of this project created for a university course named Cryptography was to create an application for sensitive file sharing. It is designed to be used by +users who are on the same network and want share encrypted files without the user of a server, that means using peer-to-peer communication. The application is +written in Python 3.12 with the use of supporting libraries which will be described later in this introduction. The app is accopmanied by a graphical user +interface. Application is designed to be used on any operating system that supports Python 3.12 and the required libraries. \\ + +The required files to run this application are all in this repository. Apart from the \texttt{main.py} file all of the code is in the file\_share directory. +The \texttt{main.py} file is the entry point of the application and is used to start the GUI. As input for the app there is nothing required apart from a file +that a user wants to send. The output is a file that was sent to the user from another user using the app. \\ + +The application uses the following libraries: +\begin{itemize} + \item \texttt{Tkinter} - for the Graphical User Interface + \item \texttt{Uvicorn} - used for reciever realisation + \item \texttt{FastAPI} - for the reciever API + \item \texttt{SQLAlchemy} - for the database implementation + \item \texttt{Cryptography} - for encryption and decryption of files + \item \texttt{AIOHTTP} - for communication between the sender and the reciever +\end{itemize} \ No newline at end of file diff --git a/file_share/app/app.py b/file_share/app/app.py index 1d60bb1..1f461df 100644 --- a/file_share/app/app.py +++ b/file_share/app/app.py @@ -41,6 +41,11 @@ def __init__(self, token: bytes, config: dict[str, Any]): # Helper methods for Tkinter interactions def get_file(self, app_window): + """ + Select a file to be sent. + Args: + app_window: Tkinter window + """ self.file_path = fd.askopenfilename() file_label = Entry(app_window) file_label.delete(0, END) @@ -48,15 +53,29 @@ def get_file(self, app_window): file_label.grid(row=4, column=1, sticky=EW) def set_target(self, friend): + """ + Set target user for file transfer. + Args: + friend: username of the target user + """ self.target_field.delete(0, END) self.target_field.insert(0, friend) - # Takes regular file and prepares it to be sent via the app def prepare_file(self, file_path, target): + """ + Prepare a file to be sent via the app. + Args: + file_path: path to the file + target: username of the target user + """ return load_file(file_path, target) - # Get list of friends from db and insert it into a listbox def show_friends(self): + """ + Show a list of friends in a new window. + Args: + None + """ top = Tk() friends_listbox = Listbox(top, width=50) friends = self.list_friends() @@ -67,8 +86,12 @@ def show_friends(self): friends_listbox.pack() top.mainloop() - # Gets currently selected file from a listbox def get_selected_file_from_listbox(self, ListBox): + """ + Retrieves the currently selected file from a listbox. + Args: + ListBox: Tkinter listbox + """ get_selected_file = ListBox.get(ACTIVE) # Make sure to always just use the database file index, since file might have a different index in the files list, listbox and db selected_file_index = re.search("\d+", get_selected_file).group(0) @@ -81,6 +104,11 @@ def get_selected_file_from_listbox(self, ListBox): # Pulls the outgoing queue from the db and inserts it into a listbox, currently also testing file saving from queue here def show_outgoing_queue(self): + """ + Takes the outgoing queue from the database and inserts it into a listbox. + Args: + None + """ top = Tk() outgoing_listbox = Listbox(top, width=50) files = self.list_outgoing_queue() @@ -91,13 +119,20 @@ def show_outgoing_queue(self): outgoing_listbox.pack() top.mainloop() - # Lists incoming queue, with the options to either save all the files, save a specific one, or to remote a file def show_incoming_queue(self): + """ + Show the incoming queue in a new window where the user can save files, ignore them or save all. + Args: + None + """ top = Tk() incoming_listbox = Listbox(top, selectmode=SINGLE, width=50) incoming_listbox.pack() def update_list(): + """ + Updates the list of incoming files. + """ files = self.list_incoming_queue() incoming_listbox.delete(0, END) for file in files: @@ -145,6 +180,11 @@ def update_list(): top.mainloop() def show_non_friends(self): + """ + Shows a list of non-friends who are reacheable by the app and allows user to to add them asi friends. + Args: + None + """ top = Tk() befriend_button = Button( top, @@ -162,12 +202,20 @@ def show_non_friends(self): top.mainloop() def get_own_fingerprint(self): + """ + Show logged in user's fingerprint. + """ top = Tk() fingerprint = self.get_my_fingerprint() finger_label = Label(top, text=fingerprint) finger_label.pack() def get_friends_fingerprint(self, name): + """ + Show a friend's fingerprint. + Args: + name: username of the friend + """ top = Tk() fingerprint = self.get_user_fingerprint(username=name) print(fingerprint) @@ -175,6 +223,11 @@ def get_friends_fingerprint(self, name): fingerprint_label.pack() def get_all_users(self): + """ + Return all users who are reachable whetever they are in friends or not + Args: + None + """ friends = self.list_friends() non_friends = self.list_non_friends() return friends + non_friends @@ -349,16 +402,25 @@ def list_outgoing_queue(self) -> list[Files]: # Implemented return self.database.get_all_files(False) def save_file_from_queue(self, file: Files, path: Union[str, Path]): # Implemented - """Save an incoming file.""" + """ + Save an incoming file. + Args: + file: File to be saved + path: Path where the file will be saved + """ try: decrypted_file = self.database.decrypt_file(file.idx, self.token) decrypted_file.save(path) self.database.remove_file_from_queue(file.idx) except OSError as e: - print(f"File {file.filename} could not be saved.", e) + print(f"File {file.filenaKRYTex/main.texme} could not be saved.", e) def save_all_files_from_queue(self, path: Union[str, Path]): # Implemented - """Save all files in the queue to the specified location.""" + """ + Save all files in the queue to the specified location. + Args: + path: Path where the files will be saved + """ if isinstance(path, str): path = Path(path) if not path.is_dir(): @@ -367,7 +429,11 @@ def save_all_files_from_queue(self, path: Union[str, Path]): # Implemented self.save_file_from_queue(file, path) def ignore_incoming_file(self, file: Files) -> bool: # Implemented - """Ignore a file that is incoming and remove it from the database.""" + """ + Ignore a file that is incoming and remove it from the database. + Args: + file: File to be ignored + """ if not file.incoming: return False self.database.remove_file_from_queue(idx=file.idx) @@ -389,8 +455,9 @@ def check_ip(self, ip_address: str) -> Optional[str]: # NOT implemented """ Check if the user with this IP uses this protocol. This person will be added to the known users (not friends yet). - - returns username on success, None otherwise + Args: + ip_address: IP address to check + returns: username on success, None otherwise """ try: asyncio.run(send_cert(ip_address, self.database)) diff --git a/file_share/app/init_app.py b/file_share/app/init_app.py index ec625f4..7666286 100644 --- a/file_share/app/init_app.py +++ b/file_share/app/init_app.py @@ -9,10 +9,27 @@ def is_first_init(): + """ + Fuction to check if this is the first time the app is run. + Args: + None + Returns: + bool: True if this is the first time the app is run, False otherwise + """ return db_instance.get_me() is None +# def first_init_app(name: str, password: str, config: dict[str, Any]) -> FileShareApp: + """ + If this is the first time the app is run, this function will create a new user and generate a cert for it. + Args: + name (str): Name of the user + password (str): Password of the user + config (dict[str, Any]): Configuration dictionary + Returns: + FileShareApp: FileShareApp instance + """ if not db_instance.add_me(name, password): raise ValueError("This is not first app run.") token = db_instance.get_token(password) @@ -21,4 +38,12 @@ def first_init_app(name: str, password: str, config: dict[str, Any]) -> FileShar def init_app(password: str, config: dict[str, Any]) -> FileShareApp: + """ + Function to initialize the app if the user already exists. + Args: + password (str): Password of the user + config (dict[str, Any]): Configuration dictionary + returns: + FileShareApp: FileShareApp instance + """ return FileShareApp(db_instance.get_token(password), config) diff --git a/file_share/definitions/procedures.py b/file_share/definitions/procedures.py index 3927932..08c8f71 100644 --- a/file_share/definitions/procedures.py +++ b/file_share/definitions/procedures.py @@ -19,11 +19,25 @@ def _get_key(token: bytes, seed: bytes) -> bytes: def encrypt(data: bytes, token: bytes, seed: bytes) -> bytes: + """ + Encrypt data using token and seed. + Args: + data (bytes): data to be encrypted + token (bytes): token used for encryption + seed (bytes): seed used for encryption + """ encryption_factory = Fernet(_get_key(token, seed)) return encryption_factory.encrypt(data) def decrypt(data: bytes, token: bytes, seed: bytes) -> bytes: + """ + Decrypt data using token and seed. + Args: + data (bytes): data to be decrypted + token (bytes): token used for decryption + seed (bytes): seed used for decryption + """ decryption_factory = Fernet(_get_key(token, seed)) return decryption_factory.decrypt(data) diff --git a/file_share/receiver/receiver_api.py b/file_share/receiver/receiver_api.py index 121a473..79f8f22 100644 --- a/file_share/receiver/receiver_api.py +++ b/file_share/receiver/receiver_api.py @@ -73,6 +73,12 @@ async def auth(self, name: str): return base64.b64encode(encrypted) async def friends(self, file: UploadFile, request: Request): + """ + Add friend to the database + Args: + file (UploadFile): PEM-encoded certificate + request (Request): Request object + """ try: data = await file.read() certificate = Certificate(data) diff --git a/file_share/sender/sender.py b/file_share/sender/sender.py index 3302a2c..aa24061 100644 --- a/file_share/sender/sender.py +++ b/file_share/sender/sender.py @@ -56,6 +56,12 @@ async def is_active(username: str, address: Optional[str] = None) -> bool: async def send_all_from_queue(token: bytes, db_connection: Database): + """ + Function that sends all files from the queue to the users if they are active. + Args: + token (bytes): Auth token of this app to decrypt private key + db_connection (Database): Database object + """ queue = db_connection.get_all_files(False) for file in queue: username = file.username @@ -128,11 +134,21 @@ async def send_file(file: DecryptedFile, token: bytes, db_connection: Database) async def send_or_store_file( token: bytes, file: DecryptedFile, db_connection: Database ) -> SendStatus: + """ + Function that checks if the user is active and sends the file if they are, otherwise it stores it + in queue. + Args: + token (bytes): Auth token of this app to decrypt private key + file (DecryptedFile): The file which is to be sent + db_connection (Database): Database object + """ + # Retrieve user from db user = db_connection.get_user(file.username, only_friends=False) if not user: return SendStatus.UNKNOWN_USER if not user.is_friend: return SendStatus.NOT_FRIEND + # Check if user is active if not store file to queue if not await is_active(file.username, file.override_address): db_connection.store_file(file, token) print("User inactive, storing file to queue.") @@ -147,6 +163,10 @@ async def send_or_store_file( class StoppableQueueSender(StoppableThread): + """ + Definition of a thread that periodically checks the queue and sends files to users if they are active. + """ + def __init__(self, token: bytes, *args, **kwargs): self.token = token self.database: Database = Database()