diff --git a/.gitignore b/.gitignore index e37173b..30a8a26 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ dropkit/_version.txt # Virtual environments .venv + +# Git worktrees +.worktrees/ diff --git a/CLAUDE.md b/CLAUDE.md index 42e5564..42a478a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,7 +55,7 @@ dropkit/ ### Username - **Derived from DigitalOcean account email**, not configured - Fetched via `/v2/account`, sanitized for Linux compatibility -- `john.doe@trailofbits.com` → `john_doe` +- `john.doe@example.com` → `john_doe` ### SSH Hostname - All SSH entries use `dropkit.` format diff --git a/dropkit/api.py b/dropkit/api.py index eb43c18..afd62d9 100644 --- a/dropkit/api.py +++ b/dropkit/api.py @@ -117,9 +117,10 @@ class DigitalOceanAPI: @staticmethod def _sanitize_email_for_username(email: str) -> str: """ - Sanitize email address to create a valid username. + Sanitize email address to create a valid Linux username. - Removes @trailofbits.com suffix and replaces special characters. + Extracts the local part (before @) and replaces special characters + with underscores. Ensures the result is a valid Linux username. Args: email: Email address from DigitalOcean account @@ -127,12 +128,8 @@ class DigitalOceanAPI: Returns: Sanitized username suitable for Linux user creation """ - # Remove @trailofbits.com suffix (case insensitive) - username = re.sub(r"@trailofbits\.com$", "", email, flags=re.IGNORECASE) - - # If no @trailofbits.com, just take the part before @ - if "@" in username: - username = username.split("@")[0] + # Extract local part (before @) + username = email.split("@")[0] # Replace dots, hyphens, and other special characters with underscores username = re.sub(r"[^a-z0-9_]", "_", username.lower()) diff --git a/tests/test_api.py b/tests/test_api.py index 877f1ff..5e564ac 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -8,8 +8,8 @@ from dropkit.api import DigitalOceanAPI class TestDigitalOceanAPI: """Tests for DigitalOceanAPI class.""" - def test_sanitize_email_for_username_trailofbits(self): - """Test sanitizing trailofbits.com email.""" + def test_sanitize_email_for_username_trailofbits_backwards_compat(self): + """Test backwards compatibility: trailofbits.com emails still work.""" username = DigitalOceanAPI._sanitize_email_for_username("john.doe@trailofbits.com") assert username == "john_doe" @@ -33,6 +33,26 @@ class TestDigitalOceanAPI: username = DigitalOceanAPI._sanitize_email_for_username("@trailofbits.com") assert username == "user" + def test_sanitize_email_for_username_google(self): + """Test sanitizing google.com email.""" + username = DigitalOceanAPI._sanitize_email_for_username("jane.smith@google.com") + assert username == "jane_smith" + + def test_sanitize_email_for_username_gmail(self): + """Test sanitizing gmail.com email.""" + username = DigitalOceanAPI._sanitize_email_for_username("user123@gmail.com") + assert username == "user123" + + def test_sanitize_email_for_username_corporate(self): + """Test sanitizing corporate email with subdomain.""" + username = DigitalOceanAPI._sanitize_email_for_username("dev.ops@corp.company.com") + assert username == "dev_ops" + + def test_sanitize_email_for_username_plus_addressing(self): + """Test sanitizing email with plus addressing (any domain).""" + username = DigitalOceanAPI._sanitize_email_for_username("user+tag@outlook.com") + assert username == "user_tag" + class TestValidatePositiveInt: """Tests for _validate_positive_int method."""