Skip to content

Commit c6e5d88

Browse files
authored
Merge pull request #306 from FlowFuse/dashboard-iframe
Allow Dashboards to be loaded in an iFrame
2 parents 10980df + 6c87b60 commit c6e5d88

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

lib/runtimeSettings.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ function getSettingsFile (settings) {
6464
if (settings.settings.dashboardUI !== undefined) {
6565
dashboardSettings.push(`path: '${settings.settings.dashboardUI}'`)
6666
}
67-
if (authMiddlewareRequired) {
67+
if (authMiddlewareRequired && settings.settings.dashboardIFrame) {
68+
dashboardSettings.push('middleware: flowforgeAuthMiddleware.concat(DashboardIFrameMiddleware)')
69+
} else if (authMiddlewareRequired && !settings.settings.dashboardIFrame) {
6870
dashboardSettings.push('middleware: flowforgeAuthMiddleware')
71+
} else if (!authMiddlewareRequired && settings.settings.dashboardIFrame) {
72+
dashboardSettings.push('middleware: [ DashboardIFrameMiddleware ]')
6973
}
7074
projectSettings.dashboardUI = `ui: { ${dashboardSettings.join(', ')}},`
7175
}
@@ -251,6 +255,10 @@ function getSettingsFile (settings) {
251255

252256
const settingsTemplate = `
253257
${projectSettings.setupAuthMiddleware}
258+
const DashboardIFrameMiddleware = async function(req,res,next) {
259+
res.set("Content-Security-Policy", "frame-ancestors *");
260+
next()
261+
}
254262
module.exports = {
255263
flowFile: 'flows.json',
256264
flowFilePretty: true,

test/unit/lib/runtimeSettings_spec.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,71 @@ describe('Runtime Settings', function () {
313313
err.toString().should.match(/Cannot find module '@flowfuse\/nr-auth\/middleware'/)
314314
}
315315
})
316+
it('includes httpNodeMiddleware and dashboard iFrame middleware if flowforge-user auth type set and dashboard ui set to be loaded into iFrame', async function () {
317+
const result = runtimeSettings.getSettingsFile({
318+
baseURL: 'https://BASEURL',
319+
forgeURL: 'https://FORGEURL',
320+
settings: {
321+
dashboardUI: '/foo',
322+
httpNodeAuth: { type: 'flowforge-user' },
323+
dashboardIFrame: true
324+
}
325+
})
326+
327+
const settings = await loadSettings(result)
328+
settings.should.have.property('ui')
329+
settings.ui.should.have.property('path', '/foo')
330+
settings.ui.should.have.property('middleware')
331+
settings.ui.middleware.should.be.an.Array().and.have.length(3)
332+
;(typeof settings.ui.middleware[0]).should.equal('function')
333+
;(typeof settings.ui.middleware[1]).should.equal('function')
334+
;(typeof settings.ui.middleware[2]).should.equal('function') // iFrame middleware
335+
// calling the middleware function should add the CSP header & call next
336+
const headers = {}
337+
let nextCalled = false
338+
const res = {
339+
set: function (header, value) {
340+
headers[header] = value
341+
}
342+
}
343+
const next = function () {
344+
nextCalled = true
345+
}
346+
settings.ui.middleware[2]({}, res, next)
347+
headers.should.have.property('Content-Security-Policy', 'frame-ancestors *')
348+
nextCalled.should.equal(true)
349+
})
350+
it('includes only dashboard iFrame middleware if flowforge-user auth type is not set and dashboard ui set to be loaded into iFrame', async function () {
351+
const result = runtimeSettings.getSettingsFile({
352+
baseURL: 'https://BASEURL',
353+
forgeURL: 'https://FORGEURL',
354+
settings: {
355+
dashboardUI: '/foo',
356+
dashboardIFrame: true
357+
}
358+
})
359+
360+
const settings = await loadSettings(result)
361+
settings.should.have.property('ui')
362+
settings.ui.should.have.property('path', '/foo')
363+
settings.ui.should.have.property('middleware')
364+
settings.ui.middleware.should.be.an.Array().and.have.length(1)
365+
;(typeof settings.ui.middleware[0]).should.equal('function') // iFrame middleware
366+
// calling the middleware function should add the CSP header & call next
367+
const headers = {}
368+
let nextCalled = false
369+
const res = {
370+
set: function (header, value) {
371+
headers[header] = value
372+
}
373+
}
374+
const next = function () {
375+
nextCalled = true
376+
}
377+
settings.ui.middleware[0]({}, res, next)
378+
headers.should.have.property('Content-Security-Policy', 'frame-ancestors *')
379+
nextCalled.should.equal(true)
380+
})
316381
it('includes HA settings when enabled', async function () {
317382
const result = runtimeSettings.getSettingsFile({
318383
baseURL: 'https://BASEURL',

0 commit comments

Comments
 (0)