Skip to content
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fa6f811
Logging setup in DBProfile.py and minor changes in application init
li-ruihao Jul 27, 2024
b692a39
Logging setup in LocalProfile.py and minor changes in DBProfile.py
li-ruihao Jul 28, 2024
4a48a1f
Logging setup in general application python files
li-ruihao Jul 28, 2024
be67225
Modify and declare logger in individual files instead of utilizing co…
IreneLime Aug 5, 2024
fea6f8f
Reverted logging changes before abort
li-ruihao Sep 8, 2024
5636256
reverted changes made to abort calls
li-ruihao Sep 8, 2024
c87deaa
Remove unnecessary log messages from exception or abort.
IreneLime Sep 20, 2024
224c592
Merge pull request #1 from li-ruihao/iCtrl_logging_work_profile_appli…
ece496-2024017 Sep 27, 2024
f6abc97
Add clp-loglib-py as a submodule
xx12345798 Sep 27, 2024
bf0d837
try integrating clp logging file handler to logconfig
xx12345798 Sep 27, 2024
05a3242
Merge pull request #2 from IreneLime/logs-feature
ece496-2024017 Sep 27, 2024
2e5ff20
push the workaround that inputs to clp file handler
xx12345798 Sep 27, 2024
b39aed1
change response dump from json.dump to jsonify
xx12345798 Sep 27, 2024
99eeb04
add clp filehandler into log config
xx12345798 Sep 28, 2024
fa66164
Add initial logic to log upload mechanism and verification system.
IreneLime Sep 29, 2024
7d8efca
Merge pull request #5 from IreneLime/ictrlservice-internal
ece496-2024017 Sep 29, 2024
7c2d688
Enhance upload functionality and add log name policy.
IreneLime Oct 19, 2024
d959547
Merge pull request #6 from IreneLime/ictrlservice-internal
ece496-2024017 Oct 19, 2024
db38c86
Integrate multipart upload and sha256 checksum
IreneLime Nov 7, 2024
8442aa9
Restore etag checking on segments.
IreneLime Nov 7, 2024
29299ec
Clean up functions.
IreneLime Nov 7, 2024
4b3beaa
Merge pull request #7 from IreneLime/ictrlservice-internal
ece496-2024017 Nov 7, 2024
6bd9c6a
Merge pull request #4 from xx12345798/main
ece496-2024017 Nov 10, 2024
1329d8f
Merge pull request #8 from iCtrlService/s3-cloud-implementation
ece496-2024017 Nov 10, 2024
ed567b1
Use multipart upload mechanism
IreneLime Nov 13, 2024
4079546
Merge pull request #9 from IreneLime/ictrlservice-internal
ece496-2024017 Nov 13, 2024
f3da7d9
Merge branch 'junhaoliao:main' into ictrlservice-internal
IreneLime Nov 13, 2024
a63223c
Add clp-loglib-py as a submodule
xx12345798 Sep 27, 2024
bfdcb13
try integrating clp logging file handler to logconfig
xx12345798 Sep 27, 2024
483df56
push the workaround that inputs to clp file handler
xx12345798 Sep 27, 2024
a0dafb1
change response dump from json.dump to jsonify
xx12345798 Sep 27, 2024
eea19f0
add clp filehandler into log config
xx12345798 Sep 28, 2024
3755e6a
Enable log name configuration during ictrl runtime without changing l…
IreneLime Nov 13, 2024
6b1f640
Merge branch 'main' into ictrlservice-internal
IreneLime Nov 13, 2024
7497ef7
Merge pull request #11 from IreneLime/ictrlservice-internal
ece496-2024017 Nov 13, 2024
9a3ab61
feat: implement log rotation with safe stream swapping and backup man…
Nuovaxu Nov 16, 2024
a799d1f
Merge pull request #12 from ece496-2024017/feature/log-rotation-handler
ece496-2024017 Nov 22, 2024
bcb03e0
minor changes from the code review for PR #35
li-ruihao Dec 26, 2024
5e0b6d2
Merge branch 'main' into iCtrl_logging_work_profile_application
li-ruihao Dec 26, 2024
b6c0756
changes made according to code review
li-ruihao Dec 27, 2024
55c57ca
change logging statements to use the %s operator instead of f-strings
li-ruihao Dec 27, 2024
edccf85
changed logging statements to use %s operators in application/utils.py
li-ruihao Dec 27, 2024
a86f91f
Updated utils.py based on code review
li-ruihao Dec 27, 2024
49d84fc
Minor change from code review
li-ruihao Dec 27, 2024
bc4f01f
update local rotate file size to 5MB
Dec 29, 2024
dc31645
Merge pull request #13 from ece496-2024017/feature/update-local-rotat…
Nuovaxu Dec 29, 2024
2d4a021
Merge branch 'main' into iCtrl_logging_work_profile_application
li-ruihao Jan 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 64 additions & 12 deletions application/Profile/DBProfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import base64
import json
import logging
import os
import re
import uuid
Expand Down Expand Up @@ -55,6 +56,9 @@ class ActivationType(IntEnum):
NORMAL_USER = 1


