Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TAuthManager class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TAuthManager.php 2526 2008-10-15 11:03:36Z mikl $
10
 * @package System.Security
11
 */
12
 
13
/**
14
 * Using IUserManager interface
15
 */
16
Prado::using('System.Security.IUserManager');
17
 
18
/**
19
 * TAuthManager class
20
 *
21
 * TAuthManager performs user authentication and authorization for a Prado application.
22
 * TAuthManager works together with a {@link IUserManager} module that can be
23
 * specified via the {@link setUserManager UserManager} property.
24
 * If an authorization fails, TAuthManager will try to redirect the client
25
 * browser to a login page that is specified via the {@link setLoginPage LoginPage}.
26
 * To login or logout a user, call {@link login} or {@link logout}, respectively.
27
 *
28
 * The {@link setAuthExpire AuthExpire} property can be used to define the time
29
 * in seconds after which the authentication should expire.
30
 * {@link setAllowAutoLogin AllowAutoLogin} specifies if the login information
31
 * should be stored in a cookie to perform automatic login. Enabling this
32
 * feature will cause that {@link setAuthExpire AuthExpire} has no effect
33
 * since the user will be logged in again on authentication expiration.
34
 *
35
 * To load TAuthManager, configure it in application configuration as follows,
36
 * <module id="auth" class="System.Security.TAuthManager" UserManager="users" LoginPage="login" />
37
 * <module id="users" class="System.Security.TUserManager" />
38
 *
39
 * @author Qiang Xue <qiang.xue@gmail.com>
40
 * @version $Id: TAuthManager.php 2526 2008-10-15 11:03:36Z mikl $
41
 * @package System.Security
42
 * @since 3.0
43
 */
44
class TAuthManager extends TModule
45
{
46
	/**
47
	 * GET variable name for return url
48
	 */
49
	const RETURN_URL_VAR='ReturnUrl';
50
	/**
51
	 * @var boolean if the module has been initialized
52
	 */
53
	private $_initialized=false;
54
	/**
55
	 * @var IUserManager user manager instance
56
	 */
57
	private $_userManager;
58
	/**
59
	 * @var string login page
60
	 */
61
	private $_loginPage;
62
	/**
63
	 * @var boolean whether authorization should be skipped
64
	 */
65
	private $_skipAuthorization=false;
66
	/**
67
	 * @var string the session var name for storing return URL
68
	 */
69
	private $_returnUrlVarName;
70
	/**
71
	 * @var boolean whether to allow auto login (using cookie)
72
	 */
73
	private $_allowAutoLogin=false;
74
	/**
75
	 * @var string variable name used to store user session or cookie
76
	 */
77
	private $_userKey;
78
	/**
79
	 * @var integer authentication expiration time in seconds. Defaults to zero (no expiration)
80
	 */
81
	private $_authExpire=0;
82
 
83
	/**
84
	 * Initializes this module.
85
	 * This method is required by the IModule interface.
86
	 * @param TXmlElement configuration for this module, can be null
87
	 * @throws TConfigurationException if user manager does not exist or is not IUserManager
88
	 */
89
	public function init($config)
90
	{
91
		if($this->_userManager===null)
92
			throw new TConfigurationException('authmanager_usermanager_required');
93
		if($this->_returnUrlVarName===null)
94
			$this->_returnUrlVarName=$this->getApplication()->getID().':'.self::RETURN_URL_VAR;
95
		$application=$this->getApplication();
96
		if(is_string($this->_userManager))
97
		{
98
			if(($users=$application->getModule($this->_userManager))===null)
99
				throw new TConfigurationException('authmanager_usermanager_inexistent',$this->_userManager);
100
			if(!($users instanceof IUserManager))
101
				throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager);
102
			$this->_userManager=$users;
103
		}
104
		$application->attachEventHandler('OnAuthentication',array($this,'doAuthentication'));
105
		$application->attachEventHandler('OnEndRequest',array($this,'leave'));
106
		$application->attachEventHandler('OnAuthorization',array($this,'doAuthorization'));
107
		$this->_initialized=true;
108
	}
