iobroker.mywebui 1.37.28 → 1.37.29

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/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "mywebui",
4
- "version": "1.37.28",
4
+ "version": "1.37.29",
5
5
  "titleLang": {
6
6
  "en": "mywebui",
7
7
  "de": "mywebui",
@@ -29,6 +29,13 @@
29
29
  "zh-cn": "使用万维网传送器的高锰用户接口"
30
30
  },
31
31
  "news": {
32
+ "1.37.29": {
33
+ "en": "security fix: lang sync via session-verified HTTP endpoint instead of sendTo — username no longer trusted from frontend",
34
+ "az": "təhlükəsizlik düzəlişi: dil sinxronizasiyası session-yoxlamalı HTTP endpoint ilə — username artıq frontend-dən alınmır",
35
+ "tr": "güvenlik düzeltmesi: dil senkronizasyonu session doğrulamalı HTTP endpoint ile — username artık frontend'den gelmiyor",
36
+ "ru": "исправление безопасности: синхронизация языка через HTTP endpoint с проверкой сессии — username больше не берётся из frontend",
37
+ "de": "Sicherheitsfix: Sprachsync über session-verifizierten HTTP-Endpoint — username kommt nicht mehr vom Frontend"
38
+ },
32
39
  "1.37.28": {
33
40
  "en": "fix: rename webExtension.js to .cjs to avoid ES module conflict",
34
41
  "az": "düzəliş: ES module konflikti aradan qaldırmaq üçün webExtension.js → .cjs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.mywebui",
3
- "version": "1.37.28",
3
+ "version": "1.37.29",
4
4
  "description": "ioBroker mywebui - Custom edited mywebui by gokturk413",
5
5
  "type": "module",
6
6
  "main": "dist/backend/main.js",
package/webExtension.cjs CHANGED
@@ -1,5 +1,35 @@
1
1
  'use strict';
2
2
 
3
+ const https = require('https');
4
+ const http = require('http');
5
+
6
+ function mapGrafanaLanguage(lang) {
7
+ switch (lang) {
8
+ case 'az': return 'az-AZ';
9
+ case 'ru': return 'ru-RU';
10
+ case 'en': return 'en-US';
11
+ case 'de': return 'de-DE';
12
+ case 'tr': return 'tr-TR';
13
+ default: return 'en-US';
14
+ }
15
+ }
16
+
17
+ function httpPatch(urlStr, headers, body) {
18
+ return new Promise((resolve, reject) => {
19
+ const parsed = new URL(urlStr);
20
+ const lib = parsed.protocol === 'https:' ? https : http;
21
+ const req = lib.request(parsed, { method: 'PATCH', headers }, (res) => {
22
+ let data = '';
23
+ res.on('data', c => data += c);
24
+ res.on('end', () => resolve({ status: res.statusCode, body: data }));
25
+ });
26
+ req.on('error', reject);
27
+ req.setTimeout(3000, () => { req.destroy(); reject(new Error('timeout')); });
28
+ req.write(body);
29
+ req.end();
30
+ });
31
+ }
32
+
3
33
  class MywebuiWebExtension {
4
34
  /**
5
35
  * @param {import('http').Server} server
@@ -10,25 +40,66 @@ class MywebuiWebExtension {
10
40
  */
11
41
  constructor(server, settings, adapter, config, app) {
12
42
  this._adapter = adapter;
43
+ const native = config?.native || {};
13
44
 
14
- const ssoEnabled = config?.native?.grafanaEnabled && config?.native?.grafanaSsoEnabled;
15
-
16
- if (!ssoEnabled) {
17
- adapter.log.debug('[mywebui/WebExt] SSO disabled — /mywebui-auth-check not registered');
45
+ if (!native.grafanaEnabled) {
46
+ adapter.log.debug('[mywebui/WebExt] Grafana disabled — no endpoints registered');
18
47
  return;
19
48
  }
20
49
 
21
- app.get('/mywebui-auth-check', (req, res) => {
22
- const username = req.session?.passport?.user;
23
- if (!username) {
24
- res.status(401).end();
25
- return;
26
- }
27
- res.setHeader('X-User', username);
28
- res.status(200).end();
29
- });
50
+ const grafanaUrl = (native.grafanaUrl || 'http://localhost:3000').replace(/\/$/, '');
51
+
52
+ // ── SSO auth check (nginx auth_request) ──────────────────────────────
53
+ if (native.grafanaSsoEnabled) {
54
+ app.get('/mywebui-auth-check', (req, res) => {
55
+ const username = req.session?.passport?.user;
56
+ if (!username) { res.status(401).end(); return; }
57
+ res.setHeader('X-User', username);
58
+ res.status(200).end();
59
+ });
60
+ adapter.log.info('[mywebui/WebExt] /mywebui-auth-check registered for nginx SSO');
61
+ }
62
+
63
+ // ── Language sync (session-verified, no username from frontend) ──────
64
+ if (native.grafanaLangSyncEnabled) {
65
+ app.post('/mywebui-grafana-lang', async (req, res) => {
66
+ const username = req.session?.passport?.user;
67
+ if (!username) { res.status(401).json({ error: 'not authenticated' }); return; }
30
68
 
31
- adapter.log.info('[mywebui/WebExt] /mywebui-auth-check registered for nginx SSO');
69
+ let body = '';
70
+ req.on('data', c => body += c);
71
+ await new Promise(r => req.on('end', r));
72
+
73
+ let lang;
74
+ try { lang = JSON.parse(body).lang; } catch (e) { /**/ }
75
+ if (!lang) { res.status(400).json({ error: 'missing lang' }); return; }
76
+
77
+ const grafanaLang = mapGrafanaLanguage(lang);
78
+ const payload = JSON.stringify({ language: grafanaLang });
79
+ try {
80
+ const r = await httpPatch(
81
+ grafanaUrl + '/api/user/preferences',
82
+ {
83
+ 'Content-Type': 'application/json',
84
+ 'Content-Length': Buffer.byteLength(payload),
85
+ 'X-WEBAUTH-USER': username
86
+ },
87
+ payload
88
+ );
89
+ if (r.status === 200) {
90
+ adapter.log.debug(`[mywebui/WebExt] lang synced: ${username} → ${grafanaLang}`);
91
+ res.json({ success: true });
92
+ } else {
93
+ adapter.log.warn(`[mywebui/WebExt] Grafana ${r.status}: ${r.body}`);
94
+ res.status(502).json({ error: `Grafana HTTP ${r.status}` });
95
+ }
96
+ } catch (e) {
97
+ adapter.log.warn(`[mywebui/WebExt] lang sync error: ${e.message}`);
98
+ res.status(500).json({ error: e.message });
99
+ }
100
+ });
101
+ adapter.log.info('[mywebui/WebExt] /mywebui-grafana-lang registered (session-verified)');
102
+ }
32
103
  }
33
104
  }
34
105
 
@@ -497,9 +497,12 @@ export class IobrokerHandler {
497
497
  localStorage.setItem('webui-language', lang);
498
498
  this.languageChanged.emit(lang);
499
499
  window.dispatchEvent(new CustomEvent('languageChanged', { detail: lang }));
500
- if (this.userName && this.connection) {
501
- this.connection.sendTo('mywebui.0', 'grafanaLangSync', { username: this.userName, lang }).catch(() => {});
502
- }
500
+ fetch('/mywebui-grafana-lang', {
501
+ method: 'POST',
502
+ headers: { 'Content-Type': 'application/json' },
503
+ credentials: 'include',
504
+ body: JSON.stringify({ lang })
505
+ }).catch(() => {});
503
506
  }
504
507
  /** Translation helper - hər yerdən istifadə üçün
505
508
  * iobrokerHandler.t('pid.op')