DooPHP IRC channel


User Auth Class

This is for general DooPHP discussion. Ask your questions here.

User Auth Class

Postby benh » Sun Jul 04, 2010 10:42 am

Hi,

Im new to Doo and wondering if their is an out of the box user auth class available to cover login, logout, register, forgot password etc?

I can see there are a few exaples in the download bundle, but is their a standard set of methods that are reccomended and used in production?

Best regards, Ben.
benh
 
Posts: 17
Joined: Sun Jun 27, 2010 5:14 pm

Re: User Auth Class

Postby RichardM » Sun Jul 04, 2010 10:50 am

Hi,

There is currently no specific class to handle this. In part it needs you to create your own controller to handle much of this otherwise doophp would define what user data is being collected.

I have however been working on an abstract DooUser class which you can extend to specify your own login and logout functions and if using roles you can then either use basic Acl or RbAcl.

I'm just out right now...will upload something this evening though.

Richard
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Re: User Auth Class

Postby benh » Sun Jul 04, 2010 10:57 am

Many thanks Richard. Yes, I appreciate it is difficult to have a "covers all use cases" Auth class. I guess a good staring point is what Im after rather than writing my own from scratch.

Look forward to reading the abstract DooUser class.

Best regards, Ben.
benh
 
Posts: 17
Joined: Sun Jun 27, 2010 5:14 pm

Re: User Auth Class

Postby leng » Sun Jul 04, 2010 5:15 pm

I have my own implementation which covers what you had stated but I won't want to share it out.
On registration/forgot password/login/logout etc, I think it would be best want to make your own solution or customized from a DooUser class. I am concern about security issue for a common used solution. Doesn't seems right to me.
Just Doo IT!
leng
 
Posts: 1482
Joined: Thu Jul 16, 2009 11:33 pm

Re: User Auth Class

Postby benh » Sun Jul 04, 2010 5:34 pm

OK no worries.

Just seems a shame that it is so time consuming to write those methods. Im not sure I share your concern around security given that any open source app that uses some sort of authentication exposes their internals (Joomla, Wordpress etc etc). This forces the code to be good and secure! Using a given solution allows security holes to be patched quickly. I know thereare pros and cons.

So for a basic webapp with any user interaction typically needs (no way a full list):
- signup (captcha, check username taken, password strength, email signup)
- login (user lookup, set cookie, session management)
- logout (session destroy, unset cookie)
- change password
- change email
- password reminder

This is a significant investment to any new user to the framework, and something that I personally was hoping to find! Looks like I will have to port some previous code!

Best regards, Ben.
benh
 
Posts: 17
Joined: Sun Jun 27, 2010 5:14 pm

Re: User Auth Class

Postby RichardM » Sun Jul 04, 2010 5:53 pm

Hey Ben

The problem with such a "standard implementation" is it forces you to have a very strict setup.

You would have a predefined controller. Predefined user table. Predefined method of hashing passwords etc...

What about if you do Want password resetting to be done by simply sending a password reset email...what if you want to have the awnser some personal question...and when did you collect this...

What if you want to collect information like the city they live in or there address..what if you do not want to collect this...

Every site has it's own requirements... DooPHP provides classes for secure sessions, creating controllers etc...also there is a difference between frameworks like doophp and zend framework etc...and content management systems like joomola etc... There are designed for 2 very different reasons... A framework enables the development of a cms...however a cms is not a framework...

The class(es) I have for DooUser do not provide things like registration, password resetting etc...just a means of making it easier to get hold of information about a user. You need to create these yourself...

Can't remember where  I got to with my guide on the learn doophp blog but I think it started to cover this topic with some sample code.

Richard
Ps: should be home soon...will load some code up later...
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Re: User Auth Class

Postby benh » Sun Jul 04, 2010 8:51 pm

Hi Richard,

Thanks for the info, yes, understood. Look forward to the code, and I will crack on with porting my user classes.

Many thanks, Ben.
benh
 
Posts: 17
Joined: Sun Jun 27, 2010 5:14 pm

Re: User Auth Class

