← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:14 2015

Filename/var/www/foswikidev/core/lib/Foswiki/LoginManager.pm
StatementsExecuted 173 statements in 5.59ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1113.48ms3.87msFoswiki::LoginManager::::BEGIN@57Foswiki::LoginManager::BEGIN@57
111638µs880µsFoswiki::LoginManager::::makeLoginManagerFoswiki::LoginManager::makeLoginManager
111116µs116µsFoswiki::LoginManager::::expireDeadSessionsFoswiki::LoginManager::expireDeadSessions
11164µs83µsFoswiki::LoginManager::::newFoswiki::LoginManager::new
11137µs153µsFoswiki::LoginManager::::loadSessionFoswiki::LoginManager::loadSession
51133µs46µsFoswiki::LoginManager::::endRenderingHandlerFoswiki::LoginManager::endRenderingHandler
11125µs92µsFoswiki::LoginManager::::userLoggedInFoswiki::LoginManager::userLoggedIn
11120µs92µsFoswiki::LoginManager::::_LOGOUTURLFoswiki::LoginManager::_LOGOUTURL
11119µs146µsFoswiki::LoginManager::::finishFoswiki::LoginManager::finish
11114µs24µsFoswiki::LoginManager::::_LOGINFoswiki::LoginManager::_LOGIN
11113µs26µsFoswiki::LoginManager::::BEGIN@51Foswiki::LoginManager::BEGIN@51
11110µs42µsFoswiki::LoginManager::::BEGIN@76Foswiki::LoginManager::BEGIN@76
11110µs127µsFoswiki::LoginManager::::completeFoswiki::LoginManager::complete
11110µs37µsFoswiki::LoginManager::::BEGIN@78Foswiki::LoginManager::BEGIN@78
31110µs10µsFoswiki::LoginManager::::getCGISessionFoswiki::LoginManager::getCGISession
1119µs13µsFoswiki::LoginManager::::BEGIN@52Foswiki::LoginManager::BEGIN@52
1119µs37µsFoswiki::LoginManager::::BEGIN@53Foswiki::LoginManager::BEGIN@53
1118µs10µsFoswiki::LoginManager::::checkAccessFoswiki::LoginManager::checkAccess
1118µs105µsFoswiki::LoginManager::::BEGIN@54Foswiki::LoginManager::BEGIN@54
5525µs5µsFoswiki::LoginManager::::__ANON__[:237]Foswiki::LoginManager::__ANON__[:237]
1115µs5µsFoswiki::LoginManager::::BEGIN@59Foswiki::LoginManager::BEGIN@59
1114µs4µsFoswiki::LoginManager::::BEGIN@56Foswiki::LoginManager::BEGIN@56
1114µs4µsFoswiki::LoginManager::::setSessionValueFoswiki::LoginManager::setSessionValue
1113µs3µsFoswiki::LoginManager::::clearSessionValueFoswiki::LoginManager::clearSessionValue
0000s0sFoswiki::LoginManager::::_AUTHENTICATEDFoswiki::LoginManager::_AUTHENTICATED
0000s0sFoswiki::LoginManager::::_CANLOGINFoswiki::LoginManager::_CANLOGIN
0000s0sFoswiki::LoginManager::::_IP2SIDFoswiki::LoginManager::_IP2SID
0000s0sFoswiki::LoginManager::::_LOGINURLFoswiki::LoginManager::_LOGINURL
0000s0sFoswiki::LoginManager::::_LOGOUTFoswiki::LoginManager::_LOGOUT
0000s0sFoswiki::LoginManager::::_SESSION_VARIABLEFoswiki::LoginManager::_SESSION_VARIABLE
0000s0sFoswiki::LoginManager::::__ANON__[:1573]Foswiki::LoginManager::__ANON__[:1573]
0000s0sFoswiki::LoginManager::::_addSessionCookieToResponseFoswiki::LoginManager::_addSessionCookieToResponse
0000s0sFoswiki::LoginManager::::_delSessionCookieFromResponseFoswiki::LoginManager::_delSessionCookieFromResponse
0000s0sFoswiki::LoginManager::::_dispLogonFoswiki::LoginManager::_dispLogon
0000s0sFoswiki::LoginManager::::_loadCreateCGISessionFoswiki::LoginManager::_loadCreateCGISession
0000s0sFoswiki::LoginManager::::_myScriptURLREFoswiki::LoginManager::_myScriptURLRE
0000s0sFoswiki::LoginManager::::_real_traceFoswiki::LoginManager::_real_trace
0000s0sFoswiki::LoginManager::::_rewriteFORMFoswiki::LoginManager::_rewriteFORM
0000s0sFoswiki::LoginManager::::_rewriteURLFoswiki::LoginManager::_rewriteURL
0000s0sFoswiki::LoginManager::::_skinSelectFoswiki::LoginManager::_skinSelect
0000s0sFoswiki::LoginManager::::forceAuthenticationFoswiki::LoginManager::forceAuthentication
0000s0sFoswiki::LoginManager::::getSessionValueFoswiki::LoginManager::getSessionValue
0000s0sFoswiki::LoginManager::::getSessionValuesFoswiki::LoginManager::getSessionValues
0000s0sFoswiki::LoginManager::::getUserFoswiki::LoginManager::getUser
0000s0sFoswiki::LoginManager::::isValidLoginNameFoswiki::LoginManager::isValidLoginName
0000s0sFoswiki::LoginManager::::loginUrlFoswiki::LoginManager::loginUrl
0000s0sFoswiki::LoginManager::::purge_userFoswiki::LoginManager::purge_user
0000s0sFoswiki::LoginManager::::redirectToLoggedOutUrlFoswiki::LoginManager::redirectToLoggedOutUrl
0000s0sFoswiki::LoginManager::::removeUserSessionsFoswiki::LoginManager::removeUserSessions
0000s0sFoswiki::LoginManager::::rewriteRedirectUrlFoswiki::LoginManager::rewriteRedirectUrl
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::LoginManager
6
7The package is also a Factory for login managers and also the base class
8for all login managers.
9
10On it's own, an object of this class is used when you specify 'none' in
11the security setup section of
12[[%SCRIPTURL{"configure"}%][configure]]. When it is used,
13logins are not supported. If you want to authenticate users then you should
14consider TemplateLogin or ApacheLogin, which are subclasses of this class.
15
16If you are building a new login manager, then you should write a new subclass
17of this class, implementing the methods marked as *VIRTUAL*. There are already
18examples in the =lib/Foswiki/LoginManager= directory.
19
20The class has extensive tracing, which is enabled by
21$Foswiki::cfg{Trace}{LoginManager}. The tracing is done in such a way as to
22let the perl optimiser optimise out the trace function as a no-op if tracing
23is disabled.
24
25Here's an overview of how it works:
26
27Early in Foswiki::new, the login manager is created. The creation of the login manager does two things:
28 1 If sessions are in use, it loads CGI::Session but doesn't initialise the session yet.
29 1 Creates the login manager object
30Slightly later in Foswiki::new, loginManager->loadSession is called.
31 1 Calls loginManager->getUser to get the username *before* the session is created
32 * Foswiki::LoginManager::ApacheLogin looks at REMOTE_USER (only for authenticated scripts)
33 * Foswiki::LoginManager::TemplateLogin just returns undef
34 1 If the NO_FOSWIKI_SESSION environment variable is defined, then no session is created and the username is returned. This might be defined for search engine bots, depending on how the web server is configured
35 1 Reads the FOSWIKISID cookie to get the SID (or the FOSWIKISID parameters in the CGI query if cookies aren't available, or IP2SID mapping if that's enabled).
36 1 Creates the CGI::Session object, and the session is thereby read.
37 1 If the username still isn't known, reads it from the cookie. Thus Foswiki::LoginManager::ApacheLogin overrides the cookie using REMOTE_USER, and Foswiki::LoginManager::TemplateLogin *always* uses the session.
38
39Later again in Foswiki::new, plugins are given a chance to *override* the username found from the loginManager.
40
41The last step in Foswiki::new is to find the user, using whatever user mapping manager is in place.
42
43---++ ObjectData =twiki=
44
45The Foswiki object this login manager is attached to.
46
47=cut
48
49package Foswiki::LoginManager;
50
51226µs240µs
# spent 26µs (13+13) within Foswiki::LoginManager::BEGIN@51 which was called: # once (13µs+13µs) by Foswiki::Users::BEGIN@64 at line 51
use strict;
# spent 26µs making 1 call to Foswiki::LoginManager::BEGIN@51 # spent 13µs making 1 call to strict::import
52222µs218µs
# spent 13µs (9+4) within Foswiki::LoginManager::BEGIN@52 which was called: # once (9µs+4µs) by Foswiki::Users::BEGIN@64 at line 52
use warnings;
# spent 13µs making 1 call to Foswiki::LoginManager::BEGIN@52 # spent 4µs making 1 call to warnings::import
53227µs265µs
# spent 37µs (9+28) within Foswiki::LoginManager::BEGIN@53 which was called: # once (9µs+28µs) by Foswiki::Users::BEGIN@64 at line 53
use Assert;
# spent 37µs making 1 call to Foswiki::LoginManager::BEGIN@53 # spent 28µs making 1 call to Exporter::import
54228µs2203µs
# spent 105µs (8+98) within Foswiki::LoginManager::BEGIN@54 which was called: # once (8µs+98µs) by Foswiki::Users::BEGIN@64 at line 54
use Error qw( :try );
# spent 105µs making 1 call to Foswiki::LoginManager::BEGIN@54 # spent 98µs making 1 call to Error::import
55
56218µs14µs
# spent 4µs within Foswiki::LoginManager::BEGIN@56 which was called: # once (4µs+0s) by Foswiki::Users::BEGIN@64 at line 56
use Foswiki::Sandbox ();
# spent 4µs making 1 call to Foswiki::LoginManager::BEGIN@56
572123µs13.87ms
# spent 3.87ms (3.48+388µs) within Foswiki::LoginManager::BEGIN@57 which was called: # once (3.48ms+388µs) by Foswiki::Users::BEGIN@64 at line 57
use CGI::Session ();
# spent 3.87ms making 1 call to Foswiki::LoginManager::BEGIN@57
58
59
# spent 5µs within Foswiki::LoginManager::BEGIN@59 which was called: # once (5µs+0s) by Foswiki::Users::BEGIN@64 at line 64
BEGIN {
6015µs if ( $Foswiki::cfg{UseLocale} ) {
61 require locale;
62 import locale();
63 }
64177µs15µs}
# spent 5µs making 1 call to Foswiki::LoginManager::BEGIN@59
65
66# Marker chars
671600nsour $M1 = chr(5);
681100nsour $M2 = chr(6);
691200nsour $M3 = chr(7);
70
71# Some session keys are secret (not to be given to the browser) and
72# others read only (not to be changed from the browser)
7312µsour %secretSK = ( STRIKEONESECRET => 1, VALID_ACTIONS => 1 );
7413µsour %readOnlySK = ( %secretSK, AUTHUSER => 1, SUDOFROMAUTHUSER => 1 );
75
76242µs273µs
# spent 42µs (10+31) within Foswiki::LoginManager::BEGIN@76 which was called: # once (10µs+31µs) by Foswiki::Users::BEGIN@64 at line 76
use constant TRACE => $Foswiki::cfg{Trace}{LoginManager} || 0;
# spent 42µs making 1 call to Foswiki::LoginManager::BEGIN@76 # spent 31µs making 1 call to constant::import
77
7824.81ms263µs
# spent 37µs (10+27) within Foswiki::LoginManager::BEGIN@78 which was called: # once (10µs+27µs) by Foswiki::Users::BEGIN@64 at line 78
use constant CGIDRIVER => 'driver:File;serializer:Storable';
# spent 37µs making 1 call to Foswiki::LoginManager::BEGIN@78 # spent 27µs making 1 call to constant::import
79
80# GusestSessions should default to enabled, since much of Foswiki depends on
81# having a valid session.
8213µsmy $guestSessions =
83 ( !defined $Foswiki::cfg{Sessions}{EnableGuestSessions}
84 || $Foswiki::cfg{Sessions}{EnableGuestSessions} );
85
86=begin TML
87
88---++ StaticMethod makeLoginManager( $session ) -> $Foswiki::LoginManager
89
90Factory method, used to generate a new Foswiki::LoginManager object
91for the given session.
92
93=cut
94
95
# spent 880µs (638+242) within Foswiki::LoginManager::makeLoginManager which was called: # once (638µs+242µs) by Foswiki::Users::new at line 108 of /var/www/foswikidev/core/lib/Foswiki/Users.pm
sub makeLoginManager {
961700ns my $session = shift;
97
98 ASSERT( $session->isa('Foswiki') ) if DEBUG;
99
100 #user is trying to sudo login - use BaseUserMapping
10114µs131µs if ( $session->{request}->param('sudo') ) {
# spent 31µs making 1 call to Foswiki::Request::param
102
103 #promote / login to internal wiki admin
104 $session->enterContext('sudo_login');
105 }
106
10714µs14µs if ( $Foswiki::cfg{UseClientSessions}
# spent 4µs making 1 call to Foswiki::inContext
108 && !$session->inContext('command_line') )
109 {
110
111 my $sessionname;
112 my $use = 'use Foswiki::LoginManager::Session';
113 if ( $Foswiki::cfg{Sessions}{UseIPMatching} ) {
114 $use .= ' qw(-ip_match)';
115 }
116 $use .= '; use CGI::Cookie ()';
117 eval $use;
118 throw Error::Simple($@) if $@;
119 if ( $session->{request}->https() ) {
120 $sessionname = 'SFOSWIKISID';
121 }
122 else {
123 $sessionname = 'FOSWIKISID';
124 }
125 if ( $Foswiki::LoginManager::Session::VERSION eq '4.10' ) {
126
127 # 4.10 is broken; see Item1989
128 $Foswiki::LoginManager::Session::NAME = $sessionname;
129 }
130 else {
131 Foswiki::LoginManager::Session->name($sessionname);
132 }
133 }
134
1351200ns my $mgr;
13612µs if ( $Foswiki::cfg{LoginManager} eq 'none' ) {
137
138 # No login manager; just use default behaviours
139 $mgr = new Foswiki::LoginManager($session);
140 }
141 else {
142
143 # Rename from old "Client" to new "LoginManager" - see TWikibug:Item3375
14412µs $Foswiki::cfg{LoginManager} =~ s/::Client::/::LoginManager::/;
1451800ns my $loginManager = $Foswiki::cfg{LoginManager};
14611µs12µs if ( $session->inContext('sudo_login') )
# spent 2µs making 1 call to Foswiki::inContext
147 { #TODO: move selection into BaseUserMapper
148 $loginManager = 'Foswiki::LoginManager::TemplateLogin';
149 }
150122µs eval "require $loginManager";
# spent 82µs executing statements in string eval
1511400ns die $@ if $@;
15214µs1117µs $mgr = $loginManager->new($session);
# spent 117µs making 1 call to Foswiki::LoginManager::ApacheLogin::new
153 }
15413µs return $mgr;
155}
156
157=begin TML
158
159---++ ClassMethod new ($session, $impl)
160
161Construct the user management object
162
163=cut
164
165# protected: Construct new client object.
166
# spent 83µs (64+20) within Foswiki::LoginManager::new which was called: # once (64µs+20µs) by Foswiki::LoginManager::ApacheLogin::new at line 48 of /var/www/foswikidev/core/lib/Foswiki/LoginManager/ApacheLogin.pm
sub new {
1671900ns my ( $class, $session ) = @_;
16818µs my $this = bless(
169 {
170 session => $session,
171 twiki => $session, # backwards compatibility
172 _haveCookie => 0,
173 },
174 $class
175 );
176
177 # make sure the filePermission setting has got a sensible default
17812µs $Foswiki::cfg{Session}{filePermission} = 0600
179 unless defined $Foswiki::cfg{Session}{filePermission};
180
18113µs15µs $session->leaveContext('can_login');
# spent 5µs making 1 call to Foswiki::leaveContext
182118µs map { $this->{_authScripts}{$_} = 1; }
183 split( /[\s,]+/, $Foswiki::cfg{AuthScripts} );
184
185 # register tag handlers and values
18613µs13µs Foswiki::registerTagHandler( 'LOGINURL', \&_LOGINURL );
# spent 3µs making 1 call to Foswiki::registerTagHandler
18712µs12µs Foswiki::registerTagHandler( 'LOGIN', \&_LOGIN );
# spent 2µs making 1 call to Foswiki::registerTagHandler
18812µs12µs Foswiki::registerTagHandler( 'LOGOUT', \&_LOGOUT );
# spent 2µs making 1 call to Foswiki::registerTagHandler
18912µs12µs Foswiki::registerTagHandler( 'LOGOUTURL', \&_LOGOUTURL );
# spent 2µs making 1 call to Foswiki::registerTagHandler
19012µs12µs Foswiki::registerTagHandler( 'SESSION_VARIABLE', \&_SESSION_VARIABLE );
# spent 2µs making 1 call to Foswiki::registerTagHandler
19112µs12µs Foswiki::registerTagHandler( 'AUTHENTICATED', \&_AUTHENTICATED );
# spent 2µs making 1 call to Foswiki::registerTagHandler
19212µs12µs Foswiki::registerTagHandler( 'CANLOGIN', \&_CANLOGIN );
# spent 2µs making 1 call to Foswiki::registerTagHandler
193
19414µs return $this;
195}
196
197=begin TML
198
199---++ ObjectMethod finish()
200Break circular references.
201
202=cut
203
204# Note to developers; please undef *all* fields in the object explicitly,
205# whether they are references or not. That way this method is "golden
206# documentation" of the live fields in the object.
207
# spent 146µs (19+126) within Foswiki::LoginManager::finish which was called: # once (19µs+126µs) by Foswiki::Users::finish at line 163 of /var/www/foswikidev/core/lib/Foswiki/Users.pm
sub finish {
2081700ns my $this = shift;
20917µs1127µs $this->complete(); # call to flush the session if not already done
# spent 127µs making 1 call to Foswiki::LoginManager::complete
21016µs undef $this->{_authScripts};
21111µs undef $this->{_cgisession};
2121500ns undef $this->{_haveCookie};
2131500ns undef $this->{_MYSCRIPTURL};
21414µs undef $this->{session};
215}
216
217=begin TML
218
219---++ ClassMethod _real_trace ($session, $impl)
220
221Construct the user management object
222
223=cut
224
225sub _real_trace {
226 my ( $this, $mess ) = @_;
227 my $id =
228 'SESSION ' . ( $this->{_cgisession} ? $this->{_cgisession}->id() : '?' );
229 $id .= '(c)' if $this->{_haveCookie};
230 print STDERR "$id: $mess\n";
231}
232
2331600nsif (TRACE) {
234 *_trace = \&_real_trace;
235}
236else {
237619µs
# spent 5µs within Foswiki::LoginManager::__ANON__[/var/www/foswikidev/core/lib/Foswiki/LoginManager.pm:237] which was called 5 times, avg 980ns/call: # once (2µs+0s) by Foswiki::LoginManager::loadSession at line 300 # once (900ns+0s) by Foswiki::LoginManager::userLoggedIn at line 814 # once (700ns+0s) by Foswiki::LoginManager::ApacheLogin::getUser at line 152 of /var/www/foswikidev/core/lib/Foswiki/LoginManager/ApacheLogin.pm # once (700ns+0s) by Foswiki::LoginManager::userLoggedIn at line 798 # once (700ns+0s) by Foswiki::LoginManager::loadSession at line 311
*_trace = sub { undef };
238}
239
240=begin TML
241
242---++ ClassMethod _IP2SID ($session, $impl)
243
244 read/write IP to SID map, return SID
245
246=cut
247
248sub _IP2SID {
249 my ( $this, $sid ) = @_;
250
251 my $ip = $this->{session}->{request}->address;
252
253 return unless $ip; # no IP address, can't map
254
255 my %ips;
256 my $IPMAP;
257 if ( open( $IPMAP, '<', $Foswiki::cfg{WorkingDir} . '/tmp/ip2sid' ) ) {
258 local $/ = undef;
259 %ips = map { split( /:/, $_ ) } split( /\r?\n/, <$IPMAP> );
260 close($IPMAP);
261 }
262 if ($sid) {
263
264 # known SID, map the IP addr to it
265 $ips{$ip} = $sid;
266 open( $IPMAP, '>', $Foswiki::cfg{WorkingDir} . '/tmp/ip2sid' )
267 || die
268"Failed to open ip2sid map for write. Ask your administrator to make sure that the {Sessions}{Dir} is writable by the webserver user.";
269 print $IPMAP map { "$_:$ips{$_}\n" } keys %ips;
270 close($IPMAP);
271 }
272 else {
273
274 # Return the SID for this IP address
275 $sid = $ips{$ip};
276 }
277 return $sid;
278}
279
280=begin TML
281
282---++ ObjectMethod loadSession($defaultUser, $pwchecker) -> $login
283
284Get the client session data, using the cookie and/or the request URL.
285Set up appropriate session variables in the session object and return
286the login name.
287
288$pwchecker is a pointer to an object that implements checkPassword
289
290$defaultUser is a username to use if one is not available from other
291sources. The username passed when you create a Foswiki instance is
292passed in here.
293
294=cut
295
296
# spent 153µs (37+116) within Foswiki::LoginManager::loadSession which was called: # once (37µs+116µs) by Foswiki::Users::loadSession at line 145 of /var/www/foswikidev/core/lib/Foswiki/Users.pm
sub loadSession {
2971900ns my ( $this, $defaultUser, $pwchecker ) = @_;
2981700ns my $session = $this->{session};
299
30012µs12µs _trace( $this, "loadSession" );
301
3021500ns $defaultUser = $Foswiki::cfg{DefaultUserLogin}
303 unless ( defined($defaultUser) );
304
305 # Try and get the user from the webserver. This is referred to as
306 # the "webserver user". the webserver user is authenticated by some
307 # means beyond foswiki e.g. Basic Auth. getUser is defined in the
308 # LoginManager. The default returns undef.
309
31012µs115µs my $authUser = $this->getUser($this);
# spent 15µs making 1 call to Foswiki::LoginManager::ApacheLogin::getUser
31111µs1700ns _trace( $this, "Webserver says user is $authUser" ) if ($authUser);
312
313 # If the NO_FOSWIKI_SESSION environment variable is defined, then
314 # do not create the session. This might be defined if the request
315 # is made by a search engine bot, depending on how the web server
316 # is configured
317
31811µs if ( $ENV{NO_FOSWIKI_SESSION} ) {
319 _trace( $this, "ENV{NO_FOSWIKI_SESSION} blocked the session" );
320 return $authUser;
321 }
322
323 # Client sessions processing either cookie or IP2SID based:
32412µs12µs if ( $Foswiki::cfg{UseClientSessions}
# spent 2µs making 1 call to Foswiki::inContext
325 && !$session->inContext('command_line')
326 && $Foswiki::cfg{WorkingDir} )
327 {
328 $this->{_haveCookie} = $session->{request}->header('Cookie');
329
330 _trace( $this,
331 $this->{_haveCookie}
332 ? "Cookie $this->{_haveCookie}"
333 : "No cookie " );
334
335 # rough check - if we have a cookie for the SID,
336 # or we are using IP2SID mapping then keep going.
337 if (
338 (
339 $this->{_haveCookie}
340 && $this->{_haveCookie} =~ m/FOSWIKISID=([^\s]*)/
341 )
342 || $Foswiki::cfg{Sessions}{MapIP2SID}
343 )
344 {
345 _trace( $this, " ... Found session id $1" )
346 unless ( $Foswiki::cfg{Sessions}{MapIP2SID} );
347
348 if ( $Foswiki::cfg{Sessions}{MapIP2SID} ) {
349
350 # map the end user IP address to a session ID
351 my $sid = $this->_IP2SID();
352
353 $this->{_cgisession} = $this->_loadCreateCGISession($sid);
354
355 _trace( $this, "New IP2SID session" ) unless ($sid);
356 $this->_IP2SID( $this->{_cgisession}->id() );
357 }
358 else {
359
360 # IP mapping is off; use the request cookie
361 $this->{_cgisession} =
362 $this->_loadCreateCGISession( $session->{request} );
363
364 }
365
366 die Foswiki::LoginManager::Session->errstr()
367 unless $this->{_cgisession};
368
369 # Get the authorised user stored in the session
370
371 my $sessionUser = Foswiki::Sandbox::untaintUnchecked(
372 $this->{_cgisession}->param('AUTHUSER') );
373
374 _trace( $this, "AUTHUSER from session is $sessionUser" )
375 if defined $sessionUser;
376
377 # If we are bootstrapping, and the defaultUser from Foswiki.pm is admin
378 # Then override the session user to become admin. This gets around a stale
379 # browser cookie from blocking the bootstrap admin login.
380
381 $authUser = $defaultUser
382 if ( $Foswiki::cfg{isBOOTSTRAPPING}
383 && $defaultUser eq 'admin' );
384
385 _trace( $this, "AUTHUSER after BOOTSTRAP check is $authUser" )
386 if defined $authUser;
387
388 # An admin user stored in the session can override the webserver
389 # user; handy for sudo
390
391 $authUser = $sessionUser
392 if ( !defined($authUser)
393 || $sessionUser
394 && $sessionUser eq $Foswiki::cfg{AdminUserLogin} );
395 }
396 }
397
398 # Checking for URI parameters
3991500ns if ( !$authUser ) {
400
401 _trace( $this, "No session, checking URI Params for a user" );
402
403 # if we couldn't get the login manager or the http session to tell
404 # us who the user is, check the username and password URI params.
405 #
406 # Note that this code only applies to scripts other than login.
407 # The login script is handled separately.
408
409 my $script = $session->{request}->base_action();
410
411 my $login; # username from CLI/URI parameters
412 my $pass; # password from CLI/URI parameters
413
414 # If we are in the CLI environment, then
415 # the only option is to pass "URL parameters"
416 # - The CLI overrides the "defaultUser" to
417 # be admin. CLI runs as admin by default.
418 # - -username/-password parameters allow
419 # CLI to use conventional authentication
420
421 if ( $session->inContext('command_line')
422 && $session->{request}->param('username') )
423 {
424 $login = $session->{request}->param('username');
425 $pass = $session->{request}->param('password') || '';
426 $session->{request}->delete( 'username', 'password' );
427
428 # CLI defaults to Admin User, but if a
429 # user/pass was provided on the cli, and was wrong,
430 # we probably don't want to fall back
431 # to Admin, so override the default.
432 $defaultUser = $Foswiki::cfg{DefaultUserLogin};
433
434 _trace( $this,
435"CLI Credentials $login accepted from command line for further validation"
436 );
437 }
438
439 # If the configuration allows URL params,
440 # and the correct HTTP method is in use,
441 # Then accept the username & password,
442 # and delete them from the request to avoid
443 # them being accessed later.
444
445 if ( !$login ) {
446 if ( defined $session->{request}->param('username')
447 && defined $Foswiki::cfg{Session}{AcceptUserPwParam}
448 && $script =~ m/$Foswiki::cfg{Session}{AcceptUserPwParam}/ )
449 {
450 if (
451 $Foswiki::cfg{Session}{AcceptUserPwParamOnGET}
452 || ( defined $session->{request}->method()
453 && uc( $session->{request}->method() ) eq 'POST' )
454 )
455 {
456 $login = $session->{request}->param('username');
457 $pass = $session->{request}->param('password');
458 $session->{request}->delete( 'username', 'password' );
459 _trace( $this,
460 "URI Credentials $login accepted for further validation"
461 );
462 }
463 }
464 }
465
466 # Implements the X-Authorization header if one is present
467 # Nothing was in the query params. Check query headers.
468 if ( !$login ) {
469
470 my $auth = $session->{request}->http('X-Authorization');
471 if ( defined $auth ) {
472 _trace( $this, "X-Authorization: $auth" );
473 if ( $auth =~ m/^FoswikiBasic (.+)$/ ) {
474
475 # If the user agent wishes to send the userid "Aladdin"
476 # and password "open sesame", it would use the following
477 # header field:
478 # Authorization: Foswiki QWxhZGRpbjpvcGVuIHNlc2FtZQ==
479 require MIME::Base64;
480 my $cred = MIME::Base64::decode_base64($1);
481 if ( $cred =~ m/:/ ) {
482 ( $login, $pass ) = split( ':', $cred, 2 );
483 _trace( $this,
484"Login credentials taken from query headers for further validation"
485 );
486 }
487 } # TODO: implement FoswikiDigest here
488 }
489 }
490
491 # A login credential was found, verify the userid & password
492 if ( $login && defined $pass && $pwchecker ) {
493 _trace( $this, "Validating password for $login" );
494 my $validation = $pwchecker->checkPassword( $login, $pass );
495 unless ($validation) {
496 _trace( $this,
497 "URI validation FAILED: Falling back to $defaultUser" );
498
499 # Item1953: You might think that this is needed:
500 # $res->header( -type => 'text/html', -status => '401' );
501 # throw Foswiki::EngineException( 401, $err, $res );
502 # but it would be wrong, because it would require the
503 # exception to be handled before the session object is
504 # properly initialised, which would cause an error.
505 # Instead, we do this, and let the caller handle the error.
506 undef $login;
507 }
508 $authUser = $login || $defaultUser;
509 _trace( $this, "After password validation, user is: $authUser" );
510 }
511 else {
512
513 # Last ditch attempt; if a user was passed in to this function,
514 # then use it (it is normally {remoteUser} from the session object
515 # or the -user parameter in the command_line (defaults to admin)
516 # Also used in unit tests when creating a newFoswikiSession
517 $authUser = $defaultUser;
518 _trace( $this, "Falling back to DEFAULT USER: $defaultUser" )
519 if $authUser;
520
521 }
522 }
523
524 # We should have a user at this point; or $defaultUser if there
525 # was no better information available.
526
527 # is this a logout?
52812µs if (
529 ( $authUser && $authUser ne $Foswiki::cfg{DefaultUserLogin} )
530 && ( $this->{_cgisession}
531 && $session->{request}
532 && $session->{request}->param('logout') )
533 )
534 {
535
536 # SMELL: is there any way to get evil data into the CGI session such
537 # that this untaint is less than safe?
538 my $sudoUser = Foswiki::Sandbox::untaintUnchecked(
539 $this->{_cgisession}->param('SUDOFROMAUTHUSER') );
540
541 if ($sudoUser) {
542 _trace( $this, "User is logging out from $sudoUser" );
543 $session->logger->log(
544 {
545 level => 'info',
546 action => 'sudo logout',
547 extra => 'from ' . ( $authUser || '' ),
548 user => $sudoUser
549 }
550 );
551 $this->{_cgisession}->clear('SUDOFROMAUTHUSER');
552 $authUser = $sudoUser;
553 }
554 else {
555 $authUser =
556 $this->redirectToLoggedOutUrl( $authUser, $defaultUser );
557 }
558 }
55913µs15µs $session->{request}->delete('logout');
# spent 5µs making 1 call to Foswiki::Request::delete
560
561 # SMELL: EXPERIMENTAL - Guest sessions can be made optional:
562 # - unset $Foswiki::cfg{Sessions}{EnableGuestSessions}
563 # No sense creating or keeping sessions for guest users.
564 # Note that if guests can comment, update, or otherwise POST to
565 # Foswiki, then Guest Sessions should be enabled.
566
567 # Call to getLoggedIn inserts the auth user into the cgi session
56815µs192µs $this->userLoggedIn($authUser)
# spent 92µs making 1 call to Foswiki::LoginManager::userLoggedIn
569 unless ( $authUser eq $Foswiki::cfg{DefaultUserLogin}
570 && !$guestSessions );
571
572 # Cleanup unused guest sessions
5731700ns if ( $this->{_cgisession}
574 && !$guestSessions
575 && $authUser eq $Foswiki::cfg{DefaultUserLogin} )
576 {
577 $this->{_cgisession}->delete();
578 $this->{_cgisession}->flush();
579 $this->{_cgisession} = undef;
580 $this->_delSessionCookieFromResponse();
581 }
582
5831800ns if ( $this->{_cgisession} ) {
584 $session->{prefs}->setInternalPreferences(
585 SESSIONID => $this->{_cgisession}->id(),
586 SESSIONVAR => $CGI::Session::NAME
587 );
588
589 # Restore CGI Session parameters
590 for ( $this->{_cgisession}->param ) {
591 my $value = $this->{_cgisession}->param($_);
592 $session->{prefs}->setInternalPreferences( $_ => $value );
593 $this->_trace( "Setting internal preference $_ to "
594 . ( $value ? $value : 'null' ) );
595 }
596
597 # May end up doing this several times; but this is the only place
598 # if should really need to be done, unless someone allocates a
599 # new response object.
600 $this->_addSessionCookieToResponse();
601 }
602
60316µs return $authUser;
604}
605
606=begin TML
607
608---++ ObjectMethod redirectToLoggedOutUrl($authUser, $defaultUser)
609
610Helper method, called by loadSession, to redirect to the non-authenticated url and return the non-authenticated "default user" login name.
611
612$authUser is the currently logged in user, derived from the request's username.
613
614$defaultUser is a username to use if one is not available from other
615sources. The username passed when you create a Foswiki instance is
616passed in here.
617
618=cut
619
620sub redirectToLoggedOutUrl {
621 my ( $this, $authUser, $defaultUser ) = @_;
622 _trace( $this, "User is logging out" );
623
624 my $session = $this->{session};
625 $defaultUser = $Foswiki::cfg{DefaultUserLogin}
626 unless ( defined($defaultUser) );
627
628 $session->logger->log(
629 {
630 level => 'info',
631 action => 'logout',
632 extra => "AUTHENTICATION LOGOUT - $authUser - ",
633 user => $authUser
634 }
635 );
636
637 #TODO: consider if we should risk passing on the urlparams on logout
638 my $path_info = Foswiki::urlDecode( $session->{request}->path_info() );
639 if ( my $topic = $session->{request}->param('topic') )
640 { #we should at least respect the ?topic= request
641 my $topicRequest = Foswiki::Sandbox::untaintUnchecked($topic);
642 ( my $web, $topic ) =
643 $this->{session}->normalizeWebTopicName( undef, $topicRequest );
644 $path_info = '/' . $web . '/' . $topic;
645 }
646
647 if ( $path_info =~ m/['"]/ ) {
648 $path_info = substr( $path_info, 0, ( ( pos $path_info ) - 1 ) );
649 }
650
651 $path_info = Foswiki::urlEncode($path_info);
652
653 my $redirectUrl;
654 if ( $Foswiki::cfg{ForceDefaultUrlHost} ) {
655
656 # ForceDefaultUrlHost enabled - probably a reverse proxy.
657 $path_info ||= '';
658 $redirectUrl = $session->{urlHost} . $path_info;
659 }
660 elsif ($path_info) {
661 $redirectUrl = $session->{request}->url() . $path_info;
662 }
663 else {
664 $redirectUrl = $session->{request}->referer();
665 }
666
667 #lets avoid infinite loops
668 $session->{request}->delete('logout');
669 $authUser = $defaultUser;
670 $session->redirect( $redirectUrl, 0 );
671
672 return $authUser;
673}
674
675=begin TML
676
677---++ ObjectMethod checkAccess()
678
679Check if the script being run in this session is authorised for execution.
680If not, throw an access control exception.
681
682=cut
683
684
# spent 10µs (8+2) within Foswiki::LoginManager::checkAccess which was called: # once (8µs+2µs) by Foswiki::UI::__ANON__[/var/www/foswikidev/core/lib/Foswiki/UI.pm:376] at line 373 of /var/www/foswikidev/core/lib/Foswiki/UI.pm
sub checkAccess {
685
6861900ns return unless ( $Foswiki::cfg{UseClientSessions} );
687
6881300ns my $this = shift;
6891800ns my $session = $this->{session};
690
69115µs12µs return if $session->inContext('command_line');
# spent 2µs making 1 call to Foswiki::inContext
692
693 unless ( $session->inContext('authenticated')
694 || $Foswiki::cfg{LoginManager} eq 'none' )
695 {
696
697 # This checks the *base_action* which is the action in the
698 # request *before* any request cache was restored. Otherwise
699 # you can end up with an infinite loop - see
700 # Foswiki:Development.FoswikiRedirectCache
701 my $action = $session->{request}->base_action();
702
703 if ( defined $action && $this->{_authScripts}{$action} ) {
704 my $topic = $session->{topicName};
705 my $web = $session->{webName};
706 require Foswiki::AccessControlException;
707 throw Foswiki::AccessControlException( $action, $session->{user},
708 $web, $topic, $action . ' requires authentication' );
709 }
710 }
711}
712
713=begin TML
714
715---++ ObjectMethod complete()
716
717Complete processing after the client's HTTP request has been responded
718to. Flush the user's session (if any) to disk.
719
720=cut
721
722
# spent 127µs (10+116) within Foswiki::LoginManager::complete which was called: # once (10µs+116µs) by Foswiki::LoginManager::finish at line 209
sub complete {
7231400ns my $this = shift;
724
7251600ns if ( $this->{_cgisession} ) {
726 $this->{_cgisession}->flush();
727 die $this->{_cgisession}->errstr()
728 if $this->{_cgisession}->errstr();
729 }
730
73112µs return unless ( $Foswiki::cfg{Sessions}{ExpireAfter} > 0 );
732
73317µs1116µs expireDeadSessions();
# spent 116µs making 1 call to Foswiki::LoginManager::expireDeadSessions
734}
735
736=begin TML
737
738---++ StaticMethod expireDeadSessions()
739
740Delete sessions and passthrough files that are sitting around but are really expired.
741This *assumes* that the sessions are stored as files.
742
743This is a static method, but requires Foswiki::cfg. It is designed to be
744run from a session or from a cron job.
745
746=cut
747
748
# spent 116µs within Foswiki::LoginManager::expireDeadSessions which was called: # once (116µs+0s) by Foswiki::LoginManager::complete at line 733
sub expireDeadSessions {
7491800ns return unless $Foswiki::cfg{WorkingDir};
750
7511800ns my $time = time() || 0;
75211µs my $exp = $Foswiki::cfg{Sessions}{ExpireAfter} || 36000; # 10 hours
7531800ns $exp = -$exp if $exp < 0;
754
755136µs opendir( D, "$Foswiki::cfg{WorkingDir}/tmp" ) || return;
756113µs foreach my $file ( readdir(D) ) {
757
758 # Validate
759914µs next unless $file =~ m/^((passthru|cgisess)_[0-9a-f]{32})$/;
76064µs $file = $1; # untaint validated file name
761
762630µs my @stat = stat("$Foswiki::cfg{WorkingDir}/tmp/$file");
763
764 # CGI::Session updates the session file each time a browser views a
765 # topic setting the access and expiry time as values in the file. This
766 # also sets the mtime (modification time) for the file which is all
767 # we need. We know that the expiry time is mtime +
768 # $Foswiki::cfg{Sessions}{ExpireAfter} so we do not need to waste
769 # execution time opening and reading the file. We just check the
770 # mtime. As a fallback we also check ctime. Files are deleted when
771 # they expire.
77262µs my $lat = $stat[9] || $stat[10] || 0;
77363µs unlink "$Foswiki::cfg{WorkingDir}/tmp/$file"
774 if ( $time - $lat >= $exp );
77563µs next;
776 }
777110µs closedir D;
778}
779
780=begin TML
781
782---++ ObjectMethod userLoggedIn( $authUser, $wikiname)
783
784Called when the user is known. It's invoked from Foswiki::UI::Register::finish
785and from loadSession (above) once credentials are validated.
786 1 when the user follows the link in their verification email message
787 2 or when the session store is read
788 3 when the user authenticates (via templatelogin / sudo)
789
790 * =$authUser= - string login name
791 * =$wikiname= - string wikiname
792
793=cut
794
795
# spent 92µs (25+67) within Foswiki::LoginManager::userLoggedIn which was called: # once (25µs+67µs) by Foswiki::LoginManager::loadSession at line 568
sub userLoggedIn {
79611µs my ( $this, $authUser, $wikiName ) = @_;
797
79812µs1700ns _trace( $this,
799 "userLoggedIn called with "
800 . ( $authUser || 'undef' ) . " - "
801 . ( $wikiName || 'undef' ) );
802
8031700ns my $session = $this->{session};
80414µs161µs if ( $session->{users} ) {
# spent 61µs making 1 call to Foswiki::Users::getCanonicalUserID
805 $session->{user} = $session->{users}->getCanonicalUserID($authUser);
806 }
807
80812µs12µs if ( $session->inContext('command_line') ) {
# spent 2µs making 1 call to Foswiki::inContext
809
810 # Command line is automatically 'authenticated' unless the guest user
811 # is explicitly requested. No need for cgi sessions so just return.
81212µs12µs $session->enterContext('authenticated')
# spent 2µs making 1 call to Foswiki::enterContext
813 if ( $authUser && $authUser ne $Foswiki::cfg{DefaultUserLogin} );
81412µs1900ns _trace( $this,
815 'userLoggedIn: CLI Session established for: ' . $authUser );
81613µs return;
817 }
818
819 if ( $Foswiki::cfg{UseClientSessions} ) {
820
821 # create new session if necessary
822 unless ( $this->{_cgisession} ) {
823
824 _trace( $this,
825 "Creating a new client session - _cgisession is empty" );
826 $this->{_cgisession} =
827 $this->_loadCreateCGISession( $session->{request} );
828
829 die Foswiki::LoginManager::Session->errstr()
830 unless $this->{_cgisession};
831 }
832 }
833
834 my $sessUser = $this->{_cgisession}->param('AUTHUSER')
835 || $Foswiki::cfg{DefaultUserLogin};
836
837 _trace( $this, "==== Initial user is $sessUser" );
838 _trace( $this,
839 "==== Remote user is " . $session->{request}->remoteUser() )
840 if defined $session->{request}->remoteUser();
841
842 if ( $authUser && $authUser ne $Foswiki::cfg{DefaultUserLogin} ) {
843 _trace( $this,
844 'Authenticated; converting from '
845 . ( $session->{remoteUser} || $sessUser || 'undef' ) . ' to '
846 . $authUser
847 . ' - default '
848 . $Foswiki::cfg{DefaultUserLogin} );
849
850 # SMELL: right now anyone that makes a template login url can log
851 # in multiple times - should i forbid it
852 if ( $Foswiki::cfg{UseClientSessions} ) {
853 if ( defined( $session->{remoteUser} )
854 && $session->inContext('sudo_login') )
855 {
856 $session->logger->log(
857 {
858 level => 'info',
859 action => 'sudo login',
860 extra => 'from ' . ( $session->{remoteUser} || '' ),
861 user => $authUser
862 }
863 );
864 $this->{_cgisession}
865 ->param( 'SUDOFROMAUTHUSER', $session->{remoteUser} );
866 }
867
868 # SMELL: these are bare logins, so if and when there are
869 # multiple usermappings, this would need to include cUID..
870 $this->{_cgisession}->param( 'AUTHUSER', $authUser );
871 }
872 $session->enterContext('authenticated');
873 }
874 else {
875 _trace( $this, "Session is NOT authenticated" );
876
877 # if we are not authenticated, expire any existing session
878 $this->{_cgisession}->clear( ['AUTHUSER'] )
879 if ( $Foswiki::cfg{UseClientSessions} );
880 $session->leaveContext('authenticated');
881 }
882 if ( $Foswiki::cfg{UseClientSessions} ) {
883
884 # The user has changed. Create a new session.
885 if ( $sessUser ne $authUser ) {
886
887 my $oldid = $this->{_cgisession}->id();
888 my $dataref = $this->{_cgisession}->dataref();
889
890# SMELL: Needed to both delete and undef the old sesson or for some reason
891# Session->new() manages to find / use the old session and the ID doesn't change
892 $this->{_cgisession}->delete();
893 $this->{_cgisession}->flush();
894 $this->{_cgisession} = undef;
895
896 # Don't make a session for the guest user.
897 unless ( $authUser eq $Foswiki::cfg{DefaultUserLogin}
898 && !$guestSessions )
899
900 {
901 $this->{_cgisession} =
902 $this->_loadCreateCGISession( $session->{request} );
903 _trace( $this,
904 "Changed SID from $oldid to "
905 . $this->{_cgisession}->id() );
906 foreach my $key ( keys %$dataref ) {
907 next if ( substr( $key, 0, 1 ) eq '_' );
908 $this->{_cgisession}->param( $key, $dataref->{$key} );
909 _trace( $this,
910 " - $key = " . ( $dataref->{$key} || 'undef' ) );
911 }
912
913 }
914 }
915
916 if ( $this->{_cgisession} ) {
917
918 # flush the session, to try to fix Item1820 and Item2234
919 $this->{_cgisession}->flush();
920 die $this->{_cgisession}->errstr()
921 if $this->{_cgisession}->errstr();
922 }
923 }
924}
925
926=begin TML
927
928---++ ObjectMethod _myScriptURLRE ($thisl)
929
930=cut
931
932# get an RE that matches a local script URL
933sub _myScriptURLRE {
934 my $this = shift;
935
936 my $s = $this->{_MYSCRIPTURL};
937 unless ($s) {
938 $s = quotemeta( $this->{session}->getScriptUrl( 1, $M1, $M2, $M3 ) );
939 $s =~ s@\\$M1@[^/]*?@go;
940 $s =~ s@\\$M2@[^/]*?@go;
941 $s =~ s@\\$M3@[^#\?/]*@go;
942
943 # now add alternates for the various script-specific overrides
944 foreach my $v ( values %{ $Foswiki::cfg{ScriptUrlPaths} } ) {
945 my $over = $v;
946
947 # escape non-alphabetics
948 $over =~ s/(\W)/\\$1/g;
949 $s .= '|' . $over;
950 }
951 $this->{_MYSCRIPTURL} = "($s)";
952 }
953 return $s;
954}
955
956=begin TML
957
958---++ ObjectMethod _rewriteURL ($this, $url) -> $url
959
960=cut
961
962# Rewrite a URL inserting the session id
963sub _rewriteURL {
964 my ( $this, $url ) = @_;
965
966 return $url unless $url;
967
968 my $sessionId = $this->{_cgisession}->id();
969 return $url unless $sessionId;
970 return $url if $url =~ m/\?$Foswiki::LoginManager::Session::NAME=/;
971
972 my $s = _myScriptURLRE($this);
973
974 # If the URL has no colon in it, or it matches the local script
975 # URL, it must be an internal URL and therefore needs the session.
976 if ( $url !~ /:/ || $url =~ m/^$s/ ) {
977
978 # strip off the anchor
979 my $anchor = '';
980 if ( $url =~ s/(#.*)// ) {
981 $anchor = $1;
982 }
983
984 # strip off existing params
985 my $params = "?$Foswiki::LoginManager::Session::NAME=$sessionId";
986
987 # implicit untaint is OK because recombined with url later
988 if ( $url =~ s/\?(.*)$// ) {
989 $params .= ';' . $1;
990 }
991
992 # rebuild the URL
993 $url .= $params . $anchor;
994 } # otherwise leave it untouched
995
996 return $url;
997}
998
999=begin TML
1000
1001---++ ObjectMethod _rewriteFORM ($thisl)
1002
1003
1004=cut
1005
1006# Catch all FORMs and add a hidden Session ID variable.
1007# Only do this if the form is pointing to an internal link.
1008# This occurs if there are no colons in its target, if it has
1009# no target, or if its target matches a getScriptUrl URL.
1010# '$rest' is the bit of the initial form tag up to the closing >
1011sub _rewriteFORM {
1012 my ( $this, $url, $rest ) = @_;
1013
1014 return $url . $rest unless $this->{_cgisession};
1015
1016 my $s = _myScriptURLRE($this);
1017
1018 if ( $url !~ /:/ || $url =~ m/^($s)/ ) {
1019 $rest .= CGI::hidden(
1020 -name => $Foswiki::LoginManager::Session::NAME,
1021 -value => $this->{_cgisession}->id()
1022 );
1023 }
1024 return $url . $rest;
1025}
1026
1027=begin TML
1028
1029---++ ObjectMethod endRenderingHandler()
1030
1031This handler is called by getRenderedVersion just before the plugins
1032postRenderingHandler. So it is passed all HTML text just before it is
1033printed.
1034
1035*DEPRECATED* Use postRenderingHandler instead.
1036
1037=cut
1038
1039
# spent 46µs (33+14) within Foswiki::LoginManager::endRenderingHandler which was called 5 times, avg 9µs/call: # 5 times (33µs+14µs) by Foswiki::Render::getRenderedVersion at line 567 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 9µs/call
sub endRenderingHandler {
104055µs return unless ( $Foswiki::cfg{UseClientSessions} );
1041
104252µs my $this = shift;
1043521µs514µs return if $this->{session}->inContext('command_line');
# spent 14µs making 5 calls to Foswiki::inContext, avg 3µs/call
1044
1045 # If cookies are not turned on and transparent CGI session IDs are,
1046 # grab every URL that is an internal link and pass a CGI variable
1047 # with the session ID
1048 unless ( $this->{_haveCookie} || !$Foswiki::cfg{Sessions}{IDsInURLs} ) {
1049
1050 # rewrite internal links to include the transparent session ID
1051 # Doesn't catch Javascript, because there are just so many ways
1052 # to generate links from JS.
1053 # SMELL: this would probably be done better using javascript
1054 # that handles navigation away from this page, and uses the
1055 # rules to rewrite any relative URLs at that time.
1056
1057 # a href= rewriting
1058 $_[0] =~
1059s/(<a[^>]*(?<=\s)href=(["']))(.*?)(\2)/$1.$this->_rewriteURL($3).$4/gei;
1060
1061 # form action= rewriting
1062 # SMELL: Forms that have no target are also implicit internal
1063 # links, but are not handled. Does this matter>
1064 $_[0] =~
1065s/(<form[^>]*(?<=\s)(?:action)=(["']))(.*?)(\2[^>]*>)/$1._rewriteFORM( $this,$3, $4)/gei;
1066 }
1067
1068 # And, finally, the logon stuff
1069 $_[0] =~ s/%SESSIONLOGON%/_dispLogon( $this )/ge;
1070 $_[0] =~ s/%SKINSELECT%/_skinSelect( $this )/ge;
1071}
1072
1073sub _loadCreateCGISession {
1074 my $this = shift;
1075 my $sid = shift; #IP, Object or undef
1076
1077 _trace( $this, "_loadCreateCGISession called ..." );
1078
1079 # Item3568: CGI::Session from 4.0 already does the -d and creates the
1080 # sessions directory if it does not exist. For performance reasons we
1081 # only test for and create session file directory for older
1082 # CGI::Session
1083 my $sessionDir = "$Foswiki::cfg{WorkingDir}/tmp";
1084 if ( $Foswiki::LoginManager::Session::VERSION < 4.0 ) {
1085 unless (
1086 -d $sessionDir
1087 || ( mkdir( $Foswiki::cfg{WorkingDir} )
1088 && mkdir($sessionDir) )
1089 )
1090 {
1091 die "Could not create $sessionDir for storing sessions";
1092 }
1093 }
1094
1095 # force an appropriate umask
1096 my $oldUmask =
1097 umask(
1098 oct(777) - ( ( $Foswiki::cfg{Session}{filePermission} + 0 ) ) &
1099 oct(777) );
1100
1101 my $newsess;
1102
1103 $newsess = Foswiki::LoginManager::Session->new(
1104 CGIDRIVER,
1105 $sid,
1106 {
1107 Directory => $sessionDir,
1108 UMask => $Foswiki::cfg{Session}{filePermission}
1109 }
1110 );
1111
1112 # restore old umask
1113 umask($oldUmask);
1114
1115 return $newsess;
1116}
1117
1118sub _addSessionCookieToResponse {
1119 my $this = shift;
1120
1121 my $cookie = CGI::Cookie->new(
1122 -name => $Foswiki::LoginManager::Session::NAME,
1123 -value => $this->{_cgisession}->id(),
1124 -path => '/',
1125 -domain => $Foswiki::cfg{Sessions}{CookieRealm} || '',
1126 -httponly => 1,
1127 -secure => $this->{session}->{request}->secure,
1128 );
1129
1130 # An expiry time is only set if the session has the REMEMBER variable
1131 # in it. This is to prevent accidentally remembering cookies with
1132 # login managers where the authority is cached in the browser and
1133 # *not* in the session. Otherwise another user might be able to login
1134 # on the same machine and inherit the authorities of a prior user.
1135 if ( $Foswiki::cfg{Sessions}{ExpireCookiesAfter}
1136 && $this->getSessionValue('REMEMBER') )
1137 {
1138 require Foswiki::Time;
1139 my $exp = Foswiki::Time::formatTime(
1140 time() + $Foswiki::cfg{Sessions}{ExpireCookiesAfter},
1141 '$wday, $day-$month-$ye $hours:$minutes:$seconds GMT'
1142 );
1143
1144 $cookie->expires($exp);
1145 }
1146
1147 $this->{session}->{response}->cookies( [$cookie] );
1148}
1149
1150sub _delSessionCookieFromResponse {
1151 my $this = shift;
1152
1153 _trace( $this, "Session cookie deleted " );
1154
1155 my $cookie = CGI::Cookie->new(
1156 -name => $Foswiki::LoginManager::Session::NAME,
1157 -value => '',
1158 -path => '/',
1159 -domain => $Foswiki::cfg{Sessions}{CookieRealm} || '',
1160 -httponly => 1,
1161 -secure => $this->{session}->{request}->secure,
1162 -expires => '-1d'
1163 );
1164
1165 $this->{session}->{response}->cookies( [$cookie] );
1166}
1167
1168=begin TML
1169
1170---++ ObjectMethod rewriteRedirectUrl( $url ) ->$url
1171
1172Rewrite the URL used in a redirect if necessary to include any session
1173identification.
1174 * =$url= - target of the redirection.
1175
1176=cut
1177
1178sub rewriteRedirectUrl {
1179
1180 my ( $this, $url ) = @_;
1181
1182 return $url unless $this->{_cgisession};
1183
1184 if ( $Foswiki::cfg{Sessions}{IDsInURLs} && !$this->{_haveCookie} ) {
1185 $url = _rewriteURL( $this, $url );
1186 }
1187
1188 # This usually won't be important, but just in case they haven't
1189 # yet received the cookie and happen to be redirecting, be sure
1190 # they do have the cookie.
1191 $this->_addSessionCookieToResponse();
1192
1193 return $url;
1194}
1195
1196=begin TML
1197
1198---++ ObjectMethod getSessionValues() -> \%values
1199
1200Get a name->value hash of all the defined session variables
1201
1202=cut
1203
1204sub getSessionValues {
1205 my ($this) = @_;
1206
1207 return unless $this->{_cgisession};
1208
1209 return $this->{_cgisession}->param_hashref();
1210}
1211
1212=begin TML
1213
1214---++ ObjectMethod getCGISession()
1215Get the currect CGI session object
1216
1217=cut
1218
1219
# spent 10µs within Foswiki::LoginManager::getCGISession which was called 3 times, avg 3µs/call: # 3 times (10µs+0s) by Foswiki::Users::getCGISession at line 421 of /var/www/foswikidev/core/lib/Foswiki/Users.pm, avg 3µs/call
sub getCGISession {
122031µs my $this = shift;
1221311µs return $this->{_cgisession};
1222}
1223
1224=begin TML
1225
1226---++ ObjectMethod getSessionValue( $name ) -> $value
1227
1228Get the value of a session variable.
1229
1230=cut
1231
1232sub getSessionValue {
1233 my ( $this, $key ) = @_;
1234 return unless $this->{_cgisession};
1235
1236 return $this->{_cgisession}->param($key);
1237}
1238
1239=begin TML
1240
1241---++ ObjectMethod setSessionValue( $name, $value )
1242
1243Set the value of a session variable.
1244
1245=cut
1246
1247
# spent 4µs within Foswiki::LoginManager::setSessionValue which was called: # once (4µs+0s) by Foswiki::Func::setSessionValue at line 428 of /var/www/foswikidev/core/lib/Foswiki/Func.pm
sub setSessionValue {
124811µs my ( $this, $key, $value ) = @_;
1249
12501800ns if ( $this->{_cgisession}
1251 && defined( $this->{_cgisession}->param( $key, $value ) ) )
1252 {
1253 return 1;
1254 }
1255
125613µs return;
1257}
1258
1259=begin TML
1260
1261---++ ObjectMethod clearSessionValue( $name ) -> $boolean
1262
1263Clear the value of a session variable.
1264We do not allow setting of AUTHUSER.
1265
1266=cut
1267
1268
# spent 3µs within Foswiki::LoginManager::clearSessionValue which was called: # once (3µs+0s) by Foswiki::Func::clearSessionValue at line 445 of /var/www/foswikidev/core/lib/Foswiki/Func.pm
sub clearSessionValue {
126911µs my ( $this, $key ) = @_;
1270
1271 # We do not allow clearing of AUTHUSER.
12721500ns if ( $this->{_cgisession}
1273 && $key ne 'AUTHUSER'
1274 && defined( $this->{_cgisession}->param($key) ) )
1275 {
1276 $this->{_cgisession}->clear( [ $_[1] ] );
1277
1278 return 1;
1279 }
1280
128113µs return;
1282}
1283
1284=begin TML
1285
1286---++ ObjectMethod forceAuthentication() -> boolean
1287
1288*VIRTUAL METHOD* implemented by subclasses
1289
1290Triggered by an access control violation, this method tests
1291to see if the current session is authenticated or not. If not,
1292it does whatever is needed so that the user can log in, and returns 1.
1293
1294If the user has an existing authenticated session, the function simply drops
1295though and returns 0.
1296
1297=cut
1298
1299sub forceAuthentication {
1300 return 0;
1301}
1302
1303=begin TML
1304
1305---++ ObjectMethod loginUrl( ... ) -> $url
1306
1307*VIRTUAL METHOD* implemented by subclasses
1308
1309Return a full URL suitable for logging in.
1310 * =...= - url parameters to be added to the URL, in the format required by Foswiki::getScriptUrl()
1311
1312=cut
1313
1314sub loginUrl {
1315 return '';
1316}
1317
1318=begin TML
1319
1320---++ ObjectMethod getUser()
1321
1322Should be implemented by subclasses
1323
1324If there is some other means of getting a username - for example,
1325Apache has remote_user() - then return it. Otherwise, return undef and
1326the username stored in the session will be used.
1327
1328This method of getting the user *assumes* that the identified user
1329has been authenticated in some way (for example, by the web server)
1330
1331=cut
1332
1333sub getUser {
1334 return;
1335}
1336
1337=begin TML
1338
1339---++ ObjectMethod isValidLoginName( $name ) -> $boolean
1340
1341Check for a valid login name (not an existance check, just syntax).
1342Default behaviour is to check the login name against
1343$Foswiki::cfg{LoginNameFilterIn}
1344
1345=cut
1346
1347sub isValidLoginName {
1348 my ( $this, $name ) = @_;
1349
1350 # this function was erroneously marked as static
1351 ASSERT( !ref($name) ) if DEBUG;
1352 return $name =~ m/$Foswiki::cfg{LoginNameFilterIn}/;
1353}
1354
1355=begin TML
1356
1357---++ ObjectMethod _LOGIN ($thisl)
1358
1359
1360=cut
1361
1362
# spent 24µs (14+10) within Foswiki::LoginManager::_LOGIN which was called: # once (14µs+10µs) by Foswiki::_expandMacroOnTopicRendering at line 3435 of /var/www/foswikidev/core/lib/Foswiki.pm
sub _LOGIN {
1363
1364 #my( $session, $params, $topic, $web ) = @_;
13651600ns my $session = shift;
136612µs18µs my $this = $session->getLoginManager();
# spent 8µs making 1 call to Foswiki::getLoginManager
1367
136816µs12µs return '' if $session->inContext('authenticated');
# spent 2µs making 1 call to Foswiki::inContext
1369
1370 my $url = $this->loginUrl();
1371 if ($url) {
1372 my $text = $session->templates->expandTemplate('LOG_IN');
1373 return CGI::a( { href => $url }, $text );
1374 }
1375 return '';
1376}
1377
1378=begin TML
1379
1380---++ ObjectMethod _LOGOUTURL ($thisl)
1381
1382
1383=cut
1384
1385
# spent 92µs (20+72) within Foswiki::LoginManager::_LOGOUTURL which was called: # once (20µs+72µs) by Foswiki::_expandMacroOnTopicRendering at line 3435 of /var/www/foswikidev/core/lib/Foswiki.pm
sub _LOGOUTURL {
138611µs my ( $session, $params, $topic, $web ) = @_;
138711µs14µs my $this = $session->getLoginManager();
# spent 4µs making 1 call to Foswiki::getLoginManager
1388
1389111µs368µs return $session->getScriptUrl(
# spent 63µs making 1 call to Foswiki::getScriptUrl # spent 5µs making 2 calls to Foswiki::Prefs::getPreference, avg 2µs/call
1390 0, 'view',
1391 $session->{prefs}->getPreference('BASEWEB'),
1392 $session->{prefs}->getPreference('BASETOPIC'),
1393 'logout' => 1
1394 );
1395}
1396
1397=begin TML
1398
1399---++ ObjectMethod _LOGOUT ($thisl)
1400
1401
1402=cut
1403
1404sub _LOGOUT {
1405 my ( $session, $params, $topic, $web ) = @_;
1406 my $this = $session->getLoginManager();
1407
1408 return '' unless $session->inContext('authenticated');
1409
1410 my $url = _LOGOUTURL(@_);
1411 if ($url) {
1412 my $text = $session->templates->expandTemplate('LOG_OUT');
1413 return CGI::a( { href => $url }, $text );
1414 }
1415 return '';
1416}
1417
1418=begin TML
1419
1420---++ ObjectMethod _AUTHENTICATED ($thisl)
1421
1422
1423=cut
1424
1425sub _AUTHENTICATED {
1426 my ( $session, $params ) = @_;
1427 my $this = $session->getLoginManager();
1428
1429 if ( $session->inContext('authenticated') ) {
1430 return $params->{then} || 1;
1431 }
1432 else {
1433 return $params->{else} || 0;
1434 }
1435}
1436
1437=begin TML
1438
1439---++ ObjectMethod _CANLOGIN ($thisl)
1440
1441=cut
1442
1443sub _CANLOGIN {
1444 my ( $session, $params ) = @_;
1445 my $this = $session->getLoginManager();
1446 if ( $session->inContext('can_login') ) {
1447 return $params->{then} || 1;
1448 }
1449 else {
1450 return $params->{else} || 0;
1451 }
1452}
1453
1454=begin TML
1455
1456---++ ObjectMethod _SESSION_VARIABLE ($thisl)
1457
1458=cut
1459
1460sub _SESSION_VARIABLE {
1461 my ( $session, $params ) = @_;
1462 my $this = $session->getLoginManager();
1463 my $name = $params->{_DEFAULT};
1464
1465 if ( defined $name ) {
1466 if ( defined( $params->{set} ) ) {
1467 unless ( $readOnlySK{$name} ) {
1468 $this->setSessionValue( $name, $params->{set} );
1469 }
1470 }
1471 elsif ( defined( $params->{clear} ) ) {
1472 unless ( $readOnlySK{$name} ) {
1473 $this->clearSessionValue($name);
1474 }
1475 }
1476 elsif ( !$secretSK{$name} ) {
1477 my $val = $this->getSessionValue($name);
1478 $val = '' unless defined $val;
1479 return $val;
1480 }
1481 }
1482 return '';
1483}
1484
1485=begin TML
1486
1487---++ ObjectMethod _LOGINURL ($thisl)
1488
1489=cut
1490
1491sub _LOGINURL {
1492 my ( $session, $params ) = @_;
1493 my $this = $session->{users}->getLoginManager();
1494 return $this->loginUrl();
1495}
1496
1497=begin TML
1498
1499---++ ObjectMethod _dispLogon ($thisl)
1500
1501=cut
1502
1503sub _dispLogon {
1504 my $this = shift;
1505
1506 return '' unless $this->{_cgisession};
1507
1508 my $session = $this->{session};
1509 my $topic = $session->{topicName};
1510 my $web = $session->{webName};
1511 my $sessionId = $this->{_cgisession}->id();
1512
1513 my $urlToUse = $this->loginUrl();
1514
1515 unless ( $this->{_haveCookie} || !$Foswiki::cfg{Sessions}{IDsInURLs} ) {
1516 $urlToUse = _rewriteURL( $this, $urlToUse );
1517 }
1518
1519 my $text = $session->templates->expandTemplate('LOG_IN');
1520 return CGI::a( { class => 'foswikiAlert', href => $urlToUse }, $text );
1521}
1522
1523=begin TML
1524
1525---++ PrivateMethod _skinSelect ()
1526
1527Internal use only
1528TODO: what does it do?
1529
1530=cut
1531
1532sub _skinSelect {
1533 my $this = shift;
1534 my $session = $this->{session};
1535 my $skins = $session->{prefs}->getPreference('SKINS');
1536 my $skin = $session->getSkin();
1537 my @skins = split( /,/, $skins );
1538 unshift( @skins, 'default' );
1539 my $options = '';
1540 foreach my $askin (@skins) {
1541 $askin =~ s/\s//g;
1542 if ( $askin eq $skin ) {
1543 $options .=
1544 CGI::option( { selected => 'selected', name => $askin }, $askin );
1545 }
1546 else {
1547 $options .= CGI::option( { name => $askin }, $askin );
1548 }
1549 }
1550 return CGI::Select( { name => 'stickskin' }, $options );
1551}
1552
1553=begin TML
1554
1555---++ StaticMethod removeUserSessions()
1556
1557Delete session files for a user that is being removed from the system.
1558Removing the Session prevents any further damage from a spammer when the
1559account has been removed.
1560
1561This is a static method, but requires Foswiki::cfg. It is designed to be
1562run from a session.
1563
1564=cut
1565
1566sub removeUserSessions {
1567 my $user = shift;
1568 ASSERT($user) if DEBUG;
1569
1570 my $msg = '';
1571 CGI::Session->find(
1572 CGIDRIVER,
1573 sub { purge_user( @_, $user, $msg ) },
1574 {
1575 Directory => "$Foswiki::cfg{WorkingDir}/tmp",
1576 UMask => $Foswiki::cfg{Session}{filePermission},
1577 }
1578 );
1579
1580 sub purge_user {
1581
1582 #my ($session, $user, $msg) = @_;
1583 next if $_[0]->is_empty; # <-- already expired?!
1584 if ( $_[0]->param('AUTHUSER') && $_[0]->param('AUTHUSER') eq $_[1] ) {
1585 $_[2] .= 'cgisess_' . $_[0]->id() . ',';
1586 $_[0]->delete();
1587 $_[0]->flush()
1588 ; # Recommended practice says use flush() after delete().
1589 }
1590 }
1591 return $msg;
1592}
1593
159417µs1;
1595__END__