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:
@@ -1153,6 +1153,18 @@ td.actions .btn + .btn {
|
||||
margin: 0 0 1rem 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
/* A settings block whose feature is not built yet. Reads as a warning rather
|
||||
than body text, because the failure it prevents is an administrator
|
||||
believing a control does something. */
|
||||
.setting-notice {
|
||||
border-left: 3px solid var(--warning);
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
margin: 0 0 1rem 0;
|
||||
padding: 0.6rem 0.8rem;
|
||||
}
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
<div class="setting-group">
|
||||
<h3>Audit Log Retention</h3>
|
||||
<p class="setting-description">
|
||||
Configure how long audit logs are retained. Older logs will be automatically purged.
|
||||
Set to 0 to keep logs indefinitely.
|
||||
How long audit entries are kept. 0 keeps everything forever, which is
|
||||
the default. Saving a period does NOT delete anything by itself:
|
||||
purge from Settings > Site & Facility, or run
|
||||
<code>flask audit purge --apply</code> on the server.
|
||||
</p>
|
||||
|
||||
<div class="setting-row">
|
||||
@@ -17,13 +19,13 @@
|
||||
<span>Retention Period (days)</span>
|
||||
<input
|
||||
type="number"
|
||||
v-model="settings.audit_retention_days"
|
||||
v-model="settings.audit_retentiondays"
|
||||
min="0"
|
||||
placeholder="90"
|
||||
@blur="saveSetting('audit_retention_days', settings.audit_retention_days)"
|
||||
placeholder="0"
|
||||
@blur="saveSetting('audit_retentiondays', settings.audit_retentiondays)"
|
||||
:disabled="saving"
|
||||
>
|
||||
<small class="input-hint">0 = keep forever, recommended: 90 days</small>
|
||||
<small class="input-hint">0 = keep forever</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,9 +7,16 @@
|
||||
<div class="section-card">
|
||||
<div class="setting-group">
|
||||
<h3>SAML Single Sign-On</h3>
|
||||
<p class="setting-notice">
|
||||
<strong>Not implemented yet.</strong> These settings are stored so a
|
||||
site can be configured ahead of the feature landing, but nothing
|
||||
consumes them today: there is no SAML login route, and no assertion
|
||||
is accepted. Sign-in is local accounts only, whatever is set here.
|
||||
</p>
|
||||
<p class="setting-description">
|
||||
Enable SAML SSO for enterprise authentication. Users can sign in using your organization's
|
||||
identity provider (Azure AD, Okta, etc.). Local login can remain enabled as a fallback.
|
||||
When it ships, users will sign in through your organization's identity
|
||||
provider (Azure AD, Okta, etc.), with local login available as a
|
||||
fallback.
|
||||
</p>
|
||||
|
||||
<div class="setting-row">
|
||||
@@ -82,19 +89,26 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Locked on until SAML exists. A toggle that reads "Allow Local
|
||||
Login: off" while password login still works is the dangerous
|
||||
one on this page: an administrator can believe they have closed
|
||||
the local door, and nothing tells them otherwise. It is not a
|
||||
setting anyone can honour yet, so it does not accept a click. -->
|
||||
<div class="setting-row">
|
||||
<label class="toggle-label">
|
||||
<span>Allow Local Login</span>
|
||||
<button
|
||||
class="toggle-btn"
|
||||
:class="{ active: settings.saml_allow_local_login }"
|
||||
@click="toggleSetting('saml_allow_local_login')"
|
||||
:disabled="saving"
|
||||
class="toggle-btn active"
|
||||
disabled
|
||||
title="Local login cannot be turned off until SAML sign-in exists"
|
||||
>
|
||||
<span class="toggle-slider"></span>
|
||||
</button>
|
||||
</label>
|
||||
<small class="input-hint toggle-hint">Keep enabled for admin fallback access</small>
|
||||
<small class="input-hint toggle-hint">
|
||||
Always on for now. Local accounts are the only way in until SAML
|
||||
sign-in ships, so this cannot be turned off yet.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="setting-row">
|
||||
@@ -148,15 +162,20 @@ const {
|
||||
// Per-page connection status (stays with the page, not the composable).
|
||||
const samlStatus = computed(() => {
|
||||
if (!settings.saml_enabled) return 'inactive'
|
||||
if (!settings.saml_idp_metadata_url || !settings.saml_entity_id) return 'warning'
|
||||
// 'pending' rather than a healthy state even when every field is filled in:
|
||||
// nothing is connected until the feature exists.
|
||||
return 'pending'
|
||||
})
|
||||
|
||||
// Never says "available". It used to report "Configured - SAML login
|
||||
// available" as soon as two fields held text, which is a claim about a feature
|
||||
// that does not exist: an administrator could reasonably read that as SSO
|
||||
// being live and stop issuing local accounts.
|
||||
const samlMessage = computed(() => {
|
||||
if (!settings.saml_enabled) return 'Disabled - using local authentication only'
|
||||
if (!settings.saml_idp_metadata_url) return 'IdP metadata URL not configured'
|
||||
if (!settings.saml_entity_id) return 'Entity ID not configured'
|
||||
return 'Configured - SAML login available'
|
||||
if (!settings.saml_enabled) return 'Off - local accounts only'
|
||||
if (!settings.saml_idp_metadata_url) return 'IdP metadata URL not set'
|
||||
if (!settings.saml_entity_id) return 'Entity ID not set'
|
||||
return 'Settings saved - waiting on SAML support, local accounts still in use'
|
||||
})
|
||||
|
||||
onMounted(loadSettings)
|
||||
|
||||
Reference in New Issue
Block a user