package OAuth::Lite::ServerUtil;
use strict;
use warnings;
use base 'Class::ErrorHandler';
use OAuth::Lite::Util qw(
decode_param
create_signature_base_string
);
use OAuth::Lite::Problems qw(:all);
use List::MoreUtils qw(any none);
use UNIVERSAL::require;
use Carp ();
=head1 NAME
OAuth::Lite::ServerUtil - server side utility
=head1 SYNOPSIS
my $util = OAuth::Lite::ServerUtil->new;
$util->support_signature_method('HMAC-SHA1');
$util->allow_extra_params(qw/file size/);
unless ($util->validate_params($oauth_params)) {
return $server->error(400, $util->errstr);
}
$util->verify_signature(
method => $r->method,
params => $oauth_params,
url => $request_uri,
consumer_secret => $consumer->secret,
) or return $server->error(401, $util->errstr);
And see L<OAuth::Lite::Server::mod_perl2> source code.
=head1 DESCRIPTION
This module helps you to implement application that acts as OAuth Service Provider.
=head1 METHODS
=head2 new
Constructor
my $util = OAuth::Lite::ServerUtil->new;
Set strict true by default, and it judge unsupported param as invalid when validating params.
You can build ServerUtil as non-strict mode, then it accepts unsupported parameters.
my $util = OAuth::Lite::ServerUtil->new( strict => 0 );
=cut
sub new {
my $class = shift;
my %args = @_;
my $strict = exists $args{strict} ? $args{strict} : 1;
my $self = bless {
supported_signature_methods => {},
allowed_extra_params => [],
strict => $strict,
}, $class;
$self;
}
=head2 allow_extra_param($param_name);
When you validate oauth parameters, if an extra parameter
is included, the validation will fail.
my $params = {
oauth_version => '1.0',
...and other oauth parameters,
};
$params->{file} = "foo.jpg";
# fail!
unless ($util->validate_params($params)) {
$your_app->error( $util->errstr );
}
So, if you want allow extra parameter, use this method.
$util->allow_extra_param('file');
my $params = {
oauth_version => '1.0',
...and other oauth parameters,
};
$params->{file} = "foo.jpg";
# Now this results successfully.
unless ($util->validate_params($params)) {
$your_app->error( $util->errstr );
}
=cut
sub allow_extra_param {
my ($self, $param) = @_;
push @{ $self->{allowed_extra_params} }, $param;
}
=head2 allow_extra_params($param1, $param2, ...)
You can allow multiple extra parameters at once.
$util->allow_extra_params(qw/file size/);
=cut
sub allow_extra_params {
my $self = shift;
$self->allow_extra_param($_) for @_;
}
=head2 support_signature_method($method_class_name);
Set the signature method class's name that your server can supports.
$util->support_signature_method('HMAC_SHA1');
This method requires indicated signature method class inside.
So, you should install OAuth::Lite::SignatureMethod::$method_class_name beforehand.
For example, when your choise is HMAC_SHA1, you need to have
OAuth::Lite::SignatureMethod::HMAC_SHA1 installed in your server.
=cut
sub support_signature_method {
my ($self, $method_class) = @_;
$method_class =~ s/-/_/g;
my $class = join('::', 'OAuth::Lite::SignatureMethod', $method_class);
$class->require or Carp::croak sprintf(q{Couldn't require class, %s}, $class);
$self->{supported_signature_methods}{$class->method_name} = $class;
}
=head2 support_signature_methods($method1, $method2, ...);
You can set multiple signature method class at once.
$util->support_signature_methods(qw/HMAC_SHA1 RSA_SHA1/);
=cut
sub support_signature_methods {
my $self = shift;
$self->support_signature_method($_) for @_;
}
=head2 validate_params($params, [$check_token]);
Check if the request includes all required params
and doesn't include unsupported params.
It doesn't check unsupported params when working on strict mode.
unless ($util->validate_params($params)) {
$your_app->error( $util->errstr );
}
When the request is to exchange tokens or to access to protected resources,
pass 1 for second argument. This flag indicates that oauth_token param is needed.
unless ($util->validate_params($params, 1)) {
$your_app->error( $util->errstr );
}
=cut
sub validate_params {
my ($self, $origin_params, $check_token) = @_;
my $params = {%$origin_params}; #copy
delete $params->{oauth_consumer_key} or return $self->error(PARAMETER_ABSENT);
delete $params->{oauth_nonce} or return $self->error(PARAMETER_ABSENT);
delete $params->{oauth_timestamp} or return $self->error(PARAMETER_ABSENT);
delete $params->{oauth_signature_method} or return $self->error(PARAMETER_ABSENT);
delete $params->{oauth_signature} or return $self->error(PARAMETER_ABSENT);
delete $params->{oauth_version};
if ($check_token) {
delete $params->{oauth_token} or return $self->error(PARAMETER_ABSENT);
}
if ( $self->{strict} ) {
my @extra_params = keys %$params;
my @allowed = @{ $self->{allowed_extra_params} };
for my $extra ( @extra_params ) {
if (none { $extra eq $_ } @allowed) {
return $self->error(PARAMETER_REJECTED);
}
}
}
1;
}
=head2 validate_signature_method($method_name)
unless ($util->validate_signature_method('HMAC-SHA1')) {
$your_app->error(qq/Unsupported signature method/);
...
}
=cut
sub validate_signature_method {
my ($self, $method) = @_;
return unless $method;
any { $_ eq $method } keys %{$self->{supported_signature_methods}};
}
=head2 verify_signature(%args)
=over 4
=item method - HTTP request method
=item params - parameters hash reference
=item url - requested uri
=item consumer_secret - consumer secret value(optional)
=item token_secret - token secret value(optional)
=back
# you can omit consumer_secret and token_secret if you don't need them.
$util->verify_signature(
method => $r->method,
params => $params,
url => $requested_uri,
consumer_secret => $consumer_secret,
token_secret => $token_secret,
) or die $utl->errstr;
=cut
sub verify_signature {
my ($self, %args) = @_;
my $http_method = $args{method} or Carp::croak(qq/method not found/);
my $url = $args{url} or Carp::croak(qq/url not found/);
my $params = $args{params} or Carp::croak(qq/params not found/);
my $consumer_secret = $args{consumer_secret} || '';
my $token_secret = $args{token_secret} || '';
my $signature_method = $params->{oauth_signature_method};
my $signature = $params->{oauth_signature};
my $base_string = create_signature_base_string($http_method, $url, $params);
unless ($self->validate_signature_method($signature_method)) {
return $self->error(SIGNATURE_METHOD_REJECTED);
}
my $method_class = $self->{supported_signature_methods}{$signature_method};
my $method = $method_class->new(
consumer_secret => $consumer_secret,
token_secret => $token_secret,
);
unless ($method->verify($base_string, $signature)) {
return $self->error(SIGNATURE_INVALID);
}
1;
}
=head1 SEE ALSO
L<OAuth::Lite::Server::mod_perl2>
=head1 AUTHOR
Lyo Kato, C<lyo.kato _at_ gmail.com>
=head1 COPYRIGHT AND LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
=cut
1;