Automatically invalidating all sessions by updating the secret.

170 views
Skip to first unread message

David Oswald

unread,
Jun 14, 2012, 6:47:34 AM6/14/12
to mojol...@googlegroups.com
I have a full app that a company uses internally during its normal operating hours.

I would like to change the application secret automatically at a given time, after hours.

If it were enough to just change it every, say, 24 hours, I think I could use a timer like this:

    Mojo::IOLoop->timer( $HOURS * $SECS_PER_HOUR =>
        sub { $app->secret( gen_random_string() ) }
    );

But I don't want sessions getting invalidated possibly in the middle of business hours.  I want to change the application secret at a set time when nobody is in the office.

Any suggestions?

Dave

Ben van Staveren

unread,
Jun 14, 2012, 6:55:15 AM6/14/12
to mojol...@googlegroups.com
Invalidating the session won't stop people from accessing the app again after
office hours anyway, your best bet is to:


The original goes like this:

my $r = $self->routes;

What you want to do is this:

my $routes = $self->routes;
my $r = $routes->bridge('/')->to('auto#after_office_hours_checker');



Then in the Auto controller, you do this:

sub after_office_hours_checker {
my $self = shift;
my $now = DateTime->now();

# assume you have a user object somewhere, perhaps even with a time zone
$now->set_time_zone($self->current_user->{timezone});

# given $now, you can check for day of week in order to lock it down on
# weekends and/or public holidays, etc. sky is the limit here.

# but let's say between 6am and 6pm we shut it down, so between 6 and 6,
we allow access
return 1 if($now->hour >= 6 && $now->hour < 18);

$self->render(template => 'out_of_hours') and return 0;
}

And that solves your problem. Their sessions will still work, but the app
itself will just control the hours of access. As an added benefit you also get
timezone awareness.
> --
> You received this message because you are subscribed to the Google Groups
> "Mojolicious" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/mojolicious/-/Vr3DtNeqPVEJ.
> To post to this group, send email to mojol...@googlegroups.com.
> To unsubscribe from this group, send email to
> mojolicious...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/mojolicious?hl=en.

--
Ben van Staveren
phone: +62 81 70777529
email: benvans...@gmail.com

David Oswald

unread,
Jun 14, 2012, 7:04:52 AM6/14/12
to mojol...@googlegroups.com


On Thursday, June 14, 2012 3:55:15 AM UTC-7, Ben van Staveren wrote:
Invalidating the session won't stop people from accessing the app again after
office hours anyway, your best bet is to:


The original goes like this:

my $r = $self->routes;

What you want to do is this:

my $routes = $self->routes;
my $r = $routes->bridge('/')->to('auto#after_office_hours_checker');
 
That is a great suggestion that resolves one issue in a smarter way than invalidating the cookies.  Thanks.

There is a second issue that resetting the application secret helps with: Creating a moving target with respect to the MD5 hash that signs the cookies.  I still think I'd like to update the secret at a regular (and after-hours) interval.

As for your bridge suggestion, I've currently got this sort of a set of routes:

    $r->any('/login')->to( controller => 'user', action => 'login' )
      ->name('login');
    $r->any('/')->to( controller => 'user', action => 'welcome' )
      ->name('welcome');
    $r->any('/:controller/:action')->over( authenticated => 1 )->to(
        controller => $app->stash('controller'),
        action     => $app->stash('action')
    );

...letting URI's automatically bind to their controllers, but only once a user has passed the authentication (provided by Mojolicious::Plugin::Authentication, and Class::User::DBI).  Would my 'over' clause change the nature of your bridge? 

I thank you for your response.  Setting up an effective routing strategy seems to be one of the bigger parts of the learning curve here. :)

Ben van Staveren

unread,
Jun 14, 2012, 7:20:27 AM6/14/12
to mojol...@googlegroups.com
Hi David,

>
> The original goes like this:
>
> my $r = $self->routes;
>
> What you want to do is this:
>
> my $routes = $self->routes;
> my $r = $routes->bridge('/')->to('auto#after_office_hours_checker');
>
>
> That is a great suggestion that resolves one issue in a smarter way than
> invalidating the cookies. Thanks.
>
> There is a second issue that resetting the application secret helps with:
> Creating a moving target with respect to the MD5 hash that signs the
> cookies. I still think I'd like to update the secret at a regular (and
> after-hours) interval.
>

