WIP - AdminImpersonation skeleton (#1783)
This commit is contained in:
parent
2d5b38fb95
commit
a2ebaf31b1
|
@ -5,11 +5,12 @@ our $VERSION = '2.0.5';
|
|||
|
||||
sub defaultValues {
|
||||
return {
|
||||
'activeTimer' => 1,
|
||||
'ADPwdExpireWarning' => 0,
|
||||
'ADPwdMaxAge' => 0,
|
||||
'apacheAuthnLevel' => 4,
|
||||
'applicationList' => {
|
||||
'activeTimer' => 1,
|
||||
'adminImpersonationRule' => 1,
|
||||
'ADPwdExpireWarning' => 0,
|
||||
'ADPwdMaxAge' => 0,
|
||||
'apacheAuthnLevel' => 4,
|
||||
'applicationList' => {
|
||||
'default' => {
|
||||
'catname' => 'Default category',
|
||||
'type' => 'category'
|
||||
|
|
|
@ -257,6 +257,10 @@ sub attributes {
|
|||
'default' => 1,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'adminImpersonationRule' => {
|
||||
'default' => 1,
|
||||
'type' => 'boolOrExpr'
|
||||
},
|
||||
'ADPwdExpireWarning' => {
|
||||
'default' => 0,
|
||||
'type' => 'int'
|
||||
|
|
|
@ -486,6 +486,11 @@ sub attributes {
|
|||
documentation => 'Skip session empty values',
|
||||
flags => 'p',
|
||||
},
|
||||
adminImpersonationRule => {
|
||||
type => 'boolOrExpr',
|
||||
default => 1,
|
||||
documentation => 'adminImpersonation activation rule',
|
||||
},
|
||||
skipRenewConfirmation => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
|
|
|
@ -114,6 +114,16 @@ sub params {
|
|||
$self->p->_sfEngine->display2fRegisters( $req, $req->userData );
|
||||
$self->logger->debug("Display 2fRegisters link") if $res{sfaManager};
|
||||
|
||||
# Display adminImpersonation link only if allowed
|
||||
my $impPlugin = $self->p->loadedModules->{
|
||||
'Lemonldap::NG::Portal::Plugins::AdminImpersonation'};
|
||||
$res{adminImpersonation} =
|
||||
$impPlugin
|
||||
? $impPlugin->displayAdminImpersonation( $req, $req->userData )
|
||||
: '';
|
||||
$self->logger->debug("Display AdminImpersonation link")
|
||||
if $res{adminImpersonation};
|
||||
|
||||
return %res;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ our @pList = (
|
|||
portalForceAuthn => '::Plugins::ForceAuthn',
|
||||
checkUser => '::Plugins::CheckUser',
|
||||
impersonationRule => '::Plugins::Impersonation',
|
||||
adminImpersonationRule => '::Plugins::AdminImpersonation',
|
||||
);
|
||||
|
||||
##@method list enabledPlugins
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
package Lemonldap::NG::Portal::Plugins::AdminImpersonation;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants
|
||||
qw( PE_OK PE_BADCREDENTIALS PE_IMPERSONATION_SERVICE_NOT_ALLOWED PE_MALFORMEDUSER );
|
||||
|
||||
our $VERSION = '2.0.6';
|
||||
|
||||
extends
|
||||
qw(Lemonldap::NG::Portal::Main::Plugin Lemonldap::NG::Portal::Lib::_tokenRule);
|
||||
|
||||
# INITIALIZATION
|
||||
|
||||
has rule => ( is => 'rw', default => sub { 1 } );
|
||||
has idRule => ( is => 'rw', default => sub { 1 } );
|
||||
|
||||
sub hAttr {
|
||||
$_[0]->{conf}->{impersonationHiddenAttributes} . ' '
|
||||
. $_[0]->{conf}->{hiddenAttributes};
|
||||
}
|
||||
|
||||
has ott => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub {
|
||||
my $ott =
|
||||
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
||||
$ott->timeout( $_[0]->{conf}->{formTimeout} );
|
||||
return $ott;
|
||||
}
|
||||
);
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
my $hd = $self->p->HANDLER;
|
||||
$self->addAuthRoute( impersonate => 'run', ['POST'] );
|
||||
$self->addAuthRoute( impersonate => 'display', ['GET'] );
|
||||
|
||||
# Parse activation rule
|
||||
$self->logger->debug(
|
||||
'AdminImpersonation rule -> ' . $self->conf->{adminImpersonationRule} );
|
||||
my $rule =
|
||||
$hd->buildSub( $hd->substitute( $self->conf->{adminImpersonationRule} ) );
|
||||
unless ($rule) {
|
||||
$self->error(
|
||||
'Bad adminImpersonation rule -> ' . $hd->tsv->{jail}->error );
|
||||
return 0;
|
||||
}
|
||||
$self->rule($rule);
|
||||
|
||||
# Parse identity rule
|
||||
$self->logger->debug( "Impersonation identity rule -> "
|
||||
. $self->conf->{impersonationIdRule} );
|
||||
$rule =
|
||||
$hd->buildSub( $hd->substitute( $self->conf->{impersonationIdRule} ) );
|
||||
unless ($rule) {
|
||||
$self->error(
|
||||
"Bad impersonation identity rule -> " . $hd->tsv->{jail}->error );
|
||||
return 0;
|
||||
}
|
||||
$self->idRule($rule);
|
||||
return 1;
|
||||
}
|
||||
|
||||
# RUNNING METHOD
|
||||
|
||||
sub display {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
# Display form
|
||||
my $params = {
|
||||
PORTAL => $self->conf->{portal},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => 'impersonate',
|
||||
ALERTE => 'alert-danger',
|
||||
LOGIN => '',
|
||||
SPOOFID => $self->conf->{adminImpersonationRule},
|
||||
TOKEN => (
|
||||
$self->ottRule->( $req, {} )
|
||||
? $self->ott->createToken()
|
||||
: ''
|
||||
)
|
||||
};
|
||||
|
||||
return $self->p->sendHtml( $req, 'adminImpersonation', params => $params, );
|
||||
}
|
||||
|
||||
sub run {
|
||||
my ( $self, $req ) = @_;
|
||||
my $statut = PE_OK;
|
||||
my $spoofId = $req->param('spoofId') || ''; # Impersonation required ?
|
||||
|
||||
unless ($spoofId) {
|
||||
$self->logger->debug("No impersonation required");
|
||||
$req->mustRedirect(1);
|
||||
return $self->p->do( $req, [ sub { PE_OK } ] );
|
||||
}
|
||||
|
||||
unless ( $spoofId =~ /$self->{conf}->{userControl}/o ) {
|
||||
$self->userLogger->error('Malformed spoofed Id');
|
||||
$self->logger->debug(
|
||||
"AdminImpersonation tried with spoofed Id: $spoofId");
|
||||
$spoofId = $req->{user};
|
||||
$statut = PE_MALFORMEDUSER;
|
||||
}
|
||||
|
||||
# Check activation rule
|
||||
if ($spoofId) {
|
||||
$self->logger->debug("Spoof Id: $spoofId");
|
||||
unless ( $self->rule->( $req, $req->sessionInfo ) ) {
|
||||
$self->userLogger->error(
|
||||
'adminImpersonation service not authorized');
|
||||
$spoofId = '';
|
||||
$statut = PE_IMPERSONATION_SERVICE_NOT_ALLOWED;
|
||||
}
|
||||
}
|
||||
|
||||
# Fill spoof session
|
||||
my ( $realSession, $spoofSession ) = ( {}, {} );
|
||||
$self->logger->debug("Rename real attributes...");
|
||||
my $spk = '';
|
||||
foreach my $k ( keys %{ $req->{userData} } ) {
|
||||
if ( $self->{conf}->{impersonationSkipEmptyValues} ) {
|
||||
next unless defined $req->{userData}->{$k};
|
||||
}
|
||||
$spk = "$self->{conf}->{impersonationPrefix}$k";
|
||||
unless ( $self->hAttr =~ /\b$k\b/
|
||||
|| $k =~ /^(?:_imp|token|_type)\w*\b/ )
|
||||
{
|
||||
$realSession->{$spk} = $req->{userData}->{$k};
|
||||
$self->logger->debug("-> Store $k in realSession key: $spk");
|
||||
}
|
||||
$self->logger->debug("Delete $k");
|
||||
delete $req->{userData}->{$k};
|
||||
}
|
||||
|
||||
$spoofSession = $self->_userData( $req, $spoofId, $realSession );
|
||||
if ( $req->error ) {
|
||||
if ( $req->error == PE_BADCREDENTIALS ) {
|
||||
$statut = PE_BADCREDENTIALS;
|
||||
}
|
||||
else {
|
||||
return $req->error;
|
||||
}
|
||||
}
|
||||
|
||||
# Update spoof session
|
||||
$self->logger->debug("Populating spoof session...");
|
||||
foreach (qw (_auth _userDB _session_id)) {
|
||||
$self->logger->debug("Processing $_...");
|
||||
$spk = "$self->{conf}->{impersonationPrefix}$_";
|
||||
$spoofSession->{$_} = $realSession->{$spk};
|
||||
}
|
||||
|
||||
# Merging SSO Groups and hGroups & dedup
|
||||
$spoofSession->{groups} ||= '';
|
||||
$spoofSession->{hGroups} ||= {};
|
||||
if ( $self->{conf}->{impersonationMergeSSOgroups} ) {
|
||||
$self->userLogger->warn("MERGING SSO groups and hGroups...");
|
||||
my $spg = "$self->{conf}->{impersonationPrefix}groups";
|
||||
my $sphg = "$self->{conf}->{impersonationPrefix}hGroups";
|
||||
my $separator = $self->{conf}->{multiValuesSeparator};
|
||||
|
||||
## GROUPS
|
||||
my @spoofGrps = split /\Q$separator/, $spoofSession->{groups};
|
||||
my @realGrps = split /\Q$separator/, $realSession->{$spg};
|
||||
|
||||
## hGROUPS
|
||||
$realSession->{$sphg} ||= {};
|
||||
|
||||
# Merge specified groups/hGroups only
|
||||
unless ( $self->{conf}->{impersonationMergeSSOgroups} eq 1 ) {
|
||||
my %SSOgroups = map { $_, 1 } split /\Q$separator/,
|
||||
$self->{conf}->{impersonationMergeSSOgroups};
|
||||
|
||||
$self->logger->debug("Filtering specified groups/hGroups...");
|
||||
@realGrps = grep { exists $SSOgroups{$_} } @realGrps;
|
||||
my %intersct =
|
||||
map {
|
||||
$realSession->{$sphg}->{$_}
|
||||
? ( $_, $realSession->{$sphg}->{$_} )
|
||||
: ()
|
||||
} keys %SSOgroups;
|
||||
$realSession->{$sphg} = \%intersct;
|
||||
}
|
||||
|
||||
$self->logger->debug("Processing groups...");
|
||||
@spoofGrps = ( @spoofGrps, @realGrps );
|
||||
my %hash = map { $_, 1 } @spoofGrps;
|
||||
$spoofSession->{groups} = join $separator, sort keys %hash;
|
||||
|
||||
$self->logger->debug("Processing hGroups...");
|
||||
$spoofSession->{hGroups} =
|
||||
{ %{ $spoofSession->{hGroups} }, %{ $realSession->{$sphg} } };
|
||||
}
|
||||
|
||||
# Main session
|
||||
$self->p->updateSession( $req, $spoofSession );
|
||||
return $self->p->do( $req, [ sub { $statut } ] );
|
||||
}
|
||||
|
||||
sub _userData {
|
||||
my ( $self, $req, $spoofId, $realSession ) = @_;
|
||||
my $realId = $req->{user};
|
||||
$req->{user} = $spoofId;
|
||||
my $raz = 0;
|
||||
|
||||
# Compute Macros and Groups with real and spoof sessions
|
||||
$req->{sessionInfo} = {%$realSession};
|
||||
|
||||
# Search user in database
|
||||
$req->steps( [
|
||||
'getUser', 'setSessionInfo',
|
||||
'setMacros', 'setGroups',
|
||||
'setLocalGroups'
|
||||
]
|
||||
);
|
||||
if ( my $error = $self->p->process($req) ) {
|
||||
if ( $error == PE_BADCREDENTIALS ) {
|
||||
$self->userLogger->warn(
|
||||
'Impersonation requested for an unvalid user ('
|
||||
. $req->{user}
|
||||
. ")" );
|
||||
}
|
||||
$self->logger->debug("Process returned error: $error");
|
||||
$req->error($error);
|
||||
$raz = 1;
|
||||
}
|
||||
|
||||
# Check identity rule if Impersonation required
|
||||
if ( $realId ne $spoofId ) {
|
||||
unless ( $self->idRule->( $req, $req->sessionInfo ) ) {
|
||||
$self->userLogger->warn(
|
||||
'Impersonation requested for an unvalid user ('
|
||||
. $req->{user}
|
||||
. ")" );
|
||||
$self->logger->debug('Identity not authorized');
|
||||
$raz = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Same real and spoof session - Compute Macros and Groups
|
||||
if ($raz) {
|
||||
$req->{sessionInfo} = {};
|
||||
$req->{sessionInfo} = {%$realSession};
|
||||
$req->{user} = $realId;
|
||||
$req->steps( [
|
||||
'getUser', 'setSessionInfo',
|
||||
'setMacros', 'setGroups',
|
||||
'setLocalGroups'
|
||||
]
|
||||
);
|
||||
$self->logger->debug('Spoof session equal real session');
|
||||
$req->error(PE_BADCREDENTIALS);
|
||||
if ( my $error = $self->p->process($req) ) {
|
||||
$self->logger->debug("Process returned error: $error");
|
||||
$req->error($error);
|
||||
}
|
||||
}
|
||||
return $req->{sessionInfo};
|
||||
}
|
||||
|
||||
sub displayAdminImpersonation {
|
||||
my ( $self, $req ) = @_;
|
||||
return $self->rule->( $req, $req->userData )
|
||||
|| $req->userData->{"$self->{conf}->{impersonationPrefix}_session_id"};
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,38 @@
|
|||
<TMPL_INCLUDE NAME="header.tpl">
|
||||
|
||||
<div id="errorcontent" class="container">
|
||||
<!--
|
||||
<div class="message message-positive alert"><span trspan="<TMPL_VAR NAME="MSG">"></span></div>
|
||||
-->
|
||||
<div class="alert <TMPL_VAR NAME="ALERTE"> alert"><div class="text-center"><span trspan="<TMPL_VAR NAME="MSG">"></span></div></div>
|
||||
<form id="adminImpersonation" action="/impersonate" method="post" class="password" role="form">
|
||||
<div class="buttons">
|
||||
<TMPL_IF NAME="TOKEN">
|
||||
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
|
||||
</TMPL_IF>
|
||||
|
||||
<TMPL_INCLUDE NAME="impersonation.tpl">
|
||||
|
||||
<button type="submit" class="btn btn-success">
|
||||
<span class="fa fa-random"></span>
|
||||
<span trspan="impersonate">Impersonate</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="buttons">
|
||||
<!--
|
||||
<button type="submit" class="btn btn-success">
|
||||
<span class="fa fa-sign-in"></span>
|
||||
<span trspan="search">Search</span>
|
||||
</button>
|
||||
-->
|
||||
<a href="<TMPL_VAR NAME="PORTAL_URL">" class="btn btn-primary" role="button">
|
||||
<span class="fa fa-home"></span>
|
||||
<span trspan="goToPortal">Go to portal</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TMPL_INCLUDE NAME="footer.tpl">
|
|
@ -67,6 +67,12 @@
|
|||
<span trspan="sfaManager">sfaManager</span>
|
||||
</a></li>
|
||||
</TMPL_IF>
|
||||
<TMPL_IF NAME="adminImpersonation">
|
||||
<li class="dropdown-item"><a href="/impersonate" class="nav-link">
|
||||
<img src="<TMPL_VAR NAME="STATIC_PREFIX">common/icons/sfa_manager.png" width="16" height="16" alt="refresh" />
|
||||
<span trspan="adminImpersonate">adminImpersonate</span>
|
||||
</a></li>
|
||||
</TMPL_IF>
|
||||
<li class="dropdown-item"><a href="/refresh" class="nav-link">
|
||||
<img src="<TMPL_VAR NAME="STATIC_PREFIX">common/icons/arrow_refresh.png" width="16" height="16" alt="refresh" />
|
||||
<span trspan="refreshrights">Refresh</span>
|
||||
|
|
Loading…
Reference in New Issue
Block a user