Caching and password protected pages

Hi all,

I’ve been trying to get a simple password protection going on pages but having issues when turning on caching. I’m setting up a section for events where each event may have a unique password. I don’t want to redirect visitors to a global login page, so I’d like to include the password field on the page itself.

I’ve tried using a snippet that generates a form instead of the content, but the cache just serves the content when turned on. I’ve also tried setting up a controller (thinking it would run outside of the cache), but still also just serving the content.

Is there any way to include the password / authentication login for each page AND still use the cache without having to setup a unique user for each page?

Thanks!

The cache always stores the first HTML representation of the page that it gets and doesn’t run controllers and templates after that. So it is only useful for static content that doesn’t depend on any user state.

What you can do though is to exclude the dynamic pages from the cache:

c::set('cache.ignore', array('events/*'));

Yep, that’s what I’m doing now if I can’t find another way around it. The pages I’m trying to protect have a ton of images on each of them, so I would love to cache them so it doesn’t have to rebuild the page each time, but it just doesn’t seem like I can protect them and cache them.

Maybe JS is the only way around this…

Thanks for your response!

If you mean “thumbnail” with “image”: Thumbs are always cached on the server, so that shouldn’t be much of a difference between caching and not caching the HTML code.

I meant needing to generate the HTML code of all of the thumbs on the page. Each page has 75 - 200 images that are displayed. I imagine that it takes a bit of time to run through all of the image files to generate the page. I realize the thumbnail images themselves are cached, but was trying to get the page to cache as well.

Oh, that’s very much! Caching won’t really help much here as the generation isn’t really the main bottleneck. Think of all the HTML code that needs to be transferred for all of those images even if not all of them are being viewed by the user. You should probably consider pagination.

Yep, unfortunately its a lot of images. My assumption was that if it was cached at least I would get the structure of the page faster than waiting for PHP to generate it on the fly each time. The images are lazy loaded, so its quick once the page is loaded, but I’m trying to get the pages generated once and then protect them.

I’ll keep looking!

Thought I’d update for future reference that I found what I think will work great for this purpose.

Instead of building the logic of checking a simple password in the page itself, I’m using routes to check for a cookie containing the md5 encrypted password. If it’s not there, it redirects to a simple login page that passes all of the page information along. I can exclude the ‘login’ page from the cache without worrying about it since the page is tiny.

This lets me fully cache the pages with a lot of images but still use a unique password on each page.

The following code probably needs to be cleaned up and isn’t as efficient as it could be I’m sure, but thought it may be helpful for someone looking for the same thing. It lets logged in users bypass the password and I also have a ‘global’ password set in my site options that lets a visitor view all events without having a user account.

config.php (routes & cache):

c::set('routes', array(
    array(
        'pattern' => 'events/(:any)',
        'action'  => function($event) {
            $passEvent = page('events/'.$event)->password();
            $passGlobal = site()->globalPassword();
            $cookie = cookie::get('auth');

            if(site()->user() or $cookie == md5($passEvent) or $cookie == md5($passGlobal)) {
                return page('events/'.$event);
            } else {
                go('login/'.$event);
            }
       }
  ),
    array(
        'pattern' => 'login/(:any)',
        'action' => function($event) {
            if(!page('events/'.$event)) {
                go('/');
            }
           $passEvent = page('events/'.$event)->password();
           $passGlobal = site()->globalPassword();
           $cookie = cookie::get('auth');
           $data = array (
               'event' => $event
           );

           if(site()->user() or $cookie == md5($passEvent) or $cookie == md5($passGlobal)) {
               go('events/'.$event);
           } else {
               return array('login', $data);
           }
    },
    'method' => 'GET|POST'
  ),
  array(
    'pattern' => 'login',
    'action' => function() {
      go('/');
    }
  )
));

c::set('cache', true);
c::set('cache.ignore', array(
  'login'
));

controllers/login.php:

  <?php
  return function($site, $pages, $page, $data) {
      $event = $pages->find('events/'.$data['event']);

      $error = array(
        'status' => false,
        'message' => '<span class="error">'.$site->error().'</span>',
        'class' => 'class="input-error"'
      );

      $passEvent = $event->password();
      $passGlobal = $site->globalPassword();

      if(r::is('post')) {
         if(get('password') == $passEvent or get('password') == $passGlobal) {
           cookie::set('auth', md5(get('password')), 10800);
           go($event);
         } else {
         $error['status'] = true;
      }
  }

  return compact('event', 'error');

};

templates/login.php (just the form section):

    <form method="post" class="forms">
      <section>
        <label>Password <?php e($error['status'],$error['message']) ?></label>
        <input type="password" name="password" <?php e($error['status'], $error['class']) ?> autofocus>
        <?php e(!$event->hint()->empty(),'<div class="desc">Hint:&nbsp;&nbsp;'.$event->hint().'</div>') ?>
      </section>
      <section>
        <button upper type="submit">Enter</button>
      </section>
    </form>
1 Like