our
$VERSION
=
'2.0.16'
;
our
$secureTokenMemcachedConnection
;
BEGIN {
eval
{
threads::share(
$secureTokenMemcachedConnection
);
};
}
sub
run {
my
$class
=
shift
;
my
$r
=
shift
;
my
(
$ret
,
$session
) =
$class
->Lemonldap::NG::Handler::Main::run(
$r
);
return
$ret
unless
(
$ret
==
$class
->OK );
my
$uri
=
$r
->{env}->{REQUEST_URI};
my
$localConfig
=
$class
->localConfig;
our
$secureTokenMemcachedServers
=
$localConfig
->{secureTokenMemcachedServers} || [
'127.0.0.1:11211'
];
my
$secureTokenExpiration
=
$localConfig
->{secureTokenExpiration} || 60;
my
$secureTokenAttribute
=
$localConfig
->{secureTokenAttribute} ||
'uid'
;
our
$secureTokenUrls
=
$localConfig
->{
'secureTokenUrls'
} || [
'.*'
];
my
$secureTokenHeader
=
$localConfig
->{secureTokenHeader} ||
'Auth-Token'
;
my
$secureTokenAllowOnError
=
$localConfig
->{
'secureTokenAllowOnError'
}
// 1;
foreach
(
qw/secureTokenMemcachedServers secureTokenUrls/
) {
no
strict
'refs'
;
unless
(
ref
${
$_
} eq
"ARRAY"
) {
$class
->logger->debug(
"Transform $_ value into an array reference"
);
my
@array
=
split
( /\s+/, ${
$_
} );
${
$_
} = \
@array
;
}
}
$class
->logger->debug(
'secureTokenMemcachedServers: '
.
join
', '
,
@$secureTokenMemcachedServers
);
$class
->logger->debug(
"secureTokenExpiration: $secureTokenExpiration"
);
$class
->logger->debug(
"secureTokenAttribute: $secureTokenAttribute"
);
$class
->logger->debug(
'secureTokenUrls: '
.
join
', '
,
@$secureTokenUrls
);
$class
->logger->debug(
"secureTokenHeader: $secureTokenHeader"
);
$class
->logger->debug(
"secureTokenAllowOnError: $secureTokenAllowOnError"
);
my
$checkurl
;
foreach
(
@$secureTokenUrls
) {
if
(
$uri
=~ m
$checkurl
= 1;
$class
->logger->debug(
"URL $uri detected as an SecureToken URL (rule: $_)"
);
last
;
}
}
return
$class
->OK
unless
$checkurl
;
unless
(
$class
->_isAlive() ) {
$secureTokenMemcachedConnection
=
$class
->_createMemcachedConnection(
$secureTokenMemcachedServers
);
}
return
$class
->_returnError(
$r
,
$secureTokenAllowOnError
)
unless
$class
->_isAlive();
my
$value
=
$class
->data->{
$secureTokenAttribute
};
my
$key
=
$class
->_setToken(
$value
,
$secureTokenExpiration
);
return
$class
->_returnError(
$r
,
$secureTokenAllowOnError
)
unless
$key
;
$class
->set_header_in(
$r
,
$secureTokenHeader
=>
$key
);
eval
'use Apache2::Filter'
unless
(
$INC
{
"Apache2/Filter.pm"
} );
if
(
$INC
{
"Apache2/Filter.pm"
} and
defined
$r
->{env}->{
'psgi.r'
} ) {
$r
->{env}->{
'psgi.r'
}->add_output_filter(
sub
{
my
$f
=
shift
;
while
(
$f
->
read
(
my
$buffer
, 1024 ) ) {
$f
->
print
(
$buffer
);
}
if
(
$f
->seen_eos ) {
$class
->_deleteToken(
$key
);
}
return
$class
->OK;
}
);
}
return
$class
->OK;
}
sub
_createMemcachedConnection {
my
(
$class
,
$secureTokenMemcachedServers
) =
@_
;
my
$memd
= new Cache::Memcached {
'servers'
=>
$secureTokenMemcachedServers
,
'debug'
=> 0,
};
$class
->logger->debug(
"Memcached connection created"
);
return
$memd
;
}
sub
_setToken {
my
(
$class
,
$value
,
$secureTokenExpiration
) =
@_
;
my
$key
= Apache::Session::Generate::MD5::generate();
my
$res
=
$secureTokenMemcachedConnection
->set(
$key
,
$value
,
$secureTokenExpiration
);
unless
(
$res
) {
$class
->logger->error(
"Unable to store secure token $key"
);
return
;
}
$class
->logger->info(
"Set $value in token $key"
);
return
$key
;
}
sub
_deleteToken {
my
(
$class
,
$key
) =
@_
;
my
$res
=
$secureTokenMemcachedConnection
->
delete
(
$key
);
if
(
$res
) {
$class
->logger->info(
"Token $key deleted"
);
}
else
{
$class
->logger->error(
"Unable to delete secure token $key"
);
}
return
$res
;
}
sub
_isAlive {
my
(
$class
) =
@_
;
return
0
unless
defined
$secureTokenMemcachedConnection
;
my
$stats
=
$secureTokenMemcachedConnection
->stats();
if
(
$stats
and
defined
$stats
->{
'total'
} ) {
my
$total_c
=
$stats
->{
'total'
}->{
'connection_structures'
};
my
$total_i
=
$stats
->{
'total'
}->{
'total_items'
};
$class
->logger->debug(
"Memcached connection is alive ($total_c connections / $total_i items)"
);
return
1;
}
$class
->logger->error(
'Memcached connection is down'
);
return
0;
}
sub
_returnError {
my
(
$class
,
$r
,
$secureTokenAllowOnError
) =
@_
;
if
(
$secureTokenAllowOnError
) {
$class
->logger->debug(
"Allow request without secure token"
);
return
$class
->OK;
}
if
(
$class
->tsv->{useRedirectOnError} ) {
$class
->logger->debug(
"Use redirect for error"
);
return
$class
->goToError(
'/'
, 500 );
}
else
{
$class
->logger->debug(
"Return error"
);
return
$class
->SERVER_ERROR;
}
}
1;