Skip to content

Commit

Permalink
Working draft of user search (from home user view)
Browse files Browse the repository at this point in the history
allows selecting users to initiate a chat with based on a pattern
  • Loading branch information
etrian-dev committed May 2, 2022
1 parent 6848ed7 commit 83bf172
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10.2-bullseye
FROM python:3.9-alpine
ARG FLASK_APP=chatroom
ARG FLASK_ENV=development
WORKDIR /usr/src/
Expand Down
5 changes: 4 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
- index page on route '/'
- User detail page
- Encryption/decryption of message data with RSA
- Local message storage (sent messages) in plaintext on file or db
- logout function and proper authentication (maybe sessions)
- Fix msgstore to write into ./instance
- Improve searching (onchange, but clearing the previous matches from the select)
- Fix timestamp for message viewing order in messages.html
+ Needs revision on Chat.py:display_chat
- Develop & testing of query maker (in db.py)
# Container
- Add volume to make source changes shared between host and container
25 changes: 25 additions & 0 deletions chatroom/Admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from . import db

from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from sqlite3 import Connection, Cursor, DatabaseError

blueprint = Blueprint('admin', __name__,url_prefix='/admin')

@blueprint.route('/', methods=['GET'])
@blueprint.route('/users', methods=['GET'])
def show_users():
'''Shows all users in the application'''
conn = db.get_db()
try:
cursor = conn.execute('''
SELECT * FROM Users''')
all_users = dict()
for user in cursor.fetchall():
all_users[user['user_id']] = {
"username": user['username'],
"password": user['password']}
return render_template('admin_console.html', all_users=all_users)
except DatabaseError:
return "DB error"
63 changes: 38 additions & 25 deletions chatroom/Chat.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
from . import db
from . import Msg

from time import time
from datetime import datetime
from sqlite3 import Connection, Cursor, DatabaseError
from sqlite3 import Connection, DatabaseError
from json import load

from flask import (Blueprint, request, render_template)
from . import db
from . import Msg
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
Blueprint, flash, g, redirect, render_template, request, url_for
)


class Chat:
"""Defines a chat between two users.
"""

def __init__(self, user1: str, user2: str):
self.chat_id = None
self.participant1 = user1
Expand All @@ -25,6 +27,7 @@ def add_message(data: str, timestamp):
"""Adds a new message to this chat.
"""
self.messages.append(Msg.EncryptedMsg(data, timestamp))

def delete_message(target):
"""Deletes a message from this chat.
"""
Expand All @@ -34,18 +37,20 @@ def delete_message(target):
except ValueError:
print("Message", target, "not found")

from flask import (Blueprint, request, render_template)

blueprint = Blueprint('chat', __name__, url_prefix='/chats')


@blueprint.route('/<int:user_id>', methods=['GET'])
def home_user(user_id: int):
cur = db.get_db().cursor()
user_data = dict()
user_data['user_id'] = user_id
try:
# Fetch the username
cur.execute('''SELECT username FROM Users WHERE user_id = ?''', [user_id])
cur.execute(
'''SELECT username FROM Users WHERE user_id = ?''',
[user_id])
user_data['username'] = cur.fetchone()['username']
# fetch this users's chats
# FIXME: maybe use ? IN (participant1, participant2)
Expand All @@ -57,8 +62,10 @@ def home_user(user_id: int):
# create a chat object
ch = Chat(row['participant1'], row['participant2'])
ch.chat_id = row['chat_id']
ch.creation_time = datetime.strptime(row['creation_tm'], "%Y-%m-%d %H:%M:%S").isoformat()
ch.last_activity = datetime.strptime(row['last_mod_tm'], "%Y-%m-%d %H:%M:%S").isoformat()
ch.creation_time = datetime.strptime(
row['creation_tm'], "%Y-%m-%d %H:%M:%S").isoformat()
ch.last_activity = datetime.strptime(
row['last_mod_tm'], "%Y-%m-%d %H:%M:%S").isoformat()
# insert that into the user_data dict
if 'chats' not in user_data:
user_data['chats'] = [ch]
Expand All @@ -69,21 +76,23 @@ def home_user(user_id: int):
return render_template('chats.html')
return render_template('chats.html', user_data=user_data)


