import { sitemapAllowedForClient } from './security.js';
+ import { sitemapAllowedForClient, widgetsFilterForClient } from './security.js';
import { requireHeader } from './../middleware.js';
import { backendInfo } from '../../server.js';
-import { getAllSitemaps, getSitemap } from './backend.js';
+import { getAllSitemaps, getSitemap, getSitemapPage } from './backend.js';
import proxy from 'express-http-proxy';
const sitemapAccess = () => {
@@ -58,7 +58,7 @@ components/sitemaps/routes.js
const org = req.headers['x-openhab-org'] || '';
const user = req.headers['x-openhab-user'];
try {
- const allowed = await sitemapAllowedForClient(user, org, req.params.sitemapname);
+ const allowed = await sitemapAllowedForClient(backendInfo.HOST, req, user, org, req.params.sitemapname);
if (allowed === true) {
next();
} else {
@@ -112,14 +112,17 @@ components/sitemaps/routes.js
* 403:
* description: Forbidden
*/
- app.get('/auth/sitemaps', requireHeader('X-OPENHAB-USER'), requireHeader('X-ORIGINAL-URI'), (req, res, next) => {
+ app.get('/auth/sitemaps', requireHeader('X-OPENHAB-USER'), requireHeader('X-ORIGINAL-URI'), async (req, res) => {
const org = req.headers['x-openhab-org'] || '';
const user = req.headers['x-openhab-user'];
- const regex = /(\?|&)sitemap=([a-zA-Z_0-9]+)[&]?/;
- const sitemapname = regex.exec(req.headers['x-original-uri']);
+ const regex1 = /(\?|&)sitemap=([a-zA-Z_0-9]+)[&]?/;
+ const regex2 = /\/sitemaps\/([a-zA-Z_0-9]+)/;
+ const sitemapname1 = regex1.exec(req.headers['x-original-uri']);
+ const sitemapname2 = regex2.exec(req.headers['x-original-uri']);
+ const sitemapname = (sitemapname1 == null) ? sitemapname2 : sitemapname1;
if (sitemapname == null) return res.status(403).send();
try {
- const allowed = sitemapAllowedForClient(user, org, sitemapname[2]);
+ const allowed = await sitemapAllowedForClient(backendInfo.HOST, req, user, org, sitemapname[2]);
if (allowed === true) {
res.status(200).send();
} else {
@@ -168,9 +171,9 @@ components/sitemaps/routes.js
const allSitemaps = await getAllSitemaps(backendInfo.HOST, req);
let filteredSitemaps = [];
for (const i in allSitemaps) {
- if (await sitemapAllowedForClient(user, org, allSitemaps[i].name) === true) {
- filteredSitemaps.push(allSitemaps[i]);
- }
+ if (await sitemapAllowedForClient(backendInfo.HOST, req, user, org, allSitemaps[i].name) === true) {
+ filteredSitemaps.push(allSitemaps[i]);
+ }
}
res.status(200).send(filteredSitemaps);
} catch (e) {
@@ -220,8 +223,17 @@ components/sitemaps/routes.js
* description: Sitemap not found
*/
app.get('/rest/sitemaps/:sitemapname', requireHeader('X-OPENHAB-USER'), sitemapAccess(), async (req, res) => {
+ const org = req.headers['x-openhab-org'] || '';
+ const user = req.headers['x-openhab-user'];
try {
const response = await getSitemap(backendInfo.HOST, req, req.params.sitemapname);
+ const tempWidget = response.json.homepage.widgets;
+ //recursive filtering of child widgets
+ if (Array.isArray(tempWidget)) {
+ response.json.homepage.widgets = [];
+ const tempChWidgets = await widgetsFilterForClient(backendInfo.HOST, req, user, org, tempWidget);
+ response.json.homepage.widgets = tempChWidgets;
+ }
res.status(response.status).send(response.json);
} catch (e) {
console.info(e);
@@ -250,6 +262,13 @@ components/sitemaps/routes.js
* schema:
* type: string
* style: form
+ * - in: query
+ * name: parameters
+ * required: false
+ * description: Query parameters from API (subscriptionid)
+ * schema:
+ * type: string
+ * style: form
* - in: header
* name: X-OPENHAB-USER
* required: true
@@ -271,13 +290,34 @@ components/sitemaps/routes.js
* application/json:
* schema:
* type: object
+ * 403:
+ * description: Sitemap access forbidden
+ * 404:
+ * description: Sitemap not found
*/
- //app.get('/rest/sitemaps/:sitemapname/:pageid', requireHeader('X-OPENHAB-USER'), sitemapAccess(), proxy(backendInfo.HOST + '/rest/sitemaps/'));
-
+ app.get('/rest/sitemaps/:sitemapname/:pageid', requireHeader('X-OPENHAB-USER'), sitemapAccess(), async (req, res) => {
+ const org = req.headers['x-openhab-org'] || '';
+ const user = req.headers['x-openhab-user'];
+ try {
+ const response = await getSitemapPage(backendInfo.HOST, req, req.params.sitemapname, req.params.pageid);
+ const tempWidget = response.json.widgets;
+ //recursive filtering of child widgets
+ if (Array.isArray(tempWidget)) {
+ response.json.widgets = [];
+ const tempChWidgets = await widgetsFilterForClient(backendInfo.HOST, req, user, org, tempWidget);
+ response.json.widgets = tempChWidgets;
+ }
+ res.status(response.status).send(response.json);
+ } catch (e) {
+ console.info(e);
+ res.status(500).send();
+ }
+ });
+
/**
* @swagger
* /rest/sitemaps/events/{subscriptionid}:
- * post:
+ * get:
* summary: Get Sitemap events. Requires nginx.
* tags: [Sitemaps]
* parameters:
@@ -323,6 +363,33 @@ components/sitemaps/routes.js
* 503:
* description: Subscriptions limit reached.
*/
+
+ /**
+ * @swagger
+ * /basicui/:
+ * get:
+ * summary: Gets BasicUI. Requires nginx.
+ * tags: [Sitemaps]
+ * parameters:
+ * - in: path
+ * name: app
+ * required: true
+ * description: basic UI app and other components
+ * schema:
+ * type: string
+ * style: form
+ * - in: query
+ * name: parameters
+ * required: false
+ * description: Query parameters (e.g. sitemap, w)
+ * schema:
+ * type: string
+ * style: form
+ * responses:
+ * 200:
+ * description: OK
+ */
+
};
export default sitemaps;
@@ -340,7 +407,7 @@ components/sitemaps/routes.js
diff --git a/nodejs/jsdoc/components_sitemaps_security.js.html b/nodejs/jsdoc/components_sitemaps_security.js.html
index d52581e..a3b0c2b 100644
--- a/nodejs/jsdoc/components_sitemaps_security.js.html
+++ b/nodejs/jsdoc/components_sitemaps_security.js.html
@@ -31,7 +31,7 @@
- Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
+ Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
@@ -48,10 +48,12 @@ components/sitemaps/security.js
import logger from './../../logger.js';
-import { ORG_SEPARATOR, ADMIN_OU, EVERYONE_OU } from '../../server.js';
+import { getSitemapsForUser } from './backend.js';
+import { getItemsForUser } from './../items/backend.js';
+import { ADMIN_OU, SITEMAPS_DISABLE } from '../../server.js';
/**
- * Items security namespace. Provides security checks for Item access.
+ * Sitemaps security namespace. Provides security checks for Sitemaps access.
*
* @namespace sitemapsSecurity
*/
@@ -61,30 +63,107 @@ components/sitemaps/security.js
* Must be used with await in async functions.
*
* @memberof sitemapsSecurity
+ * @param {String} HOST hostname of openHAB server
+ * @param {*} expressReq request object from expressjs*
* @param {String} user username
* @param {String|Array<String>} org organizations the client is member of
* @param {String} sitemapname name of Sitemap
* @returns {Boolean} whether Sitemap access is allowed or not
*/
-export const sitemapAllowedForClient = (user, org, sitemapname) => {
+export const sitemapAllowedForClient = async function (HOST, expressReq, user, org, sitemapname) {
+ if (!user) throw Error('Parameter user is required!');
+ if (!org) org = [];
if (typeof org === 'string') org = org.toString().split('.');
- // If Sitemap name includes ORG_SEPARATOR, return string before ORG_SEPARATOR, else return Sitemap name.
- const orgOfSitemap = (sitemapname.includes(ORG_SEPARATOR)) ? sitemapname.split(ORG_SEPARATOR)[0] : sitemapname;
- logger.trace(`sitemapAllowedForClient(): Organization of Sitemap ${sitemapname} is ${orgOfSitemap}`);
- let allowed;
+ if (SITEMAPS_DISABLE == 'true') {
+ //Sitemaps disabled for all clients
+ logger.info({ user: user, orgs: org }, `sitemapAllowedForClient(): Sitemap ${sitemapname} allowed: false - Sitemaps disabled for all clients`);
+ return false;
+ }
if (org.includes(ADMIN_OU)) {
logger.info({ user: user, orgs: org }, `sitemapAllowedForClient(): Sitemap ${sitemapname} allowed: true due to admin privileges`);
return true;
}
- if (sitemapname === user || org.includes(orgOfSitemap) || orgOfSitemap === EVERYONE_OU) {
- //Access allow when sitename is user name, user org or EVERYONE_OU
- allowed = true;
- } else {
- allowed = false;
+ try {
+ const userSitemaps = await getSitemapsForUser(HOST, expressReq, user, org);
+ const allowed = userSitemaps.includes(sitemapname);
+ logger.info({ user: user, orgs: org }, `sitemapAllowedForClient(): Sitemap ${sitemapname} allowed: ${allowed}`);
+ return allowed;
+ } catch (err) {
+ logger.error(err);
+ return false;
}
- logger.info({ user: user, orgs: org }, `sitemapAllowedForClient(): Sitemap ${sitemapname} allowed: ${allowed}`);
- return allowed;
};
+
+/**
+ * Filter Items in widgets allowed for client - used for recursive filtering of Sitemap widgets.
+ * Must be used with await in async functions.
+ *
+ * @memberof sitemapsSecurity
+ * @param {String} HOST hostname of openHAB server
+ * @param {*} expressReq request object from expressjs
+ * @param {String} user username
+ * @param {String|Array<String>} org organizations the client is member of
+ * @param {String|Array<String>} list of widgets to filter
+ * @returns {String|Array<String>} list of filtered widgets
+ */
+export const widgetsFilterForClient = async function (HOST, expressReq, user, org, allWidgets) {
+ if (!user) throw Error('Parameter user is required!');
+ if (!org) org = [];
+ if (typeof org === 'string') org = org.toString().split('.');
+ if (org.includes(ADMIN_OU)) {
+ for (const i in allWidgets) {
+ if (allWidgets[i].hasOwnProperty('item')) logger.info({ user: user, orgs: org }, `widgetsFilterForClient(): Widget ${allWidgets[i].label} with Item ${allWidgets[i].item.name} allowed: true due to admin privileges`);
+ }
+ return allWidgets;
+ }
+ try {
+ const userItems = await getItemsForUser(HOST, expressReq, user, org);
+ let filteredWidgets = [];
+ for (const i in allWidgets) {
+ //filter current widget
+ let tempWidget = allWidgets[i];
+ if (tempWidget.hasOwnProperty('item')) {
+ const tempItem = tempWidget.item;
+ const allowed = userItems.includes(tempItem.name);
+ if (allowed === true) {
+ //recursive filtering of child widgets
+ const tempWidget2 = tempWidget.widgets;
+ if (Array.isArray(tempWidget2)) {
+ tempWidget.widgets = [];
+ const tempChWidgets = await widgetsFilterForClient(HOST, expressReq, user, org, tempWidget2);
+ tempWidget.widgets = tempChWidgets;
+ }
+ filteredWidgets.push(tempWidget);
+ }
+ logger.info({ user: user, orgs: org }, `widgetsFilterForClient(): Item ${tempItem.name} allowed: ${allowed}`);
+ } else if (tempWidget.hasOwnProperty('linkedPage')) {
+ //recursive filtering of linkedPage and its child widgets
+ const tempLinkedPage = tempWidget.linkedPage;
+ const tempWidget2 = tempLinkedPage.widgets;
+ if (Array.isArray(tempWidget2)) {
+ tempWidget.linkedPage.widgets = [];
+ const tempChWidgets = await widgetsFilterForClient(HOST, expressReq, user, org, tempWidget2);
+ tempWidget.linkedPage.widgets = tempChWidgets;
+ }
+ filteredWidgets.push(tempWidget);
+ } else {
+ //recursive filtering of child widgets
+ const tempWidget2 = tempWidget.widgets;
+ if (Array.isArray(tempWidget2)) {
+ tempWidget.widgets = [];
+ const tempChWidgets = await widgetsFilterForClient(HOST, expressReq, user, org, tempWidget2);
+ tempWidget.widgets = tempChWidgets;
+ }
+ filteredWidgets.push(tempWidget);
+ }
+ }
+ return filteredWidgets;
+ } catch (err) {
+ logger.error(err);
+ return [];
+ }
+};
+
@@ -99,7 +178,7 @@ components/sitemaps/security.js
diff --git a/nodejs/jsdoc/db.js.html b/nodejs/jsdoc/db.js.html
index 2db630e..5c2ee72 100644
--- a/nodejs/jsdoc/db.js.html
+++ b/nodejs/jsdoc/db.js.html
@@ -31,7 +31,7 @@
- Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
+ Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
@@ -107,6 +107,18 @@ db.js
autosave: false,
autosaveInterval: 10000
});
+/**
+ * LokiJS database that holds the Sitemaps allowed for a user.
+ *
+ * @memberof lokijs
+ */
+export const sitemapsForUserDb = db.addCollection('sitemapsForUser', {
+ exact: ['name', 'lastupdate', 'sitemaps'],
+ indices: ['name'],
+ autoload: false,
+ autosave: false,
+ autosaveInterval: 10000
+});
/**
* LokiJS database that holds the Pages allowed for a user.
*
@@ -145,7 +157,7 @@ db.js
diff --git a/nodejs/jsdoc/global.html b/nodejs/jsdoc/global.html
index 0aaa6ce..86f32b1 100644
--- a/nodejs/jsdoc/global.html
+++ b/nodejs/jsdoc/global.html
@@ -31,7 +31,7 @@
- Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
+ Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
@@ -350,7 +350,7 @@ Source:
@@ -419,7 +419,7 @@ Source:
@@ -472,6 +472,144 @@ (constant) ORG_SEPARATOR
+
+
+
+
+
+
+
+ - Description:
+ Separates the organization name at beginning of Sitemap name from the full name.
+Defaults to org
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Separates the organization name at beginning of Sitemap name from the full name.
+Defaults to org
+
+
+
+
+
+
+
+
+
+
+
+(constant) SITEMAPS_DISABLE
+
+
+
+
+
+
+
+ - Description:
+ Disable/filter all Sitemaps for all clients. Usefull in case only MainUI is used.
+Defaults to true
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Disable/filter all Sitemaps for all clients. Usefull in case only MainUI is used.
+Defaults to true
+
+
+
+
+
+
+
+
+
+
+
(constant) HOME_SEPARATOR
@@ -561,7 +699,7 @@
diff --git a/nodejs/jsdoc/index.html b/nodejs/jsdoc/index.html
index 0c77d3c..c952b1d 100644
--- a/nodejs/jsdoc/index.html
+++ b/nodejs/jsdoc/index.html
@@ -31,7 +31,7 @@
- Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
+ Home
Namespaces
- adminSecurity
- itemsBackend
- itemsSecurity
- middlewares
- pagesBackend
- pagesSecurity
- routes
- sitemapsBackend
- sitemapsSecurity
- lokijs
- utils
@@ -46,7 +46,7 @@ Home
Namespaces