Skip to content

Commit 3fc7191

Browse files
authored
Merge branch 'main' into bes/uv-vis-reformat-for-plugin-dev
2 parents a5f1c35 + a7ad44c commit 3fc7191

File tree

3 files changed

+85
-5
lines changed

3 files changed

+85
-5
lines changed

pydatalab/src/pydatalab/routes/v0_1/files.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,22 @@ def _(): ...
2222

2323
@FILES.route("/files/<string:file_id>/<string:filename>", methods=["GET"])
2424
def get_file(file_id: str, filename: str):
25+
"""If this user has the appropriate permissions, return the file with the
26+
given database ID and filename.
27+
28+
Parameters:
29+
file_id: The file ID in the database.
30+
filename: The filename in the database.
31+
32+
"""
2533
try:
2634
_file_id = ObjectId(file_id)
2735
except InvalidId:
2836
# If the ID is invalid, then there will be no results in the database anyway,
2937
# so just 401
3038
_file_id = file_id
31-
if not pydatalab.mongo.flask_mongo.db.files.find_one(
32-
{"_id": _file_id, **get_default_permissions(user_only=False)}
39+
if not pydatalab.mongo.flask_mongo.db.items.find_one(
40+
{"file_ObjectIds": {"$in": [_file_id]}, **get_default_permissions(user_only=False)}
3341
):
3442
return (
3543
jsonify(
@@ -41,6 +49,7 @@ def get_file(file_id: str, filename: str):
4149
),
4250
401,
4351
)
52+
4453
path = os.path.join(CONFIG.FILE_DIRECTORY, secure_filename(file_id))
4554
return send_from_directory(path, filename)
4655

pydatalab/tests/server/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ def client(app, user_api_key):
162162
yield client_factory(app, user_api_key)
163163

164164

165+
@pytest.fixture(scope="function")
166+
def another_client(app, another_user_api_key):
167+
"""Returns a test client for the API with a second normal user access."""
168+
yield client_factory(app, another_user_api_key)
169+
170+
165171
@pytest.fixture(scope="function")
166172
def unauthenticated_client(app):
167173
"""Returns an unauthenticated test client for the API."""
@@ -201,6 +207,11 @@ def user_api_key() -> str:
201207
return generate_api_key()
202208

203209

210+
@pytest.fixture(scope="session")
211+
def another_user_api_key() -> str:
212+
return generate_api_key()
213+
214+
204215
@pytest.fixture(scope="session")
205216
def unverified_user_api_key() -> str:
206217
return generate_api_key()
@@ -216,6 +227,11 @@ def user_id():
216227
yield ObjectId(24 * "1")
217228

218229

230+
@pytest.fixture(scope="session")
231+
def another_user_id():
232+
yield ObjectId(24 * "7")
233+
234+
219235
@pytest.fixture(scope="session")
220236
def admin_user_id():
221237
yield ObjectId(24 * "8")
@@ -253,6 +269,8 @@ def insert_demo_users(
253269
app,
254270
user_id,
255271
user_api_key,
272+
another_user_id,
273+
another_user_api_key,
256274
admin_user_id,
257275
admin_api_key,
258276
deactivated_user_id,
@@ -262,6 +280,7 @@ def insert_demo_users(
262280
real_mongo_client,
263281
):
264282
insert_user(user_id, user_api_key, "user", real_mongo_client)
283+
insert_user(another_user_id, another_user_api_key, "user", real_mongo_client)
265284
insert_user(admin_user_id, admin_api_key, "admin", real_mongo_client)
266285
insert_user(
267286
deactivated_user_id,

pydatalab/tests/server/test_files.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def test_upload(client, default_filepath, insert_default_sample, default_sample)
5252
)
5353
assert isinstance(response.json["file_id"], str)
5454
assert response.json["file_information"]
55-
assert response.json["status"], "success"
55+
assert response.json["status"] == "success"
5656
assert response.status_code == 201
5757

5858

@@ -99,6 +99,7 @@ def test_get_file_and_delete(client, default_filepath, default_sample):
9999
assert not response.json["files_data"]
100100

101101

102+
@pytest.mark.dependency(depends=["test_get_file_and_delete"])
102103
def test_upload_new_version(
103104
client, default_filepath, insert_default_sample, default_sample, tmpdir
104105
): # pylint: disable=unused-argument
@@ -120,7 +121,7 @@ def test_upload_new_version(
120121
file_id = response.json["file_id"]
121122
assert file_id
122123
assert response.json["file_information"]
123-
assert response.json["status"], "success"
124+
assert response.json["status"] == "success"
124125
assert response.status_code == 201
125126

126127
# Copy the file to a new temp directory so its fs metadata changes
@@ -142,10 +143,61 @@ def test_upload_new_version(
142143
)
143144
assert isinstance(response_reup.json["file_id"], str)
144145
assert response_reup.json["file_information"]
145-
assert response_reup.json["status"], "success"
146+
assert response_reup.json["status"] == "success"
146147
assert response_reup.status_code == 201
147148
assert (
148149
response_reup.json["file_information"]["location"]
149150
== response.json["file_information"]["location"]
150151
)
151152
assert response_reup.json["file_id"] == response.json["file_id"]
153+
154+
155+
@pytest.mark.dependency(depends=["test_upload_new_version"])
156+
def test_file_permissions(
157+
client,
158+
another_client,
159+
another_user_id,
160+
default_filepath,
161+
insert_default_sample,
162+
default_sample,
163+
tmpdir,
164+
): # pylint: disable=unused-argument
165+
"""Upload a file as one user, then test access as two different users."""
166+
filename = "my_secret_file.txt"
167+
with open(default_filepath, "rb") as f:
168+
response = client.post(
169+
"/upload-file/",
170+
buffered=True,
171+
content_type="multipart/form-data",
172+
data={
173+
"item_id": default_sample.item_id,
174+
"file": [(f, filename)],
175+
"type": "application/octet-stream",
176+
"replace_file": "null",
177+
"relativePath": "null",
178+
},
179+
)
180+
181+
assert response.status_code == 201
182+
resp_json = response.json
183+
assert resp_json["status"] == "success"
184+
file_id = resp_json["file_id"]
185+
186+
# Test that a random user cannot access the file directly
187+
response = another_client.get(f"/files/{file_id}/{filename}")
188+
assert response.status_code == 401
189+
assert response.json["status"] == "error"
190+
191+
# Give the user access to the item, then check again
192+
# First get refcode for item ID
193+
response = client.get(f"/get-item-data/{default_sample.item_id}")
194+
refcode = response.json["item_data"]["refcode"]
195+
196+
# Add normal user to the item
197+
response = client.patch(
198+
f"/items/{refcode}/permissions", json={"creators": [{"immutable_id": str(another_user_id)}]}
199+
)
200+
201+
# Now check they have access to the file
202+
response = another_client.get(f"/files/{file_id}/{filename}")
203+
assert response.status_code == 200

0 commit comments

Comments
 (0)