Postby RichardM » Sun Jul 04, 2010 9:16 pm

Hi,

Below the the code for DooUser. Please note its not final and I am still trying to work out how its best used in more than just my own application(s). You will need to extend it so you will have something like:
Code: Select all
Doo::loadClass('DooUser');
class MyAppUser extends DooUser {

public function login($username, $password, $rememberMe) {
 // Should test $username and $password and check user is valid
 // update the internal class variables
 // return true or false depending on if they are valid
}

}


I have setup my BaseController beforeRun method to checkIfUserClassExistsOrCreate method which first loads the user class (I do not use autoloading right now...and it needs to be loaded before the next step). It then starts a DooSession before checking to see if Doo::session()->user exists. If it does it just returns...otherwise it creates a new MyDooUser object by calling the constructor...

Therefore every user has an authentication object which I can then test throughout the application. By default users are seen as a guest and have just one role. Then in my login action of my account controller I call Doo::session()->user->login($username, $password, $rememberMe) and check the result. As the object is in the session and every controller extends BaseController then I know it will have an authentication object I can go testing / using. I also have my base controllers beforeRun method test if the user has access to a given resource by parsing the roles the user has to an instance of DooRbAcl.

The class as it currently stands is as follows:
Code: Select all
abstract class DooUser {

   protected $isLoggedIn = false;
   protected $userId = false;
   protected $username = false;
   protected $roles = array();

   public function __construct() {
      $this->roles = array('anonymous');
   }

   /**
    * Is the user logged in?
    * @return bool true if logged in, false otherwise
    */
   public function isLoggedIn() {
      return $this->isLoggedIn;
   }

   /**
    * Get an array containing all the roles assigned to the user
    * This can be used with DooAcl and DooRbAcl
    * @return array of the role names assigned to the user
    */
   public function getRoles() {
      return $this->roles;
   }

   /**
    * Returns the name of the user
    * @return string the username the user used to login to the system
    */
   public function getUsername() {
      return $this->username;
   }

   /**
    * Returns the ID of the user who logged in
    * @return int The id of the user
    */
   public function getUserId() {
      return $this->userId;
   }

   /**
    * Checks to see if the user has a given role (or roles). This can be used for example when
    * wanting to only show something to the user if they have a given role.
    * @param string|array $role The role (or roles) to check the user for
    * @param bool If true and $role is an array require user to have ALL roles otherwise any match will return true
    * @return bool True if the user has the role(s) required
    */
   public function hasRole($role, $requireAll=false) {
      if (is_string($role)){
         return in_array($role, $this->roles);
      } elseif (is_array($role)) {
         if ($requireAll) {
            foreach($role as $r) {
               if (!in_array($r, $this->roles)) {
                  return false;
               }
            }
            return true;
         } else {
            foreach ($role as $r) {
               if (in_array($r, $this->roles)) {
                  return true;
               }
            }
         }
      }
      return false;
   }

   /**
    * Function to be called to log the user in.
    *
    * On success it should set the nessecary internal variables and return true.
    *
    * If the credentials are invalid it should return false
    * @param string $username The username to be tested
    * @param string $password The password to be tested
    * @param bool $rememberMe Should the authentication system try and remember the user
    */
   abstract public function login($username, $password, $rememberMe=false);

}


Note: you need to put this in your own class folder and therefore use Doo::loadClass until I migrate it into the dooframework and therefore add support for Doo::loadCore(...);

If I get some time I will try and post a bit more on how I am using this class but this is rather interwoven with my apps and therefore means putting up a lot of classes for it to make sense and I do not really want to go copying all this code into some posts right now.


Richard
PS: There are examples of using DooSession, DooAcl, DooRbAcl around on the forums / blog etc so everything you need is 'somewhere' just a case of playing around with things
PPS: You will see an example of why user authentication is not currently in the framework...my class assumes you WILL be using (Rb)Acl although you do not have to...but extra weight in the class if its not used.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Re: User Auth Class

Postby benh » Mon Jul 05, 2010 9:00 pm

Superb, thanks Richard, appreciate the detail.

Regards, Ben.
benh
 
