Protecting files behind a firewall on multiple folders

Hi!

I’m working on a clients’ area and i follow this recipe Restricting access to your site | Kirby CMS

it works perfectly!

My idea would be to improve the plugin by extending the firewall to all the files contained in the children of the clients’ area page.

my current files-firewall/index.php plugin:

Kirby::plugin('cookbook/files-firewall', [
  'routes'       => [
    [
     'pattern' => 'clients-area/(:any)',
     'action'  => function ($filename) {
       if (kirby()->user() && 
         ($page = page('clients-area')) && 
         $file = $page->files()->findBy('filename', $filename)) {
         return $file->download();
       }
       return site()->errorPage();
     }
    ]
  ],
  
  ...
  ...

but i have no idea how to apply this to all subfolders, for example:
clients-area/child1
clients-area/child2
etc.

my idea, definitely wrong:

Kirby::plugin('cookbook/files-firewall', [
  'routes'       => [
    [
      'pattern' => 'clients-area/(:any)',
      'action'  => function ($filename) {
        if (kirby()->user() && 
          ($page = page('clients-area')) && 
          $file = $page->files()->findBy('filename', $filename)) {
          return $file->download();
        }
        return site()->errorPage();
      }
    ],
    [
      'pattern' => 'clients-area/(:any)/(:any)',
      'action'  => function ($filename2) {
        if (kirby()->user() && 
          ($page2 = isChildOf('clients-area')) && 
          $file2 = $page2->files()->findBy('filename', $filename2)) {
          return $file2->download();
        }
        return site()->errorPage();
      }
    ],
  ],
  
  ...
  ...

I understand that the problem is in ‘pattern’, but I am not very good with this.
do you have any suggestions for me? Thank you so much!

Since you want to get at the subpage here, you need to path to the subpage

     'pattern' => 'clients-area/(:any)/(:any)',
     'action'  => function ($slug, $filename2) {
            if (kirby()->user() && 
          ($page2 = page('clients-area/' . $slug)) && 
          $file2 = $page2->files()->findBy('filename', $filename2)) {
          return $file2->download();
        }
        return site()->errorPage();
     }

I think you missed a round bracket after $slug, now it works but in the child page return error page 404.

Which child page? Do you mean that a page clients-area/something can have children as well?

no, clients-area has files and children. The child page of clients-area has only files.

for example
clients-area has files like document.pdf
clients-area/document.pdf
works

clients-area child has files like document.pdf
clients-area/child1/anotherdocument.pdf
now, all children return 404

That’s probably because of this route, because you send them to the error page. Instead you should check if $filename is a page, if so, return the page. Then check if $filename is a file and return the file for download. As the last step, return the error page.

I have made several attempts but I have not found a working solution. The one that comes closest is this, which works and protects all the files contained in the children but does not protect in reserved-area files.

‘routes’ => [

[
  'pattern' => 'clients-area/(:any)/(:any)',
  'action'  => function ($slug, $filename) {
    if (kirby()->user() && 
      ($page = page('clients-area/' . $slug)) && 
      $file = $page->files()->findBy('filename', $filename)) {
      return $file->download();
    }
  
    return site()->errorPage();
  }
],

],

based on your suggestions, I had thought something like this, but doesn’t work

[
‘pattern’ => ‘clients-area/(:any)/(:any)’,
‘action’ => function ($slug, $filename) {

    if (kirby()->user() && $path = page('clients-area/' . $filename)) {
      return $path;
    }

    if (kirby()->user() && 
      ($page = page('clients-area')) && 
      $file = $page->files()->findBy('filename', $filename)) {
      return $file->download();
    }
  
    return site()->errorPage();
  }
],

As I wrote above, I think this route needs a change, not the one with the two placeholders.

 [
      'pattern' => 'clients-area/(:any)',
      'action'  => function ($filename) {
        if ($page = page('clients-area/' . $filename)) { // maybe check for logged-in user as well if this is protected
          return $page;
        }
        if (kirby()->user() && 
          ($page = page('clients-area')) && 
          $file = $page->files()->findBy('filename', $filename)) {
          return $file->download();
        }
        return site()->errorPage();
      }
    ],
  'routes'       => [
    // clients-area page
    [
      'pattern' => 'clients-area/(:any)',
      'action'  => function ($filename) {
        if 
          (kirby()->user() && // not necessary
          ($page = page('clients-area/' . $filename))) {
         
          return $page;
        }
        if (kirby()->user() && 
          ($page = page('clients-area')) && 
          $file = $page->files()->findBy('filename', $filename)) {
          return $file->download();
        }
        return site()->errorPage();
      }
    ],

    // all the clients-area children
   [
      'pattern' => 'clients-area/(:any)/(:any)',
      'action'  => function ($slug, $filename) {
        if (kirby()->user() && 
          ($page = page('clients-area/' . $slug)) && 
          $file = $page->files()->findBy('filename', $filename)) {
          return $file->download();
        }
        return site()->errorPage();
      }
    ],
  ],

thank you for your time,
this works perfectly!