I do know how to use sessions/cookies. I do keep session and a cookie + token for a user logged in.
I'm using MVC structure (my own) and i have a Login.php controller. I also have User.php class that is a singleton class having only 1 instance.
My base controller gets the instance of User and stores in a variable like this:
abstract class Controller {
private $model;
private $user;
function __construct($model = '') {
..... //some code
$this->user = User::getInstance();
}
public function user() {
return $this->user;
}
}
In my login.php i have the following once the user submits form with user name and pass:
function logUserIn() {
if (! isset($_POST['UName']) || ! isset($_POST['UPass'])) {
$this->_404();
}
$uname = strtolower($_POST['UName']);
$pass = Hash::strongHashMD5($_POST['UPass']);
$token = $_POST['token'];
$isValid = $this->model->userCheck($uname, $pass);
$res = $this->validateUser($isValid, $token, $uname);
if ($res === false) {
echo 'User Does Not Exist!';
} else if ($res === 'Token_Error') {
echo 'Invalid Form Submission';
} else if ($res === true) {
//update token
$this->model->updateToken($isValid['ID'], $token, $_SERVER['REMOTE_ADDR']);
header("Location: ../login");
}
exit;
}
this is my method that validates user
private function validateUser($UInfo, $token, $UName) {
if ($UInfo !== false && isset($UInfo['ID']) && $UInfo['ID'] > 0) {
if ($UInfo['token'] == $token) {
return 'Token_Error';
} else {
$this->user()->setValues($UInfo['ID'], $UName, $token);
$this->user()->setSessions();
return true;
}
}
return false;
}
setsessions() method just sets the session/cookies of that user
Now everytime i want to access to see whether user is logged in or not i have to do it through controller and pass it to anywhere else.
Is there any better way of doing this? Is there any problem with my code in terms of security issues etc...
Any suggestions/advices will be appreciated, thanks guys
Currently you have domain business logic leaking all over the place. It should stay in the model layer (yes, in proper MVC the Model is a layer not a class or object), instead of being in the presentation layer.
Also, please stop hashing passwords with MD5. You might as well leave them as plain-text if you do so. Instead you should be using either crypt() with CRYPT_BLOWFISH or PBKDF2 algorithm.
Oh .. and redirecting people to 404, if one of the form fields is empty, seems a bit like overreacting.
anyway ..
the main topic:
The user authentication should happen entirely in the model layer (more precisely: in some Recognition service). Controller should only provide model layer with data and notify the current view that POST has request has been sent.
The service, upon receiving data, should create domain object for the User and assign the values. If the data passes the validation (which is one of responsibilities of a domain object) the service should instantiate the appropriate data mapper and fetch the data from storage (which might or might not be an SQL database). If this goes thought with any problems (like missing record in storage), the domain object should conform the credentials.
If any of this failed at some point, service should act upon the error state and put it in temporary storage (most likely - session).
When application gets to the part where view is supposed to generate the response, the view instance would react on the indication about POST request by checking for error state in model layer and perform the redirect by sending an HTTP header as only response based on whether or not there has been an error.
Related
I have Php Login system using MVC structure. For database data validation I create LoginModel. I need to print failure error to view like: User Not Exist Now Which way is right and better:
1- Add error data validation in Login Model and get in Controller and Print to View Like This:
class LoginModel extends \App\Core\Model
{
public function login($user_name, $user_password, $set_remember_me_cookie = null)
{
$returnError = array();
// checks if user exists, if login is not blocked (due to failed logins) and if password fits the hash
$result = $this->validateAndGetUser($user_name, $user_password);
// check if that user exists.
if (!$result) {
$returnError['isMessage'] = false;
$returnError['name'] = "User Not Found";
}
return $returnError;
}
private function validateAndGetUser($user_name, $user_password){
//Check User Data Validation
}
}
2- Add Only True Or False in LoginModel and Get in Controller And Set Error Name and Print to View Like This:
class LoginModel extends \App\Core\Model
{
public function login($user_name, $user_password, $set_remember_me_cookie = null)
{
// checks if user exists, if login is not blocked (due to failed logins) and if password fits the hash
$result = $this->validateAndGetUser($user_name, $user_password);
// check if that user exists.
if (!$result) {
return false;
}
return true;
}
private function validateAndGetUser($user_name, $user_password){
//Check User Data Validation
}
}
In action my really question is: Can I add error message in Model and Get in Controller?! Which way is right and true?
There are many ways to do this.
For me the best way is the first way you have suggested, but better declare bug reports in a single file eg errors.php and make the model return you array with 2 keys, the first key is always TRUE or FALSE and then if your model returns the first key FALSE reads the error number from the second key.
Then, in the controller you can replace the error number with the corresponding key in the array you declared in errors.php
If the first key is TRUE, then the second key will be your user information.
Suggestion:
Split the login functionality into two (main) steps:
Check if the posted user already exists. If not, throw an exception.
Match the posted password against the stored one. If they don't match, throw an exception. I suggest the use of password_hash for storing passwords, and of password_verify for matching a password with the stored one.
Then - based on your current architecture, in your controller, use a try-catch block to catch the exception thrown by the login steps and proceed as you wish to display the exception message to the user.
Just as a note: In case of an ajax request, you would send a specific response header (with status code 500, for example) or a custom response header (with status code 420, for example) back to the client, e.g. browser, in order to activate the error function of the ajax request.
Notes:
The domain model is a layer. And it must have no knowledge about the outside world.
A controller should only (!) update the domain model (through services).
A view should be a class - not a template file - responsible with fetching data from the model layer (through services), preparing/formatting it for presentation, and passing it to a response object, in order for this to be returned and printed.
The controller and the view (mostly 1:1 relation) should be created separately. The controller should not have any knowledge about the view. This creation step would take place in the front-controller class or file.
As for error reporting, I would recommend to read this.
I understand that the auth functions allows a user to login etc, but I wanted a confirmation on what exactly was happening in the background.
My guess is that it's just a cookie that holds the login details, correct?
Or is it only storing the remember_token and then automatically comparing that with what is stored in the users table?
So if I wanted to create an account edit page. Would I have to do anything like comparing the auth id with the users table id that the e-mail matches up with? Or is it handling all that automatically?
Laravel Auth is nothing but its class where already all authentication methods or functions are written in laravel out of box.so you need not required to write all that function which is relating to user login.for example to check user we simply use
Auth::check();
but in laravel auth class they written like this
public function check()
{
return !is_null($this->user());
}
in the same way for login attempt we are passing parameter to attempt method .Here also laravel built in function is there
public function attempt(array $credentials = [], $remember = false, $login = true)
{
$this->fireAttemptEvent($credentials, $remember, $login);
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
// If an implementation of UserInterface was returned, we'll ask the provider
// to validate the user against the given credentials, and if they are in
// fact valid we'll log the users into the application and return true.
if ($this->hasValidCredentials($user, $credentials)) {
if ($login) {
$this->login($user, $remember);
}
return true;
}
return false;
}
Here you are passing all credentials in array and remember password and all
I don't know how to use AuthComponent then this is the way I do user authentication with multiple roles is as follows:
There is 3 roles: Administrators, Resales and Clients.. one controller for each one, for individual views, and this is my beforeFilter for each Role/Controller:
AdministratorsController:
function beforeFilter(){
if (!$this->isAuth('Administrator'))
$this->redirect('/');
}
AppController:
function isAuth($strRole = NULL){
$data = $this->Session->read('User');
if (!$this->Session->check('User') || (!is_null($strRole) && $data['Role']['nome'] != $strRole))
return false;
return true;
}
In UsersController I do only authentication checking if $this->Session->read('User') exists, if the user exists, he gets all info and put in Session like this: $this->Session->write('User', $user); assuming that $user is the find from Model with all user information.
the question is, will I have problems? is that "right"? do not know if I was clear, if missing information, ask..
You're replicating logic the framework already implements for you.
See http://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html#authorization-who-s-allowed-to-access-what
Taken from that page (you should still read it..):
public function isAuthorized($user) {
// Admin can access every action
if (isset($user['role']) && $user['role'] === 'admin') {
return true;
}
// Default deny
return false;
}
The user data gets passed to it, no need to fetch the user data yourself.
And if you ever have to fetch the user data use $this->Auth->user():
// Some field
$this->Auth->user('someField');
// Whole data
$this->Auth->user();
If the session key ever changes for some reason all you direct calls to the Session->read('User') will be in trouble. A good and not so unlikely example here is when the application has to be extended with a 2nd auth adapter that is not using the session. Auth will know about the data, Session won't have it.
I've fairly new to PHP MVC and I'm not 100% sold I'm doing this in the most logical way possible. This is a piece of code from one of my controllers. A friend said a majority of this code should be moved into the Model? Is this so?
Right now I have a DataAccess layer that does all the SQL work. For User work I have a UserDataAccess object, which will create, update and delete. It appears I'm using something along the lines of the Data Mapper pattern. My Model classes map directly to the database columns on a given table and that's it.
I'm continually confused at where each piece of code should go.
// Posted values passed all checks.
if(count($errors) == 0)
{
try
{
// Get a Connection.
$connection = ConnectionFactory::GetConnection('mysql');
try
{
$connection->beginTransaction();
// Create DataAccess object.
$uda = new UserDataAccess();
// Check if username and email are avail.
if(count($uda->CheckUsername($connection, $user->Username)) > 0)
$errors['username_taken'] = "Username is taken";
if(count($uda->CheckEmail($connection, $user->Email)) > 0)
$errors['email_taken'] = "Email is taken";
if(count($errors) == 0)
{
// Perform Create.
$userId = $uda->CreateUser($connection, $user->GetArray());
// Add User to Rookie Role.
$uda->AddUserToRole($connection, 1, $userId);
// Commit connection.
$connection->commit();
}
// Clear connection.
$connection = null;
// Redirect user.
if(count($errors) == 0)
header('location: /' . $user->Username);
}
catch(PDOException $e)
{
$connection->rollBack();
$errors['internal'] = "Opps: There was an error processing the request.";
}
}
catch(Exception $e)
{
$errors['internal'] = "Opps: There was an error processing the request.";
}
}
}
Try to move the mysql connection entirely to the model, the controllers should not know what type of database you are using and how you are connecting to it. Try to think of it in this way:
Views only show pages to the users;
Controllers answer to requests by performing calculations, interacting with the Models and loading Views;
You should always be able to change any of the components without affecting the ones that interact with it;
From the looks of it, it seems like some code that sound go into the controller.
Here's a break down of MVC:
Model: Accessing database, preferably using an object oriented method that treats data like object.
View: The HTML of the page.
Controller: Logic that allows a dynamic page to be built.
That probably sounded really abstract. The general idea of views is that they are the HTML template, with minimum codes, preferably only echoing certain dynamic element of the page (NOT THE HTML, just plain text, usually) and/or a bit of if and foreach loops. Example:
.... Your HTML code
<?php foreach ($page->users as $user): /* Loops through all the users */ ?>
<li><?php echo $user->name; /* echo their name */ ?></li> /
<?php endforeach; ?>
.... Your HTML Code
The idea behind controllers is how you get your page to actually work, by handling the logic, getting input, and such. Example:
class Controller extends BaseController{
function indexpage($args=array()){
if ($args[0] == 'user') $user = UserModel::listUsers(); // If the argument to the controller is 'user' (could be provided by GET, or just the URL, we won't go into that), list all users.
$this->render('yourViewPage', array('user' => $user)); // renders the view file named yourViewPage.php and pass the value of $user into a variable named 'user'. As shown by above accessed via $page->user.
}
}
Granted that the above is only a simple example, you get the point. The render() renders the page and passes the key => value pair in the array so that the view has access to them.
The Model is the database interaction. It allows you to access the database without using SQL (preferably). Example:
class UserModel{
public $name;
public $openid;
public static function listUsers($offset=0, $max=20){
global $persister;
return $persister->list('UserModel', 0, 20, array('order'=>'NAME DESC'));
}
}
// Create a new user. This usually goes into the controller
$user = User();
$user->name = 'Your User'; // sets name
$user->openid = 'htto://theiropenidprovider.com/openid'; // sets openid
$persister->track($user); // Adds the user to be tracked. (the tracking info is usually written in XML, but we won't go into that).
$persister->flushAll(); // Saves to the database (kinda like commit)
// Gets the user.
$persister->find('UserModel', 'name', 'Your User') // Find the user that has the name of "Your User" in all UserModel instanced tracked.
So Models don't have to have the most coding. In my opinions controllers would have a lot of code, but that totally depends on the complexity of what you're building.
Hope that clears it up for you.
As you said, the Model is where you should put your dabatase code above. A controller handles what the user sees. In PHP the controller is often not a class but to keep things practical, just the php file which the user navigates to, which controls what happens for that view. The controller should not need to know how the DB is implemented, or even that a DB is used, so for example having transaction information is not good.
I was hoping someone could help me with a question I've come up on.
I have a Session object that handles storage of general session data, I also have a Authentication object which validates a users credentials.
Initially I passed the desired Authentication class name to my Session object then had a login method that created an instance of the Authentication object and validate the credentials. I stored the result of this validation in a Session variable and made it available via a getter. The user data was also stored in the Session for later use. In addition to all this, I have a logout method, which removes the user data from the Session and thus logging the user out.
My question is what role should the Session object play in users logging into their account?
And what other ways might one suggest I go about handling user login, as it stands right now I feel as though I'm getting too much wrapped up in my Session object.
Simply calling your authenticate method should trigger logic within Auth to store the proper data in the session (or some other data store) and Auth should also be used exclusively to retreive/revoke this info. So using the example form your comment it might be:
class Auth {
public static function authenticate($identity, $pass)
{
// do lookup to match identity/pass if its good then
/* assume $auth is an array with the username/email or
whatever data you need to store as part of authentication */
Session::set('auth', $auth);
return true;
// if auth failed then
Session::set('auth', array('user'=>'anonymous'));
return false;
}
public function isAuthenticated()
{
$auth = Session::get('auth');
if(!$auth)
{
return false;
}
return (isset($auth['user']) && $auth['user'] !== 'anonymous');
}
}
[...] as it stands right now I feel as
though I'm getting too much wrapped up
in my Session object.
And id agree. Idelaly for authentication/credentials you shoudl only be interacting with the Auth/Acl object(s). They would then utilize the session as stateful store... but you shouldnt care that its even stored in session. The code utilizng the Auth/Acl object(s) should be completely unaware of this fact.
For example:
//Bad
if($session->get('authenticated', 'auth'))
{
// do stuff
}
// also bad
if(isset($_SESSION['authenticated']))
{
// do stuff
}
//Good
if($auth->isAuthenticated())
{
// do stuff
}
// inside $auth class it might look like this
public function isAuthenticated()
{
$store = $this->getSotrage(); // assume this returns the $_SESSION['auth']
return isset($store['authenticated']);
}
The session is a good place for holding user data that you want to have managed in some sort of state across various pages or if you need a fast accessible way to get at it without hitting the database. It is a bad idea to keep secure information (re: passwords/etc) in the session, but rapid access information like username, name, email address, preferences, etc is all good data to put in the session. Try to keep it simple though.
Keep in mind though, that the session (or related cookie) should only ever be used for identification. It should not be used for authentication.
Authentication object is a good method. Make sure that it only holds secure information as long as it needs to and that it has all the necessary functions available to keep sensitive data protected.