UNPKG

nightscout

Version:

Nightscout acts as a web-based CGM (Continuous Glucose Monitor) to allow multiple caregivers to remotely view a patients glucose data in realtime.

267 lines (224 loc) 8.76 kB
'use strict'; var crypto = require('crypto'); var Storages = require('js-storage'); var hashauth = { initialized: false }; hashauth.init = function init (client, $) { hashauth.apisecret = ''; hashauth.storeapisecret = false; hashauth.apisecrethash = null; hashauth.authenticated = false; hashauth.tokenauthenticated = false; hashauth.hasReadPermission = false; hashauth.isAdmin = false; hashauth.hasWritePermission = false; hashauth.permissionlevel = 'NONE'; hashauth.verifyAuthentication = function verifyAuthentication (next) { hashauth.authenticated = false; $.ajax({ method: 'GET' , url: '/api/v1/verifyauth?t=' + Date.now() //cache buster , headers: client.headers() }).done(function verifysuccess (response) { var message = response.message; if (message.canRead) { hashauth.hasReadPermission = true; } if (message.canWrite) { hashauth.hasWritePermission = true; } if (message.isAdmin) { hashauth.isAdmin = true; } if (message.permissions) { hashauth.permissionlevel = message.permissions; } if (message.rolefound == 'FOUND') { hashauth.tokenauthenticated = true; console.log('Token Authentication passed.'); next(true); return; } if (response.message === 'OK' || message.message === 'OK') { hashauth.authenticated = true; console.log('Authentication passed.'); next(true); return; } console.log('Authentication failed!', response); hashauth.removeAuthentication(); next(false); return; }).fail(function verifyfail (err) { console.log('Authentication failure', err); hashauth.removeAuthentication(); next(false); }); }; hashauth.injectHtml = function injectHtml () { if (!hashauth.injectedHtml) { $('#authentication_placeholder').html(hashauth.inlineCode()); hashauth.injectedHtml = true; } }; hashauth.initAuthentication = function initAuthentication (next) { hashauth.apisecrethash = hashauth.apisecrethash || Storages.localStorage.get('apisecrethash') || null; hashauth.verifyAuthentication(function() { hashauth.injectHtml(); if (next) { next(hashauth.isAuthenticated()); } }); return hashauth; }; hashauth.removeAuthentication = function removeAuthentication (event) { Storages.localStorage.remove('apisecrethash'); if (hashauth.authenticated || hashauth.tokenauthenticated) { client.browserUtils.reload(); } // clear eveything just in case hashauth.apisecret = null; hashauth.apisecrethash = null; hashauth.authenticated = false; if (event) { event.preventDefault(); } return false; }; hashauth.requestAuthentication = function requestAuthentication (eventOrNext) { var translate = client.translate; hashauth.injectHtml(); var clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; clientWidth = Math.min(400, clientWidth); $('#requestauthenticationdialog').dialog({ width: clientWidth , height: 270 , closeText: '' , buttons: [ { id: 'requestauthenticationdialog-btn' , text: translate('Authenticate') , click: function() { var dialog = this; hashauth.processSecret($('#apisecret').val(), $('#storeapisecret').is(':checked'), function done (close) { if (close) { if (eventOrNext && eventOrNext.call) { eventOrNext(true); } else { client.afterAuth(true); } $(dialog).dialog('close'); } else { $('#apisecret').val('').focus(); } }); } } ] , open: function open () { $('#apisecret').off('keyup').on('keyup', function pressed (e) { if (e.keyCode === $.ui.keyCode.ENTER) { $('#requestauthenticationdialog-btn').trigger('click'); } }); $('#apisecret').val('').focus(); } }); if (eventOrNext && eventOrNext.preventDefault) { eventOrNext.preventDefault(); } return false; }; hashauth.processSecret = function processSecret (apisecret, storeapisecret, callback) { var translate = client.translate; hashauth.apisecret = apisecret; hashauth.storeapisecret = storeapisecret; if (!hashauth.apisecret || hashauth.apisecret.length < 12) { window.alert(translate('Too short API secret')); if (callback) { callback(false); } } else { var shasum = crypto.createHash('sha1'); shasum.update(hashauth.apisecret); hashauth.apisecrethash = shasum.digest('hex'); hashauth.verifyAuthentication(function(isok) { if (isok) { if (hashauth.storeapisecret) { Storages.localStorage.set('apisecrethash', hashauth.apisecrethash); // TODO show dialog first, then reload if (hashauth.tokenauthenticated) client.browserUtils.reload(); } $('#authentication_placeholder').html(hashauth.inlineCode()); if (callback) { callback(true); } } else { alert(translate('Wrong API secret')); if (callback) { callback(false); } } }); } }; hashauth.inlineCode = function inlineCode () { var translate = client.translate; var status = null; if (!hashauth.isAdmin) { $('.needsadminaccess').hide(); } else { $('.needsadminaccess').show(); } if (client.updateAdminMenu) client.updateAdminMenu(); if (client.authorized || hashauth.tokenauthenticated) { status = translate('Authorized by token'); if (client.authorized && client.authorized.sub) { status += '<br>' + translate('Auth role') + ': ' + client.authorized.sub; if (hashauth.hasReadPermission) { status += '<br>' + translate('Data reads enabled'); } if (hashauth.hasWritePermission) { status += '<br>' + translate('Data writes enabled'); } if (!hashauth.hasWritePermission) { status += '<br>' + translate('Data writes not enabled'); } } if (hashauth.apisecrethash) { status += '<br> <a href="#" onclick="Nightscout.client.hashauth.removeAuthentication(); return false;">(' + translate('Remove stored token') + ')</a>'; } else { status += '<br><a href="/">(' + translate('view without token') + ')</a>'; } } else if (hashauth.isAuthenticated()) { status = translate('Admin authorized') + ' <a href="#" onclick="Nightscout.client.hashauth.removeAuthentication(); return false;">(' + translate('Remove') + ')</a>'; } else { status = translate('Unauthorized') + '<br>' + translate('Reads enabled in default permissions') + '<br>' + ' <a href="#" onclick="Nightscout.client.hashauth.requestAuthentication(); return false;">(' + translate('Authenticate') + ')</a>'; } var html = '<div id="requestauthenticationdialog" style="display:none" title="' + translate('Device authentication') + '">' + '<label for="apisecret">' + translate('Your API secret or token') + ': </label>' + '<input type="password" id="apisecret" size="20" style="width: 100%;"/>' + '<br>' + '<input type="checkbox" id="storeapisecret" /> <label for="storeapisecret">' + translate('Remember this device. (Do not enable this on public computers.)') + '</label>' + '</div>' + '<div id="authorizationstatus">' + status + '</div>'; return html; }; hashauth.updateSocketAuth = function updateSocketAuth () { client.socket.emit( 'authorize' , { client: 'web' , secret: client.authorized && client.authorized.token ? null : client.hashauth.hash() , token: client.authorized && client.authorized.token } , function authCallback (data) { if (!data.read && !client.authorized) { hashauth.requestAuthentication(); } } ); }; hashauth.hash = function hash () { return hashauth.apisecrethash; }; hashauth.isAuthenticated = function isAuthenticated () { return hashauth.authenticated || hashauth.tokenauthenticated; }; hashauth.initialized = true; return hashauth; } module.exports = hashauth;