109
 
110
	/**
111
	 * @return IUserManager user manager instance
112
	 */
113
	public function getUserManager()
114
	{
115
		return $this->_userManager;
116
	}
117
 
118
	/**
119
	 * @param string|IUserManager the user manager module ID or the user manager object
120
	 * @throws TInvalidOperationException if the module has been initialized or the user manager object is not IUserManager
121
	 */
122
	public function setUserManager($provider)
123
	{
124
		if($this->_initialized)
125
			throw new TInvalidOperationException('authmanager_usermanager_unchangeable');
126
		if(!is_string($provider) && !($provider instanceof IUserManager))
127
			throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager);
128
		$this->_userManager=$provider;
129
	}
130
 
131
	/**
132
	 * @return string path of login page should login is required
133
	 */
134
	public function getLoginPage()
135
	{
136
		return $this->_loginPage;
137
	}
138
 
139
	/**
140
	 * Sets the login page that the client browser will be redirected to if login is needed.
141
	 * Login page should be specified in the format of page path.
142
	 * @param string path of login page should login is required
143
	 * @see TPageService
144
	 */
145
	public function setLoginPage($pagePath)
146
	{
147
		$this->_loginPage=$pagePath;
148
	}
149
 
150
	/**
151
	 * Performs authentication.
152
	 * This is the event handler attached to application's Authentication event.
153
	 * Do not call this method directly.
154
	 * @param mixed sender of the Authentication event
155
	 * @param mixed event parameter
156
	 */
157
	public function doAuthentication($sender,$param)
158
	{
159
		$this->onAuthenticate($param);
160
 
161
		$service=$this->getService();
162
		if(($service instanceof TPageService) && $service->getRequestedPagePath()===$this->getLoginPage())
163
			$this->_skipAuthorization=true;
164
	}
165
 
166
	/**
167
	 * Performs authorization.
168
	 * This is the event handler attached to application's Authorization event.
169
	 * Do not call this method directly.
170
	 * @param mixed sender of the Authorization event
171
	 * @param mixed event parameter
172
	 */
173
	public function doAuthorization($sender,$param)
174
	{
175
		if(!$this->_skipAuthorization)
176
		{
177
			$this->onAuthorize($param);
178
		}
179
	}
180
 
181
	/**
182
	 * Performs login redirect if authorization fails.
183
	 * This is the event handler attached to application's EndRequest event.
184
	 * Do not call this method directly.
185
	 * @param mixed sender of the event
186
	 * @param mixed event parameter
187
	 */
188
	public function leave($sender,$param)
189
	{
190
		$application=$this->getApplication();
191
		if($application->getResponse()->getStatusCode()===401)
192
		{
193
			$service=$application->getService();
194
			if($service instanceof TPageService)
195
			{
196
				$returnUrl=$application->getRequest()->getRequestUri();
197
				$this->setReturnUrl($returnUrl);
198
				$url=$service->constructUrl($this->getLoginPage());
199
				$application->getResponse()->redirect($url);
200
			}
201
		}
202
	}
203
 
204
	/**
205
	 * @return string the name of the session variable storing return URL. It defaults to 'AppID:ReturnUrl'
206
	 */
207
	public function getReturnUrlVarName()
208
	{
209
		return $this->_returnUrlVarName;
210
	}
211
 
212
	/**
213
	 * @param string the name of the session variable storing return URL.
214
	 */
215
	public function setReturnUrlVarName($value)
216
	{
217
		$this->_returnUrlVarName=$value;
218
	}
219
 
220
	/**
221
	 * @return string URL that the browser should be redirected to when login succeeds.
222
	 */
223
	public function getReturnUrl()
224
	{
225
		return $this->getSession()->itemAt($this->getReturnUrlVarName());
226
	}
227
 
228
	/**
229
	 * Sets the URL that the browser should be redirected to when login succeeds.
230
	 * @param string the URL to be redirected to.
231
	 */
232
	public function setReturnUrl($value)
233
	{
234
		$this->getSession()->add($this->getReturnUrlVarName(),$value);
235
	}
