0

I have two tables in my database, one for admins (named Admins) and the other one for normal users, named : utilisateurs (in french). I know i have to use cakePHP's convention which says i must create a table named users, which has the fields username and password. But the problem is that i have two tables and not only one. Each one has its particular fields, so i really need them to stay separated. And even informations required to login are different depending on the user :

  • admins need their login + password

  • normal users need a specific id (in their id cards) + password

What i want to do, is create two login pages, one for admins and the other for normal user. After logging in, the user is redirected to the page he is supposed to see. But yet, if the user tries attempt a forbidden location. I want be able to stop him (beforeFilter + isAuthorized i think)

How can i make all this work ?

I'm not beginner in cakephp, i've already made an authentication system in anotehr app using Auth Component, but it was a little easier because needed only one table for users.

Your help would be much appreciated.

codeless
  • 145
  • 5
  • 14
  • 3
    That is usually already failure by design. Try to keep all users in the same table - and distinguish them via role. And there should also be always only one login page. Keep it simple. – mark Jul 08 '14 at 16:04
  • I can't keep all users on the same table because of foreign keys constraints, for example : an employee belongs to a department while the admin doesn't belong anywhere. Naturally, users table will have a foreign key which refers to departments table. And if i wanna create an admin account i can't just put nothing or null on department_id. This was just one example among others. The design isn't fail i think ! – codeless Jul 08 '14 at 16:17
  • 2
    @codeless Why not put null on department_id? Also, there are other ways you can design your database, such as having a users table for that data that is in common with all users (i.e. login and password), then have an admin table and a regular user table that you associate with your users table to contain those fields that are unique to admin or regular users. – Kai Jul 08 '14 at 16:22
  • Listen to mark and after login redirect the user by UserTypes e.g. User redirect to some page Admin to an other page – Fury Jul 08 '14 at 16:25
  • Ok now tell me which method i should apply ? keeping only one table users for all users and differentiate them by the field role ? Or associate admins and normal users tables with the 'Users' one ? Thank you for advices. – codeless Jul 08 '14 at 16:43
  • @Kai u can't put null on a foreign key i think !! – codeless Jul 08 '14 at 16:48
  • @codeless Sure you can. See this question: http://stackoverflow.com/questions/2366854/can-table-columns-with-a-foreign-key-be-null – Kai Jul 08 '14 at 17:11
  • @Kai Yeah it's a trick but it can engender other issues in the future. i understnd from the example that the id can be null even if he is primary key. We can force it, yes, but not the best solution i guess ! i'm hoping to build a robust application – codeless Jul 08 '14 at 17:26

1 Answers1

0

Assuming the following:

  • You have 2 tables associated with model User and Admin, where:
    • User has idcard and password fields.
    • Admin has login and passowrd field.
  • The passwords are hashed using User::hasPassword and Admin::hasPassword functions.
  • You login form is created as follows:

    echo $this->Form->create(null, '') ; echo $this->Form->input('login') ; echo $this->Form->input('password') ; echo $this->Form->end(__('Submit')) ;

You can create a new Authenticate component under App/Controller/Component/Auth/MyAuthenticate.php:

<?php

App::uses('FormAuthenticate', 'Controller/Component/Auth');

class MyAuthenticate extends FormAuthenticate {

    public function authenticate(CakeRequest $request, CakeResponse $response) {

        $username = $request->data['login'] ;
        $password = $request->data['password'] ;

        App::import('Model', 'User') ;
        $userModel = new User () ;

        /* Try to authenticate as a user... */
        $user = $userModel->find('first', array(
            'conditions' => array(
                'idcard' => $username,
                'password' => User::hashPassword($password) ;
            )
        )) ;

        if ($user) {
            $user = $user['User'] ; // Get only useful info
            $user['type'] = 'user'; // Save user type
            return $user ;
        }

        /* Same thing for admin. */

        App::import('Model', 'Admin') ;
        $adminModel = new Admin () ;

        $user = $adminModel->find('first', array(
            'conditions' => array(
                'login' => $username,
                'password' => Admin::hashPassword($password) ;
            )
        )) ;

        if ($user) {
            $user = $user['Admin'] ; // Get only useful info
            $user['type'] = 'admin'; // Save user type
            return $user ;
        }

        return null ;

    }

};

