"""Changing how a PC is reached has to leave a record. Editing a computer wrote an audit entry for its named fields and nothing at all for two changes that matter most to anybody investigating later: * the primary IP address - how the PC is found on the network * the remote-access protocols and their ports - how somebody connects to it Both are set on the same form, in the same request, and both were invisible afterwards. A PC that moved address had no history of moving, and a PC that gained a VNC listener had no record of gaining one. """ import pytest from shopdb.api import db @pytest.fixture def pc(client, db, auth_headers): from shopdb.core.models import AssetType, AssetStatus if not AssetType.query.filter_by(assettype='computer').first(): db.session.add(AssetType(assettype='computer')) if not AssetStatus.query.first(): db.session.add(AssetStatus(status='In Use')) db.session.commit() created = client.post('/api/computers', headers=auth_headers, json={ 'assetnumber': 'AUDIT-PC-1', 'hostname': 'auditpc1', 'ipaddress': '10.20.30.40', }) assert created.status_code == 201, created.get_json() data = created.get_json()['data'] return data.get('computerid') or data['computer']['computerid'] def _entries(entityid): from shopdb.core.models import AuditLog return (AuditLog.query .filter_by(entitytype='Computer', entityid=entityid) .order_by(AuditLog.auditlogid.desc()).all()) def test_moving_a_pc_to_another_address_is_recorded(client, auth_headers, pc): response = client.put('/api/computers/%d' % pc, headers=auth_headers, json={'ipaddress': '10.20.30.99'}) assert response.status_code == 200, response.get_json() latest = _entries(pc)[0] assert 'ipaddress' in (latest.changes or {}), latest.changes assert latest.changes['ipaddress']['old'] == '10.20.30.40' assert latest.changes['ipaddress']['new'] == '10.20.30.99' def test_an_unchanged_address_records_nothing(client, auth_headers, pc): """A PUT that re-sends the same value is not a change, and an audit log full of non-events is one nobody reads.""" before = len(_entries(pc)) response = client.put('/api/computers/%d' % pc, headers=auth_headers, json={'ipaddress': '10.20.30.40'}) assert response.status_code == 200 assert len(_entries(pc)) == before def test_changing_the_remote_access_protocols_is_recorded(client, db, auth_headers, pc): from plugins.computers.models import AccessProtocol rdp = AccessProtocol(name='RDP', scheme='rdp', linktemplate='rdp://{host}') vnc = AccessProtocol(name='VNC', scheme='vnc', linktemplate='vnc://{host}') db.session.add_all([rdp, vnc]) db.session.commit() client.put('/api/computers/%d' % pc, headers=auth_headers, json={'accessmethods': [{'protocolid': rdp.protocolid}]}) response = client.put('/api/computers/%d' % pc, headers=auth_headers, json={'accessmethods': [ {'protocolid': vnc.protocolid, 'portoverride': 5999}]}) assert response.status_code == 200, response.get_json() latest = _entries(pc)[0] assert 'accessmethods' in (latest.changes or {}), latest.changes change = latest.changes['accessmethods'] assert change['old'] == ['%d:default' % rdp.protocolid] assert change['new'] == ['%d:5999' % vnc.protocolid] def test_resending_the_same_protocols_records_nothing(client, db, auth_headers, pc): from plugins.computers.models import AccessProtocol ssh = AccessProtocol(name='SSH', scheme='ssh', linktemplate='ssh://{host}') db.session.add(ssh) db.session.commit() client.put('/api/computers/%d' % pc, headers=auth_headers, json={'accessmethods': [{'protocolid': ssh.protocolid}]}) before = len(_entries(pc)) client.put('/api/computers/%d' % pc, headers=auth_headers, json={'accessmethods': [{'protocolid': ssh.protocolid}]}) assert len(_entries(pc)) == before