Welcome Guest, Not a member yet? Register   Sign In
Multi-level routing filters not working
#1

(This post was last modified: 11-28-2024, 12:45 AM by WitER.)

Good day!
I want restrict routing groups by shield permissions , but filter working only for parent group. In children groups permission filter not working. Where my bad?
CI 4.5.5 \ Shield 1.1.0

PHP Code:
$routes->group('admin', ['filter' => 'permission:admin.access'], static function (RouteCollection $routes) { // permission:admin.access is working
    $routes->get('/''Admin::index', ['as' => 'admin']);

    $routes->group('users', ['filter' => 'permission:users.access'], static function (RouteCollection $routes) { // permission:users.access is ignoring
        $routes->get('/''Auth\Users::index', ['as' => 'admin-users']);
    });
}); 
Reply
#2

Code:
For this to work you have to enable multiple filters in Config/Feature.php
PHP Code:
public bool $multipleFilters true
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply
#3

(11-28-2024, 09:55 PM)InsiteFX Wrote:
Code:
For this to work you have to enable multiple filters in Config/Feature.php
PHP Code:
public bool $multipleFilters true

It's not work for me, and search by multipleFilters in project or vendor directory not founding any using for this parameter.
Maybe there are any other ideas? Or is there something wrong with my configuration in principle?
Reply
#4

!Important

Since v4.5.0, Multiple Filters are always enabled. Prior to v4.5.0, Multiple Filters were disabled by
default. If you want to use with prior to v4.5.0, See Upgrading from 4.1.4 to 4.1.5 for the details.
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply
#5

(This post was last modified: 11-30-2024, 03:36 AM by WitER.)

(11-29-2024, 10:29 PM)InsiteFX Wrote: !Important

Since v4.5.0, Multiple Filters are always enabled. Prior to v4.5.0, Multiple Filters were disabled by
default. If you want to use with prior to v4.5.0, See Upgrading from 4.1.4 to 4.1.5 for the details.

Yep, but i created project from v4.5.0, now 4.5.5 and filters for nested routing groups does not work.
Reply
#6

If you think there is a problem then open up an issue on GitHub for it.
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply
#7

See in console or test controller command():
spark routes
spark filter:check /users
https://codeigniter.com/user_guide/incom...lter-check
Simple CI 4 project for beginners codeigniter-expenses ( topic )
Reply
#8

(12-01-2024, 09:26 PM)ozornick Wrote: See in console or test controller command():
spark routes
spark filter:check /users
https://codeigniter.com/user_guide/incom...lter-check

spark routes
Code:
CodeIgniter v4.5.5 Command Line Tool - Server Time: 2024-12-02 11:47:12 UTC+00:00

+--------+------------------------------+-------------------------------------+--------------------------------------------------------------------+------------------------------------+-----------------------+
| Method | Route                        | Name                                | Handler                                                            | Before Filters                    | After Filters        |
+--------+------------------------------+-------------------------------------+--------------------------------------------------------------------+------------------------------------+-----------------------+
| GET    | /                            | main                                | \App\Controllers\Home::index                                      | csrf                              |                      |
| GET    | profile                      | »                                  | \App\Controllers\Auth\Profile::index                              | csrf session session              | session              |
| GET    | profile/(.*)                | profile-tab                        | \App\Controllers\Auth\Profile::index/$1                            | csrf session session              | session              |
| GET    | admin                        | »                                  | \App\Controllers\Admin::index                                      | csrf session permission            | permission            |
| GET    | admin/users                  | admin-users                        | \App\Controllers\Auth\Users::index                                | csrf session permission permission | permission permission |
| GET    | register                    | »                                  | \CodeIgniter\Shield\Controllers\RegisterController::registerView  | csrf auth-rates                    |                      |
| GET    | login                        | »                                  | \CodeIgniter\Shield\Controllers\LoginController::loginView        | csrf auth-rates                    |                      |
| GET    | login/magic-link            | magic-link                          | \CodeIgniter\Shield\Controllers\MagicLinkController::loginView    | csrf auth-rates                    |                      |
| GET    | login/verify-magic-link      | verify-magic-link                  | \CodeIgniter\Shield\Controllers\MagicLinkController::verify        | csrf auth-rates                    |                      |
| GET    | logout                      | »                                  | \CodeIgniter\Shield\Controllers\LoginController::logoutAction      | csrf                              |                      |
| GET    | auth/a/show                  | auth-action-show                    | \CodeIgniter\Shield\Controllers\ActionController::show            | csrf auth-rates                    |                      |
| POST  | profile/update              | update-profile                      | \App\Controllers\Auth\Profile::update                              | csrf session session              | session              |
| POST  | profile/change-password      | update-profile-password            | \App\Controllers\Auth\Profile::changePassword                      | csrf session session              | session              |
| POST  | profile/toggle-2fa          | update-profile-2fa                  | \App\Controllers\Auth\Profile::toggle2FA                          | csrf session session              | session              |
| POST  | profile/purge-remember-token | update-profile-purge-remember-token | \App\Controllers\Auth\Profile::purgeRememberToken                  | csrf session session              | session              |
| POST  | register                    | »                                  | \CodeIgniter\Shield\Controllers\RegisterController::registerAction | csrf auth-rates                    |                      |
| POST  | login                        | »                                  | \CodeIgniter\Shield\Controllers\LoginController::loginAction      | csrf auth-rates                    |                      |
| POST  | login/magic-link            | »                                  | \CodeIgniter\Shield\Controllers\MagicLinkController::loginAction  | csrf auth-rates                    |                      |
| POST  | auth/a/handle                | auth-action-handle                  | \CodeIgniter\Shield\Controllers\ActionController::handle          | csrf auth-rates                    |                      |
| POST  | auth/a/verify                | auth-action-verify                  | \CodeIgniter\Shield\Controllers\ActionController::verify          | csrf auth-rates                    |                      |
+--------+------------------------------+-------------------------------------+--------------------------------------------------------------------+------------------------------------+-----------------------+

