iobroker.eos-admin 7.10.4 → 7.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL_TEST_V7.10.6.md +8 -0
- package/MERGE_UPDATE_README_V7.10.6.md +7 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +19 -18
- package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +40 -41
- package/PUBLISH_STABLE_V7.10.6.md +8 -0
- package/README_STABILITY_V7.10.6.md +5 -0
- package/RELEASE_ACCEPTANCE_V7.10.6.md +11 -0
- package/RELEASE_NOTES_V7.10.6.md +14 -0
- package/SECURITY_ROLE_MATRIX_V7.10.6.md +12 -0
- package/adminWww/assets/Intro-DkwRiz1n-v84.js +2 -2
- package/adminWww/assets/Objects-DPan0bzw-v84.js +1 -1
- package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
- package/adminWww/css/nexowatt-native-shell.css +12 -1
- package/adminWww/index.html +14 -15
- package/adminWww/js/eos-role-bootstrap.js +5 -5
- package/adminWww/js/eos-role-ui.js +211 -17
- package/adminWww/js/nexowatt-native-shell.js +1 -1
- package/adminWww/static/js/{nexowatt-stable-v7101.js → nexowatt-stable-v7106.js} +1 -1
- package/build/lib/web.js +34 -0
- package/build/main.js +13 -15
- package/io-package.json +32 -5
- package/package.json +11 -10
- package/tools/nexowatt-account-password-flow-selftest.cjs +3 -3
- package/tools/nexowatt-clean-legacy-runtime.cjs +6 -0
- package/tools/nexowatt-internal-reserve-selftest.cjs +1 -1
- package/tools/nexowatt-login-layout-selftest.cjs +3 -3
- package/tools/nexowatt-merge-update-selftest.cjs +1 -1
- package/tools/nexowatt-modern-ui-selftest.cjs +2 -2
- package/tools/nexowatt-role-security-selftest.cjs +147 -41
- package/tools/nexowatt-runtime-cleanup-selftest.cjs +4 -0
- package/tools/nexowatt-stable-v7103-selftest.cjs +4 -4
- package/tools/nexowatt-ui-overview-runtime-selftest.cjs +2 -2
- package/tools/nexowatt-user-area-selftest.cjs +36 -0
- package/tools/nexowatt-validate-package.cjs +9 -8
- package/INSTALL_TEST_V7.10.4.md +0 -9
- package/MERGE_UPDATE_README_V7.10.4.md +0 -3
- package/PUBLISH_STABLE_V7.10.4.md +0 -10
- package/README_STABILITY_V7.10.4.md +0 -12
- package/RELEASE_ACCEPTANCE_V7.10.4.md +0 -3
- package/RELEASE_NOTES_V7.10.4.md +0 -11
- package/adminWww/nexowatt-role-security.js +0 -336
- package/build/lib/eosRoleSecurity.js +0 -310
- package/src-admin/README.md +0 -69
- package/src-admin/nexowatt-role-security.js +0 -336
|
@@ -1184,12 +1184,23 @@ html.eos-login .eos-login-card-modern {
|
|
|
1184
1184
|
/* Internal fallback services are never exposed outside Admin / Service. */
|
|
1185
1185
|
html.eos-role-installer [data-instance^="admin."],
|
|
1186
1186
|
html.eos-role-installer [data-instance^="backitup."],
|
|
1187
|
+
html.eos-role-installer [data-instance^="xterm."],
|
|
1187
1188
|
html.eos-role-enduser [data-instance^="admin."],
|
|
1188
1189
|
html.eos-role-enduser [data-instance^="backitup."],
|
|
1190
|
+
html.eos-role-enduser [data-instance^="xterm."],
|
|
1189
1191
|
html.eos-role-installer [data-instance-id^="admin."],
|
|
1190
1192
|
html.eos-role-installer [data-instance-id^="backitup."],
|
|
1193
|
+
html.eos-role-installer [data-instance-id^="xterm."],
|
|
1191
1194
|
html.eos-role-enduser [data-instance-id^="admin."],
|
|
1192
|
-
html.eos-role-enduser [data-instance-id^="backitup."]
|
|
1195
|
+
html.eos-role-enduser [data-instance-id^="backitup."],
|
|
1196
|
+
html.eos-role-enduser [data-instance-id^="xterm."] { display: none !important; }
|
|
1197
|
+
|
|
1198
|
+
html.eos-role-enduser #app-paper[data-eos-datapoints-readonly] [data-eos-object-writable="1"],
|
|
1199
|
+
html.eos-role-enduser #app-paper[data-eos-datapoints-readonly] [data-eos-enduser-write-blocked="1"] {
|
|
1200
|
+
pointer-events: none !important;
|
|
1201
|
+
cursor: default !important;
|
|
1202
|
+
opacity: 0.62 !important;
|
|
1203
|
+
}
|
|
1193
1204
|
|
|
1194
1205
|
|
|
1195
1206
|
|
package/adminWww/index.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
9
9
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
10
10
|
<meta http-equiv="Expires" content="0" />
|
|
11
|
-
<script src="./js/eos-manual-write-policy.js?v=
|
|
11
|
+
<script src="./js/eos-manual-write-policy.js?v=7106"></script>
|
|
12
12
|
<script type="module" src="./assets/hostInit-v84.js?v=84"></script>
|
|
13
13
|
|
|
14
14
|
<meta charset="utf-8" />
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
rel="stylesheet"
|
|
37
37
|
href="css/leaflet.css"
|
|
38
38
|
/>
|
|
39
|
-
<link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=
|
|
40
|
-
<link rel="stylesheet" href="./css/eos-ems-overview.css?v=
|
|
39
|
+
<link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=7106" />
|
|
40
|
+
<link rel="stylesheet" href="./css/eos-ems-overview.css?v=7106" />
|
|
41
41
|
<link rel="stylesheet" href="./css/eos-auto-update.css?v=7103" />
|
|
42
42
|
<link
|
|
43
43
|
rel="manifest"
|
|
@@ -160,23 +160,22 @@
|
|
|
160
160
|
</script>
|
|
161
161
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
|
|
162
162
|
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
|
|
163
|
-
<script defer src="./js/eos-role-bootstrap.js?v=
|
|
164
|
-
<script defer src="./js/eos-policy-client.js?v=
|
|
165
|
-
<script defer src="./js/eos-branding-sanitizer.js?v=
|
|
166
|
-
<script defer src="./js/nexowatt-native-shell.js?v=
|
|
167
|
-
<script defer src="./js/eos-native-security.js?v=
|
|
168
|
-
<script defer src="./js/eos-basic-settings.js?v=
|
|
169
|
-
<script defer src="./js/eos-role-ui.js?v=
|
|
170
|
-
<script defer src="./js/eos-ems-overview.js?v=
|
|
163
|
+
<script defer src="./js/eos-role-bootstrap.js?v=7106" data-eos-entry="./assets/index-CQZugZ1z-v84.js?v=84"></script>
|
|
164
|
+
<script defer src="./js/eos-policy-client.js?v=7106"></script>
|
|
165
|
+
<script defer src="./js/eos-branding-sanitizer.js?v=7106"></script>
|
|
166
|
+
<script defer src="./js/nexowatt-native-shell.js?v=7106"></script>
|
|
167
|
+
<script defer src="./js/eos-native-security.js?v=7106"></script>
|
|
168
|
+
<script defer src="./js/eos-basic-settings.js?v=7106"></script>
|
|
169
|
+
<script defer src="./js/eos-role-ui.js?v=7106"></script>
|
|
170
|
+
<script defer src="./js/eos-ems-overview.js?v=7106"></script>
|
|
171
171
|
|
|
172
172
|
<script defer src="./js/eos-auto-update.js?v=7103"></script>
|
|
173
|
-
<script defer src="./js/eos-assistant.js?v=
|
|
173
|
+
<script defer src="./js/eos-assistant.js?v=7106"></script>
|
|
174
174
|
</head>
|
|
175
175
|
<body>
|
|
176
176
|
<noscript>Für NexoWatt EOS muss JavaScript aktiviert sein.</noscript>
|
|
177
177
|
<div id="root"></div>
|
|
178
|
-
<script defer src="static/js/nexowatt-stable-login.js?v=
|
|
179
|
-
<script defer src="static/js/nexowatt-stable-
|
|
180
|
-
<script defer src="./nexowatt-role-security.js?v=7104"></script>
|
|
178
|
+
<script defer src="static/js/nexowatt-stable-login.js?v=7106"></script>
|
|
179
|
+
<script defer src="static/js/nexowatt-stable-v7106.js?v=7106"></script>
|
|
181
180
|
</body>
|
|
182
181
|
</html>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v7106-admin-authoritative-rbac';
|
|
5
5
|
const script = document.currentScript || document.querySelector('script[src*="eos-role-bootstrap.js"]');
|
|
6
6
|
const entry = script?.dataset?.eosEntry || '';
|
|
7
7
|
let launched = false;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const roleFromPolicy = policy => {
|
|
18
18
|
const raw = normalize(policy?.role || policy?.eosRole || policy?.accessRole || '');
|
|
19
|
-
if (policy?.isAdmin || policy?.isEosAdminGroup || policy?.isAdministrator || /^(?:admin|administrator)$/.test(raw)) return 'admin';
|
|
19
|
+
if (policy?.user === 'system.user.admin' || policy?.userId === 'system.user.admin' || policy?.isAdmin || policy?.isEosAdminGroup || policy?.isAdministrator || /^(?:admin|administrator)$/.test(raw)) return 'admin';
|
|
20
20
|
if (/nexowatt service|eos service|service admin|service administrator/.test(raw)) return 'admin';
|
|
21
21
|
if (policy?.isInstaller || /installateur|installer|installation|inbetriebnahme|techniker|technician|integrator|partner/.test(raw)) return 'installer';
|
|
22
22
|
return 'enduser';
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
const isCustomerBackupTab = tab => /^(?:tab-)?(?:nexowatt-backup|eos-backup|nexowatt-sicherung)(?:-|$)/.test(normalize(tab));
|
|
26
26
|
const isReleasedEndUserTab = tab => /^(?:tab-)?(?:nexowatt-ui|nexowatt-cockpit|eos-cockpit|eos-dashboard|kunden-cockpit|endkunden-cockpit|lovelace|jarvis|vis|iqontrol|material)(?:-|$)/.test(normalize(tab));
|
|
27
27
|
const isOfficialReserveTab = tab => /^(?:tab-)?(?:admin|backitup)(?:-\d+)?$/.test(normalize(tab));
|
|
28
|
-
const isInstallerDenied = tab => /(?:tab-hosts|tab-files|tab-xterm|tab-xtrem|tab-system|hosts|files|console|terminal|security)/.test(normalize(tab)) || isOfficialReserveTab(tab);
|
|
28
|
+
const isInstallerDenied = tab => /(?:tab-users|tab-hosts|tab-files|tab-xterm|tab-xtrem|tab-system|users|hosts|files|console|terminal|security)/.test(normalize(tab)) || isOfficialReserveTab(tab);
|
|
29
29
|
const isAllowed = (role, route) => route !== 'easy' && (role === 'admin'
|
|
30
30
|
|| (role === 'installer'
|
|
31
|
-
? route === 'tab-intro' ||
|
|
32
|
-
: route === 'tab-intro' || route === 'tab-enums' || isCustomerBackupTab(route) || isReleasedEndUserTab(route)));
|
|
31
|
+
? route === 'tab-intro' || isCustomerBackupTab(route) || !isInstallerDenied(route)
|
|
32
|
+
: route === 'tab-intro' || route === 'tab-enums' || route === 'tab-objects' || isCustomerBackupTab(route) || isReleasedEndUserTab(route)));
|
|
33
33
|
const defaultTab = () => 'tab-intro';
|
|
34
34
|
const currentRoute = () => {
|
|
35
35
|
const hash = decodeURIComponent(window.location.hash || '').toLowerCase();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v7106-admin-authoritative-rbac';
|
|
5
5
|
window.NEXOWATT_EOS_ROLE_UI_VERSION = VERSION;
|
|
6
6
|
|
|
7
7
|
const state = {
|
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
reserveObserver: null,
|
|
16
16
|
reservePaper: null,
|
|
17
17
|
reserveScheduled: false,
|
|
18
|
+
readonlySystemInfo: null,
|
|
19
|
+
readonlySystemInfoPromise: null,
|
|
18
20
|
};
|
|
19
21
|
const abort = new AbortController();
|
|
20
22
|
|
|
21
23
|
const safe = fn => { try { return fn(); } catch (_) { return undefined; } };
|
|
24
|
+
const escapeHtml = value => String(value ?? '').replace(/[&<>"']/g, match => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[match] || match));
|
|
22
25
|
const normalize = value => String(value || '')
|
|
23
26
|
.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ß/g, 'ss')
|
|
24
27
|
.replace(/[^a-z0-9#/_:.-]+/g, ' ').trim();
|
|
@@ -37,7 +40,7 @@
|
|
|
37
40
|
|
|
38
41
|
const roleFromPolicy = policy => {
|
|
39
42
|
const raw = normalize(policy?.role || policy?.eosRole || policy?.accessRole || '');
|
|
40
|
-
if (policy?.isAdmin || policy?.isEosAdminGroup || policy?.isAdministrator || /^(?:admin|administrator)$/.test(raw)) return 'admin';
|
|
43
|
+
if (policy?.user === 'system.user.admin' || policy?.userId === 'system.user.admin' || policy?.isAdmin || policy?.isEosAdminGroup || policy?.isAdministrator || /^(?:admin|administrator)$/.test(raw)) return 'admin';
|
|
41
44
|
if (/nexowatt service|eos service|service admin|service administrator/.test(raw)) return 'admin';
|
|
42
45
|
if (policy?.isInstaller || /installateur|installer|installation|inbetriebnahme|techniker|technician|integrator|partner/.test(raw)) return 'installer';
|
|
43
46
|
return 'enduser';
|
|
@@ -47,10 +50,10 @@
|
|
|
47
50
|
const isOfficialReserveTab = tab => isOfficialAdminTab(tab) || isOfficialBackupTab(tab);
|
|
48
51
|
const isCustomerBackupTab = tab => /^tab-(?:nexowatt-backup|eos-backup|nexowatt-sicherung)(?:-\d+)?$/.test(normalize(tab));
|
|
49
52
|
const endUserAllowedByText = value => /(eos cockpit|nexowatt ui|nexowatt cockpit|kunden cockpit|kundenbereich|endkundenbereich|visualisierung|visualisation|visu|dashboard|energie cockpit|energy cockpit|bedienung|smart home|smart-home|lovelace|jarvis|iqontrol|material)/.test(normalize(value));
|
|
50
|
-
const endUserDeniedByText = value => /(module|dienste|
|
|
53
|
+
const endUserDeniedByText = value => /(module|dienste|systemlogs|logs|zugange|rechte|benutzer|users|gruppen|groups|system hosts|system-hosts|hosts|dateien|files|konsole|console|terminal|xterm|adapter|instances?|instanzen|geraetesuche|discovery|systemschutz|security)/.test(normalize(value));
|
|
51
54
|
const isEndUserTab = (tab, label = '') => {
|
|
52
55
|
const clean = normalize(tab); const text = `${clean} ${normalize(label)}`;
|
|
53
|
-
if (clean === 'tab-intro' || clean === 'tab-enums' || isCustomerBackupTab(clean)) return true;
|
|
56
|
+
if (clean === 'tab-intro' || clean === 'tab-enums' || clean === 'tab-objects' || isCustomerBackupTab(clean)) return true;
|
|
54
57
|
if (/^tab-(nexowatt-ui|nexowatt-cockpit|eos-cockpit|eos-dashboard|kunden-cockpit|endkunden-cockpit|lovelace|jarvis|vis|iqontrol|material)(?:-|$)/.test(clean)) return true;
|
|
55
58
|
return !!clean && !endUserDeniedByText(text) && endUserAllowedByText(text);
|
|
56
59
|
};
|
|
@@ -58,14 +61,14 @@
|
|
|
58
61
|
const clean = normalize(tab); const text = `${clean} ${normalize(label)}`;
|
|
59
62
|
if (isOfficialReserveTab(clean)) return true;
|
|
60
63
|
if (isCustomerBackupTab(clean)) return false;
|
|
61
|
-
return /(?:tab-hosts|tab-files|tab-xterm|tab-xtrem|tab-system|system-hosts|system hosts|hosts|dateien|files|konsole|console|terminal|xterm|systemschutz|security)/.test(text);
|
|
64
|
+
return /(?:tab-users|tab-hosts|tab-files|tab-xterm|tab-xtrem|tab-system|users|benutzer|gruppen|groups|system-hosts|system hosts|hosts|dateien|files|konsole|console|terminal|xterm|systemschutz|security)/.test(text);
|
|
62
65
|
};
|
|
63
66
|
const isRouteAllowed = (role, route, label = '') => {
|
|
64
67
|
if (route === 'easy') return false;
|
|
65
68
|
if (role === 'admin') return true;
|
|
66
69
|
if (isOfficialReserveTab(route)) return false;
|
|
67
|
-
if (role === 'installer') return route === 'tab-intro' || route
|
|
68
|
-
if (role === 'enduser') return
|
|
70
|
+
if (role === 'installer') return route === 'tab-intro' || (route !== 'tab-users' && !isInstallerDenied(route, label));
|
|
71
|
+
if (role === 'enduser') return isEndUserTab(route, label);
|
|
69
72
|
return false;
|
|
70
73
|
};
|
|
71
74
|
const defaultTab = () => 'tab-intro';
|
|
@@ -133,7 +136,7 @@
|
|
|
133
136
|
return normalize(`${button?.getAttribute?.('aria-label') || ''} ${button?.getAttribute?.('title') || ''} ${testIds}`);
|
|
134
137
|
};
|
|
135
138
|
const isSystemSettingsButton = button => /buildicon|system settings|systemeinstellungen|systeeminstellingen/.test(toolbarButtonSignature(button));
|
|
136
|
-
const isExpertModeButton = button => /expert|experten/.test(toolbarButtonSignature(button));
|
|
139
|
+
const isExpertModeButton = button => button?.dataset?.eosExpertControl === '1' || /expert|experten/.test(toolbarButtonSignature(button));
|
|
137
140
|
const markAdminOnlyToolbarControls = () => safe(() => {
|
|
138
141
|
const toolbar = document.querySelector('.eos-top-toolbar,.MuiAppBar-root .MuiToolbar-root');
|
|
139
142
|
if (!toolbar) return;
|
|
@@ -226,7 +229,6 @@
|
|
|
226
229
|
['Datenpunkte', 'Messwerte prüfen und freigegebene Werte schreiben', 'tab-objects', 'datapoints'],
|
|
227
230
|
['Struktur', 'Räume und Funktionen für Smart Home zuordnen', 'tab-enums', 'structure'],
|
|
228
231
|
['Geräte', 'Geräteintegration und Inbetriebnahme öffnen', 'tab-devicemanager', 'devices'],
|
|
229
|
-
['Zugänge & Rechte', 'Passwörter im nativen Benutzereditor verwalten', 'tab-users', 'rights'],
|
|
230
232
|
['Basis-Einstellungen', 'Standort, Sprache und Anlagenparameter', 'basic-settings', 'settings'],
|
|
231
233
|
...(backupTab ? [['NexoWatt Sicherung', 'Sicherung und Wiederherstellung des EOS-Systems', backupTab, 'backup']] : []),
|
|
232
234
|
['EOS Cockpit', 'Energie-, Lade- und Gebäudesteuerung öffnen', uiTab, 'eos'],
|
|
@@ -234,6 +236,7 @@
|
|
|
234
236
|
}
|
|
235
237
|
return [
|
|
236
238
|
['EOS Cockpit', 'Energie, Laden und Gebäude bedienen', uiTab, 'eos'],
|
|
239
|
+
['Datenpunkte', 'Messwerte und Zustände ausschließlich lesen', 'tab-objects', 'datapoints'],
|
|
237
240
|
['Smart Home', 'Räume, Funktionen und freigegebene Geräte zuordnen', 'tab-enums', 'structure'],
|
|
238
241
|
...(backupTab ? [['NexoWatt Sicherung', 'Eigene Sicherungen ausführen und wiederherstellen', backupTab, 'backup']] : []),
|
|
239
242
|
];
|
|
@@ -268,6 +271,55 @@
|
|
|
268
271
|
role.setAttribute('data-nexowatt-overview-role', state.role);
|
|
269
272
|
}
|
|
270
273
|
};
|
|
274
|
+
|
|
275
|
+
const fetchReadonlySystemInfo = () => {
|
|
276
|
+
if (state.readonlySystemInfo) return Promise.resolve(state.readonlySystemInfo);
|
|
277
|
+
if (state.readonlySystemInfoPromise) return state.readonlySystemInfoPromise;
|
|
278
|
+
state.readonlySystemInfoPromise = fetch('/nexowatt/readonly/system-info', {
|
|
279
|
+
credentials: 'same-origin',
|
|
280
|
+
cache: 'no-store',
|
|
281
|
+
headers: { Accept: 'application/json' },
|
|
282
|
+
})
|
|
283
|
+
.then(response => (response.ok ? response.json() : null))
|
|
284
|
+
.then(data => {
|
|
285
|
+
state.readonlySystemInfo = data && typeof data === 'object' ? data : null;
|
|
286
|
+
return state.readonlySystemInfo;
|
|
287
|
+
})
|
|
288
|
+
.catch(() => null)
|
|
289
|
+
.finally(() => {
|
|
290
|
+
state.readonlySystemInfoPromise = null;
|
|
291
|
+
scheduleApply();
|
|
292
|
+
});
|
|
293
|
+
return state.readonlySystemInfoPromise;
|
|
294
|
+
};
|
|
295
|
+
const applyReadonlySystemInfoCard = () => safe(() => {
|
|
296
|
+
if (state.role !== 'enduser' || currentRoute() !== 'tab-intro') return;
|
|
297
|
+
const paper = appPaper();
|
|
298
|
+
if (!paper) return;
|
|
299
|
+
const title = Array.from(paper.querySelectorAll('h1,h2,h3,h4,h5,h6')).find(node => normalize(node.textContent || '') === 'nexowatt');
|
|
300
|
+
const card = title?.closest?.('.MuiCard-root');
|
|
301
|
+
if (!card || !/platform|ram|node\.js|npm/i.test(String(card.textContent || ''))) return;
|
|
302
|
+
Array.from(card.querySelectorAll('button')).forEach(button => {
|
|
303
|
+
if (/info/i.test(textOf(button))) {
|
|
304
|
+
button.classList.add('eos-role-hidden');
|
|
305
|
+
button.setAttribute('aria-hidden', 'true');
|
|
306
|
+
button.disabled = true;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
if (!state.readonlySystemInfo && !state.readonlySystemInfoPromise) {
|
|
310
|
+
void fetchReadonlySystemInfo();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const data = state.readonlySystemInfo;
|
|
314
|
+
const list = card.querySelector('ul');
|
|
315
|
+
if (!data || !list) return;
|
|
316
|
+
list.innerHTML = `
|
|
317
|
+
<li><span><span style="font-weight:600">Platform: </span>${escapeHtml(data.platform || '--')}</span></li>
|
|
318
|
+
<li><span><span style="font-weight:600">RAM: </span>${escapeHtml(Number.isFinite(Number(data.ramMb)) ? `${Math.round(Number(data.ramMb))} MB` : '--')}</span></li>
|
|
319
|
+
<li><span><span style="font-weight:600">Node.js: </span>${escapeHtml(data.nodejs || '--')}</span></li>
|
|
320
|
+
<li><span><span style="font-weight:600">NPM: </span>${escapeHtml(data.npm || '--')}</span></li>`;
|
|
321
|
+
});
|
|
322
|
+
|
|
271
323
|
const hideIntroEditControls = () => {
|
|
272
324
|
const paper = appPaper();
|
|
273
325
|
if (!paper) return;
|
|
@@ -320,7 +372,7 @@
|
|
|
320
372
|
'tr', '.MuiTableRow-root', '.MuiAccordion-root', '.MuiCard-root', '.MuiListItem-root',
|
|
321
373
|
'[role="row"]', '[data-instance-row]', '[data-testid="adapter-card"]', '[data-testid="instance-card"]',
|
|
322
374
|
].join(',');
|
|
323
|
-
const reserveId = /^(?:system\.adapter\.)?(?:admin|backitup)\.\d+$/i;
|
|
375
|
+
const reserveId = /^(?:system\.adapter\.)?(?:admin|backitup|xterm)\.\d+$/i;
|
|
324
376
|
const normalizeInstance = value => String(value || '').trim().replace(/^system\.adapter\./i, '');
|
|
325
377
|
const identityValues = node => {
|
|
326
378
|
if (!node) return [];
|
|
@@ -337,7 +389,7 @@
|
|
|
337
389
|
.replace(/nexowatt-backup\.\d+/gi, '')
|
|
338
390
|
.replace(/eos-backup\.\d+/gi, '')
|
|
339
391
|
.replace(/system\.adapter\.(?:eos-admin|nexowatt-backup|eos-backup)\.\d+/gi, '');
|
|
340
|
-
return /(?:^|[\s|,;])(?:system\.adapter\.)?(?:admin|backitup)\.\d+(?=$|[\s|,;])/i.test(text.trim());
|
|
392
|
+
return /(?:^|[\s|,;])(?:system\.adapter\.)?(?:admin|backitup|xterm)\.\d+(?=$|[\s|,;])/i.test(text.trim());
|
|
341
393
|
};
|
|
342
394
|
const rowIsReserve = row => identityValues(row).some(value => reserveId.test(value)) || rowTextContainsReserve(row);
|
|
343
395
|
const setHidden = (row, hidden) => {
|
|
@@ -363,12 +415,12 @@
|
|
|
363
415
|
|
|
364
416
|
paper.querySelectorAll(rowSelector).forEach(row => setHidden(row, rowIsReserve(row)));
|
|
365
417
|
paper.querySelectorAll([
|
|
366
|
-
'[data-id^="system.adapter.admin"]','[data-id^="system.adapter.backitup"]',
|
|
367
|
-
'[data-instance^="admin."]','[data-instance^="backitup."]',
|
|
368
|
-
'[data-instance-id^="admin."]','[data-instance-id^="backitup."]',
|
|
369
|
-
'[data-name="admin"]','[data-name="backitup"]',
|
|
370
|
-
'a[href*="#tab-admin"]','a[href*="#tab-backitup"]',
|
|
371
|
-
'a[href*="system.adapter.admin"]','a[href*="system.adapter.backitup"]',
|
|
418
|
+
'[data-id^="system.adapter.admin"]','[data-id^="system.adapter.backitup"]','[data-id^="system.adapter.xterm"]',
|
|
419
|
+
'[data-instance^="admin."]','[data-instance^="backitup."]','[data-instance^="xterm."]',
|
|
420
|
+
'[data-instance-id^="admin."]','[data-instance-id^="backitup."]','[data-instance-id^="xterm."]',
|
|
421
|
+
'[data-name="admin"]','[data-name="backitup"]','[data-name="xterm"]',
|
|
422
|
+
'a[href*="#tab-admin"]','a[href*="#tab-backitup"]','a[href*="#tab-xterm"]',
|
|
423
|
+
'a[href*="system.adapter.admin"]','a[href*="system.adapter.backitup"]','a[href*="system.adapter.xterm"]',
|
|
372
424
|
].join(',')).forEach(node => setHidden(rowFor(node) || node, true));
|
|
373
425
|
|
|
374
426
|
// Admin 7 table cells may have no metadata. Match only an exact visible instance ID and
|
|
@@ -429,6 +481,139 @@
|
|
|
429
481
|
}
|
|
430
482
|
});
|
|
431
483
|
|
|
484
|
+
|
|
485
|
+
const privilegedFrameObservers = new WeakMap();
|
|
486
|
+
const privilegedActionPattern = /^(?:ems apps?(?: offnen| öffnen)?|app center|app-center|appcenter|simulation|simulator|lizenz|license)$/;
|
|
487
|
+
const isPrivilegedAction = element => {
|
|
488
|
+
const signature = normalize(textOf(element));
|
|
489
|
+
const href = normalize(element?.getAttribute?.('href') || element?.href || element?.getAttribute?.('data-href') || '');
|
|
490
|
+
return privilegedActionPattern.test(signature)
|
|
491
|
+
|| /(?:^|[\/#?&_.-])(?:appcenter|app-center|simulation|simulator|license|lizenz)(?:$|[\/#?&=_.-])/.test(href);
|
|
492
|
+
};
|
|
493
|
+
const setPrivilegedActionHidden = (element, hidden) => {
|
|
494
|
+
if (!element) return;
|
|
495
|
+
const target = element.closest?.('a,button,[role="button"],[role="tab"],.MuiButtonBase-root') || element;
|
|
496
|
+
if (hidden) {
|
|
497
|
+
target.dataset.eosPrivilegedActionHidden = '1';
|
|
498
|
+
target.classList.add('eos-role-hidden');
|
|
499
|
+
target.setAttribute('aria-hidden', 'true');
|
|
500
|
+
if ('disabled' in target) target.disabled = true;
|
|
501
|
+
} else if (target.dataset.eosPrivilegedActionHidden === '1') {
|
|
502
|
+
delete target.dataset.eosPrivilegedActionHidden;
|
|
503
|
+
target.classList.remove('eos-role-hidden');
|
|
504
|
+
target.removeAttribute('aria-hidden');
|
|
505
|
+
if ('disabled' in target) target.disabled = false;
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
const applyPrivilegedActionsInDocument = doc => safe(() => {
|
|
509
|
+
if (!doc?.documentElement) return;
|
|
510
|
+
const admin = state.role === 'admin';
|
|
511
|
+
doc.querySelectorAll('[data-eos-privileged-action-hidden="1"]').forEach(element => {
|
|
512
|
+
if (admin || !isPrivilegedAction(element)) setPrivilegedActionHidden(element, false);
|
|
513
|
+
});
|
|
514
|
+
for (const element of doc.querySelectorAll('a,button,[role="button"],[role="tab"],.MuiButtonBase-root')) {
|
|
515
|
+
if (isPrivilegedAction(element)) setPrivilegedActionHidden(element, !admin);
|
|
516
|
+
}
|
|
517
|
+
if (!doc.documentElement.dataset.eosPrivilegedClickGuard) {
|
|
518
|
+
doc.documentElement.dataset.eosPrivilegedClickGuard = '1';
|
|
519
|
+
doc.addEventListener('click', event => {
|
|
520
|
+
if (state.role === 'admin' || state.role === 'unknown') return;
|
|
521
|
+
const target = event.target?.closest?.('a,button,[role="button"],[role="tab"],.MuiButtonBase-root');
|
|
522
|
+
if (!target || !isPrivilegedAction(target)) return;
|
|
523
|
+
event.preventDefault();
|
|
524
|
+
event.stopImmediatePropagation();
|
|
525
|
+
}, true);
|
|
526
|
+
}
|
|
527
|
+
if (!privilegedFrameObservers.has(doc)) {
|
|
528
|
+
const observer = new MutationObserver(() => {
|
|
529
|
+
window.setTimeout(() => applyPrivilegedActionsInDocument(doc), 0);
|
|
530
|
+
});
|
|
531
|
+
observer.observe(doc.documentElement, { childList: true, subtree: true });
|
|
532
|
+
privilegedFrameObservers.set(doc, observer);
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
const applyPrivilegedIframePolicy = () => safe(() => {
|
|
536
|
+
applyPrivilegedActionsInDocument(document);
|
|
537
|
+
document.querySelectorAll('iframe').forEach(frame => {
|
|
538
|
+
const applyFrame = () => safe(() => applyPrivilegedActionsInDocument(frame.contentDocument));
|
|
539
|
+
if (frame.dataset.eosRoleFrameGuard !== '1') {
|
|
540
|
+
frame.dataset.eosRoleFrameGuard = '1';
|
|
541
|
+
frame.addEventListener('load', applyFrame);
|
|
542
|
+
}
|
|
543
|
+
applyFrame();
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
const scrubInheritedUiAdminSession = () => safe(() => {
|
|
547
|
+
if (state.role === 'admin' || state.role === 'unknown') return;
|
|
548
|
+
const user = String(state.policy?.user || state.policy?.userId || '').replace(/^system\.user\./, '') || state.role;
|
|
549
|
+
const key = `eosUiAdminSessionScrubbed:v7106:${user}:${state.role}`;
|
|
550
|
+
if (sessionStorage.getItem(key) === '1') return;
|
|
551
|
+
sessionStorage.setItem(key, '1');
|
|
552
|
+
const frame = document.createElement('iframe');
|
|
553
|
+
frame.hidden = true;
|
|
554
|
+
frame.tabIndex = -1;
|
|
555
|
+
frame.setAttribute('aria-hidden', 'true');
|
|
556
|
+
frame.src = `${window.location.protocol}//${window.location.hostname}:8188/logout?source=eos-admin&role=${encodeURIComponent(state.role)}&t=${Date.now()}`;
|
|
557
|
+
frame.addEventListener('load', () => window.setTimeout(() => frame.remove(), 1200), { once: true });
|
|
558
|
+
document.body?.appendChild(frame);
|
|
559
|
+
window.setTimeout(() => frame.remove(), 7000);
|
|
560
|
+
});
|
|
561
|
+
const applyEndUserDatapointPolicy = () => safe(() => {
|
|
562
|
+
const paper = appPaper();
|
|
563
|
+
if (!paper) return;
|
|
564
|
+
const readonly = state.role === 'enduser' && currentRoute() === 'tab-objects';
|
|
565
|
+
paper.toggleAttribute('data-eos-datapoints-readonly', readonly);
|
|
566
|
+
const clearControl = control => {
|
|
567
|
+
if (!control) return;
|
|
568
|
+
control.removeAttribute('aria-disabled');
|
|
569
|
+
control.removeAttribute('data-eos-enduser-write-blocked');
|
|
570
|
+
control.removeAttribute('data-eos-enduser-action-hidden');
|
|
571
|
+
control.classList.remove('eos-role-hidden');
|
|
572
|
+
control.removeAttribute('aria-hidden');
|
|
573
|
+
if ('disabled' in control) control.disabled = false;
|
|
574
|
+
};
|
|
575
|
+
paper.querySelectorAll('[data-eos-enduser-write-blocked="1"],[data-eos-enduser-action-hidden="1"]').forEach(clearControl);
|
|
576
|
+
if (!readonly) return;
|
|
577
|
+
const blockControl = control => {
|
|
578
|
+
if (!control) return;
|
|
579
|
+
control.setAttribute('aria-disabled', 'true');
|
|
580
|
+
control.setAttribute('data-eos-enduser-write-blocked', '1');
|
|
581
|
+
};
|
|
582
|
+
const hideControl = control => {
|
|
583
|
+
if (!control) return;
|
|
584
|
+
control.setAttribute('aria-disabled', 'true');
|
|
585
|
+
control.setAttribute('data-eos-enduser-action-hidden', '1');
|
|
586
|
+
control.classList.add('eos-role-hidden');
|
|
587
|
+
control.setAttribute('aria-hidden', 'true');
|
|
588
|
+
if ('disabled' in control) control.disabled = true;
|
|
589
|
+
};
|
|
590
|
+
paper.querySelectorAll('[data-eos-object-writable="1"]').forEach(blockControl);
|
|
591
|
+
paper.querySelectorAll([
|
|
592
|
+
'button[title*="edit" i]','button[title*="delete" i]','button[title*="remove" i]','button[title*="setting" i]','button[title*="einstell" i]',
|
|
593
|
+
'button[aria-label*="edit" i]','button[aria-label*="delete" i]','button[aria-label*="remove" i]','button[aria-label*="setting" i]','button[aria-label*="einstell" i]',
|
|
594
|
+
'[role="button"][title*="edit" i]','[role="button"][title*="delete" i]','[role="button"][title*="setting" i]',
|
|
595
|
+
'[role="button"][aria-label*="edit" i]','[role="button"][aria-label*="delete" i]','[role="button"][aria-label*="setting" i]'
|
|
596
|
+
].join(',')).forEach(hideControl);
|
|
597
|
+
paper.querySelectorAll('svg[data-testid*="Edit"],svg[data-testid*="Delete"],svg[data-testid*="Settings"],svg[data-testid*="Tune"],svg[data-testid*="Build"]').forEach(icon => hideControl(icon.closest('button,[role="button"]')));
|
|
598
|
+
});
|
|
599
|
+
const clearLegacyHeuristicRestrictionsForAdmin = () => safe(() => {
|
|
600
|
+
if (state.role !== 'admin') return;
|
|
601
|
+
document.querySelectorAll('[data-eos-role-hidden]').forEach(element => {
|
|
602
|
+
element.removeAttribute('data-eos-role-hidden');
|
|
603
|
+
element.removeAttribute('aria-hidden');
|
|
604
|
+
element.style.removeProperty('display');
|
|
605
|
+
});
|
|
606
|
+
document.querySelectorAll('[data-eos-readonly],[data-eos-enduser-write-blocked],[data-eos-enduser-action-hidden]').forEach(element => {
|
|
607
|
+
element.removeAttribute('data-eos-readonly');
|
|
608
|
+
element.removeAttribute('data-eos-enduser-write-blocked');
|
|
609
|
+
element.removeAttribute('data-eos-enduser-action-hidden');
|
|
610
|
+
element.removeAttribute('aria-disabled');
|
|
611
|
+
element.style.removeProperty('pointer-events');
|
|
612
|
+
element.style.removeProperty('opacity');
|
|
613
|
+
if ('disabled' in element) element.disabled = false;
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
|
|
432
617
|
const apply = () => {
|
|
433
618
|
if (!state.policy || state.role === 'unknown') return;
|
|
434
619
|
setRoleClasses(state.role);
|
|
@@ -436,10 +621,15 @@
|
|
|
436
621
|
applyMenuPolicy();
|
|
437
622
|
markAdminOnlyToolbarControls();
|
|
438
623
|
applySensitiveDialogPolicy();
|
|
624
|
+
clearLegacyHeuristicRestrictionsForAdmin();
|
|
439
625
|
redirectIfNeeded();
|
|
440
626
|
ensureSafeOverview();
|
|
627
|
+
applyReadonlySystemInfoCard();
|
|
441
628
|
filterNativeUsersPage();
|
|
442
629
|
hideOfficialReserveSurfaces();
|
|
630
|
+
applyEndUserDatapointPolicy();
|
|
631
|
+
applyPrivilegedIframePolicy();
|
|
632
|
+
scrubInheritedUiAdminSession();
|
|
443
633
|
ensureReserveObserver();
|
|
444
634
|
suppressPermissionErrors();
|
|
445
635
|
window.NEXOWATT_EOS_ACCOUNT_MANAGEMENT?.refreshEntry?.();
|
|
@@ -491,6 +681,10 @@
|
|
|
491
681
|
if (state.role === 'installer') window.NEXOWATT_EOS_BASIC_SETTINGS?.open?.();
|
|
492
682
|
return;
|
|
493
683
|
}
|
|
684
|
+
if (state.role === 'enduser' && currentRoute() === 'tab-objects') {
|
|
685
|
+
const writeTarget = event.target?.closest?.('[data-eos-object-writable="1"],[data-eos-enduser-write-blocked="1"],button[title*="edit" i],button[title*="delete" i],button[aria-label*="edit" i],button[aria-label*="delete" i]');
|
|
686
|
+
if (writeTarget) { event.preventDefault(); event.stopImmediatePropagation(); return; }
|
|
687
|
+
}
|
|
494
688
|
const target = event.target?.closest?.('[data-eos-admin-only-control="1"],[data-eos-system-settings-control="1"],[data-eos-role-tab],a[href*="#tab-"],a[href*="#/tab-"]');
|
|
495
689
|
if (!target) return;
|
|
496
690
|
if (target.matches('[data-eos-admin-only-control="1"]')) { event.preventDefault(); event.stopImmediatePropagation(); return; }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v7106-nexowatt-native-shell-native-overview';
|
|
5
5
|
const previous = window.NEXOWATT_NATIVE_SHELL;
|
|
6
6
|
if (previous?.version === VERSION) return;
|
|
7
7
|
previous?.destroy?.();
|
package/build/lib/web.js
CHANGED
|
@@ -8,6 +8,7 @@ const node_util_1 = require("node:util");
|
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const node_os_1 = require("node:os");
|
|
10
10
|
const node_stream_1 = require("node:stream");
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
12
|
const compression = require("compression");
|
|
12
13
|
const mime_1 = require("mime");
|
|
13
14
|
const node_zlib_1 = require("node:zlib");
|
|
@@ -1371,6 +1372,35 @@ class Web {
|
|
|
1371
1372
|
this.adapter.log.info(`EOS installer basic settings updated by ${access.userId || 'unknown user'}`);
|
|
1372
1373
|
await this.sendEosBasicSettings(req, res);
|
|
1373
1374
|
}
|
|
1375
|
+
|
|
1376
|
+
getEosReadonlySystemInfo() {
|
|
1377
|
+
let npm = '--';
|
|
1378
|
+
try {
|
|
1379
|
+
const binary = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
1380
|
+
npm = (0, node_child_process_1.execFileSync)(binary, ['-v'], { encoding: 'utf8', timeout: 1500 }).trim() || '--';
|
|
1381
|
+
}
|
|
1382
|
+
catch {
|
|
1383
|
+
// ignore
|
|
1384
|
+
}
|
|
1385
|
+
return {
|
|
1386
|
+
platform: process.platform || '--',
|
|
1387
|
+
ramMb: Math.round((0, node_os_1.totalmem)() / 1024 / 1024),
|
|
1388
|
+
nodejs: process.version || '--',
|
|
1389
|
+
npm,
|
|
1390
|
+
active: true,
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
async sendEosReadonlySystemInfo(req, res) {
|
|
1394
|
+
const access = await this.getEosRequestAccess(req);
|
|
1395
|
+
const authenticated = !this.settings.auth || !!access.userId;
|
|
1396
|
+
if (!authenticated) {
|
|
1397
|
+
res.status(401).json({ error: 'authenticationRequired' });
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
1401
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
1402
|
+
res.status(200).json(this.getEosReadonlySystemInfo());
|
|
1403
|
+
}
|
|
1374
1404
|
async sendEosSecuritySession(req, res) {
|
|
1375
1405
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
1376
1406
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
@@ -1806,6 +1836,10 @@ class Web {
|
|
|
1806
1836
|
this.server.app.get('/eos/security/status', sendSecuritySession);
|
|
1807
1837
|
this.server.app.get('/nexowatt/security/session', sendSecuritySession);
|
|
1808
1838
|
this.server.app.get('/nexowatt/security/context', sendSecuritySession);
|
|
1839
|
+
this.server.app.get('/nexowatt/readonly/system-info', (req, res) => { void this.sendEosReadonlySystemInfo(req, res).catch(e => {
|
|
1840
|
+
this.adapter.log.warn(`Cannot read EOS readonly system info: ${e instanceof Error ? e.message : e}`);
|
|
1841
|
+
res.status(500).json({ error: 'readonlySystemInfoFailed' });
|
|
1842
|
+
}); });
|
|
1809
1843
|
this.server.app.get('/nexowatt/role-settings/basic', (req, res) => {
|
|
1810
1844
|
void this.sendEosBasicSettings(req, res).catch(e => {
|
|
1811
1845
|
this.adapter.log.warn(`Cannot read EOS basic settings: ${e instanceof Error ? e.message : e}`);
|
package/build/main.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const eosRoleSecurity = require("./lib/eosRoleSecurity");
|
|
4
|
-
eosRoleSecurity.installHttpGuard();
|
|
5
3
|
/**
|
|
6
4
|
* Admin backend
|
|
7
5
|
*
|
|
@@ -332,8 +330,6 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
332
330
|
* Is called when databases are connected and adapter received configuration.
|
|
333
331
|
*/
|
|
334
332
|
onReady = async () => {
|
|
335
|
-
eosRoleSecurity.attachAdapter(this);
|
|
336
|
-
await eosRoleSecurity.enforceRoleAcls(this);
|
|
337
333
|
const systemConfig = await this.getForeignObjectAsync('system.config');
|
|
338
334
|
if (systemConfig) {
|
|
339
335
|
systemConfig.native = systemConfig.native || {};
|
|
@@ -1272,14 +1268,9 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
1272
1268
|
return deny(socketClient, command, callback, 'technical diagnostics are installer/service-only');
|
|
1273
1269
|
}
|
|
1274
1270
|
if (command === 'changePassword') {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
const allowed = role === 'installer'
|
|
1279
|
-
? new Set([selfUser, 'system.user.installer', 'system.user.user', 'system.user.guest']).has(targetUser)
|
|
1280
|
-
: targetUser === selfUser;
|
|
1281
|
-
if (!allowed) return deny(socketClient, command, callback, 'password target outside EOS role scope');
|
|
1282
|
-
return true;
|
|
1271
|
+
// Initial password activation has a separate, tightly scoped backend endpoint.
|
|
1272
|
+
// The normal Admin socket password editor remains Service/Admin-only.
|
|
1273
|
+
return deny(socketClient, command, callback, 'password administration is Service-only');
|
|
1283
1274
|
}
|
|
1284
1275
|
if (['addUser', 'delUser', 'addGroup', 'delGroup'].includes(command)) {
|
|
1285
1276
|
return deny(socketClient, command, callback, 'account administration is Service-only');
|
|
@@ -1300,9 +1291,16 @@ class Admin extends adapter_core_1.Adapter {
|
|
|
1300
1291
|
return deny(socketClient, command, callback, 'end users may only maintain Smart Home assignments');
|
|
1301
1292
|
}
|
|
1302
1293
|
}
|
|
1303
|
-
if (['setState', 'delState', 'createState'].includes(command)
|
|
1304
|
-
|
|
1305
|
-
|
|
1294
|
+
if (['setState', 'delState', 'createState'].includes(command)) {
|
|
1295
|
+
if (role === 'enduser') {
|
|
1296
|
+
return deny(socketClient, command, callback, 'end-user datapoints are read-only in EOS Admin');
|
|
1297
|
+
}
|
|
1298
|
+
if (objectId === `${this.namespace}.info.uiTabsVisible`) {
|
|
1299
|
+
return deny(socketClient, command, callback, 'navigation layout is Service-only');
|
|
1300
|
+
}
|
|
1301
|
+
if (/^(?:admin|backitup|xterm)\.\d+(?:\.|$)/.test(objectId)) {
|
|
1302
|
+
return deny(socketClient, command, callback, 'internal Service reserve is Admin/Service-only');
|
|
1303
|
+
}
|
|
1306
1304
|
}
|
|
1307
1305
|
return original(socketClient, command, callback, ...args);
|
|
1308
1306
|
};
|