Skip to content

Commit ab75a27

Browse files
committed
feat(hardware): version the in-band consent warning in all 7 languages (#609 phase 2)
The compliance warning text is now localized (en/de/fr/es/pt/ko/it) while the VERSION stays language-spanning: _HW_CONSENT_TEXT holds the same v1 warning in each language, and hw_consent_warning(lang) injects the shared version + require_delay_seconds so they can never drift per-language (bump all together). * GET /api/hardware-monitoring/consent?lang=<code> returns the warning in the requested language (falls back to English). HW_CONSENT_WARNING kept as an English alias for existing callers/tests. * The acknowledged LANGUAGE is now recorded alongside the version — set consent stores ack_lang and the audit line reads 'acknowledged compliance warning v1 [de]', so the non-repudiation record captures which exact text the user saw. * Frontend passes the current UI language to the consent GET (?lang=) and the enable POST (lang=), and re-fetches the localized warning on language switch. Translations adversarially verified by 6 native-level reviewers vs the English source (control IDs CMMC/NIST 800-171 3.4.6+3.1.5/DISA STIG preserved verbatim in every language). Applied the confirmed fixes: es 'encendido'->'alimentación' (the operation-list 'power' means power-control, not power-on), es informar+de grammar, de closing-quote typography, ko install-vs-may modal + 'override' precision. Tests: +4 integration cases (per-lang warning, unknown-lang fallback, ack_lang recorded in audit + status, unknown-lang recorded as en). Full suite 311 passed.
1 parent 0d04b4e commit ab75a27

5 files changed

Lines changed: 169 additions & 22 deletions

File tree

pegaprox/api/nodes.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,18 @@ def _hw_consent_state():
254254
@bp.route('/api/hardware-monitoring/consent', methods=['GET'])
255255
@require_auth(perms=['node.view'])
256256
def get_hw_monitoring_consent():
257-
"""Current consent status + the versioned warning the UI must present."""
257+
"""Current consent status + the versioned warning the UI must present, in the
258+
requested language (?lang=de|en|fr|es|pt|ko|it; falls back to English)."""
258259
from pegaprox.core import bmc
259260
enabled, ack = _hw_consent_state()
260261
return jsonify({
261262
'enabled': enabled,
262263
'acknowledged_by': ack.get('acknowledged_by'),
263264
'acknowledged_at': ack.get('acknowledged_at'),
264265
'ack_version': ack.get('ack_version'),
266+
'ack_lang': ack.get('ack_lang'),
265267
'current_version': bmc.HW_CONSENT_VERSION,
266-
'warning': bmc.HW_CONSENT_WARNING,
268+
'warning': bmc.hw_consent_warning(request.args.get('lang')),
267269
})
268270

269271

@@ -295,18 +297,23 @@ def set_hw_monitoring_consent():
295297
return jsonify({'enabled': False})
296298

297299
if data.get('acknowledge') is True and _hw_int(data.get('ack_version')) == bmc.HW_CONSENT_VERSION:
300+
# which localized text the user actually saw (part of the non-repudiation record)
301+
ack_lang = (str(data.get('lang') or 'en').split('-')[0].lower())[:8]
302+
if ack_lang not in bmc._HW_CONSENT_TEXT:
303+
ack_lang = 'en'
298304
rec = {
299305
'enabled': True,
300306
'acknowledged_by': usr,
301307
'acknowledged_at': datetime.now().isoformat(),
302308
'ack_version': bmc.HW_CONSENT_VERSION,
309+
'ack_lang': ack_lang,
303310
}
304311
settings['hardware_monitoring'] = rec
305312
save_server_settings(settings)
306313
# Durable, attributed non-repudiation record of the acknowledgement.
307314
log_audit(usr, 'hardware_monitoring.enabled',
308315
'In-band hardware monitoring enabled; acknowledged compliance '
309-
'warning v%d' % bmc.HW_CONSENT_VERSION)
316+
'warning v%d [%s]' % (bmc.HW_CONSENT_VERSION, ack_lang))
310317
return jsonify(rec)
311318