logger = logging.getLogger(__name__)


class DBProfile(Profile):

def __init__(self, app):
Expand All @@ -67,13 +71,21 @@ def __init__(self, app):
self.salt = bcrypt.gensalt()

# key: user_id, value: activation code
self.activation_cache = TTLCache(maxsize=1024, ttl=ACTIVATION_TTL_SECOND)
size = 1024
self.activation_cache = TTLCache(maxsize=size, ttl=ACTIVATION_TTL_SECOND)
logger.debug("activation_cache set up with %d, expiration time = %d", size, ACTIVATION_TTL_SECOND)

# key: user_id, value: True (to indicate the entry exists; can be any dummy value)
self.resend_cooldown = TTLCache(maxsize=1024, ttl=RESEND_COOLDOWN_TTL_SECOND)
self.resend_cooldown = TTLCache(maxsize=size, ttl=RESEND_COOLDOWN_TTL_SECOND)
logger.debug("resend_cooldown cache set up with %d, expiration time = %d", size, RESEND_COOLDOWN_TTL_SECOND)

with open('/var/www/ictrl/application/resources/activation_email_template.html', 'r') as f:
self.activation_email_body_template = f.read()
activation_email_template = '/var/www/ictrl/application/resources/activation_email_template.html'
logger.debug("Opening %s in read-only mode", activation_email_template)
try:
with open(activation_email_template) as f:
self.activation_email_body_template = f.read()
except IOError as e:
logger.exception("Failed to open %s, does file exist? Error: %s", activation_email_template, e)

class User(db.Model):
__table_args__ = {"schema": "ictrl"}
Expand All @@ -85,8 +97,11 @@ class User(db.Model):

@validates('username')
def validate_username(self, key, username):
assert re.match("^[A-Za-z0-9_-]+$", username), \
'User name should contain only letters, numbers, underscores and dashes'
username_error = 'User name should contain only letters, numbers, underscores and dashes'
if not re.match("^[A-Za-z0-9_-]+$", username):
logger.error(username_error)
raise AssertionError(username_error)

return username

# this field is for the hashed passwords
Expand All @@ -97,16 +112,23 @@ def validate_username(self, key, username):