@blueprint.route('/<int:creator>/', methods=['POST'])
def create_chat(creator):
# TODO: check that the current session for this client is logged as <creator>
# TODO: check that the current session for this client is logged as
# <creator>
error = None
matching_users = []
if request.method == 'POST':
# get the username of the user the creator wants to chat with
username = request.form['username']
username = request.form['matching-users']
# get matching users
db_conn = db.get_db()
try:
cursor = db_conn.execute('''
SELECT user_id, username, password
FROM Users
WHERE username=?;''', [username])
WHERE user_id=?;''', [username])

# TODO: handle multiple users with the same username
match = cursor.fetchone()
Expand All @@ -95,14 +104,16 @@ def create_chat(creator):
db_conn.commit()
cursor.close()

return redirect(url_for('chat.display_chat', user=creator, other=match['user_id']))
return redirect(url_for('chat.display_chat',
user=creator, other=match['user_id']))
except DatabaseError:
error = 'DB lookup or insert failed'
return render_template('chat_choice.html', matching_users)
return "Unimplemented"


@blueprint.route('/<int:user>/<int:other>', methods=['GET'])
def display_chat(user, other):
chat_info = dict()
chat_info = {}
chat_info['this_user_id'] = user
chat_info['other_user_id'] = other
db_conn = db.get_db()
Expand All @@ -128,11 +139,12 @@ def display_chat(user, other):
''', [chat['chat_id'], other])
msgs_encoded = cur.fetchall()
# build breadcrumb
breadcrumb = dict()
breadcrumb = {}
breadcrumb['home'] = url_for('chat.home_user', user_id=user)
breadcrumb[chat_info['other_user']] = url_for('chat.display_chat', user=user, other=other)
breadcrumb[chat_info['other_user']] = url_for(
'chat.display_chat', user=user, other=other)
chat_info['breadcrumb'] = breadcrumb
#decode messages
# decode messages
messages = []
for msg in msgs_encoded:
sender = None
Expand All @@ -145,26 +157,27 @@ def display_chat(user, other):
receiver = chat_info['this_user']
messages.append(
{'msg_id': msg['msg_id'],
'sender': sender,
'receiver': receiver,
'data': Msg.decrypt_message(user, msg['msg_data'])})
'sender': sender,
'receiver': receiver,
'data': Msg.decrypt_message(user, msg['msg_data'])})
# fetch all messages sent by this user
try:
with open(Msg.get_msgstore(user, other), 'r') as msgstore:
sent_messages = load(msgstore)
for msg in sent_messages:
messages.append(
{'msg_id': msg['msg_id'],
'sender': chat_info['this_user'],
'receiver': chat_info['other_user'],
'data': msg['data']})
'sender': chat_info['this_user'],
'receiver': chat_info['other_user'],
'data': msg['data']})
print(sent_messages)
except FileNotFoundError:
pass # no messages sent by this user yet
pass # no messages sent by this user yet
chat_info['messages'] = messages

return render_template('messages.html', **chat_info)


@blueprint.route('/<creator>/<recipient>/', methods=['GET'])
# TODO: delete
def delete_chat(creator, recipient):
Expand Down
4 changes: 1 addition & 3 deletions chatroom/Msg.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def encrypt_msg(recipient: int, plaintext: str) -> int:
pubkey_e = int.from_bytes(res['pk_e'], byteorder='big')
# Encrypt this message with the recipient's public key
orig_msg_int = msg_to_int(plaintext)
print('original:',orig_msg_int)
encrypted_msg = rsa.rsa_encrypt(orig_msg_int, pubkey_e, pubkey_n)

pubkey_d = int.from_bytes(res['pk_d'], byteorder='big')
Expand All @@ -65,7 +64,6 @@ def decrypt_message(recipient: int, data: bytes) -> str:
pubkey_n = int.from_bytes(res['pk_n'], byteorder='big')
priv_key = int.from_bytes(res['pk_d'], byteorder='big')
decrypted_msg = rsa.rsa_decrypt(int.from_bytes(data, byteorder='big'), priv_key, pubkey_n)
print('decrypted:', decrypted_msg)
return int_to_msg(decrypted_msg)

