* Little CSS for abort()

* Comments for lemonldap-ng.ini
* lemonldap-ng.ini was not well read in Manager
* New target 'unprotect' in rules
* Status update
This commit is contained in:
Xavier Guimard 2010-03-09 21:42:31 +00:00
parent 5b77fa66b4
commit d439cab42e
11 changed files with 207 additions and 81 deletions

View File

@ -169,12 +169,25 @@ localStorageOptions={ 'namespace' => 'MyNamespace', 'default_expires_in' => 600,
applicationList={ 'Menu' => { type => 'category', 'Example' => { type => 'category', 'test1' => { type => 'application', options => { name => 'Application Test 1', uri => 'http://test1.__DNSDOMAIN__/', description => 'A simple application displaying authenticated user', logo => 'wheels.png', display => 'auto', }, },'test2' => { type => 'application', options => { name => 'Application Test 2', uri => 'http://test2.__DNSDOMAIN__/', description => 'The same simple application displaying authenticated user', logo => 'wheels.png', display => 'auto', }, }, },'Administration' => { type => 'category', 'manager' => { type => 'application', options => { name => 'WebSSO Manager', uri => 'http://manager.__DNSDOMAIN__/', description => 'Configure LemonLDAP::NG WebSSO', logo => 'tools.png', display => 'on', }, },'sessions' => { type => 'application', options => { name => 'Sessions explorer', uri => 'http://manager.__DNSDOMAIN__/sessions.pl', description => 'Explore WebSSO sessions', logo => 'tools.png', display => 'on', }, }, },'Documentation' => { type => 'category', 'localdoc' => { type => 'application', options => { name => 'Local documentation', uri => 'http://manager.__DNSDOMAIN__/doc/', description => 'Documentation supplied with LemonLDAP::NG', logo => 'docs.png', display => 'on', }, },'officialwebsite' => { type => 'application', options => { name => 'Offical Website', uri => 'http://wiki.lemonldap.objectweb.org/xwiki/bin/view/NG/Presentation', description => 'Official LemonLDAP::NG Website', logo => 'web.png', display => 'on', }, }, }, }, }
[handler]
# Set https to 1 if your handler protect a https website (used only for
# redirections to the portal
https = 0
https=0
# Set status to 1 if you want to have the report of activity (used for
# example to inform MRTG)
status = 0
[manager]
;protection = authenticate
# Manager protection: by default, the manager isn't protected. You can
# protect it :
# * by Apache itself,
# * by the parameter 'protection' which can take one of the following
# values :
# * 'authenticate' : all authenticated users can access
# * 'manager' : manager is protected like other virtual hosts: you
# have to set rules in the corresponding virtual host
# * 'rule: <rule> : you can set here directly the rule to apply
;protection = manager
[apply]

View File

@ -210,9 +210,20 @@ sub abort {
print $cgi->start_html(
-title => $t1,
-encoding => 'utf8',
-style => {
-code => '
body{
background:#000;
color:#fff;
padding:10px 50px;
font-family:sans-serif;
}
'
},
);
print "<h1>$t1</h1>";
print "<p>$t2</p>";
print "<h1>$t1</h1><p>$t2</p>";
print
'<center><img alt="Lemonldap::NG" src="http://lemonldap.ow2.org/logo_lemonldap-ng.png" /></center>';
print STDERR ( ref($self) || $self ) . " error: $t1, $t2\n";
exit;
}

View File

@ -22,6 +22,7 @@ use Config::IniFiles;
our $VERSION = 0.70;
our $msg;
our $configFiles;
## @cmethod Lemonldap::NG::Common::Conf new(hashRef arg)
# Constructor.
@ -62,27 +63,27 @@ sub new {
}
}
unless ( $self->{type} ) {
$msg .= 'Error: configStorage: type is not defined';
$msg .= ' Error: configStorage: type is not defined.';
return 0;
}
unless ( $self->{type} =~ /^[\w:]+$/ ) {
$msg .= "Error: configStorage: type is not well formed";
$msg .= ' Error: configStorage: type is not well formed.';
}
$self->{type} = "Lemonldap::NG::Common::Conf::$self->{type}"
unless $self->{type} =~ /^Lemonldap::/;
eval "require $self->{type}";
if ($@) {
$msg .= "Error: Unknown package $self->{type}";
$msg .= " Error: Unknown package $self->{type}.";
return 0;
}
return 0 unless $self->prereq;
$self->{mdone}++;
$msg = "$self->{type} loaded";
$msg = "$self->{type} loaded.";
}
if ( $self->{localStorage} and not defined( $self->{refLocalStorage} ) ) {
eval "use $self->{localStorage};";
if ($@) {
$msg .= "Unable to load $self->{localStorage}: $@";
$msg .= " Unable to load $self->{localStorage}: $@.";
}
else {
$self->{refLocalStorage} =
@ -107,7 +108,7 @@ sub saveConf {
return DATABASE_LOCKED if ( $self->isLocked or not $self->lock );
}
$conf->{cfgNum} = $last + 1 unless ( $self->{cfgNumFixed} );
$msg = "Configuration $conf->{cfgNum} stored";
$msg = "Configuration $conf->{cfgNum} stored.";
return $self->store($conf);
}
@ -125,24 +126,24 @@ sub getConf {
and ref( $self->{refLocalStorage} )
and my $res = $self->{refLocalStorage}->get('conf') )
{
$msg = "get configuration from cache without verification";
$msg = "get configuration from cache without verification.";
return $res;
}
else {
$args->{cfgNum} ||= $self->lastCfg;
unless ( $args->{cfgNum} ) {
$msg = "No configuration available";
$msg = "No configuration available.";
return 0;
}
my $r;
unless ( ref( $self->{refLocalStorage} ) ) {
$msg = "get remote configuration (localStorage unavailable)";
$msg = "get remote configuration (localStorage unavailable).";
$r = $self->getDBConf($args);
}
else {
$r = $self->{refLocalStorage}->get('conf');
if ( $r->{cfgNum} == $args->{cfgNum} ) {
$msg = "configuration unchanged, get configuration from cache";
$msg = "configuration unchanged, get configuration from cache.";
}
else {
$r = $self->getDBConf($args);
@ -159,7 +160,7 @@ sub getConf {
);
};
if ($@) {
$msg = "Bad key : $@";
$msg = "Bad key : $@.";
return 0;
}
}
@ -181,20 +182,25 @@ sub getLocalConf {
$section ||= DEFAULTSECTION;
$file ||= DEFAULTCONFFILE;
$loaddefault = 1 unless ( defined $loaddefault );
my $cfg;
# First, search if this file has been parsed
unless ( $cfg = $configFiles->{$file} ) {
# If default configuration cannot be read
# - Error if configuration section is requested
# - Silent exit for other section requests
unless ( -r $file ) {
if ( $section eq CONFSECTION ) {
$msg = "Cannot read $file to get configuration access parameters";
$msg =
"Cannot read $file to get configuration access parameters.";
return 0;
}
return $r;
return 0;
}
# Parse ini file
my $cfg = Config::IniFiles->new( -file => $file, -allowcontinue => 1 );
$cfg = Config::IniFiles->new( -file => $file, -allowcontinue => 1 );
unless ( defined $cfg ) {
$msg = "Local config error: " . @Config::IniFiles::errors;
@ -203,15 +209,17 @@ sub getLocalConf {
# Check if default section exists
unless ( $cfg->SectionExists(DEFAULTSECTION) ) {
$msg = "Default section (" . DEFAULTSECTION . ") is missing";
$msg = "Default section (" . DEFAULTSECTION . ") is missing.";
return 0;
}
# Check if configuration section exists
if ( $section eq CONFSECTION and !$cfg->SectionExists(CONFSECTION) ) {
$msg = "Configuration section (" . CONFSECTION . ") is missing";
$msg = "Configuration section (" . CONFSECTION . ") is missing.";
return 0;
}
$configFiles->{$file} = $cfg;
}
# First load all default section parameters
if ($loaddefault) {
@ -220,7 +228,7 @@ sub getLocalConf {
if ( $r->{$_} =~ /^[{\[].*[}\]]$/ ) {
eval "\$r->{$_} = $r->{$_}";
if ($@) {
$msg = "Warning: error in file $file: $@. ";
$msg = "Warning: error in file $file: $@.";
return 0;
}
}
@ -239,7 +247,7 @@ sub getLocalConf {
if ( $r->{$_} =~ /^[{\[].*[}\]]$/ ) {
eval "\$r->{$_} = $r->{$_}";
if ($@) {
$msg = "Warning: error in file $file: $@. ";
$msg = "Warning: error in file $file: $@.";
return 0;
}
}
@ -273,7 +281,7 @@ sub getDBConf {
: $a[0];
}
my $conf = $self->load( $args->{cfgNum} );
$msg = "Get configuration $conf->{cfgNum}";
$msg = "Get configuration $conf->{cfgNum}.";
my $re = Regexp::Assemble->new();
foreach ( keys %{ $conf->{locationRules} } ) {
$_ = quotemeta($_);

View File

@ -38,14 +38,14 @@ our @EXPORT;
# Shared variables
our (
$locationRegexp, $locationCondition, $defaultCondition,
$forgeHeaders, $apacheRequest, $locationCount,
$cookieName, $datas, $globalStorage,
$globalStorageOptions, $localStorage, $localStorageOptions,
$whatToTrace, $https, $refLocalStorage,
$safe, $port, $statusPipe,
$statusOut, $customFunctions, $transform,
$cda, $childInitDone, $httpOnly,
$cookieExpiration,
$locationProtection, $defaultProtection, $forgeHeaders,
$apacheRequest, $locationCount, $cookieName,
$datas, $globalStorage, $globalStorageOptions,
$localStorage, $localStorageOptions, $whatToTrace,
$https, $refLocalStorage, $safe,
$port, $statusPipe, $statusOut,
$customFunctions, $transform, $cda,
$childInitDone, $httpOnly, $cookieExpiration,
);
##########################################
@ -60,6 +60,7 @@ BEGIN {
locationRules => [
qw(
$locationCondition $defaultCondition $locationCount
$locationProtection $defaultProtection
$locationRegexp $apacheRequest $datas safe $customFunctions
)
],
@ -118,6 +119,8 @@ BEGIN {
threads::shared::share($locationRegexp);
threads::shared::share($locationCondition);
threads::shared::share($defaultCondition);
threads::shared::share($locationProtection);
threads::shared::share($defaultProtection);
threads::shared::share($forgeHeaders);
threads::shared::share($locationCount);
threads::shared::share($cookieName);
@ -505,56 +508,80 @@ sub locationRulesInit {
# Pre compilation : both regexp and conditions
foreach ( sort keys %{ $args->{locationRules} } ) {
if ( $_ eq 'default' ) {
$defaultCondition =
( $defaultCondition, $defaultProtection ) =
$class->conditionSub( $args->{locationRules}->{$_} );
}
else {
$locationCondition->[$locationCount] =
$class->conditionSub( $args->{locationRules}->{$_} );
(
$locationCondition->[$locationCount],
$locationProtection->[$locationCount]
) = $class->conditionSub( $args->{locationRules}->{$_} );
$locationRegexp->[$locationCount] = qr/$_/;
$locationCount++;
}
}
# Default police: all authenticated users are accepted
$defaultCondition = $class->conditionSub('accept')
( $defaultCondition, $defaultProtection ) = $class->conditionSub('accept')
unless ($defaultCondition);
1;
}
## @imethod protected codeRef conditionSub(string cond)
# Returns a compiled function used to grant users (used by
# locationRulesInit().
# locationRulesInit(). The second value returned is a boolean that
# tell if URL is protected.
# @param $cond The boolean expression to use
# @return array (ref(sub),boolean)
sub conditionSub {
my ( $class, $cond ) = splice @_;
return sub { 1 }
my ( $OK, $NOK ) = ( sub { 1 }, sub { 0 } );
# Simple cases : accept and deny
return ( $OK, 1 )
if ( $cond =~ /^accept$/i );
return sub { 0 }
return ( $NOK, 1 )
if ( $cond =~ /^deny$/i );
# Case unprotect : 2nd value is 0 since this URL is not protected
return ( $OK, 0 )
if ( $cond =~ /^unprotect$/i );
# Case logout
if ( $cond =~ /^logout(?:_sso)?(?:\s+(.*))?$/i ) {
my $url = $1;
return $url
? sub { $datas->{_logout} = $url; return 0 }
: sub { $datas->{_logout} = portal(); return 0 };
return (
$url
? ( sub { $datas->{_logout} = $url; return 0 }, 1 )
: ( sub { $datas->{_logout} = portal(); return 0 }, 1 )
);
}
# Since filter exists only with Apache>=2, logout_app and logout_app_sso
# targets are available only for it.
if ( MP() == 2 ) {
# logout_app
if ( $cond =~ /^logout_app(?:\s+(.*))?$/i ) {
my $u = $1 || 'portal()';
eval 'use Apache2::Filter' unless ( $INC{"Apache2/Filter.pm"} );
return sub {
return (
sub {
$apacheRequest->add_output_filter(
sub {
return $class->redirectFilter( $u, @_ );
}
);
1;
};
},
1
);
}
elsif ( $cond =~ /^logout_app_sso(?:\s+(.*))?$/i ) {
eval 'use Apache2::Filter' unless ( $INC{"Apache2/Filter.pm"} );
my $u = $1 || 'portal()';
return sub {
return (
sub {
$class->localUnlog;
$apacheRequest->add_output_filter(
sub {
@ -567,14 +594,16 @@ sub conditionSub {
}
);
1;
};
},
1
);
}
}
$cond =~ s/\$date/&POSIX::strftime("%Y%m%d%H%M%S",localtime())/e;
$cond =~ s/\$(\w+)/\$datas->{$1}/g;
$cond =~ s/\$datas->{vhost}/\$apacheRequest->hostname/g;
my $sub = $class->safe->reval("sub {return ( $cond )}");
return $sub;
return ( $sub, 1 );
}
## @imethod protected void defaultValuesInit(hashRef args)
@ -759,6 +788,17 @@ sub updateStatus {
};
}
## @rmethod protected boolean isProtected()
# @return True if URI isn't protected (rule "unprotect")
sub isProtected {
my ( $class, $uri ) = splice @_;
for ( my $i = 0 ; $i < $locationCount ; $i++ ) {
return $locationProtection->[$i]
if ( $uri =~ $locationRegexp->[$i] );
}
return $defaultProtection;
}
## @rmethod protected boolean grant()
# Grant or refuse client using compiled regexp and functions
# @return True if the user is granted to access to the current URL
@ -933,6 +973,15 @@ sub run ($$) {
# I - recover the cookie
my $id;
unless ( $id = $class->fetchId ) {
# 1.1 Ignore unprotected URIs
unless ( $class->isProtected($uri) ) {
$class->updateStatus( $apacheRequest->connection->remote_ip,
$apacheRequest->uri, 'UNPROTECT' );
return OK;
}
# 1.2 Redirect users to the portal
$class->lmLog( "$class: No cookie found", 'info' );
$class->updateStatus( $apacheRequest->connection->remote_ip,
$apacheRequest->uri, 'REDIRECT' );
@ -957,6 +1006,13 @@ sub run ($$) {
'info' );
$class->updateStatus( $apacheRequest->connection->remote_ip,
$apacheRequest->uri, 'EXPIRED' );
# For unprotected URI, user is not redirected
unless ( $class->isProtected($uri) ) {
$class->updateStatus( $apacheRequest->connection->remote_ip,
$apacheRequest->uri, 'UNPROTECT' );
return OK;
}
return $class->goToPortal($uri);
}
$datas->{$_} = $h{$_} foreach ( keys %h );
@ -993,6 +1049,7 @@ sub run ($$) {
PerlCleanupHandler => sub { $class->cleanLocalStorage(@_); DECLINED },
);
# Catch POST rules
if ( defined( $transform->{$uri} ) ) {
return &{ $transform->{$uri} };
}

View File

@ -22,6 +22,7 @@ our $page_title = 'Lemonldap::NG statistics';
# @return Constant hash used to convert error codes into string.
sub portalTab {
return {
-3 => 'PE_INFO',
-2 => 'PORTAL_REDIRECT',
-1 => 'PORTAL_ALREADY_AUTHENTICATED',
0 => 'PORTAL_OK',
@ -35,18 +36,33 @@ sub portalTab {
8 => 'PORTAL_APACHESESSIONERROR',
9 => 'PORTAL_FIRSTACCESS',
10 => 'PORTAL_BADCERTIFICATE',
11 => 'PORTAL_LA_FAILED',
12 => 'PORTAL_LA_ARTFAILED',
13 => 'PORTAL_LA_DEFEDFAILED',
14 => 'PORTAL_LA_QUERYEMPTY',
15 => 'PORTAL_LA_SOAPFAILED',
16 => 'PORTAL_LA_SLOFAILED',
17 => 'PORTAL_LA_SSOFAILED',
18 => 'PORTAL_LA_SSOINITFAILED',
19 => 'PORTAL_LA_SESSIONERROR',
20 => 'PORTAL_LA_SEPFAILED',
21 => 'PORTAL_PP_ACCOUNT_LOCKED',
22 => 'PORTAL_PP_PASSWORD_EXPIRED',
23 => 'PORTAL_CERTIFICATEREQUIRED',
24 => 'PORTAL_ERROR',
25 => 'PORTAL_PP_CHANGE_AFTER_RESET',
26 => 'PORTAL_PP_PASSWORD_MOD_NOT_ALLOWED',
27 => 'PORTAL_PP_MUST_SUPPLY_OLD_PASSWORD',
28 => 'PORTAL_PP_INSUFFICIENT_PASSWORD_QUALITY',
29 => 'PORTAL_PP_PASSWORD_TOO_SHORT',
30 => 'PORTAL_PP_PASSWORD_TOO_YOUNG',
31 => 'PORTAL_PP_PASSWORD_IN_HISTORY',
32 => 'PORTAL_PP_GRACE',
33 => 'PORTAL_PP_EXP_WARNING',
34 => 'PORTAL_PASSWORD_MISMATCH',
35 => 'PORTAL_PASSWORD_OK',
36 => 'PORTAL_NOTIFICATION',
37 => 'PORTAL_BADURL',
38 => 'PORTAL_NOSCHEME',
39 => 'PORTAL_BADOLDPASSWORD',
40 => 'PORTAL_MALFORMEDUSER',
41 => 'PORTAL_SESSIONNOTGRANTED',
42 => 'PORTAL_CONFIRM',
43 => 'PORTAL_MAILFORMEMPTY',
44 => 'PORTAL_BADMAILTOKEN',
45 => 'PORTAL_MAILERROR',
46 => 'PORTAL_MAILOK',
47 => 'PORTAL_LOGOUT_OK',
};
}
@ -81,7 +97,10 @@ sub run {
$lastMn = $mn;
# Activity collect
if (/^(\S+)\s+=>\s+(\S+)\s+(OK|REJECT|REDIRECT|LOGOUT|\-?\d+)$/) {
if (
/^(\S+)\s+=>\s+(\S+)\s+(OK|REJECT|REDIRECT|LOGOUT|UNPROTECT|\-?\d+)$/
)
{
my ( $user, $uri, $code ) = ( $1, $2, $3 );
# Portal error translation

View File

@ -29,13 +29,16 @@ sub locationRulesInit {
$locationCount->{$vhost} = 0;
foreach ( sort keys %{ $args->{locationRules}->{$vhost} } ) {
if ( $_ eq 'default' ) {
$defaultCondition->{$vhost} =
( $defaultCondition->{$vhost}, $defaultProtection->{$vhost} ) =
$class->conditionSub(
$args->{locationRules}->{$vhost}->{$_} );
}
else {
$locationCondition->{$vhost}->[ $locationCount->{$vhost} ] =
$class->conditionSub(
(
$locationCondition->{$vhost}->[ $locationCount->{$vhost} ],
$locationProtection->{$vhost}->[ $locationCount->{$vhost} ]
)
= $class->conditionSub(
$args->{locationRules}->{$vhost}->{$_} );
$locationRegexp->{$vhost}->[ $locationCount->{$vhost} ] =
qr/$_/;
@ -44,7 +47,8 @@ sub locationRulesInit {
}
# Default police
$defaultCondition->{$vhost} = $class->conditionSub('accept')
( $defaultCondition->{$vhost}, $defaultProtection->{$vhost} ) =
$class->conditionSub('accept')
unless ( $defaultCondition->{$vhost} );
}
1;
@ -96,6 +100,19 @@ sub sendHeaders {
}
}
## @rmethod protected boolean isProtected()
# @return True if URI isn't protected (rule "unprotect")
sub isProtected {
my ( $class, $uri ) = splice @_;
my $vhost = $apacheRequest->hostname;
for ( my $i = 0 ; $i < $locationCount->{$vhost} ; $i++ ) {
if ( $uri =~ $locationRegexp->{$vhost}->[$i] ) {
return $locationProtection->{$vhost}->[$i];
}
}
return $defaultProtection->{$vhost};
}
## @cmethod boolean grant()
# Grant or refuse client using compiled regexp and functions
# @return True if the user is granted to access to the current URL

View File

@ -26,18 +26,19 @@ our @ISA = qw(
#@return Lemonldap::NG::Manager object
sub new {
my ( $class, $args ) = @_;
# Try to load local configuration parameters
my $conf = Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
or Lemonldap::NG::Handler::CGI->abort( 'Unable to get configuration',
$Lemonldap::NG::Common::Conf::msg );
if ( my $localconf = $conf->getLocalConf(MANAGERSECTION) ) {
$args->{$_} ||= $localconf->{$_} foreach ( keys %$localconf );
}
my $self = $class->SUPER::new($args)
or $class->abort( 'Unable to start ' . __PACKAGE__,
'See Apache logs for more' );
# Try to load local configuration parameters
my $conf = Lemonldap::NG::Common::Conf->new( $self->{configStorage} );
my $localconf = $conf->getLocalConf(MANAGERSECTION);
if ($localconf) {
$self->{$_} = $args->{$_} || $localconf->{$_}
foreach ( keys %$localconf );
}
# Default values
$self->{managerSkin} = "default" unless defined $self->{managerSkin};
$self->{managerCss} = "accordion.css" unless defined $self->{managerCss};

View File

@ -541,8 +541,9 @@ access is granted or not depending on the boolean expression result&nbsp;:</p>
</pre>
<p> This rule means that all URL starting with '/protected', are reserved to
users member of 'group1'. You can also use 'accept' and 'deny' keywords.
'accept' keyword means that all authenticated users are granted.</p>
users member of 'group1'. You can also use 'accept', 'deny' and 'unprotect'
keywords. 'accept' keyword means that all authenticated users are granted,
'unprotect' means that authentication is not required.</p>
<p> If URL doesn't match any regular expression, 'default' rule is called to
grant or not.</p>
@ -607,9 +608,10 @@ Exemple&nbsp;:</p>
<p> La r&egrave;gle ci-dessus signifie que pour les URL commen&ccedil;ant par
'/protected', les utilisateurs doivent appartenir au groupe 'group1'. Vous
pouvez &eacute;galement utiliser les mots-clefs 'accept' et 'deny'. Attention,
'accept' signifie que tous les utilisateurs authentifi&eacute;s peuvent
acc&eacute;der.</p>
pouvez &eacute;galement utiliser les mots-clefs 'accept', 'deny' et
'unprotect'. Attention, 'accept' signifie que tous les utilisateurs
authentifi&eacute;s peuvent acc&eacute;der, 'unprotect' signifie que
l'authentification n'est pas obligatoire.</p>
<p>Si l'URL demand&eacute;e ne correspond &agrave; aucune des expressions
r&eacute;guli&egrave;res, le droit d'acc&egrave;s est calcul&eacute; &agrave;

View File

@ -597,7 +597,7 @@ sub testStruct {
keyTest => $pcre,
test => sub {
my $e = shift;
return 1 if ( $e eq 'accept' or $e eq 'deny' );
return 1 if ( $e =~ /^(?:accept|deny|unprotect)$/i );
if ( $e =~ s/^logout(?:_(?:app|sso|app_sso))?\s*// ) {
return (
$e =~ /^(?:https?:\/\/\S+)?$/
@ -609,9 +609,7 @@ sub testStruct {
},
warnTest => sub {
my $e = shift;
return ( 0, $assignMsg )
if ( $e =~ $assignTest
and $e !~ /^(?:accept|deny|logout)/ );
return ( 0, $assignMsg ) if ( $e =~ $assignTest );
1;
},
},

View File

@ -559,7 +559,7 @@ sub _compileRules {
sub _conditionSub {
my ( $self, $cond ) = splice @_;
return sub { 1 }
if ( $cond =~ /^accept$/i );
if ( $cond =~ /^(?:accept|unprotect)$/i );
return sub { 0 }
if ( $cond =~ /^(?:deny$|logout)/i );
$cond =~ s/\$date/&POSIX::strftime("%Y%m%d%H%M%S",localtime())/e;

View File

@ -356,7 +356,7 @@ sub _conditionSub {
my ( $self, $id, $cond ) = splice @_;
my $h = $self->getApacheSession( $id, 1 );
return sub { 1 }
if ( $cond =~ /^accept$/i );
if ( $cond =~ /^(?:accept|unprotect)$/i );
return sub { 0 }
if ( !$h or $cond =~ /^(?:deny$|logout)/i );
$cond =~ s/\$date/&POSIX::strftime("%Y%m%d%H%M%S",localtime())/e;