Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 35 additions & 1 deletion test/integration/oidc/galaxy-realm-export.json
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,40 @@
"notBefore": 0,
"groups": []
},
{
"id": "f5e8b2c4-9a1d-4e3f-8c6a-7b2d5e4f3c1a",
"createdTimestamp": 1694376671733,
"username": "rincewind_test",
"enabled": true,
"totp": false,
"emailVerified": true,
"firstName": "Rincewind",
"lastName": "Ankh-Morpork",
"email": "[email protected]",
"attributes": {
"preferred_username": [
"Rincewind (Ankh-Morpork)"
]
},
"credentials": [
{
"id": "e5d4c3b2-a1f0-9e8d-7c6b-5a4f3e2d1c0b",
"type": "password",
"userLabel": "My password",
"createdDate": 1694376754826,
"secretData": "{\"value\":\"uNBI+UnpCLpXWHhm/tPSnnhuINiNw2MNt1XeDmImJaQ=\",\"salt\":\"fHS/FpnORylnSIco16UHwA==\",\"additionalParameters\":{}}",
"credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
}
],
"disableableCredentialTypes": [],
"requiredActions": [],
"realmRoles": [
"galaxy-access-role",
"default-roles-gxyrealm"
],
"notBefore": 0,
"groups": []
},
{
"id": "24ffa3ff-d351-4d5e-b10b-8d615082ec9d",
"createdTimestamp": 1694376671733,
Expand Down Expand Up @@ -2565,4 +2599,4 @@
"clientPolicies": {
"policies": []
}
}
}
18 changes: 18 additions & 0 deletions test/integration/oidc/test_auth_oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,24 @@ def test_oidc_login_new_user(self):
self._assert_status_code_is(response, 200)
assert response.json()["email"] == "[email protected]"

def test_oidc_login_username_sanitization(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really beautiful test setup - what a cool way to bootstrap auth-based testing! Not added here per se but I wanted to compliment it. These particular tests are also really clear and useful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Claude helped with all the boilerplate.

"""Test that OIDC usernames with special characters are properly sanitized."""
_, response = self._login_via_keycloak("rincewind_test", KEYCLOAK_TEST_PASSWORD, save_cookies=True)

response = self._get("users/current")
self._assert_status_code_is(response, 200)
assert response.json()["email"] == "[email protected]"

username = response.json()["username"]
from galaxy.security.validate_user_input import validate_publicname_str

error = validate_publicname_str(username)
assert error == "", f"OIDC-created username '{username}' is invalid: {error}"
assert "(" not in username, f"Username '{username}' should not contain parentheses"
assert ")" not in username, f"Username '{username}' should not contain parentheses"
assert " " not in username, f"Username '{username}' should not contain spaces"
assert username == username.lower(), f"Username '{username}' should be lowercase"

def test_oidc_login_repeat_no_notification(self):
"""
Test that repeat logins do NOT show the 'identity has been linked' notification.
Expand Down
25 changes: 25 additions & 0 deletions test/unit/util/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,28 @@ def test_validate_doi_fail_too_long():
doi = f"doi:10.1000/{long_suffix}"
assert util.validate_doi(doi)
assert not util.validate_doi(doi + "a") # Increase length by 1 past max limit


@pytest.mark.parametrize(
"input_name,expected_output",
[
# Existing documented behavior
("My Cool Object", "My-Cool-Object"),
("!My Cool Object!", "My-Cool-Object"),
("Hello\u20a9\u25ce\u0491\u029f\u217e", "Hello"),
# Additional edge cases
("simple", "simple"),
("UPPERCASE", "UPPERCASE"), # Note: lowercase applied separately
("with-dash", "with-dash"),
("with spaces", "with-spaces"),
(" multiple spaces ", "-multiple-spaces"),
("trailing!", "trailing"),
("!leading", "leading"),
("special@#$chars", "specialchars"),
("parentheses(test)", "parenthesestest"),
("Rincewind (Ankh-Morpork)", "Rincewind-Ankh-Morpork"),
],
)
def test_ready_name_for_url(input_name, expected_output):
"""Test that ready_name_for_url correctly sanitizes names for URL use."""
assert util.ready_name_for_url(input_name) == expected_output
Loading