cakePHP 2.x Custom Authentication - cakephp

So I am working with cakePHP 2.3 and I try to use different Frameworks when possible a) to keep my 41 Year old mind aware b) To make sure I use every tool in the shed for myself and my customer.
I have a personal SaaS app Im building and need to know the best way to add "where site_id = 2" to the authentication calls basically based on how they are viewing the app i.e. subdomain or domain sets a particular site_id in AppController.
I have looked for custom authentication but I havent seen anything that stood out. I also have a roles column & table which is comma delim I need to join in the auth request
Any good how to's or pointers would be great
Thanks

I'm just taking a shot in the dark here with limited info but i think this is somewhere around the woods of what your looking for.
Locate your cake build and navigate to /lib/Cake/Controller/Component/Auth/BaseAuthenticate.php
Locate:
public $settings = array(
'fields' => array(
'username' => 'username',
'password' => 'password'
),
'userModel' => 'User',
'scope' => array(),
'recursive' => 0,
'contain' => null,
);
and make your mods there.

Related

CakePHP 3.5 Auth use multiple tables

I have an Auth process which works fine with one userModel. But not only because of my DB schema I need to have one login method/action which works with multiple models.
So far I've tried everything I was able to think of or find online - for example editing this Cake 1.3 solution into Cake 3 and a few more hints I was able to find.
However, I'm not able to figure it out.
Thank you for any answer.
My AppController component load:
$this->loadComponent('ExtendedAuth', [
'authenticate' => [
'Form' => [
//'userModel' => 'Admins',
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Admins',
'action' => 'login'
],
// If unauthorized, return them to page they were just on
'unauthorizedRedirect' => $this->referer(),
]);
My ExtendedAuthComponent:
class ExtendedAuthComponent extends AuthComponent
{
function identify($user = null, $conditions = null) {
$models = array('Admins', 'Users');
foreach ($models as $model) {
//$this->userModel = $model; // switch model
parent::setConfig('authenticate', [
AuthComponent::ALL => [
'userModel' => $model
]
]);
$result = parent::identify(); // let cake do its thing
if ($result) {
return $result; // login success
}
}
return null; // login failure
}
}
EDIT1: Description of situation
I have two separate tables (Admins, Users). I need just one login action which tries to use Admins table prior to Users. Because of the application logic I can't combine them to one table with something like 'is_admin' flag. So basically what I need is instead of one specific userModel set in Auth config, I need a set of models. Sounds simple and yet I'm not able to achieve it.
EDIT2: Chosen solution
Based on the answer below, I decided to update my schema. Auth users table is just simplified table with login credentials and role and other role-specific fields are then in separate tables which are used as a connection for other role-specific tables. Even though the answer is not exactly a solution for the asked question, it made me think more about any possible changes of the schema and I found this solution because of it so I'm marking it as a solution. I appreciate all comments as well.
As Mark already said in a comment: Don't use two users tables. Add a type field or role or whatever else and associated data in separate tables if it's different like admin_profiles and user_profiles.
Don't extend the Auth component. I wouldn't recommend to use it anymore any way because it's going to get deprecated in the upcoming 3.7 / 4.0 release. Use the new official authentication and authorization plugins instead.
If you insist on the rocky path and want to make your life harder, well go for it but then you should still not extend the auth component but instead write a custom authentication adapter. This is the right place to implement your custom 2-table-weirdness. Read this section of the manual on how to do it.

Cakephp Containable not working at all

I have been banging my head on the wall over this. I have a model Sku that belongs to model Purchase. My AppModel has $actAs=array('Containable') and $recursive=-1
Inside SkuController, when I do $this->Sku->find('all', array('contain' => 'Purchase')); I don't get Purchase. I have searched many old questions here and elsewhere on Internet but just can't seem to resolve this. To check if Containable behavior is being loaded, I edited ContainableBehavior.php in lib\Cake\Model\Behavior to make it an invalid php file but that didn't produce any errors. What the heck is wrong!!
Here's the SQL from debug:
SELECT Sku.id, Sku.purchase_id, Sku.item_id, Sku.upc,
Sku.quantity_avail, Sku.per_unit_price_amt,
Sku.do_not_delete, Sku.created, Sku.modified,
(concat('SK',lpad(Sku.id,8,'0'))) AS Sku__idFormatted FROM
sellble.skus AS Sku WHERE 1 = 1 ORDER BY Sku.id desc
CakePHP ver: 2.4.4
Not sure if this is different across versions but I have always specified the contain within an array and that works fine for me.
$this->Sku->find('all', array('contain' => array('Purchase')));
Or for mapping only the fields or conditions you want:
$this->Sku->find('all',
array('contain' => array(
'Purchase' => array(
'fields' => Purchase.name
'conditions' => array(
Purchase.name = 'somename'
)
)
)
)
);

Multiple image upload - Meio Upload

I am using the 'MeioUpload' plugin found here 'https://github.com/jrbasso/MeioUpload' and Cakephp 2.x.
Currently using this for single image uploads, please can anyone give advice on how to handle multiple image uploads using this plugin. Currently the db table storing the images holds filename, dir, mimetype and filesize fields for each image. I want to store more than one image for each of my posts when adding a new post. Any help would be much appreciated, thanks in advance :).
As I mentioned in my comment, you might want to try https://github.com/josegonzalez/upload as MeioUpload is now deprecated, and it's developer is working on that new upload plugin I linked to.
Either way, the following info for MeioUpload holds true for the new plugin, too.
MeioUpload is built to handle one uploaded file per corresponding set of fields. I don't think the example in MeioUpload's ReadMe is ideal, as it seems to imply that you have to have a table of 'images', where as in reality, you can have a table of just about anything, where each record holds one or more uploaded files (be it images, PDF's, MP3's... anything).
So, with that in mind, you have two solutions:
1) If your posts will have a potentially infinite number of images (ie, not a fixed, small number) then you can have Posts and Images in separate tables, and set up a hasMany relationship between them. See http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
2) If you know that each post will only have a max of say 3 or 4 (or some other relatively small number) of images, then you can implement 3 (or 4, or X) sets of image fields in your Posts table / model, each to handle a separate upload. They'd be named, eg. featured_image_filename, feautred_image_dir, etc; image2_filename, image2_dir, image2_mimetype, etc; image3_filename, image3_dir, etc.
Your acts as would look something like:
var $actsAs = array(
'MeioUpload.MeioUpload' => array(
'featured_image_filename' => array(
'fields' => array(
'dir' => 'featured_image_dir',
'filesize' => 'featured_image_filesize',
'mimetype' => 'featured_image_mimetype'
),
),
'image2_filename' => array(
'fields' => array(
'dir' => 'image2_dir',
'filesize' => 'image2_filesize',
'mimetype' => 'image2_mimetype'
),
),
'image3_filename' => array(
'fields' => array(
'dir' => 'image3_dir',
'filesize' => 'image3_filesize',
'mimetype' => 'image3_mimetype'
),
),
)
);
This second solution is hardly ideal database design, but sometimes when you know there'll never be more than a few images, it's just the easiest way to do it - both in terms of developing, and in terms of an easy to use UI.
Make sense?

