summaryrefslogtreecommitdiff
path: root/framework/Security/TDbUserManager.php
blob: 0832dfe5a8d1aecbaaaf275f4420c6fe9b232c6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
<?php
/**
 * TDbUserManager class
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2014 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @package System.Security
 */

/**
 * Using IUserManager interface
 */
Prado::using('System.Security.IUserManager');
Prado::using('System.Data.TDataSourceConfig');
Prado::using('System.Security.TUser');

/**
 * TDbUserManager class
 *
 * TDbUserManager manages user accounts that are stored in a database.
 * TDbUserManager is mainly designed to be used together with {@link TAuthManager}
 * which manages how users are authenticated and authorized in a Prado application.
 *
 * To use TDbUserManager together with TAuthManager, configure them in
 * the application configuration like following:
 * <code>
 * <module id="db"
 *     class="System.Data.TDataSourceConfig" ..../>
 * <module id="users"
 *     class="System.Security.TDbUserManager"
 *     UserClass="Path.To.MyUserClass"
 *     ConnectionID="db" />
 * <module id="auth"
 *     class="System.Security.TAuthManager"
 *     UserManager="users" LoginPage="Path.To.LoginPage" />
 * </code>
 *
 * In the above, {@link setUserClass UserClass} specifies what class will be used
 * to create user instance. The class must extend from {@link TDbUser}.
 * {@link setConnectionID ConnectionID} refers to the ID of a {@link TDataSourceConfig} module
 * which specifies how to establish database connection to retrieve user information.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @package System.Security
 * @since 3.1.0
 */
class TDbUserManager extends TModule implements IUserManager
{
	private $_connID='';
	private $_conn;
	private $_guestName='Guest';
	private $_userClass='';
	private $_userFactory;

	/**
	 * Initializes the module.
	 * This method is required by IModule and is invoked by application.
	 * @param TXmlElement module configuration
	 */
	public function init($config)
	{
		if($this->_userClass==='')
			throw new TConfigurationException('dbusermanager_userclass_required');
		$this->_userFactory=Prado::createComponent($this->_userClass,$this);
		if(!($this->_userFactory instanceof TDbUser))
			throw new TInvalidDataTypeException('dbusermanager_userclass_invalid',$this->_userClass);
	}

	/**
	 * @return string the user class name in namespace format. Defaults to empty string, meaning not set.
	 */
	public function getUserClass()
	{
		return $this->_userClass;
	}

	/**
	 * @param string the user class name in namespace format. The user class must extend from {@link TDbUser}.
	 */
	public function setUserClass($value)
	{
		$this->_userClass=$value;
	}

	/**
	 * @return string guest name, defaults to 'Guest'
	 */
	public function getGuestName()
	{
		return $this->_guestName;
	}

	/**
	 * @param string name to be used for guest users.
	 */
	public function setGuestName($value)
	{
		$this->_guestName=$value;
	}

	/**
	 * Validates if the username and password are correct.
	 * @param string user name
	 * @param string password
	 * @return boolean true if validation is successful, false otherwise.
	 */
	public function validateUser($username,$password)
	{
		return $this->_userFactory->validateUser($username,$password);
	}

	/**
	 * Returns a user instance given the user name.
	 * @param string user name, null if it is a guest.
	 * @return TUser the user instance, null if the specified username is not in the user database.
	 */
	public function getUser($username=null)
	{
		if($username===null)
		{
			$user=Prado::createComponent($this->_userClass,$this);
			$user->setIsGuest(true);
			return $user;
		}
		else
			return $this->_userFactory->createUser($username);
	}

	/**
	 * @return string the ID of a TDataSourceConfig module. Defaults to empty string, meaning not set.
	 */
	public function getConnectionID()
	{
		return $this->_connID;
	}

	/**
	 * Sets the ID of a TDataSourceConfig module.
	 * The datasource module will be used to establish the DB connection
	 * that will be used by the user manager.
	 * @param string module ID.
	 */
	public function setConnectionID($value)
	{
		$this->_connID=$value;
	}

	/**
	 * @return TDbConnection the database connection that may be used to retrieve user data.
	 */
	public function getDbConnection()
	{
		if($this->_conn===null)
		{
			$this->_conn=$this->createDbConnection($this->_connID);
			$this->_conn->setActive(true);
		}
		return $this->_conn;
	}

