diff --git a/aurweb/models/ssh_pub_key.py b/aurweb/models/ssh_pub_key.py new file mode 100644 index 00000000..01ff558e --- /dev/null +++ b/aurweb/models/ssh_pub_key.py @@ -0,0 +1,41 @@ +import os +import tempfile + +from subprocess import PIPE, Popen + +from sqlalchemy.orm import backref, mapper, relationship + +from aurweb.models.user import User +from aurweb.schema import SSHPubKeys + + +class SSHPubKey: + def __init__(self, **kwargs): + self.UserID = kwargs.get("UserID") + self.Fingerprint = kwargs.get("Fingerprint") + self.PubKey = kwargs.get("PubKey") + + +def get_fingerprint(pubkey): + with tempfile.TemporaryDirectory() as tmpdir: + pk = os.path.join(tmpdir, "ssh.pub") + + with open(pk, "w") as f: + f.write(pubkey) + + proc = Popen(["ssh-keygen", "-l", "-f", pk], stdout=PIPE, stderr=PIPE) + out, err = proc.communicate() + + # Invalid SSH Public Key. Return None to the caller. + if proc.returncode != 0: + return None + + parts = out.decode().split() + fp = parts[1].replace("SHA256:", "") + + return fp + + +mapper(SSHPubKey, SSHPubKeys, properties={ + "User": relationship(User, backref=backref("ssh_pub_key", uselist=False)) +}) diff --git a/test/test_ssh_pub_key.py b/test/test_ssh_pub_key.py new file mode 100644 index 00000000..fe9df047 --- /dev/null +++ b/test/test_ssh_pub_key.py @@ -0,0 +1,58 @@ +import pytest + +from aurweb.db import query +from aurweb.models.account_type import AccountType +from aurweb.models.ssh_pub_key import SSHPubKey, get_fingerprint +from aurweb.testing import setup_test_db +from aurweb.testing.models import make_user + +TEST_SSH_PUBKEY = """ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCycoCi5yGCvSclH2wmNBUuwsYEzRZZBJaQquRc4ysl+Tg+/jiDkR3Zn9fIznC4KnFoyrIHzkKuePZ3bNDYwkZxkJKoWBCh4hXKDXSm87FMN0+VDC+1QxF/z0XaAGr/P6f4XukabyddypBdnHcZiplbw+YOSqcAE2TCqOlSXwNMOcF9U89UsR/Q9i9I52hlvU0q8+fZVGhou1KCowFSnHYtrr5KYJ04CXkJ13DkVf3+pjQWyrByvBcf1hGEaczlgfobrrv/y96jDhgfXucxliNKLdufDPPkii3LhhsNcDmmI1VZ3v0irKvd9WZuauqloobY84zEFcDTyjn0hxGjVeYFejm4fBnvjga0yZXORuWksdNfXWLDxFk6MDDd1jF0ExRbP+OxDuU4IVyIuDL7S3cnbf2YjGhkms/8voYT2OBE7FwNlfv98Kr0NUp51zpf55Arxn9j0Rz9xTA7FiODQgCn6iQ0SDtzUNL0IKTCw26xJY5gzMxbfpvzPQGeulx/ioM= kevr@volcano +""" + +user, ssh_pub_key = None, None + + +@pytest.fixture(autouse=True) +def setup(): + from aurweb.db import session + + global user, ssh_pub_key + + setup_test_db("Users", "SSHPubKeys") + + account_type = query(AccountType, + AccountType.AccountType == "User").first() + user = make_user(Username="test", Email="test@example.org", + RealName="Test User", Passwd="testPassword", + AccountType=account_type) + + assert account_type == user.AccountType + assert account_type.ID == user.AccountTypeID + + ssh_pub_key = SSHPubKey(UserID=user.ID, + Fingerprint="testFingerprint", + PubKey="testPubKey") + + session.add(ssh_pub_key) + session.commit() + + yield ssh_pub_key + + session.delete(ssh_pub_key) + session.commit() + + +def test_ssh_pub_key(): + assert ssh_pub_key.UserID == user.ID + assert ssh_pub_key.User == user + assert ssh_pub_key.Fingerprint == "testFingerprint" + assert ssh_pub_key.PubKey == "testPubKey" + + +def test_ssh_pub_key_fingerprint(): + assert get_fingerprint(TEST_SSH_PUBKEY) is not None + + +def test_ssh_pub_key_invalid_fingerprint(): + assert get_fingerprint("ssh-rsa fake and invalid") is None diff --git a/test/test_user.py b/test/test_user.py index 4f144819..473b035a 100644 --- a/test/test_user.py +++ b/test/test_user.py @@ -12,6 +12,7 @@ from aurweb.db import query from aurweb.models.account_type import AccountType from aurweb.models.ban import Ban from aurweb.models.session import Session +from aurweb.models.ssh_pub_key import SSHPubKey from aurweb.models.user import User from aurweb.testing import setup_test_db from aurweb.testing.models import make_session, make_user @@ -26,7 +27,7 @@ def setup(): global account_type, user - setup_test_db("Users", "Sessions", "Bans") + setup_test_db("Users", "Sessions", "Bans", "SSHPubKeys") account_type = query(AccountType, AccountType.AccountType == "User").first() @@ -160,3 +161,20 @@ def test_user_update_password(): def test_user_minimum_passwd_length(): passwd_min_len = aurweb.config.getint("options", "passwd_min_len") assert User.minimum_passwd_length() == passwd_min_len + + +def test_user_ssh_pub_key(): + from aurweb.db import session + + assert user.ssh_pub_key is None + + ssh_pub_key = SSHPubKey(UserID=user.ID, + Fingerprint="testFingerprint", + PubKey="testPubKey") + session.add(ssh_pub_key) + session.commit() + + assert user.ssh_pub_key == ssh_pub_key + + session.delete(ssh_pub_key) + session.commit()