Posts: 17
Joined: Sun Jun 27, 2010 5:14 pm

Re: User Auth Class

Postby RichardM » Tue Jul 06, 2010 10:34 pm

Hey,

Thought I would post a bit more information on this...note I am just grabbing bits of code and mashing it together here as I don't want to copy all my code here:

1) Update index.php to setup your acl rules
Code: Select all
// Load the ACL Rules - See the example for info on doing this...
include $config['SITE_PATH'] . 'protected/config/acl.conf.php';

...

Doo::acl('DooRbAcl')->rules = $acl;  // Load RbAcl Module and define the rules
Doo::acl()->defaultFailedRoute = '/authentication-error';


2) Have a BaseController. Mil0s posted a good example starting point for this on the learn doophp blog. Note: I often refer to it as a BaseController which is just the terminology I'm more used to but its the same idea as the CoreController. Find the code here:
http://learn.doophp.com/2009/09/concept ... framework/

3) In my BaseController I added some code to my BaseController..there is more but this is what you need for this task:
Code: Select all
Doo::loadClass('auth/MyUserClass'); // Class defined above...change to something more user friendly
Doo::loadCore('DooController');
class BaseController extends DooController {

   public function beforeRun($resource, $action) {
      parent::beforeRun($resource, $action);

      $this->checkUserObjectInSessionOrCreateNew();

// See if user has access to called controller / action
      return $this->checkUserHasAccessTo($resource, $action);
   }

   /**
    * Checks to see if the user already has an active session and recovers there user object
    * otherwise it will create a new user object and assign it to there session
    *
    * The object can then be used to authenticate a user, log them out and check there roles
    */
   private function checkUserObjectInSessionOrCreateNew() {

      // Disable URL Session ID's
      ini_set('session.use_cookies', 1);
      ini_set('session.use_only_cookies', 1);

      // Set the session namespace and start it
      Doo::session('MyAppNameSpace'); // Change to suitable namespace for your application

      // If they do not have a user object specified we will create a new one (anonymous)
      if (isset(Doo::session()->user) == false) {
         Doo::session()->user = new MyUserClass();
      }
   }

     /**
     * Check if the user's role is able to access the resource/action.
     *
     * @param string $resource Resource name (use Controller class name)
     * @param string $action Action name (use Method name)
     * @return array|string Returns the fail route if user cannot access the resource
    */
   private function checkUserHasAccessTo($resource, $action) {
      if(($rs = $this->acl()->process(Doo::session()->user->getRoles(), $resource, $action ))){
         return $rs;
      }
   }
}


4) Thats all you really need (just make sure you setup the user class from my earlier post correctly

5) I have then added a renderAction() function I can call from any of the child classes so I can use layouts etc and setup access to translations etc...I also have this set a $data['user'] = Doo::session()->user so I can then make use of the user object in my templates...

6) in your template you can do things like:
Code: Select all
{% user->hasRole('some-role') %}
You have the role some-role!
{% endif %}


Hope this helps get things moving a little more for you.

You will then need to setup your own controller like AccountController and setup some actions in here so you will probably want:
  • Register
  • Login
  • Logout
  • MyAccount
  • RecoverPassword

The login function would then need to call:
Code: Select all
Doo::session()->user->login($username, $password, $rememberMe);


The logout function would then need to call:
Code: Select all
Doo::session()->user = null;
Doo::session()->destroy();


You would also likely redirect the user onto there 'home page' or the site homepage after completing these tasks

Oh and you need to define your user table and have your MyUserClass define the login function so something like:
Code: Select all
   public function login($username, $password, $rememberMe=false) {
      // You would check all this against your db and populate things from here but leave this to you
      if ($username = 'Demo' && $password = 'p@55w0rd') {
         $this->userId = 1;
         $this->username = $username;
         $this->name = 'Demo User';

         $this->userRoles = array('role1', 'role2', 'role3');

         $this->isLoggedIn = true;
         return true;
      }
      return false;
   }


Good luck!

Richard
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Next

Return to General Discussion

Who is online

Users browsing this forum: No registered users and 0 guests