Page 1 of 1

User classes

Posted: Fri Aug 24, 2007 7:26 pm
by ReDucTor
While working with MVC, I'm wondering how people work in their user credentials.

My theory is have a user model, something like the following.

Code: Select all

<?php

define('ACCESS_ADMIN',2);
define('ACCESS_MOD',1);

class UserModel {
	private $data=false;

	public function __construct($value='',$key='username') {
		global $db;
		if(func_num_args()) {
			$q = $db->execute('SELECT * FROM users WHERE `'.$key.'`=\'' . addslashes($value)."'");
			if($q->numrows == 0) {
				throw new Exception('Unable to find user');
			} else {
				$this->data = $q->fetchArray($q);
			}
		}
	}

	static function Login() {
		global $db;
		if(isset($_SESSION['userid'])) {
			$user = new UserModel($_SESSION['userid'],'id');
			if($user->password == $_SESSION['password']) {
				return $user;
			} else {
				unset($_SESSION['userid']);
				unset($_SESSION['password']);
			}
		}

		$in = input(array('login_username'=>FILTER_UNSAFE_RAW,'login_password'=>FILTER_UNSAFE_RAW));
		if($in['login_username']) {
			$user = new UserModel($in['login_username']);
			if($user->password == md5($in['login_password'])) {
				$_SESSION['userid']		= $user->id;
				$_SESSION['password']	= md5($user->password);
			} else {
				throw new Exception('Invalid Username/Password');
			}
		}
	}

	public function __get($key) {
		return $this->data[$key];
	}

	public function __set($key,$val) {
		$this->data[$key] = $val;
	}

	public function commit() {
		if(isset($this->data['id'])) {
			$sql = 'UPDATE users SET ';
			foreach($this->data as $k=>$v) {
				if($k != 'id') {
					$sql .= '`'.$k.'`=\'' .addslashes($v).'\',';
				}
			}
			$sql = substr($sql,0,-1);
			$sql .= ' WHERE id='.intval($this->data['id']);
			$q = $db->execute($sql);
			return $q->affectedRows;
		} else {
			$fields = '';
			$values = '';
			foreach($this->data as $k=>$v) {
				$fields.='`'.$k.'`,';
				$values .= "'".addslashes($v)."',";
			}
			$fields = substr($fields,0,-1);
			$values = substr($values,0,-1);
			$q = $db->execute('INSERT INTO users ('.$fields.') VALUES ('.$values.')');
			return $db->insertID;
		}
	}

	function checkAccess($access) {
		return $this->data['access'] >= $access;
	}
}

?>

Then to get the current user.

Code: Select all

$user = UserModel::login();
To register a user.

Code: Select all

$user = new UserModel();
$user->username = 'administrator';
$user->password = md5('something');
$user->email = 'whatever';
$user->commit();
To update a user.

Code: Select all

$user = UserModel::login();
$user->email = 'someemail@blah.com';
$user->commit();
Are we allowed to do certain things.

Code: Select all

$user = UserModel::login();
if($user->checkAccess(ACCESS_MOD)) {
  echo 'Your a admin';
}
Comments, suggestions?

Posted: Tue Aug 28, 2007 1:52 am
by John Cartwright
Hrmm I'm not quite sure I like the way you've handled your authentication process.. primarily due to the technical faults that make your object tightly coupled to your existing code (globals, user defined functions), among other knit picks of mine.

For starters..
  • divide the responsibilities of user authentication and user creation into their respective objects since they have two completely seperate responsibilities.. UserCreateModel, UserLoginModel, UserAuthorizationModel
  • defines() should likely be object constants, accessed through UserModel::ACCESS_ADMIN
  • pass your database object as a function parameter and rid yourself of the globals.
  • not sure why your login() method is essentially a factory, more so a factory of itself. You should probably just have this method perform the login authentication itself.
  • your UserModel is required to have a input() function defined, which makes this code even less portable
  • addslashes() should be replaced with mysql_real_escape_string, better yet a escaping method provided by your database abstraction object.
As for handling permissions within the application, I would generally create an ACL which controls which users have permissions for specific actions, instead of having to riddle my code with object calls. I much prefer to have authorization done transparantly. For more info on ACL