Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/LoginManager/TemplateLogin.pm |
Statements | Executed 16 statements in 1.92ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 45µs | 275µs | new | Foswiki::LoginManager::TemplateLogin::
1 | 1 | 1 | 30µs | 39µs | BEGIN@19 | Foswiki::LoginManager::TemplateLogin::
1 | 1 | 1 | 23µs | 72µs | BEGIN@21 | Foswiki::LoginManager::TemplateLogin::
1 | 1 | 1 | 22µs | 48µs | BEGIN@20 | Foswiki::LoginManager::TemplateLogin::
1 | 1 | 1 | 10µs | 10µs | BEGIN@23 | Foswiki::LoginManager::TemplateLogin::
0 | 0 | 0 | 0s | 0s | _packRequest | Foswiki::LoginManager::TemplateLogin::
0 | 0 | 0 | 0s | 0s | _unpackRequest | Foswiki::LoginManager::TemplateLogin::
0 | 0 | 0 | 0s | 0s | forceAuthentication | Foswiki::LoginManager::TemplateLogin::
0 | 0 | 0 | 0s | 0s | login | Foswiki::LoginManager::TemplateLogin::
0 | 0 | 0 | 0s | 0s | loginUrl | Foswiki::LoginManager::TemplateLogin::
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::TemplateLogin | ||||
6 | |||||
7 | This is a login manager that you can specify in the security setup section of | ||||
8 | [[%SCRIPTURL{"configure"}%][configure]]. It provides users with a | ||||
9 | template-based form to enter usernames and passwords, and works with the | ||||
10 | PasswordManager that you specify to verify those passwords. | ||||
11 | |||||
12 | Subclass of Foswiki::LoginManager; see that class for documentation of the | ||||
13 | methods of this class. | ||||
14 | |||||
15 | =cut | ||||
16 | |||||
17 | package Foswiki::LoginManager::TemplateLogin; | ||||
18 | |||||
19 | 2 | 55µs | 2 | 49µs | # spent 39µs (30+9) within Foswiki::LoginManager::TemplateLogin::BEGIN@19 which was called:
# once (30µs+9µs) by Foswiki::LoginManager::makeLoginManager at line 19 # spent 39µs making 1 call to Foswiki::LoginManager::TemplateLogin::BEGIN@19
# spent 9µs making 1 call to strict::import |
20 | 2 | 52µs | 2 | 74µs | # spent 48µs (22+26) within Foswiki::LoginManager::TemplateLogin::BEGIN@20 which was called:
# once (22µs+26µs) by Foswiki::LoginManager::makeLoginManager at line 20 # spent 48µs making 1 call to Foswiki::LoginManager::TemplateLogin::BEGIN@20
# spent 26µs making 1 call to warnings::import |
21 | 2 | 56µs | 2 | 121µs | # spent 72µs (23+49) within Foswiki::LoginManager::TemplateLogin::BEGIN@21 which was called:
# once (23µs+49µs) by Foswiki::LoginManager::makeLoginManager at line 21 # spent 72µs making 1 call to Foswiki::LoginManager::TemplateLogin::BEGIN@21
# spent 49µs making 1 call to Assert::import |
22 | |||||
23 | 2 | 1.70ms | 1 | 10µs | # spent 10µs within Foswiki::LoginManager::TemplateLogin::BEGIN@23 which was called:
# once (10µs+0s) by Foswiki::LoginManager::makeLoginManager at line 23 # spent 10µs making 1 call to Foswiki::LoginManager::TemplateLogin::BEGIN@23 |
24 | 1 | 10µs | our @ISA = ('Foswiki::LoginManager'); | ||
25 | |||||
26 | =begin TML | ||||
27 | |||||
28 | ---++ ClassMethod new ($session, $impl) | ||||
29 | |||||
30 | Construct the TemplateLogin object | ||||
31 | |||||
32 | =cut | ||||
33 | |||||
34 | # spent 275µs (45+230) within Foswiki::LoginManager::TemplateLogin::new which was called:
# once (45µs+230µs) by Foswiki::LoginManager::makeLoginManager at line 127 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/LoginManager.pm | ||||
35 | 1 | 2µs | my ( $class, $session ) = @_; | ||
36 | 1 | 22µs | 1 | 221µs | my $this = $class->SUPER::new($session); # spent 221µs making 1 call to Foswiki::LoginManager::new |
37 | 1 | 6µs | 1 | 9µs | $session->enterContext('can_login'); # spent 9µs making 1 call to Foswiki::enterContext |
38 | 1 | 2µs | if ( $Foswiki::cfg{Sessions}{ExpireCookiesAfter} ) { | ||
39 | $session->enterContext('can_remember_login'); | ||||
40 | } | ||||
41 | 1 | 2µs | if ( $Foswiki::cfg{TemplateLogin}{PreventBrowserRememberingPassword} ) { | ||
42 | $session->enterContext('no_auto_complete_login'); | ||||
43 | } | ||||
44 | 1 | 9µs | return $this; | ||
45 | } | ||||
46 | |||||
47 | # Pack key request parameters into a single value | ||||
48 | # Used for passing meta-information about the request | ||||
49 | # through a URL (without requiring passthrough) | ||||
50 | sub _packRequest { | ||||
51 | my ( $uri, $method, $action ) = @_; | ||||
52 | return '' unless $uri; | ||||
53 | if ( ref($uri) ) { # first parameter is a $session | ||||
54 | my $r = $uri->{request}; | ||||
55 | $uri = $r->uri(); | ||||
56 | $method = $r->method() || 'UNDEFINED'; | ||||
57 | $action = $r->action(); | ||||
58 | } | ||||
59 | return "$method,$action,$uri"; | ||||
60 | } | ||||
61 | |||||
62 | # Unpack single value to key request parameters | ||||
63 | sub _unpackRequest { | ||||
64 | my $packed = shift || ''; | ||||
65 | my ( $method, $action, $uri ) = split( ',', $packed, 3 ); | ||||
66 | return ( $uri, $method, $action ); | ||||
67 | } | ||||
68 | |||||
69 | =begin TML | ||||
70 | |||||
71 | ---++ ObjectMethod forceAuthentication () -> $boolean | ||||
72 | |||||
73 | method called when authentication is required - redirects to (...|view)auth | ||||
74 | Triggered on auth fail | ||||
75 | |||||
76 | =cut | ||||
77 | |||||
78 | sub forceAuthentication { | ||||
79 | my $this = shift; | ||||
80 | my $session = $this->{session}; | ||||
81 | |||||
82 | unless ( $session->inContext('authenticated') ) { | ||||
83 | my $query = $session->{request}; | ||||
84 | |||||
85 | # Redirect with passthrough so we don't lose the original query params | ||||
86 | |||||
87 | my $url = $session->getScriptUrl( 0, 'login' ); | ||||
88 | |||||
89 | # We use the query here to ensure the original path_info | ||||
90 | # from the request gets through to the login form. See also | ||||
91 | # PATH_INFO below. | ||||
92 | $url .= Foswiki::urlEncode( $query->path_info() ); | ||||
93 | |||||
94 | $query->param( | ||||
95 | -name => 'foswiki_origin', | ||||
96 | -value => _packRequest($session) | ||||
97 | ); | ||||
98 | $session->redirect( $url, 1 ); # with passthrough | ||||
99 | return 1; | ||||
100 | } | ||||
101 | return 0; | ||||
102 | } | ||||
103 | |||||
104 | =begin TML | ||||
105 | |||||
106 | ---++ ObjectMethod loginUrl () -> $loginUrl | ||||
107 | |||||
108 | Overrides LoginManager. Content of a login link. | ||||
109 | |||||
110 | =cut | ||||
111 | |||||
112 | sub loginUrl { | ||||
113 | my $this = shift; | ||||
114 | my $session = $this->{session}; | ||||
115 | my $topic = $session->{topicName}; | ||||
116 | my $web = $session->{webName}; | ||||
117 | return $session->getScriptUrl( 0, 'login', $web, $topic, | ||||
118 | foswiki_origin => _packRequest($session) ); | ||||
119 | } | ||||
120 | |||||
121 | =begin TML | ||||
122 | |||||
123 | ---++ ObjectMethod login( $query, $session ) | ||||
124 | |||||
125 | If a login name and password have been passed in the query, it | ||||
126 | validates these and if authentic, redirects to the original | ||||
127 | script. If there is no username in the query or the username/password is | ||||
128 | invalid (validate returns non-zero) then it prompts again. | ||||
129 | |||||
130 | If a flag to remember the login has been passed in the query, then the | ||||
131 | corresponding session variable will be set. This will result in the | ||||
132 | login cookie being preserved across browser sessions. | ||||
133 | |||||
134 | The password handler is expected to return a perl true value if the password | ||||
135 | is valid. This return value is stored in a session variable called | ||||
136 | VALIDATION. This is so that password handlers can return extra information | ||||
137 | about the user, such as a list of Wiki groups stored in a separate | ||||
138 | database, that can then be displayed by referring to | ||||
139 | %<nop>SESSION_VARIABLE{"VALIDATION"}% | ||||
140 | |||||
141 | =cut | ||||
142 | |||||
143 | sub login { | ||||
144 | my ( $this, $query, $session ) = @_; | ||||
145 | my $users = $session->{users}; | ||||
146 | |||||
147 | my $origin = $query->param('foswiki_origin'); | ||||
148 | my ( $origurl, $origmethod, $origaction ) = _unpackRequest($origin); | ||||
149 | my $loginName = $query->param('username'); | ||||
150 | my $loginPass = $query->param('password'); | ||||
151 | my $remember = $query->param('remember'); | ||||
152 | |||||
153 | # Eat these so there's no risk of accidental passthrough | ||||
154 | $query->delete( 'foswiki_origin', 'username', 'password' ); | ||||
155 | |||||
156 | # UserMappings can over-ride where the login template is defined | ||||
157 | my $loginTemplate = $users->loginTemplateName(); #defaults to login.tmpl | ||||
158 | my $tmpl = $session->templates->readTemplate($loginTemplate); | ||||
159 | |||||
160 | my $banner = $session->templates->expandTemplate('LOG_IN_BANNER'); | ||||
161 | my $note = ''; | ||||
162 | my $topic = $session->{topicName}; | ||||
163 | my $web = $session->{webName}; | ||||
164 | |||||
165 | my $cgisession = $this->{_cgisession}; | ||||
166 | |||||
167 | $cgisession->param( 'REMEMBER', $remember ) if $cgisession; | ||||
168 | if ( $cgisession | ||||
169 | && $cgisession->param('AUTHUSER') | ||||
170 | && $loginName | ||||
171 | && $loginName ne $cgisession->param('AUTHUSER') ) | ||||
172 | { | ||||
173 | $banner = $session->templates->expandTemplate('LOGGED_IN_BANNER'); | ||||
174 | $note = $session->templates->expandTemplate('NEW_USER_NOTE'); | ||||
175 | } | ||||
176 | |||||
177 | my $error = ''; | ||||
178 | |||||
179 | if ($loginName) { | ||||
180 | my $validation = $users->checkPassword( $loginName, $loginPass ); | ||||
181 | $error = $users->passwordError(); | ||||
182 | |||||
183 | if ($validation) { | ||||
184 | |||||
185 | # SUCCESS our user is authenticated. Note that we may already | ||||
186 | # have been logged in by the userLoggedIn call in loadSession, | ||||
187 | # because the username-password URL params are the same as | ||||
188 | # the params passed to this script, and they will be used | ||||
189 | # in loadSession if no other user info is available. | ||||
190 | $this->userLoggedIn($loginName); | ||||
191 | $session->logEvent( | ||||
192 | 'login', | ||||
193 | $web . '.' . $topic, | ||||
194 | "AUTHENTICATION SUCCESS - $loginName - " | ||||
195 | ); | ||||
196 | |||||
197 | # remove the sudo param - its only to tell TemplateLogin | ||||
198 | # that we're using BaseMapper.. | ||||
199 | $query->delete('sudo'); | ||||
200 | |||||
201 | $cgisession->param( 'VALIDATION', $validation ) if $cgisession; | ||||
202 | if ( !$origurl || $origurl eq $query->url() ) { | ||||
203 | $origurl = $session->getScriptUrl( 0, 'view', $web, $topic ); | ||||
204 | } | ||||
205 | else { | ||||
206 | |||||
207 | # Unpack params encoded in the origurl and restore them | ||||
208 | # to the query. If they were left in the query string they | ||||
209 | # would be lost if we redirect with passthrough. | ||||
210 | # First extract the params, ignoring any trailing fragment. | ||||
211 | if ( $origurl =~ s/\?([^#]*)// ) { | ||||
212 | foreach my $pair ( split( /[&;]/, $1 ) ) { | ||||
213 | if ( $pair =~ /(.*?)=(.*)/ ) { | ||||
214 | $query->param( $1, TAINT($2) ); | ||||
215 | } | ||||
216 | } | ||||
217 | } | ||||
218 | |||||
219 | # Restore the action too | ||||
220 | $query->action($origaction) if $origaction; | ||||
221 | } | ||||
222 | |||||
223 | # Restore the method used on origUrl so if it was a GET, we | ||||
224 | # get another GET. | ||||
225 | $query->method($origmethod); | ||||
226 | $session->redirect( $origurl, 1 ); | ||||
227 | return; | ||||
228 | } | ||||
229 | else { | ||||
230 | |||||
231 | # Tasks:Item1029 After much discussion, the 403 code is not | ||||
232 | # used for authentication failures. RFC states: "Authorization | ||||
233 | # will not help and the request SHOULD NOT be repeated" which | ||||
234 | # is not the situation here. | ||||
235 | $session->{response}->status(200); | ||||
236 | $session->logEvent( | ||||
237 | 'login', | ||||
238 | $web . '.' . $topic, | ||||
239 | "AUTHENTICATION FAILURE - $loginName - " | ||||
240 | ); | ||||
241 | $banner = $session->templates->expandTemplate('UNRECOGNISED_USER'); | ||||
242 | } | ||||
243 | } | ||||
244 | else { | ||||
245 | |||||
246 | # If the loginName is unset, then the request was likely a perfectly | ||||
247 | # valid GET call to http://foswiki/bin/login | ||||
248 | # 4xx cannot be a correct status, as we want the user to retry the | ||||
249 | # same URL with a different login/password | ||||
250 | $session->{response}->status(200); | ||||
251 | } | ||||
252 | |||||
253 | # Remove the validation_key from the *passed through* params. It isn't | ||||
254 | # required, because the form will have a new validation key, and | ||||
255 | # giving the parameter twice will confuse the strikeone Javascript. | ||||
256 | $session->{request}->delete('validation_key'); | ||||
257 | |||||
258 | # set the usernamestep value so it can be re-displayed if we are here due | ||||
259 | # to a failed authentication attempt. | ||||
260 | $query->param( -name => 'usernamestep', -value => $loginName ); | ||||
261 | |||||
262 | # TODO: add JavaScript password encryption in the template | ||||
263 | $origurl ||= ''; | ||||
264 | |||||
265 | # Set session preferences that will be expanded when the login | ||||
266 | # template is instantiated | ||||
267 | $session->{prefs}->setSessionPreferences( | ||||
268 | FOSWIKI_ORIGIN => Foswiki::entityEncode( | ||||
269 | _packRequest( $origurl, $origmethod, $origaction ) | ||||
270 | ), | ||||
271 | |||||
272 | # Path to be used in the login form action. | ||||
273 | # Could have used %ENV{PATH_INFO} (after extending {AccessibleENV}) | ||||
274 | # but decided against it as the path_info might have been rewritten | ||||
275 | # from the original env var. | ||||
276 | PATH_INFO => $query->path_info(), | ||||
277 | BANNER => $banner, | ||||
278 | NOTE => $note, | ||||
279 | ERROR => $error | ||||
280 | ); | ||||
281 | |||||
282 | my $topicObject = Foswiki::Meta->new( $session, $web, $topic ); | ||||
283 | $tmpl = $topicObject->expandMacros($tmpl); | ||||
284 | $tmpl = $topicObject->renderTML($tmpl); | ||||
285 | $tmpl =~ s/<nop>//g; | ||||
286 | $session->writeCompletePage($tmpl); | ||||
287 | } | ||||
288 | |||||
289 | 1 | 6µs | 1; | ||
290 | __END__ |