Commit 7477ce08 authored by Xavier Guimard's avatar Xavier Guimard

Merge branch 'manager-2ndFA-module' into 'master'

Manager 2ndfa module

See merge request !25
parents 0d72ad10 14a34d85
Pipeline #1423 passed with stage
in 2 minutes and 26 seconds
......@@ -42,17 +42,17 @@ sub addRoutes {
['DELETE']
);
## ADD 2FA DEVICE
#->addRoute(
#sfa => { ':sessionType' => { ':sessionId' => 'add2FA' } },
#['PUT']
#)
## VERIFY 2FA DEVICE
#->addRoute(
#sfa => { ':sessionType' => { ':sessionId' => 'verify2FA' } },
#['POST']
#);
## ADD 2FA DEVICE
#->addRoute(
#sfa => { ':sessionType' => { ':sessionId' => 'add2FA' } },
#['PUT']
#)
## VERIFY 2FA DEVICE
#->addRoute(
#sfa => { ':sessionType' => { ':sessionId' => 'verify2FA' } },
#['POST']
#);
$self->setTypes($conf);
......@@ -75,22 +75,15 @@ sub delete2FA {
my $mod = $self->getMod($req)
or return $self->sendError( $req, undef, 400 );
my $params = $req->parameters();
my $type = $params->{type};
my $epoch = $params->{epoch};
my $params = $req->parameters();
my $type = $params->{type};
my $epoch = $params->{epoch};
if ( $type =~ /\b(?:U2F|TOTP|UBK)\b/ and $epoch ) {
$self->logger->debug("Call procedure delete2F with type=$type and epoch=$epoch");
$self->logger->debug(
"Call procedure delete2F with type=$type and epoch=$epoch");
return $self->delete2F( $req, $session, $skey );
}
#elsif ( $type =~ /\bTOTP\b/ ) {
#$self->logger->debug("Call procedure deleteTOTP");
#return $self->deleteTOTP( $req, $session, $skey );
#}
#elsif ( $type =~ /\bUBK\b/ ) {
#$self->logger->debug("Call procedure deleteUBK");
#return $self->deleteUBK( $req, $session, $skey );
#}
else {
return $self->sendError( $req, undef, 400 );
}
......@@ -98,22 +91,22 @@ sub delete2FA {
#sub add2FA {
#my ( $self, $req, $session, $skey ) = @_;
#my ( $self, $req, $session, $skey ) = @_;
#eval 'use Crypt::U2F::Server::Simple';
#if ($@) {
#$self->error("Can't load U2F library: $@");
#return 0;
#}
#eval 'use Crypt::U2F::Server::Simple';
#if ($@) {
#$self->error("Can't load U2F library: $@");
#return 0;
#}
#return $self->addU2FKey( $req, $session, $skey );
#return $self->addU2FKey( $req, $session, $skey );
#}
#sub verify2FA {
#my ( $self, $req, $session, $skey ) = @_;
#my ( $self, $req, $session, $skey ) = @_;
#return $self->addU2FKey( $req, $session, $skey );
#return $self->addU2FKey( $req, $session, $skey );
#}
########################
......@@ -141,10 +134,8 @@ sub sfa {
my $whatToTrace = Lemonldap::NG::Handler::PSGI::Main->tsv->{whatToTrace};
# 2.1 Get fields to require
my @fields = (
'_httpSessionType', $self->{ipField},
$whatToTrace, '_2fDevices'
);
my @fields =
( '_httpSessionType', $self->{ipField}, $whatToTrace, '_2fDevices' );
if ( my $groupBy = $params->{groupBy} ) {
$groupBy =~ s/^substr\((\w+)(?:,\d+(?:,\d+)?)?\)$/$1/;
$groupBy =~ s/^_whatToTrace$/$whatToTrace/o
......@@ -160,7 +151,10 @@ sub sfa {
$moduleOptions->{backend} = $mod->{module};
# Select 2FA sessions to display
if ( defined $params->{TOTPCheck} or defined $params->{U2FCheck} or defined $params->{UBKCheck}) {
if ( defined $params->{TOTPCheck}
or defined $params->{U2FCheck}
or defined $params->{UBKCheck} )
{
$self->{TOTPCheck} = delete $params->{TOTPCheck};
$self->{U2FCheck} = delete $params->{U2FCheck};
$self->{UBKCheck} = delete $params->{UBKCheck};
......@@ -313,11 +307,11 @@ qq{Use of an uninitialized attribute "$group" to group sessions},
}
# Else, $res elements will be like:
# { session => <sessionId>, date => <timestamp> }
# { session => <sessionId>, userId => <_session_uid> }
else {
$res = [
sort { $a->{date} <=> $b->{date} }
map { { session => $_, date => $res->{$_}->{_utime} } }
map { { session => $_, userId => $res->{$_}->{_session_uid} } }
keys %$res
];
}
......
......@@ -2,7 +2,6 @@
# 2ndFA Session explorer
###
setMsg = (msg, level) ->
$('#msg').html window.translate msg
$('#color').removeClass 'message-positive message-warning alert-success alert-warning'
......@@ -18,7 +17,6 @@ displayError = (j, status, err) ->
console.log 'Returned error', res
setMsg res, 'warning'
# Max number of session to display (see overScheme)
max = 25
......@@ -29,26 +27,7 @@ schemes =
(t,v) ->
"groupBy=substr(#{t},1)"
(t,v) ->
"#{t}=#{v}*&groupBy=#{t}"
(t,v) ->
"#{t}=#{v}"
]
_startTime: [
(t,v) ->
"groupBy=substr(#{t},8)"
(t,v) ->
"#{t}=#{v}*&groupBy=substr(#{t},10)"
(t,v) ->
"#{t}=#{v}*&groupBy=substr(#{t},11)"
(t,v) ->
"#{t}=#{v}*&groupBy=substr(#{t},12)"
(t,v) ->
"#{t}=#{v}*&groupBy=_whatToTrace"
(t,v,q) ->
console.log t
console.log v
console.log q
q.replace(/\&groupBy.*$/, '') + "&_whatToTrace=#{v}"
"#{t}=#{v}*"
]
overScheme =
......@@ -57,46 +36,16 @@ overScheme =
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else
null
ipAddr: (t,v,level,over) ->
if level > 0 and level < 4
"#{t}=#{v}*&groupBy=net(#{t},#{16*level+4*(over+1)},2)"
else
null
hiddenAttributes = '_password'
# Attributes to group in session display
categories =
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen']
connectionTitle: ['ipAddr', '_timezone', '_url']
dateTitle: ['_utime', '_startTime', '_updateTime']
sfaTitle: ['_2fDevices']
# Menu entries
menu =
#delU2FKey: [
#title: 'deleteU2FKey'
#icon: 'trash'
#]
#addU2FKey: [
#title: 'addU2FKey'
#icon: 'plus'
#]
#verifyU2FKey: [
#title: 'verifyU2FKey'
#icon: 'check'
#]
#delTOTPKey: [
#title: 'deleteTOTPKey'
#icon: 'trash'
#]
#addTOTPKey: [
#title: 'addTOTPKey'
#icon: 'plus'
#]
#verifyTOTPKey: [
#title: 'verifyTOTPKey'
#icon: 'check'
#]
home: []
###
......@@ -155,20 +104,14 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
# Delete 2FA device
$scope.delete2FA = (type, epoch) ->
item = angular.element("#data-#{epoch}")
item = angular.element(".data-#{epoch}")
item.remove()
$scope.waiting = true
$http['delete']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?type=#{type}&epoch=#{epoch}").then (response) ->
#$scope.currentSession = null
#$scope.currentScope.remove()
#$scope.data = []
$scope.waiting = false
, (resp) ->
#$scope.currentSession = null
#$scope.currentScope.remove()
$scope.waiting = false
$scope.showT = false
#$scope.data = []
## Add 2FA device
#$scope.add2FA (type) = ->
......@@ -242,7 +185,10 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
else if key.match /^(_utime|_lastAuthnUTime|_lastSeen|notification)$/
session[key] = $scope.localeDate value
else if key.match /^(_startTime|_updateTime)$/
session[key] = _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]}"
#session[key] = _stToStr value
res = []
# 2. Push session keys in result, grouped by categories
......@@ -254,7 +200,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
subres.push
title: "type"
value: "name"
sdate: "date"
epoch: "date"
array = JSON.parse(session[attr]);
for sfDevice in array
for key, value of sfDevice
......@@ -263,21 +209,17 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
if key == 'name'
name = value
if key == 'epoch'
epoch = value
newDate = new Date(value * 1000)
myDate = newDate.toLocaleString()
epoch = value
subres.push
title: title
value: name
epoch: epoch
sdate: myDate
delete session[attr]
else
subres.push
title: attr
value: session[attr]
delete session[attr]
delete session[attr]
if subres.length >0
res.push
title: "__#{category}__"
......@@ -331,10 +273,6 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
scheme = if schemes[$scope.type]
schemes[$scope.type]
# - _updateTime must be displayed as startDate
else if $scope.type == '_updateTime'
schemes._startTime
# - default to _whatToTrace scheme
else
schemes._whatToTrace
......@@ -365,18 +303,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
n.level = level + 1
n.query = query
n.over = over
# Date display in tree
if $scope.type.match /^(?:start|update)Time$/
n.title = n.value
# 12 digits -> 12:34
.replace(/^(\d{8})(\d{2})(\d{2})$/,'$2:$3')
# 11 digits -> 12:30
.replace(/^(\d{8})(\d{2})(\d)$/,'$2:$30')
# 10 digits -> 12h
.replace(/^(\d{8})(\d{2})$/,'$2h')
# 8 digits -> 2016-03-15
.replace(/^(\d{4})(\d{2})(\d{2})/,'$1-$2-$3')
node.push n
$scope.total = data.total if value == ''
$scope.waiting = false
......@@ -428,17 +355,6 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
n.query = query
n.over = over
# Date display in tree
if $scope.type.match /^(?:start|update)Time$/
n.title = n.value
# 12 digits -> 12:34
.replace(/^(\d{8})(\d{2})(\d{2})$/,'$2:$3')
# 11 digits -> 12:30
.replace(/^(\d{8})(\d{2})(\d)$/,'$2:$30')
# 10 digits -> 12h
.replace(/^(\d{8})(\d{2})$/,'$2h')
# 8 digits -> 2016-03-15
.replace(/^(\d{4})(\d{2})(\d{2})/,'$1-$2-$3')
node.push n
$scope.total = data.total if value == ''
$scope.waiting = false
......@@ -464,6 +380,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
# Default to '_whatToTrace'
c = $location.path().match /^\/(\w+)/
$scope.type = if c then c[1] else '_whatToTrace'
#$scope.myStyle = "{color: '#ffb84d'}"
]
......
......@@ -77,13 +77,14 @@ hiddenAttributes = '_password'
categories =
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen']
connectionTitle: ['ipAddr', '_timezone', '_url']
authenticationTitle:['_session_id', '_user', '_password', 'authenticationLevel', '_2fDevices']
authenticationTitle:['_session_id', '_user', '_password', 'authenticationLevel']
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti']
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump']
groups: ['groups', 'hGroups']
ldap: ['dn']
BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw']
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token']
sfaTitle: ['_2fDevices']
# Menu entries
menu =
......@@ -195,7 +196,11 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
else if key.match /^(_utime|_lastAuthnUTime|_lastSeen|notification)$/
session[key] = $scope.localeDate value
else if key.match /^(_startTime|_updateTime)$/
session[key] = _stToStr value
#session[key] = _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 = []
......@@ -203,11 +208,33 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
for category, attrs of categories
subres = []
for attr in attrs
if session[attr]
subres.push
title: attr
value: session[attr]
delete session[attr]
if session[attr] and session[attr].match(/\w+/)
if session[attr].match(/"type":\s*"(?:TOTP|U2F|UBK)"/)
subres.push
title: "type"
value: "name"
epoch: "date"
array = JSON.parse(session[attr]);
for sfDevice in array
for key, value of sfDevice
if key == 'type'
title = value
if key == 'name'
name = value
if key == 'epoch'
epoch = value
subres.push
title: title
value: name
epoch: epoch
delete session[attr]
else
subres.push
title: attr
value: session[attr]
epoch: ''
delete session[attr]
if subres.length >0
res.push
title: "__#{category}__"
......
......@@ -35,27 +35,7 @@
function(t, v) {
return "groupBy=substr(" + t + ",1)";
}, function(t, v) {
return t + "=" + v + "*&groupBy=" + t;
}, function(t, v) {
return t + "=" + v;
}
],
_startTime: [
function(t, v) {
return "groupBy=substr(" + t + ",8)";
}, function(t, v) {
return t + "=" + v + "*&groupBy=substr(" + t + ",10)";
}, function(t, v) {
return t + "=" + v + "*&groupBy=substr(" + t + ",11)";
}, function(t, v) {
return t + "=" + v + "*&groupBy=substr(" + t + ",12)";
}, function(t, v) {
return t + "=" + v + "*&groupBy=_whatToTrace";
}, function(t, v, q) {
console.log(t);
console.log(v);
console.log(q);
return q.replace(/\&groupBy.*$/, '') + ("&_whatToTrace=" + v);
return t + "=" + v + "*";
}
]
};
......@@ -67,21 +47,13 @@
} else {
return null;
}
},
ipAddr: function(t, v, level, over) {
if (level > 0 && level < 4) {
return t + "=" + v + "*&groupBy=net(" + t + "," + (16 * level + 4 * (over + 1)) + ",2)";
} else {
return null;
}
}
};
hiddenAttributes = '_password';
categories = {
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen'],
connectionTitle: ['ipAddr', '_timezone', '_url'],
dateTitle: ['_utime', '_startTime', '_updateTime'],
sfaTitle: ['_2fDevices']
};
......@@ -150,7 +122,7 @@
};
$scope.delete2FA = function(type, epoch) {
var item;
item = angular.element("#data-" + epoch);
item = angular.element(".data-" + epoch);
item.remove();
$scope.waiting = true;
$http['delete'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?type=" + type + "&epoch=" + epoch).then(function(response) {
......@@ -171,7 +143,7 @@
$scope.displaySession = function(scope) {
var sessionId, transformSession;
transformSession = function(session) {
var _insert, _stToStr, array, attr, attrs, category, epoch, i, id, k, key, len, len1, myDate, name, newDate, res, sfDevice, subres, time, title, value;
var _insert, _stToStr, array, arrayDate, attr, attrs, category, epoch, i, id, k, key, len, len1, name, pattern, res, sfDevice, subres, time, title, value;
_stToStr = function(s) {
return s;
};
......@@ -212,7 +184,9 @@
} else if (key.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)) {
session[key] = $scope.localeDate(value);
} else if (key.match(/^(_startTime|_updateTime)$/)) {
session[key] = _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];
}
}
}
......@@ -228,7 +202,7 @@
subres.push({
title: "type",
value: "name",
sdate: "date"
epoch: "date"
});
array = JSON.parse(session[attr]);
for (k = 0, len1 = array.length; k < len1; k++) {
......@@ -243,15 +217,12 @@
}
if (key === 'epoch') {
epoch = value;
newDate = new Date(value * 1000);
myDate = newDate.toLocaleString();
}
}
subres.push({
title: title,
value: name,
epoch: epoch,
sdate: myDate
epoch: epoch
});
}
delete session[attr];
......@@ -314,7 +285,7 @@
$scope.updateTree = function(value, node, level, over, currentQuery, count) {
var query, scheme, tmp;
$scope.waiting = true;
scheme = schemes[$scope.type] ? schemes[$scope.type] : $scope.type === '_updateTime' ? schemes._startTime : schemes._whatToTrace;
scheme = schemes[$scope.type] ? schemes[$scope.type] : schemes._whatToTrace;
query = scheme[level]($scope.type, value, currentQuery);
if (count > max && overScheme[$scope.type]) {
if (tmp = overScheme[$scope.type]($scope.type, value, level, over, currentQuery)) {
......@@ -341,9 +312,6 @@
n.level = level + 1;
n.query = query;
n.over = over;
if ($scope.type.match(/^(?:start|update)Time$/)) {
n.title = n.value.replace(/^(\d{8})(\d{2})(\d{2})$/, '$2:$3').replace(/^(\d{8})(\d{2})(\d)$/, '$2:$30').replace(/^(\d{8})(\d{2})$/, '$2h').replace(/^(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
}
}
node.push(n);
}
......@@ -386,9 +354,6 @@
n.level = level + 1;
n.query = query;
n.over = over;
if ($scope.type.match(/^(?:start|update)Time$/)) {
n.title = n.value.replace(/^(\d{8})(\d{2})(\d{2})$/, '$2:$3').replace(/^(\d{8})(\d{2})(\d)$/, '$2:$30').replace(/^(\d{8})(\d{2})$/, '$2h').replace(/^(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
}
}
node.push(n);
}
......
......@@ -94,13 +94,14 @@
categories = {
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen'],
connectionTitle: ['ipAddr', '_timezone', '_url'],
authenticationTitle: ['_session_id', '_user', '_password', 'authenticationLevel', '_2fDevices'],
authenticationTitle: ['_session_id', '_user', '_password', 'authenticationLevel'],
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti'],
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump'],
groups: ['groups', 'hGroups'],
ldap: ['dn'],
BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw'],
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token']
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token'],
sfaTitle: ['_2fDevices']
};
menu = {
......@@ -185,7 +186,7 @@
$scope.displaySession = function(scope) {
var sessionId, transformSession;
transformSession = function(session) {
var _insert, _stToStr, attr, attrs, category, i, id, j, k, key, l, len, len1, len2, ref, ref1, res, subres, time, tmp, value;
var _insert, _stToStr, array, arrayDate, attr, attrs, category, epoch, i, id, j, k, key, l, len, len1, len2, len3, m, name, pattern, ref, ref1, res, sfDevice, subres, time, title, tmp, value;
_stToStr = function(s) {
return s;
};
......@@ -226,7 +227,9 @@
} else if (key.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)) {
session[key] = $scope.localeDate(value);
} else if (key.match(/^(_startTime|_updateTime)$/)) {
session[key] = _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];
}
}
}
......@@ -237,12 +240,43 @@
subres = [];
for (i = 0, len = attrs.length; i < len; i++) {
attr = attrs[i];
if (session[attr]) {
subres.push({
title: attr,
value: session[attr]
});
delete session[attr];
if (session[attr] && session[attr].match(/\w+/)) {
if (session[attr].match(/"type":\s*"(?:TOTP|U2F|UBK)"/)) {
subres.push({
title: "type",
value: "name",
epoch: "date"
});
array = JSON.parse(session[attr]);
for (j = 0, len1 = array.length; j < len1; j++) {
sfDevice = array[j];
for (key in sfDevice) {
value = sfDevice[key];
if (key === 'type') {
title = value;
}
if (key === 'name') {
name = value;
}
if (key === 'epoch') {
epoch = value;
}