cakephp-tinyauth icon indicating copy to clipboard operation
cakephp-tinyauth copied to clipboard

auth_allow.ini not parsing route prefix

Open marianodonal opened this issue 2 years ago • 11 comments

Hi Mark, when I try to use prefixed routes to allow routes in the Api prefix, it also allows the same controllers in non-prefixed routes.

ie on auth_allow.ini: Api/Countries This entry also allows non-prefixed Countries controller. I cannot figure out the reason for this behavior.

Thanks in advance.

marianodonal avatar Jul 06 '22 14:07 marianodonal

Can you Show the details? Like URLs and Controller code?

dereuromark avatar Jul 06 '22 14:07 dereuromark

Initialize on AppController.php (non-prefixed)

$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('FormProtection', [
'validationFailureCallback' => function (BadRequestException $exception) {
$this->Flash->error(__('Something went wrong... <strong>Please refresh the page.</strong>'), ['escape' => false]);
return $this->redirect( $this->request->referer() );
}
]);
$this->loadComponent('TinyAuth.Authentication');
$this->loadComponent('TinyAuth.Authorization');

Initialize on AppController.php (Api prefixed, inside Api folder)

$this->loadComponent('RequestHandler');
$this->loadComponent('TinyAuth.Authentication');
$this->loadComponent('TinyAuth.Authorization');

CountriesController.php (common CRUD functions) CountriesController.php on Api folder (only index function)

auth_allow.ini Api/Countries = index

Application.php getAuthenticationService function:

  if (strpos($path, '/api') === 0) {
     $service->loadAuthenticator('Authentication.Jwt', [
       'returnPayload' => false
     ]);
     $service->loadAuthenticator('ApiForm');
     $service->loadIdentifier('Authentication.JwtSubject', [
       'resolver' => [
         'className' => 'Authentication.Orm',
         'finder' => 'jwt'
       ]
     ]);
     $service->loadIdentifier('Password', [
       'fields' => $pwd_fields,
       'resolver' => [
         'className' => 'Authentication.Orm',
         'finder' => 'jwt'
       ]
     ]);

     return $service;
   }

   $service->setConfig([
     'unauthenticatedRedirect' => '/backoffice',
     //'queryParam' => 'redirect',
   ]);
   $service->loadAuthenticator('Authentication.Session');
   $service->loadAuthenticator('Authentication.Form', [
     'fields' => $form_fields,
     'loginUrl' => '/backoffice',
   ]);
   $service->loadAuthenticator('Authentication.Cookie', [
     'fields' => $form_fields,
     'loginUrl' => '/backoffice',
     'cookie' => [
       'expires' => new \DateTime('+1 week')
     ]
   ]);
   $service->loadIdentifier('Password', [
     'fields' => $pwd_fields,
   ]);

getAuthorizationService function:

$resolver = new MapResolver();
    $policy = new RequestPolicy([
      'includeAuthentication' => true,
    ]);
    $resolver->map(ServerRequest::class, $policy);

    return new AuthorizationService($resolver);

Let me know if you need more code or something else. Thanks!

marianodonal avatar Jul 06 '22 15:07 marianodonal

@dereuromark, have you found anything or do you have any ideas to fix this problem? Thanks in advance

marianodonal avatar Jul 12 '22 12:07 marianodonal

Are you able to reproduce sth similar with demo actions on the sandbox? https://github.com/dereuromark/cakephp-sandbox That would help, as once reproduced it is easy to track down and find the issue or a fix.

dereuromark avatar Jul 12 '22 12:07 dereuromark

Ok, I'll try. Something to keep in mind is that I am using it with the Authentication and Authorization plugins.

marianodonal avatar Jul 12 '22 13:07 marianodonal

I think I found something. In AllowTrait.php:

protected function _getAllowRule(array $params) {
		$rules = $this->_getAllow($this->getConfig('allowFilePath'));

		$allowDefaults = $this->_getAllowDefaultsForCurrentParams($params);

		foreach ($rules as $rule) {
			if ($params['plugin'] && $params['plugin'] !== $rule['plugin']) {
				continue;
			}
			if (!empty($params['prefix']) && $params['prefix'] !== $rule['prefix']) {
				continue;
			}
                         /*
* this check is missing when the prefix key is not set in the parameter array and the prefix is set in the rule
*/
			if (empty($params['prefix']) && !empty($rule['prefix'])) {
				continue;
			}
/* */
			if ($params['controller'] !== $rule['controller']) {
				continue;
			}

			if ($allowDefaults) {
				$rule['allow'] = array_merge($rule['allow'], $allowDefaults);
			}

			return $rule;
		}

		return [
			'allow' => $allowDefaults,
			'deny' => [],
		];
	}

These are the $params

[
'controller' => 'Countries',
'pass' => [ ],
'action' => 'index',
'plugin' => null,
'_matchedRoute' => '/{controller}',
'_ext' => null,
]

And this is the returned $rule

[
'plugin' => null,
'prefix' => 'Api',
'controller' => 'Countries',
'map' => [
(int) 0 => 'index',
],
'deny' => [ ],
'allow' => [
(int) 0 => 'index',
],
]

marianodonal avatar Jul 12 '22 19:07 marianodonal

Do u have a fix as well? That would solve the issue?

dereuromark avatar Jul 12 '22 19:07 dereuromark

This resolves this particular scenario. Maybe we need to run some tests. Is there a scenario like plugin key or any other key is not present?

marianodonal avatar Jul 12 '22 21:07 marianodonal

Another way is to fill missing keys in params before send to "_getAllowRule()". In AuthenticationComponent.php there is a "_prepareAuthentication()" function, maybe we can fill missing params there before calling _getAllowRule().

This is in line 99 of AuthenticationComponent.php $rule = $this->_getAllowRule($this->_registry->getController()->getRequest()->getAttribute('params')); change to

$params = $this->_registry->getController()->getRequest()->getAttribute('params');
if ( !isset($params['plugin']) ) {
$params['plugin'] = NULL;
}
if ( !isset($params['prefix']) ) {
$params['prefix'] = NULL;
}
$rule = $this->_getAllowRule($params);

marianodonal avatar Jul 12 '22 21:07 marianodonal

And this for _getAllowRule()

protected function _getAllowRule(array $params) {
	$rules = $this->_getAllow($this->getConfig('allowFilePath'));
	$allowDefaults = $this->_getAllowDefaultsForCurrentParams($params);

	foreach ($rules as $rule) {
		if ( isset($params['plugin']) && !is_null($params['plugin']) ) {
			if ($params['plugin'] !== $rule['plugin']) {
				continue;
			}
		} else {
			if (!empty($rule['plugin'])) {
				continue;
			}
		}
		if ( isset($params['prefix']) && !is_null($params['prefix']) ) {
			if ($params['prefix'] !== $rule['prefix']) {
				continue;
			}
		} else {
			if (!empty($rule['prefix'])) {
				continue;
			}
		}
		if ($params['controller'] !== $rule['controller']) {
			continue;
		}

		if ($allowDefaults) {
			$rule['allow'] = array_merge($rule['allow'], $allowDefaults);
		}

		return $rule;
	}

	return [
		'allow' => $allowDefaults,
		'deny' => [],
	];
}

Because same behavior for Plugin Routes such as Api.Countries = index

marianodonal avatar Jul 12 '22 21:07 marianodonal

Are u able to make a PR with suggested change?

dereuromark avatar Jul 12 '22 21:07 dereuromark