and check
Code:
php spark filter:check GET /admin/users/

CodeIgniter v4.5.5 Command Line Tool - Server Time: 2024-12-02 11:48:42 UTC+00:00

+--------+---------------+---------------------------------------------------------+-----------------------------------------------------+
| Method | Route        | Before Filters                                          | After Filters                                      |
+--------+---------------+---------------------------------------------------------+-----------------------------------------------------+
| GET    | /admin/users/ | forcehttps pagecache csrf session permission permission | permission permission pagecache performance toolbar |
+--------+---------------+---------------------------------------------------------+-----------------------------------------------------+
Reply
#9

(This post was last modified: 12-11-2024, 08:13 AM by WitER.)

@ozornick , i found why "permission:users.access" filter not work for child route "admin-users". This is because the parent group filter overwrites the filter arguments.
In my example - permission filter arguments for route group "/admin" rewrites arguments for route "/admin/users".

I just override "Codeigniter\Filters\Filters" class and it work.
PHP Code:
<?php

declare(strict_types=1);

namespace 
App\Filters;

use 
CodeIgniter\Filters\Exceptions\FilterException;
use 
CodeIgniter\Filters\FilterInterface;
use 
CodeIgniter\Filters\Filters as BaseFilters;
use 
CodeIgniter\HTTP\RequestInterface;
use 
CodeIgniter\HTTP\ResponseInterface;