	/**
	 * Creates the DB connection.
	 * @param string the module ID for TDataSourceConfig
	 * @return TDbConnection the created DB connection
	 * @throws TConfigurationException if module ID is invalid or empty
	 */
	protected function createDbConnection($connectionID)
	{
		if($connectionID!=='')
		{
			$conn=$this->getApplication()->getModule($connectionID);
			if($conn instanceof TDataSourceConfig)
				return $conn->getDbConnection();
			else
				throw new TConfigurationException('dbusermanager_connectionid_invalid',$connectionID);
		}
		else
			throw new TConfigurationException('dbusermanager_connectionid_required');
	}

	/**
	 * Returns a user instance according to auth data stored in a cookie.
	 * @param THttpCookie the cookie storing user authentication information
	 * @return TDbUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
	 * @since 3.1.1
	 */
	public function getUserFromCookie($cookie)
	{
		return $this->_userFactory->createUserFromCookie($cookie);
	}

	/**
	 * Saves user auth data into a cookie.
	 * @param THttpCookie the cookie to receive the user auth data.
	 * @since 3.1.1
	 */
	public function saveUserToCookie($cookie)
	{
		$user=$this->getApplication()->getUser();
		if($user instanceof TDbUser)
			$user->saveUserToCookie($cookie);
	}
}


/**
 * TDbUser class
 *
 * TDbUser is the base user class for using together with {@link TDbUserManager}.
 * Two methods are declared and must be implemented in the descendant classes:
 * - {@link validateUser()}: validates if username and password are correct entries.
 * - {@link createUser()}: creates a new user instance given the username
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @package System.Security
 * @since 3.1.0
 */
abstract class TDbUser extends TUser
{
	private $_connection;

	/**
	 * Returns a database connection that may be used to retrieve data from database.
	 *
	 * @return TDbConnection database connection that may be used to retrieve data from database
	 */
	public function getDbConnection()
	{
		if($this->_connection===null)
		{
			$userManager=$this->getManager();
			if($userManager instanceof TDbUserManager)
			{
				$connection=$userManager->getDbConnection();
				if($connection instanceof TDbConnection)
				{
					$connection->setActive(true);
					$this->_connection=$connection;
				}
			}
			if($this->_connection===null)
				throw new TConfigurationException('dbuser_dbconnection_invalid');
		}
		return $this->_connection;
	}

	/**
	 * Validates if username and password are correct entries.
	 * Usually, this is accomplished by checking if the user database
	 * contains this (username, password) pair.
	 * You may use {@link getDbConnection DbConnection} to deal with database.
	 * @param string username (case-sensitive)
	 * @param string password
	 * @return boolean whether the validation succeeds
	 */
	abstract public function validateUser($username,$password);

	/**
	 * Creates a new user instance given the username.
	 * This method usually needs to retrieve necessary user information
	 * (e.g. role, name, rank, etc.) from the user database according to
	 * the specified username. The newly created user instance should be
	 * initialized with these information.
	 *
	 * If the username is invalid (not found in the user database), null
	 * should be returned.
	 *
	 * You may use {@link getDbConnection DbConnection} to deal with database.
	 *
	 * @param string username (case-sensitive)
	 * @return TDbUser the newly created and initialized user instance
	 */
	abstract public function createUser($username);

	/**
	 * Creates a new user instance given the cookie containing auth data.
	 *
	 * This method is invoked when {@link TAuthManager::setAllowAutoLogin AllowAutoLogin} is set true.
	 * The default implementation simply returns null, meaning no user instance can be created
	 * from the given cookie.
	 *
	 * If you want to support automatic login (remember login), you should override this method.
	 * Typically, you obtain the username and a unique token from the cookie's value.
	 * You then verify the token is valid and use the username to create a user instance.
	 *
	 * @param THttpCookie the cookie storing user authentication information
	 * @return TDbUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data.
	 * @see saveUserToCookie
	 * @since 3.1.1
	 */
	public function createUserFromCookie($cookie)
	{
		return null;
	}

	/**
	 * Saves necessary auth data into a cookie.
	 * This method is invoked when {@link TAuthManager::setAllowAutoLogin AllowAutoLogin} is set true.
	 * The default implementation does nothing, meaning auth data is not stored in the cookie
	 * (and thus automatic login is not supported.)
	 *
	 * If you want to support automatic login (remember login), you should override this method.
	 * Typically, you generate a unique token according to the current login information
	 * and save it together with the username in the cookie's value.
	 * You should avoid revealing the password in the generated token.
	 *
	 * @param THttpCookie the cookie to store the user auth information
	 * @see createUserFromCookie
	 * @since 3.1.1
	 */
	public function saveUserToCookie($cookie)
	{
	}
}