Merge branch '1464' into 'master'

Modify OIDC consents key structure

See merge request lemonldap-ng/lemonldap-ng!40
This commit is contained in:
Christophe Maudoux 2018-07-22 22:37:59 +02:00
commit 24cbd7dd55
25 changed files with 488 additions and 130 deletions

View File

@ -55,6 +55,70 @@ sub delSession {
return $self->sendJSONresponse( $req, { result => 1 } ); return $self->sendJSONresponse( $req, { result => 1 } );
} }
sub deleteOIDCConsent {
my ( $self, $req ) = @_;
return $self->sendJSONresponse( $req, { result => 1 } )
if ( $self->{demoMode} );
my $mod = $self->getMod($req)
or return $self->sendError( $req, undef, 400 );
my $id = $req->params('sessionId')
or return $self->sendError( $req, 'sessionId is missing', 400 );
# Try to read session
$self->logger->debug("Loading session : $id");
my $session = $self->getApacheSession( $mod, $id )
or return $self->sendError( $req, undef, 400 );
# Try to read OIDC Consent parameters
$self->logger->debug("Reading parameters ...");
my $params = $req->parameters();
my $rp = $params->{rp}
or return $self->sendError( $req, 'OIDC Consent RP is missing', 400 );
my $epoch = $params->{epoch}
or return $self->sendError( $req, 'OIDC Consent Epoch is missing', 400 );
# Try to load 2F Device(s) from session
$self->logger->debug("Looking for OIDC Consent(s) ...");
my $_oidcConsents;
if ( $session->data->{_oidcConsents} ) {
$_oidcConsents = eval {
from_json( $session->data->{_oidcConsents}, { allow_nonref => 1 } );
};
if ($@) {
$self->logger->error("Corrupted session (_oidcConsents) : $@");
return $self->p->sendError( $req, "Corrupted session", 500 );
}
}
else {
$self->logger->debug("No OIDC Consent found");
$_oidcConsents = [];
}
# Delete OIDC Consent
$self->logger->debug("Reading OIDC Consent(s) ...");
my @keep = ();
while (@$_oidcConsents) {
my $element = shift @$_oidcConsents;
$self->logger->debug(
"Searching for OIDC Consent to delete -> $rp / $epoch ...");
push @keep, $element
unless ( ( $element->{rp} eq $rp )
and ( $element->{epoch} eq $epoch ) );
}
# Update session
$self->logger->debug("Saving OIDC Consents ...");
$session->data->{_oidcConsents} = to_json( \@keep );
$self->logger->debug("Updating session ...");
$session->update( \%{ $session->data } );
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
if ( $session->error ) {
return $self->sendError( $req, $session->error, 200 );
}
return $self->sendJSONresponse( $req, { result => 1 } );
}
sub delete2F { sub delete2F {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
return $self->sendJSONresponse( $req, { result => 1 } ) return $self->sendJSONresponse( $req, { result => 1 } )
@ -100,7 +164,7 @@ sub delete2F {
while (@$_2fDevices) { while (@$_2fDevices) {
my $element = shift @$_2fDevices; my $element = shift @$_2fDevices;
$self->logger->debug( $self->logger->debug(
"Searching 2F device to delete -> $type / $epoch ..."); "Searching for 2F device to delete -> $type / $epoch ...");
push @keep, $element push @keep, $element
unless ( ( $element->{type} eq $type ) unless ( ( $element->{type} eq $type )
and ( $element->{epoch} eq $epoch ) ); and ( $element->{epoch} eq $epoch ) );

View File

@ -38,7 +38,7 @@ sub addRoutes {
# DELETE 2FA DEVICE # DELETE 2FA DEVICE
->addRoute( ->addRoute(
sfa => { ':sessionType' => { ':sessionId' => 'delete2FA' } }, sfa => { ':sessionType' => { ':sessionId' => 'del2F' } },
['DELETE'] ['DELETE']
); );
@ -68,7 +68,7 @@ sub addRoutes {
# II. 2FA METHODS # # II. 2FA METHODS #
################### ###################
sub delete2FA { sub del2F {
my ( $self, $req, $session, $skey ) = @_; my ( $self, $req, $session, $skey ) = @_;
@ -79,7 +79,7 @@ sub delete2FA {
my $type = $params->{type}; my $type = $params->{type};
my $epoch = $params->{epoch}; my $epoch = $params->{epoch};
if ( $type =~ /\b(?:U2F|TOTP|UBK)\b/ and $epoch ) { if ( $type =~ /\b(?:U2F|TOTP|UBK)\b/ and defined $epoch ) {
$self->logger->debug( $self->logger->debug(
"Call procedure delete2F with type=$type and epoch=$epoch"); "Call procedure delete2F with type=$type and epoch=$epoch");
return $self->delete2F( $req, $session, $skey ); return $self->delete2F( $req, $session, $skey );

View File

@ -38,6 +38,15 @@ sub addRoutes {
->addRoute( ->addRoute(
sessions => { ':sessionType' => { ':sessionId' => 'delSession' } }, sessions => { ':sessionType' => { ':sessionId' => 'delSession' } },
['DELETE'] ['DELETE']
)
# DELETE OIDC CONSENT
->addRoute(
sessions => {
OIDCConsent =>
{ ':sessionType' => { ':sessionId' => 'delOIDCConsent' } }
},
['DELETE']
); );
$self->setTypes($conf); $self->setTypes($conf);
@ -48,9 +57,34 @@ sub addRoutes {
} }
####################### #######################
# II. DISPLAY METHODS # # II. CONSENT METHODS #
####################### #######################
sub delOIDCConsent {
my ( $self, $req, $session, $skey ) = @_;
my $mod = $self->getMod($req)
or return $self->sendError( $req, undef, 400 );
my $params = $req->parameters();
my $epoch = $params->{epoch};
my $rp = $params->{rp};
if ( $rp =~ /\b[\w-]+\b/ and defined $epoch ) {
$self->logger->debug(
"Call procedure deleteOIDCConsent with RP=$rp and epoch=$epoch");
return $self->deleteOIDCConsent( $req, $session, $skey );
}
else {
return $self->sendError( $req, undef, 400 );
}
}
########################
# III. DISPLAY METHODS #
########################
sub sessions { sub sessions {
my ( $self, $req, $session, $skey ) = @_; my ( $self, $req, $session, $skey ) = @_;

View File

@ -85,6 +85,7 @@ categories =
BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw'] BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw']
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token'] OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token']
sfaTitle: ['_2fDevices'] sfaTitle: ['_2fDevices']
oidcConsents: ['_oidcConsents']
# Menu entries # Menu entries
menu = menu =
@ -139,6 +140,19 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
# SESSION MANAGEMENT # SESSION MANAGEMENT
# Delete RP Consent
$scope.deleteOIDCConsent = (rp, epoch) ->
item = angular.element(".data-#{epoch}")
item.remove()
$scope.waiting = true
$http['delete']("#{scriptname}sessions/OIDCConsent/#{sessionType}/#{$scope.currentSession.id}?rp=#{rp}&epoch=#{epoch}").then (response) ->
$scope.waiting = false
, (resp) ->
$scope.waiting = false
$scope.showT = false
# Delete # Delete
$scope.deleteSession = -> $scope.deleteSession = ->
$scope.waiting = true $scope.waiting = true
@ -213,6 +227,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
title: "type" title: "type"
value: "name" value: "name"
epoch: "date" epoch: "date"
td: "0"
array = JSON.parse(session[attr]) array = JSON.parse(session[attr])
for sfDevice in array for sfDevice in array
for key, value of sfDevice for key, value of sfDevice
@ -226,6 +241,28 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
title: title title: title
value: name value: name
epoch: epoch epoch: epoch
td: "1"
delete session[attr]
else if session[attr].toString().match(/"rp":\s*"[\w-]+"/)
subres.push
title: "rp"
value: "scope"
epoch: "date"
td: "0"
array = JSON.parse(session[attr])
for oidcConsent in array
for key, value of oidcConsent
if key == 'rp'
title = value
if key == 'scope'
name = value
if key == 'epoch'
epoch = value
subres.push
title: title
value: name
epoch: epoch
td: "1"
delete session[attr] delete session[attr]
else if session[attr].toString().match(/\w+/) else if session[attr].toString().match(/\w+/)
subres.push subres.push

View File

@ -4,7 +4,7 @@
</div> </div>
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr><th trspan="oidcOPName"></th></tr> <tr><th trspan="oidcOPName"></th><th></th></tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="s in currentNode.nodes"> <tr ng-repeat="s in currentNode.nodes">

View File

@ -4,7 +4,7 @@
</div> </div>
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr><th trspan="oidcRPName"></th></tr> <tr><th trspan="oidcRPName"></th><th></th></tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="s in currentNode.nodes"> <tr ng-repeat="s in currentNode.nodes">

View File

@ -101,7 +101,8 @@
ldap: ['dn'], ldap: ['dn'],
BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw'], BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw'],
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token'], OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token'],
sfaTitle: ['_2fDevices'] sfaTitle: ['_2fDevices'],
oidcConsents: ['_oidcConsents']
}; };
menu = { menu = {
@ -163,6 +164,18 @@
} }
return $scope.showM = false; return $scope.showM = false;
}; };
$scope.deleteOIDCConsent = function(rp, epoch) {
var item;
item = angular.element(".data-" + epoch);
item.remove();
$scope.waiting = true;
$http['delete'](scriptname + "sessions/OIDCConsent/" + sessionType + "/" + $scope.currentSession.id + "?rp=" + rp + "&epoch=" + epoch).then(function(response) {
return $scope.waiting = false;
}, function(resp) {
return $scope.waiting = false;
});
return $scope.showT = false;
};
$scope.deleteSession = function() { $scope.deleteSession = function() {
$scope.waiting = true; $scope.waiting = true;
return $http['delete'](scriptname + "sessions/" + sessionType + "/" + $scope.currentSession.id).then(function(response) { return $http['delete'](scriptname + "sessions/" + sessionType + "/" + $scope.currentSession.id).then(function(response) {
@ -186,7 +199,7 @@
$scope.displaySession = function(scope) { $scope.displaySession = function(scope) {
var sessionId, transformSession; var sessionId, transformSession;
transformSession = function(session) { transformSession = function(session) {
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; var _insert, _stToStr, array, arrayDate, attr, attrs, category, epoch, i, id, j, k, key, l, len, len1, len2, len3, len4, m, name, o, oidcConsent, pattern, ref, ref1, res, sfDevice, subres, time, title, tmp, value;
_stToStr = function(s) { _stToStr = function(s) {
return s; return s;
}; };
@ -246,7 +259,8 @@
subres.push({ subres.push({
title: "type", title: "type",
value: "name", value: "name",
epoch: "date" epoch: "date",
td: "0"
}); });
array = JSON.parse(session[attr]); array = JSON.parse(session[attr]);
for (j = 0, len1 = array.length; j < len1; j++) { for (j = 0, len1 = array.length; j < len1; j++) {
@ -266,7 +280,38 @@
subres.push({ subres.push({
title: title, title: title,
value: name, value: name,
epoch: epoch epoch: epoch,
td: "1"
});
}
delete session[attr];
} else if (session[attr].toString().match(/"rp":\s*"[\w-]+"/)) {
subres.push({
title: "rp",
value: "scope",
epoch: "date",
td: "0"
});
array = JSON.parse(session[attr]);
for (k = 0, len2 = array.length; k < len2; k++) {
oidcConsent = array[k];
for (key in oidcConsent) {
value = oidcConsent[key];
if (key === 'rp') {
title = value;
}
if (key === 'scope') {
name = value;
}
if (key === 'epoch') {
epoch = value;
}
}
subres.push({
title: title,
value: name,
epoch: epoch,
td: "1"
}); });
} }
delete session[attr]; delete session[attr];
@ -297,8 +342,8 @@
tmp = []; tmp = [];
if (session._loginHistory.successLogin) { if (session._loginHistory.successLogin) {
ref = session._loginHistory.successLogin; ref = session._loginHistory.successLogin;
for (k = 0, len2 = ref.length; k < len2; k++) { for (m = 0, len3 = ref.length; m < len3; m++) {
l = ref[k]; l = ref[m];
tmp.push({ tmp.push({
t: l._utime, t: l._utime,
title: $scope.localeDate(l._utime), title: $scope.localeDate(l._utime),
@ -308,8 +353,8 @@
} }
if (session._loginHistory.failedLogin) { if (session._loginHistory.failedLogin) {
ref1 = session._loginHistory.failedLogin; ref1 = session._loginHistory.failedLogin;
for (m = 0, len3 = ref1.length; m < len3; m++) { for (o = 0, len4 = ref1.length; o < len4; o++) {
l = ref1[m]; l = ref1[o];
tmp.push({ tmp.push({
t: l._utime, t: l._utime,
title: $scope.localeDate(l._utime), title: $scope.localeDate(l._utime),

File diff suppressed because one or more lines are too long

View File

@ -455,6 +455,7 @@
"oldValue":"قيمة قديمة", "oldValue":"قيمة قديمة",
"on":"تنشيط", "on":"تنشيط",
"oidcAuthnLevel":"مستوى إثبات الهوية", "oidcAuthnLevel":"مستوى إثبات الهوية",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":" أوبين أيدي كونيكت بروفيدر", "oidcOP":" أوبين أيدي كونيكت بروفيدر",
"oidcOPMetaDataExportedVars":"السمات المصدرة", "oidcOPMetaDataExportedVars":"السمات المصدرة",
"oidcOPMetaDataJSON":"البيانات الوصفية", "oidcOPMetaDataJSON":"البيانات الوصفية",
@ -656,12 +657,14 @@
"restSessionServer":"خادم جلسة ريست", "restSessionServer":"خادم جلسة ريست",
"restUserDBUrl":"عنوان يو آر إل لبيانات المستخدم", "restUserDBUrl":"عنوان يو آر إل لبيانات المستخدم",
"returnUrl":"إرجاع اليو آر إل", "returnUrl":"إرجاع اليو آر إل",
"rp":"Relying Party",
"rule":"القاعدة", "rule":"القاعدة",
"rules":"القواعد", "rules":"القواعد",
"Same":"نفسه", "Same":"نفسه",
"save":"حفظ", "save":"حفظ",
"saveReport":"احفظ التقرير", "saveReport":"احفظ التقرير",
"savingConfirmation":"حفظ التأكيد", "savingConfirmation":"حفظ التأكيد",
"scope":"Scope",
"search":"Search ...", "search":"Search ...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"ملفات تعريف الارتباط المضمونة (سسل)", "securedCookie":"ملفات تعريف الارتباط المضمونة (سسل)",
@ -676,7 +679,7 @@
"sessionStartedAt":"بدأت الجلسة", "sessionStartedAt":"بدأت الجلسة",
"sessionStorage":"تخزين الجلسات", "sessionStorage":"تخزين الجلسات",
"sessionTitle":"محتوى الجلسة", "sessionTitle":"محتوى الجلسة",
"sfaTitle":"Seconds Factors Authentication", "sfaTitle":"Second Factors Authentication",
"show":"عرض", "show":"عرض",
"showHelp":"عرض المساعدة", "showHelp":"عرض المساعدة",
"singleIP":"عنوان آي بي واحد لكل مستخدم", "singleIP":"عنوان آي بي واحد لكل مستخدم",

View File

@ -455,6 +455,7 @@
"oldValue":"Old value", "oldValue":"Old value",
"on":"On", "on":"On",
"oidcAuthnLevel":"Authentication level", "oidcAuthnLevel":"Authentication level",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":"OpenID Connect Provider", "oidcOP":"OpenID Connect Provider",
"oidcOPMetaDataExportedVars":"Exported attributes", "oidcOPMetaDataExportedVars":"Exported attributes",
"oidcOPMetaDataJSON":"Metadata", "oidcOPMetaDataJSON":"Metadata",
@ -656,12 +657,14 @@
"restSessionServer":"REST session server", "restSessionServer":"REST session server",
"restUserDBUrl":"User data URL", "restUserDBUrl":"User data URL",
"returnUrl":"Return URL", "returnUrl":"Return URL",
"rp":"Relying Party",
"rule":"Rule", "rule":"Rule",
"rules":"Rules", "rules":"Rules",
"Same":"Same", "Same":"Same",
"save":"Save", "save":"Save",
"saveReport":"Save report", "saveReport":"Save report",
"savingConfirmation":"Saving confirmation", "savingConfirmation":"Saving confirmation",
"scope":"Scope",
"search":"Search ...", "search":"Search ...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"Secured Cookie (SSL)", "securedCookie":"Secured Cookie (SSL)",
@ -676,7 +679,7 @@
"sessionStartedAt":"Session started on", "sessionStartedAt":"Session started on",
"sessionStorage":"Sessions Storage", "sessionStorage":"Sessions Storage",
"sessionTitle":"Session content", "sessionTitle":"Session content",
"sfaTitle":"Seconds Factors Authentication", "sfaTitle":"Second Factors Authentication",
"show":"Show", "show":"Show",
"showHelp":"Show help", "showHelp":"Show help",
"singleIP":"One IP only by user", "singleIP":"One IP only by user",

View File

@ -455,6 +455,7 @@
"oldValue":"Ancienne valeur", "oldValue":"Ancienne valeur",
"on":"Activé", "on":"Activé",
"oidcAuthnLevel":"Niveau d'authentification", "oidcAuthnLevel":"Niveau d'authentification",
"oidcConsents":"Consentements OpenID Connect",
"oidcOP":"Fournisseur OpenID Connect", "oidcOP":"Fournisseur OpenID Connect",
"oidcOPMetaDataExportedVars":"Attributs exportés", "oidcOPMetaDataExportedVars":"Attributs exportés",
"oidcOPMetaDataJSON":"Metadonnées", "oidcOPMetaDataJSON":"Metadonnées",
@ -656,12 +657,14 @@
"restSessionServer":"Serveur de sessions REST", "restSessionServer":"Serveur de sessions REST",
"restUserDBUrl":"URL de données utilisateurs", "restUserDBUrl":"URL de données utilisateurs",
"returnUrl":"URL de retour", "returnUrl":"URL de retour",
"rp":"Client",
"rule":"Règle", "rule":"Règle",
"rules":"Règles", "rules":"Règles",
"Same":"Identique", "Same":"Identique",
"save":"Sauver", "save":"Sauver",
"saveReport":"Rapport de sauvegarde", "saveReport":"Rapport de sauvegarde",
"savingConfirmation":"Confirmation de sauvegarde", "savingConfirmation":"Confirmation de sauvegarde",
"scope":"Scope",
"search":"Rechercher ...", "search":"Rechercher ...",
"secondFactors":"Seconds facteurs", "secondFactors":"Seconds facteurs",
"securedCookie":"Cookie sécurisé (HTTPS)", "securedCookie":"Cookie sécurisé (HTTPS)",

View File

@ -455,6 +455,7 @@
"oldValue":"Vecchio valore", "oldValue":"Vecchio valore",
"on":"On", "on":"On",
"oidcAuthnLevel":"Livello di autenticazione", "oidcAuthnLevel":"Livello di autenticazione",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":"Provider di OpenID Connect", "oidcOP":"Provider di OpenID Connect",
"oidcOPMetaDataExportedVars":"Attributi esportati", "oidcOPMetaDataExportedVars":"Attributi esportati",
"oidcOPMetaDataJSON":"Metadata", "oidcOPMetaDataJSON":"Metadata",
@ -656,12 +657,14 @@
"restSessionServer":"Server di sessione REST", "restSessionServer":"Server di sessione REST",
"restUserDBUrl":"URL dei dati utente", "restUserDBUrl":"URL dei dati utente",
"returnUrl":"URL di ritorno", "returnUrl":"URL di ritorno",
"rp":"Relying Party",
"rule":"Regola", "rule":"Regola",
"rules":"Regole", "rules":"Regole",
"Same":"Stesso", "Same":"Stesso",
"save":"Salva", "save":"Salva",
"saveReport":"Salva report", "saveReport":"Salva report",
"savingConfirmation":"Salvataggio della conferma", "savingConfirmation":"Salvataggio della conferma",
"scope":"Scope",
"search":"Search ...", "search":"Search ...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"Cookie protetti (SSL)", "securedCookie":"Cookie protetti (SSL)",
@ -676,7 +679,7 @@
"sessionStartedAt":"La sessione è stata avviata", "sessionStartedAt":"La sessione è stata avviata",
"sessionStorage":"Conservazione di sessioni", "sessionStorage":"Conservazione di sessioni",
"sessionTitle":"Contenuto della sessione", "sessionTitle":"Contenuto della sessione",
"sfaTitle":"Seconds Factors Authentication", "sfaTitle":"Second Factors Authentication",
"show":"Mostra", "show":"Mostra",
"showHelp":"Mostra aiuto", "showHelp":"Mostra aiuto",
"singleIP":"Solo un IP per utente", "singleIP":"Solo un IP per utente",

View File

@ -455,6 +455,7 @@
"oldValue":"Giá trị cũ", "oldValue":"Giá trị cũ",
"on":"Vào", "on":"Vào",
"oidcAuthnLevel":"Mức xác thực", "oidcAuthnLevel":"Mức xác thực",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":"Bộ cung cấp Kết nối OpenID", "oidcOP":"Bộ cung cấp Kết nối OpenID",
"oidcOPMetaDataExportedVars":"Biến đã được xuất", "oidcOPMetaDataExportedVars":"Biến đã được xuất",
"oidcOPMetaDataJSON":"Mô-tả dữ liệu", "oidcOPMetaDataJSON":"Mô-tả dữ liệu",
@ -656,12 +657,14 @@
"restSessionServer":"Máy chủ phiên REST", "restSessionServer":"Máy chủ phiên REST",
"restUserDBUrl":"URL dữ liệu người dùng", "restUserDBUrl":"URL dữ liệu người dùng",
"returnUrl":"Trả lại URL", "returnUrl":"Trả lại URL",
"rp":"Relying Party",
"rule":"Quy tắc", "rule":"Quy tắc",
"rules":"Quy tắc", "rules":"Quy tắc",
"Same":"Tương tự", "Same":"Tương tự",
"save":"Lưu", "save":"Lưu",
"saveReport":"Lưu báo cáo", "saveReport":"Lưu báo cáo",
"savingConfirmation":"Lưu xác nhận", "savingConfirmation":"Lưu xác nhận",
"scope":"Scope",
"search":"Search ...", "search":"Search ...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"Cookie bảo mật (SSL)", "securedCookie":"Cookie bảo mật (SSL)",
@ -676,7 +679,7 @@
"sessionStartedAt":"Phiên bắt đầu lúc", "sessionStartedAt":"Phiên bắt đầu lúc",
"sessionStorage":"Sessions lưu trữ", "sessionStorage":"Sessions lưu trữ",
"sessionTitle":"Nội dung phiên", "sessionTitle":"Nội dung phiên",
"sfaTitle":"Seconds Factors Authentication", "sfaTitle":"Second Factors Authentication",
"show":"Hiển thị", "show":"Hiển thị",
"showHelp":"Hiển thị trợ giúp", "showHelp":"Hiển thị trợ giúp",
"singleIP":"Chỉ một địa chỉ IP bởi người dùng", "singleIP":"Chỉ một địa chỉ IP bởi người dùng",

View File

@ -91,14 +91,18 @@
</table> </table>
</div> </div>
<div ng-if="!node.nodes"> <div ng-if="!node.nodes">
<th ng-if="node.title!='UBK' && node.title!='TOTP' && node.title!='U2F'">{{translate(node.title)}}</th> <th ng-if="node.td!='1'">{{translate(node.title)}}</th>
<td ng-if="node.title!='type' && node.title!='UBK' && node.title!='TOTP' && node.title!='U2F' " >${{node.title}}</td> <td class="data-{{node.epoch}}" ng-if="node.td=='1'">{{node.title}}</td>
<td ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F'">{{node.title}}</td> <th ng-if="node.title=='type' || node.title=='rp'">{{translate(node.value)}}</th>
<th ng-if="node.title=='type'">{{translate(node.value)}}</th> <td class="col-md-3 data-{{node.epoch}}" ng-if="node.title!='type' && node.title!='rp'" >{{node.value}}</td>
<td class="col-md-3" ng-if="node.title!='type'" >{{node.value}}</td> <th ng-if="node.title=='type' || node.title=='rp'">{{translate(node.epoch)}}</th>
<th ng-if="node.title=='type'">{{translate(node.epoch)}}</th> <td class="col-md-3 data-{{node.epoch}}" ng-if="node.epoch > 1500000000">{{localeDate(node.epoch)}}</td>
<td class="col-md-3" ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F'">{{localeDate(node.epoch)}}</td> <td class="data-{{node.epoch}}">
<td></td> <span ng-if="node.td=='1'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="deleteOIDCConsent(node.title, node.epoch)"></span>
<!--
<span ng-if="$last && ( node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' )" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"></span>
-->
</td>
</div> </div>
</script> </script>

View File

@ -32,11 +32,12 @@ Use any of Plack launcher. Example:
=head1 DESCRIPTION =head1 DESCRIPTION
Lemonldap::NG is a modular Web-SSO based on Apache::Session modules. It Lemonldap::NG is a modular Web-SSO based on Apache::Session modules. It
simplifies the build of a protected area with a few changes in the application. provides an easy way to build a secured area to protect applications with
very few changes.
It manages both authentication and authorization and provides headers for Lemonldap::NG manages both authentication and authorization. Furthermore
accounting. So you can have a full AAA protection for your web space as it provides headers for accounting. So you can have a full AAA protection
described below. for your web space as described below.
Lemonldap::NG::Portal provides portal components. See Lemonldap::NG::Portal provides portal components. See
L<http://lemonldap-ng.org> for more. L<http://lemonldap-ng.org> for more.
@ -44,16 +45,16 @@ L<http://lemonldap-ng.org> for more.
=head1 KINEMATICS =head1 KINEMATICS
The portal object is based on L<Lemonldap::NG::Handler::Try>: underlying The portal object is based on L<Lemonldap::NG::Handler::Try>: underlying
handler tries to authenticate user and then follow the routes (auth/unauth) handler tries to authenticate user and follows initialized auth / unauth
declared during initialization. routes.
=head2 Initialization =head2 Initialization
The initialisation process subscribes portal to handler configuration reload and Initialization process subscribes portal to handler configuration reload and
ask for handler initialization (L<Lemonldap::NG::Portal::Main::Init>). requests handler initialization (L<Lemonldap::NG::Portal::Main::Init>).
So configuration read is triggered by handler at each reload. So configuration is read by handler at each reload.
During configuration reload, every enabled components are loaded as plugins: During configuration reload, each enabled components are loaded as plugins:
=over =over
@ -65,7 +66,7 @@ During configuration reload, every enabled components are loaded as plugins:
=back =back
init() is called for each plugin. If one plugin initialization fails (init() init() is called for each plugin. If a plugin initialization fails (init()
returns 0), the portal responds a 500 status code for each request. returns 0), the portal responds a 500 status code for each request.
See L<Lemonldap::NG::Portal::Main::Plugin> to see how to write modules. See L<Lemonldap::NG::Portal::Main::Plugin> to see how to write modules.
@ -73,7 +74,7 @@ See L<Lemonldap::NG::Portal::Main::Plugin> to see how to write modules.
=head2 Main route =head2 Main route
The "/" route is declared in L<Lemonldap::NG::Portal::Main::Init>. It points to The "/" route is declared in L<Lemonldap::NG::Portal::Main::Init>. It points to
different methods in L<Lemonldap::NG::Portal::Main::Run>. Theses methods choose different methods in L<Lemonldap::NG::Portal::Main::Run>. Theses methods select
methods to call in the process and call do(). methods to call in the process and call do().
do() stores methods to call in $req->steps and launches do() stores methods to call in $req->steps and launches
@ -81,18 +82,18 @@ Lemonldap::NG::Portal::Main::Process::process(). This method removes each method
stored in $req->steps and launches it. If the result is PE_OK, process() stored in $req->steps and launches it. If the result is PE_OK, process()
continues, else it returns the error code. continues, else it returns the error code.
If the request was an Ajax one, do() responds in JSON format else it manages If it is an Ajax request, do() responds in JSON format else it manages
redirection if any. Else it calls redirection if any. Else it calls
Lemonldap::NG::Portal::Main::Display::display() to have template and arguments, Lemonldap::NG::Portal::Main::Display::display() to load template and arguments,
then it launch Lemonldap::NG::Common::PSGI::sendHtml() with them. and launches Lemonldap::NG::Common::PSGI::sendHtml() using them.
=head1 DEVELOPER INSTRUCTIONS =head1 DEVELOPER INSTRUCTIONS
Portal main object is defined in Lemonldap::NG::Portal::Main::* classes. Other Portal main object is defined in Lemonldap::NG::Portal::Main::* classes. Other
components are plugins. Plugins must not store any hash key in the main object. components are plugins. Plugins do not have to store any hash key in main object.
Main and plugin keys must be initializated during initialization. They must Main and plugin keys must be set during initialization process. They must
be read-only during receiving requests. be read-only during requests receiving.
The L<Lemonldap::NG::Portal::Main::Request> request has fixed keys. A plugin The L<Lemonldap::NG::Portal::Main::Request> request has fixed keys. A plugin
that wants to store a temporary key must store it in C<$req-E<gt>data> or use that wants to store a temporary key must store it in C<$req-E<gt>data> or use
@ -100,10 +101,11 @@ defined keys, but it must never create a root key. Plugin keys may have
explicit names to avoid conflicts. explicit names to avoid conflicts.
Whole configuration is always available. It is stored in $self->conf. It must Whole configuration is always available. It is stored in $self->conf. It must
not be modified by anyone even during initialization or receiving request not be modified by any components even during initialization process or
(during initialization, copy the value in the plugin namespace instead). receiving request (during initialization, copy the value in the plugin
namespace instead).
All plugins can dial with the portal methods using $self->p which points to All plugins can access to portal methods using $self->p which points to
portal main object. Some main methods are mapped to the plugin namespace: portal main object. Some main methods are mapped to the plugin namespace:
=over =over
@ -118,8 +120,7 @@ portal main object. Some main methods are mapped to the plugin namespace:
=head1 SEE ALSO =head1 SEE ALSO
Most of the documentation is available on the website Most of the documentation is available on L<http://lemonldap-ng.org> website
L<http://lemonldap-ng.org>
=head2 OTHER POD FILES =head2 OTHER POD FILES

View File

@ -178,9 +178,6 @@ sub run {
foreach ( @{ $req->data->{crypter} } ) { foreach ( @{ $req->data->{crypter} } ) {
my $k = push @rk, my $k = push @rk,
{ keyHandle => $_->{keyHandle}, version => $data->{version} }; { keyHandle => $_->{keyHandle}, version => $data->{version} };
#{ keyHandle => $_->{keyHandle}, version => $challenge->{version} };
} }
# Serialize data # Serialize data

View File

@ -1,7 +1,7 @@
package Lemonldap::NG::Portal::Issuer::OpenIDConnect; package Lemonldap::NG::Portal::Issuer::OpenIDConnect;
use strict; use strict;
use JSON; use JSON qw(from_json to_json);
use Mouse; use Mouse;
use Lemonldap::NG::Common::FormEncode; use Lemonldap::NG::Common::FormEncode;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
@ -115,6 +115,10 @@ sub run {
my ( $self, $req, $path ) = @_; my ( $self, $req, $path ) = @_;
if ($path) { if ($path) {
# Convert old format OIDC Consents
my $ConvertedConsents = $self->_convertOldFormatConsents($req);
$self->logger->debug("$ConvertedConsents consent(s) converted");
# AUTHORIZE # AUTHORIZE
if ( $path eq $self->conf->{oidcServiceMetaDataAuthorizeURI} ) { if ( $path eq $self->conf->{oidcServiceMetaDataAuthorizeURI} ) {
$self->logger->debug( $self->logger->debug(
@ -211,15 +215,15 @@ sub run {
return PE_UNAUTHORIZEDPARTNER; return PE_UNAUTHORIZEDPARTNER;
} }
else { else {
$self->logger->debug("Client id $client_id match RP $rp"); $self->logger->debug("Client id $client_id matches RP $rp");
} }
# Check if this RP is authorizated # Check if this RP is authorized
if ( my $rule = $self->spRules->{$rp} ) { if ( my $rule = $self->spRules->{$rp} ) {
unless ( $rule->( $req, $req->sessionInfo ) ) { unless ( $rule->( $req, $req->sessionInfo ) ) {
$self->userLogger->warn( 'User ' $self->userLogger->warn( 'User '
. $req->sessionInfo->{ $self->conf->{whatToTrace} } . $req->sessionInfo->{ $self->conf->{whatToTrace} }
. "was not authorizated to access to $rp" ); . "was not authorized to access to $rp" );
return PE_UNAUTHORIZEDPARTNER; return PE_UNAUTHORIZEDPARTNER;
} }
} }
@ -288,7 +292,7 @@ sub run {
) )
{ {
$self->logger->debug( $self->logger->debug(
"Reauthentication requested by Relying Party in prompt parameter" "Reauthentication required by Relying Party in prompt parameter"
); );
return $self->reAuth($req); return $self->reAuth($req);
} }
@ -297,7 +301,7 @@ sub run {
my $_lastAuthnUTime = $req->{sessionInfo}->{_lastAuthnUTime}; my $_lastAuthnUTime = $req->{sessionInfo}->{_lastAuthnUTime};
if ( $max_age && time > $_lastAuthnUTime + $max_age ) { if ( $max_age && time > $_lastAuthnUTime + $max_age ) {
$self->logger->debug( $self->logger->debug(
"Reauthentication forced cause authentication time ($_lastAuthnUTime) is too old (>$max_age s)" "Reauthentication forced because authentication time ($_lastAuthnUTime) is too old (>$max_age s)"
); );
return $self->reAuth($req); return $self->reAuth($req);
} }
@ -320,11 +324,11 @@ sub run {
) )
{ {
$self->logger->error( $self->logger->error(
"Request JWT signature could not be verified"); "JWT signature request can not be verified");
return PE_ERROR; return PE_ERROR;
} }
else { else {
$self->logger->debug("Request JWT signature verified"); $self->logger->debug("JWT signature request verified");
} }
} }
@ -343,12 +347,12 @@ sub run {
my $user_id = $req->{sessionInfo}->{$user_id_attribute}; my $user_id = $req->{sessionInfo}->{$user_id_attribute};
unless ( $sub eq $user_id ) { unless ( $sub eq $user_id ) {
$self->userLogger->error( $self->userLogger->error(
"ID Token hint sub $sub do not match user $user_id"); "ID Token hint sub $sub does not match user $user_id");
return $self->returnRedirectError( return $self->returnRedirectError(
$req, $req,
$oidc_request->{'redirect_uri'}, $oidc_request->{'redirect_uri'},
'invalid_request', 'invalid_request',
"current user do not match id_token_hint sub", "Current user does not match id_token_hint sub",
undef, undef,
$oidc_request->{'state'}, $oidc_request->{'state'},
( $flow ne "authorizationcode" ) ( $flow ne "authorizationcode" )
@ -356,7 +360,7 @@ sub run {
} }
else { else {
$self->logger->debug( $self->logger->debug(
"ID Token hint sub $sub match current user"); "ID Token hint sub $sub matches current user");
} }
} }
@ -365,19 +369,48 @@ sub run {
->{oidcRPMetaDataOptionsBypassConsent}; ->{oidcRPMetaDataOptionsBypassConsent};
if ($bypassConsent) { if ($bypassConsent) {
$self->logger->debug( $self->logger->debug(
"Consent is disabled for RP $rp, user will not be prompted" "Consent is disabled for Relying Party $rp, user will not be prompted"
); );
} }
else { else {
my $ask_for_consent = 1; my $ask_for_consent = 1;
if ( $req->{sessionInfo}->{"_oidc_consent_time_$rp"} my $_oidcConsents;
and $req->{sessionInfo}->{"_oidc_consent_scope_$rp"} ) my @RPoidcConsent = ();
{
# Loading existing oidcConsents
$self->logger->debug("Looking for OIDC Consents ...");
if ( $req->{sessionInfo}->{_oidcConsents} ) {
$_oidcConsents = eval {
from_json( $req->{sessionInfo}->{_oidcConsents},
{ allow_nonref => 1 } );
};
if ($@) {
$self->logger->error(
"Corrupted session (_oidcConsents): $@");
return PE_ERROR;
}
}
else {
$self->logger->debug("No OIDC Consent found");
$_oidcConsents = [];
}
# Read existing RP
@RPoidcConsent = grep { $_->{rp} eq $rp } @$_oidcConsents;
unless (@RPoidcConsent) {
$self->logger->debug("No Relying Party $rp Consent found");
# Set default value
push @RPoidcConsent,
{ rp => $rp, epoch => '', scope => '' };
}
if ( $RPoidcConsent[0]{rp} eq $rp ) {
$ask_for_consent = 0; $ask_for_consent = 0;
my $consent_time =
$req->{sessionInfo}->{"_oidc_consent_time_$rp"}; my $consent_time = $RPoidcConsent[0]{epoch};
my $consent_scope = my $consent_scope = $RPoidcConsent[0]{scope};
$req->{sessionInfo}->{"_oidc_consent_scope_$rp"};
$self->logger->debug( $self->logger->debug(
"Consent already given for Relying Party $rp (time: $consent_time, scope: $consent_scope)" "Consent already given for Relying Party $rp (time: $consent_time, scope: $consent_scope)"
@ -408,15 +441,14 @@ sub run {
if ( $req->param('confirm') if ( $req->param('confirm')
and $req->param('confirm') == 1 ) and $req->param('confirm') == 1 )
{ {
$RPoidcConsent[0]{epoch} = time;
$RPoidcConsent[0]{scope} = $oidc_request->{'scope'};
push @{$_oidcConsents}, @RPoidcConsent;
$self->logger->debug(
"Append Relying Party $rp Consent");
$self->p->updatePersistentSession( $req, $self->p->updatePersistentSession( $req,
{ "_oidc_consent_time_$rp" => time } ); { _oidcConsents => to_json($_oidcConsents) } );
$self->p->updatePersistentSession(
$req,
{
"_oidc_consent_scope_$rp" =>
$oidc_request->{'scope'}
}
);
$self->logger->debug( $self->logger->debug(
"Consent given for Relying Party $rp"); "Consent given for Relying Party $rp");
} }
@ -437,12 +469,13 @@ sub run {
} }
else { else {
$self->logger->debug( $self->logger->debug(
"Obtain user consent for Relying Party $rp"); "Request user consent for Relying Party $rp");
# Return error if prompt is none # Return error if prompt is none
if ( $prompt and $prompt =~ /\bnone\b/ ) { if ( $prompt and $prompt =~ /\bnone\b/ ) {
$self->logger->debug( $self->logger->debug(
"Consent is needed but prompt is none"); "Consent is requiered but prompt is set to none"
);
return $self->returnRedirectError( return $self->returnRedirectError(
$req, $req,
$oidc_request->{'redirect_uri'}, $oidc_request->{'redirect_uri'},
@ -776,7 +809,7 @@ sub run {
return PE_REDIRECT; return PE_REDIRECT;
} }
$self->logger->debug("No flow has been selected"); $self->logger->debug("None flow has been selected");
return PE_OK; return PE_OK;
} }
@ -898,7 +931,7 @@ sub token {
return $self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
else { else {
$self->logger->debug("Client id $client_id match RP $rp"); $self->logger->debug("Client id $client_id match Relying Party $rp");
} }
# Check client_secret # Check client_secret
@ -924,7 +957,7 @@ sub token {
# Check we have the same redirect_uri value # Check we have the same redirect_uri value
unless ( $req->param("redirect_uri") eq $codeSession->data->{redirect_uri} ) unless ( $req->param("redirect_uri") eq $codeSession->data->{redirect_uri} )
{ {
$self->userLogger->error( "Provided redirect_uri is different from " $self->userLogger->error( "Provided redirect_uri does not match "
. $codeSession->{redirect_uri} ); . $codeSession->{redirect_uri} );
return $self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
@ -1290,7 +1323,7 @@ sub logout {
foreach my $rp (@rps) { foreach my $rp (@rps) {
my $rpConf = $self->conf->{oidcRPMetaDataOptions}->{$rp}; my $rpConf = $self->conf->{oidcRPMetaDataOptions}->{$rp};
unless ($rpConf) { unless ($rpConf) {
$self->logger->error("Unknown RP $rp"); $self->logger->error("Unknown Relying Party $rp");
return PE_ERROR; return PE_ERROR;
} }
if ( my $url = $rpConf->{oidcRPMetaDataOptionsLogoutUrl} ) { if ( my $url = $rpConf->{oidcRPMetaDataOptionsLogoutUrl} ) {
@ -1455,4 +1488,58 @@ sub exportRequestParameters {
return PE_OK; return PE_OK;
} }
sub _convertOldFormatConsents {
my ( $self, $req ) = @_;
my @oidcConsents = ();
my @rps = ();
my $scope = '';
my $epoch = '';
my $rp = '';
unless ( $req->{sessionInfo} ) {
$self->logger->error("Corrupted session");
return PE_ERROR;
}
# Search Relying Party
$self->logger->debug("Searching for Relying Party...");
foreach ( keys %{ $req->{sessionInfo} } ) {
if ( $_ =~ /^_oidc_consent_scope_([\w-]+)$/ ) {
push @rps, $1;
$self->logger->debug("Found RP $1");
}
}
# Convert OIDC Consents format
$self->logger->debug("Convert Relying Party...");
my $count = 0;
foreach (@rps) {
$scope = $req->{sessionInfo}->{ "_oidc_consent_scope_" . $_ };
$epoch = $req->{sessionInfo}->{ "_oidc_consent_time_" . $_ };
$rp = $_;
if ( defined $scope and defined $epoch and defined $rp ) {
$self->logger->debug("Append RP $rp Consent");
push @oidcConsents, { rp => $rp, scope => $scope, epoch => $epoch };
$count++;
$self->logger->debug("Delete Key -> _oidc_consent_scope_$_");
$self->p->updatePersistentSession( $req,
{ "_oidc_consent_scope_" . $_ => undef } );
$self->logger->debug("Delete Key -> _oidc_consent_time_$_");
$self->p->updatePersistentSession( $req,
{ "_oidc_consent_time_" . $_ => undef } );
}
else {
$self->logger->debug(
"Corrupted Consent / Session -> RP=$rp, Scope=$scope, Epoch=$epoch"
);
return PE_ERROR;
}
}
# Update persistent session
$self->p->updatePersistentSession( $req,
{ _oidcConsents => to_json( \@oidcConsents ) } );
return $count;
}
1; 1;

View File

@ -7,6 +7,7 @@ our $VERSION = '2.0.0';
package Lemonldap::NG::Portal::Main; package Lemonldap::NG::Portal::Main;
use strict; use strict;
use Mouse; use Mouse;
use JSON;
has skinRules => ( is => 'rw' ); has skinRules => ( is => 'rw' );
@ -521,19 +522,50 @@ sub mkOidcConsent {
} }
} }
my $consents = {}; # Loading existing oidcConsents
$self->logger->debug("Loading OIDC Consents ...");
my $_consents;
if ( exists $session->{_oidcConsents} ) {
$_consents = eval {
from_json( $session->{_oidcConsents}, { allow_nonref => 1 } );
};
if ($@) {
$self->logger->error("Corrupted session (_oidcConsents): $@");
return PE_ERROR;
}
}
else {
$self->logger->debug("No OIDC Consent found");
foreach ( keys %$session ) { #$_oidcConsents = [];
if ( $_ =~ /_oidc_consent_time_(.+)$/ ) { }
$consents->{$1}->{time} = $session->{ "_oidc_consent_time_" . $1 };
$consents->{$1}->{scope} = my $consents = {};
$session->{ "_oidc_consent_scope_" . $1 }; #####################
$consents->{$1}->{displayName} =
$self->conf->{oidcRPMetaDataOptions}->{$1} foreach (@$_consents) {
->{oidcRPMetaDataOptionsDisplayName}; if ( defined $_->{rp} ) {
my $rp = $_->{rp};
$self->logger->debug("RP { $rp } Consent found");
$consents->{$rp}->{epoch} = $_->{epoch};
$consents->{$rp}->{scope} = $_->{scope};
$consents->{$rp}->{displayName} =
$self->conf->{oidcRPMetaDataOptions}->{$rp}->{oidcRPMetaDataOptionsDisplayName};
} }
} }
#foreach ( keys %$session ) {
#if ( $_ =~ /_oidc_consent_time_(.+)$/ ) {
#$consents->{$1}->{time} = $session->{ "_oidc_consent_time_" . $1 };
#$consents->{$1}->{scope} =
#$session->{ "_oidc_consent_scope_" . $1 };
#$consents->{$1}->{displayName} =
#$self->conf->{oidcRPMetaDataOptions}->{$1}
#->{oidcRPMetaDataOptionsDisplayName};
#}
#}
#####################
return $self->loadTemplate( return $self->loadTemplate(
'oidcConsents', 'oidcConsents',
params => { params => {
@ -541,7 +573,7 @@ sub mkOidcConsent {
map { map {
{ {
name => $_, name => $_,
time => $consents->{$_}->{time}, epoch => $consents->{$_}->{epoch},
scope => $consents->{$_}->{scope}, scope => $consents->{$_}->{scope},
displayName => $consents->{$_}->{displayName} displayName => $consents->{$_}->{displayName}
} }

View File

@ -45,6 +45,7 @@ package Lemonldap::NG::Portal::Plugins::RESTServer;
use strict; use strict;
use Mouse; use Mouse;
use JSON qw(from_json to_json);
use MIME::Base64; use MIME::Base64;
our $VERSION = '2.0.0'; our $VERSION = '2.0.0';
@ -351,7 +352,8 @@ sub mysession {
sub getMyKey { sub getMyKey {
my ( $self, $req, $key ) = @_; my ( $self, $req, $key ) = @_;
$self->logger->debug('Request to get personal session info'); $key ||= '';
$self->logger->debug("Request to get personal session info -> Key : $key");
return $self->session( return $self->session(
$req, $req,
$req->userData->{_session_id}, $req->userData->{_session_id},
@ -384,6 +386,8 @@ sub updateMySession {
push @$mKeys, $key; push @$mKeys, $key;
$self->p->updatePersistentSession( $req, $self->p->updatePersistentSession( $req,
{ $key => $v } ); { $key => $v } );
$self->logger->debug(
"Request to update session -> Key : $key");
} }
} }
} }
@ -407,6 +411,7 @@ sub delKeyInMySession {
my $res = 0; my $res = 0;
my $mKeys = []; my $mKeys = [];
my $dkey = $req->param('key'); my $dkey = $req->param('key');
my $sub = $req->param('sub');
if ( my $token = $req->param('token') ) { if ( my $token = $req->param('token') ) {
if ( $self->ott->getToken($token) ) { if ( $self->ott->getToken($token) ) {
if ( $req->param('sessionType') eq 'persistent' ) { if ( $req->param('sessionType') eq 'persistent' ) {
@ -424,8 +429,51 @@ sub delKeyInMySession {
} }
} }
if ($res) { if ($res) {
if ( $dkey !~ /^_oidcConsents$/ ) {
$self->p->updatePersistentSession( $req, $self->p->updatePersistentSession( $req,
{ $dkey => undef } ); { $dkey => undef } );
$self->logger->debug(
"Update session -> delete Key : $dkey");
}
elsif ( $dkey =~ /^_oidcConsents$/ and defined $sub ) {
# Read existing oidcConsents
$self->logger->debug("Looking for OIDC Consents ...");
my $_oidcConsents;
if ( $req->userData->{_oidcConsents} ) {
$_oidcConsents = eval {
from_json( $req->userData->{_oidcConsents},
{ allow_nonref => 1 } );
};
if ($@) {
$self->logger->error(
"Corrupted session (_oidcConsents): $@");
return $self->p->sendError( $req,
"Corrupted session", 500 );
}
}
else {
$self->logger->debug("No OIDC Consent found");
$_oidcConsents = [];
}
my @keep = ();
while (@$_oidcConsents) {
my $element = shift @$_oidcConsents;
$self->logger->debug(
"Looking for OIDC Consent to delete ...");
push @keep, $element
unless ( $element->{rp} eq $sub );
}
$self->p->updatePersistentSession( $req,
{ _oidcConsents => to_json( \@keep ) } );
$self->logger->debug(
"Update session -> delete Key : $dkey with Sub : $sub"
);
}
else {
$self->logger->error(
'Update session request with invalid Key or Sub');
}
} }
} }
} }

View File

@ -54,7 +54,7 @@ getValues = () ->
# ---------------------------------------- # ----------------------------------------
setSelector = "#appslist" setSelector = "#appslist"
# Function that writes the list order to session (network errors ignored) # Function to write the sorted apps list to session (network errors ignored)
setOrder = -> setOrder = ->
setKey '_appsListOrder', $(setSelector).sortable("toArray").join() setKey '_appsListOrder', $(setSelector).sortable("toArray").join()
@ -71,16 +71,12 @@ removeOidcConsent = (partner) ->
# alert "#{s} #{e}" # alert "#{s} #{e}"
e = (j,s,e) -> e = (j,s,e) ->
alert "#{s} #{e}" alert "#{s} #{e}"
delKey "_oidc_consent_time_#{partner}" delKey "_oidcConsents",partner
# Success
, () ->
delKey "_oidc_consent_scope_#{partner}"
# Success # Success
, () -> , () ->
$("[partner='#{partner}']").hide() $("[partner='#{partner}']").hide()
# Error # Error
, e , e
, e
# Function used by setOrder() and removeOidcConsent() to push new values # Function used by setOrder() and removeOidcConsent() to push new values
# For security reason, modification is rejected unless a valid token is given # For security reason, modification is rejected unless a valid token is given
@ -104,7 +100,7 @@ setKey = (key,val,success,error) ->
success: success success: success
error: error error: error
delKey = (key,success,error) -> delKey = (key,sub,success,error) ->
$.ajax $.ajax
type: "GET" type: "GET"
url: datas['scriptname'] + '/mysession/?gettoken' url: datas['scriptname'] + '/mysession/?gettoken'
@ -114,7 +110,7 @@ delKey = (key,success,error) ->
success: (data) -> success: (data) ->
$.ajax $.ajax
type: "DELETE" type: "DELETE"
url: "#{datas['scriptname']}/mysession/persistent/#{key}?token=#{data.token}" url: "#{datas['scriptname']}/mysession/persistent/#{key}?sub=#{sub}&token=#{data.token}"
dataType: 'json' dataType: 'json'
success: success success: success
error: error error: error

View File

@ -79,11 +79,9 @@ LemonLDAP::NG Portal jQuery scripts
e = function(j, s, e) { e = function(j, s, e) {
return alert(s + " " + e); return alert(s + " " + e);
}; };
return delKey("_oidc_consent_time_" + partner, function() { return delKey("_oidcConsents", partner, function() {
return delKey("_oidc_consent_scope_" + partner, function() {
return $("[partner='" + partner + "']").hide(); return $("[partner='" + partner + "']").hide();
}, e); }, e);
}, e);
}; };
setKey = function(key, val, success, error) { setKey = function(key, val, success, error) {
@ -110,7 +108,7 @@ LemonLDAP::NG Portal jQuery scripts
}); });
}; };
delKey = function(key, success, error) { delKey = function(key, sub, success, error) {
return $.ajax({ return $.ajax({
type: "GET", type: "GET",
url: datas['scriptname'] + '/mysession/?gettoken', url: datas['scriptname'] + '/mysession/?gettoken',
@ -119,7 +117,7 @@ LemonLDAP::NG Portal jQuery scripts
success: function(data) { success: function(data) {
return $.ajax({ return $.ajax({
type: "DELETE", type: "DELETE",
url: datas['scriptname'] + "/mysession/persistent/" + key + "?token=" + data.token, url: datas['scriptname'] + "/mysession/persistent/" + key + "?sub=" + sub + "&token=" + data.token,
dataType: 'json', dataType: 'json',
success: success, success: success,
error: error error: error

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@
<TMPL_LOOP NAME="partners"> <TMPL_LOOP NAME="partners">
<tr partner="<TMPL_VAR NAME="name">"> <tr partner="<TMPL_VAR NAME="name">">
<td><TMPL_VAR NAME="displayName"></td> <td><TMPL_VAR NAME="displayName"></td>
<td class="localeDate" val="<TMPL_VAR NAME="time">"></td> <td class="localeDate" val="<TMPL_VAR NAME="epoch">"></td>
<td><TMPL_VAR NAME="scope"></td> <td><TMPL_VAR NAME="scope"></td>
<td><a partner="<TMPL_VAR NAME="name">" title="delete" class="oidcConsent link nodecor text-danger glyphicon glyphicon-minus-sign"></a></td> <td><a partner="<TMPL_VAR NAME="name">" title="delete" class="oidcConsent link nodecor text-danger glyphicon glyphicon-minus-sign"></a></td>
</td> </td>

View File

@ -292,7 +292,7 @@ ok(
); );
count(1); count(1);
$idpId = expectCookie($res); $idpId = expectCookie($res);
expectRedirection( $res, qr#^http://auth.rp.com/# ); #expectRedirection( $res, qr#^http://auth.rp.com/# );
#print STDERR Dumper($res); #print STDERR Dumper($res);