236
 
237
	/**
238
	 * @return boolean whether to allow remembering login so that the user logs on automatically next time. Defaults to false.
239
	 * @since 3.1.1
240
	 */
241
	public function getAllowAutoLogin()
242
	{
243
		return $this->_allowAutoLogin;
244
	}
245
 
246
	/**
247
	 * @param boolean whether to allow remembering login so that the user logs on automatically next time. Users have to enable cookie to make use of this feature.
248
	 * @since 3.1.1
249
	 */
250
	public function setAllowAutoLogin($value)
251
	{
252
		$this->_allowAutoLogin=TPropertyValue::ensureBoolean($value);
253
	}
254
 
255
	/**
256
	 * @return integer authentication expiration time in seconds. Defaults to zero (no expiration).
257
	 * @since 3.1.3
258
	 */
259
	public function getAuthExpire()
260
	{
261
		return $this->_authExpire;
262
	}
263
 
264
	/**
265
	 * @param integer authentication expiration time in seconds. Defaults to zero (no expiration).
266
	 * @since 3.1.3
267
	 */
268
	public function setAuthExpire($value)
269
	{
270
		$this->_authExpire=TPropertyValue::ensureInteger($value);
271
	}
272
 
273
	/**
274
	 * Performs the real authentication work.
275
	 * An OnAuthenticate event will be raised if there is any handler attached to it.
276
	 * If the application already has a non-null user, it will return without further authentication.
277
	 * Otherwise, user information will be restored from session data.
278
	 * @param mixed parameter to be passed to OnAuthenticate event
279
	 * @throws TConfigurationException if session module does not exist.
280
	 */
281
	public function onAuthenticate($param)
282
	{
283
		$application=$this->getApplication();
284
 
285
		// restoring user info from session
286
		if(($session=$application->getSession())===null)
287
			throw new TConfigurationException('authmanager_session_required');
288
		$session->open();
289
		$sessionInfo=$session->itemAt($this->getUserKey());
290
		$user=$this->_userManager->getUser(null)->loadFromString($sessionInfo);
291
 
292
		// check for authentication expiration
293
		$isAuthExpired = $this->_authExpire>0 && !$user->getIsGuest() &&
294
        ($expiretime=$session->itemAt('AuthExpireTime')) && $expiretime<time();
295
 
296
		// try authenticating through cookie if possible
297
		if($this->getAllowAutoLogin() && ($user->getIsGuest() || $isAuthExpired))
298
		{
299
			$cookie=$this->getRequest()->getCookies()->itemAt($this->getUserKey());
300
			if($cookie instanceof THttpCookie)
301
			{
302
				if(($user2=$this->_userManager->getUserFromCookie($cookie))!==null)
303
				{
304
					$user=$user2;
305
					$this->updateSessionUser($user);
306
					// user is restored from cookie, auth may not expire
307
					$isAuthExpired = false;
308
				}
309
			}
310
		}
311
 
312
		$application->setUser($user);
313
 
314
		// handle authentication expiration or update expiration time
315
		if($isAuthExpired)
316
			$this->onAuthExpire($param);
317
		else
318
			$session->add('AuthExpireTime', time() + $this->_authExpire);
319
 
320
		// event handler gets a chance to do further auth work
321
		if($this->hasEventHandler('OnAuthenticate'))
322
			$this->raiseEvent('OnAuthenticate',$this,$application);
323
	}
324
 
325
	/**
326
	 * Performs user logout on authentication expiration.
327
	 * An 'OnAuthExpire' event will be raised if there is any handler attached to it.
328
	 * @param mixed parameter to be passed to OnAuthExpire event.
329
	 */
330
	public function onAuthExpire($param)
331
	{
332
		$this->logout();
333
		if($this->hasEventHandler('OnAuthExpire'))
334
			$this->raiseEvent('OnAuthExpire',$this,$param);
335
	}
336
 
