(function () {
'use strict';
/*
* 2ndFA Session explorer
*/
/*
* AngularJS applications
*/
var categories, hiddenAttributes, llapp, max, menu, overScheme, schemes;
// Max number of session to display (see overScheme)
max = 25;
// Queries to do each type of display: each array item corresponds to the depth
// of opened nodes in the tree
schemes = {
_whatToTrace: [function (t, v) {
return `groupBy=substr(${t},1)`;
}, function (t, v) {
return `${t}=${v}*`;
}]
};
overScheme = {
_whatToTrace: function (t, v, level, over) {
console.debug('overSchema => level', level, 'over', over);
if (level === 1 && v.length > over) {
return `${t}=${v}*&groupBy=substr(${t},${level + over + 1})`;
} else {
return null;
}
}
};
hiddenAttributes = '_password';
// Attributes to group in session display
categories = {
dateTitle: ['_utime', '_startTime', '_updateTime'],
sfaTitle: ['_2fDevices']
};
// Menu entries
menu = {
home: []
};
llapp = angular.module('llngSessionsExplorer', ['ui.tree', 'ui.bootstrap', 'llApp']);
// Main controller
llapp.controller('SessionsExplorerCtrl', ['$scope', '$translator', '$location', '$q', '$http', function ($scope, $translator, $location, $q, $http) {
var autoId, c, pathEvent, sessionType;
$scope.links = links;
$scope.menulinks = menulinks;
$scope.staticPrefix = staticPrefix;
$scope.scriptname = scriptname;
$scope.formPrefix = formPrefix;
$scope.availableLanguages = availableLanguages;
$scope.waiting = true;
$scope.showM = false;
$scope.showT = true;
$scope.data = [];
$scope.currentScope = null;
$scope.currentSession = null;
$scope.menu = menu;
$scope.searchString = '';
$scope.sfatypes = {};
// Import translations functions
$scope.translateP = $translator.translateP;
$scope.translate = $translator.translate;
$scope.translateTitle = function (node) {
return $translator.translateField(node, 'title');
};
sessionType = 'persistent';
// Handle menu items
$scope.menuClick = function (button) {
if (button.popup) {
window.open(button.popup);
} else {
if (!button.action) {
button.action = button.title;
}
switch (typeof button.action) {
case 'function':
button.action($scope.currentNode, $scope);
$scope[button.action]();
break;
case 'string':
$scope[button.action]();
break;
default:
console.warn('Unknown action type', typeof button.action);
}
}
return $scope.showM = false;
};
//# SESSIONS MANAGEMENT
// Search 2FA sessions
$scope.search2FA = function (clear) {
if (clear) {
$scope.searchString = '';
}
$scope.currentSession = null;
$scope.data = [];
return $scope.updateTree2('', $scope.data, 0, 0);
};
// Delete 2FA device
$scope.delete2FA = function (type, epoch) {
var e, i, items, len;
items = document.querySelectorAll(`.data-${epoch}`);
for (i = 0, len = items.length; i < len; i++) {
e = items[i];
e.remove();
}
$scope.waiting = true;
$http['delete'](`${scriptname}sfa/${sessionType}/${$scope.currentSession.id}?type=${type}&epoch=${epoch}`).then(function (response) {
return $scope.waiting = false;
}, function (resp) {
return $scope.waiting = false;
});
return $scope.showT = false;
};
// Open node
$scope.stoggle = function (scope) {
var node;
node = scope.$modelValue;
if (node.nodes.length === 0) {
$scope.updateTree(node.value, node.nodes, node.level, node.over, node.query, node.count);
}
return scope.toggle();
};
// Display selected session
$scope.displaySession = function (scope) {
var sessionId, transformSession;
// Private functions
// Session preparation
transformSession = function (session) {
var _stToStr, array, arrayDate, attr, attrs, category, epoch, i, k, key, len, len1, name, pattern, res, sfDevice, subres, time, type, value;
_stToStr = function (s) {
return s;
};
time = session._utime;
// 1. Replace values if needed
for (key in session) {
value = session[key];
if (!value) {
delete session[key];
} else {
if (typeof session === 'string' && value.match(/; /)) {
session[key] = value.split('; ');
}
if (typeof session[key] !== 'object') {
if (hiddenAttributes.match(new RegExp('\b' + key + '\b'))) {
session[key] = '********';
} else if (key.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)) {
session[key] = $scope.localeDate(value);
} else if (key.match(/^(_startTime|_updateTime)$/)) {
value = _stToStr(value);
pattern = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/;
arrayDate = value.match(pattern);
session[key] = `${arrayDate[3]}/${arrayDate[2]}/${arrayDate[1]} à ${arrayDate[4]}:${arrayDate[5]}:${arrayDate[6]}`;
}
}
}
}
res = [];
// 2. Push session keys in result, grouped by categories
for (category in categories) {
attrs = categories[category];
subres = [];
for (i = 0, len = attrs.length; i < len; i++) {
attr = attrs[i];
if (session[attr]) {
if (attr === "_2fDevices" && session[attr]) {
array = JSON.parse(session[attr]);
if (array.length > 0) {
subres.push({
title: "2fid",
value: "name",
type: "type",
epoch: "date"
});
for (k = 0, len1 = array.length; k < len1; k++) {
sfDevice = array[k];
for (key in sfDevice) {
value = sfDevice[key];
if (key === 'type') {
type = value;
}
if (key === 'name') {
name = value;
}
if (key === 'epoch') {
epoch = value;
}
}
subres.push({
title: '[' + type + ']' + epoch,
type: type,
value: name,
epoch: epoch,
sfrow: true
});
}
}
delete session[attr];
} else if (session[attr].toString().match(/\w+/)) {
subres.push({
title: attr,
value: session[attr]
});
delete session[attr];
} else {
delete session[attr];
}
} else {
delete session[attr];
}
}
if (subres.length > 0) {
res.push({
title: `__${category}__`,
nodes: subres
});
}
}
return {
_utime: time,
nodes: res
};
};
$scope.currentScope = scope;
sessionId = scope.$modelValue.session;
$http.get(`${scriptname}sfa/${sessionType}/${sessionId}`).then(function (response) {
$scope.currentSession = transformSession(response.data);
return $scope.currentSession.id = sessionId;
});
return $scope.showT = false;
};
$scope.localeDate = function (s) {
var d;
d = new Date(s * 1000);
return d.toLocaleString();
};
// Function to change interface language
$scope.getLanguage = function (lang) {
$scope.lang = lang;
$scope.form = 'white';
$scope.init();
return $scope.showM = false;
};
// URI local path management
pathEvent = function (event, next, current) {
var n;
n = next.match(/#!?\/(\w+)/);
if (n === null || n[1].match(/^(persistent)$/)) {
$scope.type = '_session_uid';
}
return $scope.init();
};
$scope.$on('$locationChangeSuccess', pathEvent);
// Functions to update tree: download value of opened subkey
autoId = 0;
$scope.updateTree = function (value, node, level, over, currentQuery, count) {
var query, scheme, tmp;
$scope.waiting = true;
// Query scheme selection:
// - if defined above
// - default to _whatToTrace scheme
scheme = schemes[$scope.type] ? schemes[$scope.type] : schemes._whatToTrace;
// Build query using schemes
query = scheme[level]($scope.type, value, currentQuery);
// If number of session exceeds "max" and overScheme exists, call it
if (count > max && overScheme[$scope.type]) {
if (tmp = overScheme[$scope.type]($scope.type, value, level, over, currentQuery)) {
over++;
query = tmp;
level = level - 1;
} else {
over = 0;
}
} else {
over = 0;
}
// Launch HTTP query
return $http.get(`${scriptname}sfa/${sessionType}?${query}` + Object.entries($scope.sfatypes).map(function (x) {
if (x[1]) {
return "&type=" + x[0];
} else {
return "";
}
}).join("")).then(function (response) {
var data, i, len, n, ref;
data = response.data;
if (data.result) {
ref = data.values;
for (i = 0, len = ref.length; i < len; i++) {
n = ref[i];
autoId++;
n.id = `node${autoId}`;
if (level < scheme.length - 1) {
n.nodes = [];
n.level = level + 1;
n.query = query;
n.over = over;
}
node.push(n);
}
if (value === '') {
$scope.total = data.total;
}
}
return $scope.waiting = false;
}, function (resp) {
return $scope.waiting = false;
});
};
// Functions to filter U2F sessions tree : download value of opened subkey
$scope.updateTree2 = function (value, node, level, over, currentQuery, count) {
var query, scheme, tmp;
$scope.waiting = true;
// Query scheme selection:
// - if defined above
// - _updateTime must be displayed as startDate
// - default to _whatToTrace scheme
scheme = schemes[$scope.type] ? schemes[$scope.type] : $scope.type === '_updateTime' ? schemes._startTime : schemes._whatToTrace;
// Build query using schemes
query = scheme[level]($scope.type, value, currentQuery);
// If number of session exceeds "max" and overScheme exists, call it
if (count > max && overScheme[$scope.type]) {
if (tmp = overScheme[$scope.type]($scope.type, value, level, over, currentQuery)) {
over++;
query = tmp;
level = level - 1;
} else {
over = 0;
}
} else {
over = 0;
}
// Launch HTTP
return $http.get(`${scriptname}sfa/${sessionType}?_session_uid=${$scope.searchString}*&groupBy=substr(_session_uid,${$scope.searchString.length})` + Object.entries($scope.sfatypes).map(function (x) {
if (x[1]) {
return "&type=" + x[0];
} else {
return "";
}
}).join("")).then(function (response) {
var data, i, len, n, ref;
data = response.data;
if (data.result) {
ref = data.values;
for (i = 0, len = ref.length; i < len; i++) {
n = ref[i];
autoId++;
n.id = `node${autoId}`;
if (level < scheme.length - 1) {
n.nodes = [];
n.level = level + 1;
n.query = query;
n.over = over;
}
node.push(n);
}
if (value === '') {
$scope.total = data.total;
}
}
return $scope.waiting = false;
}, function (resp) {
return $scope.waiting = false;
});
};
// Intialization function
// Simply set $scope.waiting to false during $translator and tree root
// initialization
$scope.init = function () {
$scope.waiting = true;
$scope.data = [];
$q.all([$translator.init($scope.lang), $scope.updateTree('', $scope.data, 0, 0)]).then(function () {
return $scope.waiting = false;
}, function (resp) {
return $scope.waiting = false;
});
// Colorized link
$scope.activeModule = "2ndFA";
return $scope.myStyle = {
color: '#ffb84d'
};
};
// Query scheme initialization
// Default to '_whatToTrace'
c = $location.path().match(/^\/(\w+)/);
return $scope.type = c ? c[1] : '_whatToTrace';
}]);
})();