encryptare + adaugare de imagini
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
from fastapi import APIRouter, Header, HTTPException
|
||||
from .get_private_key import get_private_key
|
||||
|
||||
'''
|
||||
The 'messages' packages contains the router configuration for the 'Messages' endpoints.
|
||||
'''
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/get_private_key")
|
||||
async def get_private_key_router(authorization_key: str = Header('Authorization_Key', convert_underscores=False)):
|
||||
expected_value = 'messenger'
|
||||
if not authorization_key == expected_value:
|
||||
raise HTTPException(status_code=401, detail="Unauthorized: Incorrect header value")
|
||||
return get_private_key()
|
||||
|
||||
__all__ = ["router"]
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
def get_private_key():
|
||||
# Your implementation to retrieve the private key goes here
|
||||
# For testing purposes, return a dummy private key
|
||||
return {"private_key": '8f0240aeb3fe8e16'}
|
||||
|
||||
Binary file not shown.
@@ -2,9 +2,11 @@ from fastapi import FastAPI
|
||||
from api.v1.endpoints.users import router as users_router
|
||||
from api.v1.endpoints.messages import router as messages_router
|
||||
from api.v1.endpoints.auth import router as auth_router
|
||||
from api.v1.endpoints.encryption_key import router as encryption_key_router
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.include_router(users_router, tags=["Users"], prefix="/users")
|
||||
app.include_router(messages_router, tags=["Messages"], prefix="/messages")
|
||||
app.include_router(auth_router, tags=["Auth"], prefix="/auth")
|
||||
app.include_router(encryption_key_router, tags=["Encryption"], prefix="/encryption")
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.primitives import padding
|
||||
|
||||
|
||||
class AESEncryptor:
|
||||
def __init__(self, key):
|
||||
if not isinstance(key, bytes):
|
||||
key = key.encode('utf-8') # Assuming UTF-8 encoding, adjust if needed
|
||||
if len(key) != 16:
|
||||
raise ValueError("AES key must be 16 bytes in length")
|
||||
self.key = key
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
cipher = Cipher(algorithms.AES(self.key), modes.ECB(), backend=default_backend())
|
||||
encryptor = cipher.encryptor()
|
||||
plaintext = plaintext.encode('utf-8')
|
||||
padder = padding.PKCS7(algorithms.AES.block_size).padder()
|
||||
padded_plaintext = padder.update(plaintext) + padder.finalize()
|
||||
ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize()
|
||||
return ciphertext.hex()
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
cipher = Cipher(algorithms.AES(self.key), modes.ECB(), backend=default_backend())
|
||||
decryptor = cipher.decryptor()
|
||||
ciphertext = bytes.fromhex(ciphertext)
|
||||
decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
|
||||
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
|
||||
return unpadded_data.decode('utf-8')
|
||||
@@ -0,0 +1 @@
|
||||
from .AESEncryptor import AESEncryptor
|
||||
@@ -13,7 +13,6 @@ class RequestBuilder:
|
||||
__method (str): HTTP method (GET, POST, ...)
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.__url = ""
|
||||
self.__headers = {}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import json
|
||||
import tkinter as tk
|
||||
|
||||
from app.http import request_builder
|
||||
from app.views.login import LoginView
|
||||
from app.views.initial import InitialView
|
||||
from app.views.signup import SignupView
|
||||
from app.views.main_page import MainPageView
|
||||
from app.encryption import AESEncryptor
|
||||
import json
|
||||
|
||||
|
||||
class MainApplication(tk.Tk):
|
||||
@@ -20,9 +24,21 @@ class MainApplication(tk.Tk):
|
||||
|
||||
self.current_logged_user = {}
|
||||
|
||||
self.define_http_params()
|
||||
self.encryptor = self.initialize_encryptor()
|
||||
|
||||
self.current_view = None
|
||||
self.change_to_initial_view()
|
||||
|
||||
def define_http_params(self):
|
||||
self.url = "http://localhost:8000/encryption/get_private_key"
|
||||
self.headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization_Key": "messenger"
|
||||
}
|
||||
self.method = "GET"
|
||||
self.params = None
|
||||
|
||||
def change_to_initial_view(self):
|
||||
self.change_view(InitialView(self))
|
||||
|
||||
@@ -51,3 +67,14 @@ class MainApplication(tk.Tk):
|
||||
self.current_logged_user['id'] = data['info']['id']
|
||||
self.current_logged_user['username'] = data['info']['username']
|
||||
self.current_logged_user['email'] = data['info']['email']
|
||||
|
||||
def initialize_encryptor(self):
|
||||
request_builder.set_url(self.url)
|
||||
request_builder.set_headers(self.headers)
|
||||
request_builder.set_method(self.method)
|
||||
|
||||
result = request_builder.build().make_request()
|
||||
json_data = json.loads(result.text)
|
||||
private_key = json_data.get("private_key", None)
|
||||
|
||||
return AESEncryptor(private_key)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import tkinter as tk
|
||||
from app.http import request_builder
|
||||
from app.views.message import MessageView
|
||||
import tkinter.filedialog
|
||||
|
||||
|
||||
class ChatWindow(tk.Toplevel):
|
||||
@@ -9,13 +12,16 @@ class ChatWindow(tk.Toplevel):
|
||||
This view is opened in a separate window and takes care of the communication between the two users. It receives
|
||||
both the users' information and retrieves the conversation_url.
|
||||
"""
|
||||
def __init__(self, master=None, user_data=None, current_logged_user=None):
|
||||
|
||||
def __init__(self, master=None, encryptor=None, user_data=None, current_logged_user=None):
|
||||
super().__init__(master)
|
||||
|
||||
self.user_data = user_data
|
||||
self.current_logged_user = current_logged_user
|
||||
self.define_get_conversation_url_params()
|
||||
|
||||
self.encryptor = encryptor
|
||||
|
||||
self.geometry("700x700")
|
||||
|
||||
self.conversation_url = self.get_conversation_url()
|
||||
@@ -46,7 +52,7 @@ class ChatWindow(tk.Toplevel):
|
||||
self.params = {}
|
||||
self.body = {
|
||||
'conversation_url': self.conversation_url,
|
||||
'message': message,
|
||||
'message': self.encryptor.encrypt(message),
|
||||
'message_type': message_type,
|
||||
'sender': self.current_logged_user['id']
|
||||
}
|
||||
@@ -59,6 +65,7 @@ class ChatWindow(tk.Toplevel):
|
||||
|
||||
# Display existing conversation
|
||||
for message in conversation:
|
||||
message['message'] = self.encryptor.decrypt(message['message'])
|
||||
message_view = MessageView(self.messages_frame, message, self.current_logged_user, self.user_data)
|
||||
message_view.pack()
|
||||
|
||||
@@ -74,6 +81,28 @@ class ChatWindow(tk.Toplevel):
|
||||
self.send_button = tk.Button(self.input_frame, text="Send", command=lambda: self.send_message())
|
||||
self.send_button.pack(side=tk.RIGHT)
|
||||
|
||||
self.select_photo_button = tk.Button(self.input_frame, text="Select Photo", command=self.select_photo)
|
||||
self.select_photo_button.pack(side=tk.LEFT)
|
||||
|
||||
def select_photo(self):
|
||||
initial_dir = os.path.expanduser("~")
|
||||
file_path = tkinter.filedialog.askopenfilename(
|
||||
initialdir=initial_dir, filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif;*.bmp")])
|
||||
with open(file_path, 'rb') as file:
|
||||
file_content = file.read()
|
||||
image_base64 = base64.b64encode(file_content).decode('utf-8')
|
||||
self.define_send_message_params(image_base64, "image")
|
||||
|
||||
request_builder.set_url(self.url)
|
||||
request_builder.set_body(self.body)
|
||||
request_builder.set_headers(self.headers)
|
||||
request_builder.set_method(self.method)
|
||||
request_builder.set_params(self.params)
|
||||
|
||||
request_builder.build().make_request()
|
||||
|
||||
self.update_chat()
|
||||
|
||||
def update_chat(self):
|
||||
"""
|
||||
This function is scheduled each second to make a request to the API to retrieve the latest chat between the
|
||||
@@ -85,6 +114,7 @@ class ChatWindow(tk.Toplevel):
|
||||
|
||||
self.define_get_conversation_params()
|
||||
for message in self.get_conversation():
|
||||
message['message'] = self.encryptor.decrypt(message['message'])
|
||||
message_view = MessageView(self.messages_frame, message, self.current_logged_user, self.user_data)
|
||||
message_view.pack()
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ class MainPageView(tk.Frame):
|
||||
def create_widgets(self):
|
||||
"""Takes care of the creation of the UI"""
|
||||
# Create a main frame to center the user frame and button frame
|
||||
main_frame = tk.Frame(self.master)
|
||||
main_frame.pack(expand=True)
|
||||
self.main_frame = tk.Frame(self)
|
||||
self.main_frame.pack(expand=True)
|
||||
|
||||
# Create a frame to hold the user frames
|
||||
user_frame_container = tk.Frame(main_frame)
|
||||
user_frame_container = tk.Frame(self.main_frame)
|
||||
user_frame_container.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
|
||||
|
||||
users_data = self.get_user_list()
|
||||
@@ -49,7 +49,7 @@ class MainPageView(tk.Frame):
|
||||
user_frame_instance.grid(row=row, column=column, padx=10, pady=10)
|
||||
|
||||
# Create a frame to hold the buttons
|
||||
button_frame = tk.Frame(main_frame)
|
||||
button_frame = tk.Frame(self.main_frame)
|
||||
button_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
|
||||
|
||||
# Create buttons in the button frame
|
||||
@@ -64,7 +64,7 @@ class MainPageView(tk.Frame):
|
||||
def open_chat(self, user_data):
|
||||
"""Opens a chat in a different window"""
|
||||
# Pass both the clicked user data and the current logged user info to ChatWindow
|
||||
chat_window = ChatWindow(self, user_data, self.master.current_logged_user)
|
||||
chat_window = ChatWindow(self, self.master.encryptor, user_data, self.master.current_logged_user)
|
||||
chat_window.mainloop() # Main loop for the new window
|
||||
|
||||
def get_user_list(self):
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import tkinter as tk
|
||||
|
||||
import emoji
|
||||
from PIL import Image, ImageTk
|
||||
from PIL import Image, ImageTk, UnidentifiedImageError
|
||||
import io
|
||||
import base64
|
||||
|
||||
import emoji
|
||||
|
||||
class MessageView(tk.Frame):
|
||||
"""
|
||||
This view is the message that appears in main windows of the chat. This is configured to hold text and images.
|
||||
"""
|
||||
def __init__(self, master, message, current_logged_user, user_data, *args, **kwargs):
|
||||
super().__init__(master, *args, **kwargs)
|
||||
self.current_logged_user = current_logged_user
|
||||
@@ -18,49 +13,49 @@ class MessageView(tk.Frame):
|
||||
|
||||
def create_widgets(self, message):
|
||||
is_sent_by_current_user = 1
|
||||
sender_name = None
|
||||
if is_sent_by_current_user:
|
||||
sender_name = self.current_logged_user['username']
|
||||
else:
|
||||
sender_name = self.user_data['username']
|
||||
|
||||
message_frame = tk.Frame(self) # Set a background color for the message frame
|
||||
message_label = None
|
||||
image_label = None
|
||||
sender_name = (
|
||||
self.current_logged_user["username"]
|
||||
if is_sent_by_current_user
|
||||
else self.user_data["username"]
|
||||
)
|
||||
|
||||
message_frame = tk.Frame(self)
|
||||
pack = None
|
||||
|
||||
match message['message_type']:
|
||||
case 'text':
|
||||
decoded_message = self.decode_emoji(f"sender: {sender_name}\nmessage: {message['message']}")
|
||||
pack = tk.Label(message_frame, text=decoded_message, bg='white')
|
||||
case 'image':
|
||||
decoded_image = self.decode_image(message['message'])
|
||||
match message["message_type"]:
|
||||
case "text":
|
||||
decoded_message = self.decode_emoji(
|
||||
f"sender: {sender_name}\nmessage: {message['message']}"
|
||||
)
|
||||
pack = tk.Label(message_frame, text=decoded_message, bg="white")
|
||||
self.decoded_message = decoded_message # Keep a reference to prevent garbage collection
|
||||
case "image":
|
||||
decoded_image = self.decode_image(message["message"])
|
||||
pack = tk.Label(message_frame, image=decoded_image)
|
||||
pack.image = decoded_image # Keep a reference to prevent garbage collection
|
||||
|
||||
pack.pack()
|
||||
|
||||
if is_sent_by_current_user:
|
||||
alignment = 'right'
|
||||
alignment = "right"
|
||||
else:
|
||||
alignment = 'left'
|
||||
alignment = "left"
|
||||
|
||||
message_frame.pack(side=alignment, padx=5, pady=5)
|
||||
|
||||
def decode_emoji(self, message):
|
||||
emoji_mapping = {
|
||||
':)': '😊',
|
||||
':(': '☹️',
|
||||
':D': '😃',
|
||||
':P': '😛',
|
||||
';)': '😉',
|
||||
'XD': '😆',
|
||||
'<3': '❤️',
|
||||
'O:)': '😇',
|
||||
':/': '😕',
|
||||
':*': '😘',
|
||||
'8)': '😎',
|
||||
":)": "😊",
|
||||
":(": "☹️",
|
||||
":D": "😃",
|
||||
":P": "😛",
|
||||
";)": "😉",
|
||||
"XD": "😆",
|
||||
"<3": "❤️",
|
||||
"O:)": "😇",
|
||||
":/": "😕",
|
||||
":*": "😘",
|
||||
"8)": "😎",
|
||||
}
|
||||
|
||||
for emoji_string, unicode_character in emoji_mapping.items():
|
||||
@@ -68,8 +63,19 @@ class MessageView(tk.Frame):
|
||||
return message
|
||||
|
||||
def decode_image(self, encoded_image):
|
||||
# Assume encoded_image is a base64-encoded image
|
||||
decoded_image = base64.b64decode(encoded_image)
|
||||
image = Image.open(io.BytesIO(decoded_image))
|
||||
image = ImageTk.PhotoImage(image)
|
||||
return image
|
||||
try:
|
||||
decoded_image = base64.b64decode(encoded_image)
|
||||
image_io = io.BytesIO(decoded_image)
|
||||
|
||||
original_image = Image.open(image_io)
|
||||
resized_image = original_image.resize((100, 100), Image.ADAPTIVE)
|
||||
|
||||
self.image = resized_image
|
||||
image = ImageTk.PhotoImage(self.image)
|
||||
return image
|
||||
except UnidentifiedImageError as e:
|
||||
print(f"Error decoding image: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user