class 
Filters extends BaseFilters
{
    
/**
     * Ensures that specific filters are on and enabled for the current request.
     *
     * Filters can have "arguments". This is done by placing a colon immediately
     * after the filter name, followed by a comma-separated list of arguments that
     * are passed to the filter when executed.
     *
     * @params array<string> $names filter_name or filter_name:arguments like 'role:admin,manager'
     *
     * @return \App\Filters\Filters
     */
    
public function enableFilters(array $namesstring $when 'before'): self
    
{
        foreach (
$names as $filter) {
            
$this->enableFilter($filter$when);
        }

        return 
$this;
    }

    
/**
     * Runs through all of the filters for the specified
     * uri and position.
     *
     * @param         string           $uri      URI path relative to baseURL
     * @phpstan-param 'before'|'after' $position
     *
     * @return RequestInterface|ResponseInterface|string|null
     *
     * @throws FilterException
     */
    
public function run(string $uristring $position 'before')
    {
        
$this->initialize(strtolower($uri));

        if (
$position === 'before') {
            return 
$this->runBefore($this->filtersClass[$position]);
        }

        
// After
        
return $this->runAfter($this->filtersClass[$position]);
    }

    private function 
enableFilter(string $namestring $when 'before'): void
    
{
        
// Get arguments and clean name
        
[$name$arguments]     = $this->getCleanName($name);

        if (! 
array_key_exists($name$this->arguments)) {
            
$this->arguments[$name] = [];
        }

        if (
$arguments !== []) {
            
$this->arguments[$name][] = $arguments;
        }

        if (
$this->arguments[$name] !== []) {
            
$this->arguments[$name] = array_unique($this->arguments[$name] ?? [], SORT_REGULAR);
        }
        if (
$this->arguments[$name] === []) {
            
$this->arguments[$name] = null;
        }

        if (
class_exists($name)) {
            
$this->config->aliases[$name] = $name;
        } elseif (! 
array_key_exists($name$this->config->aliases)) {
            throw 
FilterException::forNoAlias($name);
        }

        
$classNames = (array) $this->config->aliases[$name];

        foreach (
$classNames as $className) {
            
$this->argumentsClass[$className] = $this->arguments[$name] ?? null;
        }

        if (! isset(
$this->filters[$when][$name])) {
            
$this->filters[$when][]    = $name;
            
$this->filtersClass[$when] = array_merge($this->filtersClass[$when], $classNames);
        }
    }

    
/**
     * @return RequestInterface|ResponseInterface|string
     */
    
private function runBefore(array $filterClasses)
    {
        foreach (
$filterClasses as $className) {
            
$class = new $className();

            if (! 
$class instanceof FilterInterface) {
                throw 
FilterException::forIncorrectInterface($class::class);
            }

            if (
array_key_exists($className$this->argumentsClass) === true &&
                
$this->argumentsClass[$className] !== null &&
                
is_array($this->argumentsClass[$className][0])
            ) {
                foreach (
$this->argumentsClass[$className] as $classArgumentsRow) {
                    
$result $class->before(
                        
$this->request,
                        
$classArgumentsRow
                    
);

                    if (
$result instanceof RequestInterface) {
                        
$this->request $result;

                        continue;
                    }

                    
// If the response object was sent back,
                    // then send it and quit.
                    
if ($result instanceof ResponseInterface) {
                        
// short circuit - bypass any other filters
                        
return $result;
                    }

                    
// Ignore an empty result
                    
if (empty($result)) {
                        continue;
                    }

                    return 
$result;
                }
            } else {

                
$result $class->before(
                    
$this->request,
                    
$this->argumentsClass[$className] ?? null
                
);

                if (
$result instanceof RequestInterface) {
                    
$this->request $result;

                    continue;
                }

                
// If the response object was sent back,
                // then send it and quit.
                
if ($result instanceof ResponseInterface) {
                    
// short circuit - bypass any other filters
                    
return $result;
                }

                
// Ignore an empty result
                
if (empty($result)) {
                    continue;
                }

                return 
$result;
            }
        }

        return 
$this->request;
    }

    private function 
runAfter(array $filterClasses): ResponseInterface
    
{
        foreach (
$filterClasses as $className) {
            
$class = new $className();

            if (! 
$class instanceof FilterInterface) {
                throw 
FilterException::forIncorrectInterface($class::class);
            }

            if (
array_key_exists($className$this->argumentsClass) === true &&
                
$this->argumentsClass[$className] !== null &&
                
is_array($this->argumentsClass[$className][0])
            ) {
                foreach (
$this->argumentsClass[$className] as $classArgumentsRow) {
                    
$result $class->after(
                        
$this->request,
                        
$this->response,
                        
$classArgumentsRow
                    
);

                    if (
$result instanceof ResponseInterface) {
                        
$this->response $result;

                        continue;
                    }
                }
            } else {
                
$result $class->after(
                    
$this->request,
                    
$this->response,
                    
$this->argumentsClass[$className] ?? null
                
);

                if (
$result instanceof ResponseInterface) {
                    
$this->response $result;

                    continue;
                }
            }
        }

        return 
$this->response;
    }

    
/**
     * Get clean name and arguments
     *
     * @param string $name filter_name or filter_name:arguments like 'role:admin,manager'
     *
     * @return array{0: string, 1: list<string>} [name, arguments]
     */
    
private function getCleanName(string $name): array
    {
        
$arguments = [];

        if (
str_contains($name':')) {
            [
$name$arguments] = explode(':'$name);

            
$arguments explode(','$arguments);
            
array_walk($arguments, static function (&$item): void {
                
$item trim($item);
            });
        }

        return [
$name$arguments];
    }



and my Cofing\Services :
PHP Code:
use App\Filters\Filters;
use 
Config\Filters as FiltersConfig;
use 
Config\Services as AppServices;

class 
Services extends BaseService
{

    /**
    * Filters allow you to run tasks before and/or after a controller
    * is executed. During before filters, the request can be modified,
    * and actions taken based on the request, while after filters can
    * act on or modify the response itself before it is sent to the client.
    *
    * @return Filters
    */
    public static function filters(?FiltersConfig $config nullbool $getShared true)
    {
        if ($getShared) {
            return static::getSharedInstance('filters'$config);
        }

        $config ??= config(FiltersConfig::class);

        return new Filters($configAppServices::get('request'), AppServices::get('response'));
    }



This is probably not the best solution, but at the moment I haven't come up with a better oneSad
Reply




Theme © iAndrew 2016 - Forum software by © MyBB