-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #100 from flowforge/roll-in-plugins
Move storage/auth/auditLogger/theme into launcher
- Loading branch information
Showing
32 changed files
with
2,006 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/** | ||
* | ||
*/ | ||
|
||
const got = require('got') | ||
|
||
module.exports = (settings) => { | ||
const baseURL = settings.loggingURL | ||
const projectID = settings.projectID | ||
const token = settings.token | ||
|
||
const logger = function (msg) { | ||
if (/^(comms\.|.*\.get$)/.test(msg.event)) { | ||
// Ignore comms events and any .get event that is just reading data | ||
return | ||
} | ||
if (/^auth/.test(msg.event) && !/^auth.log/.test(msg.event)) { | ||
return | ||
} | ||
if (msg.user) { | ||
msg.user = msg.user.userId | ||
} | ||
delete msg.username | ||
delete msg.level | ||
|
||
got.post(baseURL + '/' + projectID + '/audit', { | ||
json: msg, | ||
responseType: 'json', | ||
headers: { | ||
'user-agent': 'FlowForge Audit Logging v0.1', | ||
authorization: 'Bearer ' + token | ||
} | ||
}) | ||
.catch(err => { | ||
// ignore errors for now | ||
console.log(err) | ||
}) | ||
} | ||
|
||
return logger | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
const { OAuth2 } = require('oauth') | ||
const { Strategy } = require('./strategy') | ||
|
||
module.exports = (options) => { | ||
['clientID', 'clientSecret', 'forgeURL', 'baseURL'].forEach(prop => { | ||
if (!options[prop]) { | ||
throw new Error(`Missing configuration option ${prop}`) | ||
} | ||
}) | ||
|
||
const clientID = options.clientID | ||
const clientSecret = options.clientSecret | ||
const forgeURL = options.forgeURL | ||
const baseURL = options.baseURL | ||
|
||
const callbackURL = `${baseURL}/auth/strategy/callback` | ||
const authorizationURL = `${forgeURL}/account/authorize` | ||
const tokenURL = `${forgeURL}/account/token` | ||
const userInfoURL = `${forgeURL}/api/v1/user` | ||
|
||
const oa = new OAuth2(clientID, clientSecret, '', authorizationURL, tokenURL) | ||
|
||
const version = require('../../package.json').version | ||
|
||
const activeUsers = {} | ||
|
||
function addUser (username, profile, refreshToken, expiresIn) { | ||
if (activeUsers[username]) { | ||
clearTimeout(activeUsers[username].refreshTimeout) | ||
} | ||
activeUsers[username] = { | ||
profile, | ||
refreshToken, | ||
expiresIn | ||
} | ||
activeUsers[username].refreshTimeout = setTimeout(function () { | ||
oa.getOAuthAccessToken(refreshToken, { | ||
grant_type: 'refresh_token' | ||
}, function (err, accessToken, refreshToken, results) { | ||
if (err) { | ||
delete activeUsers[username] | ||
} else { | ||
addUser(username, profile, refreshToken, results.expires_in) | ||
} | ||
}) | ||
}, expiresIn * 1000) | ||
} | ||
|
||
return { | ||
type: 'strategy', | ||
strategy: { | ||
name: 'FlowForge', | ||
autoLogin: true, | ||
label: 'Sign in', | ||
strategy: Strategy, | ||
options: { | ||
authorizationURL, | ||
tokenURL, | ||
callbackURL, | ||
userInfoURL, | ||
scope: `editor-${version}`, | ||
clientID, | ||
clientSecret, | ||
pkce: true, | ||
state: true, | ||
verify: function (accessToken, refreshToken, params, profile, done) { | ||
profile.permissions = [params.scope || 'read'] | ||
addUser(profile.username, profile, refreshToken, params.expires_in) | ||
done(null, profile) | ||
} | ||
} | ||
}, | ||
users: async function (username) { | ||
return activeUsers[username] && activeUsers[username].profile | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const { Passport } = require('passport') | ||
const { Strategy } = require('./strategy') | ||
|
||
let options | ||
let passport | ||
|
||
module.exports = { | ||
init (_options) { | ||
options = _options | ||
return (req, res, next) => { | ||
try { | ||
if (req.session.ffSession) { | ||
next() | ||
} else { | ||
req.session.redirectTo = req.originalUrl | ||
passport.authenticate('FlowForge', { session: false })(req, res, next) | ||
} | ||
} catch (err) { | ||
console.log(err.stack) | ||
throw err | ||
} | ||
} | ||
}, | ||
|
||
setupAuthRoutes (app) { | ||
if (!options) { | ||
// If `init` has not been called, then the flowforge-user auth type | ||
// has not been selected. No need to setup any further routes. | ||
return | ||
} | ||
// 'app' is RED.httpNode - the express app that handles all http routes | ||
// exposed by the flows. | ||
|
||
passport = new Passport() | ||
app.use(passport.initialize()) | ||
|
||
const callbackURL = `${options.baseURL}/_ffAuth/callback` | ||
const authorizationURL = `${options.forgeURL}/account/authorize` | ||
const tokenURL = `${options.forgeURL}/account/token` | ||
const userInfoURL = `${options.forgeURL}/api/v1/user` | ||
const version = require('../../package.json').version | ||
|
||
passport.use('FlowForge', new Strategy({ | ||
authorizationURL, | ||
tokenURL, | ||
callbackURL, | ||
userInfoURL, | ||
scope: `editor-${version}`, | ||
clientID: options.clientID, | ||
clientSecret: options.clientSecret, | ||
pkce: true, | ||
state: true | ||
}, function (accessToken, refreshToken, params, profile, done) { | ||
done(null, profile) | ||
})) | ||
|
||
app.get('/_ffAuth/callback', passport.authenticate('FlowForge', { | ||
session: false | ||
}), (req, res) => { | ||
req.session.user = req.user | ||
req.session.ffSession = true | ||
if (req.session?.redirectTo) { | ||
const redirectTo = req.session.redirectTo | ||
delete req.session.redirectTo | ||
res.redirect(redirectTo) | ||
} else { | ||
res.redirect('/') | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const { setupAuthRoutes } = require('./httpAuthMiddleware') | ||
|
||
module.exports = (RED) => { | ||
RED.plugins.registerPlugin('ff-auth-plugin', { | ||
onadd: () => { | ||
RED.log.info('FlowForge HTTP Authentication Plugin loaded') | ||
setupAuthRoutes(RED.httpNode) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
const util = require('util') | ||
const OAuth2Strategy = require('passport-oauth2') | ||
|
||
function Strategy (options, verify) { | ||
this.options = options | ||
this._base = Object.getPrototypeOf(Strategy.prototype) | ||
this._base.constructor.call(this, this.options, verify) | ||
this.name = 'FlowForge' | ||
} | ||
|
||
util.inherits(Strategy, OAuth2Strategy) | ||
|
||
Strategy.prototype.userProfile = function (accessToken, done) { | ||
this._oauth2.useAuthorizationHeaderforGET(true) | ||
this._oauth2.get(this.options.userInfoURL, accessToken, (err, body) => { | ||
if (err) { | ||
return done(err) | ||
} | ||
try { | ||
const json = JSON.parse(body) | ||
done(null, { | ||
username: json.username, | ||
image: json.avatar, | ||
name: json.name, | ||
userId: json.id | ||
}) | ||
} catch (e) { | ||
done(e) | ||
} | ||
}) | ||
} | ||
|
||
module.exports = { Strategy } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
const got = require('got') | ||
|
||
let settings | ||
|
||
module.exports = { | ||
init: (nrSettings) => { | ||
settings = nrSettings.httpStorage || {} | ||
|
||
if (Object.keys(settings) === 0) { | ||
const err = Promise.reject(new Error('No settings for flow storage module found')) | ||
// err.catch(err => {}) | ||
return err | ||
} | ||
|
||
const projectID = settings.projectID | ||
|
||
// console.log(settings) | ||
// console.log(settings.baseURL + "/" + projectID + "/") | ||
|
||
this._client = got.extend({ | ||
prefixUrl: settings.baseURL + '/' + projectID + '/', | ||
headers: { | ||
'user-agent': 'FlowForge HTTP Storage v0.1', | ||
authorization: 'Bearer ' + settings.token | ||
}, | ||
timeout: { | ||
request: 10000 | ||
} | ||
}) | ||
|
||
return Promise.resolve() | ||
}, | ||
getFlows: async () => { | ||
return this._client.get('flows').json() | ||
}, | ||
saveFlows: async (flow) => { | ||
return this._client.post('flows', { | ||
json: flow, | ||
responseType: 'json' | ||
}) | ||
}, | ||
getCredentials: async () => { | ||
return this._client.get('credentials').json() | ||
}, | ||
saveCredentials: async (credentials) => { | ||
return this._client.post('credentials', { | ||
json: credentials, | ||
responseType: 'json' | ||
}) | ||
}, | ||
getSettings: () => { | ||
return this._client.get('settings').json() | ||
}, | ||
saveSettings: (settings) => { | ||
return this._client.post('settings', { | ||
json: settings, | ||
responseType: 'json' | ||
}) | ||
}, | ||
getSessions: () => { | ||
this._client.get('sessions').json() | ||
}, | ||
saveSessions: (sessions) => { | ||
return this._client.post('sessions', { | ||
json: sessions, | ||
responseType: 'json' | ||
}) | ||
}, | ||
getLibraryEntry: (type, name) => { | ||
return this._client.get('library/' + type, { | ||
searchParams: { | ||
name | ||
} | ||
}).then(entry => { | ||
if (entry.headers['content-type'].startsWith('application/json')) { | ||
return JSON.parse(entry.body) | ||
} else { | ||
return entry.body | ||
} | ||
}) | ||
}, | ||
saveLibraryEntry: (type, name, meta, body) => { | ||
return this._client.post('library/' + type, { | ||
json: { | ||
name, | ||
meta, | ||
body | ||
}, | ||
responseType: 'json' | ||
}) | ||
} | ||
} |
Oops, something went wrong.