312319
return jsonify({

pegaprox/core/bmc.py

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,115 @@
4444
# audit record can reference an exact version — bump the version when the wording
4545
# materially changes and a stored ack below it re-prompts. (The network-Redfish
4646
# opt-in is a separate, sharper warning with an enforced delay — a later phase.)
47+
#
48+
# The VERSION spans ALL languages: every _HW_CONSENT_TEXT entry is the SAME warning,
49+
# v1, in a different language — they must stay semantically equivalent and be bumped
50+
# together. version + require_delay_seconds are injected by hw_consent_warning() so
51+
# they can never drift per-language. The acknowledged language is recorded alongside
52+
# the version at ack time (which exact text the user actually saw).
4753
HW_CONSENT_VERSION = 1
48-
HW_CONSENT_WARNING = {
49-
'version': HW_CONSENT_VERSION,
50-
'require_delay_seconds': 0, # in-band: mandatory confirm, no forced wait (Redfish will use >0)
51-
'title': 'Enable in-band hardware monitoring (IPMI)',
52-
'summary': 'PegaProx will read hardware health directly on each node through its local IPMI interface.',
53-
'points': [
54-
'Reads happen on the node over its local IPMI channel (/dev/ipmi0) — no BMC network credentials are stored, and the out-of-band management network is not accessed.',
55-
'Only read-only IPMI commands are issued (sensors, event log, FRU inventory, power). No power, virtual-media or firmware operations.',
56-
'If you enable installation, PegaProx will install the "ipmitool" package and may load the IPMI kernel modules on the node.',
57-
'On strictly hardened systems the local IPMI interface may be intentionally disabled under a least-functionality baseline. Enabling this here does not override that — where the interface is absent, PegaProx reports no data rather than re-enabling it.',
58-
],
59-
'compliance_note': 'Enabling this may be relevant to your least-functionality and BMC-hardening controls (e.g. CMMC / NIST 800-171 3.4.6 and 3.1.5, DISA STIG BMC/OOB guidance). Confirm with your compliance owner. This is not legal advice.',
60-
'confirm_label': 'I understand, and I accept responsibility for enabling this',
54+
HW_CONSENT_DELAY_SECONDS = 0 # in-band: mandatory confirm, no forced wait (Redfish will use >0)
55+
56+
_HW_CONSENT_TEXT = {
57+
'en': {
58+
'title': 'Enable in-band hardware monitoring (IPMI)',
59+
'summary': 'PegaProx will read hardware health directly on each node through its local IPMI interface.',
60+
'points': [
61+
'Reads happen on the node over its local IPMI channel (/dev/ipmi0) — no BMC network credentials are stored, and the out-of-band management network is not accessed.',
62+
'Only read-only IPMI commands are issued (sensors, event log, FRU inventory, power). No power, virtual-media or firmware operations.',
63+
'If you enable installation, PegaProx will install the "ipmitool" package and may load the IPMI kernel modules on the node.',
64+
'On strictly hardened systems the local IPMI interface may be intentionally disabled under a least-functionality baseline. Enabling this here does not override that — where the interface is absent, PegaProx reports no data rather than re-enabling it.',
65+
],
66+
'compliance_note': 'Enabling this may be relevant to your least-functionality and BMC-hardening controls (e.g. CMMC / NIST 800-171 3.4.6 and 3.1.5, DISA STIG BMC/OOB guidance). Confirm with your compliance owner. This is not legal advice.',
67+
'confirm_label': 'I understand, and I accept responsibility for enabling this',
68+
},
69+
'de': {
70+
'title': 'In-Band-Hardware-Überwachung (IPMI) aktivieren',
71+
'summary': 'PegaProx liest den Hardware-Zustand direkt auf jedem Node über dessen lokale IPMI-Schnittstelle.',
72+
'points': [
73+
'Die Lesevorgänge erfolgen auf dem Node über dessen lokalen IPMI-Kanal (/dev/ipmi0) — es werden keine BMC-Netzwerk-Zugangsdaten gespeichert, und auf das Out-of-Band-Management-Netzwerk wird nicht zugegriffen.',
74+
'Es werden ausschließlich lesende IPMI-Befehle ausgeführt (Sensoren, Ereignisprotokoll, FRU-Inventar, Stromverbrauch). Keine Power-, Virtual-Media- oder Firmware-Operationen.',
75+
'Wenn Sie die Installation aktivieren, installiert PegaProx das Paket „ipmitool“ und lädt ggf. die IPMI-Kernelmodule auf dem Node.',
76+
'Auf streng gehärteten Systemen kann die lokale IPMI-Schnittstelle bewusst im Rahmen einer Least-Functionality-Baseline deaktiviert sein. Die Aktivierung hier hebt das nicht auf — wo die Schnittstelle fehlt, meldet PegaProx keine Daten, statt sie wieder zu aktivieren.',
77+
],
78+
'compliance_note': 'Die Aktivierung kann für Ihre Least-Functionality- und BMC-Härtungs-Kontrollen relevant sein (z. B. CMMC / NIST 800-171 3.4.6 und 3.1.5, DISA STIG BMC/OOB-Vorgaben). Stimmen Sie sich mit Ihrem Compliance-Verantwortlichen ab. Dies ist keine Rechtsberatung.',
79+
'confirm_label': 'Ich verstehe dies und übernehme die Verantwortung für die Aktivierung',
80+
},
81+
'fr': {
82+
'title': "Activer la surveillance matérielle en bande (IPMI)",
83+
'summary': "PegaProx lira l'état du matériel directement sur chaque nœud via son interface IPMI locale.",
84+
'points': [
85+
"Les lectures s'effectuent sur le nœud via son canal IPMI local (/dev/ipmi0) — aucun identifiant réseau du BMC n'est stocké et le réseau de gestion hors bande n'est pas utilisé.",
86+
"Seules des commandes IPMI en lecture seule sont émises (capteurs, journal d'événements, inventaire FRU, consommation). Aucune opération d'alimentation, de média virtuel ou de micrologiciel.",
87+
"Si vous activez l'installation, PegaProx installera le paquet « ipmitool » et pourra charger les modules noyau IPMI sur le nœud.",
88+
"Sur les systèmes fortement durcis, l'interface IPMI locale peut être volontairement désactivée dans le cadre d'une base de moindre fonctionnalité. L'activer ici ne l'annule pas — là où l'interface est absente, PegaProx ne renvoie aucune donnée plutôt que de la réactiver.",
89+
],
90+
'compliance_note': "Cette activation peut concerner vos contrôles de moindre fonctionnalité et de durcissement du BMC (par ex. CMMC / NIST 800-171 3.4.6 et 3.1.5, recommandations DISA STIG BMC/OOB). Vérifiez avec votre responsable conformité. Ceci ne constitue pas un conseil juridique.",
91+
'confirm_label': "Je comprends et j'accepte la responsabilité de cette activation",
92+
},
93+
'es': {
94+
'title': 'Activar la supervisión de hardware en banda (IPMI)',
95+
'summary': 'PegaProx leerá el estado del hardware directamente en cada nodo a través de su interfaz IPMI local.',
96+
'points': [
97+
'Las lecturas se realizan en el nodo a través de su canal IPMI local (/dev/ipmi0): no se almacenan credenciales de red del BMC y no se accede a la red de gestión fuera de banda.',
98+
'Solo se emiten comandos IPMI de solo lectura (sensores, registro de eventos, inventario FRU, consumo). Ninguna operación de alimentación, medios virtuales o firmware.',
99+
'Si activa la instalación, PegaProx instalará el paquete «ipmitool» y podrá cargar los módulos del kernel IPMI en el nodo.',
100+
'En sistemas muy reforzados, la interfaz IPMI local puede estar deshabilitada intencionadamente bajo una base de funcionalidad mínima. Activarla aquí no anula eso: donde la interfaz no existe, PegaProx no informa de ningún dato en lugar de reactivarla.',
101+
],
102+
'compliance_note': 'Activar esto puede ser relevante para sus controles de funcionalidad mínima y de refuerzo del BMC (p. ej., CMMC / NIST 800-171 3.4.6 y 3.1.5, directrices DISA STIG BMC/OOB). Confírmelo con su responsable de cumplimiento. Esto no es asesoramiento legal.',
103+
'confirm_label': 'Entiendo y acepto la responsabilidad de activar esto',
104+
},
105+
'pt': {
106+
'title': 'Ativar a monitorização de hardware em banda (IPMI)',
107+
'summary': 'O PegaProx lerá o estado do hardware diretamente em cada nó através da sua interface IPMI local.',
108+
'points': [
109+
'As leituras são feitas no nó através do seu canal IPMI local (/dev/ipmi0) — não são armazenadas credenciais de rede do BMC e a rede de gestão fora de banda não é acedida.',
110+
'Apenas são emitidos comandos IPMI de leitura (sensores, registo de eventos, inventário FRU, consumo). Nenhuma operação de energia, media virtual ou firmware.',
111+
'Se ativar a instalação, o PegaProx instalará o pacote «ipmitool» e poderá carregar os módulos de kernel IPMI no nó.',
112+
'Em sistemas fortemente reforçados, a interface IPMI local pode estar intencionalmente desativada sob uma base de funcionalidade mínima. Ativá-la aqui não anula isso — onde a interface não existe, o PegaProx não devolve dados em vez de a reativar.',
113+
],
114+
'compliance_note': 'Ativar isto pode ser relevante para os seus controlos de funcionalidade mínima e de reforço do BMC (por ex., CMMC / NIST 800-171 3.4.6 e 3.1.5, orientações DISA STIG BMC/OOB). Confirme com o seu responsável de conformidade. Isto não constitui aconselhamento jurídico.',
115+
'confirm_label': 'Compreendo e aceito a responsabilidade por ativar isto',
116+
},
117+
'ko': {
118+
'title': '인밴드 하드웨어 모니터링(IPMI) 활성화',
119+
'summary': 'PegaProx는 각 노드의 로컬 IPMI 인터페이스를 통해 하드웨어 상태를 직접 읽습니다.',
120+
'points': [
121+
'읽기는 노드의 로컬 IPMI 채널(/dev/ipmi0)을 통해 수행됩니다. BMC 네트워크 자격 증명은 저장되지 않으며, 대역 외 관리 네트워크에 접근하지 않습니다.',
122+
'읽기 전용 IPMI 명령만 실행됩니다(센서, 이벤트 로그, FRU 인벤토리, 전력). 전원, 가상 미디어 또는 펌웨어 작업은 수행하지 않습니다.',
123+
'설치를 활성화하면 PegaProx가 노드에 "ipmitool" 패키지를 설치하며, IPMI 커널 모듈을 로드할 수도 있습니다.',
124+
'강력하게 강화된 시스템에서는 최소 기능 기준에 따라 로컬 IPMI 인터페이스가 의도적으로 비활성화되어 있을 수 있습니다. 여기서 활성화하더라도 이를 무효화하지 않습니다. 인터페이스가 없는 경우 PegaProx는 이를 다시 활성화하지 않고 데이터를 보고하지 않습니다.',
125+
],
126+
'compliance_note': '이 기능을 활성화하는 것은 최소 기능 및 BMC 강화 통제(예: CMMC / NIST 800-171 3.4.6 및 3.1.5, DISA STIG BMC/OOB 지침)와 관련될 수 있습니다. 규정 준수 책임자와 확인하십시오. 이것은 법률 자문이 아닙니다.',
127+
'confirm_label': '이해했으며 이 기능을 활성화하는 것에 대한 책임을 수락합니다',
128+
},
129+
'it': {
130+
'title': "Abilitare il monitoraggio hardware in banda (IPMI)",
131+
'summary': "PegaProx leggerà lo stato dell'hardware direttamente su ogni nodo tramite la sua interfaccia IPMI locale.",
132+
'points': [
133+
"Le letture avvengono sul nodo tramite il suo canale IPMI locale (/dev/ipmi0): non vengono memorizzate credenziali di rete del BMC e la rete di gestione fuori banda non viene utilizzata.",
134+
"Vengono emessi solo comandi IPMI di sola lettura (sensori, registro eventi, inventario FRU, consumo). Nessuna operazione di alimentazione, supporti virtuali o firmware.",
135+
"Se abiliti l'installazione, PegaProx installerà il pacchetto « ipmitool » e potrà caricare i moduli kernel IPMI sul nodo.",
136+
"Su sistemi fortemente irrobustiti, l'interfaccia IPMI locale può essere disattivata intenzionalmente secondo una baseline di funzionalità minima. Abilitarla qui non annulla questo — dove l'interfaccia è assente, PegaProx non restituisce dati anziché riattivarla.",
137+
],
138+
'compliance_note': "L'abilitazione può essere rilevante per i tuoi controlli di funzionalità minima e di irrobustimento del BMC (ad es. CMMC / NIST 800-171 3.4.6 e 3.1.5, linee guida DISA STIG BMC/OOB). Verifica con il tuo responsabile della conformità. Questo non è un parere legale.",
139+
'confirm_label': "Comprendo e accetto la responsabilità di abilitare questa funzione",
140+
},
61141
}
62142

63143

144+
def hw_consent_warning(lang=None):
145+
"""The consent warning for `lang` (falls back to English), with the shared
146+
version + delay injected so they can never drift per-language."""
147+
base = (lang or 'en').split('-')[0].lower()
148+
text = _HW_CONSENT_TEXT.get(base) or _HW_CONSENT_TEXT['en']
149+
return {'version': HW_CONSENT_VERSION, 'require_delay_seconds': HW_CONSENT_DELAY_SECONDS, **text}
150+
151+
152+
# Backward-compat English alias for callers/tests that referenced the old constant.
153+
HW_CONSENT_WARNING = hw_consent_warning('en')
154+
155+
64156
def _num(s):
65157
"""First numeric token in a string as float, or None."""
66158
m = re.search(r'-?\d+(?:\.\d+)?', s or '')

tests/test_integration_hardware.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,51 @@ def test_enable_writes_audit_record(api, seed, monkeypatch):
127127
assert status['enabled'] is True and status['acknowledged_by'] == 'root'
128128

129129

130+
# ===========================================================================
131+
# Localized versioned warning (#609 phase 2) — same version, 7 languages
132+
# ===========================================================================
133+
134+
def test_consent_warning_is_localized_by_lang(api, seed):
135+
viewer = seed.user('ro', role='viewer', tenant_id='default')
136+
en = api.as_user(viewer).get(CONSENT_ROUTE + '?lang=en').get_json()['warning']
137+
de = api.as_user(viewer).get(CONSENT_ROUTE + '?lang=de').get_json()['warning']
138+
fr = api.as_user(viewer).get(CONSENT_ROUTE + '?lang=fr').get_json()['warning']
139+
# same version across languages, but distinct localized text
140+
assert en['version'] == de['version'] == fr['version'] == bmc.HW_CONSENT_VERSION
141+
assert 'IPMI' in en['title'] and 'aktivieren' in de['title'] and 'Activer' in fr['title']
142+
assert de['title'] != en['title'] and fr['title'] != en['title']
143+
assert len(de['points']) == 4 and de['confirm_label'] and de['compliance_note']
144+
145+
146+
def test_consent_warning_unknown_lang_falls_back_to_english(api, seed):
147+
viewer = seed.user('ro', role='viewer', tenant_id='default')
148+
w = api.as_user(viewer).get(CONSENT_ROUTE + '?lang=zz').get_json()['warning']
149+
assert w['title'] == bmc.hw_consent_warning('en')['title']
150+
151+
152+
def test_enable_records_acknowledged_language(api, seed, monkeypatch):
153+
audit = []
154+
monkeypatch.setattr('pegaprox.api.nodes.log_audit',
155+
lambda u, a, d=None, **k: audit.append((u, a, d)))
156+
admin = seed.user('root', role='admin', tenant_id='default')
157+
resp = api.as_user(admin).post(CONSENT_ROUTE, json={
158+
'acknowledge': True, 'ack_version': bmc.HW_CONSENT_VERSION, 'lang': 'de'})
159+
assert resp.status_code == 200, resp.get_data(as_text=True)
160+
assert resp.get_json()['ack_lang'] == 'de'
161+
# the acknowledged language is part of the non-repudiation record
162+
assert len(audit) == 1 and '[de]' in audit[0][2]
163+
# and it is surfaced back on the status endpoint
164+
assert api.as_user(admin).get(CONSENT_ROUTE).get_json()['ack_lang'] == 'de'
165+
166+
167+
def test_enable_unknown_language_is_recorded_as_english(api, seed):
168+
admin = seed.user('root', role='admin', tenant_id='default')
169+
resp = api.as_user(admin).post(CONSENT_ROUTE, json={
170+
'acknowledge': True, 'ack_version': bmc.HW_CONSENT_VERSION, 'lang': 'zz'})
171+
assert resp.status_code == 200, resp.get_data(as_text=True)
172+
assert resp.get_json()['ack_lang'] == 'en'
173+
174+
130175
# ===========================================================================
131176
# READ ROUTE — refuses until consent, serves after, cluster-access enforced
132177
# ===========================================================================

0 commit comments

Comments
 (0)