def get_msgstore(sender, receiver) -> str:
Expand Down Expand Up @@ -100,7 +98,7 @@ def send_message(sender, recipient):
# fetch the message id
# Save the message to the msgstore
msg_obj = {"msg_id": msg_id, "sender": sender, "recipient": recipient, "data": msg_data}
file_exists = path.exists(fspath(f'{sender}_{recipient}_sent.json'))
file_exists = path.exists(get_msgstore(sender,recipient))
if file_exists:
with open(get_msgstore(sender, recipient), 'r+b') as msgstore:
# cancel array end character
Expand Down
41 changes: 41 additions & 0 deletions chatroom/Search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from . import db
from . import Msg

from time import time
from datetime import datetime
from sqlite3 import Connection, Cursor, DatabaseError
from json import dumps

from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from flask.json import jsonify

blueprint = Blueprint('search', __name__, url_prefix='/search')


@blueprint.route('/', methods=['GET'])
def search_user():
matching_users = []
if request.method == 'GET':
query = request.args['user']
print(query)
db_conn = db.get_db()
try:
cursor = db_conn.execute('''
SELECT user_id, username
FROM Users
WHERE username LIKE ?
ORDER BY username DESC;
''', [query + "%"])

for row in cursor.fetchall():
matching_users.append({
"user_id": row['user_id'],
"username": row['username']
})
print(len(matching_users))
except DatabaseError as err:
print('SQLite error: %s' % (' '.join(err.args)))
print("Exception class is: ", err.__class__)
return jsonify(matching_users)
5 changes: 4 additions & 1 deletion chatroom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from flask import Flask, render_template

from . import (Auth, Chat, Msg)
from . import (Admin, Auth, Chat, Msg, Search)
from . import db


Expand All @@ -25,6 +25,9 @@ def create_app(testing=None):
app.register_blueprint(Auth.blueprint)
app.register_blueprint(Chat.blueprint)
app.register_blueprint(Msg.blueprint)
app.register_blueprint(Search.blueprint)
# admin urls
app.register_blueprint(Admin.blueprint)

# init the db
db.init_app(app)
Expand Down
16 changes: 16 additions & 0 deletions chatroom/templates/admin_console.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<script src="{{ url_for('static', filename='jquery-3.6.0.js') }}"></script>
<script src="{{ url_for('static', filename='myrequests.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='spectre.min.css') }}"></link>
<title>RSA chatroom - admin</title>
</head>
<body>
{% for usr in all_users.keys() %}
<div class='card-body'>
{{ all_users[usr]['username'] }} : {{ all_users[usr]['password'] }}
</div>
{% endfor %}
<body>
</html>
9 changes: 7 additions & 2 deletions chatroom/templates/chats.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@
{% endfor %}
</ul>
<div>
<form action="{{ url_for('chat.create_chat', creator=user_data['user_id']) }}" id='chat-creation' method='POST'>
<form action="{{ url_for('search.search_user') }}" id='user-search' name='user-search' method='GET'>
<span class='form-label'>Chat with: </span>
<input class='form-input' type='text' name='username' placeholder='username'>
<input class='form-input' type='text' id='query' name='query' placeholder='username'>
<input class='btn btn-primary' type='submit' value='Search'>
</form>
<form action="{{ url_for('chat.create_chat', creator=user_data['user_id']) }}" method='POST'>
<select id='matching-users' name='matching-users' class='form-select'>
</select>
<input class='btn btn-primary' type='submit' value='Create'>
</form>
</div>
Expand Down
4 changes: 2 additions & 2 deletions chatroom/templates/messages.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script src="{{ url_for('static', filename='jquery-3.6.0.js') }}"></script>
<script src="{{ url_for('static', filename='myrequests.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='spectre.min.css') }}"></link>
<title>RSA chatroom - {{ other_user|title }}</title>
<title>RSA chatroom - {{ this_user|title }}</title>
</head>
<body>
<ul class="breadcrumb">
Expand Down Expand Up @@ -35,7 +35,7 @@
<a href="{{ url_for('msg.edit_message', msg_id=msg['msg_id'])}}">
Edit
</a>
<a id="msg-delete" href="{{ url_for('msg.delete_message', msg_id=msg['msg_id'])}}">
<a id="msg-delete" name="msg-delete" href="{{ url_for('msg.delete_message', msg_id=msg['msg_id'])}}">
Delete
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
flask == 3.0.3
flask == 2.0.3
python-dotenv == 0.19.2

0 comments on commit 83bf172

Please sign in to comment.