True, it does do that, although on the other hand you could do that with a
cron job too. If you use one of the Config plugins that ships with
Mojolicious, you can read the configuration, and use a configuration value to
set the secret. Then just set up a cron job that changes the secret to
something random every X days or however you want to set that up.


> As for your bridge suggestion, I've currently got this sort of a set of routes:
>
> $r->any('/login')->to( controller => 'user', action => 'login' )
> ->name('login');
> $r->any('/')->to( controller => 'user', action => 'welcome' )
> ->name('welcome');
> $r->any('/:controller/:action')->over( authenticated => 1 )->to(
> controller => $app->stash('controller'),
> action => $app->stash('action')
> );
>
> ...letting URI's automatically bind to their controllers, but only once a
> user has passed the authentication (provided by
> Mojolicious::Plugin::Authentication, and Class::User::DBI). Would my 'over'
> clause change the nature of your bridge?

Yes, you'd want to move that to the bridge as well, so your bridge code would do:

sub the_bridge {
my $self = shift;

if($self->is_user_authenticated) {
# code for the office hours, user setup, etc. etc. here
} else {
# emulate 'over' behaviour and...
$self->render(text => 'Not Found', status => 404);
}
}

The "over" authentication bit will effectively 404 anything for a
non-authenticated user so we can emulate that, or you can just pop up a page
telling people they need to go log in or otherwise contact someone or do
something. It's theoretically also possible to put the office hour check into
the load_user callback although you won't be able to give any specific
feedback as to why someone can't log in at that point.

>
> I thank you for your response. Setting up an effective routing strategy
> seems to be one of the bigger parts of the learning curve here. :)

Well, it's the same as with Perl, there's always more than one way to do it
which is why I put the routing condition thing into
Mojolicious::Plugin::Authentication. I'm a bridge guy, I dig them and use them
for a lot of things, but I know other people like using routing conditions
since it saves on a few calls here and there. However, the routing condition
(in my head anyway) was always meant as a quick shortcut to enable
authenticated access to bits of site that might fall outside a global "member"
area - or to select different outputs for the same page.

Side-tracking: Imagine this:

$r->route('/')->over(authenticated => 1)->to('foo#baz');
$r->route('/')->to('foo#bar');

If I'm not mistaken (going off the top of my head here) if the user isn't
authenticated the first route won't match, but the second one will. And vice
versa, if the user is authenticated, the first one will match and the 2nd one
won't match.

So, back on track, personally I think bridges are the way to go, because it's
relatively easy to chain them up, and it can be a real benefit if you want to
do things like, "/user/<user_id>", you can bridge /user to a sub that finds
out a) if that user exists, and b) if the current user is allowed to see it,
then add a '/' route to the bridge to render the content you need.

Take my ramblings with a grain of salt though, this way works for me since it
was similar to what Catalyst's chained routes do and that's where I came from,
so there might be a much better/cleaner/shinier way of doing it, but this
works for me, as always though: YMMV :)

David Oswald

unread,
Jun 14, 2012, 11:48:26 AM6/14/12
to mojol...@googlegroups.com
Thanks again.  A bridge does seem like a more powerful suggestion.  I'll try that tack, and reserve the possibility of additional followup question(s). :)

My next step is to also layer in Mojolicious::Plugin::Authorization, as my application's users have various roles that will dictate what they have access to, and how things they do have access to render.

By the way, I created Class::User::DBI (on CPAN) as a model for the user.  It features user authentication via salted (random, per user) SHA2-512 passphrases, IP whitelisting, and user roles (ie, a user is a this, and a user is a that).  Users can have multiple roles.  I guess they're sort of analogous to groups.  It is made to accept a DBIx::Connector object for the database work.  Salts are 512 bits, generated with a cryptographically suitable random number generator.  I made it with Mojolicious::Plugin::Authentication's validate_user and load_user callbacks in mind, as well as with the idea of meshing with Mojolicious::Plugin::Authorization.

It's still in 'beta' stage; some of the design decisions could change as I get a better feel for how it fits into a M::P::Auth* setup.  But it should stabilize within a week or two.

 

Ben van Staveren

unread,
Jun 14, 2012, 11:54:23 AM6/14/12
to mojol...@googlegroups.com
That's awesome :D When it stabilizes out let me know, I'll add it to the 'see
also' bits in the POD, especially useful if you can provide a simple example
that uses M::P::Auth and your module :D
Reply all
Reply to author
Forward
0 new messages