@validates('email')
def validate_email(self, key, email):
assert re.match(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', email), \
f'Invalid email address: "{email}"'
invalid_email_error = f'Invalid email address: "{email}"'
if not re.match(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', email):
logger.error(invalid_email_error)
raise AssertionError(invalid_email_error)

# FIXME: remove this utoronto mail restriction in the future
assert email.endswith('utoronto.ca'), "Currently, only Uoft emails are supported"
not_uoft_email_error = "Currently, only UofT emails are supported, emails must end with utoronto.ca"
if not email.endswith('utoronto.ca'):
logger.error(not_uoft_email_error)
raise AssertionError(not_uoft_email_error)

return email

activation_type = db.Column(db.Integer, nullable=False, default=ActivationType.NOT_ACTIVATED)

logger.info("Defined a database table: User")

class Session(db.Model):
__table_args__ = {"schema": "ictrl"}

Expand All @@ -118,27 +140,35 @@ class Session(db.Model):

@validates('nickname')
def validate_nickname(self, key, nickname):
assert len(nickname) <= 8, \
'Entered nickname is too long'
nickname_too_long = 'Entered nickname is too long'
if len(nickname) > 8:
logger.error(nickname_too_long)
raise AssertionError(nickname_too_long)

return nickname

username = db.Column(db.String, nullable=False)
private_key = db.Column(db.Text, nullable=True)

logger.info("Defined a database table: Session")

class VNCCredentials(db.Model):
__table_args__ = {"schema": "ictrl"}

session_id = db.Column(UUID(as_uuid=True), db.ForeignKey('ictrl.session.id'), primary_key=True,
nullable=False)
credentials = db.Column(db.Text, nullable=False)

logger.info("Defined a database table: VNCCredentials")

self.User = User
self.Session = Session
self.VNCCredentials = VNCCredentials

# db.drop_all()
db.engine.execute("CREATE SCHEMA IF NOT EXISTS ictrl;")
db.create_all()
logger.info("Created database SCHEMA ictrl and created all databases defined")

def login(self, username, password):
username = username.lower()
Expand Down Expand Up @@ -169,7 +199,8 @@ def login(self, username, password):
@staticmethod
def logout():
# remove the username from the session if it's there
flask_session.pop('userid', None)
userid = flask_session.pop('userid', None)
logger.info("Removed session user: %s", userid)

return True

Expand All @@ -188,6 +219,8 @@ def query(self):
"username": session.username
}

logger.info("Query user sessions successful")

return _profile

def add_user(self, username, password, email):
Expand Down Expand Up @@ -236,6 +269,8 @@ def activate_user(self, userid, code):
user.activation_type = ActivationType.NORMAL_USER
self.save_profile()

logger.info("Successfully activated user with userid=%s", userid)

return True

return False
Expand All @@ -249,6 +284,7 @@ def get_user(self):
if user is None:
abort(403, 'Cannot find any matching record')

logger.info("Successfully retrieved user with userid=%s", userid)
return user

def add_session(self, host, username, conn=None):
Expand All @@ -273,6 +309,8 @@ def add_session(self, host, username, conn=None):
session.private_key = f.encrypt(clear_private_key).decode('ascii')

self.save_profile()

logger.info("Successfully added a new session: session_id = %s", session.id)
except AssertionError as e:
abort(403, e)
except sqlalchemy.exc.IntegrityError as e:
Expand All @@ -285,6 +323,7 @@ def _get_session(self, session_id):
abort(403, 'You are not logged in')
userid = flask_session['userid']

logger.info("Returning session, session_id = %s", session_id)
return self.Session.query.filter_by(id=session_id, user_id=userid).first()

def delete_session(self, session_id):
Expand All @@ -295,6 +334,8 @@ def delete_session(self, session_id):
self.db.session.delete(session)
self.save_profile()

logger.info("Successfully deleted session, session_id = %s", session_id)

return True, ''

def change_host(self, session_id, new_host):
Expand All @@ -305,14 +346,18 @@ def change_host(self, session_id, new_host):
session.host = new_host
self.save_profile()

logger.info("Successfully changed host for session, session_id = %s", session_id)

return True, ''

def save_profile(self):
self.db.session.commit()
logger.info("Profile saved")

def get_session_info(self, session_id):
session = self._get_session(session_id)
if session is None:
logger.debug("Session %s does not exist, cannot retrieve session info", session_id)
return None, None, None, None, None

f = Fernet(flask_session['session_crypt_key'])
Expand All @@ -329,6 +374,8 @@ def set_session_nickname(self, session_id, nickname):
session.nickname = nickname
self.save_profile()

logger.info("Successfully set session nickname=%s for session %s", nickname, session_id)

return True, ''

def set_session_vnc_credentials(self, session_id, credentials):
Expand All @@ -340,6 +387,7 @@ def set_session_vnc_credentials(self, session_id, credentials):
# it is a delete request
vnc_credential = self.VNCCredentials.query.filter_by(session_id=session_id).first()
self.db.session.delete(vnc_credential)
logger.info("Successfully deleted vnc credentials for session %s", session_id)
else:
# it is an add / update request
json_str = json.dumps(credentials)
Expand All @@ -352,12 +400,14 @@ def set_session_vnc_credentials(self, session_id, credentials):
# add
vnc_credential = self.VNCCredentials(session_id=session_id, credentials=base64_str)
self.db.session.add(vnc_credential)
logger.info("Successfully added/updated vnc credentials for session %s", session_id)

self.save_profile()

return True, ''

def get_session_vnc_credentials(self, session_id):
logger.debug("Getting vnc credentials for session: %s", session_id)
session = self._get_session(session_id)
if session is None:
return False, f'failed: session {session_id} does not exist'
Expand Down Expand Up @@ -389,4 +439,6 @@ def send_activation_email(self, username):
expire_min=int(ACTIVATION_TTL_SECOND / 60))
send_email(user.email, 'Activate Your iCtrl Account', body)

logger.info("Successfully sent out activation email to email=%s", user.email)

return True
Loading
Loading