Skip to content

Commit

Permalink
Merge pull request nightscout#8132 from nightscout/wip/bewest/8107-pr…
Browse files Browse the repository at this point in the history
…ofile-buttons

alarmSocket: customizing authorization requirements
  • Loading branch information
bewest authored Oct 24, 2023
2 parents 73c8089 + a7ebb30 commit 44f70db
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 28 deletions.
84 changes: 65 additions & 19 deletions lib/api3/alarmSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ function AlarmSocket (app, env, ctx) {
socket.on('subscribe', function onSubscribe (message, returnCallback) {
self.subscribe(socket, message, returnCallback);
});

});

// Turns all notifications on the event bus back into events to be
// broadcast to clients.
ctx.bus.on('notification', self.emitNotification);
};

Expand All @@ -65,7 +67,7 @@ function AlarmSocket (app, env, ctx) {
self.subscribe = function subscribe (socket, message, returnCallback) {
const shouldCallBack = typeof(returnCallback) === 'function';

// Native client
// Native client
if (message && message.accessToken) {
return ctx.authorization.resolveAccessToken(message.accessToken, function resolveFinishForToken (err, auth) {
if (err) {
Expand All @@ -76,24 +78,47 @@ function AlarmSocket (app, env, ctx) {
}
return err;
} else {
// Subscribe for acking alarms
socket.on('ack', function onAck (level, group, silenceTime) {
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
});

var okResponse = { success: true, message: 'Subscribed for alarms' }
// Subscribe for acking alarms
// Client sends ack, which sends a notificaiton through our internal bus
socket.on('ack', function onAck (level, group, silenceTime) {
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
});

var okResponse = { success: true, message: 'Subscribed for alarms' }
if (shouldCallBack) {
returnCallback(okResponse);
}
return okResponse;
}
});
}

// Web client (jwt access token or api_hash)
if (message && (message.jwtToken || message.secret)) {

if (!message) { message = {}; }
// Web client (jwt access token or api_hash)
/*
* On the web: a client may have saved a secret or using a jwtToken, or may have none.
* Some pages will automatically prompt for authorization, when needed.
* To make the main homepage require authorization as well, set
* AUTHENTICATION_PROMPT_ON_LOAD=true.
*
* If there is missing authorization when authorization is required,
* rejecting the attempt in order to trigger a prompt on the client.
* If there is no authorization required, or there are available
* credentials, attempt to resolve the available permissions.
* When processing ACK messages that dismiss alarms, Authorization should be
* required.
*/
var shouldTry = true;
if (env.settings.authenticationPromptOnLoad) {
if (!message.jwtToken && !message.secret) {
shouldTry = false;
}
}

if (message && shouldTry) {
return ctx.authorization.resolve({ api_secret: message.secret, token: message.jwtToken, ip: getRemoteIP(socket.request) }, function resolveFinish (err, auth) {

if (err) {
console.log(`${LOG_ERROR} Authorization failed for jwtToken:`, message.jwtToken);

Expand All @@ -102,13 +127,34 @@ function AlarmSocket (app, env, ctx) {
}
return err;
} else {
// Subscribe for acking alarms
socket.on('ack', function onAck (level, group, silenceTime) {
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
});

var okResponse = { success: true, message: 'Subscribed for alarms' }
var perms = {
read: ctx.authorization.checkMultiple('api:*:read', auth.shiros)
, ack: ctx.authorization.checkMultiple('notifications:*:ack', auth.shiros)
};
// Subscribe for acking alarms
// TODO: does this produce double ACK after the authorizing? Only if reconnecting?
// TODO: how will perms get updated after authorizing?
socket.on('ack', function onAck (level, group, silenceTime) {
if (perms.ack) {
// This goes through the server-wide event bus.
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
} else {
// TODO: send a message to client to silence locally, but not
// globally, and request authorization.
// This won't go through th event bus.
// var acked = { silenceTime, group, level };
// socket.emit('authorization_needed', acked);
}
});
/* TODO: need to know when to update the permissions.
// Can we use
socket.on('resubscribe', function update_permissions ( ) {
// perms = { ... };
});
*/

var okResponse = { success: true, message: 'Subscribed for alarms', ...perms };
if (shouldCallBack) {
returnCallback(okResponse);
}
Expand Down
38 changes: 29 additions & 9 deletions lib/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1146,15 +1146,13 @@ client.load = function load (serverSettings, callback) {
}

console.log('Subscribed for alarms', data);
if (client.settings.authenticationPromptOnLoad && !data.success) {
client.hashauth.requestAuthentication(function afterRequest () {
client.hashauth.updateSocketAuth();
if (callback) {
callback();
}
});
} else if (callback) {
callback();
var shouldAuthenticationPromptOnLoad = client.settings.authenticationPromptOnLoad ;
if (!data.success) {
if (!data.read || !hasRequiredPermission() || shouldAuthenticationPromptOnLoad) {
return client.hashauth.requestAuthentication(function afterRequest () {
return client.hashauth.updateSocketAuth();
});
}
}
}
);
Expand Down Expand Up @@ -1235,6 +1233,28 @@ client.load = function load (serverSettings, callback) {
stopAlarm(false, null, notify);
}
});
/*
*
// TODO: When an unauthorized client attempts to silence an alarm, we should
// allow silencing locally, request for authorization, and if the
// authorization succeeds even republish the ACK notification. something like...
alarmSocket.on('authorization_needed', function(details) {
if (alarmInProgress) {
console.log('clearing alarm');
stopAlarm(true, details.silenceTime, currentNotify);
}
client.hashauth.requestAuthentication(function afterRequest () {
console.log("SUCCESSFULLY AUTHORIZED, REPUBLISHED ACK?");
// easiest way to update permission set on server side is to send another message.
alarmSocket.emit('resubscribe', currentNotify, details);
if (isClient && currentNotify) {
alarmSocket.emit('ack', currentNotify.level, currentNotify.group, details.silenceTime);
}
});
});
*/

$('#testAlarms').click(function(event) {

Expand Down
6 changes: 6 additions & 0 deletions lib/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,19 @@ function init (env, ctx) {
notifications.ack(1, group, time);
}

/*
* TODO: modify with a local clear, this will clear all connected clients,
* globally
*/
if (sendClear) {
var notify = {
clear: true
, title: 'All Clear'
, message: group + ' - ' + ctx.levels.toDisplay(level) + ' was ack\'d'
, group: group
};
// When web client sends ack, this translates the websocket message into
// an event on our internal bus.
ctx.bus.emit('notification', notify);
logEmitEvent(notify);
}
Expand Down
1 change: 1 addition & 0 deletions lib/profile/profileeditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var init = function init () {

client.init(function loaded () {

console.log("LOADING CLIENT INIT");
if (c_profile !== null) {
return; // already loaded so don't load again
}
Expand Down

0 comments on commit 44f70db

Please sign in to comment.