[Jifty-commit] r4258 - in jifty/trunk: . lib/Jifty/Plugin/OAuth
lib/Jifty/Plugin/OAuth/Model
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Thu Oct 18 21:52:05 EDT 2007
Author: sartak
Date: Thu Oct 18 21:52:03 2007
New Revision: 4258
Added:
jifty/trunk/t/TestApp-Plugin-OAuth/t/id_rsa
jifty/trunk/t/TestApp-Plugin-OAuth/t/id_rsa.pub
Removed:
jifty/trunk/t/TestApp-Plugin-OAuth/t/02-request-token.t-config.yml
Modified:
jifty/trunk/ (props changed)
jifty/trunk/Makefile.PL
jifty/trunk/lib/Jifty/Plugin/OAuth/Dispatcher.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/View.pm
jifty/trunk/t/TestApp-Plugin-OAuth/t/02-request-token.t
Log:
r43849 at onn: sartak | 2007-10-18 21:51:56 -0400
Expand the tests, now they actually test something real!
So right now the OAuth plugin is rejecting a few kinds of bad requests (e.g. unknown consumer) while accepting a good request. Now to make more requests of each type :)
Modified: jifty/trunk/Makefile.PL
==============================================================================
--- jifty/trunk/Makefile.PL (original)
+++ jifty/trunk/Makefile.PL Thu Oct 18 21:52:03 2007
@@ -170,6 +170,7 @@
recommends('Net::OAuth::Request'),
recommends('Crypt::OpenSSL::RSA'),
recommends('Digest::HMAC_SHA1'),
+ recommends('MIME::Base64'),
],
);
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/Dispatcher.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/Dispatcher.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Dispatcher.pm Thu Oct 18 21:52:03 2007
@@ -15,7 +15,7 @@
# helper function to abort with a debug message
sub abortmsg {
my ($code, $msg) = @_;
- Jifty->log->error($msg) if defined($msg);
+ Jifty->log->debug($msg) if defined($msg);
abort($code || 400);
}
@@ -24,15 +24,16 @@
my @params = qw/consumer_key signature_method signature
timestamp nonce version/;
- my %oauth_params = get_parameters(@params);
- validate_signature_method($oauth_params{signature_method});
- my $consumer = get_consumer($oauth_params{consumer_key});
+ my %oauth_params = get_parameters(@params);
+ my $consumer = get_consumer($oauth_params{consumer_key});
+ my $signature_key = get_signature_key($oauth_params{signature_method}, $consumer);
# Net::OAuth::Request will die hard if it doesn't get everything it wants
my $request = eval { Net::OAuth::RequestTokenRequest->new(
request_url => Jifty->web->url(path => '/oauth/request_token'),
request_method => Jifty->handler->apache->method(),
consumer_secret => $consumer->secret,
+ signature_key => $signature_key,
map { $_ => $oauth_params{$_} } @params
) };
@@ -56,9 +57,10 @@
};
abortmsg(401, "Unable to create a Request Token: " . $@ || $msg)
- if $@ || !defined($token) || !$ok;
+ if $@ || !$ok;
# XXX: actually send the token
+ abort(200);
}
# the user is authorizing (or denying) a consumer's request token
@@ -87,9 +89,9 @@
my @params = qw/consumer_key signature_method signature
timestamp nonce token version/;
- my %oauth_params = get_parameters(@params);
- validate_signature_method($oauth_params{signature_method});
- my $consumer = get_consumer($oauth_params{consumer_key});
+ my %oauth_params = get_parameters(@params);
+ my $consumer = get_consumer($oauth_params{consumer_key});
+ my $signature_key = get_signature_key($oauth_params{signature_method}, $consumer);
# is the request token they're using still valid?
my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
@@ -106,6 +108,7 @@
request_method => Jifty->handler->apache->method(),
consumer_secret => $consumer->secret,
token_secret => $request_token->secret,
+ signature_key => $signature_key,
map { $_ => $oauth_params{$_} } @params
) };
@@ -138,11 +141,29 @@
return $consumer;
}
-my %valid_signature_methods = map { $_ => 1 } qw/PLAINTEXT HMAC-SHA1 RSA-SHA1/;
-sub validate_signature_method {
- my $method = shift;
- return if $valid_signature_methods{$method};
- abortmsg(400, "Unsupported signature method requested: $method");
+{
+ my %valid_signature_methods = map { $_ => 1 }
+ qw/PLAINTEXT HMAC-SHA1 RSA-SHA1/;
+ my %key_field = ('RSA-SHA1' => 'rsa_key');
+
+ sub get_signature_key {
+ my ($method, $consumer) = @_;
+ if (!$valid_signature_methods{$method}) {
+ abortmsg(400, "Unsupported signature method requested: $method");
+ }
+
+ my $field = $key_field{$method};
+
+ # this MUST return undef if the signature method requires no prior key
+ return undef if !defined($field);
+
+ my $key = $consumer->$field;
+
+ abortmsg(400, "Consumer does not have necessary field $field required for signature method $method")
+ unless defined $key;
+
+ return $key;
+ }
}
sub get_parameters {
@@ -152,8 +173,10 @@
# XXX: Check WWW-Authenticate header
my %params = Jifty->handler->apache->params();
- use Data::Dumper; warn Dumper \%params;
- @p{@_} = @params{map {"oauth_$_"} @_};
+ for (@_) {
+ $p{$_} = $params{"oauth_$_"}
+ if !defined $p{$_};
+ }
$p{version} ||= '1.0';
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm Thu Oct 18 21:52:03 2007
@@ -29,6 +29,10 @@
column url =>
type is 'varchar';
+ column rsa_key =>
+ type is 'varchar',
+ hints are 'This is only necessary if you want to support RSA-SHA1 signatures';
+
};
1;
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm Thu Oct 18 21:52:03 2007
@@ -2,6 +2,7 @@
package Jifty::Plugin::OAuth::Token;
use strict;
use warnings;
+use Scalar::Util 'blessed';
# this just provides some helper methods for both token classes to use
@@ -15,7 +16,13 @@
# check if we're seeing a replay attack
my $token = $self->new(current_user => Jifty::CurrentUser->superuser);
$token->load_by_cols(nonce => $attr->{nonce}, time_stamp => $attr->{time_stamp});
- return if $token->id;
+ if ($token->id) {
+ use Data::Dumper;
+ Jifty->log->warn(Dumper $token);
+ Jifty->log->warn(Dumper $self);
+ Jifty->log->warn("Duplicate nonce ($attr->{nonce}) and timestamp ($attr->{time_stamp}) from " . $token->id . " (self is " . $self->id . "). Possibly a replay attack.");
+ return;
+ }
# attempt 20 times to create a unique token string
for (1..20) {
@@ -25,7 +32,10 @@
last if !$token->id;
delete $attr->{token};
}
- return if !defined($attr->{token});
+ if (!defined $attr->{token}) {
+ Jifty->log->warn("Failed 20 times to create a unique token. Giving up.");
+ return;
+ }
# generate a secret. need not be unique, just hard to guess
$attr->{secret} = generate_token();
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/View.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/View.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/View.pm Thu Oct 18 21:52:03 2007
@@ -14,7 +14,7 @@
}
p {
- "This application supports OAuth. If you'd like to access the private resources of users of this site, you must first establish a Consumer Key and Consumer Secret with us. You can do so by contacting " . (Jifty->config->framework('AdminEmail')||'us') . ".";
+ "This application supports OAuth. If you'd like to access the private resources of users of this site, you must first establish a Consumer Key, Consumer Secret, and, if applicable, RSA public key with us. You can do so by contacting " . (Jifty->config->framework('AdminEmail')||'us') . ".";
}
p {
Modified: jifty/trunk/t/TestApp-Plugin-OAuth/t/02-request-token.t
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-OAuth/t/02-request-token.t (original)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/t/02-request-token.t Thu Oct 18 21:52:03 2007
@@ -5,86 +5,216 @@
use lib 't/lib';
use Jifty::SubTest;
-use Jifty::Test tests => 9;
+use Jifty::Test tests => 16;
use Jifty::Test::WWW::Mechanize;
+use MIME::Base64;
+use Crypt::OpenSSL::RSA;
+use Digest::HMAC_SHA1 'hmac_sha1';
+
my $server = Jifty::Test->make_server;
isa_ok($server, 'Jifty::Server');
my $URL = $server->started_ok;
my $mech = Jifty::Test::WWW::Mechanize->new();
-my $url = $URL . '/oauth/request_token';
+our $url = $URL . '/oauth/request_token';
# helper functions {{{
-sub response_is ($%;$) {
- my ($code, $params, $testname) = @_;
-
- my $method = (delete $params->{method}) || 'POST';
- my $token_secret = (delete $params->{token_secret}) || '';
- my $consumer_secret = delete $params->{consumer_secret}
+my $timestamp = 0;
+sub response_is {
+ ++$timestamp;
+
+ my %params = (
+ oauth_timestamp => $timestamp,
+ oauth_nonce => scalar(reverse $timestamp),
+ oauth_signature_method => 'HMAC-SHA1',
+ oauth_version => '1.0',
+
+ code => 400,
+ testname => "",
+ method => 'POST',
+ token_secret => '',
+ @_,
+ );
+
+ my $code = delete $params{code};
+ my $testname = delete $params{testname} || "Response was $code";
+ my $method = delete $params{method};
+ my $token_secret = delete $params{token_secret};
+ my $consumer_secret = delete $params{consumer_secret}
or die "consumer_secret not passed to response_is!";
- $params->{oauth_signature} ||= sign($params, $method, $token_secret, $consumer_secret);
+ $params{oauth_signature} ||= sign($method, $token_secret, $consumer_secret, %params);
my $r;
if ($method eq 'POST') {
- $r = $mech->post($url);
+ $r = $mech->post($url, [%params]);
}
else {
- $r = $mech->get($url);
+ my $query = join '&',
+ map { "$_=" . Jifty->web->escape_uri($params{$_}||'') }
+ keys %params;
+ $r = $mech->get("$url?$query");
}
- is($r->code, $code, $testname || "Request got $code");
+ is($r->code, $code, $testname);
}
sub sign {
- my ($params, $method, $token_secret, $consumer_secret) = @_;
+ my ($method, $token_secret, $consumer_secret, %params) = @_;
+
+ local $url = delete $params{url} || $url;
+
+ my $key = delete $params{signature_key};
+
+ if ($params{oauth_signature_method} eq 'PLAINTEXT') {
+ my $signature = join '&',
+ map { Jifty->web->escape_uri($_||'') }
+ $consumer_secret,
+ $token_secret;
+ return $signature;
+ }
my $normalized_request_parameters
= join '&',
- map { "$_=" . Jifty->web->escape_uri($params->{$_}||'') }
- sort keys %$params;
+ map { "$_=" . Jifty->web->escape_uri($params{$_}||'') }
+ sort keys %params;
my $signature_base_string
= join '&',
- map { Jifty->web->escape_uri($params->{$_}||'') }
+ map { Jifty->web->escape_uri($_||'') }
uc($method),
$url,
$normalized_request_parameters,
$consumer_secret,
$token_secret;
- # XXX: do some signing based on $params->{signature_method}!
- return '!!';
+ my $signature;
+
+ if ($params{oauth_signature_method} eq 'RSA-SHA1') {
+ my $pubkey = Crypt::OpenSSL::RSA->new_private_key($key);
+ $signature = encode_base64($pubkey->sign($signature_base_string));
+ }
+ elsif ($params{oauth_signature_method} eq 'HMAC-SHA1') {
+ my $key = join '&',
+ map { Jifty->web->escape_uri($_||'') }
+ $consumer_secret,
+ $token_secret;
+ my $hmac = Digest::HMAC_SHA1->new($key);
+ $hmac->add($signature_base_string);
+ $signature = $hmac->b64digest;
+ }
+
+ return ($signature, $signature_base_string, $normalized_request_parameters)
+ if wantarray;
+ return $signature;
+
}
# }}}
+# load the RSA keys {{{
+sub slurp {
+ my $file = shift;
+ local $/;
+ local @ARGV = $file;
+ my $contents = scalar <>
+ or die "Unable to slurp $file";
+ return $contents;
+}
+my $pubkey = slurp 't/id_rsa.pub';
+my $seckey = slurp 't/id_rsa';
+# }}}
+# testing the local sign function {{{
+# PLAINTEXT {{{
+is(sign('POST', 'jjd999tj88uiths3', 'djr9rjt0jd78jf88',
+ oauth_signature_method => 'PLAINTEXT'),
+ 'djr9rjt0jd78jf88&jjd999tj88uiths3', 'PLAINTEXT example 1 works');
+is(sign('POST', 'jjd99$tj88uiths3', 'djr9rjt0jd78jf88',
+ oauth_signature_method => 'PLAINTEXT'),
+ 'djr9rjt0jd78jf88&jjd99%24tj88uiths3', 'PLAINTEXT example 2 works');
+is(sign('POST', undef, 'djr9rjt0jd78jf88',
+ oauth_signature_method => 'PLAINTEXT'),
+ 'djr9rjt0jd78jf88&', 'PLAINTEXT example 2 works');
+# }}}
+# HMAC-SHA1 {{{
+my ($sig, $sbs, $nrp) = sign(
+ 'GET',
+ 'pfkkdhi9sl3r4s00',
+ 'kd94hf93k423kf44',
+ url => 'http://photos.example.net/photos',
+ oauth_consumer_key => 'dpf43f3p2l4k3l03',
+ oauth_signature_method => 'HMAC-SHA1',
+ oauth_timestamp => '1191242096',
+ oauth_nonce => 'kllo9940pd9333jh',
+ oauth_token => 'nnch734d00sl2jdk',
+ file => 'vacation.jpg',
+ size => 'original',
+ oauth_version => '1.0');
+
+is($nrp, 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original', 'HMAC-SHA1 normalized request paramaters correct');
+is($sbs, 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal&kd94hf93k423kf44&pfkkdhi9sl3r4s00', 'HMAC-SHA1 signature-base-string correct');
+is($sig, 'Gcg/323lvAsQ707p+y41y14qWfY', 'HMAC-SHA1 signature correct');
+# }}}
+# RSA-SHA1 {{{
+($sig, $sbs, $nrp) = sign(
+ 'GET',
+ 'pfkkdhi9sl3r4s00',
+ 'kd94hf93k423kf44',
+ url => 'http://photos.example.net/photos',
+ signature_key => $seckey,
+ oauth_consumer_key => 'dpf43f3p2l4k3l03',
+ oauth_signature_method => 'RSA-SHA1',
+ oauth_timestamp => '1191242096',
+ oauth_nonce => 'kllo9940pd9333jh',
+ oauth_token => 'nnch734d00sl2jdk',
+ file => 'vacation.jpg',
+ size => 'original',
+ oauth_version => '1.0');
+
+is($nrp, 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=RSA-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original', 'RSA-SHA1 normalized request paramaters correct');
+is($sbs, 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal&kd94hf93k423kf44&pfkkdhi9sl3r4s00', 'RSA-SHA1 signature-base-string correct');
+$sig =~ s/\s+//g;
+is($sig, 'oSjbUzMjD4E+LeHMaYzYx1KyULDwuR6V9oeNgTLoO9m90iJh4d01J/8SzvHKT8N0y2vs1o8s72z19Eicj6l+mEmH5Rp0cwWOE9UdvC+JdFSIA1bmlwVPCFL7jDQqRSBJsXEiT44T5j9P+Dh5Z5WUjEgCExQyNP38Z3nMnYYOCRM=', 'RSA-SHA1 signature correct');
+# }}}
+# }}}
# get a request token as a known consumer {{{
my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
my ($ok, $msg) = $consumer->create(
consumer_key => 'foo',
- secret => 'bar',
- name => 'FooBar industries',
- url => 'http://foo.bar.example.com',
+ secret => 'bar',
+ name => 'FooBar industries',
+ url => 'http://foo.bar.example.com',
+ rsa_key => $pubkey,
);
ok($ok, $msg);
-
+response_is(
+ code => 200,
+ consumer_secret => 'bar',
+ oauth_consumer_key => 'foo',
+ oauth_signature_method => 'PLAINTEXT',
+);
# }}}
-
# unknown consumer {{{
-response_is 401, {
+response_is(
+ code => 401,
consumer_secret => 'zzz',
oauth_consumer_key => 'whoami',
- oauth_signature_method => 'RSA-SHA1',
- oauth_timestamp => 100,
- oauth_nonce => 'haa haa',
- oauth_version => '1.0',
-};
+);
# }}}
# wrong secret {{{
+response_is (
+ code => 401,
+ consumer_secret => 'not bar!',
+ oauth_consumer_key => 'foo',
+);
# }}}
# wrong signature {{{
+response_is(
+ code => 401,
+ consumer_secret => 'bar',
+ oauth_consumer_key => 'foo',
+);
# }}}
# duplicate timestamp and nonce {{{
# }}}
Added: jifty/trunk/t/TestApp-Plugin-OAuth/t/id_rsa
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/t/id_rsa Thu Oct 18 21:52:03 2007
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC1ekM402pEiZ6MyaG0RzDNrw0digCV0e45mCgaQs2F0q4v2O8C
+xjl9pbsuf2qz1jHKGdJIXuhaW1XRqCOE2ZHc/n/+s2s8TUIcBve3B2glKxJhgyV8
+nDpZkjOEctef8uFPU3Alfm382kj0THcXdgsQ+jreLJ1VCS5xNcU6VpXa4QIDAQAB
+AoGAOHsl4tDB2TTvuKekgURK5ykdLt1dk0N0Hk7B5HJ4HrdUaSXeNYHWMMnc+PrF
+DdWTR3BD5yxKqpyUmBz5eQZyA8vVKzEVmYCkA+EO6TQeo6xveH/9xaFbTtXpwtvS
+N9m3kwEfmfudJvQRFb3q79I+17/g8rWbZlDYK7CKyfVs17UCQQDxdMOz/Q7xpP+f
+sXTHxvhtw4FFvAZEOEQA1a+uHGSmz+Vq0SIOpwZwri4aFG1YVUS2FUGHuhSpsuUJ
+Pg3kY1N3AkEAwGiiObgemFQLvCVigP8YcZyt98a+vE2Joq3iJyd/4DnEqvN98WNm
+5zaSDEXAJzC1ZuqnMUFVbiYBt2W4InBqZwJAEpgyZg8L8pIJWYv5+VSaVyGiN/OV
+6/UFT6clI1xuZ+ZEvagjXkuAlHbld/6wuQfABeG3LTOoWbU8LC0KNtdrWwJAF0gR
+6R4IRbJVwSxc4PL9CDJHMqYPykUvlEmqBcbXyE/1JiJUaPL4Lp4Byg5ek99m888M
+7/7R0YQzzPc38qLbnQJBAMbs/L0td6AponlpHCLmhHd7dka6GNIdyaALLNSVefD+
++MLQ7dATQne1y5n08vswMX9QnNTxFnlK59gWk/0gow4=
+-----END RSA PRIVATE KEY-----
Added: jifty/trunk/t/TestApp-Plugin-OAuth/t/id_rsa.pub
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/t/id_rsa.pub Thu Oct 18 21:52:03 2007
@@ -0,0 +1,5 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALV6QzjTakSJnozJobRHMM2vDR2KAJXR7jmYKBpCzYXSri/Y7wLGOX2l
+uy5/arPWMcoZ0khe6FpbVdGoI4TZkdz+f/6zazxNQhwG97cHaCUrEmGDJXycOlmS
+M4Ry15/y4U9TcCV+bfzaSPRMdxd2CxD6Ot4snVUJLnE1xTpWldrhAgMBAAE=
+-----END RSA PUBLIC KEY-----
More information about the Jifty-commit
mailing list