You just need to be sure that that a admin cannot be authenticated as a user, and reverse.

In your AppController:

public $components = array(
    'Auth' => array(
        'authenticate' => array('My'), // The prefix in front of your component
        'loginAction' => array(/* ... */),
        'loginRedirect' => array(/* ... */),
        'logoutRedirect' => array(/* ... */),
        'authError' => "...",
        'authorize' => 'Controller'
    )
) ;

Your login action is the same that for normal tables:

public function login () {
    if ($this->request->is('post')) {
        if ($this->Auth->login()) {
            $this->redirect($this->Auth->redirect());
        } 
        else {
            $this->request->data['password'] = "" ;
            $this->Session->setFlash('Invalid login/id or password.');
        }
    }
}

Then, in beforeFilter or isAuthorized you can check $this->Auth->user('type');. For example, in AppController:

public function isAuthorized () {
    /* Assuming you keep CakePHP convention where action starting with admin_ set admin params. */
    if(isset($this->params['admin'])) {
        return $this->Auth->user('type') == 'admin' ;
    }
    return true ;
}

Or if you want to disable access for non admin users to all action in AdminController, use beforeFilter:

class AdminController extends AppController {

    public function beforeFilter () {
        if (!$this->Auth->loggedIn()) {
            $this->Session->setFlash('You need to be logged to access this page.');
            $this->redirect(/* Login url. */) ;
            return ;
        }
        if ($this->Auth->user('type') != 'admin') {
            $this->Session->setFlash('You need to be admin to access this page.');
            $this->redirect(/* Somewhere... */) ;
            return ;
        }
        return parent::beforeFilter () ;
    }

}
Holt
  • 36,600
  • 7
  • 92
  • 139
  • thank you, i'll try this later. And i'll tell you the result. – codeless Jul 08 '14 at 18:18
  • Sorry but i didn't unsdertood well your answer, can you tell me more details, like where write it (in which file) and what should i put instead of three points : $password = $request->data['...'] ; – codeless Jul 08 '14 at 18:29
  • Just note that if you apply this solution you will need to remember to check if ``Auth::user('id')`` belongs to an admin or a simple user in *all* your code that depends on it. Just think what crazy security holes this can cause (i.e. save some data created by admin with id 123 and be visible by a normal user with the same id). This is why cake doesn't do it. You can of course extend ``AuthComponent`` to use ``Auth::user()`` for users and a new ``Auth::admin()`` for admins but that might not justify the pain and be easier to merge your models. – user221931 Jul 08 '14 at 19:02
  • @codeless You need to put this code in `App/Controller/Component/Auth` with name `AmicaleAuthenticate.php`. The `$password = $request->data[...]` just retrieve the password from your request so the `...` should be replaced by your field name. – Holt Jul 08 '14 at 20:12
  • @user221931 It depends on how your site is build. I use this because I have kind of 2 sites in one so it works pretty well. Of course, you've to be careful on what you do with this. – Holt Jul 08 '14 at 20:13
  • I'll try this as soon as possible and come back to tell you the result. – codeless Jul 08 '14 at 21:57
  • @codeless I made a very short answer because I thought you knew how the authentification components works (you "i've already made an authentication system in anotehr app using Auth Componen"). If you need more details, let me know. – Holt Jul 09 '14 at 07:51
  • I still can't understand your solution. I think the best way is regroup admins and normal users in the Users table, i'll do it the oldschool way. Neverthless, Thank you and i hope your answer will be useful for others. – codeless Jul 09 '14 at 10:27
  • @codeless I added lot of details in my answer, hope it'll help you. – Holt Jul 09 '14 at 11:35