Andreas Daoutis Andreas Daoutis - 1 year ago 243
PHP Question

cakePHP 3 best practises for using request/auth data in model (account based cloud service)

I'm creating a cloud service where there are multiple accounts and each account has multiple users. Users can only log into their account. Each account has an unique alias. (e.g. abc). If users go to their sub-domain (e.g. they are able to log into their account and see only the stuff which is assigned to that account. The login works very well.

I have a htaccess redirection rule added which transforms the sub-domain to a query.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{HTTP_HOST} ^([a-z0-9]+)\.cloud\.service$ [NC]
RewriteRule ^ index.php?alias=%1 [L]

So in all controllers I have the alias as a query available. In order to guarantee that users are only allowed to see their contents all models are related to an account. Every time I want to perform a find operation on one of those models I want to make sure that the results belong to the correct account. I'm doing that in the

// beforeFind of models which are related to an account
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) {
if ($primary) {

And to get the correct account I have a
in the Accounts model where I check the query.

// beforeFind of the Accounts model
public function beforeFind (Event $event, Query $query, ArrayObject $options, $primary) {
if (isset($_GET['alias'])) {
$query->where(['Accounts.alias' => $_GET['alias']]);

This works amazingly well as all my find operations are returning only data which is allowed for the that account. But I know what I'm doing is probably not called best practice. Question 1: So is there a better way of doing that? I don't know how to access the query data in the model?

Problem 2:
When I save any type of content I need to make sure it is assigned to the correct account.

Option 1 (ineffective due to too many occurrences)
I could manually assign the accound id before all save calls depending on the users account id. But I'm going to have too many save calls, so I would like to have a better solution here.

Option 2 (is what I'm doing right now)
I add some code to the beforeSave to all models which are related to the account:

// beforeSave in models related to Accounts model
public function beforeSave(Event $event, Entity $entity, ArrayObject $options)
if (!isset($_SESSION['Auth']['User']['account_id'])) {
$entity->account_id = $_SESSION['Auth']['User']['account_id'];

Works again very well, but I also know that accessing the session variable directly is not recommended by cakePHP. Question 2, is there a better solution here? I didn't find a working solution on accessing auth data in models

Answer Source

First of all, I would say accessing authorization information within your models is not best practice because it doesn't enforce a good level of separation of concerns between controller logic and business logic.

I would say the best case to use here would be a dispatch filter, perhaps something like:

class SubdomainFilter extends DispatcherFilter
    public function beforeFilter(Event $event)
        if (isset($_GET['alias'])) {
            $event->data['request']->subdomain_alias = $_GET['alias'];

Then initialize your new filter in bootstrap.php:


Then finally you can access the subdomain alias as a property attached to your request object in your controller to be passed as an argument into your model functions:

class FooController extends AppController
    public function exampleEndpoint() {
        $your_table->saveFunction($this->request->subdomain_alias, $data);

You can read more about Dispatch Filters in CakePHP 3 here.