Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2a73413
Add ability to provide jwts for adapter urls and wss
MarcGraham Sep 23, 2020
f678a40
refactor code -- use a function to get a configured header.
MarcGraham Sep 24, 2020
a212b86
Update readme.
MarcGraham Sep 24, 2020
df791e9
Update readme.
MarcGraham Sep 24, 2020
cb8e80d
Update readme.
MarcGraham Sep 24, 2020
bacbabb
Update readme.
MarcGraham Sep 24, 2020
f7dba82
modified mainfest to add config option for adding jwts on the adapter…
MarcGraham Sep 25, 2020
00188ed
update read me
MarcGraham Sep 25, 2020
c083a29
add check to see if secureThings is in config
MarcGraham Sep 25, 2020
1723037
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 28, 2020
bc00fa6
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 28, 2020
5a5cd3b
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 29, 2020
6f7bb59
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
348be93
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
b02674d
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
d79aeb3
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
eac57b5
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
bed17fc
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
9cc2090
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
a8a9374
https://github.com/WebThingsIO/thing-url-adapter/pull/96#discussion_r…
MarcGraham Sep 30, 2020
316825b
Latest polishing of code
MarcGraham Oct 1, 2020
a29c0be
Remove unneeded console.log statement
MarcGraham Oct 1, 2020
24102e4
Remove un-needed comments.
MarcGraham Oct 1, 2020
486f93f
Refactor for loops
MarcGraham Oct 1, 2020
fb0a14a
correct incorrect spelling
MarcGraham Oct 1, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,25 @@

