Skip to content

Commit b436316

Browse files
authored
Fix Issue 52904 (#80)
1 parent 914e139 commit b436316

File tree

7 files changed

+63
-10
lines changed

7 files changed

+63
-10
lines changed

CHANGE.txt

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ What's New in the LabKey 3.4.0 package
99
- Add "valueExpression" to PropertyDescriptor class
1010
- needed for creating/adding domain calculated fields
1111
- update Domain class to append calculatedFields to the domain's fields
12+
- Fix Issue 52904
13+
- UnexpectedRedirectError is not wrapped with ServerContextError
14+
- Allow redirects when deactivating users
1215

1316
What's New in the LabKey 3.3.0 package
1417
==============================

labkey/security.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ def deactivate_users(
121121
target_ids=target_ids,
122122
api="deactivateUsers.view",
123123
container_path=container_path,
124+
allow_redirects=True,
124125
)
125126
if response is not None and response["status_code"] == 200:
126127
return dict(success=True)
@@ -360,7 +361,11 @@ def __make_security_role_api_request(
360361

361362

362363
def __make_user_api_request(
363-
server_context: ServerContext, target_ids: List[int], api: str, container_path: str = None
364+
server_context: ServerContext,
365+
target_ids: List[int],
366+
api: str,
367+
container_path: str = None,
368+
allow_redirects: bool = False,
364369
):
365370
"""
366371
Make a request to the LabKey User Controller
@@ -372,7 +377,7 @@ def __make_user_api_request(
372377
"""
373378
url = server_context.build_url(USER_CONTROLLER, api, container_path)
374379

375-
return server_context.make_request(url, {"userId": target_ids})
380+
return server_context.make_request(url, {"userId": target_ids}, allow_redirects=allow_redirects)
376381

377382

378383
class SecurityWrapper:

labkey/server_context.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,12 @@ def webdav_client(self, webdav_options: dict = None):
164164
return client
165165

166166
def handle_request_exception(self, exception):
167-
if type(exception) in [RequestAuthorizationError, QueryNotFoundError, ServerNotFoundError]:
167+
if type(exception) in [
168+
RequestAuthorizationError,
169+
QueryNotFoundError,
170+
ServerNotFoundError,
171+
UnexpectedRedirectError,
172+
]:
168173
raise exception
169174

170175
raise ServerContextError(self, exception)

test/integration/test_domain.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,20 @@ def test_domain_save_options(api: APIWrapper, list_fixture):
265265

266266
def test_domain_add_calculated_field(api: APIWrapper, list_fixture):
267267
domain, options = api.domain.get_domain_details(LISTS_SCHEMA, LIST_NAME)
268-
domain.add_field({"name": "calcDouble", "conceptURI": "http://www.labkey.org/exp/xml#calculated", "valueExpression": "rowId * 2"})
269-
domain.add_field({"name": "calcCube", "conceptURI": "http://www.labkey.org/exp/xml#calculated", "valueExpression": "power(rowId, 3)"})
268+
domain.add_field(
269+
{
270+
"name": "calcDouble",
271+
"conceptURI": "http://www.labkey.org/exp/xml#calculated",
272+
"valueExpression": "rowId * 2",
273+
}
274+
)
275+
domain.add_field(
276+
{
277+
"name": "calcCube",
278+
"conceptURI": "http://www.labkey.org/exp/xml#calculated",
279+
"valueExpression": "power(rowId, 3)",
280+
}
281+
)
270282

271283
api.domain.save(LISTS_SCHEMA, LIST_NAME, domain, options=options)
272284
saved_domain = api.domain.get(LISTS_SCHEMA, LIST_NAME)

test/integration/test_query.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ def test_cannot_delete_qc_state_in_use(api: APIWrapper, qc_states, study, datase
158158
dataset_row_to_remove = [{"lsid": inserted_lsid}]
159159
api.query.delete_rows(SCHEMA_NAME, QUERY_NAME, dataset_row_to_remove)
160160

161+
161162
LISTS_SCHEMA = "lists"
162163
PARENT_LIST_NAME = "parent_list"
163164
PARENT_LIST_DEFINITION = {
@@ -214,6 +215,7 @@ def test_cannot_delete_qc_state_in_use(api: APIWrapper, qc_states, study, datase
214215
child_three,parent_three
215216
"""
216217

218+
217219
@pytest.fixture
218220
def parent_list_fixture(api: APIWrapper):
219221
api.domain.create(PARENT_LIST_DEFINITION)
@@ -251,11 +253,16 @@ def test_import_rows(api: APIWrapper, parent_list_fixture, child_list_fixture, t
251253
child_file.close()
252254
assert resp["success"] == False
253255
assert resp["errorCount"] == 1
254-
assert resp["errors"][0]["exception"] == "Could not convert value 'parent_one' (String) for Integer field 'parent'"
256+
assert (
257+
resp["errors"][0]["exception"]
258+
== "Could not convert value 'parent_one' (String) for Integer field 'parent'"
259+
)
255260

256261
# Should pass, because import_lookup_by_alternate_key is True
257262
child_file = child_data_path.open()
258-
resp = api.query.import_rows("lists", CHILD_LIST_NAME, data_file=child_file, import_lookup_by_alternate_key=True)
263+
resp = api.query.import_rows(
264+
"lists", CHILD_LIST_NAME, data_file=child_file, import_lookup_by_alternate_key=True
265+
)
259266
child_file.close()
260267
assert resp["success"] == True
261268
assert resp["rowCount"] == 3

test/integration/test_security.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
# JavaClientApiTest.testImpersonationConnection()
77

88
pytestmark = pytest.mark.integration # Mark all tests in this module as integration tests
9-
TEST_EMAIL = "test_user@test.test"
9+
TEST_EMAIL = "test_user@example.com"
1010
TEST_DISPLAY_NAME = "test user"
11+
DEACTIVATED_EMAIL = "[email protected]"
12+
DEACTIVATED_DISPLAY_NAME = "deactivated user"
1113

1214

1315
@pytest.fixture(scope="session")
@@ -17,7 +19,7 @@ def test_user(api: APIWrapper, project):
1719
user_id = resp["userId"]
1820
yield {"id": user_id, "email": TEST_EMAIL, "display_name": TEST_DISPLAY_NAME}
1921
url = api.server_context.build_url("security", "deleteUser.api", container_path="/")
20-
resp = api.server_context.make_request(url, {"id": user_id})
22+
api.server_context.make_request(url, {"id": user_id})
2123

2224

2325
def test_impersonation(api: APIWrapper, test_user):
@@ -44,3 +46,22 @@ def test_impersonation(api: APIWrapper, test_user):
4446

4547
# We need to stop impersonating a user before leaving so we don't mess up other tests.
4648
api.security.stop_impersonating()
49+
50+
51+
@pytest.fixture(scope="module")
52+
def deactivated_user(api: APIWrapper, project):
53+
url = api.server_context.build_url("security", "createNewUser.api")
54+
resp = api.server_context.make_request(url, {"email": DEACTIVATED_EMAIL, "sendEmail": False})
55+
user_id = resp["userId"]
56+
yield {"id": user_id, "email": DEACTIVATED_EMAIL, "display_name": DEACTIVATED_DISPLAY_NAME}
57+
url = api.server_context.build_url("security", "deleteUser.api", container_path="/")
58+
api.server_context.make_request(url, {"id": user_id})
59+
60+
61+
def test_issue_52904(api: APIWrapper, deactivated_user):
62+
resp = api.security.deactivate_users(target_ids=[deactivated_user["id"]])
63+
assert resp["success"] is True
64+
65+
# Deactivating again shouldn't issue a redirect
66+
resp = api.security.deactivate_users(target_ids=[deactivated_user["id"]])
67+
assert resp["success"] is True

test/unit/test_security.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def setUp(self):
287287
"data": {"userId": [123]},
288288
"headers": None,
289289
"timeout": 300,
290-
"allow_redirects": False,
290+
"allow_redirects": True,
291291
}
292292

293293
self.args = [mock_server_context(self.service), self.__user_id]

0 commit comments

Comments
 (0)