Open-source AI data analyst platform extracted from internal repo. Includes data sync engine, Keboola adapter, Flask web portal, server deployment scripts, and configuration templates.
118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
"""Tests for webapp.user_service SSH key validation and normalization."""
|
|
|
|
import pytest
|
|
|
|
from webapp.user_service import validate_ssh_key
|
|
|
|
|
|
# Valid SSH keys for testing
|
|
VALID_ED25519_KEY = (
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O"
|
|
" user@hostname"
|
|
)
|
|
|
|
VALID_RSA_KEY = (
|
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7vbqajxMM8qR"
|
|
"ZortjjMq2bCxPkNW0KSBiAIae6MNcL+Kk6MFSwj3pvFOyEEBO4e"
|
|
"OE1JxHoOdSPsFhPeQisdit3MfG6E7CaJm3VE/ONxQat1M/V+55og"
|
|
" user@hostname"
|
|
)
|
|
|
|
|
|
class TestValidateSshKey:
|
|
"""Test SSH key validation."""
|
|
|
|
def test_valid_ed25519_key(self):
|
|
is_valid, error = validate_ssh_key(VALID_ED25519_KEY)
|
|
assert is_valid
|
|
assert error == ""
|
|
|
|
def test_valid_rsa_key(self):
|
|
is_valid, error = validate_ssh_key(VALID_RSA_KEY)
|
|
assert is_valid
|
|
assert error == ""
|
|
|
|
def test_empty_key(self):
|
|
is_valid, error = validate_ssh_key("")
|
|
assert not is_valid
|
|
assert "required" in error.lower()
|
|
|
|
def test_private_key_rejected(self):
|
|
is_valid, error = validate_ssh_key("-----BEGIN PRIVATE KEY-----\ndata\n-----END PRIVATE KEY-----")
|
|
assert not is_valid
|
|
|
|
def test_too_short_key(self):
|
|
is_valid, error = validate_ssh_key("ssh-ed25519 AAAA user@host")
|
|
assert not is_valid
|
|
assert "short" in error.lower()
|
|
|
|
def test_invalid_format(self):
|
|
is_valid, error = validate_ssh_key("not-a-valid-ssh-key-at-all")
|
|
assert not is_valid
|
|
|
|
|
|
class TestSshKeyNewlineNormalization:
|
|
"""Test that SSH keys with line breaks are normalized to single line.
|
|
|
|
This is the core fix for issue #139: webapp was writing keys with
|
|
line breaks to authorized_keys, causing SSH auth failures.
|
|
"""
|
|
|
|
def test_key_with_newlines_between_parts(self):
|
|
"""Key with newlines between type, data, and comment."""
|
|
broken_key = (
|
|
"ssh-ed25519\n"
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O\n"
|
|
"user@hostname"
|
|
)
|
|
is_valid, error = validate_ssh_key(broken_key)
|
|
assert is_valid, f"Key with newlines should be valid after normalization: {error}"
|
|
|
|
def test_key_with_carriage_returns(self):
|
|
"""Key with Windows-style \\r\\n line breaks."""
|
|
broken_key = (
|
|
"ssh-ed25519\r\n"
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O\r\n"
|
|
"user@hostname"
|
|
)
|
|
is_valid, error = validate_ssh_key(broken_key)
|
|
assert is_valid, f"Key with \\r\\n should be valid after normalization: {error}"
|
|
|
|
def test_key_with_tabs(self):
|
|
"""Key with tabs instead of spaces."""
|
|
broken_key = (
|
|
"ssh-ed25519\t"
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O\t"
|
|
"user@hostname"
|
|
)
|
|
is_valid, error = validate_ssh_key(broken_key)
|
|
assert is_valid, f"Key with tabs should be valid after normalization: {error}"
|
|
|
|
def test_key_with_multiple_spaces(self):
|
|
"""Key with extra spaces between parts."""
|
|
broken_key = (
|
|
"ssh-ed25519 "
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O "
|
|
"user@hostname"
|
|
)
|
|
is_valid, error = validate_ssh_key(broken_key)
|
|
assert is_valid, f"Key with extra spaces should be valid after normalization: {error}"
|
|
|
|
def test_key_with_leading_trailing_whitespace(self):
|
|
"""Key with leading/trailing whitespace and newlines."""
|
|
broken_key = (
|
|
"\n ssh-ed25519 "
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O "
|
|
"user@hostname \n"
|
|
)
|
|
is_valid, error = validate_ssh_key(broken_key)
|
|
assert is_valid, f"Key with surrounding whitespace should be valid: {error}"
|
|
|
|
def test_key_without_comment_with_newlines(self):
|
|
"""Key without comment part, with newlines."""
|
|
broken_key = (
|
|
"ssh-ed25519\n"
|
|
"AAAAC3NzaC1lZDI1NTE5AAAAIDIE6+wG3U019D4AUQVOr17xxX5enS1OUVfLZ4cHa4/O\n"
|
|
)
|
|
is_valid, error = validate_ssh_key(broken_key)
|
|
assert is_valid, f"Key without comment with newlines should be valid: {error}"
|