CakePHP: Deleting a User deletes Users associated via belongsTo

I want to be able to delete a User, but a User has a Manager:
var $belongsTo = array(
'Manager' => array(
'className' => 'User',
'foreignKey' => 'manager_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
);
And whenever a User is deleted, all of it's "children" are deleted too.
For example, say I delete User A. User A is the manager of users B, C, and D. When A is deleted, so are B, C, and D, because they have A as their manager_id.
So my question is - is this supposed to be happening? And is there a way I can prevent this from happening?
Thanks!
Read the book, that is intended behavior and you can stop it by adding
'dependent' => false,
to the associations configuration array.
See http://book.cakephp.org/1.2/en/view/78/Associations-Linking-Models-Together and search for "dependent" on this page.
And I recommend you to use 2.0 if it's a new project, 1.2 is deprecated for a long time now.
It was an ACL issue - totally unexpected. Since our users operate in a tree structure with the Manager, the User has a lft and rght field that is only updated in the afterSave. The easy solution is to dissociate the user by setting their lft and rght to 0, but after deeper thought, I am setting their manager_id to NULL and saving it so that the tree reorganizes itself (via the afterSave).
Wow. That was quite the problem.

CakePHP AutoComplete Question

I am working on a book review application and I am using autoComplete to search for titles in when creating a review. The review model has an associated book_id field and the relationship is setup as the review hasOne book and a book hasMany reviews.
I am trying to pass the Book.id (into the book_id field), but I want to display Book.name for the user to select from. With the default setup (accomplished via CakePHP's tutorial), I can only pass Book.name. Is it possible to display the name and pass the id?
Also, I am passing it via the following code in the create() action of the review controller:
$this->data['Review']['book_id'] = $this->data['Book']['id'];
Is that the proper way to do it in CakePHP? I know in Ruby on Rails, it is automatic, but I can't seem to make it work automagically in CakePHP. Finally, I am not using the generator because it is not available in my shared hosting environment... so if this is the wrong way, what do I need other than associates in my models to make it happen automatically?
Thanks for the help and I promise this is my question for awhile...
UPDATE- I tried the following, but it is not working. Any ideas why?
function autoComplete() {
$this->set('books', $this->Book->find('all', array(
'conditions' => array(
'Book.name LIKE' => $this->data['Book']['name'].'%'
),
'fields' => array('id','name')
)));
$this->layout = 'ajax';
}
The problem is that when I use the code above in the controller, the form submits, but it doesn't save the record... No errors are also thrown, which is weird.
UPDATE2:
I have determine that the reason this isn't working is because the array types are different and you can't change the array type with the autoComplete helper. As a workaround, I tried the follow, but it isn't working. Can anyone offer guidance why?
function create() {
if($this->Review->create($this->data) && $this->Review->validates()) {
$this->data['Review']['user_id'] = $this->Session->read('Auth.User.id');
$this->Book->find('first', array('fields' => array('Book.id'), 'conditions' => array('Book.name' => $this->data['Book']['name'])));
$this->data['Review']['book_id'] = $this->Book->id;
$this->Review->save($this->data);
$this->redirect(array('action' => 'index'));
} else {
$errors = $this->Review->invalidFields();
}
}
FINAL UPDATE:
Ok, I found that the helper only takes the find(all) type or array and that the "id" field wasn't passing because it only applied to the autoComplete's LI list being generated. So, I used the observeField to obtain the information and then do a database lookup and tried to create a hidden field on the fly with the ID, but that didn't work. Finally, the observeField would only take the characters that I put in instead of what I clicked, due to an apparent Scriptaculous limitation. So, I ended up going to a dropdown box solution for now and may eventually look into something else. Thanks for all of the help anyway!
First of all, $this->data will only contain ['Book']['id'] if the field exists in the form (even if it's hidden).
To select something by name and return the id, use the list variant of the find method, viz:
$selectList = $this->Book->find('list', array(
'fields' => array(
'id',
'name'
)));
$this->set('selectList', $selectList);
In the view, you can now use $selectList for the options in the select element:
echo $form->input('Book.id', array('type' => 'hidden'));
echo $form->input('template_id', array(
'options' => $selectList,
'type' => 'select'
));

Resources