Mojolicious-Plugin-FormValidatorLazy
view release on metacpan or search on metacpan
lib/Mojolicious/Plugin/FormValidatorLazy.pm view on Meta::CPAN
package Mojolicious::Plugin::FormValidatorLazy;
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Plugin';
our $VERSION = '0.03';
use Data::Dumper;
use Mojo::JSON qw(decode_json encode_json);
use Mojo::Util qw{encode decode xml_escape hmac_sha1_sum secure_compare
b64_decode b64_encode};
use HTML::ValidationRules::Legacy qw{validate extract};
our $TERM_ACTION = 0;
our $TERM_SCHEMA = 1;
### ---
### register
### ---
sub register {
my ($self, $app, $opt) = @_;
my $schema_key = $opt->{namespace}. "-schema";
my $sess_key = $opt->{namespace}. '-sessid';
my $actions = ref $opt->{action} ? $opt->{action} : [$opt->{action}];
$app->hook(before_dispatch => sub {
my $c = shift;
my $req = $c->req;
if ($req->method eq 'POST' && grep {$_ eq $req->url->path} @$actions) {
my $wrapper = deserialize(unsign(
$req->param($schema_key),
($c->session($sess_key) || ''). $app->secrets->[0]
));
$req->params->remove($schema_key);
if (!$wrapper) {
return $opt->{blackhole}->($c,
'Form schema is missing, possible hacking attempt');
}
if ($req->url->path ne $wrapper->{$TERM_ACTION}) {
return $opt->{blackhole}->($c,
'Action attribute has been tampered');
}
if (my $err = validate($wrapper->{$TERM_SCHEMA}, $req->params)) {
return $opt->{blackhole}->($c, $err);
}
}
});
$app->hook(after_dispatch => sub {
my $c = shift;
if ($c->res->headers->content_type =~ qr{^text/html} &&
$c->res->body =~ qr{<form\b}i) {
my $sessid = $c->session($sess_key);
if (! $sessid) {
$sessid = hmac_sha1_sum(time(). {}. rand(), $$);
$c->session($sess_key => $sessid);
}
$c->res->body(inject(
$c->res->body,
$actions,
$schema_key,
$sessid. $app->secrets->[0],
$c->res->content->charset)
);
}
});
}
sub inject {
my ($html, $actions, $token_key, $secret, $charset) = @_;
if (! ref $html) {
$html = Mojo::DOM->new($charset ? decode($charset, $html) : $html);
}
$html->find(qq{form[action][method="post"]})->each(sub {
my $form = shift;
my $action = $form->attr('action');
return if (! grep {$_ eq $action} @$actions);
my $wrapper = sign(serialize({
$TERM_ACTION => $action,
$TERM_SCHEMA => extract($form, $charset),
}), $secret);
$form->append_content(sprintf(<<"EOF", $token_key, xml_escape $wrapper));
<div style="display:none">
<input type="hidden" name="%s" value="%s">
</div>
EOF
});
return encode($charset, $html);
}
sub serialize {
return b64_encode(encode_json(shift // return), '');
}
( run in 0.421 second using v1.01-cache-2.11-cpan-39bf76dae61 )