One key for audit retention, and stop the SAML page claiming a feature exists
Wave 6: two settings pages that told an administrator the site was configured when it was not. That is worse than a missing feature, because nobody goes looking for it. Audit retention had TWO keys. shopdb/core/services/auditretention.py reads `audit_retentiondays` (category site, default 0). A second `audit_retention_days` was seeded under category audit with a default of 90, and the Audit & Logging page wrote to that one - so an administrator set a period, reloaded, saw it persisted, and nothing ever consulted it. Retention stayed off. The page now writes the key that is read, the duplicate is out of the seed list, and migration 7d39 carries a value an operator actually set on the dead key over to the live one before deleting it: their intent was "keep 30 days", and the fix should not silently discard it. The page's copy also claimed logs "will be automatically purged", which nothing does - purging is manual, from the site settings page or `flask audit purge --apply`. The Authentication page offered a full SAML configuration - metadata URL, entity id, ACS URL, auto-create, admin group - and reported "Configured - SAML login available" once two fields held text. There is no SAML anywhere in the codebase: no login route, no ACS, no assertion handling. SAML IS planned, so the settings stay and the page keeps them; what changes is that it now says plainly that nothing consumes them yet, and the status line never claims availability. The dangerous control there was "Allow Local Login". Turning it off did nothing at all, so an administrator could believe they had closed local password login while it stayed wide open. It is locked on until there is another way in, and says why. CONFIG.md marks the whole block reserved rather than describing it as working configuration. Scope: product
This commit is contained in:
56
tests/test_core/test_audit_retention_key.py
Normal file
56
tests/test_core/test_audit_retention_key.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""One setting, one key.
|
||||
|
||||
Audit retention had two: shopdb/core/services/auditretention.py reads
|
||||
`audit_retentiondays`, and the Audit & Logging settings page wrote
|
||||
`audit_retention_days`. The page saved, reloaded, showed the value back, and
|
||||
nothing ever consulted it - retention stayed off while the screen said it was
|
||||
on. A settings page that saves a key nothing reads is worse than a missing
|
||||
feature, because it tells an administrator the site is configured when it is
|
||||
not.
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _seeded_keys():
|
||||
"""Every setting key in the DEFAULTS list, read statically."""
|
||||
source = (REPO / 'shopdb' / 'core' / 'api' / 'settings.py').read_text()
|
||||
return re.findall(r"^\s*'key': '([a-z0-9_]+)'", source, re.M)
|
||||
|
||||
|
||||
def test_only_one_audit_retention_key_is_seeded():
|
||||
keys = _seeded_keys()
|
||||
retention = sorted({key for key in keys if 'retention' in key})
|
||||
assert retention == ['audit_retentiondays'], retention
|
||||
|
||||
|
||||
def test_the_service_and_the_settings_pages_use_the_same_key():
|
||||
"""The three places that mention retention must agree: the service that
|
||||
reads it, and both settings pages that write it."""
|
||||
service = (REPO / 'shopdb' / 'core' / 'services' / 'auditretention.py').read_text()
|
||||
assert "audit_retentiondays" in service
|
||||
assert "audit_retention_days" not in service
|
||||
|
||||
for page in ('AuditSettings.vue', 'SiteSettings.vue'):
|
||||
text = (REPO / 'frontend' / 'src' / 'views' / 'settings' / page).read_text()
|
||||
if 'retention' not in text.lower():
|
||||
continue
|
||||
assert 'audit_retention_days' not in text, page
|
||||
assert 'audit_retentiondays' in text, page
|
||||
|
||||
|
||||
def test_the_retention_setting_actually_drives_the_report(client, db,
|
||||
auth_headers):
|
||||
"""End to end: set it through the API the page uses, and read it back from
|
||||
the endpoint that decides what a purge would remove."""
|
||||
saved = client.put('/api/settings/audit_retentiondays',
|
||||
json={'value': 30}, headers=auth_headers)
|
||||
assert saved.status_code == 200, saved.get_json()
|
||||
|
||||
report = client.get('/api/auditlogs/retention', headers=auth_headers)
|
||||
assert report.status_code == 200, report.get_json()
|
||||
assert report.get_json()['data']['retentiondays'] == 30, \
|
||||
'the setting the page writes must be the one the service reads'
|
||||
Reference in New Issue
Block a user