Skip to content

Commit b9667bc

Browse files
Merge pull request #547 from Aditya30ag/fix/database-connection-leaks-464
Fix: Critical database connection leaks in all database functions
2 parents 589d71f + 35d0bbd commit b9667bc

9 files changed

Lines changed: 606 additions & 512 deletions

File tree

backend/app/database/albums.py

Lines changed: 129 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -40,69 +40,83 @@ def get_db_connection():
4040

4141

4242
def db_create_albums_table() -> None:
43-
conn = sqlite3.connect(DATABASE_PATH)
44-
cursor = conn.cursor()
45-
cursor.execute(
46-
"""
47-
CREATE TABLE IF NOT EXISTS albums (
48-
album_id TEXT PRIMARY KEY,
49-
album_name TEXT UNIQUE,
50-
description TEXT,
51-
is_hidden BOOLEAN DEFAULT 0,
52-
password_hash TEXT
43+
conn = None
44+
try:
45+
conn = sqlite3.connect(DATABASE_PATH)
46+
cursor = conn.cursor()
47+
cursor.execute(
48+
"""
49+
CREATE TABLE IF NOT EXISTS albums (
50+
album_id TEXT PRIMARY KEY,
51+
album_name TEXT UNIQUE,
52+
description TEXT,
53+
is_hidden BOOLEAN DEFAULT 0,
54+
password_hash TEXT
55+
)
56+
"""
5357
)
54-
"""
55-
)
56-
conn.commit()
57-
conn.close()
58+
conn.commit()
59+
finally:
60+
if conn is not None:
61+
conn.close()
5862

5963

6064
def db_create_album_images_table() -> None:
61-
conn = sqlite3.connect(DATABASE_PATH)
62-
cursor = conn.cursor()
63-
cursor.execute(
64-
"""
65-
CREATE TABLE IF NOT EXISTS album_images (
66-
album_id TEXT,
67-
image_id TEXT,
68-
PRIMARY KEY (album_id, image_id),
69-
FOREIGN KEY (album_id) REFERENCES albums(album_id) ON DELETE CASCADE,
70-
FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE
65+
conn = None
66+
try:
67+
conn = sqlite3.connect(DATABASE_PATH)
68+
cursor = conn.cursor()
69+
cursor.execute(
70+
"""
71+
CREATE TABLE IF NOT EXISTS album_images (
72+
album_id TEXT,
73+
image_id TEXT,
74+
PRIMARY KEY (album_id, image_id),
75+
FOREIGN KEY (album_id) REFERENCES albums(album_id) ON DELETE CASCADE,
76+
FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE
77+
)
78+
"""
7179
)
72-
"""
73-
)
74-
conn.commit()
75-
conn.close()
80+
conn.commit()
81+
finally:
82+
if conn is not None:
83+
conn.close()
7684

7785

7886
def db_get_all_albums(show_hidden: bool = False):
7987
conn = sqlite3.connect(DATABASE_PATH)
8088
cursor = conn.cursor()
81-
if show_hidden:
82-
cursor.execute("SELECT * FROM albums")
83-
else:
84-
cursor.execute("SELECT * FROM albums WHERE is_hidden = 0")
85-
albums = cursor.fetchall()
86-
conn.close()
87-
return albums
89+
try:
90+
if show_hidden:
91+
cursor.execute("SELECT * FROM albums")
92+
else:
93+
cursor.execute("SELECT * FROM albums WHERE is_hidden = 0")
94+
albums = cursor.fetchall()
95+
return albums
96+
finally:
97+
conn.close()
8898

8999

90100
def db_get_album_by_name(name: str):
91101
conn = sqlite3.connect(DATABASE_PATH)
92102
cursor = conn.cursor()
93-
cursor.execute("SELECT * FROM albums WHERE album_name = ?", (name,))
94-
album = cursor.fetchone()
95-
conn.close()
96-
return album if album else None
103+
try:
104+
cursor.execute("SELECT * FROM albums WHERE album_name = ?", (name,))
105+
album = cursor.fetchone()
106+
return album if album else None
107+
finally:
108+
conn.close()
97109

98110

99111
def db_get_album(album_id: str):
100112
conn = sqlite3.connect(DATABASE_PATH)
101113
cursor = conn.cursor()
102-
cursor.execute("SELECT * FROM albums WHERE album_id = ?", (album_id,))
103-
album = cursor.fetchone()
104-
conn.close()
105-
return album if album else None
114+
try:
115+
cursor.execute("SELECT * FROM albums WHERE album_id = ?", (album_id,))
116+
album = cursor.fetchone()
117+
return album if album else None
118+
finally:
119+
conn.close()
106120

107121

