CakePHP perform Auth before Constructing Models? -


in cakephp application have multi-tenancy provided through isolated databases (each tenant has own, tenant-specific database).

there 'global' database contains users , tenancy information. 'tenants' table contains name of database particular tenant occupies. each user contains single tenant_id.

structure:

global_db:     users (contains tenant_id foreign key)     tenants (contains tenant-specific database name, ie: 'isolated_tenant1_db')  isolated_tenant1_db:     orders     jobs     customers  isolated_tenant2_db:     orders     jobs     customers 

this system works correctly when user logged in via forms / sessions. when login through /users/login tenancy verified, stored in session, , database parameters loaded own 'isolated' models can use dynamic connection.

however, issues arise when user tries login via basic auth, , directly request controller function want access. example /orders/view/1.xml. in case, cakephp attempts construct 'order' model before user has been logged in, , therefore before tenancy information available - means has no idea database connect in order access orders.

from putting debug() statements around place can see order in models / controllers / auth constructed / executed follows (when executing /orders/view/1.xml):

  1. model __construct: user
  2. controller __construct: orderscontroller
  3. model __construct: permission
  4. model __construct: order
  5. function: orderscontroller/beforefilter
  6. authcomponent __startup
  7. model __construct: models related order

my problem authcomponent::_startup executed after order model has been constructed. need attempt login user (and database information) before 'order' model constructed.

questions:

  • what causes user model constructed before else? (i have default cakephp acl enabled)
  • where in app can put call auth->login() attempt login if request contains basicauth headers, executed prior trying load tenant-specific models? assume putting inside user __construct bad idea.

== update 01/05/2014 == inserting code samples.

bootstrap.php: checks whether request being made api. subdomain:

// determine whether request coming api.* subdomain, , if set api_request define true.  if (preg_match('/^api\./i',$_server['http_host'])) {     define('api_request',true);      // links generated (in emails etc), contain full base url. if cron job logged in via api generating     // e-mails, users receive links api.mydomain, instead of mydomain.     $full_base_url = router::fullbaseurl();     $new_full_base_url = preg_replace('/\/\/api\./i', '//', $full_base_url);     router::fullbaseurl($new_full_base_url);     cakelog::write('auth_base_url_debug', 'modified fullbaseurl ' . $full_base_url . ' ' . $new_full_base_url); } else {     define('api_request',false); } 

appcontroller.php:

public $components = array(             'security',             'session',             'acl',             'auth' => array(                     'classname' => 'extendedauth',                     'authenticate' => array(                             'formalias',                     ),                     'authorize' => array(                             'actions' => array('actionpath' => 'controllers')                     ),                     'loginredirect' => array('controller' => 'consignments', 'action' => 'index'),                     'logoutredirect' => array('controller' => 'users', 'action' => 'login'),             ),             //'users.rememberme',     );  function beforefilter()  { // reroute requests api subdomain (ie: api.mydomain) api_ prefixed actions.         // also, enable basic authentication if user accessing via api.*         // if login fails, return 401 error instead of 302 redirect login page.         if(api_request == true)         {                $this->params['action'] = 'api_'.$this->params['action'];   // prefix actions api_              $this->auth->authenticate = array('basicalias');            // switch using basic authentication              if($this->auth->login() == false)                           // attempt basic auth login             {   // login failed                 cakelog::write('auth_api', 'unauthorized api request to: ' . $this->params['action']);                 header("http/1.0 401 unauthorized");                    // force returning unauthorized header (401)                 exit;                                                   // must called prevent 302 being sent!             }         } } 

it important note basicalias auth component not included in $components within appcontroller, used dynamically if request api.* subdomain. however, order in classes constructed has no effect whether basicalias authcomponent included in $components, or used dynamically shown above.

appmodel:

function __construct($id = false, $table = null, $ds = null) {            if(($ds == null) && ($this->use_tenant_database == true))     {                    // create connection tenants database , configure model use connection.                   $tenant = classregistry::init('tenant');          $db_name = $tenant->checkandcreatetenantdatabaseconnectionforcurrentuser();          if($db_name == false)         {             header("http/1.0 500 server error");                    // force returning server error header (500)             debug('appmodel::$db_name = false, unable proceed');             cakelog::write('tenant_error', 'db_name = false, unable connect.');             exit;                                                   // must called prevent 302 being sent!         }          // point model tenant database connection:         $this->usedbconfig = $db_name;     }      parent::__construct($id, $table, $ds); } 

and within models use specific tenant database:

class order extends appmodel {     var $use_tenant_database = true;         ... } 

tenant.php:

/**      * check whether connection current users tenant database has been created , if so, return name.      * otherwise, create connection , return name.      *       * @return boolean|ambigous <mixed, multitype:, null, array, boolean>      */     public function checkandcreatetenantdatabaseconnectionforcurrentuser()     {         // check whether have tenants database connection information available in configure variable:         if(configure::check('tenant.db_name') == true)         {   // db_config available in configure, use it!             $db_name = configure::read('tenant.db_name');         }         else         {   // tenants db_name has not been set in configure variable, need create database connection ,             // set configure variable.             $tenant_id = $this->getcurrentusertenantid();              if($tenant_id == null)             {   // unable resolve tenant_id, instead, connect default database.                 debug('tried construct model without knowing tenant database!!');                              exit;             }              $db_name = $this->tenantdatabase->createconnection($tenant_id);              if($db_name == false)             {   // database connection not created.                 cakelog::write('tenant_error', 'unable find database name tenant_id: ' . $tenant_id);                 return false;             }              configure::write('tenant.db_name', $db_name);         }          return $db_name;     } 

so, if user requests url example: http://api.mydomain.com/orders/getallpendingorders have supplied basic auth credentials along request, happens classes constructed / executed in following order:

  1. model __construct: user
  2. controller __construct: orderscontroller
  3. model __construct: permission
  4. model __construct: order
  5. model __construct: tenant
  6. model __construct: tenantdatabase
  7. function: orderscontroller/beforefilter
  8. authcomponent __startup --> performs login.
  9. model __construct: other models.

the problem is: order.php being constructed user has been logged in, means when code in appmodel.php executed:

$db_name = $tenant->checkandcreatetenantdatabaseconnectionforcurrentuser(); 

it unable determine users current tenancy.

i need find out workaround this, either somehow performing login before order.php constructed, or hacking if attempt construct model has $use_tenant_database = true, , user not logged in, basicauth performed @ point try , login user.. feels wrong me.

you might want have @ authorization (who’s allowed access what) portion in cake's documentation. @ isauthorized function , how works.

you might need in orders controller:

// app/controller/orderscontroller.php  public function isauthorized($user) {     // registered users can add posts     if ($this->action === 'add') {         return true;     }      // owner of order can edit , delete     if (in_array($this->action, array('edit', 'delete'))) {         $orderid = (int) $this->request->params['pass'][0];         if ($this->order->isownedby($orderid, $user['id'])) {             return true;         }     }      return parent::isauthorized($user); } 

Comments

Popular posts from this blog

javascript - jquery or ashx not working -

opencv - DataType<cv::detail::deriv_type>::depth what is it used for -

python 3.x - Mapping specific letters onto a list of words -