This is an adapter add-on for the [Mozilla WebThings Gateway](https://github.com/mozilla-iot/gateway) that allows a user to discover native web things on their network.

## Adding Web Things to Gateway
## m2ag-labs fork
* Adds support for jwt authentication for restful and websocket connections to webthings.
* Configuration is via option field on adapter config page
* A thing that requires a jwt but has none configured will show up as an undefined thing in the gateway.
* Create jwt with code similar to [this](https://github.com/m2ag-labs/m2ag-thing/blob/master/api/helpers/auth.py)



Compatibility:
Tested with the m2ag-labs fork of the [webthing-python](https://github.com/m2ag-labs/webthing-python) project.

Installation:
* Remove the downloaded version of the webthings adapter.
* Clone this project into the .mozilla-iot/addons directory.
* run npm install in the thing-url-adapter directory.
* start gateway, go to addons and ensure plugin is enabled.
* add entry for secureWebthing whith the format `<hostname>:<port> <jwt>`

## Adding Web Things to Gateway
* Usually, your custom web things should be auto-detected on the network via mDNS, so they should appear in the usual "Add Devices" screen.
* If they're not auto-detected, you can click "Add by URL..." at the bottom of the page and use the URL of your web thing.
* If you're trying to add a server that contains multiple web things, i.e. the "multiple-things" examples from the [webthing-python](https://github.com/mozilla-iot/webthing-python), [webthing-node](https://github.com/mozilla-iot/webthing-node), or [webthing-java](https://github.com/mozilla-iot/webthing-java) libraries, you'll have to add them individually. You can do so by addressing them numerically, i.e. `http://myserver.local:8888/0` and `http://myserver.local:8888/1`.
13 changes: 11 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
"options": {
"default": {
"urls": [],
"pollInterval": 5
"pollInterval": 5,
"secureThings" : []
},
"schema": {
"type": "object",
"required": [
"urls",
"pollInterval"
"pollInterval",
"secureThings"
],
"properties": {
"urls": {
Expand All @@ -36,6 +38,13 @@
"pollInterval": {
"description": "The interval, in seconds, at which to poll property values.",
"type": "number"
},
"secureThings": {
"description": "Hostname and JWT to be used to access secureThings. Separate by a space.",
"type":"array",
"items": {
"type": "string"
}
}
}
}
Expand Down
90 changes: 63 additions & 27 deletions thing-url-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ const POLL_INTERVAL = 5 * 1000;
const WS_INITIAL_BACKOFF = 1000;
const WS_MAX_BACKOFF = 30 * 1000;

const JWT_AUTH = {}; // Auth tokens to access things with
// gets filled in loadThingURLAdapter

// This function checks the supplied url against keys
// in the jwt_auth object.
function getHeaders(url, contentType = false) {
const header = {Accept: 'application/json'};
if (contentType) {
header['Content-Type'] = 'application/json';
}
// Check for an auth token in the jwt_auth object
for (const i in JWT_AUTH) {
if (JWT_AUTH.hasOwnProperty(i)) {
if (url.includes(i)) {
header.Authorization = `Bearer ${JWT_AUTH[i]}`;
break;
}
}
}
return header;
}

class ThingURLProperty extends Property {
constructor(device, name, url, propertyDescription) {
super(device, name, propertyDescription);
Expand Down Expand Up @@ -71,12 +93,10 @@ class ThingURLProperty extends Property {
return Promise.resolve(value);
}

const header = getHeaders(this.url, true);
return fetch(this.url, {
method: 'PUT',
headers: {
'Content-type': 'application/json',
Accept: 'application/json',
},
headers: header,
body: JSON.stringify({
[this.name]: value,
}),
Expand Down Expand Up @@ -165,11 +185,10 @@ class ThingURLDevice extends Device {
propertyUrl = this.baseHref + propertyDescription.href;
}

const header = getHeaders(propertyUrl);
this.propertyPromises.push(
fetch(propertyUrl, {
headers: {
Accept: 'application/json',
},
headers: header,
}).then((res) => {
return res.json();
}).then((res) => {
Expand Down Expand Up @@ -264,8 +283,22 @@ class ThingURLDevice extends Device {
if (this.closing) {
return;
}
// if we have an entry for the wss url stub in jwt_auth
// add auth to query string
let auth = '';
for (const i in JWT_AUTH) {
if (this.wsUrl.includes(i)) {
auth = `?jwt=${JWT_AUTH[i]}`;
break;
}
}
// -- added '' and {} to get eslint to shut up
// this.ws will still be null here if no jwt has been entered
// for the current device
if (this.ws === null) {
this.ws = new WebSocket(`${this.wsUrl}${auth}`, '', {});
}

this.ws = new WebSocket(this.wsUrl);

this.ws.on('open', () => {
this.connectedNotify(true);
Expand Down Expand Up @@ -363,10 +396,9 @@ class ThingURLDevice extends Device {

// Update properties
await Promise.all(Array.from(this.properties.values()).map((prop) => {
const header = getHeaders(prop.url);
return fetch(prop.url, {
headers: {
Accept: 'application/json',
},
headers: header,
}).then((res) => {
return res.json();
}).then((res) => {
Expand All @@ -381,10 +413,9 @@ class ThingURLDevice extends Device {
})).then(() => {
// Check for new actions
if (this.actionsUrl !== null) {
const header = getHeaders(this.actionsUrl);
return fetch(this.actionsUrl, {
headers: {
Accept: 'application/json',
},
headers: header,
}).then((res) => {
return res.json();
}).then((actions) => {
Expand All @@ -408,10 +439,9 @@ class ThingURLDevice extends Device {
}).then(() => {
// Check for new events
if (this.eventsUrl !== null) {
const header = getHeaders(this.eventsUrl, true);
return fetch(this.eventsUrl, {
headers: {
Accept: 'application/json',
},
headers: header,
}).then((res) => {
return res.json();
}).then((events) => {
Expand Down Expand Up @@ -464,13 +494,10 @@ class ThingURLDevice extends Device {

performAction(action) {
action.start();

const header = getHeaders(this.actionsUrl);
return fetch(this.actionsUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
headers: header,
body: JSON.stringify({[action.name]: {input: action.input}}),
}).then((res) => {
return res.json();
Expand All @@ -488,11 +515,11 @@ class ThingURLDevice extends Device {

this.requestedActions.forEach((action, actionHref) => {
if (action.name === actionName && action.id === actionId) {
const header = getHeaders(actionHref);

promise = fetch(actionHref, {
method: 'DELETE',
headers: {
Accept: 'application/json',
},
headers: header,
}).catch((e) => {
console.log(`Failed to cancel action: ${e}`);
});
Expand Down Expand Up @@ -537,8 +564,10 @@ class ThingURLAdapter extends Adapter {
}

let res;
// Check for an auth token
const header = getHeaders(url);
try {
res = await fetch(url, {headers: {Accept: 'application/json'}});
res = await fetch(url, {headers: header});
} catch (e) {
// Retry the connection at a 2 second interval up to 5 times.
if (retryCounter >= 5) {
Expand Down Expand Up @@ -798,7 +827,14 @@ function loadThingURLAdapter(addonManager) {
if (typeof config.pollInterval === 'number') {
adapter.pollInterval = config.pollInterval * 1000;
}

// Add secureJWTs
if ('secureThings' in config) {
for (const item of config.secureThings) {
const i = item.split(' ');
JWT_AUTH[i[0]] = i[1];
}
console.log(JWT_AUTH);
}
for (const url of config.urls) {
adapter.loadThing(url);
}
Expand Down