108122
def db_insert_album(
@@ -114,20 +128,22 @@ def db_insert_album(
114128
):
115129
conn = sqlite3.connect(DATABASE_PATH)
116130
cursor = conn.cursor()
117-
password_hash = None
118-
if password:
119-
password_hash = bcrypt.hashpw(
120-
password.encode("utf-8"), bcrypt.gensalt()
121-
).decode("utf-8")
122-
cursor.execute(
123-
"""
124-
INSERT INTO albums (album_id, album_name, description, is_hidden, password_hash)
125-
VALUES (?, ?, ?, ?, ?)
126-
""",
127-
(album_id, album_name, description, int(is_hidden), password_hash),
128-
)
129-
conn.commit()
130-
conn.close()
131+
try:
132+
password_hash = None
133+
if password:
134+
password_hash = bcrypt.hashpw(
135+
password.encode("utf-8"), bcrypt.gensalt()
136+
).decode("utf-8")
137+
cursor.execute(
138+
"""
139+
INSERT INTO albums (album_id, album_name, description, is_hidden, password_hash)
140+
VALUES (?, ?, ?, ?, ?)
141+
""",
142+
(album_id, album_name, description, int(is_hidden), password_hash),
143+
)
144+
conn.commit()
145+
finally:
146+
conn.close()
131147

132148

133149
def db_update_album(
@@ -139,38 +155,52 @@ def db_update_album(
139155
):
140156
conn = sqlite3.connect(DATABASE_PATH)
141157
cursor = conn.cursor()
142-
password_hash = None
143-
if password:
144-
password_hash = bcrypt.hashpw(
145-
password.encode("utf-8"), bcrypt.gensalt()
146-
).decode("utf-8")
147-
cursor.execute(
148-
"""
149-
UPDATE albums
150-
SET album_name = ?, description = ?, is_hidden = ?, password_hash = ?
151-
WHERE album_id = ?
152-
""",
153-
(album_name, description, int(is_hidden), password_hash, album_id),
154-
)
155-
conn.commit()
156-
conn.close()
158+
try:
159+
if password is not None:
160+
# Update with new password
161+
password_hash = bcrypt.hashpw(
162+
password.encode("utf-8"), bcrypt.gensalt()
163+
).decode("utf-8")
164+
cursor.execute(
165+
"""
166+
UPDATE albums
167+
SET album_name = ?, description = ?, is_hidden = ?, password_hash = ?
168+
WHERE album_id = ?
169+
""",
170+
(album_name, description, int(is_hidden), password_hash, album_id),
171+
)
172+
else:
173+
# Update without changing password
174+
cursor.execute(
175+
"""
176+
UPDATE albums
177+
SET album_name = ?, description = ?, is_hidden = ?
178+
WHERE album_id = ?
179+
""",
180+
(album_name, description, int(is_hidden), album_id),
181+
)
182+
conn.commit()
183+
finally:
184+
conn.close()
157185

158186

159187
def db_delete_album(album_id: str):
160-
conn = sqlite3.connect(DATABASE_PATH)
161-
cursor = conn.cursor()
162-
cursor.execute("DELETE FROM albums WHERE album_id = ?", (album_id,))
163-
conn.commit()
164-
conn.close()
188+
with get_db_connection() as conn:
189+
cursor = conn.cursor()
190+
cursor.execute("DELETE FROM albums WHERE album_id = ?", (album_id,))
165191

166192

167193
def db_get_album_images(album_id: str):
168194
conn = sqlite3.connect(DATABASE_PATH)
169195
cursor = conn.cursor()
170-
cursor.execute("SELECT image_id FROM album_images WHERE album_id = ?", (album_id,))
171-
images = cursor.fetchall()
172-
conn.close()
173-
return [img[0] for img in images]
196+
try:
197+
cursor.execute(
198+
"SELECT image_id FROM album_images WHERE album_id = ?", (album_id,)
199+
)
200+
images = cursor.fetchall()
201+
return [img[0] for img in images]
202+
finally:
203+
conn.close()
174204

175205

176206
def db_add_images_to_album(album_id: str, image_ids: list[str]):
@@ -214,20 +244,26 @@ def db_remove_image_from_album(album_id: str, image_id: str):
214244
def db_remove_images_from_album(album_id: str, image_ids: list[str]):
215245
conn = sqlite3.connect(DATABASE_PATH)
216246
cursor = conn.cursor()
217-
cursor.executemany(
218-
"DELETE FROM album_images WHERE album_id = ? AND image_id = ?",
219-
[(album_id, img_id) for img_id in image_ids],
220-
)
221-
conn.commit()
222-
conn.close()
247+
try:
248+
cursor.executemany(
249+
"DELETE FROM album_images WHERE album_id = ? AND image_id = ?",
250+
[(album_id, img_id) for img_id in image_ids],
251+
)
252+
conn.commit()
253+
finally:
254+
conn.close()
223255

224256

225257
def verify_album_password(album_id: str, password: str) -> bool:
226258
conn = sqlite3.connect(DATABASE_PATH)
227259
cursor = conn.cursor()
228-
cursor.execute("SELECT password_hash FROM albums WHERE album_id = ?", (album_id,))
229-
row = cursor.fetchone()
230-
conn.close()
231-
if not row or not row[0]:
232-
return False
233-
return bcrypt.checkpw(password.encode("utf-8"), row[0].encode("utf-8"))
260+
try:
261+
cursor.execute(
262+
"SELECT password_hash FROM albums WHERE album_id = ?", (album_id,)
263+
)
264+
row = cursor.fetchone()
265+
if not row or not row[0]:
266+
return False
267+
return bcrypt.checkpw(password.encode("utf-8"), row[0].encode("utf-8"))
268+
finally:
269+
conn.close()

0 commit comments

Comments
 (0)