337
	/**
338
	 * Performs the real authorization work.
339
	 * Authorization rules obtained from the application will be used to check
340
	 * if a user is allowed. If authorization fails, the response status code
341
	 * will be set as 401 and the application terminates.
342
	 * @param mixed parameter to be passed to OnAuthorize event
343
	 */
344
	public function onAuthorize($param)
345
	{
346
		$application=$this->getApplication();
347
		if($this->hasEventHandler('OnAuthorize'))
348
			$this->raiseEvent('OnAuthorize',$this,$application);
349
		if(!$application->getAuthorizationRules()->isUserAllowed($application->getUser(),$application->getRequest()->getRequestType(),$application->getRequest()->getUserHostAddress()))
350
		{
351
			$application->getResponse()->setStatusCode(401);
352
			$application->completeRequest();
353
		}
354
	}
355
 
356
	/**
357
	 * @return string a unique variable name for storing user session/cookie data
358
	 * @since 3.1.1
359
	 */
360
	public function getUserKey()
361
	{
362
		if($this->_userKey===null)
363
			$this->_userKey=$this->generateUserKey();
364
		return $this->_userKey;
365
	}
366
 
367
	/**
368
	 * @return string a key used to store user information in session
369
	 * @since 3.1.1
370
	 */
371
	protected function generateUserKey()
372
	{
373
		return md5($this->getApplication()->getUniqueID().'prado:user');
374
	}
375
 
376
	/**
377
	 * Updates the user data stored in session.
378
	 * @param IUser user object
379
	 * @throws new TConfigurationException if session module is not loaded.
380
	 */
381
	public function updateSessionUser($user)
382
	{
383
		if(!$user->getIsGuest())
384
		{
385
			if(($session=$this->getSession())===null)
386
				throw new TConfigurationException('authmanager_session_required');
387
			else
388
				$session->add($this->getUserKey(),$user->saveToString());
389
		}
390
	}
391
 
392
	/**
393
	 * Switches to a new user.
394
	 * This method will logout the current user first and login with a new one (without password.)
395
	 * @param string the new username
396
	 * @return boolean if the switch is successful
397
	 */
398
	public function switchUser($username)
399
	{
400
		if(($user=$this->_userManager->getUser($username))===null)
401
			return false;
402
		$this->updateSessionUser($user);
403
		$this->getApplication()->setUser($user);
404
		return true;
405
	}
406
 
407
	/**
408
	 * Logs in a user with username and password.
409
	 * The username and password will be used to validate if login is successful.
410
	 * If yes, a user object will be created for the application.
411
	 * @param string username
412
	 * @param string password
413
	 * @param integer number of seconds that automatic login will remain effective. If 0, it means user logs out when session ends. This parameter is added since 3.1.1.
414
	 * @return boolean if login is successful
415
	 */
416
	public function login($username,$password,$expire=0)
417
	{
418
		if($this->_userManager->validateUser($username,$password))
419
		{
420
			if(($user=$this->_userManager->getUser($username))===null)
421
				return false;
422
			$this->updateSessionUser($user);
423
			$this->getApplication()->setUser($user);
424
 
425
			if($expire>0)
426
			{
427
				$cookie=new THttpCookie($this->getUserKey(),'');
428
				$cookie->setExpire(time()+$expire);
429
				$this->_userManager->saveUserToCookie($cookie);
430
				$this->getResponse()->getCookies()->add($cookie);
431
			}
432
			return true;
433
		}
434
		else
435
			return false;
436
	}
437
 
438
	/**
439
	 * Logs out a user.
440
	 * User session will be destroyed after this method is called.
441
	 * @throws TConfigurationException if session module is not loaded.
442
	 */
443
	public function logout()
444
	{
445
		if(($session=$this->getSession())===null)
446
			throw new TConfigurationException('authmanager_session_required');
447
		$this->getApplication()->getUser()->setIsGuest(true);
448
		$session->destroy();
449
		if($this->getAllowAutoLogin())
450
		{
451
			$cookie=new THttpCookie($this->getUserKey(),'');
452
			$this->getResponse()->getCookies()->add($cookie);
453
		}
454
	}
455
}
456
 
457
?>